python_日志模块_logging

说明:

1.日志模块默认是直接输出到终端的,除非在 logging.basicConfig()的参数中使用filename,使用filename后,日志都会输入到filename对应的日志文件中,,

2.可以通过使用handler,将文件同时输出到终端和文件中,但是值得注意的是:

当logging.basicConfig设置的日志级别低于handler中设置的日志级别,handler中显示的日志级别也只会按照logging.basicConfig中设置的级别显示,

当logging.basicConfig设置的日志级别高于handler中设置的日志级别,handler中显示的日志级别根据自身(handler)中设置的级别显示,

3.logging多线程,多进程场景:

按照官方文档的介绍,logging 是线程安全的,也就是说,在一个进程内的多个线程同时往同一个文件写日志是安全的。但是(对,这里有个但是)多个进程往同一个文件写日志不是安全的。官方的说法是这样的:

Because there is no standard way to serialize access to a single file across multiple processes in Python. If you need to log to a single file from multiple processes, 
one way of doing this is to have all the processes log to a SocketHandler, and have a separate process which implements a socket server which reads from the socket and logs to file. 
(If you prefer, you can dedicate one thread in one of the existing processes to perform this function.)

有的人会说,那我不用多进程不就可以了。但是 Python 有一个 GIL 的大锁(关于 GIL 的纠葛可以看这里),使用多线程是没法利用到多核 CPU 的,大部分情况下会改用多进程来利用多核 CPU,因此我们还是绕不开不开多进程下日志的问题。

为了解决这个问题,可以使用 ConcurrentLogHandler,ConcurrentLogHandler 可以在多进程环境下安全的将日志写入到同一个文件,并且可以在日志文件达到特定大小时,分割日志文件。在默认的 logging 模块中,有个 TimedRotatingFileHandler 类,可以按时间分割日志文件,可惜 ConcurrentLogHandler 不支持这种按时间分割日志文件的方式。

重新修改下 handlers 中的 class。

logging.config.dictConfig({
    ...
    'handlers': {
        'file': {
            'level''DEBUG',
            # 如果没有使用并发的日志处理类,在多实例的情况下日志会出现缺失
            'class''cloghandler.ConcurrentRotatingFileHandler',
            # 当达到10MB时分割日志
            'maxBytes'1024 * 1024 * 10,
            # 最多保留50份文件
            'backupCount'50,
            # If delay is true,
            # then file opening is deferred until the first call to emit().
            'delay'True,
            'filename''logs/mysite.log',
            'formatter''verbose'
        }
    },
    ...
})
运行后可以发现,会自动创建一个.lock文件,通过锁的方式来安全的写日志文件。

 

 

1.以配置文件的方式使用logging模块

说明:使用logging.config.fileConfig("Logging.ini")载入后,启动程序报错.KeyError Formatters 在网上查了很久,说是日志路径的问题,结果就是路径问题

[loggers]
keys=root,infoLogger,errorlogger
 
[logger_root]
level=DEBUG
handlers=infohandler,errorhandler
 
[logger_infoLogger]
handlers=infohandler
qualname=infoLogger
propagate=0
 
[logger_errorlogger]
handlers=errorhandler
qualname=errorlogger
propagate=0
 
###############################################
 
[handlers]
keys=infohandler,errorhandler
 
[handler_infohandler]
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stdout,)
 
[handler_errorhandler]
class=FileHandler
level=ERROR
formatter=form01
args=('logs/mylog.log', 'a')
 
###############################################
 
[formatters]
keys=form01,form02
 
[formatter_form01]
format=%(asctime)s %(filename)s %(levelname)s  %(message)s
datefmt=%Y-%m-%d %H:%M:%S
 
[formatter_form02]
format=%(asctime)s %(filename)s %(levelname)s  %(message)s
datefmt=%Y-%m-%d %H:%M:%S
 
 
字段说明:
 
 
[loggers]
# 定义logger模块,root是父类,必需存在的,其它的是自定义。
# logging.getLogger(NAME)便相当于向logging模块注册了一种日志打印
# name 中用 . 表示 log 的继承关系
 
[handlers]
# 定义handler
[formatters]
# 定义格式化输出
  
[logger_root]
 
# 实现上面定义的logger模块,必需是[logger_xxxx]这样的形式
 
# [logger_xxxx] logger_模块名称
# level     级别,级别有DEBUG、INFO、WARNING、ERROR、CRITICAL
# handlers  处理类,可以有多个,用逗号分开
# qualname  logger名称,应用程序通过 logging.getLogger获取。对于不能获取的名称,则记录到root模块。
# propagate 是否继承父类的log信息,0:否 1:是
 
[handler_infohandler]
# [handler_xxxx]
# class handler类名
# level 日志级别
# formatter,上面定义的formatter
# args handler初始化函数参数
 
[formatter_form01]
 
# 日志格式
#--------------------------------------------------
使用方式:
from logging.config import fileConfig
 
fileConfig('loggin_config.ini')
logger=logging.getLogger('infoLogger')
logger.info('test1')
logger_error=logging.getLogger('errorhandler')
logger_error.error('test5')

 

2.在code中直接使用logging模块

 

# 模块级函数
#
# logging.getLogger([name]):返回一个logger对象,如果没有指定名字将返回root logger
# logging.debug()、logging.info()、logging.warning()、logging.error()、logging.critical():设定root logger的日志级别
# logging.basicConfig():用默认Formatter为日志系统建立一个StreamHandler,设置基础配置并加到root logger中
#
# Loggers
#
# Logger.setLevel(lel):指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高
# Logger.addFilter(filt)、Logger.removeFilter(filt):添加或删除指定的filter
# Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或删除指定的handler
# Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():可以设置的日志级别
# Handlers
#
# handler对象负责发送相关的信息到指定目的地。可以通过addHandler()方法添加多个多handler
# Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
# Handler.setFormatter():给这个handler选择一个格式
# Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象
# Formatters
#
# Formatter对象设置日志信息最后的规则、结构和内容,默认的时间格式为%Y-%m-%d %H:%M:%S,下面是Formatter常用的一些信息
 
# %(name)s Logger的名字
#
# %(levelno)s 数字形式的日志级别
#
# %(levelname)s 文本形式的日志级别
#
# %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
#
# %(filename)s 调用日志输出函数的模块的文件名
#
# %(module)s 调用日志输出函数的模块名
#
# %(funcName)s 调用日志输出函数的函数名
#
# %(lineno)d 调用日志输出函数的语句所在的代码行
#
# %(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
#
# %(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
#
# %(asctime)s 字符串形式的当前时间。默认格式是 年-月-日 时-分-秒,毫秒 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
#
# %(thread)d 线程ID。可能没有
#
# %(threadName)s 线程名。可能没有
#
# %(process)d       进程id
#
# %(processName)s   进程名
#
# %(message)s 用户输出的消息
 
阅读更多
个人分类: Python
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