24 模块 日志模块 re模块

模块 日志模块 re模块

1 日志模块

1.1 日志

日志(Log)是一种追踪记录在程序运行过程中所发生的事件的方法。
通过记录和分析日志,可以了解一个系统是否处于正常运行状态,也可以在出现故障时快速定位问题根源。

1.1.1 作用
  1. 程序调试;
  2. 了解程序运行情况;
  3. 程序运行故障分析与问题定位;
  4. 做用户行为分析,实现改进业务。
1.1.2 日志内容

一条日志信息对应的是一个事件的发生,而一个事件通常需要包括以下几个内容:

  1. 事件发生时间
  2. 事件发生位置
  3. 事件的严重程度–日志级别
  4. 事件内容
1.2 日志级别
import logging

# 程序在调试过程中运行状态确认
# logging.debug(msg, *args, **kwargs)
logging.debug('调试')  # 级别:10

# 程序正常运行时的状态信息
logging.info('消息')  # 级别:20

# 警告消息,预测程序之后可能会发生问题,此时程序还处于正常工作状态
logging.warning('警告')  # 级别:30

# 错误消息,程序运行过程中发生了错误,部分功能已无法运行。  
logging.error('错误')  # 级别:40

# 严重错误消息,程序大部分功能已无法正常运行。
logging.critical('严重')  # 级别:50

默认日志输出级别为30,即warning。

1.3 日志基本配置

函数 logging.basicConfig(**kwargs) 用于调整日志级别、输出格式等
函数 logging.basicConfig()为日志系统建立一个默认的流处理器(StreamHandler),设置基础配置(如日志级别等)并加到root logger(根Logger)中。

import logging

LOG_FORMAT = "%(asctime)s %(name)s %(levelname)s %(pathname)s %(message)s
TIME_FORMAT = '%Y-%m-%d %H:%M:%S %p'

logging.basicConfig(
    # 1. 日志输出位置:终端 文件; 不指定,默认输出到终端
    filename=r'./access.log',

    # 2. 日志格式
    format=LOG_FORMAT,

    # 3. 时间格式
    datefmt=TIME_FORMAT,

    # 4、日志级别
    # critical => 50
    # error => 40
    # warning => 30 默认
    # info => 20
    # debug => 10
    level=10,
    # level=logging.DEBUG
)
logging.debug("msg1")
  1. logging.basicConfig()函数只有在第一次调用时会起作用,后续再次调用该函数时不会产生任何操作。
  2. 日志器(Logger)是有层级关系的,上面调用的logging模块级别的函数所使用的日志器是RootLogger类的实例,其名称为 root,它是处于日志器层级关系最顶层的日志器,且该实例是以单例模式存在的。
  3. 如果要记录的日志中包含变量数据,可使用一个格式字符串作为这个事件的描述消息(logging.debug、logging.info等函数的第一个参数),然后将变量数据作为第二个参数*args的值进行传递。
logging.warning('%s is %d years old.', 'Tom', 10)  # WARNING:root:Tom is 10 years old.
1.4 日志详细配置
1.4.2 logging日志模块四大组件
  1. 日志器 Logger
    提供了应用程序可一直使用的接口
  2. 处理器 Handler
    将logger创建的日志记录发送到合适的目的输出
  3. 过滤器 Filter
    提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录
  4. 格式器 Formatter
    决定日志记录的最终输出格式

日志器(logger)需要通过处理器(handler)将日志信息输出到目标位置;
日志器可以设置多个处理器,实现同一条日志以不同的格式输出到不同的地方;
每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志;
每个处理器(handler)都可以设置自己的格式器(formatter)。
settings.py

# 1. 定义三种日志输出格式,日志中可能用到的格式化串如下
# %(name)s Logger的名字 强调:%(name)s为getlogger时指定的名字
# %(levelno)s 数字形式的日志级别
# %(levelname)s 文本形式的日志级别
# %(pathname)s 调用日志输出函数的模块的完整路径名,相当于sys.argv[0]
# %(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
# %(message)s 用户输出的日志信息

