#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 日誌管理模組 Author: PANJIT IT Team Created: 2024-01-28 Modified: 2024-01-28 """ import logging import os from pathlib import Path from logging.handlers import RotatingFileHandler from flask import current_app, has_request_context, request, g def get_logger(name): """取得指定名稱的日誌器""" logger = logging.getLogger(name) # 避免重複設定 handler if not logger.handlers: setup_logger(logger) return logger def setup_logger(logger): """設定日誌器""" if has_request_context() and current_app: log_level = current_app.config.get('LOG_LEVEL', 'INFO') log_file = current_app.config.get('LOG_FILE', 'logs/app.log') else: log_level = os.environ.get('LOG_LEVEL', 'INFO') log_file = os.environ.get('LOG_FILE', 'logs/app.log') # 確保日誌目錄存在 log_path = Path(log_file) log_path.parent.mkdir(parents=True, exist_ok=True) # 設定日誌等級 logger.setLevel(getattr(logging, log_level.upper())) # 建立格式化器 formatter = logging.Formatter( '%(asctime)s [%(levelname)s] %(name)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) # 檔案處理器(使用輪轉) file_handler = RotatingFileHandler( log_file, maxBytes=10*1024*1024, # 10MB backupCount=5, encoding='utf-8' ) file_handler.setLevel(getattr(logging, log_level.upper())) file_handler.setFormatter(formatter) logger.addHandler(file_handler) # 控制台處理器 console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) console_handler.setFormatter(formatter) logger.addHandler(console_handler) class DatabaseLogHandler(logging.Handler): """資料庫日誌處理器""" def emit(self, record): """發送日誌記錄到資料庫""" try: from app.models.log import SystemLog # 取得使用者和任務資訊(如果有的話) user_id = None job_id = None extra_data = {} if has_request_context(): user_id = g.get('current_user_id') extra_data.update({ 'method': request.method, 'endpoint': request.endpoint, 'url': request.url, 'ip_address': request.remote_addr, 'user_agent': request.headers.get('User-Agent') }) # 儲存到資料庫 SystemLog.log( level=record.levelname, module=record.name, message=record.getMessage(), user_id=user_id, job_id=job_id, extra_data=extra_data if extra_data else None ) except Exception: # 避免日誌記錄失敗影響主程序 pass def init_logging(app): """初始化應用程式日誌""" # 設定根日誌器 root_logger = logging.getLogger() root_logger.setLevel(logging.INFO) # 添加資料庫日誌處理器(僅對重要日誌) if app.config.get('SQLALCHEMY_DATABASE_URI'): db_handler = DatabaseLogHandler() db_handler.setLevel(logging.WARNING) # 只記錄警告以上等級到資料庫 root_logger.addHandler(db_handler) # 設定 Flask 應用日誌 if not app.logger.handlers: setup_logger(app.logger) # 設定第三方庫日誌等級 logging.getLogger('werkzeug').setLevel(logging.WARNING) logging.getLogger('urllib3').setLevel(logging.WARNING) logging.getLogger('requests').setLevel(logging.WARNING)