logging模塊詳解

import logging 是 Python 內置的日志記錄模塊,用于替代 print() 進行更專業(yè)的日志管理。
基本用法

  1. 最簡單的配置
import logging

# 基本配置
logging.basicConfig(level=logging.INFO)
logging.info("這是一條信息")
logging.warning("這是一條警告")
logging.error("這是一條錯誤")
  1. 不同日志級別
import logging

logging.debug("調試信息")     # 級別 10
logging.info("普通信息")      # 級別 20
logging.warning("警告信息")   # 級別 30
logging.error("錯誤信息")     # 級別 40
logging.critical("嚴重錯誤")  # 級別 50

常用配置

  1. 配置輸出格式
import logging

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

logging.info("程序啟動")
# 輸出: 2024-01-15 10:30:45 - root - INFO - 程序啟動
  1. 輸出到文件
import logging

logging.basicConfig(
    filename='app.log',
    filemode='w',  # 'w' 覆蓋, 'a' 追加
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

logging.info("這條會寫入文件")
  1. 同時輸出到文件和控制臺
import logging

# 創(chuàng)建 logger
logger = logging.getLogger('myapp')
logger.setLevel(logging.DEBUG)

# 文件 handler
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.INFO)

# 控制臺 handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# 設置格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

# 添加 handler
logger.addHandler(file_handler)
logger.addHandler(console_handler)

# 使用
logger.debug("調試信息")
logger.info("普通信息")
logger.error("錯誤信息")

在函數/類中使用

import logging

def my_function():
    logger = logging.getLogger(__name__)
    logger.info("函數被調用")
    try:
        result = 10 / 0
    except ZeroDivisionError as e:
        logger.error(f"發(fā)生錯誤: {e}", exc_info=True)  # exc_info 打印完整堆棧

class MyClass:
    def __init__(self):
        self.logger = logging.getLogger(self.__class__.__name__)
    
    def do_something(self):
        self.logger.info("執(zhí)行操作")

最佳實踐

import logging

# 推薦的通用配置
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(pathname)s:%(lineno)d - %(message)s',
    handlers=[
        logging.FileHandler("app.log"),
        logging.StreamHandler()
    ]
)

# 在模塊中
logger = logging.getLogger(__name__)

def main():
    logger.info("程序啟動")
    # 你的代碼
    logger.info("程序結束")

一、logging 模塊架構
logging 模塊采用模塊化設計,包含四個主要組件:

Logger (日志器) → Handler (處理器) → Formatter (格式化器) → 輸出
     ↓
Filter (過濾器) - 控制日志級別和內容

二、日志級別詳解

import logging

# 預定義級別(數值越高越嚴重)
logging.NOTSET     = 0   # 未設置
logging.DEBUG      = 10  # 調試信息
logging.INFO       = 20  # 常規(guī)信息
logging.WARNING    = 30  # 警告信息
logging.ERROR      = 40  # 錯誤信息
logging.CRITICAL   = 50  # 嚴重錯誤

# 級別判斷示例
logger = logging.getLogger('demo')
logger.setLevel(logging.WARNING)

logger.debug("調試")      # 不會輸出(10 < 30)
logger.info("信息")       # 不會輸出(20 < 30)
logger.warning("警告")    # 會輸出(30 >= 30)
logger.error("錯誤")      # 會輸出(40 >= 30)
logger.critical("嚴重")   # 會輸出(50 >= 30)

三、Logger 對象詳解
3.1 創(chuàng)建 Logger

import logging

# 創(chuàng)建根日志器(root logger)
root_logger = logging.getLogger()
root_logger.setLevel(logging.INFO)

# 創(chuàng)建命名日志器(推薦)
app_logger = logging.getLogger('myapp')
module_logger = logging.getLogger('myapp.module')

# 層級關系:myapp.module 繼承 myapp 的設置
print(app_logger.parent)      # <RootLogger root>
print(module_logger.parent)   # <Logger myapp (INFO)>

3.2 Logger 的方法

import logging

logger = logging.getLogger(__name__)

