gpt4 book ai didi

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

转载 作者:IT王子 更新时间:2023-10-29 01:11:56 26 4
gpt4 key购买 nike

更新,2013 年 9 月 12 日:

我对 systemd 进行了更深入的研究,它是 journal,而且我偶然发现了 this ,即:

systemd-journald will forward all received log messages to the AF_UNIX SOCK_DGRAM socket /run/systemd/journal/syslog, if it exists, which may be used by Unix syslog daemons to process the data further.

根据联机帮助页,我确实将我的环境设置为在下面也有系统日志,我相应地调整了我的代码:

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 文档说这应该是可能的。代码有什么问题?

原文:

我有一个工作原型(prototype),只需:

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

// do stuff
}

但是这样感觉不对,太耗系统资源,后来发现journald有sockets。

我已尝试连接并读取:

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

读取套接字中没有数据,我假设我做错了什么。

问题、想法:

我的目标是读取消息并根据其中的一些执行回调

谁能指出我如何以编程方式阅读日志消息的正确方向?

最佳答案

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

官方的方法是直接从journal files读取,然后用inotify观察变化(和journalctl --follow 甚至 tail -f/var/log/syslog 代替轮询)。在 C 程序中,您可以使用 libsystemd-journal 中的函数,它将为您进行必要的解析甚至过滤。

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

最近的 systemd 版本 ( v193 ) 也带有 systemd-journal-gatewayd ,本质上是 journalctl 的基于 HTTP 的版本;也就是说,您可以在 http://localhost:19531/entries 获取 JSON 或日志导出流。 (gatewaydjournalctl 甚至支持 server-sent events 从 HTML 5 网页访问流。)但是,由于明显的安全问题,gatewayd默认情况下禁用。


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

<?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 - 以编程方式从 systemd 的日志中读取消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18711610/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com