shared_logging/
context.rs1use serde::{Deserialize, Serialize};
11use uuid::Uuid;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct Context {
16 pub trace_id: Option<String>,
18 pub span_id: Option<String>,
20 pub request_id: Option<String>,
22 pub user_id: Option<String>,
24 pub tenant_id: Option<String>,
26}
27
28impl Context {
29 pub fn new() -> Self {
31 Self {
32 trace_id: None,
33 span_id: None,
34 request_id: None,
35 user_id: None,
36 tenant_id: None,
37 }
38 }
39
40 pub fn generate_request_id() -> String {
42 Uuid::new_v4().to_string()
43 }
44
45 pub fn with_trace_id(mut self, trace_id: impl Into<String>) -> Self {
47 self.trace_id = Some(trace_id.into());
48 self
49 }
50
51 pub fn with_span_id(mut self, span_id: impl Into<String>) -> Self {
53 self.span_id = Some(span_id.into());
54 self
55 }
56
57 pub fn with_request_id(mut self, request_id: impl Into<String>) -> Self {
59 self.request_id = Some(request_id.into());
60 self
61 }
62
63 pub fn with_user_id(mut self, user_id: impl Into<String>) -> Self {
65 self.user_id = Some(user_id.into());
66 self
67 }
68
69 pub fn with_tenant_id(mut self, tenant_id: impl Into<String>) -> Self {
71 self.tenant_id = Some(tenant_id.into());
72 self
73 }
74
75 pub fn from_span() -> Self {
80 Self::new()
83 }
84
85 pub fn merge(mut self, other: Context) -> Self {
87 if other.trace_id.is_some() {
88 self.trace_id = other.trace_id;
89 }
90 if other.span_id.is_some() {
91 self.span_id = other.span_id;
92 }
93 if other.request_id.is_some() {
94 self.request_id = other.request_id;
95 }
96 if other.user_id.is_some() {
97 self.user_id = other.user_id;
98 }
99 if other.tenant_id.is_some() {
100 self.tenant_id = other.tenant_id;
101 }
102 self
103 }
104
105 pub fn to_fields(&self) -> Vec<(&'static str, String)> {
107 let mut fields = Vec::new();
108 if let Some(ref trace_id) = self.trace_id {
109 fields.push(("trace_id", trace_id.clone()));
110 }
111 if let Some(ref span_id) = self.span_id {
112 fields.push(("span_id", span_id.clone()));
113 }
114 if let Some(ref request_id) = self.request_id {
115 fields.push(("request_id", request_id.clone()));
116 }
117 if let Some(ref user_id) = self.user_id {
118 fields.push(("user_id", user_id.clone()));
119 }
120 if let Some(ref tenant_id) = self.tenant_id {
121 fields.push(("tenant_id", tenant_id.clone()));
122 }
123 fields
124 }
125}
126
127impl Default for Context {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133#[derive(Debug, Default)]
135pub struct ContextBuilder {
136 context: Context,
137}
138
139impl ContextBuilder {
140 pub fn new() -> Self {
142 Self {
143 context: Context::new(),
144 }
145 }
146
147 pub fn trace_id(mut self, trace_id: impl Into<String>) -> Self {
149 self.context.trace_id = Some(trace_id.into());
150 self
151 }
152
153 pub fn span_id(mut self, span_id: impl Into<String>) -> Self {
155 self.context.span_id = Some(span_id.into());
156 self
157 }
158
159 pub fn request_id(mut self, request_id: impl Into<String>) -> Self {
161 self.context.request_id = Some(request_id.into());
162 self
163 }
164
165 pub fn generate_request_id(mut self) -> Self {
167 self.context.request_id = Some(Context::generate_request_id());
168 self
169 }
170
171 pub fn user_id(mut self, user_id: impl Into<String>) -> Self {
173 self.context.user_id = Some(user_id.into());
174 self
175 }
176
177 pub fn tenant_id(mut self, tenant_id: impl Into<String>) -> Self {
179 self.context.tenant_id = Some(tenant_id.into());
180 self
181 }
182
183 pub fn build(self) -> Context {
185 self.context
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[test]
194 fn test_context_builder() {
195 let ctx = ContextBuilder::new()
196 .trace_id("trace123")
197 .request_id("req456")
198 .user_id("user789")
199 .build();
200
201 assert_eq!(ctx.trace_id, Some("trace123".to_string()));
202 assert_eq!(ctx.request_id, Some("req456".to_string()));
203 assert_eq!(ctx.user_id, Some("user789".to_string()));
204 }
205
206 #[test]
207 fn test_context_merge() {
208 let ctx1 = ContextBuilder::new()
209 .trace_id("trace1")
210 .request_id("req1")
211 .build();
212
213 let ctx2 = ContextBuilder::new()
214 .trace_id("trace2")
215 .user_id("user1")
216 .build();
217
218 let merged = ctx1.merge(ctx2);
219 assert_eq!(merged.trace_id, Some("trace2".to_string()));
220 assert_eq!(merged.request_id, Some("req1".to_string()));
221 assert_eq!(merged.user_id, Some("user1".to_string()));
222 }
223}
224