六、日志模块

目录

  一、日志级别

  二、默认级别

  三、logging模块指定全局配置

  四、logging模块的Formatter,Handler,Logger,Filter对象

  五、Logger与Handler的级别

  六、Logger的继承

  七、日志应用

 

1.日志级别

CRITICAL = 50 #严重
ERROR = 40    #错误
WARNING = 30 #警告
INFO = 20        #信息
DEBUG = 10    #调试
NOTSET = 0 #不设置

2.默认级别为warning ,级别30,默认打印到终端

#默认级别是显示30及以上
logging.debug('调试debug')  #level 10
logging.info('消息info')    #level 20
logging.warning('警告warn')#level 30
logging.error('错误error')  #level 40
logging.critical('严重critical') #level 50

'''
输出结果:
WARNING:root:警告warn
ERROR:root:错误error
CRITICAL:root:严重critical
'''

3.为logging模块指定全局配置,针对所有Logger有效,控制打印到文件中

#介绍
可在logging.baseConfig()函数中通过具体参数来更改logging模块默认行为,可用参数有: filename:用指定的文件名创建FileHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。 filemode:文件打开方式,在指定了filename时使用这个参数,默认值为
'a',还可指定为‘w’ format:指定handler使用的日志显示格式 datefmt:指定日期时间格式 level:设置rootlogger(后边会讲解具体概念)的日志级别 stream:用指定的stream创建StreamHandler.可以指定输出到sys.stderr,sys.stdout或者文件,默认为sys.stderr。若同时列出了filename和stream俩个参数,则stream参数会被忽略 #格式 %(name)s:Logger的名字,并非用户名 %(levelno)s:数字形式的日志级别 %(levelname)s:文本形式的日志级别 %(pathname)s:调用日志输出函数的模块的完整路径名,可能没有 %(module)s:调用日志输出函数的模块名 %(funcName)s:调用日志输出函数的函数名 %(lineno)d:调用日志输出函数的语句所在的代码行 %(created)f:当前时间,用UNIX标准的表示时间的浮点数表示 %(relativeCreated)d:输出日志信息时的,自Logger创建以来的毫秒数 %(asctime)s:字符串形式的当前时间。默认格式'2018-04-10 19:12:12,898'。逗号后面的是毫秒 %(thread)d:线程ID。可能没有 %(threadName)s:线程名,可能没有 %(process)d:进程ID,可能没有 %(message)s:用户输出消息
import logging

#默认就是a模式,不用指定也行
logging.basicConfig(
    filename='access.log',
    filemode='a',
    format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
    datefmt='%Y-%m-%d %H-%M-%S %p',
    level=10
)

#默认级别是显示30及以上
logging.debug('debug')  #level 10
logging.info('info')    #level 20
logging.warning('warn')#level 30
logging.error('error')  #level 40
logging.critical('critial') #level 50


'''
结果:在access.log文件里:
2018-04-10 17-07-12 PM - root - DEBUG -日志模块logging: debug
2018-04-10 17-07-12 PM - root - INFO -日志模块logging: info
2018-04-10 17-07-12 PM - root - WARNING -日志模块logging: warn
2018-04-10 17-07-12 PM - root - ERROR -日志模块logging: error
2018-04-10 17-07-12 PM - root - CRITICAL -日志模块logging: critial
'''
示例代码及结果

 

4.logging模块的Formatter,Handler,Logger,Filter对象

  原理图:

 

import logging
#1.Logger对象:产生日志
logger1 = logging.getLogger('访问日志')   #名字无所谓,fmt格式里%(name)s
#2.Filter对象:过滤日志功能,几乎不用

#3.Handler对象:负责接收Logger传过来的日志,进行日志格式化,可以打印到终端,也可以打印到文件
sh=logging.StreamHandler()   #往终端打印
fh1=logging.FileHandler('s1.log',encoding='utf-8')
fh2=logging.FileHandler('s2.log',mode='w',encoding='utf-8')
#4.Formatter对象:日志格式
formatter1=logging.Formatter(
    fmt='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
    datefmt='%Y-%m-%d %H-%M-%S %p',
)
formatter2=logging.Formatter(
    fmt='%(asctime)s : %(message)s',
    datefmt='%Y-%m-%d %H-%M-%S %p',
)

#5. 为Handler绑定日志格式
sh.setFormatter(formatter1)
fh1.setFormatter(formatter2)
fh2.setFormatter(formatter1)

#6.为Logger绑定handler
logger1.addHandler(sh)
logger1.addHandler(fh1)
logger1.addHandler(fh2)

#7.设置日志级别,Logger与Handler都可以设置,Logger对象设置的级别<=Handler对象设置级别
logger1.setLevel(10)
sh.setLevel(40)

