feat(logging): Optimize Loki log configuration to support distributed tracing - Disable readMarkers and verbose mode to improve performance - Configure low-cardinality static labels, following official recommended practices

- Add support for structured metadata to associate trace_id and span_id
- Update the log message format to include complete exception stack trace information
- Configure HTTP timeout parameters to enhance connection stability
- Add batch processing configuration to prevent log truncation and set an appropriate buffer size
- Add downgrade handling to accommodate missing features in older API versions
This commit is contained in:
smile 2025-11-04 16:23:29 +08:00
parent 1f727a420f
commit 5e5e5c9e3a

View file

@ -29,32 +29,61 @@ public class LokiAppenderConfig implements ApplicationListener<ApplicationStarte
lokiAppender.setName("LOKI");
lokiAppender.setContext(context);
lokiAppender.setMetricsEnabled(true);
lokiAppender.setReadMarkers(true);
lokiAppender.setStructuredMetadata("level=%level\nthread=%thread\nlogger=%logger\n*=%%mdc\n*=%%kvp");
lokiAppender.setReadMarkers(false);
lokiAppender.setVerbose(false);
// 标签只使用低基数的静态标签官方推荐
lokiAppender.setLabels(String.format(
"app=%s\nhost=%s",
serviceName,
context.getProperty(CoreConstants.HOSTNAME_KEY)
));
{
// 使用 PatternLayout 设置日志格式
PatternLayout layout = new PatternLayout();
layout.setContext(context);
layout.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%X{traceId:-}] - %msg%n%throwable");
layout.setOutputPatternAsHeader(true);
layout.start();
lokiAppender.setMessage(layout);
}
{
// 设置 HTTP 配置
PipelineConfigAppenderBase.HttpCfg httpCfg = new Loki4jAppender.HttpCfg();
httpCfg.setUrl(logEndpoint);
lokiAppender.setHttp(httpCfg);
// 结构化元数据包含 trace_id span_id支持 Tempo 关联
lokiAppender.setStructuredMetadata(
"level=%level\n" +
"thread=%thread\n" +
"logger=%logger\n" +
"trace_id=%mdc{traceId}\n" +
"span_id=%mdc{spanId}\n" +
"*=%%mdc\n" +
"*=%%kvp"
);
// 日志消息格式包含 traceIdspanId 和完整异常堆栈
PatternLayout messageLayout = new PatternLayout();
messageLayout.setContext(context);
messageLayout.setPattern(
"%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} " +
"[traceId=%X{traceId:-}, spanId=%X{spanId:-}] - %msg%n%ex{full}"
);
messageLayout.setOutputPatternAsHeader(false);
messageLayout.start();
lokiAppender.setMessage(messageLayout);
// HTTP 配置
PipelineConfigAppenderBase.HttpCfg httpCfg = new Loki4jAppender.HttpCfg();
httpCfg.setUrl(logEndpoint);
httpCfg.setConnectionTimeoutMs(10000);
httpCfg.setRequestTimeoutMs(60000);
lokiAppender.setHttp(httpCfg);
// 批处理配置防止日志被截断关键配置
try {
// 官方文档参数: maxItems, maxBytes, timeoutMs
PipelineConfigAppenderBase.BatchCfg batchCfg = new Loki4jAppender.BatchCfg();
batchCfg.setMaxItems(1000);
batchCfg.setMaxBytes(4194304); // 4MB足够容纳完整堆栈
batchCfg.setTimeoutMs(1000);
lokiAppender.setBatch(batchCfg);
} catch (Exception e) {
// 如果 BatchCfg API 不存在使用默认配置并记录警告
System.err.println("Warning: Cannot configure batch settings, using defaults. " + e.getMessage());
}
lokiAppender.start();
Logger rootLogger = context.getLogger("ROOT");
rootLogger.addAppender(lokiAppender);
}
}