# 基本日志方法
logger.debug('調試信息')
logger.info('普通信息')
logger.warning('警告信息')
logger.error('錯誤信息')
logger.critical('嚴重錯誤')
logger.exception('異常信息')  # 等同于 error,但會添加堆棧信息
logger.log(logging.INFO, '自定義級別的消息')

# 檢查級別(避免不必要的計算)
if logger.isEnabledFor(logging.DEBUG):
    logger.debug(f'復雜計算: {expensive_function()}')

四、Handler 類型及使用
4.1 常用 Handler

import logging

# 1. StreamHandler - 輸出到控制臺
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# 2. FileHandler - 輸出到文件
file_handler = logging.FileHandler('app.log', encoding='utf-8')
file_handler.setLevel(logging.INFO)

# 3. RotatingFileHandler - 按大小輪轉文件
from logging.handlers import RotatingFileHandler
rotating_handler = RotatingFileHandler(
    'app.log', 
    maxBytes=1024*1024,  # 1MB
    backupCount=5         # 保留5個備份
)

# 4. TimedRotatingFileHandler - 按時間輪轉
from logging.handlers import TimedRotatingFileHandler
time_handler = TimedRotatingFileHandler(
    'app.log',
    when='midnight',   # 每天午夜輪轉
    interval=1,
    backupCount=30     # 保留30天
)

# 5. HTTPHandler - 發(fā)送到遠程服務器
from logging.handlers import HTTPHandler
http_handler = HTTPHandler(
    host='localhost:8080',
    url='/log',
    method='POST'
)

# 6. SMTPHandler - 郵件發(fā)送錯誤
from logging.handlers import SMTPHandler
smtp_handler = SMTPHandler(
    mailhost=('smtp.gmail.com', 587),
    fromaddr='alert@myapp.com',
    toaddrs=['admin@myapp.com'],
    subject='應用錯誤',
    credentials=('user', 'password')
)
smtp_handler.setLevel(logging.ERROR)

4.2 完整的多 Handler 示例

import logging
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler

def setup_logger():
    """配置完整的日志系統(tǒng)"""
    
    # 創(chuàng)建主日志器
    logger = logging.getLogger('MyApp')
    logger.setLevel(logging.DEBUG)
    
    # 1. 控制臺輸出(僅 INFO 及以上)
    console = logging.StreamHandler()
    console.setLevel(logging.INFO)
    console_format = logging.Formatter('%(levelname)s - %(message)s')
    console.setFormatter(console_format)
    
    # 2. 文件輸出(所有級別)
    file_handler = logging.FileHandler('app.log', encoding='utf-8')
    file_handler.setLevel(logging.DEBUG)
    file_format = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    file_handler.setFormatter(file_format)
    
    # 3. 輪轉文件(錯誤級別單獨記錄)
    error_handler = RotatingFileHandler(
        'error.log', 
        maxBytes=10*1024*1024,  # 10MB
        backupCount=10,
        encoding='utf-8'
    )
    error_handler.setLevel(logging.ERROR)
    error_format = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
    )
    error_handler.setFormatter(error_format)
    
    # 添加所有處理器
    logger.addHandler(console)
    logger.addHandler(file_handler)
    logger.addHandler(error_handler)
    
    return logger

# 使用
logger = setup_logger()
logger.debug('調試信息')      # 僅文件
logger.info('普通信息')        # 控制臺 + 文件
logger.error('錯誤信息')       # 控制臺 + 文件 + 錯誤文件

五、Formatter 格式化詳解
5.1 格式化字段

# 常用格式化字段
format_dict = {
    '%(name)s':        '日志器名稱',
    '%(levelno)s':     '日志級別編號',
    '%(levelname)s':   '日志級別名稱',
    '%(pathname)s':    '源文件完整路徑',
    '%(filename)s':    '源文件名',
    '%(module)s':      '模塊名',
    '%(lineno)d':      '行號',
    '%(funcName)s':    '函數名',
    '%(created)f':     '時間戳',
    '%(asctime)s':     '可讀時間',
    '%(msecs)d':       '毫秒部分',
    '%(relativeCreated)d': '相對創(chuàng)建時間',
    '%(thread)d':      '線程ID',
    '%(threadName)s':  '線程名稱',
    '%(process)d':     '進程ID',
    '%(message)s':     '日志消息'
}

