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(config: TraceConfig) -> anyhow::Result<TracingGuard> {
363 let path = config
364 .output_path
365 .unwrap_or_else(|| PathBuf::from("./logs/trace.json"));
366
367 if let Some(parent) = path.parent() {
369 std::fs::create_dir_all(parent)?;
370 }
371
372 let (chrome_layer, guard) = tracing_chrome::ChromeLayerBuilder::new()
374 .file(&path)
375 .include_args(true) .build();
377
378 let env_filter = EnvFilter::try_from_default_env()
380 .unwrap_or_else(|_| EnvFilter::new(config.max_level.as_str()));
381
382 let fmt_layer = fmt::layer()
383 .with_target(config.with_target)
384 .with_thread_ids(config.with_thread_ids)
385 .with_ansi(true)
386 .compact();
387
388 tracing_subscriber::registry()
389 .with(env_filter)
390 .with(fmt_layer)
391 .with(chrome_layer)
392 .init();
393
394 tracing::info!("🔍 开发追踪已启用(Chrome Tracing 模式)");
395 tracing::info!("📁 输出文件: {}", path.display());
396 tracing::info!("📊 日志级别: {}", config.max_level);
397 tracing::info!("🌐 查看方式: 在 Chrome 浏览器中访问 chrome://tracing 并加载文件");
398 tracing::info!("📦 包含信息: Span 时序、参数、线程 ID、进程 ID");
399 tracing::info!("⚠️ 重要: 请保持返回的 guard 直到程序结束,以确保数据被正确写入");
400
401 Ok(TracingGuard::Chrome(ChromeTracingGuard { _guard: guard }))
403 }
404
405 #[cfg(feature = "dev-tracing-perfetto")]
407 fn init_perfetto_tracing(config: TraceConfig) -> anyhow::Result<()> {
408 use std::fs::File;
409
410 let path = config
411 .output_path
412 .unwrap_or_else(|| PathBuf::from("./logs/trace.perfetto"));
413
414 if let Some(parent) = path.parent() {
416 std::fs::create_dir_all(parent)?;
417 }
418
419 let file = File::create(&path)?;
421 let perfetto_layer = tracing_perfetto::PerfettoLayer::new(file)
422 .with_debug_annotations(true);
423
424 let env_filter = EnvFilter::try_from_default_env()
426 .unwrap_or_else(|_| EnvFilter::new(config.max_level.as_str()));
427
428 let fmt_layer = fmt::layer()
429 .with_target(config.with_target)
430 .with_thread_ids(config.with_thread_ids)
431 .with_ansi(true)
432 .compact();
433
434 tracing_subscriber::registry()
435 .with(env_filter)
436 .with(fmt_layer)
437 .with(perfetto_layer)
438 .init();
439
440 log_system_info();
442
443 tracing::info!("🔍 开发追踪已启用(Perfetto 模式)");
444 tracing::info!("📁 输出文件: {}", path.display());
445 tracing::info!("📊 使用 https://ui.perfetto.dev/ 查看追踪数据");
446 tracing::info!("📊 日志级别: {}", config.max_level);
447 tracing::info!("💡 Perfetto 包含: span 时序、线程信息、系统资源");
448
449 Ok(())
450 }
451
452 #[cfg(feature = "dev-tracing-perfetto")]
454 fn log_system_info() {
455 use std::thread;
456
457 let process_id = std::process::id();
458 let thread_id = thread::current().id();
459 let thread_name =
460 thread::current().name().unwrap_or("main").to_string();
461
462 tracing::info!(
463 process_id = process_id,
464 thread_id = ?thread_id,
465 thread_name = %thread_name,
466 "系统信息"
467 );
468
469 let cpu_count = num_cpus::get();
471 tracing::info!(cpu_count = cpu_count, "CPU 信息");
472 }
473
474 pub fn init_default() -> anyhow::Result<TracingGuard> {
476 init_tracing(TraceConfig::default())
477 }
478}
479
480#[cfg(not(feature = "dev-tracing"))]
482pub mod dev_tracing {
483 use std::path::PathBuf;
484
485 pub struct TracingGuard;
487
488 #[derive(Debug, Clone)]
489 pub enum TraceFormat {
490 Console,
491 Json,
492 }
493
494 #[derive(Debug, Clone)]
495 pub struct TraceConfig {
496 pub format: TraceFormat,
497 pub output_path: Option<PathBuf>,
498 pub max_level: (),
499 pub with_target: bool,
500 pub with_thread_ids: bool,
501 pub with_file_line: bool,
502 }
503
504 impl Default for TraceConfig {
505 fn default() -> Self {
506 Self {
507 format: TraceFormat::Console,
508 output_path: None,
509 max_level: (),
510 with_target: false,
511 with_thread_ids: false,
512 with_file_line: false,
513 }
514 }
515 }
516
517 impl TraceConfig {
518 pub fn console() -> Self {
519 Self::default()
520 }
521 pub fn json(_path: impl Into<PathBuf>) -> Self {
522 Self::default()
523 }
524 pub fn with_max_level(
525 self,
526 _level: (),
527 ) -> Self {
528 self
529 }
530 }
531
532 pub fn init_tracing(_config: TraceConfig) -> anyhow::Result<TracingGuard> {
534 Ok(TracingGuard)
535 }
536
537 pub fn init_default() -> anyhow::Result<TracingGuard> {
538 Ok(TracingGuard)
539 }
540}
541
542
543#[cfg(feature = "dev-tracing")]
570#[macro_export]
571macro_rules! traced_span {
572 ($name:expr) => {{
573 let trace_id = $crate::tracing_init::dev_tracing::generate_trace_id();
574 tracing::info_span!($name, trace_id = trace_id)
575 }};
576 ($name:expr, $($field:tt)*) => {{
577 let trace_id = $crate::tracing_init::dev_tracing::generate_trace_id();
578 tracing::info_span!($name, trace_id = trace_id, $($field)*)
579 }};
580}
581
582#[cfg(not(feature = "dev-tracing"))]
583#[macro_export]
584macro_rules! traced_span {
585 ($name:expr) => {{}};
586 ($name:expr, $($field:tt)*) => {{}};
587}
588
589#[cfg(feature = "dev-tracing")]
618#[macro_export]
619macro_rules! trace_if_enabled {
620 ($method:expr) => {{
621 if $crate::tracing_init::dev_tracing::should_trace($method) {
622 Some(tracing::info_span!($method).entered())
623 } else {
624 None
625 }
626 }};
627 ($method:expr, $($field:tt)*) => {{
628 if $crate::tracing_init::dev_tracing::should_trace($method) {
629 Some(tracing::info_span!($method, $($field)*).entered())
630 } else {
631 None
632 }
633 }};
634}
635
636#[cfg(not(feature = "dev-tracing"))]
637#[macro_export]
638macro_rules! trace_if_enabled {
639 ($method:expr) => { None };
640 ($method:expr, $($field:tt)*) => { None };
641}