dmsc/log/
context.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of DMSC.
4//! The DMSC project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18#![allow(non_snake_case)]
19
20//! # Log Context
21//! 
22//! This module provides a thread-local logging context for DMSC, similar to MDC (Mapped Diagnostic Context)
23//! with built-in support for distributed tracing. It allows adding contextual information to logs
24//! that will be automatically included in all log messages from the same thread.
25//! 
26//! ## Key Features
27//! 
28//! - **Thread-Local Storage**: Context is stored per-thread, ensuring thread safety
29//! - **Distributed Tracing**: Built-in support for trace IDs, span IDs, and parent span IDs
30//! - **Flexible Context**: Can store arbitrary key-value pairs
31//! - **Easy API**: Simple methods for putting, getting, and removing context values
32//! 
33//! ## Design Principles
34//! 
35//! 1. **Thread Safety**: Uses thread-local storage to ensure thread safety
36//! 2. **Performance**: Efficient access to context values with minimal overhead
37//! 3. **Distributed Tracing Integration**: Built-in support for W3C Trace Context standard
38//! 4. **Flexibility**: Can be extended to support additional context types
39//! 5. **Simplicity**: Easy-to-use API for adding and removing context values
40//! 
41//! ## Usage
42//! 
43//! ```rust
44//! use dmsc::prelude::*;
45//! 
46//! fn example() {
47//!     // Set a context value
48//!     DMSCLogContext::put("user_id", "12345");
49//!     
50//!     // Set distributed tracing context
51//!     DMSCLogContext::set_trace_id(DMSCLogContext::generate_trace_id());
52//!     DMSCLogContext::set_span_id(DMSCLogContext::generate_span_id());
53//!     
54//!     // Get a context value
55//!     if let Some(user_id) = DMSCLogContext::get("user_id") {
56//!         println!("User ID: {}", user_id);
57//!     }
58//!     
59//!     // Get all context values
60//!     let all_ctx = DMSCLogContext::get_all();
61//!     println!("All context: {:?}", all_ctx);
62//!     
63//!     // Remove a context value
64//!     DMSCLogContext::remove("user_id");
65//!     
66//!     // Clear all context values
67//!     DMSCLogContext::clear();
68//! }
69//! ```
70
71// Logging context for DMSC, similar to MDC with distributed tracing support.
72
73use std::cell::RefCell;
74use std::collections::HashMap;
75use uuid::Uuid;
76
77// Thread-local logging context storage.
78// 
79// This thread-local variable stores the logging context for each thread, allowing
80// contextual information to be added to logs without passing it explicitly through
81// all function calls.
82thread_local! {
83    static LOGONTEXT: RefCell<HashMap<String, String>> = RefCell::new(HashMap::new());
84}
85
86/// Log context for DMSC, similar to MDC with distributed tracing support.
87/// 
88/// This struct provides a thread-local logging context that can be used to add
89/// contextual information to logs. It includes built-in support for distributed tracing
90/// with trace IDs, span IDs, and parent span IDs.
91pub struct DMSCLogContext;
92
93impl DMSCLogContext {
94    /// Puts a key-value pair into the log context.
95    /// 
96    /// # Parameters
97    /// 
98    /// - `key`: The context key
99    /// - `value`: The context value
100    pub fn put(key: impl Into<String>, value: impl Into<String>) {
101        let k = key.into();
102        let v = value.into();
103        LOGONTEXT.with(|ctx| {
104            ctx.borrow_mut().insert(k, v);
105        });
106    }
107
108    /// Puts multiple key-value pairs into the log context.
109    /// 
110    /// # Parameters
111    /// 
112    /// - `values`: A HashMap of key-value pairs to add to the context
113    pub fn put_all(values: HashMap<String, String>) {
114        LOGONTEXT.with(|ctx| {
115            let mut ctx = ctx.borrow_mut();
116            for (k, v) in values {
117                ctx.insert(k, v);
118            }
119        });
120    }
121
122    /// Gets a value from the log context.
123    /// 
124    /// # Parameters
125    /// 
126    /// - `key`: The context key to look up
127    /// 
128    /// # Returns
129    /// 
130    /// An `Option<String>` containing the value if it exists
131    pub fn get(key: &str) -> Option<String> {
132        LOGONTEXT.with(|ctx| ctx.borrow().get(key).cloned())
133    }
134
135    /// Removes a key-value pair from the log context.
136    /// 
137    /// # Parameters
138    /// 
139    /// - `key`: The context key to remove
140    pub fn remove(key: &str) {
141        LOGONTEXT.with(|ctx| {
142            ctx.borrow_mut().remove(key);
143        });
144    }
145
146    /// Gets all key-value pairs from the log context.
147    /// 
148    /// # Returns
149    /// 
150    /// A HashMap containing all key-value pairs in the context
151    pub fn get_all() -> HashMap<String, String> {
152        LOGONTEXT.with(|ctx| ctx.borrow().clone())
153    }
154
155    /// Clears all key-value pairs from the log context.
156    pub fn clear() {
157        LOGONTEXT.with(|ctx| ctx.borrow_mut().clear());
158    }
159
160    /// Sets the trace ID in the log context.
161    /// 
162    /// # Parameters
163    /// 
164    /// - `trace_id`: The trace ID to set
165    pub fn set_trace_id(trace_id: impl Into<String>) {
166        Self::put("trace_id", trace_id);
167    }
168
169    /// Gets the trace ID from the log context.
170    /// 
171    /// # Returns
172    /// 
173    /// An `Option<String>` containing the trace ID if it exists
174    pub fn get_trace_id() -> Option<String> {
175        Self::get("trace_id")
176    }
177
178    /// Generates a new trace ID.
179    /// 
180    /// # Returns
181    /// 
182    /// A new UUID string suitable for use as a trace ID
183    pub fn generate_trace_id() -> String {
184        Uuid::new_v4().to_string()
185    }
186
187    /// Sets the span ID in the log context.
188    /// 
189    /// # Parameters
190    /// 
191    /// - `span_id`: The span ID to set
192    pub fn set_span_id(span_id: impl Into<String>) {
193        Self::put("span_id", span_id);
194    }
195
196    /// Gets the span ID from the log context.
197    /// 
198    /// # Returns
199    /// 
200    /// An `Option<String>` containing the span ID if it exists
201    pub fn get_span_id() -> Option<String> {
202        Self::get("span_id")
203    }
204
205    /// Generates a new span ID.
206    /// 
207    /// # Returns
208    /// 
209    /// A new UUID string suitable for use as a span ID
210    pub fn generate_span_id() -> String {
211        Uuid::new_v4().to_string()
212    }
213
214    /// Sets the parent span ID in the log context.
215    /// 
216    /// # Parameters
217    /// 
218    /// - `parent_span_id`: The parent span ID to set
219    pub fn set_parent_span_id(parent_span_id: impl Into<String>) {
220        Self::put("parent_span_id", parent_span_id);
221    }
222
223    /// Gets the parent span ID from the log context.
224    /// 
225    /// # Returns
226    /// 
227    /// An `Option<String>` containing the parent span ID if it exists
228    pub fn get_parent_span_id() -> Option<String> {
229        Self::get("parent_span_id")
230    }
231}