Files
evus.acvisa.com/app/log/driver/Monolog.php

126 lines
4.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2021 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
declare (strict_types = 1);
namespace app\log\driver;
use think\App;
use think\contract\LogHandlerInterface;
use think\facade\Config;
/**
* 本地化调试输出到文件
*/
class Monolog implements LogHandlerInterface
{
/**
* 配置参数
* @var array
*/
protected $config = [
'time_format' => 'c',
'json_options' => JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
];
/**
* 日志写入接口
* @access public
* @param array $log 日志信息
* @return bool
*/
public function save(array $log): bool
{
$info = [];
// 日志信息封装
$time = \DateTime::createFromFormat('0.u00 U', microtime())->setTimezone(new \DateTimeZone(date_default_timezone_get()))->format($this->config['time_format']);
foreach ($log as $type => $val) {
$message = [];
foreach ($val as $msg) {
$message[] = json_encode(['time' => $time, 'type' => $type, 'msg' => $msg], $this->config['json_options']) ;
}
$info[$type] = $message;
}
if ($info) {
return $this->write($info);
}
return true;
}
/**
* 日志写入
* @access protected
* @param array $message 日志信息
* @param string $destination 日志文件
* @return bool
*/
protected function write(array $message): bool
{
$tcp_log_url = env('TCP_LOG_URL');
$tcp_log_port = (int) env('TCP_LOG_PORT');
$project = env('PROJECT_NAME');
// 快速失败:配置缺失
if (!$tcp_log_url || !$tcp_log_port || !$project) {
return true; // 或 error_log("Log config missing");
}
// 1. 安全序列化并限制大小(例如最大 64KB)
$maxSize = 64 * 1024; // 64KB
$json = json_encode([
'message' => $message,
'project' => $project
], $this->config['json_options'] ?? 0);
if ($json === false) {
error_log("Failed to encode log message: " . json_last_error_msg());
return true;
}
// 截断过大的日志(保留结构完整性)
if (strlen($json) > $maxSize) {
// 尝试只截断 message 内容(更智能)
$fallbackMessage = [
'message' => '[TRUNCATED] Log too large (' . strlen($json) . ' bytes)',
'project' => $project,
'original_size_bytes' => strlen($json)
];
$json = json_encode($fallbackMessage, $this->config['json_options'] ?? 0);
}
// 2. 添加换行符(Logstash tcp + json_lines 必需)
$payload = $json . "\n";
// 3. 建立连接(适当延长超时)
$socket = @fsockopen($tcp_log_url, $tcp_log_port, $errno, $errstr, 2); // 2秒超时
if (!$socket) {
error_log("Log socket connect failed to {$tcp_log_url}:{$tcp_log_port} - {$errstr} ({$errno})");
return true; // 不影响主流程
}
// 4. 发送数据(抑制 fwrite 警告,但可选记录)
$bytesWritten = @fwrite($socket, $payload);
$expected = strlen($payload);
if ($bytesWritten === false || $bytesWritten !== $expected) {
error_log("Partial or failed log send to {$tcp_log_url}:{$tcp_log_port} (sent {$bytesWritten}/{$expected} bytes)");
}
// 5. 确保关闭 socket
fclose($socket);
return true;
}
}