# 2. 设置日志格式
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
                  '[%(levelname)s][%(message)s]'

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

test_format = '[%(asctime)s] %(message)s'

# 3. 日志配置字典
LOGGING_DIC = {
    'version': 1,
    'disable_existing_loggers': False,  # 默认值为True,表示禁用已经存在的logger
    # 配置文件中“disable_existing_loggers” 参数设置为 False;
    # 如果不设置为False,创建了 logger,然后你又在加载日志配置文件之前就导入了模块。logging.fileConfig 与 logging.dictConfig 默认情况下会使得已经存在的 logger 失效。那么,这些配置信息就不会应用到你的 Logger 上。

    # 日志输出格式
    'formatters': {
        'standard': {
            'format': standard_format
        },
        'simple': {
            'format': simple_format
        },
        'test': {
            'format': test_format
        },
    },
    
	# 日志过滤器
    'filters': {},

    # 日志接收者
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',  # 日志输出到流,将日志输出到屏幕上
            'formatter': 'simple'
        },
        'default': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',  # 日志输出到文件, 可以实现日志回滚
            'formatter': 'standard',
            'filename': 'a1.log',  # 日志文件
            'maxBytes': 1024 * 1024 * 5,  # 日志大小 5M
            'backupCount': 5,
            'encoding': 'utf-8',  # 日志文件的编码
        },
        'other': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',  # 日志输出到文件
            'formatter': 'test',
            'filename': 'a2.log',
            'encoding': 'utf-8',
        },
    },

    # 日志产生者
    'loggers': {
        # logging.getLogger(__name__)拿到的logger配置
        'logger1': {
            'handlers': ['default', 'console'],  
            # 指定日志的接收者,即log数据既写入文件又打印到屏幕
            
            'level': 'DEBUG',  
            # 日志级别关限制: loggers(第一层) ---> handlers(第二层)
            
            'propagate': False,  
            # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
        },
        'logger2': {
            'handlers': ['other', ],
            'level': 'ERROR ',
            'propagate': False,
        },
        '': {
            'handlers': ['other', ],
            'level': 'ERROR ',
            'propagate': False,
        },
    },
}

common.py
导入日志包

from logging import config  # 子模块
from logging import getLogger  # 函数

import settings

config包是logging包下的一个子包,logging包下的__init__.py中没有导入config包,因此不能仅通过导入config包来找到其子包config。

另一种导入方式

import logging.config
# 在导入模块过程中会先导入logging模块,再导入其子模块config
# 因此logging.getLogger也会一起导入,之后使用前缀logging.config.

print(logging)  # module 'logging'
print(logging.config)  # module 'logging.config'
print(logging.getLogger)  # function getLogger

获取日志对象
logging.getLogger()方法有一个可选参数name,该参数表示将要返回的日志器的名称标识,如果不提供该参数,则其值默认为root。
若以相同的name参数值多次调用getLogger()方法,将会返回指向同一个logger对象的引用。

def get_logger(log_type):
    logging.config.dictConfig(settings.LOGGING_DIC)
    return logging.getLogger(log_type)

interface.py

from lib import common

bank_logger = common.get_logger('bank')

bank_logger.info('消息1')
bank_logger.info('消息2')

关于logger的层级结构与有效等级的说明:

  1. logger的名称是一个以’.‘分割的层级结构,每个’.‘后面的logger都是’.'前面的logger的children,例如,有一个名称为 foo 的logger,其它名称分别为 foo.bar, foo.bar.baz 和 foo.bam都是 foo 的后代。
  2. logger有一个"有效等级(effective level)"的概念。如果一个logger上没有被明确设置一个level,那么该logger就是使用它parent的level;如果它的parent也没有明确设置level则继续向上查找parent的parent的有效level,依次类推,直到找到个一个明确设置了level的祖先为止。需要说明的是,root logger总是会有一个明确的level设置(默认为 WARNING)。当决定是否去处理一个已发生的事件时,logger的有效等级将会被用来决定是否将该事件传递给该logger的handlers进行处理。
  3. child loggers在完成对日志消息的处理后,默认会将日志消息传递给与它们的祖先loggers相关的handlers。因此,我们不必为一个应用程序中所使用的所有loggers定义和配置handlers,只需要为一个顶层的logger配置handlers,然后按照需要创建child loggers就可足够了。我们也可以通过将一个logger的propagate属性设置为False来关闭这种传递机制。

