Skip to main content

tdm_server_rust/telemetry/skywalking/
logger.rs

1//! tracing 事件 → SkyWalking 原生 Log
2
3use super::context;
4use ::skywalking::logging::{logger::Logger, record::LogRecord};
5use std::sync::{Arc, OnceLock};
6use tracing::{Event, Level, Subscriber};
7use tracing_subscriber::layer::Context;
8use tracing_subscriber::Layer;
9
10static SW_LOGGER: OnceLock<Arc<Logger>> = OnceLock::new();
11
12/// 注册全局 SW Logger(init 时调用)
13pub fn set_global_logger(logger: Arc<Logger>) {
14    let _ = SW_LOGGER.set(logger);
15}
16
17/// 获取全局 SW Logger
18pub fn global_logger() -> Option<&'static Arc<Logger>> {
19    SW_LOGGER.get()
20}
21
22/// SW Log Layer 或空操作
23pub enum SwLogLayer {
24    /// 实际上报
25    Active(SkyWalkingLogLayer),
26    /// 禁用
27    Noop(crate::telemetry::noop_layer::NoopLayer),
28}
29
30impl SwLogLayer {
31    /// 空 Layer
32    pub fn noop() -> Self {
33        Self::Noop(crate::telemetry::noop_layer::NoopLayer)
34    }
35}
36
37impl<S> Layer<S> for SwLogLayer
38where
39    S: Subscriber,
40    SkyWalkingLogLayer: Layer<S>,
41    crate::telemetry::noop_layer::NoopLayer: Layer<S>,
42{
43    fn on_event(&self, event: &tracing::Event<'_>, ctx: Context<'_, S>) {
44        match self {
45            Self::Active(l) => l.on_event(event, ctx),
46            Self::Noop(l) => l.on_event(event, ctx),
47        }
48    }
49}
50
51/// SW Logger 桥接 Layer(仅 HTTP 请求上下文内上报,过滤启动/RSS 噪音)
52pub struct SkyWalkingLogLayer {
53    /// SW Logger 句柄
54    logger: Arc<Logger>,
55    /// 最低上报级别
56    min_level: Level,
57}
58
59impl SkyWalkingLogLayer {
60    /// 创建 Layer
61    pub fn new(logger: Arc<Logger>, min_level: Level) -> Self {
62        Self { logger, min_level }
63    }
64}
65
66impl<S> Layer<S> for SkyWalkingLogLayer
67where
68    S: Subscriber,
69{
70    fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
71        if !context::is_in_request_context() {
72            return;
73        }
74        let level = *event.metadata().level();
75        if level > self.min_level {
76            return;
77        }
78
79        let mut message = String::new();
80        let mut visitor = MessageVisitor(&mut message);
81        event.record(&mut visitor);
82        if message.is_empty() {
83            return;
84        }
85
86        context::with_current(|ctx| {
87            let record = LogRecord::new()
88                .content(message)
89                .with_tracing_context(ctx)
90                .add_tag("level", level.to_string());
91            self.logger.log(record);
92        });
93    }
94}
95
96/// 提取 tracing 事件 message 字段
97struct MessageVisitor<'a>(&'a mut String);
98
99impl tracing::field::Visit for MessageVisitor<'_> {
100    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
101        if field.name() == "message" {
102            self.0.push_str(&format!("{value:?}").trim_matches('"'));
103        }
104    }
105
106    fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
107        if field.name() == "message" {
108            self.0.push_str(value);
109        }
110    }
111}
112
113/// 结构化 HTTP 摘要写入 SW Log
114pub fn log_http_summary(method: &str, path: &str, status: u16, duration_ms: u128) {
115    let Some(logger) = global_logger() else {
116        return;
117    };
118    if !context::is_in_request_context() {
119        return;
120    }
121    let content = format!("HTTP {method} {path} status={status} duration_ms={duration_ms}");
122    context::with_current(|ctx| {
123        let record = LogRecord::new()
124            .content(content)
125            .with_tracing_context(ctx)
126            .add_tag("http.method", method)
127            .add_tag("url.path", path)
128            .add_tag("http.status_code", status.to_string())
129            .add_tag("duration_ms", duration_ms.to_string());
130        logger.log(record);
131    });
132}
133
134/// 结构化错误写入 SW Log
135pub fn log_error_event(content: &str) {
136    let Some(logger) = global_logger() else {
137        return;
138    };
139    if !context::is_in_request_context() {
140        return;
141    }
142    context::with_current(|ctx| {
143        let record = LogRecord::new()
144            .content(content.to_string())
145            .with_tracing_context(ctx)
146            .add_tag("level", "WARN");
147        logger.log(record);
148    });
149}