115 lines
3.2 KiB
PHP
115 lines
3.2 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace app\common\service;
|
||
|
||
use think\Config;
|
||
|
||
/**
|
||
* 工单云控同步调试日志(仅 app_debug=true 时写入 runtime/log/split_sync.log)
|
||
*/
|
||
class SplitTicketSyncLogger
|
||
{
|
||
private const LOG_FILE = 'split_sync.log';
|
||
|
||
private static ?string $ticketTag = null;
|
||
|
||
public static function isEnabled(): bool
|
||
{
|
||
return (bool) Config::get('app_debug');
|
||
}
|
||
|
||
public static function setTicketContext(?int $ticketId, ?string $ticketType = null): void
|
||
{
|
||
if ($ticketId === null || $ticketId <= 0) {
|
||
self::$ticketTag = null;
|
||
return;
|
||
}
|
||
$type = $ticketType !== null && $ticketType !== '' ? $ticketType : '-';
|
||
self::$ticketTag = sprintf('#%d(%s)', $ticketId, $type);
|
||
}
|
||
|
||
public static function clearTicketContext(): void
|
||
{
|
||
self::$ticketTag = null;
|
||
}
|
||
|
||
/**
|
||
* @param array<string, mixed> $context
|
||
*/
|
||
public static function log(string $stage, string $message, array $context = []): void
|
||
{
|
||
if (!self::isEnabled()) {
|
||
return;
|
||
}
|
||
|
||
$context = self::sanitize($context);
|
||
$ctxJson = $context !== [] ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE) : '';
|
||
$line = sprintf(
|
||
"[%s] %s [%s] %s%s\n",
|
||
date('Y-m-d H:i:s'),
|
||
self::$ticketTag ?? '[global]',
|
||
$stage,
|
||
$message,
|
||
$ctxJson
|
||
);
|
||
|
||
$runtime = defined('RUNTIME_PATH') ? RUNTIME_PATH : (ROOT_PATH . 'runtime/');
|
||
$dir = $runtime . 'log/';
|
||
if (!is_dir($dir)) {
|
||
@mkdir($dir, 0755, true);
|
||
}
|
||
@file_put_contents($dir . self::LOG_FILE, $line, FILE_APPEND | LOCK_EX);
|
||
}
|
||
|
||
/**
|
||
* @param array<string, mixed> $context
|
||
* @return array<string, mixed>
|
||
*/
|
||
private static function sanitize(array $context): array
|
||
{
|
||
$out = [];
|
||
foreach ($context as $key => $value) {
|
||
$lower = strtolower((string) $key);
|
||
if (in_array($lower, ['password', 'passwd', 'pwd', 'token', 'secret'], true)) {
|
||
continue;
|
||
}
|
||
if ($key === 'authActions' && is_array($value)) {
|
||
$out[$key] = self::sanitizeAuthActions($value);
|
||
continue;
|
||
}
|
||
if (is_array($value)) {
|
||
$out[$key] = self::sanitize($value);
|
||
continue;
|
||
}
|
||
if (is_string($value) && mb_strlen($value) > 800) {
|
||
$out[$key] = mb_substr($value, 0, 800, 'UTF-8') . '...(truncated)';
|
||
continue;
|
||
}
|
||
$out[$key] = $value;
|
||
}
|
||
return $out;
|
||
}
|
||
|
||
/**
|
||
* @param array<int, mixed> $actions
|
||
* @return array<int, mixed>
|
||
*/
|
||
private static function sanitizeAuthActions(array $actions): array
|
||
{
|
||
$sanitized = [];
|
||
foreach ($actions as $action) {
|
||
if (!is_array($action)) {
|
||
$sanitized[] = $action;
|
||
continue;
|
||
}
|
||
if (array_key_exists('value', $action)) {
|
||
$action['value'] = '***';
|
||
}
|
||
$sanitized[] = $action;
|
||
}
|
||
return $sanitized;
|
||
}
|
||
}
|