1pub mod context;
7pub mod exporter;
8pub mod tracer;
9
10pub use context::{
11 extract_from_axum_headers, extract_trace_context, inject_into_axum_headers,
12 inject_trace_context, TraceContext,
13};
14pub use exporter::{
15 ExporterError, ExporterType, JaegerExporter, OtlpCompression, OtlpExporter, OtlpProtocol,
16};
17pub use tracer::{init_tracer, shutdown_tracer, TracingConfig};
18
19use opentelemetry::global::BoxedSpan;
20use opentelemetry::trace::{Span, SpanKind, Status, Tracer};
21use opentelemetry::{global, KeyValue};
22use std::time::SystemTime;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum Protocol {
27 Http,
28 Grpc,
29 WebSocket,
30 GraphQL,
31}
32
33impl Protocol {
34 pub fn as_str(&self) -> &'static str {
35 match self {
36 Protocol::Http => "http",
37 Protocol::Grpc => "grpc",
38 Protocol::WebSocket => "websocket",
39 Protocol::GraphQL => "graphql",
40 }
41 }
42}
43
44pub fn create_request_span(
46 protocol: Protocol,
47 operation_name: &str,
48 attributes: Vec<KeyValue>,
49) -> BoxedSpan {
50 let tracer = global::tracer("mockforge");
51
52 let mut span = tracer
53 .span_builder(operation_name.to_string())
54 .with_kind(SpanKind::Server)
55 .with_start_time(SystemTime::now())
56 .with_attributes(attributes)
57 .start(&tracer);
58
59 span.set_attribute(KeyValue::new("mockforge.protocol", protocol.as_str()));
61
62 span
63}
64
65pub fn record_success(span: &mut BoxedSpan, attributes: Vec<KeyValue>) {
67 for attr in attributes {
68 span.set_attribute(attr);
69 }
70 span.set_status(Status::Ok);
71}
72
73pub fn record_error(span: &mut BoxedSpan, error_message: &str) {
75 span.set_status(Status::error(error_message.to_string()));
76 span.set_attribute(KeyValue::new("error", true));
77 span.set_attribute(KeyValue::new("error.message", error_message.to_string()));
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn test_protocol_as_str() {
86 assert_eq!(Protocol::Http.as_str(), "http");
87 assert_eq!(Protocol::Grpc.as_str(), "grpc");
88 assert_eq!(Protocol::WebSocket.as_str(), "websocket");
89 assert_eq!(Protocol::GraphQL.as_str(), "graphql");
90 }
91
92 #[test]
93 fn test_protocol_debug() {
94 assert_eq!(format!("{:?}", Protocol::Http), "Http");
95 assert_eq!(format!("{:?}", Protocol::Grpc), "Grpc");
96 assert_eq!(format!("{:?}", Protocol::WebSocket), "WebSocket");
97 assert_eq!(format!("{:?}", Protocol::GraphQL), "GraphQL");
98 }
99
100 #[test]
101 fn test_protocol_clone() {
102 let proto = Protocol::Http;
103 let cloned = Clone::clone(&proto);
104 assert_eq!(proto, cloned);
105 }
106
107 #[test]
108 fn test_protocol_copy() {
109 let proto = Protocol::Grpc;
110 let copied = proto; assert_eq!(proto, copied);
112 assert_eq!(Protocol::Grpc, copied); }
114
115 #[test]
116 fn test_protocol_eq() {
117 assert_eq!(Protocol::Http, Protocol::Http);
118 assert_ne!(Protocol::Http, Protocol::Grpc);
119 assert_ne!(Protocol::WebSocket, Protocol::GraphQL);
120 }
121
122 #[test]
123 fn test_create_request_span() {
124 use opentelemetry_sdk::trace::TracerProvider as SdkTracerProvider;
126
127 let provider = SdkTracerProvider::builder().build();
128 global::set_tracer_provider(provider);
129
130 let attributes = vec![
131 KeyValue::new("http.method", "GET"),
132 KeyValue::new("http.url", "/api/users"),
133 ];
134
135 let span = create_request_span(Protocol::Http, "test-operation", attributes);
136
137 assert!(!span.span_context().trace_id().to_string().is_empty());
139 }
140
141 #[test]
142 fn test_create_request_span_all_protocols() {
143 use opentelemetry_sdk::trace::TracerProvider as SdkTracerProvider;
144
145 let provider = SdkTracerProvider::builder().build();
146 global::set_tracer_provider(provider);
147
148 let protocols = [
149 Protocol::Http,
150 Protocol::Grpc,
151 Protocol::WebSocket,
152 Protocol::GraphQL,
153 ];
154
155 for protocol in protocols {
156 let span = create_request_span(protocol, "test-op", vec![]);
157 assert!(!span.span_context().trace_id().to_string().is_empty());
158 }
159 }
160
161 #[test]
162 fn test_record_success() {
163 use opentelemetry_sdk::trace::TracerProvider as SdkTracerProvider;
164
165 let provider = SdkTracerProvider::builder().build();
166 global::set_tracer_provider(provider);
167
168 let mut span = create_request_span(Protocol::Http, "success-test", vec![]);
169
170 let attributes = vec![
171 KeyValue::new("http.status_code", 200),
172 KeyValue::new("response.size", 1024),
173 ];
174
175 record_success(&mut span, attributes);
176 }
178
179 #[test]
180 fn test_record_success_empty_attributes() {
181 use opentelemetry_sdk::trace::TracerProvider as SdkTracerProvider;
182
183 let provider = SdkTracerProvider::builder().build();
184 global::set_tracer_provider(provider);
185
186 let mut span = create_request_span(Protocol::Grpc, "success-empty", vec![]);
187 record_success(&mut span, vec![]);
188 }
190
191 #[test]
192 fn test_record_error() {
193 use opentelemetry_sdk::trace::TracerProvider as SdkTracerProvider;
194
195 let provider = SdkTracerProvider::builder().build();
196 global::set_tracer_provider(provider);
197
198 let mut span = create_request_span(Protocol::Http, "error-test", vec![]);
199
200 record_error(&mut span, "Connection refused");
201 }
203
204 #[test]
205 fn test_record_error_with_details() {
206 use opentelemetry_sdk::trace::TracerProvider as SdkTracerProvider;
207
208 let provider = SdkTracerProvider::builder().build();
209 global::set_tracer_provider(provider);
210
211 let mut span = create_request_span(Protocol::WebSocket, "error-details", vec![]);
212
213 record_error(&mut span, "WebSocket handshake failed: 401 Unauthorized");
214 }
216
217 #[test]
218 fn test_record_error_empty_message() {
219 use opentelemetry_sdk::trace::TracerProvider as SdkTracerProvider;
220
221 let provider = SdkTracerProvider::builder().build();
222 global::set_tracer_provider(provider);
223
224 let mut span = create_request_span(Protocol::GraphQL, "error-empty", vec![]);
225 record_error(&mut span, "");
226 }
228}