Php 以编程方式从systemd';中国日报 更新,2013-09-12:

Php 以编程方式从systemd';中国日报 更新,2013-09-12:,php,linux,systemd,Php,Linux,Systemd,我对systemd进行了更深入的研究,它是journal,我偶然发现,它指出: systemd journald将所有接收到的日志消息转发到AF_UNIX SOCK_DGRAMsocket/run/systemd/journal/syslog,如果它存在,UNIX syslog守护进程可以使用它进一步处理数据 根据manpage,我确实将我的环境设置为在下面也有syslog,我相应地调整了我的代码: 这显然只在writesockets上看到(选择)更改。嗯,可能是这里出了什么问题,所以我试图从

我对
systemd
进行了更深入的研究,它是
journal
,我偶然发现,它指出:

systemd journald
将所有接收到的日志消息转发到
AF_UNIX SOCK_DGRAM
socket
/run/systemd/journal/syslog
,如果它存在,UNIX syslog守护进程可以使用它进一步处理数据

根据manpage,我确实将我的环境设置为在下面也有syslog,我相应地调整了我的代码:

这显然只在
write
sockets上看到(选择)更改。嗯,可能是这里出了什么问题,所以我试图从他们那里读到,没有运气(也不应该有):

[Thu,2013年9月12日14:45:15+0300]更改:1.

[Thu,2013年9月12日14:45:15+0300]-------

[Thu,2013年9月12日14:45:15+0300]改为:0。

[Thu,2013年9月12日14:45:15+0300]写:1.

[Thu,2013年9月12日14:45:15+0300]除外:0。

[Thu,2013年9月12日14:45:15+0300]11:资源暂时不可用

现在,这让我有点发疯<代码>系统日志文档说明这应该是可能的。代码有什么问题

原件: 我有一个工作原型,简单地说:

while(true)
{
    exec('journalctl -r -n 1 | more', $result, $exit);

    // do stuff
}
但这感觉是错误的,并且消耗了太多的系统资源,然后我发现journald有套接字

我已尝试连接并读取以下内容:

AF_UNIX, SOCK_DGRAM : /var/run/systemd/journal/socket
AF_UNIX, SOCK_STREAM : /var/run/systemd/journal/stdout
给定的套接字

使用
/var/run/systemd/journal/socket
socket\u select
可以看到0个更改。使用
/var/run/systemd/journal/stdout
我总是(每个循环)得到1个更改,数据为0字节

这是我的“读者”:


/run/systemd/journal/
下的套接字对此不起作用–
../socket
../stdout
实际上是只写的(即用于将数据馈送到日志),而
../syslog
套接字不应该被真正的syslogd以外的任何东西使用,更不用说journald不会通过它发送任何元数据。(事实上,
../syslog
套接字在默认情况下甚至不存在–syslogd必须实际侦听它,并且journald连接到它。)

官方方法是直接从日志文件中读取,并使用inotify观察更改(这与
journalctl——follow
相同,甚至
tail-f/var/log/syslog
代替轮询)。在C程序中,您可以使用中的函数,这些函数将为您执行必要的解析甚至过滤

在其他语言中,有三种选择:调用C库;自己解析日志文件(格式为);或者fork
journalctl——follow
,可以告诉它输出条目(或者更详细的格式)。第三个选项实际上工作得很好,因为它只为整个流分叉一个进程;我已经为它编写了一个PHP包装(见下文)

最近的systemd版本()还附带了,它本质上是基于HTTP的
journalctl
;也就是说,您可以在
http://localhost:19531/entries
。(gatewayd和journalctl甚至都支持从HTML5网页访问流。)但是,由于明显的安全问题,gatewayd在默认情况下被禁用


附件:
journalctl的PHP包装器——follow

<?php
/* © 2013 Mantas Mikulėnas <grawity@gmail.com>
 * Released under the MIT Expat License <https://opensource.org/licenses/MIT>
 */

/* Iterator extends Traversable {
    void    rewind()
    boolean valid()
    void    next()
    mixed   current()
    scalar  key()
}
calls:  rewind, valid==true, current, key
    next, valid==true, current, key
    next, valid==false
*/

class Journal implements Iterator {
    private $filter;
    private $startpos;
    private $proc;
    private $stdout;
    private $entry;

    static function _join_argv($argv) {
        return implode(" ",
            array_map(function($a) {
                return strlen($a) ? escapeshellarg($a) : "''";
            }, $argv));
    }

    function __construct($filter=[], $cursor=null) {
        $this->filter = $filter;
        $this->startpos = $cursor;
    }

    function _close_journal() {
        if ($this->stdout) {
            fclose($this->stdout);
            $this->stdout = null;
        }
        if ($this->proc) {
            proc_close($this->proc);
            $this->proc = null;
        }
        $this->entry = null;
    }

