Skip to main content

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}