#8.测试
logger1.debug('测试一下')
logger1.info('正常信息')
logger1.warning('警告信息')
logger1.error('错误信息')
logger1.critical('严重信息')

5 .Logger与Handler的级别

  对于上面的第7点,已经有说明一个结论,logger是第一级过滤,然后才能到handler,我们可以给logger和handler同时设置level,但是需要知道的是Logger的级别如果比handler高,那实际上handler的级别过滤是没有效果的

6.Logger的继承(了解)

import logging

formatter=logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S %p',)

ch=logging.StreamHandler()
ch.setFormatter(formatter)


logger1=logging.getLogger('root')
logger2=logging.getLogger('root.child1')
logger3=logging.getLogger('root.child1.child2')


logger1.addHandler(ch)
logger2.addHandler(ch)
logger3.addHandler(ch)
logger1.setLevel(10)
logger2.setLevel(10)
logger3.setLevel(10)

logger1.debug('log1 爷爷')
logger2.debug('log2 爸爸')
logger3.debug('log3 孙子')


'''
2018-04-10 20:17:32 PM - root - DEBUG -日志模块logging:  log1 爷爷
2018-04-10 20:17:32 PM - root.child1 - DEBUG -日志模块logging:  log2 爸爸
2018-04-10 20:17:32 PM - root.child1 - DEBUG -日志模块logging:  log2 爸爸
2018-04-10 20:17:32 PM - root.child1.child2 - DEBUG -日志模块logging:  log3 孙子
2018-04-10 20:17:32 PM - root.child1.child2 - DEBUG -日志模块logging:  log3 孙子
2018-04-10 20:17:32 PM - root.child1.child2 - DEBUG -日志模块logging:  log3 孙子
'''

7.日志应用

  可将Loggers,Handlers,Formats封装成字典,方便调用

import os
import logging.config

#1.定义三种日志输出格式
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s]\
                    [%(filename)s:%(lineno)d][%(levelname)s][%(message)s]' #其中name为getlogger指定的名字

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s]:%(lineno)d%(message)s'
id_simple_format = '[%(levelname)s[%(asctime)s] %(message)s]'

#log文件的目录
logfile_dir = os.path.dirname(os.path.abspath(__file__))
logfile_name='access.log'   #log文件名

#r如果不存在定义的日志目录就创建一个
if not os.path.isdir(logfile_dir):
    os.mkdir(logfile_dir)

#log文件的全路径
logfile_path = os.path.join(logfile_dir,logfile_name)

#log配置字典
LOGGING_DIC={
    'version':1,
    'disable_existing_loggers':False,
    'formatters':{
        'standard':{
            'format':standard_format
        },
        'simple':{
            'format':simple_format
        },
    },
    'filters':{},
    'handlers':{
        #打印到终端的日志
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',#打印到屏幕
            'formatter':'simple'
        },
        #打印到文件的日志里,手机info及以上的日志
        'default':{
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler', #保存到文件
            'formatter':'standard',
            'filename':logfile_path,    #日志文件
            'maxBytes':1024*1024*5,      #日志大小5M
            'encoding':'utf-8',         #日志文件的编码,再也不用担心中文log乱码了
        },
    },
    'loggers':{
        #logging.getLogger(__name__)拿到的logger配置
        '':{
            'handlers':{'default','console'},    #这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level':'DEBUG',
            'propagate':True,   #向上(更高level的logger)传递
        },
    },
}

def load_my_logging_cfg():
    logging.config.dictConfig(LOGGING_DIC)  #导入上面定义的logging配置
    logger = logging.getLogger(__name__)   #生成一个log实例
    logger.info('It works!')    #计入该文件的运行状态

if __name__ == '__main__':
    load_my_logging_cfg()
#关于如何拿到logger对象的详细解释

#1.有了上述方式,好处:所有与logging模块有关的配置都写到字典中就可以了,更加清晰,方便管理

#2.我们需要解决的问题是:
    2.1 从字典加载配置:logging.config.dictConfig(LOGGING_DIC)

    2.2 拿到logger对象来产生日志
    logger对象都是配置到字典的loggers键对应的子字典中的
    按照我们队logging模块的理解,要想获取某个东西都是通过名字,也就是key来获取的
    于是我么要获取不同的logger对象就是logger=logging.getLogger('loggers子字典的key名')

    但问题是:如果我们想要不同logger名的logger对象都共用一段配置,那么肯定不能再loggers子字典中定义n个key 
    'loggers':{
        #logging.getLogger(__name__)拿到的logger配置
        'l1':{
            'handlers':{'default','console'},    #这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level':'DEBUG',
            'propagate':True,   #向上(更高level的logger)传递
        },
        'l2': {
            'handlers': {'default', 'console'},  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },
        'l3': {
            'handlers': {'default', 'console'},  # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level': 'DEBUG',
            'propagate': True,  # 向上(更高level的logger)传递
        },
    }

