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}