PHP项目实现Graylog集中式日志管理,核心是通过GELF协议将PHP运行时产生的业务日志、错误日志发送到Graylog服务端,再由Graylog完成存储、索引和检索。这种方式可以解决多服务器部署时日志分散、排查问题需要登录多台机器的痛点。

环境准备
首先需要确保Graylog服务端已经部署完成,并且开启了GELF输入端口,默认UDP端口为12201,也可以根据需求配置TCP或者HTTP输入。PHP端需要安装GELF发送相关的依赖,推荐使用graylog2/gelf-php库,通过Composer进行安装。
composer require graylog2/gelf-php
基础日志发送实现
安装依赖后,可以编写基础的日志发送类,将PHP的日志信息封装成GELF格式发送到Graylog。以下是一个简单的实现示例:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use GelfPublisher;
use GelfTransportUdpTransport;
use GelfMessage;
class GraylogLogger {
private $publisher;
public function __construct($graylogHost = '127.0.0.1', $graylogPort = 12201) {
// 创建UDP传输实例
$transport = new UdpTransport($graylogHost, $graylogPort, UdpTransport::CHUNK_SIZE_LAN);
// 创建发布者
$this->publisher = new Publisher($transport);
}
/**
* 发送日志到Graylog
* @param string $message 日志内容
* @param string $level 日志级别 如info、warning、error
* @param array $extraFields 额外字段 如用户ID、请求路径等
*/
public function send($message, $level = 'info', $extraFields = []) {
$gelfMessage = new Message();
$gelfMessage->setShortMessage($message)
->setLevel($this->getLevelCode($level))
->setTimestamp(time());
// 添加额外字段
foreach ($extraFields as $key => $value) {
$gelfMessage->setAdditional($key, $value);
}
$this->publisher->publish($gelfMessage);
}
/**
* 转换日志级别为GELF对应的数字
*/
private function getLevelCode($level) {
$levelMap = [
'debug' => 7,
'info' => 6,
'notice' => 5,
'warning' => 4,
'error' => 3,
'critical' => 2,
'alert' => 1,
'emergency' => 0
];
return $levelMap[$level] ?? 6;
}
}
// 使用示例
$logger = new GraylogLogger('192.168.0.1', 12201);
$logger->send('用户登录成功', 'info', ['user_id' => 1001, 'request_path' => '/api/login']);
集成到PHP错误和异常处理器
为了让PHP的错误和异常自动发送到Graylog,可以自定义错误处理器和异常处理器,将错误信息通过上面的日志类发送出去。
<?php
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/GraylogLogger.php';
$logger = new GraylogLogger('192.168.0.1', 12201);
// 自定义错误处理器
set_error_handler(function ($errno, $errstr, $errfile, $errline) use ($logger) {
$levelMap = [
E_WARNING => 'warning',
E_NOTICE => 'notice',
E_ERROR => 'error',
E_USER_ERROR => 'error',
E_USER_WARNING => 'warning',
E_USER_NOTICE => 'notice'
];
$level = $levelMap[$errno] ?? 'error';
$message = "错误[{$errno}]: {$errstr},文件:{$errfile},行号:{$errline}";
$logger->send($message, $level, [
'error_no' => $errno,
'error_file' => $errfile,
'error_line' => $errline
]);
// 不阻止默认错误处理
return false;
});
// 自定义异常处理器
set_exception_handler(function ($exception) use ($logger) {
$message = "未捕获异常:" . $exception->getMessage();
$logger->send($message, 'error', [
'exception_class' => get_class($exception),
'exception_file' => $exception->getFile(),
'exception_line' => $exception->getLine(),
'exception_trace' => $exception->getTraceAsString()
]);
});
// 测试错误
echo $undefinedVariable;
// 测试异常
throw new Exception('测试异常发送');
常见问题处理
- 日志发送失败:检查Graylog服务是否正常运行,输入端口是否开放,防火墙是否允许对应端口的流量通过。
- 日志字段缺失:确认额外字段的键名符合Graylog的字段命名规范,不要使用特殊字符作为字段名。
- UDP丢包问题:如果日志量较大,UDP传输可能出现丢包,可以切换为TCP传输,修改传输方式为
TcpTransport即可。
注意事项
不要在日志中发送敏感信息,比如用户密码、身份证号等数据,避免日志泄露造成安全风险。同时可以根据业务需求过滤不需要的日志,减少Graylog的存储压力。如果需要发送HTTP请求的日志,可以在额外字段中添加请求方法、请求参数、响应状态码等信息,方便后续排查接口问题。