# 實際示例
import logging

# 簡單格式
simple_format = logging.Formatter('%(levelname)s: %(message)s')

# 詳細格式
detailed_format = logging.Formatter(
    '%(asctime)s | %(levelname)-8s | %(filename)s:%(lineno)-4d | %(message)s',
    datefmt='%H:%M:%S'
)

# 生產環(huán)境格式
production_format = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(process)d - %(threadName)s - %(message)s'
)

5.2 自定義 Formatter

import logging
import time

class ColoredFormatter(logging.Formatter):
    """彩色控制臺輸出格式器"""
    
    COLORS = {
        'DEBUG': '\033[36m',      # 青色
        'INFO': '\033[32m',       # 綠色
        'WARNING': '\033[33m',    # 黃色
        'ERROR': '\033[31m',      # 紅色
        'CRITICAL': '\033[35m',   # 紫色
        'RESET': '\033[0m'        # 重置
    }
    
    def format(self, record):
        # 添加顏色
        color = self.COLORS.get(record.levelname, self.COLORS['RESET'])
        record.levelname = f"{color}{record.levelname}{self.COLORS['RESET']}"
        return super().format(record)

# 使用彩色格式器
logger = logging.getLogger()
console = logging.StreamHandler()
formatter = ColoredFormatter('%(levelname)s - %(message)s')
console.setFormatter(formatter)
logger.addHandler(console)
logger.setLevel(logging.DEBUG)

logger.debug('調試信息')    # 青色
logger.info('信息')         # 綠色
logger.warning('警告')      # 黃色
logger.error('錯誤')        # 紅色

六、Filter 過濾器

import logging

class CustomFilter(logging.Filter):
    """自定義過濾器:只允許包含特定關鍵字的日志"""
    
    def __init__(self, keyword):
        super().__init__()
        self.keyword = keyword
    
    def filter(self, record):
        # 返回 True 表示允許,F(xiàn)alse 表示拒絕
        return self.keyword in record.getMessage()

# 使用過濾器
logger = logging.getLogger('app')
logger.setLevel(logging.DEBUG)

# 添加過濾器
filter_only_db = CustomFilter('database')
logger.addFilter(filter_only_db)

# 測試
logger.info('database connection')   # 會輸出
logger.info('user login')             # 不會輸出

# 移除過濾器
logger.removeFilter(filter_only_db)

七、配置方式
7.1 代碼配置(最常用)

import logging

# 快速配置
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('app.log'),
        logging.StreamHandler()
    ]
)

7.2 字典配置(推薦生產環(huán)境)

import logging.config

LOGGING_CONFIG = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        },
        'detailed': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(pathname)s:%(lineno)d - %(message)s'
        }
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'INFO',
            'formatter': 'standard',
            'stream': 'ext://sys.stdout'
        },
        'file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'level': 'DEBUG',
            'formatter': 'detailed',
            'filename': 'app.log',
            'maxBytes': 10485760,  # 10MB
            'backupCount': 5,
            'encoding': 'utf-8'
        }
    },
    'loggers': {
        '': {  # root logger
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
            'propagate': True
        },
        'myapp': {
            'handlers': ['file'],
            'level': 'INFO',
            'propagate': False
        }
    }
}

# 應用配置
logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger('myapp')

7.3 文件配置

# logging.conf
[loggers]
keys=root,myapp

[handlers]
keys=consoleHandler,fileHandler

[formatters]
keys=simpleFormatter,detailedFormatter

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_myapp]
level=INFO
handlers=consoleHandler,fileHandler
qualname=myapp
propagate=0

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[handler_fileHandler]
class=logging.handlers.RotatingFileHandler
level=INFO
formatter=detailedFormatter
args=('app.log', 'a', 10485760, 5, 'utf-8')

[formatter_simpleFormatter]
format=%(asctime)s - %(levelname)s - %(message)s

