11# Structured Logging in Katana MCP Server
22
3- The Katana MCP Server uses structured logging with [ structlog] ( https://www.structlog.org/ ) to provide rich observability and debugging capabilities.
3+ The Katana MCP Server uses structured logging with
4+ [ structlog] ( https://www.structlog.org/ ) to provide rich observability and debugging
5+ capabilities.
46
57## Features
68
7- - ** Structured JSON or text output** - Choose between JSON (for log aggregation) or human-readable text format
8- - ** Contextual information** - Every log includes relevant context (tool names, SKUs, IDs, etc.)
9+ - ** Structured JSON or text output** - Choose between JSON (for log aggregation) or
10+ human-readable text format
11+ - ** Contextual information** - Every log includes relevant context (tool names, SKUs,
12+ IDs, etc.)
913- ** Performance metrics** - Automatic duration tracking for all tool executions
1014- ** Trace IDs** - Support for request correlation across operations
11- - ** Security** - Automatic redaction of sensitive data (API keys, passwords, credentials)
15+ - ** Security** - Automatic redaction of sensitive data (API keys, passwords,
16+ credentials)
1217- ** Configurable levels** - Control log verbosity via environment variables
1318
1419## Configuration
@@ -18,21 +23,25 @@ The Katana MCP Server uses structured logging with [structlog](https://www.struc
1823Configure logging behavior with these environment variables:
1924
2025- ** ` KATANA_MCP_LOG_LEVEL ` ** - Log level (default: ` INFO ` )
26+
2127 - Options: ` DEBUG ` , ` INFO ` , ` WARNING ` , ` ERROR `
22-
28+
2329- ** ` KATANA_MCP_LOG_FORMAT ` ** - Output format (default: ` json ` )
30+
2431 - Options: ` json ` , ` text `
2532
2633### Examples
2734
2835** Development (verbose text output):**
36+
2937``` bash
3038export KATANA_MCP_LOG_LEVEL=DEBUG
3139export KATANA_MCP_LOG_FORMAT=text
3240katana-mcp-server
3341```
3442
3543** Production (structured JSON):**
44+
3645``` bash
3746export KATANA_MCP_LOG_LEVEL=INFO
3847export KATANA_MCP_LOG_FORMAT=json
@@ -66,6 +75,7 @@ katana-mcp-server
6675### Server Lifecycle
6776
6877** Server Initialization:**
78+
6979``` json
7080{
7181 "event" : " server_initializing" ,
@@ -76,6 +86,7 @@ katana-mcp-server
7686```
7787
7888** Client Ready:**
89+
7990``` json
8091{
8192 "event" : " client_initialized" ,
@@ -87,6 +98,7 @@ katana-mcp-server
8798```
8899
89100** Server Ready:**
101+
90102``` json
91103{
92104 "event" : " server_ready" ,
@@ -98,6 +110,7 @@ katana-mcp-server
98110### Tool Execution
99111
100112** Inventory Check (Success):**
113+
101114``` json
102115{
103116 "event" : " inventory_check_completed" ,
@@ -111,6 +124,7 @@ katana-mcp-server
111124```
112125
113126** Search Items (Success):**
127+
114128``` json
115129{
116130 "event" : " item_search_completed" ,
@@ -122,6 +136,7 @@ katana-mcp-server
122136```
123137
124138** Create Item (Success):**
139+
125140``` json
126141{
127142 "event" : " item_create_completed" ,
@@ -137,6 +152,7 @@ katana-mcp-server
137152### Error Logging
138153
139154** Tool Failure:**
155+
140156``` json
141157{
142158 "event" : " item_search_failed" ,
@@ -150,6 +166,7 @@ katana-mcp-server
150166```
151167
152168** Authentication Error:**
169+
153170``` json
154171{
155172 "event" : " authentication_failed" ,
@@ -166,11 +183,13 @@ katana-mcp-server
166183The logger automatically redacts sensitive information from logs:
167184
168185** Input:**
186+
169187``` python
170188logger.info(" api_call" , api_key = " secret-key-123" , username = " john" )
171189```
172190
173191** Output (JSON):**
192+
174193``` json
175194{
176195 "event" : " api_call" ,
@@ -181,6 +200,7 @@ logger.info("api_call", api_key="secret-key-123", username="john")
181200```
182201
183202** Redacted Keys:**
203+
184204- ` api_key ` , ` API_KEY `
185205- ` password ` , ` PASSWORD `
186206- ` secret ` , ` SECRET `
@@ -199,6 +219,7 @@ All tool executions include performance metrics:
199219- ** ` threshold ` ** - Configured threshold (for low stock checks)
200220
201221Example with metrics:
222+
202223``` json
203224{
204225 "event" : " low_stock_search_completed" ,
@@ -210,14 +231,238 @@ Example with metrics:
210231}
211232```
212233
234+ ## Observability Decorators
235+
236+ The Katana MCP Server provides convenience decorators to automatically instrument tools
237+ and service methods with logging, timing, and error tracking.
238+
239+ ### @observe_tool
240+
241+ Automatically instruments MCP tool functions with comprehensive observability.
242+
243+ ** Features:**
244+
245+ - Automatic logging of tool invocations with parameters
246+ - Execution timing in milliseconds
247+ - Success/failure tracking
248+ - Error details with exception types
249+ - Context parameter filtering (ctx/context excluded from logs)
250+
251+ ** Usage:**
252+
253+ ``` python
254+ from katana_mcp.logging import observe_tool
255+ from fastmcp import Context
256+
257+ @observe_tool
258+ @mcp.tool ()
259+ async def my_tool (request : MyRequest, context : Context) -> MyResponse:
260+ """ My tool implementation."""
261+ return await do_work()
262+ ```
263+
264+ ** Log Events Produced:**
265+
266+ Tool invocation (INFO level):
267+
268+ ``` json
269+ {
270+ "event" : " tool_invoked" ,
271+ "tool_name" : " my_tool" ,
272+ "params" : {"request" : {... }},
273+ "timestamp" : " 2025-01-05T17:08:40.123456Z" ,
274+ "level" : " info"
275+ }
276+ ```
277+
278+ Successful completion (INFO level):
279+
280+ ``` json
281+ {
282+ "event" : " tool_completed" ,
283+ "tool_name" : " my_tool" ,
284+ "duration_ms" : 245.67 ,
285+ "success" : true ,
286+ "timestamp" : " 2025-01-05T17:08:40.369126Z" ,
287+ "level" : " info"
288+ }
289+ ```
290+
291+ Tool failure (ERROR level):
292+
293+ ``` json
294+ {
295+ "event" : " tool_failed" ,
296+ "tool_name" : " my_tool" ,
297+ "duration_ms" : 12.34 ,
298+ "error" : " Invalid request parameters" ,
299+ "error_type" : " ValueError" ,
300+ "success" : false ,
301+ "timestamp" : " 2025-01-05T17:08:40.135796Z" ,
302+ "level" : " error"
303+ }
304+ ```
305+
306+ ** Best Practices:**
307+
308+ - Place ` @observe_tool ` before ` @mcp.tool() ` decorator
309+ - Use with ` @unpack_pydantic_params ` for parameter unpacking
310+ - Context parameters (ctx, context) are automatically filtered from logs
311+ - No need to add manual timing or error logging in tool implementation
312+
313+ ** Example with Parameter Unpacking:**
314+
315+ ``` python
316+ @observe_tool
317+ @unpack_pydantic_params
318+ async def search_items (
319+ request : Annotated[SearchItemsRequest, Unpack()], context : Context
320+ ) -> SearchItemsResponse:
321+ """ Search for items - automatically instrumented."""
322+ services = get_services(context)
323+ return await services.client.variants.search(request.query)
324+ ```
325+
326+ ### @observe_service
327+
328+ Instruments service layer methods with debug-level logging.
329+
330+ ** Features:**
331+
332+ - Service operation tracking
333+ - Method timing
334+ - Error logging with context
335+ - Class name extraction
336+
337+ ** Usage:**
338+
339+ ``` python
340+ from katana_mcp.logging import observe_service
341+
342+ class MyService :
343+ @observe_service (" get_item" )
344+ async def get (self , item_id : int ) -> Item:
345+ """ Fetch item by ID."""
346+ return await self ._fetch(item_id)
347+
348+ @observe_service (" create_item" )
349+ async def create (self , data : dict ) -> Item:
350+ """ Create new item."""
351+ return await self ._create(data)
352+ ```
353+
354+ ** Log Events Produced:**
355+
356+ Operation started (DEBUG level):
357+
358+ ``` json
359+ {
360+ "event" : " service_operation_started" ,
361+ "service" : " MyService" ,
362+ "operation" : " get_item" ,
363+ "params" : {"item_id" : 123 },
364+ "timestamp" : " 2025-01-05T17:08:40.123456Z" ,
365+ "level" : " debug"
366+ }
367+ ```
368+
369+ Operation completed (DEBUG level):
370+
371+ ``` json
372+ {
373+ "event" : " service_operation_completed" ,
374+ "service" : " MyService" ,
375+ "operation" : " get_item" ,
376+ "duration_ms" : 45.67 ,
377+ "success" : true ,
378+ "timestamp" : " 2025-01-05T17:08:40.169126Z" ,
379+ "level" : " debug"
380+ }
381+ ```
382+
383+ Operation failed (ERROR level):
384+
385+ ``` json
386+ {
387+ "event" : " service_operation_failed" ,
388+ "service" : " MyService" ,
389+ "operation" : " get_item" ,
390+ "duration_ms" : 12.34 ,
391+ "error" : " Item not found" ,
392+ "error_type" : " ItemNotFoundError" ,
393+ "success" : false ,
394+ "timestamp" : " 2025-01-05T17:08:40.135796Z" ,
395+ "level" : " error"
396+ }
397+ ```
398+
399+ ** Best Practices:**
400+
401+ - Use descriptive operation names (e.g., "get_product", "create_order")
402+ - Service operations log at DEBUG level (less verbose than tools)
403+ - Use for lower-level operations called by tools
404+ - Provides granular visibility into service layer performance
405+
406+ ### Decorator Comparison
407+
408+ | Feature | @observe_tool | @observe_service |
409+ | ----------------------- | ---------------------------------- | ------------------------------------- |
410+ | ** Use Case** | MCP tools (user-facing operations) | Service methods (internal operations) |
411+ | ** Log Level** | INFO | DEBUG |
412+ | ** Parameter Filtering** | Yes (ctx/context) | No |
413+ | ** Primary Audience** | Operations/users | Developers |
414+ | ** Visibility** | High (always logged in production) | Low (only in debug mode) |
415+
416+ ### Migration Guide
417+
418+ ** Before (Manual Logging):**
419+
420+ ``` python
421+ @mcp.tool ()
422+ async def my_tool (request : MyRequest, context : Context) -> MyResponse:
423+ logger.info(" my_tool_started" , request = request)
424+ start_time = time.monotonic()
425+ try :
426+ result = await do_work()
427+ duration_ms = (time.monotonic() - start_time) * 1000
428+ logger.info(" my_tool_completed" , duration_ms = duration_ms)
429+ return result
430+ except Exception as e:
431+ logger.error(" my_tool_failed" , error = str (e))
432+ raise
433+ ```
434+
435+ ** After (With Decorator):**
436+
437+ ``` python
438+ @observe_tool
439+ @mcp.tool ()
440+ async def my_tool (request : MyRequest, context : Context) -> MyResponse:
441+ return await do_work() # Automatic logging, timing, error tracking!
442+ ```
443+
444+ ** Benefits:**
445+
446+ - 10+ lines of boilerplate eliminated per tool
447+ - Consistent log format across all tools
448+ - Automatic error tracking without try/except
449+ - Performance metrics captured automatically
450+ - No risk of forgetting to log important events
451+
213452## Best Practices
214453
2154541 . ** Use INFO level in production** - Provides operational visibility without noise
216- 2 . ** Use DEBUG for troubleshooting** - Includes detailed execution traces
217- 3 . ** Use JSON format for log aggregation** - Easier to parse and analyze
218- 4 . ** Use text format for development** - More human-readable
219- 5 . ** Monitor duration_ms** - Identify performance bottlenecks
220- 6 . ** Never log sensitive data** - The filter catches common patterns, but be careful with custom fields
455+ 1 . ** Use DEBUG for troubleshooting** - Includes detailed execution traces and service
456+ operations
457+ 1 . ** Use JSON format for log aggregation** - Easier to parse and analyze
458+ 1 . ** Use text format for development** - More human-readable
459+ 1 . ** Monitor duration_ms** - Identify performance bottlenecks
460+ 1 . ** Never log sensitive data** - The filter catches common patterns, but be careful
461+ with custom fields
462+ 1 . ** Use @observe_tool for all MCP tools** - Automatic instrumentation with zero
463+ boilerplate
464+ 1 . ** Use @observe_service for service methods** - Granular visibility into internal
465+ operations
221466
222467## References
223468
0 commit comments