#[cfg(feature = "dev-console")]
pub mod tokio_console {
pub fn init() -> anyhow::Result<()> {
console_subscriber::init();
tracing::info!("🔍 tokio-console 已启动");
tracing::info!("📡 监听地址: 127.0.0.1:6669");
tracing::info!("💡 运行 'tokio-console' 命令连接到监控界面");
tracing::info!("📚 文档: https://docs.rs/tokio-console");
Ok(())
}
pub fn init_with_config(server_addr: &str) -> anyhow::Result<()> {
let builder = console_subscriber::ConsoleLayer::builder()
.server_addr(server_addr.parse()?);
builder.init();
tracing::info!("🔍 tokio-console 已启动(自定义配置)");
tracing::info!("📡 监听地址: {}", server_addr);
tracing::info!("💡 运行 'tokio-console' 命令连接到监控界面");
Ok(())
}
}
#[cfg(feature = "dev-tracing")]
pub mod dev_tracing {
use tracing_subscriber::{
fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter,
};
use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, Ordering};
static TRACE_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
pub fn generate_trace_id() -> u64 {
TRACE_ID_COUNTER.fetch_add(1, Ordering::SeqCst)
}
pub fn should_trace(method_name: &str) -> bool {
if let Ok(trace_methods) = std::env::var("TRACE_METHODS") {
if trace_methods == "*" {
return true; }
trace_methods.split(',').any(|m| m.trim() == method_name)
} else {
true }
}
pub fn should_trace_tr_id(tr_id: &str) -> bool {
if let Ok(target_tr_id) = std::env::var("TRACE_TR_ID") {
tr_id.starts_with(&target_tr_id)
} else {
true }
}
#[derive(Debug, Clone)]
pub enum TraceFormat {
Console,
Json,
#[cfg(feature = "dev-tracing-chrome")]
Chrome,
#[cfg(feature = "dev-tracing-perfetto")]
Perfetto,
}
#[cfg(feature = "dev-tracing-chrome")]
pub struct ChromeTracingGuard {
_guard: tracing_chrome::FlushGuard,
}
#[cfg(feature = "dev-tracing-chrome")]
impl Drop for ChromeTracingGuard {
fn drop(&mut self) {
tracing::info!("🔄 正在刷新 Chrome Tracing 数据...");
}
}
#[derive(Debug, Clone)]
pub struct TraceConfig {
pub format: TraceFormat,
pub output_path: Option<PathBuf>,
pub max_level: tracing::Level,
pub with_target: bool,
pub with_thread_ids: bool,
pub with_file_line: bool,
}
impl Default for TraceConfig {
fn default() -> Self {
Self {
format: TraceFormat::Console,
output_path: None,
max_level: tracing::Level::DEBUG,
with_target: true,
with_thread_ids: true,
with_file_line: true,
}
}
}
impl TraceConfig {
pub fn console() -> Self {
Self { format: TraceFormat::Console, ..Default::default() }
}
pub fn json(output_path: impl Into<PathBuf>) -> Self {
Self {
format: TraceFormat::Json,
output_path: Some(output_path.into()),
..Default::default()
}
}
#[cfg(feature = "dev-tracing-chrome")]
pub fn chrome(output_path: impl Into<PathBuf>) -> Self {
Self {
format: TraceFormat::Chrome,
output_path: Some(output_path.into()),
..Default::default()
}
}
#[cfg(feature = "dev-tracing-perfetto")]
pub fn perfetto(output_path: impl Into<PathBuf>) -> Self {
Self {
format: TraceFormat::Perfetto,
output_path: Some(output_path.into()),
..Default::default()
}
}
pub fn with_max_level(
mut self,
level: tracing::Level,
) -> Self {
self.max_level = level;
self
}
}
pub enum TracingGuard {
None,
#[cfg(feature = "dev-tracing-chrome")]
Chrome(ChromeTracingGuard),
}
pub fn init_tracing(config: TraceConfig) -> anyhow::Result<TracingGuard> {
match config.format {
TraceFormat::Console => {
init_console_tracing(config)?;
Ok(TracingGuard::None)
},
TraceFormat::Json => {
init_json_tracing(config)?;
Ok(TracingGuard::None)
},
#[cfg(feature = "dev-tracing-chrome")]
TraceFormat::Chrome => init_chrome_tracing(config),
#[cfg(feature = "dev-tracing-perfetto")]
TraceFormat::Perfetto => {
init_perfetto_tracing(config)?;
Ok(TracingGuard::None)
},
}
}
fn init_console_tracing(config: TraceConfig) -> anyhow::Result<()> {
use tracing_subscriber::fmt::time::ChronoLocal;
let env_filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(config.max_level.as_str()));
let fmt_layer = fmt::layer()
.with_target(config.with_target)
.with_thread_ids(config.with_thread_ids)
.with_file(config.with_file_line)
.with_line_number(config.with_file_line)
.with_ansi(true)
.with_timer(ChronoLocal::new("%H:%M:%S%.3f".to_string()))
.with_span_events(fmt::format::FmtSpan::CLOSE) .pretty();
tracing_subscriber::registry().with(env_filter).with(fmt_layer).init();
tracing::info!("🔍 开发追踪已启用(控制台模式)");
tracing::info!("📊 日志级别: {}", config.max_level);
tracing::info!("⏱️ 显示 span 执行时间");
Ok(())
}
fn init_json_tracing(config: TraceConfig) -> anyhow::Result<()> {
let path = config
.output_path
.unwrap_or_else(|| PathBuf::from("./logs/trace.json"));
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
let file = std::fs::File::create(&path)?;
let env_filter = EnvFilter::new(config.max_level.as_str());
let fmt_layer = fmt::layer()
.json()
.with_writer(file)
.with_target(config.with_target)
.with_thread_ids(config.with_thread_ids)
.with_file(config.with_file_line)
.with_line_number(config.with_file_line);
tracing_subscriber::registry().with(env_filter).with(fmt_layer).init();
tracing::info!("🔍 开发追踪已启用(JSON 模式)");
tracing::info!("📁 输出文件: {}", path.display());
tracing::info!("📊 日志级别: {}", config.max_level);
Ok(())
}
#[cfg(feature = "dev-tracing-chrome")]
fn init_chrome_tracing(
config: TraceConfig
) -> anyhow::Result<TracingGuard> {
let path = config
.output_path
.unwrap_or_else(|| PathBuf::from("./logs/trace.json"));
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
let (chrome_layer, guard) = tracing_chrome::ChromeLayerBuilder::new()
.file(&path)
.include_args(true) .build();
let env_filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(config.max_level.as_str()));
let fmt_layer = fmt::layer()
.with_target(config.with_target)
.with_thread_ids(config.with_thread_ids)
.with_ansi(true)
.compact();
tracing_subscriber::registry()
.with(env_filter)
.with(fmt_layer)
.with(chrome_layer)
.init();
tracing::info!("🔍 开发追踪已启用(Chrome Tracing 模式)");
tracing::info!("📁 输出文件: {}", path.display());
tracing::info!("📊 日志级别: {}", config.max_level);
tracing::info!(
"🌐 查看方式: 在 Chrome 浏览器中访问 chrome://tracing 并加载文件"
);
tracing::info!("📦 包含信息: Span 时序、参数、线程 ID、进程 ID");
tracing::info!(
"⚠️ 重要: 请保持返回的 guard 直到程序结束,以确保数据被正确写入"
);
Ok(TracingGuard::Chrome(ChromeTracingGuard { _guard: guard }))
}
#[cfg(feature = "dev-tracing-perfetto")]
fn init_perfetto_tracing(config: TraceConfig) -> anyhow::Result<()> {
use std::fs::File;
let path = config
.output_path
.unwrap_or_else(|| PathBuf::from("./logs/trace.perfetto"));
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
let file = File::create(&path)?;
let perfetto_layer = tracing_perfetto::PerfettoLayer::new(file)
.with_debug_annotations(true);
let env_filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(config.max_level.as_str()));
let fmt_layer = fmt::layer()
.with_target(config.with_target)
.with_thread_ids(config.with_thread_ids)
.with_ansi(true)
.compact();
tracing_subscriber::registry()
.with(env_filter)
.with(fmt_layer)
.with(perfetto_layer)
.init();
log_system_info();
tracing::info!("🔍 开发追踪已启用(Perfetto 模式)");
tracing::info!("📁 输出文件: {}", path.display());
tracing::info!("📊 使用 https://ui.perfetto.dev/ 查看追踪数据");
tracing::info!("📊 日志级别: {}", config.max_level);
tracing::info!("💡 Perfetto 包含: span 时序、线程信息、系统资源");
Ok(())
}
#[cfg(feature = "dev-tracing-perfetto")]
fn log_system_info() {
use std::thread;
let process_id = std::process::id();
let thread_id = thread::current().id();
let thread_name =
thread::current().name().unwrap_or("main").to_string();
tracing::info!(
process_id = process_id,
thread_id = ?thread_id,
thread_name = %thread_name,
"系统信息"
);
let cpu_count = num_cpus::get();
tracing::info!(cpu_count = cpu_count, "CPU 信息");
}
pub fn init_default() -> anyhow::Result<TracingGuard> {
init_tracing(TraceConfig::default())
}
}
#[cfg(not(feature = "dev-tracing"))]
pub mod dev_tracing {
use std::path::PathBuf;
pub struct TracingGuard;
#[derive(Debug, Clone)]
pub enum TraceFormat {
Console,
Json,
}
#[derive(Debug, Clone)]
pub struct TraceConfig {
pub format: TraceFormat,
pub output_path: Option<PathBuf>,
pub max_level: (),
pub with_target: bool,
pub with_thread_ids: bool,
pub with_file_line: bool,
}
impl Default for TraceConfig {
fn default() -> Self {
Self {
format: TraceFormat::Console,
output_path: None,
max_level: (),
with_target: false,
with_thread_ids: false,
with_file_line: false,
}
}
}
impl TraceConfig {
pub fn console() -> Self {
Self::default()
}
pub fn json(_path: impl Into<PathBuf>) -> Self {
Self::default()
}
pub fn with_max_level(
self,
_level: (),
) -> Self {
self
}
}
pub fn init_tracing(_config: TraceConfig) -> anyhow::Result<TracingGuard> {
Ok(TracingGuard)
}
pub fn init_default() -> anyhow::Result<TracingGuard> {
Ok(TracingGuard)
}
}
#[cfg(feature = "dev-tracing")]
#[macro_export]
macro_rules! traced_span {
($name:expr) => {{
let trace_id = $crate::tracing_init::dev_tracing::generate_trace_id();
tracing::info_span!($name, trace_id = trace_id)
}};
($name:expr, $($field:tt)*) => {{
let trace_id = $crate::tracing_init::dev_tracing::generate_trace_id();
tracing::info_span!($name, trace_id = trace_id, $($field)*)
}};
}
#[cfg(not(feature = "dev-tracing"))]
#[macro_export]
macro_rules! traced_span {
($name:expr) => {{}};
($name:expr, $($field:tt)*) => {{}};
}
#[cfg(feature = "dev-tracing")]
#[macro_export]
macro_rules! trace_if_enabled {
($method:expr) => {{
if $crate::tracing_init::dev_tracing::should_trace($method) {
Some(tracing::info_span!($method).entered())
} else {
None
}
}};
($method:expr, $($field:tt)*) => {{
if $crate::tracing_init::dev_tracing::should_trace($method) {
Some(tracing::info_span!($method, $($field)*).entered())
} else {
None
}
}};
}
#[cfg(not(feature = "dev-tracing"))]
#[macro_export]
macro_rules! trace_if_enabled {
($method:expr) => {
None
};
($method:expr, $($field:tt)*) => {
None
};
}