以编程方式从systemd的日志中读取消息

时间:2013-09-09 14:20:59

标签: php linux systemd

更新,2013-09-12:

我已经深入挖掘了systemd并且它是journal,并且我偶然发现了this,其中指出:

  

systemd-journald会将所有收到的日志消息转发到AF_UNIX SOCK_DGRAM套接字/run/systemd/journal/syslog(如果存在),Unix syslog守护程序可以使用它来进一步处理数据。

根据联机帮助页,我确实设置了我的环境,下面还有syslog,我相应地调整了我的代码:

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

$log = function ()
{
    if (func_num_args() >= 1)
    {
        $message = call_user_func_array('sprintf', func_get_args());

        echo '[' . date('r') . '] ' . $message . NL; 
    }
};

$syslog = '/var/run/systemd/journal/syslog';

$sock = socket_create(AF_UNIX, SOCK_DGRAM, 0);
$connection = socket_connect($sock, $syslog);

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

    $readables = array($sock);

    socket_set_nonblock($sock);

    while (true)
    {
        $read = $readables;
        $write = $readables;
        $except = $readables;

        $select = socket_select($read, $write, $except, 0);

        $log('Changes: %d.', $select);
        $log('-------');
        $log('Read: %d.', count($read));
        $log('Write: %d.', count($write));
        $log('Except: %d.', count($except));

        if ($select > 0)
        {
            if ($read)
            {
                foreach ($read as $readable)
                {
                    $data = socket_read($readable, 4096, PHP_BINARY_READ);

                    if ($data === false)
                    {
                        $log(socket_last_error() . ': ' . socket_strerror(socket_last_error()));
                    }
                    else if (!empty($data))
                    {
                        $log($data);
                    }
                    else
                    {
                        $log('Read empty.');
                    }
                }
            }

            if ($write)
            {
                foreach ($write as $writable)
                {
                    $data = socket_read($writable, 4096, PHP_BINARY_READ);

                    if ($data === false)
                    {
                        $log(socket_last_error() . ': ' . socket_strerror(socket_last_error()));
                    }
                    else if (!empty($data))
                    {
                        $log($data);
                    }
                    else
                    {
                        $log('Write empty.');
                    }
                }
            }
        }
    }
}

这显然只能在write套接字上看到(选择)更改。好吧,可能是这里的东西是错的,所以我试图从他们那里读,没有运气(也不应该):

  

[Thu, 12 Sep 2013 14:45:15 +0300] Changes: 1.
  [Thu, 12 Sep 2013 14:45:15 +0300] -------
  [Thu, 12 Sep 2013 14:45:15 +0300] Read: 0.
  [Thu, 12 Sep 2013 14:45:15 +0300] Write: 1.
  [Thu, 12 Sep 2013 14:45:15 +0300] Except: 0.
  [Thu, 12 Sep 2013 14:45:15 +0300] 11: Resource temporarily unavailable

现在,这让我疯了一点。 syslog文档说这应该是可能的。代码有什么问题?

原件:

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

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

    // do stuff
}

但是这感觉不对,并且消耗了太多的系统资源,然后我发现了有插槽的日记。

我试图连接并阅读:

AF_UNIX, SOCK_DGRAM : /var/run/systemd/journal/socket
AF_UNIX, SOCK_STREAM : /var/run/systemd/journal/stdout

给定的套接字。

使用/var/run/systemd/journal/socketsocket_select看到0更改。使用/var/run/systemd/journal/stdout我总是(每个循环)获得1次更改,使用0字节数据。

这是我的“读者”:

<?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.');
}

在读取套接字中没有数据,我认为我做错了。

问题,想法:

我的目标是阅读消息,并在其中一些消息上执行回调

有人能指出我如何以编程方式阅读期刊信息的正确方向吗?

1 个答案:

答案 0 :(得分:9)

/run/systemd/journal/下的套接字不起作用 - …/socket…/stdout实际上是只写的(即用于将数据提供到期刊中)虽然…/syslog套接字不应该由真正的syslogd以外的任何东西使用,更不用说journald不​​会通过它发送任何元数据。 (事实上​​,默认情况下,…/syslog套接字甚至不存在 - syslogd必须实际监听它,并且journald连接到它。)

官方方法是直接从日记文件中读取,并使用 inotify 来监视更改(这是journalctl --follow甚至{ {1}}代替投票使用)。在C程序中,您可以使用libsystemd-journal中的函数,它们将为您进行必要的解析甚至过滤。

在其他语言中,您有三种选择:调用C库;自己解析日志文件(格式为documented);或者分叉tail -f /var/log/syslog,可以告诉它输出JSON-formatted条目(或更详细的journal export格式)。第三个选项实际上非常有效,因为它只为整个流分配一个进程;我为它编写了一个PHP包装器(见下文)。

最近的系统版本(v193)也附带 systemd-journal-gatewayd ,它基本上是基于HTTP的journalctl --follow版本;也就是说,您可以在journalctl获取JSON或日记导出流。 ( gatewayd journalctl 都支持server-sent events从HTML 5网页访问流。)但是,由于明显的安全问题, gatewayd 默认情况下处于禁用状态。


附件:http://localhost:19531/entries

的PHP包装器
journalctl --follow