1use std::collections::BTreeMap;
7use std::sync::{Arc, LazyLock, Mutex, OnceLock};
8
9use serde_json::Value;
10
11use crate::backpressure::{release, try_acquire, QueueTicket};
12use crate::config::TelemetryConfig;
13use crate::consent::should_allow;
14use crate::context::get_context;
15use crate::health::increment_emitted;
16use crate::runtime::get_runtime_config;
17use crate::sampling::{set_sampling_policy, should_sample, SamplingPolicy, Signal};
18use crate::tracer::get_trace_context;
19
20mod emit;
21mod levels;
22mod pretty;
23mod processors;
24
25use emit::{emit_if_console, emit_if_json, emit_if_otel, emit_if_pretty};
26pub(crate) use emit::now_iso8601;
30pub use emit::{
31 enable_console_capture_for_tests, enable_json_capture_for_tests,
32 enable_pretty_capture_for_tests, take_console_capture, take_json_capture, take_pretty_capture,
33};
34use levels::{effective_level_threshold, level_order};
35use processors::process_event;
36
37#[inline(always)]
38fn consent_allows_logs(level: &str) -> bool {
39 should_allow("logs", Some(level))
40}
41
42const MAX_FALLBACK_EVENTS: usize = 1000;
43
44static LOGGING_CONFIG_OVERRIDE: LazyLock<Mutex<Option<crate::config::LoggingConfig>>> =
45 LazyLock::new(|| Mutex::new(None));
46
47#[cfg_attr(test, mutants::skip)] fn empty_events_mutex() -> Mutex<Vec<LogEvent>> {
49 Mutex::new(Vec::new())
50}
51
52pub fn configure_logging(config: crate::config::LoggingConfig) {
58 *crate::_lock::lock(&LOGGING_CONFIG_OVERRIDE) = Some(config);
59}
60
61pub fn reset_logging_config_for_tests() {
63 *crate::_lock::lock(&LOGGING_CONFIG_OVERRIDE) = None;
64}
65
66fn active_logging_config() -> crate::config::LoggingConfig {
69 let override_cfg = crate::_lock::lock(&LOGGING_CONFIG_OVERRIDE).clone();
70 if let Some(cfg) = override_cfg {
71 return cfg;
72 }
73 if let Some(cfg) = get_runtime_config() {
74 return cfg.logging.clone();
75 }
76 match TelemetryConfig::from_env() {
77 Ok(cfg) => cfg.logging,
78 Err(err) => {
79 eprintln!("provide_telemetry: logging config parse failed, using defaults: {err}");
80 crate::config::LoggingConfig::default()
81 }
82 }
83}
84
85fn runtime_identity_config() -> Option<TelemetryConfig> {
86 match get_runtime_config() {
87 Some(cfg) => Some(cfg),
88 None => TelemetryConfig::from_env().ok(),
89 }
90}
91
92fn apply_lazy_logger_policies_from_env() {
93 if get_runtime_config().is_some() {
94 return;
95 }
96 if std::env::var_os("PROVIDE_SAMPLING_LOGS_RATE").is_none() {
97 return;
98 }
99 let Ok(cfg) = TelemetryConfig::from_env() else {
100 return;
101 };
102 let _ = set_sampling_policy(
103 Signal::Logs,
104 SamplingPolicy {
105 default_rate: cfg.sampling.logs_rate,
106 overrides: BTreeMap::new(),
107 },
108 );
109}
110
111fn inject_identity_fields(context: &mut BTreeMap<String, Value>, cfg: TelemetryConfig) {
112 context
113 .entry("service".to_string())
114 .or_insert(Value::String(cfg.service_name));
115 context
116 .entry("env".to_string())
117 .or_insert(Value::String(cfg.environment));
118 context
119 .entry("version".to_string())
120 .or_insert(Value::String(cfg.version));
121}
122
123fn inject_runtime_identity_fields(context: &mut BTreeMap<String, Value>) {
124 let cfg = match runtime_identity_config() {
125 Some(cfg) => cfg,
126 None => return,
127 };
128 inject_identity_fields(context, cfg);
129}
130
131fn emit_event(mut event: LogEvent) {
133 process_event(&mut event);
134 emit_if_json(&event);
135 emit_if_pretty(&event);
136 emit_if_console(&event);
137 emit_if_otel(&event);
138 let mut buf = crate::_lock::lock(events());
139 if buf.len() >= MAX_FALLBACK_EVENTS {
140 return;
141 }
142 buf.push(event);
143}
144
145fn acquire_log_ticket(level: &str, target: &str, sample_key: Option<&str>) -> Option<QueueTicket> {
146 let config = active_logging_config();
147 if level_order(level) < effective_level_threshold(target, &config) {
148 return None;
149 }
150 if !consent_allows_logs(level) {
151 return None;
152 }
153 if !should_sample(Signal::Logs, sample_key).unwrap_or(true) {
154 return None;
155 }
156 try_acquire(Signal::Logs)
157}
158
159fn log_event_with_fields(
161 level: &str,
162 target: &str,
163 message: &str,
164 extra: &BTreeMap<String, Value>,
165) {
166 let Some(ticket) = acquire_log_ticket(level, target, Some(message)) else {
167 return;
168 };
169 let mut event = new_event(target, level, message);
170 event.context.extend(extra.clone());
171 emit_event(event);
172 increment_emitted(Signal::Logs, 1);
173 release(ticket);
174}
175
176fn log_event(level: &str, target: &str, message: &str) {
178 let Some(ticket) = acquire_log_ticket(level, target, Some(message)) else {
179 return;
180 };
181 emit_event(new_event(target, level, message));
182 increment_emitted(Signal::Logs, 1);
183 release(ticket);
184}
185
186fn log_event_with_event(level: &str, target: &str, ev: &crate::schema::Event) {
188 let Some(ticket) = acquire_log_ticket(level, target, Some(&ev.event)) else {
189 return;
190 };
191 let mut event = new_event(target, level, &ev.event);
192 event.event_metadata = Some(EventMetadata {
193 domain: ev.domain.clone(),
194 action: ev.action.clone(),
195 resource: ev.resource.clone(),
196 status: ev.status.clone(),
197 });
198 emit_event(event);
199 increment_emitted(Signal::Logs, 1);
200 release(ticket);
201}
202
203#[derive(Clone, Debug, PartialEq, Eq)]
206pub struct EventMetadata {
207 pub domain: String,
208 pub action: String,
209 pub resource: Option<String>,
210 pub status: String,
211}
212
213#[derive(Clone, Debug, Default, PartialEq)]
214pub struct LogEvent {
215 pub level: String,
216 pub target: String,
217 pub message: String,
218 pub context: BTreeMap<String, Value>,
219 pub trace_id: Option<String>,
220 pub span_id: Option<String>,
221 pub event_metadata: Option<EventMetadata>,
222}
223
224#[derive(Clone, Debug, Default, PartialEq, Eq)]
225pub struct Logger {
226 target: String,
227}
228
229#[derive(Clone, Debug, Default, PartialEq, Eq)]
230pub struct NullLogger {
231 target: String,
232}
233
234#[derive(Clone, Debug, Default)]
235pub struct BufferLogger {
236 target: String,
237 events: Arc<Mutex<Vec<LogEvent>>>,
238}
239
240static EVENTS: OnceLock<Mutex<Vec<LogEvent>>> = OnceLock::new();
241
242fn events() -> &'static Mutex<Vec<LogEvent>> {
243 EVENTS.get_or_init(empty_events_mutex)
244}
245
246fn default_logger() -> Logger {
247 Logger::new(None)
248}
249
250pub static logger: LazyLock<Logger> = LazyLock::new(default_logger);
251
252fn logger_target(target: Option<&str>) -> String {
253 match target {
254 Some(target) => target.to_string(),
255 None => "provide.telemetry".to_string(),
256 }
257}
258
259fn new_event(target: &str, level: &str, message: &str) -> LogEvent {
260 let trace = get_trace_context();
261 let mut context = get_context();
262 inject_runtime_identity_fields(&mut context);
263 LogEvent {
264 level: level.to_string(),
265 target: target.to_string(),
266 message: message.to_string(),
267 context,
268 trace_id: trace.get("trace_id").and_then(Clone::clone),
269 span_id: trace.get("span_id").and_then(Clone::clone),
270 event_metadata: None,
271 }
272}
273
274impl Logger {
275 pub fn new(target: Option<&str>) -> Self {
276 Self {
277 target: logger_target(target),
278 }
279 }
280
281 pub fn target(&self) -> &str {
282 &self.target
283 }
284
285 pub fn debug(&self, message: &str) {
286 self.log("DEBUG", message);
287 }
288
289 pub fn info(&self, message: &str) {
290 self.log("INFO", message);
291 }
292
293 pub fn warn(&self, message: &str) {
294 self.log("WARN", message);
295 }
296
297 pub fn error(&self, message: &str) {
298 self.log("ERROR", message);
299 }
300
301 pub fn log(&self, level: &str, message: &str) {
302 log_event(level, &self.target, message);
303 }
304
305 pub fn log_fields(&self, level: &str, message: &str, fields: &BTreeMap<String, Value>) {
307 log_event_with_fields(level, &self.target, message, fields);
308 }
309
310 pub fn debug_fields(&self, message: &str, fields: &BTreeMap<String, Value>) {
311 self.log_fields("DEBUG", message, fields);
312 }
313
314 pub fn info_fields(&self, message: &str, fields: &BTreeMap<String, Value>) {
315 self.log_fields("INFO", message, fields);
316 }
317
318 pub fn warn_fields(&self, message: &str, fields: &BTreeMap<String, Value>) {
319 self.log_fields("WARN", message, fields);
320 }
321
322 pub fn error_fields(&self, message: &str, fields: &BTreeMap<String, Value>) {
323 self.log_fields("ERROR", message, fields);
324 }
325
326 pub fn debug_event(&self, event: &crate::schema::Event) {
327 self.log_event("DEBUG", event);
328 }
329
330 pub fn info_event(&self, event: &crate::schema::Event) {
331 self.log_event("INFO", event);
332 }
333
334 pub fn warn_event(&self, event: &crate::schema::Event) {
335 self.log_event("WARN", event);
336 }
337
338 pub fn error_event(&self, event: &crate::schema::Event) {
339 self.log_event("ERROR", event);
340 }
341
342 pub fn log_event(&self, level: &str, event: &crate::schema::Event) {
343 log_event_with_event(level, &self.target, event);
344 }
345
346 pub fn drain_events_for_tests() -> Vec<LogEvent> {
347 std::mem::take(&mut *crate::_lock::lock(events()))
348 }
349}
350
351impl NullLogger {
352 pub fn new(target: Option<&str>) -> Self {
353 Self {
354 target: logger_target(target),
355 }
356 }
357
358 pub fn target(&self) -> &str {
359 &self.target
360 }
361
362 pub fn debug(&self, _message: &str) {}
363
364 pub fn info(&self, _message: &str) {}
365
366 pub fn warn(&self, _message: &str) {}
367
368 pub fn error(&self, _message: &str) {}
369}
370
371impl BufferLogger {
372 pub fn new(target: Option<&str>) -> Self {
373 Self {
374 target: logger_target(target),
375 events: Arc::new(Mutex::new(Vec::new())),
376 }
377 }
378
379 pub fn target(&self) -> &str {
380 &self.target
381 }
382
383 pub fn debug(&self, message: &str) {
384 self.log("DEBUG", message);
385 }
386
387 pub fn info(&self, message: &str) {
388 self.log("INFO", message);
389 }
390
391 pub fn warn(&self, message: &str) {
392 self.log("WARN", message);
393 }
394
395 pub fn error(&self, message: &str) {
396 self.log("ERROR", message);
397 }
398
399 pub fn log(&self, level: &str, message: &str) {
400 let config = active_logging_config();
401 if level_order(level) < effective_level_threshold(&self.target, &config) {
402 return;
403 }
404 let mut event = new_event(&self.target, level, message);
405 process_event(&mut event);
406 crate::_lock::lock(&self.events).push(event);
407 }
408
409 pub fn drain(&self) -> Vec<LogEvent> {
410 std::mem::take(&mut *crate::_lock::lock(&self.events))
411 }
412}
413
414pub fn get_logger(name: Option<&str>) -> Logger {
415 apply_lazy_logger_policies_from_env();
416 Logger::new(name)
417}
418
419pub fn null_logger(name: Option<&str>) -> NullLogger {
420 NullLogger::new(name)
421}
422
423pub fn buffer_logger(name: Option<&str>) -> BufferLogger {
424 BufferLogger::new(name)
425}
426
427impl log::Log for Logger {
428 fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
429 let config = active_logging_config();
430 let record_order: u8 = match metadata.level() {
431 log::Level::Error => 4,
432 log::Level::Warn => 3,
433 log::Level::Info => 2,
434 log::Level::Debug => 1,
435 log::Level::Trace => 0,
436 };
437 record_order >= effective_level_threshold(metadata.target(), &config)
438 }
439
440 fn log(&self, record: &log::Record<'_>) {
441 if !self.enabled(record.metadata()) {
442 return;
443 }
444 let level = match record.level() {
445 log::Level::Error => "ERROR",
446 log::Level::Warn => "WARN",
447 log::Level::Info => "INFO",
448 log::Level::Debug => "DEBUG",
449 log::Level::Trace => "TRACE",
450 };
451 log_event(level, record.target(), &record.args().to_string());
452 }
453
454 fn flush(&self) {}
455}
456
457pub fn set_as_global_logger() -> Result<(), log::SetLoggerError> {
459 match log::set_logger(&*logger) {
460 Ok(()) => {
461 log::set_max_level(log::LevelFilter::Trace);
462 Ok(())
463 }
464 Err(err) => Err(err),
465 }
466}
467
468#[cfg(test)]
469mod tests;
470
471#[cfg(test)]
472#[path = "log_trait_tests.rs"]
473mod log_trait_tests;