#我们的解决方式是,定义一个空的key
    'loggers':{
        #logging.getLogger(__name__)拿到的logger配置
        '':{
            'handlers':{'default','console'},    #这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level':'DEBUG',
            'propagate':True,   #向上(更高level的logger)传递
        },

这样我们再取logger对象时
logging.getLogger(__name__),不同的文件__name__不同,这保证了打印日志时标识信息不同,但是拿着该名字去loggers里找key名时却发现找不到,于是默认使用key=''的配置    

  

  符合软件定义规范的日志记录功能实例

#在ATM/bin/start.py文件
#入口:启动文件

import sys,os
#BASE_DIR是项目根目录,os.path.dirname取目录,os.path.abspath取文件绝对路径,__file__程序名
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
#将项目根目录加入环境变量
sys.path.append(BASE_DIR)

from core import src

if __name__ =='__main__':
    src.run()

========================================================
#在ATM/conf/文件里的setting.py

#配置文件
import os
#BASE_DIR是项目根目录
BASE_PATH=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
#路径拼接,结果E:\python3_file\Pycharm3\ATM\log\access.log
LOG_PATH=os.path.join(BASE_PATH,'log','access.log')
DB_PATH=os.path.join(BASE_PATH,'db','user')

#1.定义三种日志输出格式
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s]\
[%(filename)s:%(lineno)d][%(levelname)s][%(message)s]' #其中name为getlogger指定的名字

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s]:%(lineno)d%(message)s'
id_simple_format = '[%(levelname)s[%(asctime)s] %(message)s]'

LOGGING_DIC={
    'version':1,
    'disable_existing_loggers':False,
    'formatters':{
        'standard':{
            'format':standard_format
        },
        'simple':{
            'format':simple_format
        },
    },
    'filters':{},
    'handlers':{
        #打印到终端的日志
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',#打印到屏幕
            'formatter':'simple'
        },
        #打印到文件的日志里,手机info及以上的日志
        'default':{
            'level':'DEBUG',
            'class':'logging.handlers.RotatingFileHandler', #保存到文件
            'formatter':'standard',
            'filename':LOG_PATH,    #日志文件
            'maxBytes':1024*1024*5,      #日志大小5M
            'encoding':'utf-8',         #日志文件的编码,再也不用担心中文log乱码了
        },
    },
    'loggers':{
        #logging.getLogger(__name__)拿到的logger配置
        '':{
            'handlers':{'default','console'},    #这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
            'level':'DEBUG',
            'propagate':True,   #向上(更高level的logger)传递
        },
    },
}

========================================================
#在ATM/core/src.py文件

#程序核心代码
#调用ATM/lib/common文件里的日志记录函数
from lib import common
#调用ATM/lib/common文件里的sql数据库处理函数
from lib import sql

def shop():
    print ('购物ing....')

def check_balance():
    print ('查看余额....')
    #去数据库函数执行查询余额sql命令
    res=sql.execute('select balance from user where id=3')
    print (res)

def transfer():
    print ('转账....')
    log_msg='bank给lisl转了1亿元'
    #调用日志功能记录日志
    logger1=common.load_my_logging_cfg('hello')
    logger1.info(log_msg)


#主函数
def run():
    msg='''
        1. 购物
        2.查看余额
        3.转账
    '''
    while True:
        print (msg)
        choice= input('>>>:').strip()
        if choice == '1':
            shop()
        elif choice =='2':
            check_balance()
        elif choice =='3':
            transfer()
        else:
            print ('输入错误')

========================================================
#在ATM/db/user文件里

lisl,18,male,100
zhangsan,28,female,10000

========================================================
#在ATM/lib/common.py文件里:

#自定义库
from conf import setting
import logging.config

def load_my_logging_cfg(name):
    logging.config.dictConfig(setting.LOGGING_DIC)  #导入上面定义的logging配置
    logger = logging.getLogger(name)   #生成一个log实例
    return logger

========================================================
在ATM/lib/sql.py文件里:
#去ATM/conf/setting.py文件里找到DB_Path的路径
from conf import setting
def execute(sql):
    print ('我是数据库处理函数')


========================================================
#在ATM/log/access.log文件里
[2018-04-11 16:57:21,879][MainThread:67008][task_id:hello][src.py:21][INFO][bank给lisl转了1亿元]

 

  

posted @ 2018-04-11 17:55  森林326  阅读(619)  评论(0编辑  收藏  举报