Skip to main content
Glama
JDeun

Unified Search MCP Server

by JDeun
logging.py6.09 kB
# src/utils/logging.py """ 로깅 유틸리티 구조화된 로깅 및 감사 로깅 """ import logging import logging.config import json from typing import Dict, Any, Optional from datetime import datetime from contextvars import ContextVar import sys from ..config import get_settings # 요청 컨텍스트 request_id_var: ContextVar[Optional[str]] = ContextVar('request_id', default=None) client_id_var: ContextVar[Optional[str]] = ContextVar('client_id', default=None) class ContextFilter(logging.Filter): """컨텍스트 정보를 로그에 추가하는 필터""" def filter(self, record): record.request_id = request_id_var.get() or 'no-request-id' record.client_id = client_id_var.get() or 'anonymous' return True class AuditLogger: """감사 로깅""" def __init__(self, name: str = "audit"): self.logger = logging.getLogger(f"{name}.audit") def log_search( self, query: str, source: str, results_count: int, duration: float, metadata: Optional[Dict[str, Any]] = None ): """검색 로그""" self.logger.info( "search_performed", extra={ 'event_type': 'search', 'query': query, 'source': source, 'results_count': results_count, 'duration': duration, 'metadata': metadata or {}, 'timestamp': datetime.utcnow().isoformat() } ) def log_api_call( self, api: str, endpoint: str, status: int, duration: float, metadata: Optional[Dict[str, Any]] = None ): """API 호출 로그""" self.logger.info( "api_call", extra={ 'event_type': 'api_call', 'api': api, 'endpoint': endpoint, 'status': status, 'duration': duration, 'metadata': metadata or {}, 'timestamp': datetime.utcnow().isoformat() } ) def log_error( self, error_type: str, error_message: str, source: str, metadata: Optional[Dict[str, Any]] = None ): """에러 로그""" self.logger.error( "error_occurred", extra={ 'event_type': 'error', 'error_type': error_type, 'error_message': error_message, 'source': source, 'metadata': metadata or {}, 'timestamp': datetime.utcnow().isoformat() } ) def log_security_event( self, event_type: str, description: str, severity: str, metadata: Optional[Dict[str, Any]] = None ): """보안 이벤트 로그""" self.logger.warning( "security_event", extra={ 'event_type': 'security', 'security_event_type': event_type, 'description': description, 'severity': severity, 'metadata': metadata or {}, 'timestamp': datetime.utcnow().isoformat() } ) class PerformanceLogger: """성능 로깅을 위한 컨텍스트 매니저""" def __init__(self, operation: str, logger: logging.Logger): self.operation = operation self.logger = logger self.start_time = None self.context = {} def add_context(self, **kwargs): """컨텍스트 추가""" self.context.update(kwargs) return self async def __aenter__(self): self.start_time = datetime.utcnow() self.logger.debug(f"{self.operation} 시작", extra=self.context) return self async def __aexit__(self, exc_type, exc_val, exc_tb): duration = (datetime.utcnow() - self.start_time).total_seconds() if exc_type: self.logger.error( f"{self.operation} 실패", extra={ **self.context, 'duration': duration, 'error': str(exc_val) } ) else: self.logger.info( f"{self.operation} 완료", extra={ **self.context, 'duration': duration } ) def setup_logging(): """로깅 설정""" settings = get_settings() log_config = settings.get_log_config() # 컨텍스트 필터 추가 for handler in log_config.get('handlers', {}).values(): if 'filters' not in handler: handler['filters'] = [] handler['filters'].append('context_filter') # 필터 설정 추가 if 'filters' not in log_config: log_config['filters'] = {} log_config['filters']['context_filter'] = { '()': ContextFilter } # 로깅 설정 적용 logging.config.dictConfig(log_config) # 외부 라이브러리 로깅 레벨 조정 logging.getLogger('httpx').setLevel(logging.WARNING) logging.getLogger('scholarly').setLevel(logging.WARNING) logging.getLogger('redis').setLevel(logging.WARNING) def get_logger(name: str) -> logging.Logger: """로거 가져오기""" return logging.getLogger(name) def set_request_context(request_id: Optional[str] = None, client_id: Optional[str] = None): """요청 컨텍스트 설정""" if request_id: request_id_var.set(request_id) if client_id: client_id_var.set(client_id) def clear_request_context(): """요청 컨텍스트 클리어""" request_id_var.set(None) client_id_var.set(None) # 싱글톤 감사 로거 _audit_logger: Optional[AuditLogger] = None def get_audit_logger() -> AuditLogger: """감사 로거 가져오기""" global _audit_logger if _audit_logger is None: _audit_logger = AuditLogger() return _audit_logger

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/JDeun/unified-search-mcp-server'

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