revoke_trace/
tracer.rs

1use crate::{
2    exporter::{ExporterConfig, TraceExporter},
3    sampler::TraceSampler,
4};
5use opentelemetry::trace::TracerProvider;
6use opentelemetry_sdk::trace::{SdkTracerProvider, TraceError};
7use tracing_subscriber::{EnvFilter, Registry, layer::SubscriberExt, util::SubscriberInitExt};
8
9/// 追踪器配置
10#[derive(Debug, Clone)]
11pub struct TracerConfig {
12    /// 服务名称
13    pub service_name: String,
14    /// 服务版本
15    pub service_version: String,
16    /// 部署环境
17    pub environment: String,
18    /// 采样器
19    pub sampler: TraceSampler,
20    /// 导出器配置
21    pub exporter_config: ExporterConfig,
22    /// 是否启用日志集成
23    pub enable_logging: bool,
24    /// 日志级别过滤器
25    pub log_filter: String,
26}
27
28impl Default for TracerConfig {
29    fn default() -> Self {
30        Self {
31            service_name: "revoke-service".to_string(),
32            service_version: env!("CARGO_PKG_VERSION").to_string(),
33            environment: "development".to_string(),
34            sampler: TraceSampler::always_on(),
35            exporter_config: ExporterConfig::default(),
36            enable_logging: true,
37            log_filter: "info".to_string(),
38        }
39    }
40}
41
42/// Revoke 追踪器
43pub struct RevokeTracer {
44    provider: SdkTracerProvider,
45    config: TracerConfig,
46}
47
48impl RevokeTracer {
49    /// 创建新的追踪器
50    pub async fn new(config: TracerConfig) -> Result<Self, TraceError> {
51        // 创建导出器
52        let exporter = {
53            #[cfg(feature = "otlp")]
54            {
55                TraceExporter::otlp(config.exporter_config.clone())
56            }
57            #[cfg(not(feature = "otlp"))]
58            {
59                TraceExporter::console()
60            }
61        };
62
63        // 构建追踪器
64        let _tracer = exporter.build(&config.service_name).await?;
65
66        // 创建资源
67        use opentelemetry::KeyValue;
68        use opentelemetry_sdk::Resource;
69
70        let resource = Resource::builder()
71            .with_service_name(config.service_name.clone())
72            .with_attribute(KeyValue::new(
73                "service.version",
74                config.service_version.clone(),
75            ))
76            .build();
77
78        // 创建 provider
79        let provider = SdkTracerProvider::builder().with_resource(resource).build();
80
81        Ok(Self { provider, config })
82    }
83
84    /// 安装为全局追踪器
85    pub fn install_global(self) -> Result<(), TraceError> {
86        // 设置全局 tracer provider
87        opentelemetry::global::set_tracer_provider(self.provider.clone());
88
89        // 如果启用了日志集成,设置 tracing subscriber
90        if self.config.enable_logging {
91            self.setup_tracing_subscriber()?;
92        }
93
94        Ok(())
95    }
96
97    /// 设置 tracing subscriber
98    fn setup_tracing_subscriber(&self) -> Result<(), TraceError> {
99        // 创建 OpenTelemetry 层
100        let tracer = self.provider.tracer("revoke-trace");
101        let otel_layer = tracing_opentelemetry::layer().with_tracer(tracer);
102
103        // 创建日志格式化层
104        let fmt_layer = tracing_subscriber::fmt::layer()
105            .with_target(true)
106            .with_thread_ids(true)
107            .with_level(true);
108
109        // 创建环境过滤器
110        let filter = EnvFilter::try_from_default_env()
111            .unwrap_or_else(|_| EnvFilter::new(&self.config.log_filter));
112
113        // 组合所有层
114        Registry::default()
115            .with(filter)
116            .with(fmt_layer)
117            .with(otel_layer)
118            .try_init()
119            .map_err(|e| TraceError::Other(Box::new(e)))?;
120
121        Ok(())
122    }
123
124    /// 获取追踪器实例
125    pub fn tracer(&self, name: &str) -> opentelemetry_sdk::trace::Tracer {
126        self.provider.tracer(name.to_string())
127    }
128
129    /// 强制刷新所有待处理的 spans
130    pub fn force_flush(&self) -> Result<(), TraceError> {
131        // 在新版本中 force_flush 不返回错误
132        let _ = self.provider.force_flush();
133        Ok(())
134    }
135
136    /// 关闭追踪器
137    pub fn shutdown(self) -> Result<(), TraceError> {
138        // 需要获取 TracerProvider 的所有权
139        drop(self.provider);
140        Ok(())
141    }
142}
143
144/// 追踪器构建器
145pub struct TracerBuilder {
146    config: TracerConfig,
147}
148
149impl TracerBuilder {
150    /// 创建新的构建器
151    pub fn new(service_name: impl Into<String>) -> Self {
152        let mut config = TracerConfig::default();
153        config.service_name = service_name.into();
154
155        Self { config }
156    }
157
158    /// 设置服务版本
159    pub fn with_version(mut self, version: impl Into<String>) -> Self {
160        self.config.service_version = version.into();
161        self
162    }
163
164    /// 设置环境
165    pub fn with_environment(mut self, env: impl Into<String>) -> Self {
166        self.config.environment = env.into();
167        self
168    }
169
170    /// 设置采样器
171    pub fn with_sampler(mut self, sampler: TraceSampler) -> Self {
172        self.config.sampler = sampler;
173        self
174    }
175
176    /// 设置导出器端点
177    pub fn with_endpoint(mut self, endpoint: impl Into<String>) -> Self {
178        self.config.exporter_config.endpoint = endpoint.into();
179        self
180    }
181
182    /// 启用/禁用日志集成
183    pub fn with_logging(mut self, enable: bool) -> Self {
184        self.config.enable_logging = enable;
185        self
186    }
187
188    /// 设置日志过滤器
189    pub fn with_log_filter(mut self, filter: impl Into<String>) -> Self {
190        self.config.log_filter = filter.into();
191        self
192    }
193
194    /// 构建追踪器
195    pub async fn build(self) -> Result<RevokeTracer, TraceError> {
196        RevokeTracer::new(self.config).await
197    }
198}
199
200/// 便捷的宏,用于创建 span
201#[macro_export]
202macro_rules! trace_span {
203    ($name:expr) => {
204        $crate::span::SpanBuilder::new($name).start()
205    };
206    ($name:expr, $($key:ident = $value:expr),*) => {
207        $crate::span::SpanBuilder::new($name)
208            $(.with_attribute(stringify!($key), $value))*
209            .start()
210    };
211}
212
213/// 便捷的宏,用于记录错误
214#[macro_export]
215macro_rules! trace_error {
216    ($span:expr, $error:expr) => {{
217        use $crate::span::SpanExt;
218        $span.record_exception(&$error);
219        $span.set_status($crate::span::SpanStatus::error($error.to_string()));
220    }};
221}
222
223#[cfg(test)]
224mod tests {
225    use super::*;
226
227    #[tokio::test]
228    async fn test_tracer_builder() {
229        let tracer = TracerBuilder::new("test-service")
230            .with_version("1.0.0")
231            .with_environment("test")
232            .with_sampler(TraceSampler::always_off())
233            .with_endpoint("http://localhost:4317")
234            .with_logging(false)
235            .build()
236            .await;
237
238        assert!(tracer.is_ok());
239
240        let tracer = tracer.unwrap();
241        assert_eq!(tracer.config.service_name, "test-service");
242        assert_eq!(tracer.config.service_version, "1.0.0");
243        assert_eq!(tracer.config.environment, "test");
244    }
245
246    #[test]
247    fn test_trace_span_macro() {
248        // 测试 span 创建
249        let _span = tracing::info_span!("test_operation");
250        // 测试带属性的 span
251        let _span_with_attrs =
252            tracing::info_span!("test_operation", method = "GET", path = "/api/test");
253        // 如果没有 panic,说明成功创建
254    }
255}