1#[cfg(feature = "dev-console")]
11pub mod tokio_console {
12 pub fn init() -> anyhow::Result<()> {
60 console_subscriber::init();
61 tracing::info!("🔍 tokio-console 已启动");
62 tracing::info!("📡 监听地址: 127.0.0.1:6669");
63 tracing::info!("💡 运行 'tokio-console' 命令连接到监控界面");
64 tracing::info!("📚 文档: https://docs.rs/tokio-console");
65 Ok(())
66 }
67
68 pub fn init_with_config(server_addr: &str) -> anyhow::Result<()> {
83 let builder = console_subscriber::ConsoleLayer::builder()
84 .server_addr(server_addr.parse()?);
85
86 builder.init();
87
88 tracing::info!("🔍 tokio-console 已启动(自定义配置)");
89 tracing::info!("📡 监听地址: {}", server_addr);
90 tracing::info!("💡 运行 'tokio-console' 命令连接到监控界面");
91 Ok(())
92 }
93}
94
95#[cfg(feature = "dev-tracing")]
100pub mod dev_tracing {
101 use tracing_subscriber::{
102 fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter,
103 };
104 use std::path::PathBuf;
105 use std::sync::atomic::{AtomicU64, Ordering};
106
107 static TRACE_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
109
110 pub fn generate_trace_id() -> u64 {
114 TRACE_ID_COUNTER.fetch_add(1, Ordering::SeqCst)
115 }
116
117 pub fn should_trace(method_name: &str) -> bool {
127 if let Ok(trace_methods) = std::env::var("TRACE_METHODS") {
128 if trace_methods == "*" {
129 return true; }
131 trace_methods.split(',').any(|m| m.trim() == method_name)
132 } else {
133 true }
135 }
136
137 pub fn should_trace_tr_id(tr_id: &str) -> bool {
147 if let Ok(target_tr_id) = std::env::var("TRACE_TR_ID") {
148 tr_id.starts_with(&target_tr_id)
149 } else {
150 true }
152 }
153
154 #[derive(Debug, Clone)]
156 pub enum TraceFormat {
157 Console,
159 Json,
161 #[cfg(feature = "dev-tracing-chrome")]
163 Chrome,
164 #[cfg(feature = "dev-tracing-perfetto")]
166 Perfetto,
167 }
168
169 #[cfg(feature = "dev-tracing-chrome")]
171 pub struct ChromeTracingGuard {
172 _guard: tracing_chrome::FlushGuard,
173 }
174
175 #[cfg(feature = "dev-tracing-chrome")]
176 impl Drop for ChromeTracingGuard {
177 fn drop(&mut self) {
178 tracing::info!("🔄 正在刷新 Chrome Tracing 数据...");
179 }
180 }
181
182 #[derive(Debug, Clone)]
184 pub struct TraceConfig {
185 pub format: TraceFormat,
187 pub output_path: Option<PathBuf>,
189 pub max_level: tracing::Level,
191 pub with_target: bool,
193 pub with_thread_ids: bool,
195 pub with_file_line: bool,
197 }
198
199 impl Default for TraceConfig {
200 fn default() -> Self {
201 Self {
202 format: TraceFormat::Console,
203 output_path: None,
204 max_level: tracing::Level::DEBUG,
205 with_target: true,
206 with_thread_ids: true,
207 with_file_line: true,
208 }
209 }
210 }
211
212 impl TraceConfig {
213 pub fn console() -> Self {
215 Self { format: TraceFormat::Console, ..Default::default() }
216 }
217
218 pub fn json(output_path: impl Into<PathBuf>) -> Self {
220 Self {
221 format: TraceFormat::Json,
222 output_path: Some(output_path.into()),
223 ..Default::default()
224 }
225 }
226
227 #[cfg(feature = "dev-tracing-chrome")]
229 pub fn chrome(output_path: impl Into<PathBuf>) -> Self {
230 Self {
231 format: TraceFormat::Chrome,
232 output_path: Some(output_path.into()),
233 ..Default::default()
234 }
235 }
236
237 #[cfg(feature = "dev-tracing-perfetto")]
239 pub fn perfetto(output_path: impl Into<PathBuf>) -> Self {
240 Self {
241 format: TraceFormat::Perfetto,
242 output_path: Some(output_path.into()),
243 ..Default::default()
244 }
245 }
246
247 pub fn with_max_level(
249 mut self,
250 level: tracing::Level,
251 ) -> Self {
252 self.max_level = level;
253 self
254 }
255 }
256
257 pub enum TracingGuard {
259 None,
261 #[cfg(feature = "dev-tracing-chrome")]
263 Chrome(ChromeTracingGuard),
264 }
265
266 pub fn init_tracing(config: TraceConfig) -> anyhow::Result<TracingGuard> {
285 match config.format {
286 TraceFormat::Console => {
287 init_console_tracing(config)?;
288 Ok(TracingGuard::None)
289 },
290 TraceFormat::Json => {
291 init_json_tracing(config)?;
292 Ok(TracingGuard::None)
293 },
294 #[cfg(feature = "dev-tracing-chrome")]
295 TraceFormat::Chrome => init_chrome_tracing(config),
296 #[cfg(feature = "dev-tracing-perfetto")]
297 TraceFormat::Perfetto => {
298 init_perfetto_tracing(config)?;
299 Ok(TracingGuard::None)
300 },
301 }
302 }
303
304 fn init_console_tracing(config: TraceConfig) -> anyhow::Result<()> {
306 use tracing_subscriber::fmt::time::ChronoLocal;
307
308 let env_filter = EnvFilter::try_from_default_env()
309 .unwrap_or_else(|_| EnvFilter::new(config.max_level.as_str()));
310
311 let fmt_layer = fmt::layer()
312 .with_target(config.with_target)
313 .with_thread_ids(config.with_thread_ids)
314 .with_file(config.with_file_line)
315 .with_line_number(config.with_file_line)
316 .with_ansi(true)
317 .with_timer(ChronoLocal::new("%H:%M:%S%.3f".to_string()))
318 .with_span_events(fmt::format::FmtSpan::CLOSE) .pretty();
320
321 tracing_subscriber::registry().with(env_filter).with(fmt_layer).init();
322
323 tracing::info!("🔍 开发追踪已启用(控制台模式)");
324 tracing::info!("📊 日志级别: {}", config.max_level);
325 tracing::info!("⏱️ 显示 span 执行时间");
326 Ok(())
327 }
328
329 fn init_json_tracing(config: TraceConfig) -> anyhow::Result<()> {
331 let path = config
332 .output_path
333 .unwrap_or_else(|| PathBuf::from("./logs/trace.json"));
334
335 if let Some(parent) = path.parent() {
337 std::fs::create_dir_all(parent)?;
338 }
339
340 let file = std::fs::File::create(&path)?;
341
342 let env_filter = EnvFilter::new(config.max_level.as_str());
343
344 let fmt_layer = fmt::layer()
345 .json()
346 .with_writer(file)
347 .with_target(config.with_target)
348 .with_thread_ids(config.with_thread_ids)
349 .with_file(config.with_file_line)
350 .with_line_number(config.with_file_line);
351
352 tracing_subscriber::registry().with(env_filter).with(fmt_layer).init();
353
354 tracing::info!("🔍 开发追踪已启用(JSON 模式)");
355 tracing::info!("📁 输出文件: {}", path.display());
356 tracing::info!("📊 日志级别: {}", config.max_level);
357 Ok(())
358 }
359
360 #[cfg(feature = "dev-tracing-chrome")]
362 fn init_chrome_tracing(
363 config: TraceConfig
364 ) -> anyhow::Result<TracingGuard> {
365 let path = config
366 .output_path
367 .unwrap_or_else(|| PathBuf::from("./logs/trace.json"));
368
369 if let Some(parent) = path.parent() {
371 std::fs::create_dir_all(parent)?;
372 }
373
374 let (chrome_layer, guard) = tracing_chrome::ChromeLayerBuilder::new()
376 .file(&path)
377 .include_args(true) .build();
379
380 let env_filter = EnvFilter::try_from_default_env()
382 .unwrap_or_else(|_| EnvFilter::new(config.max_level.as_str()));
383
384 let fmt_layer = fmt::layer()
385 .with_target(config.with_target)
386 .with_thread_ids(config.with_thread_ids)
387 .with_ansi(true)
388 .compact();
389
390 tracing_subscriber::registry()
391 .with(env_filter)
392 .with(fmt_layer)
393 .with(chrome_layer)
394 .init();
395
396 tracing::info!("🔍 开发追踪已启用(Chrome Tracing 模式)");
397 tracing::info!("📁 输出文件: {}", path.display());
398 tracing::info!("📊 日志级别: {}", config.max_level);
399 tracing::info!(
400 "🌐 查看方式: 在 Chrome 浏览器中访问 chrome://tracing 并加载文件"
401 );
402 tracing::info!("📦 包含信息: Span 时序、参数、线程 ID、进程 ID");
403 tracing::info!(
404 "⚠️ 重要: 请保持返回的 guard 直到程序结束,以确保数据被正确写入"
405 );
406
407 Ok(TracingGuard::Chrome(ChromeTracingGuard { _guard: guard }))
409 }
410
411 #[cfg(feature = "dev-tracing-perfetto")]
413 fn init_perfetto_tracing(config: TraceConfig) -> anyhow::Result<()> {
414 use std::fs::File;
415
416 let path = config
417 .output_path
418 .unwrap_or_else(|| PathBuf::from("./logs/trace.perfetto"));
419
420 if let Some(parent) = path.parent() {
422 std::fs::create_dir_all(parent)?;
423 }
424
425 let file = File::create(&path)?;
427 let perfetto_layer = tracing_perfetto::PerfettoLayer::new(file)
428 .with_debug_annotations(true);
429
430 let env_filter = EnvFilter::try_from_default_env()
432 .unwrap_or_else(|_| EnvFilter::new(config.max_level.as_str()));
433
434 let fmt_layer = fmt::layer()
435 .with_target(config.with_target)
436 .with_thread_ids(config.with_thread_ids)
437 .with_ansi(true)
438 .compact();
439
440 tracing_subscriber::registry()
441 .with(env_filter)
442 .with(fmt_layer)
443 .with(perfetto_layer)
444 .init();
445
446 log_system_info();
448
449 tracing::info!("🔍 开发追踪已启用(Perfetto 模式)");
450 tracing::info!("📁 输出文件: {}", path.display());
451 tracing::info!("📊 使用 https://ui.perfetto.dev/ 查看追踪数据");
452 tracing::info!("📊 日志级别: {}", config.max_level);
453 tracing::info!("💡 Perfetto 包含: span 时序、线程信息、系统资源");
454
455 Ok(())
456 }
457
458 #[cfg(feature = "dev-tracing-perfetto")]
460 fn log_system_info() {
461 use std::thread;
462
463 let process_id = std::process::id();
464 let thread_id = thread::current().id();
465 let thread_name =
466 thread::current().name().unwrap_or("main").to_string();
467
468 tracing::info!(
469 process_id = process_id,
470 thread_id = ?thread_id,
471 thread_name = %thread_name,
472 "系统信息"
473 );
474
475 let cpu_count = num_cpus::get();
477 tracing::info!(cpu_count = cpu_count, "CPU 信息");
478 }
479
480 pub fn init_default() -> anyhow::Result<TracingGuard> {
482 init_tracing(TraceConfig::default())
483 }
484}
485
486#[cfg(not(feature = "dev-tracing"))]
488pub mod dev_tracing {
489 use std::path::PathBuf;
490
491 pub struct TracingGuard;
493
494 #[derive(Debug, Clone)]
495 pub enum TraceFormat {
496 Console,
497 Json,
498 }
499
500 #[derive(Debug, Clone)]
501 pub struct TraceConfig {
502 pub format: TraceFormat,
503 pub output_path: Option<PathBuf>,
504 pub max_level: (),
505 pub with_target: bool,
506 pub with_thread_ids: bool,
507 pub with_file_line: bool,
508 }
509
510 impl Default for TraceConfig {
511 fn default() -> Self {
512 Self {
513 format: TraceFormat::Console,
514 output_path: None,
515 max_level: (),
516 with_target: false,
517 with_thread_ids: false,
518 with_file_line: false,
519 }
520 }
521 }
522
523 impl TraceConfig {
524 pub fn console() -> Self {
525 Self::default()
526 }
527 pub fn json(_path: impl Into<PathBuf>) -> Self {
528 Self::default()
529 }
530 pub fn with_max_level(
531 self,
532 _level: (),
533 ) -> Self {
534 self
535 }
536 }
537
538 pub fn init_tracing(_config: TraceConfig) -> anyhow::Result<TracingGuard> {
540 Ok(TracingGuard)
541 }
542
543 pub fn init_default() -> anyhow::Result<TracingGuard> {
544 Ok(TracingGuard)
545 }
546}
547
548#[cfg(feature = "dev-tracing")]
575#[macro_export]
576macro_rules! traced_span {
577 ($name:expr) => {{
578 let trace_id = $crate::tracing_init::dev_tracing::generate_trace_id();
579 tracing::info_span!($name, trace_id = trace_id)
580 }};
581 ($name:expr, $($field:tt)*) => {{
582 let trace_id = $crate::tracing_init::dev_tracing::generate_trace_id();
583 tracing::info_span!($name, trace_id = trace_id, $($field)*)
584 }};
585}
586
587#[cfg(not(feature = "dev-tracing"))]
588#[macro_export]
589macro_rules! traced_span {
590 ($name:expr) => {{}};
591 ($name:expr, $($field:tt)*) => {{}};
592}
593
594#[cfg(feature = "dev-tracing")]
623#[macro_export]
624macro_rules! trace_if_enabled {
625 ($method:expr) => {{
626 if $crate::tracing_init::dev_tracing::should_trace($method) {
627 Some(tracing::info_span!($method).entered())
628 } else {
629 None
630 }
631 }};
632 ($method:expr, $($field:tt)*) => {{
633 if $crate::tracing_init::dev_tracing::should_trace($method) {
634 Some(tracing::info_span!($method, $($field)*).entered())
635 } else {
636 None
637 }
638 }};
639}
640
641#[cfg(not(feature = "dev-tracing"))]
642#[macro_export]
643macro_rules! trace_if_enabled {
644 ($method:expr) => {
645 None
646 };
647 ($method:expr, $($field:tt)*) => {
648 None
649 };
650}