    function _open_journal($filter=[], $cursor=null) {
        if ($this->proc)
            $this->_close_journal();

        $this->filter = $filter;
        $this->startpos = $cursor;

        $cmd = ["journalctl", "-f", "-o", "json"];
        if ($cursor) {
            $cmd[] = "-c";
            $cmd[] = $cursor;
        }
        $cmd = array_merge($cmd, $filter);
        $cmd = self::_join_argv($cmd);

        $fdspec = [
            0 => ["file", "/dev/null", "r"],
            1 => ["pipe", "w"],
            2 => ["file", "/dev/null", "w"],
        ];

        $this->proc = proc_open($cmd, $fdspec, $fds);
        if (!$this->proc)
            return false;
        $this->stdout = $fds[1];
    }

    function seek($cursor) {
        $this->_open_journal($this->filter, $cursor);
    }

    function rewind() {
        $this->seek($this->startpos);
    }

    function next() {
        $line = fgets($this->stdout);
        if ($line === false)
            $this->entry = false;
        else
            $this->entry = json_decode($line);
    }

    function valid() {
        return ($this->entry !== false);
        /* null is valid, it just means next() hasn't been called yet */
    }

    function current() {
        if (!$this->entry)
            $this->next();
        return $this->entry;
    }

    function key() {
        if (!$this->entry)
            $this->next();
        return $this->entry->__CURSOR;
    }
}

$a = new Journal();

foreach ($a as $cursor => $item) {
    echo "================\n";
    var_dump($cursor);
    //print_r($item);
    if ($item)
        var_dump($item->MESSAGE);
}

<?php

define('NL', "\n\r");

$journal = '/var/run/systemd/journal/socket';
$jSTDOUT = '/var/run/systemd/journal/stdout';

$journal = $jSTDOUT;

$sock = socket_create(AF_UNIX, SOCK_STREAM, 0);
$connection = @socket_connect($sock, $journal);

$log = function ($message)
{
    echo '[' . date('r') . '] ' . $message . NL; 
};

if (!$connection)
{
    $log('Couldn\'t connect to ' . $journal);
}
else
{
    $log('Connected to ' . $journal);

    $readables = array($sock);

    while (true)
    {
        $read = $readables;

        if (socket_select($read, $write = NULL, $except = NULL, 0) < 1)
        {
            continue;
        }

        foreach ($read as $read_socket)
        {
            $data = @socket_read($read_socket, 1024, PHP_BINARY_READ);

            if ($data === false)
            {
                $log('Couldn\'t read.');

                socket_shutdown($read_socket, 2);
                socket_close($read_socket);

                $log('Server terminated.');
                break 2;
            }

            $data = trim($data);

            if (!empty($data))
            {
                $log($data);
            }
        }
    }

    $log('Exiting.');
}
<?php
/* © 2013 Mantas Mikulėnas <grawity@gmail.com>
 * Released under the MIT Expat License <https://opensource.org/licenses/MIT>
 */

/* Iterator extends Traversable {
    void    rewind()
    boolean valid()
    void    next()
    mixed   current()
    scalar  key()
}
calls:  rewind, valid==true, current, key
    next, valid==true, current, key
    next, valid==false
*/

class Journal implements Iterator {
    private $filter;
    private $startpos;
    private $proc;
    private $stdout;
    private $entry;

    static function _join_argv($argv) {
        return implode(" ",
            array_map(function($a) {
                return strlen($a) ? escapeshellarg($a) : "''";
            }, $argv));
    }

    function __construct($filter=[], $cursor=null) {
        $this->filter = $filter;
        $this->startpos = $cursor;
    }

    function _close_journal() {
        if ($this->stdout) {
            fclose($this->stdout);
            $this->stdout = null;
        }
        if ($this->proc) {
            proc_close($this->proc);
            $this->proc = null;
        }
        $this->entry = null;
    }

    function _open_journal($filter=[], $cursor=null) {
        if ($this->proc)
            $this->_close_journal();

        $this->filter = $filter;
        $this->startpos = $cursor;

        $cmd = ["journalctl", "-f", "-o", "json"];
        if ($cursor) {
            $cmd[] = "-c";
            $cmd[] = $cursor;
        }
        $cmd = array_merge($cmd, $filter);
        $cmd = self::_join_argv($cmd);

        $fdspec = [
            0 => ["file", "/dev/null", "r"],
            1 => ["pipe", "w"],
            2 => ["file", "/dev/null", "w"],
        ];

        $this->proc = proc_open($cmd, $fdspec, $fds);
        if (!$this->proc)
            return false;
        $this->stdout = $fds[1];
    }

    function seek($cursor) {
        $this->_open_journal($this->filter, $cursor);
    }

    function rewind() {
        $this->seek($this->startpos);
    }

    function next() {
        $line = fgets($this->stdout);
        if ($line === false)
            $this->entry = false;
        else
            $this->entry = json_decode($line);
    }

    function valid() {
        return ($this->entry !== false);
        /* null is valid, it just means next() hasn't been called yet */
    }

    function current() {
        if (!$this->entry)
            $this->next();
        return $this->entry;
    }

    function key() {
        if (!$this->entry)
            $this->next();
        return $this->entry->__CURSOR;
    }
}

$a = new Journal();

foreach ($a as $cursor => $item) {
    echo "================\n";
    var_dump($cursor);
    //print_r($item);
    if ($item)
        var_dump($item->MESSAGE);
}