Skip to main content
Glama
hingaibm

Data Intelligence MCP Server

by hingaibm
utils.py5.84 kB
# Copyright [2025] [IBM] # Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) # See the LICENSE file in the project root for license information. """ Logging utilities and setup functions for the Data Intelligence MCP Server. This module provides utilities for setting up and configuring logging with custom formats, traceability, and structured output. """ import logging # Removed unused import: os import sys from pathlib import Path from typing import Optional from .constants import ( LOGGING_LEVELS, JSON_LOGGING, HUMAN_LOGGING, LOGGING_LEVEL, LOGGING_FORMAT, CONTAINER_NAME, ENVIRONMENT, BUILD_VERSION, LOG_FILE_PATH, set_parameter_value, parameter_values ) from .filter import LoggingTraceabilityFilter def setup_logging( logger_name: Optional[str] = None, log_level: Optional[str] = None, log_format: Optional[str] = None, log_file_path: Optional[str] = None ) -> logging.Logger: """ Configure logging based on specified parameters. This function sets up a logger with either human-readable or JSON format, includes traceability information, and configures appropriate handlers. Args: logger_name: Name for the logger (defaults to container name from config) log_level: Logging level (defaults to config value) log_format: Logging format type (defaults to config value) log_file_path: File to which logs are written. For stdio mode, this is mandatory as stdio is used by the MCP protocol itself Returns: logging.Logger: Configured logger instance Raises: ValueError: If invalid logging level is specified """ # Set parameter values from environment or defaults retrieve_parameters_from_env() # Use provided values or fall back to configuration final_logger_name = logger_name or parameter_values[CONTAINER_NAME] final_log_level = log_level or parameter_values[LOGGING_LEVEL] final_log_format = log_format or parameter_values[LOGGING_FORMAT] final_log_file_path = log_file_path or parameter_values.get(LOG_FILE_PATH) # Get logger instance logger = logging.getLogger(final_logger_name) # We don't want to propagate the logger to the root logger logger.propagate = False # Return existing logger if already configured if logger.handlers: return logger # Validate logging level if final_log_level.upper() not in LOGGING_LEVELS: raise ValueError( f"Invalid logging level '{final_log_level.upper()}'. " f"Supported levels: {list(LOGGING_LEVELS.keys())}" ) # Set logger level logger.setLevel(LOGGING_LEVELS[final_log_level.upper()]) # Create handler based on configuration if final_log_file_path: # File logging log_path = Path(final_log_file_path) log_path.parent.mkdir(parents=True, exist_ok=True) ch = logging.FileHandler(log_path) else: # Console logging - CRITICAL: Use stderr for MCP stdio transport # In stdio mode, STDOUT is exclusively used by FastMCP for JSON-RPC protocol # Writing logs to stdout corrupts the protocol stream causing client-side # JSON parse errors like "Expected ',' or '}' at position X" ch = logging.StreamHandler(sys.stderr) # Configure formatter based on format type if final_log_format == HUMAN_LOGGING: formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) elif final_log_format == JSON_LOGGING: formatter = logging.Formatter( '{"environment": "' + parameter_values[ENVIRONMENT] + '", "build_version": "' + parameter_values[BUILD_VERSION] + '", "timestamp": "%(asctime)s", "appname": "%(name)s"' + ', "loglevel": "%(levelname)s", "message": "%(message)s"' + ', "threadId": "%(thread)d", "thread": "%(threadName)s"' + ', "sequence_number": "%(sequence_number)d"' + ', "transaction_id": "%(transaction_id)s"' + ', "trace_id": "%(trace_id)s"' + '", "line": "%(lineno)d", "method_name": "%(funcName)s"' + ', "class_name": "%(module)s"' + '"}' ) # Add traceability filter for JSON logging ch.addFilter(LoggingTraceabilityFilter()) else: # Custom format provided formatter = logging.Formatter(final_log_format) # Set formatter and add handler ch.setFormatter(formatter) logger.addHandler(ch) if final_log_file_path: # Add console handler for ERROR and CRITICAL levels console_handler = logging.StreamHandler(sys.stderr) console_handler.setLevel(logging.ERROR) console_formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) console_handler.setFormatter(console_formatter) logger.addHandler(console_handler) return logger def get_logger(name: Optional[str] = None) -> logging.Logger: """ Get a logger instance with the specified name. Args: name: Logger name (defaults to container name from config) Returns: logging.Logger: Configured logger instance """ return setup_logging(logger_name=name) def retrieve_parameters_from_env() -> None: """ Get and verify configuration from the environment. This function reads all configuration parameters from environment variables and stores them in the parameter_values dictionary. """ from .constants import DEFAULT_PARAMETERS for parameter_name in DEFAULT_PARAMETERS.keys(): set_parameter_value(parameter_name) # Global logger instance LOGGER = setup_logging()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hingaibm/data-intelligence-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server