Python 日志处理全解析:从基础到进阶实战教程
在 Python 编程领域,日志处理是保障程序稳定运行、高效调试以及深入分析的关键技术。本教程深入探究 Python 日志处理的各个层面,涵盖多模块、多线程环境下的日志运用,多种处理器和格式化器的配置技巧,自定义处理级别、日志配置服务器的应用,以及应对日志阻塞和网络日志收发等进阶技术。通过丰富的代码示例、清晰的图文讲解,助力读者全面掌握日志处理技术。同时,分享日志处理的最佳实践,并结合实际项目案例,让读者切实了解其在真实场景中的应用。
多模块中的日志使用
原理 :在 Python 中,无论在同一个模块内还是跨模块调用logging.getLogger('someLogger')
,返回的都是同一个 logger 对象的引用。这使得应用程序可以在一个模块中定义和配置父 logger,在其他模块中创建子 logger,子 logger 的调用会传递给父 logger。
示例
import logging
import auxiliary_module
logger = logging.getLogger('spam_application' )
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('spam.log' )
fh.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s' )
fh.setFormatter(formatter)
ch.setFormatter(formatter)
logger.addHandler(fh)
logger.addHandler(ch)
logger.info('creating an instance of auxiliary_module.Auxiliary' )
a = auxiliary_module.Auxiliary()
logger.info('created an instance of auxiliary_module.Auxiliary' )
logger.info('calling auxiliary_module.Auxiliary.do_something' )
a.do_something()
logger.info('finished auxiliary_module.Auxiliary.do_something' )
logger.info('calling auxiliary_module.some_function()' )
auxiliary_module.some_function()
logger.info('done with auxiliary_module.some_function()' )
辅助模块(auxiliary_module.py)
import logging
module_logger = logging.getLogger('spam_application.auxiliary' )
class Auxiliary :
def __init__ (self ):
self.logger = logging.getLogger('spam_application.auxiliary.Auxiliary' )
self.logger.info('creating an instance of Auxiliary' )
def do_something (self ):
self.logger.info('doing something' )
a = 1 + 1
self.logger.info('done doing something' )
def some_function ():
module_logger.info('received a call to "some_function"' )
运行main_module.py
后,spam.log
文件中会记录详细的日志信息,包括各个操作的时间、模块名、日志级别和具体消息;当有错误发生时,控制台会输出错误级别的日志。
扩展 :在大型项目中,合理利用多模块日志可以清晰地追踪不同模块的运行状态。例如,在一个电商系统中,用户模块、订单模块、支付模块等都可以有各自的日志记录器,方便排查问题。在多模块开发时,为每个模块的日志记录器命名时,采用统一的命名规范,如项目名.模块名
的形式,这样在查看日志时能快速定位到日志来源。在电商系统中,假设项目名为ecommerce
,用户模块的日志记录器可命名为ecommerce.user
。示例代码如下:
import logging
user_logger = logging.getLogger('ecommerce.user' )
def register_user (username, password ):
user_logger.info(f'用户 {username} 开始注册' )
user_logger.info(f'用户 {username} 注册成功' )
def login_user (username, password ):
user_logger.info(f'用户 {username} 开始登录' )
user_logger.info(f'用户 {username} 登录成功' )
import logging
order_logger = logging.getLogger('ecommerce.order' )
def create_order (user_id, product_list ):
order_logger.info(f'用户 {user_id} 开始创建订单,商品列表: {product_list} ' )
order_logger.info(f'用户 {user_id} 订单创建成功' )
def cancel_order (order_id ):
order_logger.info(f'开始取消订单 {order_id} ' )
order_logger.info(f'订单 {order_id} 已取消' )
多线程中的日志记录
原理 :多线程记录日志无需特殊处理,各个线程的日志会交替输出。
示例 :
import logging
import threading
import time
def worker (arg ):
while not arg['stop' ]:
logging.debug('Hi from myfunc' )
time.sleep(0.5 )
def main ():
logging.basicConfig(level=logging.DEBUG, format ='%(relativeCreated)6d%(threadName)s %(message)s' )
info = {'stop' : False }
thread = threading.Thread(target=worker, args=(info,))
thread.start()
while True :
try :
logging.debug('Hello from main' )
time.sleep(0.75 )
except KeyboardInterrupt:
info['stop' ] = True
break
thread.join()
if __name__ == '__main__' :
main()
运行上述代码,控制台会交替输出主线程和工作线程的日志信息,展示不同线程的日志交替输出情况。
扩展 :在高并发的 Web 应用中,多线程日志记录有助于了解各个线程的执行情况,比如在处理多个用户请求时,每个请求可能由一个线程处理,通过日志可以查看每个线程的处理过程。在多线程日志记录时,为避免日志混乱,可在日志格式中加入线程 ID,方便区分不同线程产生的日志。以下是修改后的示例代码:
import logging
import threading
import time
def worker (arg ):
while not arg['stop' ]:
logging.debug(f'线程 {threading.get_ident()} - Hi from myfunc' )
time.sleep(0.5 )
def main ():
logging.basicConfig(level=logging.DEBUG, format ='%(relativeCreated)6d - %(thread)d - %(threadName)s - %(message)s' )
info = {'stop' : False }
thread = threading.Thread(target=worker, args=(info,))
thread.start()
while True :
try :
logging.debug(f'线程 {threading.get_ident()} - Hello from main' )
time.sleep(0.75 )
except KeyboardInterrupt:
info['stop' ] = True
break
thread.join()
if __name__ == '__main__' :
main()
import logging
logger = logging.getLogger('simple_example' )
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('spam.log' )
fh.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s' )
ch.setFormatter(formatter)
fh.setFormatter(formatter)
logger.addHandler(ch)
logger.addHandler(fh)
logger.debug('debug message' )
logger.info('info message' )
logger.warning('warn message' )
logger.error('error message' )
logger.critical('critical message' )
运行代码后,spam.log
文件会记录所有级别的日志,控制台则只会输出错误及以上级别的日志。
扩展 :
在实际项目中,还可以根据需求添加更多类型的处理器,如将日志发送到远程服务器的SocketHandler
,或者用于邮件通知的SMTPHandler
等。在使用多个处理器时,要注意处理器的优先级和日志级别设置,避免出现重复记录或记录丢失的情况。以下是添加SocketHandler
和SMTPHandler
的示例代码:
import logging
import logging.handlers
logger = logging.getLogger('advanced_example' )
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('advanced.log' )
fh.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
socket_handler = logging.handlers.SocketHandler('remote_server_ip' , logging.handlers.DEFAULT_TCP_LOGGING_PORT)
socket_handler.setLevel(logging.INFO)
smtp_handler = logging.handlers.SMTPHandler(
mailhost=('smtp.example.com' , 587 ),
fromaddr='your_email@example.com' ,
toaddrs=['admin@example.com' ],
subject='日志通知' ,
credentials=('your_email@example.com' , 'your_password' )
)
smtp_handler.setLevel(logging.CRITICAL)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s' )
fh.setFormatter(formatter)
ch.setFormatter(formatter)
socket_handler.setFormatter(formatter)
smtp_handler.setFormatter(formatter)
logger.addHandler(fh)
logger.addHandler(ch)
logger.addHandler(socket_handler)
logger.addHandler(smtp_handler)
logger.debug('debug message' )
logger.info('info message' )
logger.warning('warn message' )
logger.error('error message' )
logger.critical('critical message' )
在多个地方记录日志
原理 :通过配置不同的处理器和格式化器,可以将日志以不同格式写入控制台和文件,并且根据日志级别进行过滤。
示例
import logging
logging.basicConfig(level=logging.DEBUG, format ='%(asctime)s %(name)-12s%(levelname)-8s%(message)s' ,
datefmt='%m-%d %H:%M' , filename='/tmp/myapp.log' , filemode='w' )
console = logging.StreamHandler()
console.setLevel(logging.INFO)
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s' )
console.setFormatter(formatter)
logging.getLogger('' ).addHandler(console)
logging.info('Jackdaws love my big sphinx of quartz.' )
logger1 = logging.getLogger('myapp.area1' )
logger2 = logging.getLogger('myapp.area2' )
logger1.debug('Quick zephyrs blow, vexing daft Jim.' )
logger1.info('How quickly daft jumping zebras vex.' )
logger2.warning('Jail zesty vixen who grabbed pay from quack.' )
logger2.error('The five boxing wizards jump quickly.' )
运行后,控制台会输出 INFO 及以上级别的日志,/tmp/myapp.log
文件会记录 DEBUG 及以上级别的日志。
扩展 :可以根据项目需求进一步细化日志记录的规则,比如按日期分割日志文件,或者为不同模块的日志设置不同的输出路径。在按日期分割日志文件时,使用logging.handlers.TimedRotatingFileHandler
,可以方便地实现每天、每周或每月生成新的日志文件,便于管理和查找历史日志。示例代码如下:
import logging
from logging.handlers import TimedRotatingFileHandler
file_handler = TimedRotatingFileHandler('daily_log.log' , when='D' , interval=1 , backupCount=7 )
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s' ))
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s' ))
root_logger = logging.getLogger('' )
root_logger.setLevel(logging.DEBUG)
root_logger.addHandler(file_handler)
root_logger.addHandler(console_handler)
logging.debug('debug message' )
logging.info('info message' )
logging.warning('warn message' )
logging.error('error message' )
自定义处理级别
原理 :使用过滤器可以实现不同于标准级别处理方式的自定义处理。例如,将不同级别的消息发送到不同的目标。
示例
import json
import logging
import logging.config
def filter_maker (level ):
level = getattr (logging, level)
def filter (record ):
return record.levelno <= level
return filter
CONFIG = '''
{
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"simple": {
"format": "%(levelname)-8s - %(message)s"
}
},
"filters": {
"warnings_and_below": {
"()": "__main__.filter_maker",
"level": "WARNING"
}
},
"handlers": {
"stdout": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "simple",
"stream": "ext://sys.stdout",
"filters": ["warnings_and_below"]
},
"stderr": {
"class": "logging.StreamHandler",
"level": "ERROR",
"formatter": "simple",
"stream": "ext://sys.stderr"
},
"file": {
"class": "logging.FileHandler",
"formatter": "simple",
"filename": "app.log",
"mode": "w"
}
},
"root": {
"level": "DEBUG",
"handlers": [
"stderr",
"stdout",
"file"
]
}
}
'''
logging.config.dictConfig(json.loads(CONFIG))
logging.debug('A DEBUG message' )
logging.info('An INFO message' )
logging.warning('A WARNING message' )
logging.error('An ERROR message' )
logging.critical('A CRITICAL message' )
运行代码后,stdout.log
文件会记录 INFO 和 WARNING 级别的日志,stderr.log
文件会记录 ERROR 和 CRITICAL 级别的日志,app.log
文件会记录所有级别的日志。
扩展 :在复杂的项目中,自定义处理级别可以更好地控制日志的输出,比如在一个金融系统中,根据交易的重要性和风险级别来设置不同的日志处理规则。在自定义处理级别时,要确保过滤器函数的逻辑准确无误,避免因过滤错误导致重要日志丢失或错误输出。假设在金融系统中,交易金额大于 10000 元的操作记录为 CRITICAL 级别,示例代码如下:
import logging
import logging.config
def financial_filter (record ):
if hasattr (record, 'transaction_amount' ) and record.transaction_amount > 10000 :
record.levelno = logging.CRITICAL
record.levelname = logging.getLevelName(logging.CRITICAL)
return True
CONFIG = '''
{
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"financial": {
"format": "%(asctime)s - %(levelname)s - %(message)s"
}
},
"filters": {
"financial_transaction": {
"()": "__main__.financial_filter"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "DEBUG",
"formatter": "financial",
"filters": ["financial_transaction"]
},
"file": {
"class": "logging.FileHandler",
"level": "DEBUG",
"formatter": "financial",
"filename": "financial.log",
"filters": ["financial_transaction"]
}
},
"root": {
"level": "DEBUG",
"handlers": [
"console",
"file"
]
}
}
'''
logging.config.dictConfig(json.loads(CONFIG))
transaction_log = logging.getLogger('financial_transactions' )
transaction_log.info('交易金额5000元' , extra={'transaction_amount' : 5000 })
transaction_log.info('交易金额15000元' , extra={'transaction_amount' : 15000 })
日志配置服务器示例
原理 :通过日志配置服务器,可以动态修改日志配置。
示例
import logging
import logging.config
import time
import os
logging.config.fileConfig('logging.conf' )
t = logging.config.listen(9999 )
t.start()
logger = logging.getLogger('simpleExample' )
try :
while True :
logger.debug('debug message' )
logger.info('info message' )
logger.warning('warn message' )
logger.error('error message' )
logger.critical('critical message' )
time.sleep(5 )
except KeyboardInterrupt:
logging.config.stopListening()
t.join()
日志配置客户端(client.py),用于发送新的配置文件到服务器
import socket
import sys
import struct
with open (sys.argv[1 ], 'rb' ) as f:
data_to_send = f.read()
HOST = 'localhost'
PORT = 9999
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print ('connecting...' )
s.connect((HOST, PORT))
print ('sending config...' )
s.send(struct.pack('>L' , len (data_to_send)))
s.send(data_to_send)
s.close()
print ('complete' )
假设logging.conf
是初始的日志配置文件,运行server.py
启动日志配置服务器,然后可以通过运行client.py
并传入新的配置文件路径,动态修改服务器的日志配置。
[loggers]
keys =root,user_service,order_service,product_service
[handlers]
keys =consoleHandler,fileHandler
[formatters]
keys =simpleFormatter
[logger_root]
level =INFO
handlers =consoleHandler,fileHandler
[logger_user_service]
level =DEBUG
handlers =consoleHandler,fileHandler
qualname =user_service
propagate =0
[logger_order_service]
level =WARNING
handlers =consoleHandler,fileHandler
qualname =order_service
propagate =0
[logger_product_service]
level =ERROR
handlers =consoleHandler,fileHandler
qualname =product_service
propagate =0
[handler_consoleHandler]
class =StreamHandler
level =DEBUG
formatter =simpleFormatter
args =(sys.stdout,)
[handler_fileHandler]
class =FileHandler
level =DEBUG
formatter =simpleFormatter
args =('distributed_system.log' , 'a' )
[formatter_simpleFormatter]
format =%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
客户端发送新配置的代码(修改后的 client.py)
import socket
import sys
import struct
new_config_file = 'new_logging.conf'
with open (new_config_file, 'rb' ) as f:
data_to_send = f.read()
HOST = 'localhost'
PORT = 9999
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print ('connecting...' )
s.connect((HOST, PORT))
print ('sending config...' )
s.send(struct.pack('>L' , len (data_to_send)))
s.send(data_to_send)
s.close()
print ('complete' )
这样,通过日志配置服务器,不同的微服务可以根据业务需求设置不同的日志级别,方便进行问题排查和系统监控。
处理日志 handler 的阻塞
原理 :在 Web 应用等场景中,某些日志处理器(如SMTPHandler
、SocketHandler
)可能会阻塞线程。可以通过QueueHandler
和QueueListener
来解决这个问题。
示例
import queue
import logging
from logging.handlers import QueueHandler, QueueListener
from logging import StreamHandler
que = queue.Queue(-1 )
queue_handler = QueueHandler(que)
handler = StreamHandler()
listener = QueueListener(que, handler)
root = logging.getLogger()
root.addHandler(queue_handler)
formatter = logging.Formatter('%(threadName)s: %(message)s' )
handler.setFormatter(formatter)
listener.start()
root.warning('Look out!' )
listener.stop()
运行代码后,日志输出会显示生成事件的线程,避免了因日志处理阻塞线程的问题。
扩展 :
在异步代码中,使用这种方式可以避免网络或文件处理器阻塞事件循环,确保异步程序的性能。在处理日志 handler 阻塞时,要合理设置队列的大小,避免队列溢出导致日志丢失。以下是在异步代码中使用QueueHandler
和QueueListener
的示例:
import asyncio
import queue
import logging
from logging.handlers import QueueHandler, QueueListener
from logging import StreamHandler
que = queue.Queue(-1 )
queue_handler = QueueHandler(que)
handler = StreamHandler()
listener = QueueListener(que, handler)
root = logging.getLogger()
root.addHandler(queue_handler)
formatter = logging.Formatter('%(threadName)s: %(message)s' )
handler.setFormatter(formatter)
listener.start()
async def async_task ():
for _ in range (5 ):
logging.info('Async task is running' )
await asyncio.sleep(1 )
async def main ():
await asyncio.gather(async_task())
if __name__ == '__main__' :
try :
asyncio.run(main())
except KeyboardInterrupt:
listener.stop()
在这个示例中,异步任务async_task
中的日志记录通过QueueHandler
写入队列,QueueListener
在单独的线程中处理日志,避免了阻塞异步事件循环。
通过网络收发日志事件
原理 :在发送端,将SocketHandler
连接到根日志对象,将日志事件以未格式化的 pickle 形式发送到指定的网络地址和端口。在接收端,使用socketserver
模块设置接收器来处理接收到的日志事件。
示例
发送端(sender.py)
import logging
import logging.handlers
rootLogger = logging.getLogger('' )
rootLogger.setLevel(logging.DEBUG)
socketHandler = logging.handlers.SocketHandler('localhost' , logging.handlers.DEFAULT_TCP_LOGGING_PORT)
rootLogger.addHandler(socketHandler)
logging.info('Jackdaws love my big sphinx of quartz.' )
logger1 = logging.getLogger('myapp.area1' )
logger2 = logging.getLogger('myapp.area2' )
logger1.debug('Quick zephyrs blow, vexing daft Jim.' )
logger1.info('How quickly daft jumping zebras vex.' )
logger2.warning('Jail zesty vixen who grabbed pay from quack.' )
logger2.error('The five boxing wizards jump quickly.' )
接收端(receiver.py)
import pickle
import logging
import logging.handlers
import socketserver
import struct
class LogRecordStreamHandler (socketserver.StreamRequestHandler):
"""Handler for a streaming logging request.
This basically logs the record using whatever logging policy is
configured locally.
"""
def handle (self ):
"""
Handle multiple requests - each expected to be a 4-byte length,
followed by the LogRecord in pickle format. Logs the record
according to whatever policy is configured locally.
"""
while True :
chunk = self.connection.recv(4 )
if len (chunk) < 4 :
break
slen = struct.unpack('>L' , chunk)[0 ]
chunk = self.connection.recv(slen)
while len (chunk) < slen:
chunk = chunk + self.connection.recv(slen - len (chunk))
obj = pickle.loads(chunk)
record = logging.makeLogRecord(obj)
self.handleLogRecord(record)
def handleLogRecord (self, record ):
logger = logging.getLogger(record.name)
logger.handle(record)
if __name__ == '__main__' :
logging.basicConfig(level=logging.DEBUG)
server = socketserver.TCPServer(('localhost' , logging.handlers.DEFAULT_TCP_LOGGING_PORT), LogRecordStreamHandler)
print ('Starting logging server...' )
server.serve_forever()
先运行receiver.py
启动接收端,再运行sender.py
,接收端会处理发送端发送过来的日志记录,并按照本地的日志配置进行记录。
扩展:
在分布式系统中,通过网络收发日志事件可以实现集中式的日志管理,方便对整个系统的日志进行分析和监控。在通过网络收发日志事件时,要注意网络的稳定性和安全性,对传输的日志数据进行加密处理,防止数据泄露。以下是使用 SSL 加密传输日志数据的示例:
发送端(ssl_sender.py)
import logging
import logging.handlers
import ssl
rootLogger = logging.getLogger('' )
rootLogger.setLevel(logging.DEBUG)
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_cert_chain(certfile='client.crt' , keyfile='client.key' )
socketHandler = logging.handlers.SocketHandler('localhost' , logging.handlers.DEFAULT_TCP_LOGGING_PORT)
ssl_socketHandler = logging.handlers.SecureSocketHandler(socketHandler, context)
rootLogger.addHandler(ssl_socketHandler)
logging.info('Encrypted logging message' )
接收端(ssl_receiver.py)
import pickle
import logging
import logging.handlers
import socketserver
import struct
import ssl
class LogRecordStreamHandler (socketserver.StreamRequestHandler):
def handle (self ):
while True :
chunk = self.connection.recv(4 )
if len (chunk) < 4 :
break
slen = struct.unpack('>L' , chunk)[0 ]
chunk = self.connection.recv(slen)
while len (chunk) < slen:
chunk = chunk + self.connection.recv(slen - len (chunk))
obj = pickle.loads(chunk)
record = logging.makeLogRecord(obj)
self.handleLogRecord(record)
def handleLogRecord (self, record ):
logger = logging.getLogger(record.name)
logger.handle(record)
if __name__ == '__main__' :
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_cert_chain(certfile='server.crt' , keyfile='server.key' )
server = socketserver.TCPServer(('localhost' , logging.handlers.DEFAULT_TCP_LOGGING_PORT), LogRecordStreamHandler)
ssl_server = ssl.wrap_socket(server, server_side=True , ssl_context=context)
logging.basicConfig(level=logging.DEBUG)
print ('Starting encrypted logging server...' )
with ssl_server:
server.serve_forever()
在这个示例中,发送端和接收端通过 SSL 加密进行通信,提高了日志传输的安全性。
日志处理最佳实践
日志级别设置 :根据不同的环境(开发、测试、生产)设置合适的日志级别。开发环境中,可将日志级别设置为 DEBUG,方便获取详细的调试信息;生产环境中,一般设置为 INFO 或 WARNING,避免过多的日志信息影响系统性能。比如在一个 Web 应用开发时,开发阶段将日志级别设为 DEBUG,能输出函数的入参、出参等信息,帮助定位代码问题;上线到生产环境后,调整为 INFO 级别,只记录关键业务操作日志。如下是不同环境下设置日志级别的示例代码:
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.debug('This is a debug message for development' )
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info('This is an info message for production' )
日志格式规范 :采用统一且有意义的日志格式,包含时间戳、线程名、日志级别、模块名和日志消息等关键信息,便于快速定位问题和分析系统运行情况。以%(asctime)s - %(threadName)s - %(levelname)s - %(module)s - %(message)s
格式为例,能清晰看到日志产生的时间、所属线程、级别、来源模块及具体内容,方便排查多线程环境下的并发问题。以下是设置日志格式的代码示例:
import logging
logging.basicConfig(
level=logging.DEBUG,
format ='%(asctime)s - %(threadName)s - %(levelname)s - %(module)s - %(message)s' ,
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)
logger.debug('This is a debug message with formatted log' )
日志文件管理 :定期清理和归档日志文件,避免日志文件过大占用过多磁盘空间。可以结合系统的任务调度工具(如 Linux 的 Cron Job),按照一定的时间周期(如每周、每月)对日志文件进行压缩和备份。如每月 1 号凌晨 2 点,通过 Cron Job 执行压缩脚本,将上个月的日志文件压缩为.gz
格式保存。在 Python 中,使用logging.handlers.RotatingFileHandler
可以实现日志文件按大小切割,示例代码如下:
import logging
from logging.handlers import RotatingFileHandler
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
handler = RotatingFileHandler('app.log' , maxBytes=10 *1024 *1024 , backupCount=5 )
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s' )
handler.setFormatter(formatter)
logger.addHandler(handler)
异常处理与日志记录 :在代码中,对可能出现异常的地方进行捕获,并在捕获异常时记录详细的异常信息,包括异常类型、异常堆栈等,这对于排查问题至关重要。使用try - except
语句捕获异常,通过logging.exception()
记录异常,它会自动记录异常类型、堆栈信息等,方便追溯问题根源。示例代码如下:
import logging
logger = logging.getLogger(__name__)
try :
result = 1 / 0
except ZeroDivisionError:
logger.exception('An error occurred during division' )
实际项目案例
电商系统 :在一个电商项目中,使用多模块日志记录用户操作、订单处理、支付流程等关键环节的信息。当用户下单时,订单模块记录订单创建、支付状态更新等日志;支付模块记录支付请求、支付结果等日志。通过这些日志,开发团队可以快速定位订单处理过程中的问题,如支付失败的原因、订单状态异常等。同时,利用日志配置服务器,在系统出现问题时,可以动态调整日志级别,获取更详细的信息进行故障排查。例如,当某个地区的用户频繁出现支付失败问题时,运维人员通过日志配置服务器将支付模块的日志级别临时调整为 DEBUG,获取详细的支付请求和响应日志,快速定位到是支付接口的参数传递问题。假设电商系统使用 Flask 框架,以下是订单模块和支付模块的部分日志记录代码示例:
from flask import Flask
import logging
app = Flask(__name__)
order_logger = logging.getLogger('ecommerce.order' )
order_logger.setLevel(logging.INFO)
file_handler = logging.FileHandler('order.log' )
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s' )
file_handler.setFormatter(formatter)
order_logger.addHandler(file_handler)
@app.route('/create_order' , methods=['POST' ] )
def create_order ():
order_id = '12345'
user_id = 'user1'
order_logger.info(f'用户 {user_id} 创建订单,订单ID: {order_id} ' )
return 'Order created successfully'
@app.route('/update_order_status' , methods=['POST' ] )
def update_order_status ():
order_id = '12345'
new_status = 'paid'
order_logger.info(f'订单 {order_id} 状态更新为: {new_status} ' )
return 'Order status updated successfully'
if __name__ == '__main__' :
app.run()
from flask import Flask
import logging
app = Flask(__name__)
payment_logger = logging.getLogger('ecommerce.payment' )
payment_logger.setLevel(logging.INFO)
file_handler = logging.FileHandler('payment.log' )
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s' )
file_handler.setFormatter(formatter)
payment_logger.addHandler(file_handler)
@app.route('/payment_request' , methods=['POST' ] )
def payment_request ():
user_id = 'user1'
amount = 100.0
payment_logger.info(f'用户 {user_id} 发起支付请求,金额: {amount} ' )
return 'Payment request received'
@app.route('/payment_result' , methods=['POST' ] )
def payment_result ():
user_id = 'user1'
status ='success'
payment_logger.info(f'用户 {user_id} 支付结果: {status} ' )
return 'Payment result processed'
if __name__ == '__main__' :
app.run()
分布式爬虫系统 :在分布式爬虫项目中,多线程日志记录发挥了重要作用。每个爬虫线程负责抓取不同的网页,通过记录每个线程的日志,可以了解每个线程的抓取进度、是否遇到反爬虫机制等情况。并且,通过网络收发日志事件,将各个节点的日志集中收集到一个日志服务器上,便于对整个爬虫系统进行监控和管理。在处理日志 handler 阻塞时,采用QueueHandler
和QueueListener
的方式,确保爬虫线程的高效运行,避免因日志处理导致的性能瓶颈。比如,当爬虫遇到大量 403 禁止访问错误时,从集中收集的日志中能快速发现问题,分析是 IP 被封禁还是请求头设置不当等原因;同时,QueueHandler
和QueueListener
保证了在高并发抓取时,日志处理不会阻塞爬虫线程,维持系统的抓取效率。假设使用requests
库进行网页抓取,以下是分布式爬虫的部分代码示例:
import asyncio
import logging
import queue
import requests
from concurrent.futures import ThreadPoolExecutor
from logging.handlers import QueueHandler, QueueListener
from logging import StreamHandler
que = queue.Queue(-1 )
queue_handler = QueueHandler(que)
handler = StreamHandler()
listener = QueueListener(que, handler)
root = logging.getLogger()
root.addHandler(queue_handler)
formatter = logging.Formatter('%(threadName)s: %(message)s' )
handler.setFormatter(formatter)
listener.start()
def crawl (url ):
try :
response = requests.get(url)
if response.status_code == 200 :
logging.info(f'成功抓取 {url} ' )
else :
logging.warning(f'抓取 {url} 失败,状态码: {response.status_code} ' )
except Exception as e:
logging.error(f'抓取 {url} 时发生错误: {e} ' )
urls = ['http://example.com' , 'http://example2.com' , 'http://example3.com' ]
with ThreadPoolExecutor(max_workers=3 ) as executor:
executor.map (crawl, urls)
if __name__ == '__main__' :
try :
pass
except KeyboardInterrupt:
listener.stop()
总结
本文全面介绍了 Python 日志处理的各种技巧,涵盖了多模块、多线程日志使用,多种处理器和格式化器配置,自定义处理级别,日志配置服务器应用,处理日志阻塞以及网络收发日志事件等内容。同时,分享了日志处理的最佳实践,并结合电商系统、分布式爬虫系统等实际项目案例,展示了日志处理在真实场景中的应用。通过这些知识的学习,开发者可以更好地掌控程序的日志记录,提高程序的可维护性和可调试性。
TAG:Python 日志处理;多模块日志;多线程日志;日志处理器;日志格式化器;自定义日志级别;日志配置服务器;日志阻塞处理;网络日志收发;最佳实践;实际项目案例
官方文档 :Python 官方日志教程 ,提供丰富示例和详细说明,是深入学习的基础。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)