[formatter_detailedFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s

八、完整實戰(zhàn)案例
案例1:Web應用日志系統(tǒng)

import logging
import logging.handlers
import time
from functools import wraps

class WebAppLogger:
    """Web應用日志管理器"""
    
    def __init__(self, app_name, log_dir='./logs'):
        self.app_name = app_name
        self.log_dir = log_dir
        self.logger = self._setup_logger()
    
    def _setup_logger(self):
        """配置日志系統(tǒng)"""
        import os
        os.makedirs(self.log_dir, exist_ok=True)
        
        logger = logging.getLogger(self.app_name)
        logger.setLevel(logging.DEBUG)
        
        # 訪問日志
        access_handler = logging.handlers.TimedRotatingFileHandler(
            f'{self.log_dir}/access.log',
            when='midnight',
            interval=1,
            backupCount=30
        )
        access_format = logging.Formatter(
            '%(asctime)s - %(remote_ip)s - %(method)s - %(path)s - %(status)d - %(duration)dms'
        )
        access_handler.setFormatter(access_format)
        
        # 錯誤日志
        error_handler = logging.handlers.RotatingFileHandler(
            f'{self.log_dir}/error.log',
            maxBytes=10*1024*1024,
            backupCount=10
        )
        error_format = logging.Formatter(
            '%(asctime)s - %(levelname)s - %(pathname)s:%(lineno)d - %(message)s'
        )
        error_handler.setFormatter(error_format)
        error_handler.setLevel(logging.ERROR)
        
        # 控制臺輸出(開發(fā)環(huán)境)
        console = logging.StreamHandler()
        console.setLevel(logging.DEBUG)
        console_format = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        )
        console.setFormatter(console_format)
        
        logger.addHandler(access_handler)
        logger.addHandler(error_handler)
        logger.addHandler(console)
        
        return logger
    
    def log_request(self, func):
        """記錄請求的裝飾器"""
        @wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            # 模擬獲取請求信息
            remote_ip = kwargs.get('ip', 'unknown')
            method = kwargs.get('method', 'GET')
            path = kwargs.get('path', '/')
            
            try:
                result = func(*args, **kwargs)
                status = 200
                return result
            except Exception as e:
                status = 500
                self.logger.error(f"Request failed: {e}", exc_info=True)
                raise
            finally:
                duration = int((time.time() - start_time) * 1000)
                # 記錄訪問日志(使用額外參數)
                self.logger.info(
                    'Request processed',
                    extra={
                        'remote_ip': remote_ip,
                        'method': method,
                        'path': path,
                        'status': status,
                        'duration': duration
                    }
                )
        return wrapper

# 使用示例
web_logger = WebAppLogger('MyWebApp')

@web_logger.log_request
def handle_request(user_id, **request_info):
    """處理用戶請求"""
    web_logger.logger.info(f"Processing user {user_id}")
    # 業(yè)務邏輯
    return {"user_id": user_id, "status": "success"}

# 模擬請求
handle_request(123, ip='192.168.1.1', method='GET', path='/api/user')

異步日志處理

import logging
import queue
from logging.handlers import QueueHandler, QueueListener
import threading
import time

class AsyncLogger:
    """異步日志處理器"""
    
    def __init__(self):
        self.log_queue = queue.Queue(-1)
        self.queue_handler = QueueHandler(self.log_queue)
        
        # 配置實際處理器
        self.file_handler = logging.FileHandler('async.log')
        self.file_handler.setFormatter(
            logging.Formatter('%(asctime)s - %(message)s')
        )
        
        # 創(chuàng)建監(jiān)聽器
        self.listener = QueueListener(
            self.log_queue, self.file_handler, respect_handler_level=True
        )
        
        # 配置根日志器
        root_logger = logging.getLogger()
        root_logger.addHandler(self.queue_handler)
        root_logger.setLevel(logging.DEBUG)
        
        # 啟動監(jiān)聽器
        self.listener.start()
    
    def stop(self):
        self.listener.stop()

# 使用異步日志
async_logger = AsyncLogger()
logger = logging.getLogger()

# 日志記錄不會阻塞主線程
for i in range(1000):
    logger.info(f"Message {i}")
    
# 程序結束時停止
async_logger.stop()
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容