tdm_server_rust/telemetry/skywalking/
logger.rs1use 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
12pub fn set_global_logger(logger: Arc<Logger>) {
14 let _ = SW_LOGGER.set(logger);
15}
16
17pub fn global_logger() -> Option<&'static Arc<Logger>> {
19 SW_LOGGER.get()
20}
21
22pub enum SwLogLayer {
24 Active(SkyWalkingLogLayer),
26 Noop(crate::telemetry::noop_layer::NoopLayer),
28}
29
30impl SwLogLayer {
31 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
51pub struct SkyWalkingLogLayer {
53 logger: Arc<Logger>,
55 min_level: Level,
57}
58
59impl SkyWalkingLogLayer {
60 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
96struct 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
113pub 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
134pub 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}