4.1
今日内容
- 日志模块的使用
- re模块:正则表达式
- 软件开发目录规范
- 内置函数
日志模块
import logging
logging.debug('调试debug')
# 10
logging.info('消息info')
# 20
logging.warning('警告warn')
# 30
# 有风险,但是还能运行
logging.error('错误error')
# 40
# 出现错误,程序无法运行
logging.critical('严重critical')
# 50
# 严重的错误,崩溃
'''
WARNING:root:警告warn
ERROR:root:错误error
CRITICAL:root:严重critical
'''
日志自下而上匹配
可以使用level指定日志级别,在到达设置的级别以上才会输出,默认级别是warning
logging.basicConfig()
在里面可以使用多种参数,自定义纪录日志
配置日志格式
# 日志格式
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
# 时间格式
datefmt='%Y-%m-%d %H:%M:%S %p',
level = 10,
)
logging.debug('调试debug') # 10
logging.info('消息info') # 20
# 2020-04-01 09:30:40 AM - root - DEBUG -4.1: 调试debug
# 2020-04-01 09:30:40 AM - root - INFO -4.1: 消息info
日志输出位置
日志可以指定输出位置,输出到终端或纪录到文件,不指定则默认输出到终端
filename='access.log', # 不指定,默认打印到终端
往文件里面写内容默认是操作系统的字符编码,默认打开可能乱码
日志配置字典
standard_format = '%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s'
LOGGING_DIC = {
# 多个日志的格式
'formatters':{
'standard':{
'format':standard_format
},
'simple':{...},
'test':{...},
},
'handlers':{
'console':{
'level': 10,
},
'default':{},
'others':{},
}
'loggers':{
'kkk':{
'handlers':['consle','other'],
'level':'DEBUG', # y用于过滤错误级别,
'propagate':False, # 通常情况不需要的功能,改成False就好
},
'bbb':{
'handlers':['consle','other'],
'level':'ERROR', # y用于过滤错误级别,
'propagate':False, # 通常情况不需要的功能,改成False就好
},
},
}
自定义日志的格式,把格式赋值给变量名,变量名存在字典里
handlers:日志的接收者,可以定义不同的接收者:文件1,文件2,终端。。。
loggers:日志的产生者,产生的日志传递给handler
接下来,拿到日志的生产者,即loggers来产生日志,先导入日志配置字典
import settings
import loggings
from loggings import config
config.dicConfig(settings.LOGGING_DIC)
logger1 = getLogger('kkk')
logger1.info('info日志')
logger1.debug('debug日志')
日志名的命名
日志名是区分日志业务归属的一种非常重要的标识,把loggers中的日志名改成 终端提示,用户交易一类的日志名
对于像使用同一个logger的日志方法,但是不想使用logger指定的日志名:使用空名字字典
- 建一个空名字的字典,在使用get.Logger() 里传入一个logger里不存在的名字,则会以这个传入的名字跟空名字的日志绑定,作为空名字的日志名写入日志
日志轮转
当日志文件增大,运行效率会降低,因此要定期对日志文件进行切分:指定大小,当文件到达一定的大小时自动切分
设置maxBytes
和backupCount
的大小
软件编写目录
强调:getlogger应该写在common.py,在src里拿来用
把start.py放在根目录下,环境变量会自动加载当前文件夹,也就是根目录,就不用用os.path.dirname 找到根目录了,bin文件夹可以删掉了
re模块
正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法。或者说:正则就是用来描述一类事物的规则
使用 \w
等参数,小写表示取,大写表示取反
import re
print(re.findall('\w','abc123_*()/*-'))
# \w :字母,数字,下划线
# ['a', 'b', 'c', '1', '2', '3', '_']
print(re.findall('\W','abc123_*()/*-'))
# 大写W,取反,匹配非数字字母下划线
print(re.findall('\s','abc\n 123_*()/*-'))
# 匹配空白字符
print(re.findall('\d','abc123_*()/*-'))
# 匹配所有数字
print(re.findall('\Aabc','abc123_*()/*-'))
# 从头开始匹配,匹配到abc,就马上返回abc,如果开头不是abc,结果为空
print(re.findall('\Z/*-','abc123_*()/*-'))
# 从结尾开始匹配
print(re.findall('abc','abc123abc_*()/*-'))
# 匹配所有的abc,返回一个列表
#################################
print(re.findall('^abc','abc123_*()/*-'))
# 匹配开头
print(re.findall('$abc','abc123_*()/*-'))
# 匹配结尾
# 重复匹配,与上面的\w,\d等搭配使用
# .点:匹配任意一个字符,除了\n之外
print(re.findall('a.b','a1\nbc123_*()/aaab*-'))
# ['aab']
# * 星 左侧字符重复0次或无穷次,性格贪婪
# + 加号 左侧字符重复1次或无穷次,性格贪婪
# ? 问号 左侧字符重复0次或1次,性格贪婪
# {n,m} :左侧字符重复n次到m次,{n}单独一个n表示只出现一次
重复匹配 * + ? {}
匹配指定字符
print(re.findall('a[012345]b',str,re.DOTALL))
神游整理
日志
四个角色
日志分为四个角色:logger,filter,handler,format
- logger:产生日志,操作的对象
- filter:基本不用
- handler:接收logger传来的日志,可以打印到终端或文件
- format:日志的格式
logger产生日志后可以交给多个handler,每个handler需要捆绑一个格式,按照这个格式打印
基本流程
最普通的用法:使用logging模块生成一个日志,要有以下几步骤:
-
导入模块
-
造logger:
logger1 = logging.getLogger('日志名字')
获取一个logger对象,可以为这个日志指定一个名字 -
造handler:
-
sh = logging.StreamHandler()
打印到终端 -
fh = logging.FileHandler('access.log',encoding='utf-8')
打印到文件,可以指定字符编码
-
-
生成日志格式
formatter1 = logging.Formatter( fmt = '%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S %p', )
-
为handler绑定日志格式
sh.setFormatter(formatter1 = logging.Formatter()
-
为logger绑定handler
logger1.addHandler(sh)
-
测试日志
logger1.debug('debug') logger1.info('info') # 先判断日志级别是否能通过logger的关卡,通过了则传给handler,handler关卡也通过了,则debug和info作为levelname,sh和fh作为name,括号内的字符串作为message,在终端和文件中纪录日志
日志级别
在使用logger的debug方法时,不同的方法有不同的级别,并非所有的信息都会被录入日志
- 默认为30
- 可以通过
logger.setLevel(20)
或handler.setLevel(20)
改变 - 可以通过日志配置字典或logging.BasicConfig() 中指定
level
改变
配置字典
非常重要
创造logger,handler等等都是在做初始化操作。可以通过把操作写进字典,自动加载
import os
# 定义三种日志格式
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'
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# log文件的目录
LOG_PATH = os.path.join(BASE_DIR,'a1.log')
# 拼接路径,得到日志文件的地址,下面一会要用到
# 日志配置字典
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
# 不用管上面的
# 四种角色之 格式:已经在上面写好了,这里直接使用变量名
# formatters 名字是固定的,standard,simple这些是自己起的名字
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
'test': {
'format': test_format
},
},
'filters': {}, # 不用管
# 四大角色之 handler,控制接收的日志级别,往哪里打印
# handlers名字固定,consle名字是自己起的
'handlers': {
#打印到终端的日志
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'simple'
},
#打印到文件的日志,收集info及以上的日志
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,日志轮转
'formatter': 'standard',
# 可以定制日志文件路径
'filename': 'a1.log', # 日志文件
'maxBytes': 1024*1024*5, # 日志大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
'other': {
'level': 'DEBUG',
'class': 'logging.FileHandler', # 保存到文件
'formatter': 'test',
'filename': 'a2.log',
'encoding': 'utf-8',
},
},
'loggers': {
#logging.getLogger(__name__)拿到的logger配置
'': {
'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'level': 'DEBUG', # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)
'propagate': False, # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
},
'专门的采集': {
'handlers': ['other',],
'level': 'DEBUG',
'propagate': False,
},
},
}
日志配置字典LOGGING_DIC
其中loggers不指定名字,那么使用时没有找到指定名字的日志都使用这个空名字的logger
在项目中使用配置字典
-
配置字典写在settings.py
-
使用日志字典的方法放在lib下的common.py里
# common.py
import os
import logging
from conf import settings
def logger_handle(logger_name):
logging.config.dictConfig(settings.LOGING_DIC) # 从settings加载配置字典
logger = logging.getLogger(logger_name) # 生成一个logger对象,使用传入的参数作为logger的名字:配置字典里有这个名字则使用这个名字的logger,没有则使用空名字的logger
return logger
# 使用的场景,src.py...
import logging.config
from lib import common
def transfer():
logger = common.logger_handle('转账')
logger.debug('debug_msg')
正则
用特殊的符号组合在一起来描述字符串的方法,正则要做的事是通过字符的方法去匹配字符
特殊符号
以 re.findall(符号,要匹配的字符串)
方法为例
import re
#\w与\W
print(re.findall('\w','hello egon 123')) # 匹配字母数字下划线 ['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3']
print(re.findall('\W','hello egon 123')) # 非字母数字下划线 [' ', ' ']
#\s与\S
print(re.findall('\s','hello egon 123')) # 匹配空白字符和 \n,\t[' ', ' ', ' ', ' ']
print(re.findall('\S','hello egon 123')) # 匹配非空白字符['h', 'e', 'l', 'l', 'o', 'e', 'g', 'o', 'n', '1', '2', '3']
#\n \t都是空,都可以被\s匹配
print(re.findall('\s','hello \n egon \t 123')) #[' ', '\n', ' ', ' ', '\t', ' ']
#\n与\t
print(re.findall(r'\n','hello egon \n123')) #['\n']
print(re.findall(r'\t','hello egon\t123')) #['\n']
#\d与\D
print(re.findall('\d','hello egon 123')) # 数字['1', '2', '3']
print(re.findall('\D','hello egon 123')) # 非数字['h', 'e', 'l', 'l', 'o', ' ', 'e', 'g', 'o', 'n', ' ']
#\A与\Z
print(re.findall('\Ahe','hello egon 123'))
# 从字符串第一个字符开始匹配 ['he']
# 使用 ^ 代替
print(re.findall('123\Z','hello egon 123'))
# 从字符串最后开始倒着匹配 ['he'],
# 可以使用 $ 代替
#^与$
print(re.findall('^h','hello egon 123')) #['h']
print(re.findall('3$','hello egon 123')) #['3']
重复匹配
.,*,+,?
,一般与上面的特殊字符配合使用
真的绕
-
. 点号,用于以一个格式匹配任意单个字符,斜杠n除外
print(re.findall('a.','avxx sss1 as')) # 以a.格式匹配,得到a和a后面的一个字符 ['av', 'as'] print(re.findall('a.b','a=bxx sss1 asb')) # 以a b 格式匹配,得到a b和中间的字符 ['a=b', 'asb'] # a.b 不会输出 a\nb,除非在后面再加一个参数re.DOTALL 让re模块识别所有字符
-
?号,匹配?左边的字符串,一个一个与被匹配的字符串中的字符做比较,只要第一个字符匹配上,就输出第一个字符,后面的字符如果也一样匹配上了,就一起输出,没有匹配上就不会输出。
最后输出的只能是?问好左边的字符
print(re.findall('axx?','ax=bxx adxsss1 axxxasb')) # 后面的字符串与axx比对,遇到'ax=',前两个比对上了,则输出'ax',以此类推 # ['ax', 'axx']
-
号 ,左边的字符可以是0或着无穷多个
print(re.findall('ax*','ax=axx adxsss1 axxxasb')) # ax用来和后面的字符串匹配,匹配上a,就输出a,再匹配后面是不是x,如果是x,那就一起输出,如果后面是一连串的x,那一连串的x都一起输出 # 必须有a,x可以是0个或任意多个 # ['ax', 'axx', 'a', 'axxx', 'a']
-
+号,+左边的字符至少出现一次
与*相比,+左边必须匹配上所有的字符,在此基础上,如果被匹配的字符串后面还有+号左边的字符,则继续输出
print(re.findall('axx+','ax=axx adxsss1 axxxasb')) # 与axx匹配,遇到axx之后看axx后面还有没有更多的x,有则全都要 # ['axx', 'axxx']
-
{m,n} 花括号 ,花括号表示左边的字符出现m到n次,可以用来模仿 +,?一类
print(re.findall('axx{0,1}','ax=bxx adxsss1 axxxasb')) # 花括号左边的字符出现0到1次,相当于?问好 print(re.findall('ax{0,}','ax=bxx adxsss1 axxxasb')) # 左边的字符出现0到无穷次,相当于 * print(re.findall('ax{1,}','ax=bxx adxsss1 axxxasb')) # 左边的字符出现1到无穷次,相当于 *
而且,还可以指定取的字符出现几次到几次
组合使用
-
贪婪匹配 *. ** 点星
print(re.findall('a.*b','a1b22222b222b')) # 接收a,b之间的所有字符 # ['a1b22222b222b']
-
非贪婪匹配 .*? 点星问号
print(re.findall('a.*?b','a11111b22222222b')) # 返回第一次匹配成功得到的所有字符 # ['a11111b']
中括号 [ ]
print(re.findall('[12365]','ax=axx ad1234xsss1 axxx981452asb'))
# 表示只取中括号里面的字符
# 加上 ^ 符号,表示取反,只取不在中括号内的字符
print(re.findall('[^12365]','ax=axx ad1234xsss1 ax52'))
# ['a', 'x', '=', 'a', 'x', 'x', ' ', 'a', 'd', '4', 'x', 's', 's', 's', ' ', 'a', 'x']