如何使用日志总结:

  1. 在配置文件settings.py中创建log字典,设置级别,
  2. 在common.py中导入logging.config包,设置logger方法,
  3. 在不同接口文件中设置logger,在需要的位置调用并打印不同级别日志信息。
1.5 日志名的命名

上面的例子中日志名为logger1,logger2,会被日志输出格式中的**%(name)s接收。
日志名是用于区分日志种类(日志业务归属)的。
如果大部分的日志的handlers,level,propagate等参数都相同,仅仅日志名不同,可以
不指定日志名**,之后如果getLogger接收的日志名不存在,则会去找这个空日志名。

1.6 日志轮转

不能任由日志文件不断增大,因此需要在特定时机对日志文件进行分隔,将日志文件中的内容移到备份文件中存储。

'default': {
    'level': 'DEBUG',
    'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件, 日志轮转
    'formatter': 'standard',
    'filename': 'a1.log',  # 日志文件
    'maxBytes': 1024 * 1024 * 5,  # 日志大小到达5M后进行轮转
    'backupCount': 5,  # 保存的备份文件最大数量
    'encoding': 'utf-8',  # 日志文件的编码
},

当日志文件a1.log接收一条日志后体积超过5M后,会将所有内容移到备份文件a1.log.1中,之后继续在日志文件a1.log中接收新日志。

2 软件开发目录规范补充

  1. 如果程序的入口文件start.py在项目的bin文件夹下,需要配置环境变量,将项目的根目录存入配置环境中。
import os
import sys

BASE_DIR = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASE_DIR)
  1. 如果入口文件start.py在项目的根目录下,则无需配置环境变量。
    sys.path的第一个文件夹默认是当前执行文件所在的文件夹,即此时环境变量中已经存在项目的根目录了。

3 re模块

3.1 正则

正则是将一些具有特殊含义的符号组合到一起(正则表达式)来描述一类字符串的方法。
正则表达式则描述了一种字符串匹配的模式,可以用来检查检查一个字符串是否与某种模式匹配。

3.2 常用匹配模式

在这里插入图片描述

3.3 函数findall

re.findall(pattern, string, flags=0)
搜索string,以列表形式返回全部匹配的子串。

import re

# 匹配一个字符
res = re.findall('\w', '_abc123_/*- *+')  # \w 匹配字母,数字或下划线
print(res)  # ['_', 'a', 'b', 'c', '1', '2', '3', '_']

res = re.findall('\W', '_abc123/*- *+')  # \w 匹配非 字母,数字或下划线
print(res)  # ['/', '*', '-', ' ', '*', '+']  包括空格

res = re.findall('\s', 'a\tb\fc\nd\re ')  # 匹配空白字符
print(res)  # ['\t', '\x0c', '\n', '\r', ' ']

res = re.findall('\S', 'a\tb\fc\nd\re ')  # 匹配非空字符
print(res)  # ['a', 'b', 'c', 'd', 'e']

# 匹配字符字串
res = re.findall('ab', ' abcdeab ab')  # 匹配相同的字符
print(res)  # ['ab', 'ab', 'ab']

res = re.findall('\Aab', ' abcdeab ab')  # 匹配字符串开头部分
print(res)  # []

res = re.findall('^abc$', 'abc')  # \w 匹配字母,数字或下划线
print(res)  # ['abc']

# 重复匹配
res = re.findall('a.b', 'a\nb a\tb')
print(res)  # ['a\tb'] .能匹配除了\n以外的一个字符

res = re.findall('a.b', 'a\nb a\tb', re.DOTALL)
print(res)  # ['a\nb', 'a\tb'] 在此模式下.可以匹配所有字符,包括\n
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值