我们在进行自动化测试时,需要打印过程日志和结果日志等,这里记录下日志的相关配置。这里我们直接自己新建一个logger。
先贴上日志代码如下,可根据需要修改:
import logging
import os
import time
from logging.handlers import RotatingFileHandler
logger = logging.getLogger("")
while logger.hasHandlers(): # 先判断有无日志handler,如有,我们先删除再重新创建
for handler in logger.handlers:
logger.removeHandler(handler)
logs_dir = 'E:\\project\\autotest1\\logs' # 这里我直接写死了一个路径,可根据实际情况修改或修改成相对路径
if not logger.hasHandlers():
timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) # 文件名自定义即可中不能带英文冒号
log_filename = '%s.txt' % timestamp
log_filepath = os.path.join(logs_dir, log_filename)
# 设置输出格式
formatter = logging.Formatter('[%(asctime)s] [%(filename)s:%(lineno)d] [%(funcName)s] [%(levelname)s] %(message)s')
rotatingFileHandle = RotatingFileHandler(filename=log_filepath, maxBytes=1024*1024*20, encoding="utf-8",
backupCount=50)
rotatingFileHandle.setFormatter(formatter)
# 控制台handler
console = logging.StreamHandler()
console.setLevel(logging.NOTSET)
console.setFormatter(formatter)
# 添加内容到日志handler中
logger.addHandler(console)
logger.addHandler(rotatingFileHandle)
logger.setLevel(logging.DEBUG)
然后我们看下各部分的配置:
首先,我们先去除日志中的其他handler
while logger.hasHandlers(): # 先判断有无日志handler,如有,我们先删除再重新创建
for handler in logger.handlers:
logger.removeHandler(handler)
然后我们设置我们自己的日志handler,一个是输出到文件的,一个是输出到控制台的。
输出到文件:
rotatingFileHandle = RotatingFileHandler(filename=log_filepath, maxBytes=1024*1024*20, encoding="utf-8",
backupCount=50)
rotatingFileHandle.setFormatter(formatter)
这里使用了RotatingFileHandler,有以下几点好处:
1.当日志文件达到设定的最大值时,RotatingFileHandler会自动将当前日志文件切分为一个新的文件,并继续向新文件中写入日志消息,保证了单个日志文件不至于过大。
2.可以指定最多保留的日志文件数量。当日志文件数量达到设定值后,最旧的日志文件会被删除,从而保持日志文件数量的控制。
其中:maxBytes即为每份日志文件大小的最大值,backupCount为日志文件最多保存多少份。
这里附上RotatingFileHandler的注释配合理解:
"""
Handler for logging to a set of files, which switches from one file
to the next when the current file reaches a certain size.
Open the specified file and use it as the stream for logging.
By default, the file grows indefinitely. You can specify particular
values of maxBytes and backupCount to allow the file to rollover at
a predetermined size.
Rollover occurs whenever the current log file is nearly maxBytes in
length. If backupCount is >= 1, the system will successively create
new files with the same pathname as the base file, but with extensions
".1", ".2" etc. appended to it. For example, with a backupCount of 5
and a base file name of "app.log", you would get "app.log",
"app.log.1", "app.log.2", ... through to "app.log.5". The file being
written to is always "app.log" - when it gets filled up, it is closed
and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
exist, then they are renamed to "app.log.2", "app.log.3" etc.
respectively.
If maxBytes is zero, rollover never occurs.
"""
输出到控制台:
console = logging.StreamHandler()
console.setLevel(logging.NOTSET)
console.setFormatter(formatter)
因为我们平时本地调试时,还是直接看控制台方便,所以这里添加了到控制台的输出。
最后,我们看下formatter的配置,formatter即为日志打印的格式。这里我们贴一下Formatter的注释部分,这里其实就给出了用法:
"""
Formatter instances are used to convert a LogRecord to text.
Formatters need to know how a LogRecord is constructed. They are
responsible for converting a LogRecord to (usually) a string which can
be interpreted by either a human or an external system. The base Formatter
allows a formatting string to be specified. If none is supplied, the
style-dependent default value, "%(message)s", "{message}", or
"${message}", is used.
The Formatter can be initialized with a format string which makes use of
knowledge of the LogRecord attributes - e.g. the default value mentioned
above makes use of the fact that the user's message and arguments are pre-
formatted into a LogRecord's message attribute. Currently, the useful
attributes in a LogRecord are described by:
%(name)s Name of the logger (logging channel)
%(levelno)s Numeric logging level for the message (DEBUG, INFO,
WARNING, ERROR, CRITICAL)
%(levelname)s Text logging level for the message ("DEBUG", "INFO",
"WARNING", "ERROR", "CRITICAL")
%(pathname)s Full pathname of the source file where the logging
call was issued (if available)
%(filename)s Filename portion of pathname
%(module)s Module (name portion of filename)
%(lineno)d Source line number where the logging call was issued
(if available)
%(funcName)s Function name
%(created)f Time when the LogRecord was created (time.time_ns() / 1e9
return value)
%(asctime)s Textual time when the LogRecord was created
%(msecs)d Millisecond portion of the creation time
%(relativeCreated)d Time in milliseconds when the LogRecord was created,
relative to the time the logging module was loaded
(typically at application startup time)
%(thread)d Thread ID (if available)
%(threadName)s Thread name (if available)
%(taskName)s Task name (if available)
%(process)d Process ID (if available)
%(message)s The result of record.getMessage(), computed just as
the record is emitted
"""
然后我们根据这个选取的信息为:
formatter = logging.Formatter('[%(asctime)s] [%(filename)s:%(lineno)d] [%(funcName)s] [%(levelname)s] %(message)s')
主要就是打印了时间、文件名、行号、方法名、日志级别、具体日志信息,个人认为这还是比较全面的,当然如果涉及到其它例如多线程之类的,可根据自己的需要选取打印信息。
我们还拿之前的测试用例举例,这里在测试用例里添加日志信息:
在控制台打印日志后,也会输出一份到日志文件,每次运行自动化工程时,会生成一份按时间命名的日志文件。
这是日志文件中的内容。
还有打印的日志等级,有时可能我们只需要打印info以上级别的日志,修改日志级别即可
logger.setLevel(logging.INFO)
再次执行一下test_1的方法
可以看到只打印info以上级别的日志。
这里就日志的配置了,可根据自己的需要修改或增加内容。