fastmcp_core/logging.rs
1//! Structured logging for FastMCP.
2//!
3//! This module provides structured logging support built on the standard
4//! [`log`] facade. All FastMCP crates use these logging utilities.
5//!
6//! # Log Levels
7//!
8//! - **error**: Unrecoverable errors, transport failures
9//! - **warn**: Recoverable issues, deprecation warnings
10//! - **info**: Server lifecycle events (start, stop, client connect)
11//! - **debug**: Request/response flow, handler invocations
12//! - **trace**: Wire-level message details, internal state
13//!
14//! # Usage
15//!
16//! The log macros are re-exported for convenience:
17//!
18//! ```ignore
19//! use fastmcp_core::logging::{error, warn, info, debug, trace};
20//!
21//! info!("Server started on {}", transport);
22//! debug!(target: "mcp::request", method = %method, "Handling request");
23//! error!("Transport error: {}", err);
24//! ```
25//!
26//! # Initialization
27//!
28//! FastMCP does not include a log implementation. Applications should
29//! initialize logging using their preferred backend:
30//!
31//! ```ignore
32//! // Using env_logger (simple)
33//! env_logger::init();
34//!
35//! // Using simple_logger
36//! simple_logger::init_with_level(log::Level::Info).unwrap();
37//! ```
38//!
39//! # Log Targets
40//!
41//! FastMCP uses hierarchical log targets for filtering:
42//!
43//! - `fastmcp_rust`: Root target for all FastMCP logs
44//! - `fastmcp_rust::server`: Server lifecycle and request handling
45//! - `fastmcp_rust::transport`: Transport layer messages
46//! - `fastmcp_rust::router`: Request routing and dispatch
47//! - `fastmcp_rust::handler`: Tool/resource/prompt handler execution
48//!
49//! Example filter: `RUST_LOG=fastmcp_rust::server=debug,fastmcp_rust::transport=trace`
50
51// Re-export log macros for ergonomic use
52pub use log::{debug, error, info, trace, warn};
53
54// Re-export log level types for programmatic use
55pub use log::{Level, LevelFilter};
56
57/// Log targets used by FastMCP components.
58///
59/// Use these constants with the `target:` argument to log macros
60/// for consistent filtering.
61pub mod targets {
62 /// Root target for all FastMCP logs.
63 pub const FASTMCP: &str = "fastmcp_rust";
64
65 /// Server lifecycle and request handling.
66 pub const SERVER: &str = "fastmcp_rust::server";
67
68 /// Transport layer (stdio, SSE, WebSocket).
69 pub const TRANSPORT: &str = "fastmcp_rust::transport";
70
71 /// Request routing and method dispatch.
72 pub const ROUTER: &str = "fastmcp_rust::router";
73
74 /// Tool, resource, and prompt handler execution.
75 pub const HANDLER: &str = "fastmcp_rust::handler";
76
77 /// Client connections and sessions.
78 pub const SESSION: &str = "fastmcp_rust::session";
79
80 /// Codec operations (JSON encoding/decoding).
81 pub const CODEC: &str = "fastmcp_rust::codec";
82}
83
84/// Returns whether logging is enabled at the given level for the given target.
85///
86/// This is useful for conditionally computing expensive log message data:
87///
88/// ```ignore
89/// use fastmcp_core::logging::{is_enabled, Level, targets};
90///
91/// if is_enabled(Level::Debug, targets::HANDLER) {
92/// let stats = compute_expensive_stats();
93/// debug!(target: targets::HANDLER, "Handler stats: {:?}", stats);
94/// }
95/// ```
96#[inline]
97#[must_use]
98pub fn is_enabled(level: Level, target: &str) -> bool {
99 log::log_enabled!(target: target, level)
100}
101
102/// Logs a server lifecycle event at INFO level.
103///
104/// This is a convenience macro for common server events.
105#[macro_export]
106macro_rules! log_server {
107 ($($arg:tt)*) => {
108 log::info!(target: "fastmcp_rust::server", $($arg)*)
109 };
110}
111
112/// Logs a transport event at DEBUG level.
113#[macro_export]
114macro_rules! log_transport {
115 ($($arg:tt)*) => {
116 log::debug!(target: "fastmcp_rust::transport", $($arg)*)
117 };
118}
119
120/// Logs a request routing event at DEBUG level.
121#[macro_export]
122macro_rules! log_router {
123 ($($arg:tt)*) => {
124 log::debug!(target: "fastmcp_rust::router", $($arg)*)
125 };
126}
127
128/// Logs a handler execution event at DEBUG level.
129#[macro_export]
130macro_rules! log_handler {
131 ($($arg:tt)*) => {
132 log::debug!(target: "fastmcp_rust::handler", $($arg)*)
133 };
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn log_targets_are_hierarchical() {
142 // Verify targets follow the fastmcp_rust:: prefix pattern
143 assert!(targets::SERVER.starts_with(targets::FASTMCP));
144 assert!(targets::TRANSPORT.starts_with(targets::FASTMCP));
145 assert!(targets::ROUTER.starts_with(targets::FASTMCP));
146 assert!(targets::HANDLER.starts_with(targets::FASTMCP));
147 assert!(targets::SESSION.starts_with(targets::FASTMCP));
148 assert!(targets::CODEC.starts_with(targets::FASTMCP));
149 }
150
151 #[test]
152 fn level_ordering() {
153 // Verify log level ordering (lower = more severe)
154 assert!(Level::Error < Level::Warn);
155 assert!(Level::Warn < Level::Info);
156 assert!(Level::Info < Level::Debug);
157 assert!(Level::Debug < Level::Trace);
158 }
159
160 #[test]
161 fn is_enabled_returns_bool_without_panic() {
162 // Without a logger installed, is_enabled returns false but must not panic
163 let result = is_enabled(Level::Info, targets::FASTMCP);
164 assert!(!result); // no logger configured in test
165 }
166
167 #[test]
168 fn target_constants_have_expected_values() {
169 assert_eq!(targets::FASTMCP, "fastmcp_rust");
170 assert_eq!(targets::SERVER, "fastmcp_rust::server");
171 assert_eq!(targets::TRANSPORT, "fastmcp_rust::transport");
172 assert_eq!(targets::ROUTER, "fastmcp_rust::router");
173 assert_eq!(targets::HANDLER, "fastmcp_rust::handler");
174 assert_eq!(targets::SESSION, "fastmcp_rust::session");
175 assert_eq!(targets::CODEC, "fastmcp_rust::codec");
176 }
177
178 #[test]
179 fn target_constants_are_all_distinct() {
180 let all = [
181 targets::FASTMCP,
182 targets::SERVER,
183 targets::TRANSPORT,
184 targets::ROUTER,
185 targets::HANDLER,
186 targets::SESSION,
187 targets::CODEC,
188 ];
189 for i in 0..all.len() {
190 for j in (i + 1)..all.len() {
191 assert_ne!(all[i], all[j], "targets at {} and {} collide", i, j);
192 }
193 }
194 }
195
196 #[test]
197 fn log_macros_compile_and_do_not_panic() {
198 // Macros expand to log calls; without a logger they are no-ops
199 log_server!("test server event: {}", 42);
200 log_transport!("test transport event");
201 log_router!("test router event: {}", "route");
202 log_handler!("test handler event");
203 }
204}