Locating bugs through logs in 3 minutes, this skill tester must be able to!

♥ Foreword

In software development, it is a good habit to record the running status of the program through logs, which is very helpful for error troubleshooting and system operation and maintenance.

The Python standard library comes with a powerful logging module, which is widely used in various python modules.

1. Easy to use

1. Introductory small case

import logging
logging.basicConfig(level=logging.DEBUG,  #设置级别,根据等级显示
    format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s')  # 设置输出格式
logging.debug('This is a debug log')
logging.info('This is a info log')
logging.warning('This is a warning log')
logging.error('This is a error log')
logging.critical('This is a critical log')

2. Log level

Five log levels are set according to different situations, and different levels of logs are output in different situations.

log level

(level)

describe

DEBUG 

Debug information, usually useful when diagnosing problems 

INFO

General information, confirming that the program works as expected 

WARNING 

A warning message that indicates that something unexpected happened, or that something might go wrong next time, but the program continues to run 

ERROR 

Error message, there are some problems in the running of the program, some functions of the program cannot be executed 

CRITICAL

Danger message, a critical error that prevents the program from continuing 

If you want to learn automated testing, here I recommend a set of videos for you. This video can be said to be the first interface automation testing tutorial on the entire network at station B. At the same time, the number of online users has reached 1,000, and there are notes to collect and various Lu Dashen Technical Exchange: 798478386   

[Updated] The most detailed collection of practical tutorials for automated testing of Python interfaces taught by station B (the latest version of actual combat)_哔哩哔哩_bilibili [Updated] The most detailed collection of practical tutorials for automated testing of Python interfaces taught by station B (actual combat) The latest version) has a total of 200 videos, including: 1. Why should interface automation be done in interface automation, 2. The overall view of request in interface automation, 3. Interface combat in interface automation, etc. UP hosts more exciting videos, please pay attention to UP account . https://www.bilibili.com/video/BV17p4y1B77x/?spm_id_from=333.337.search-card.all.click The level set by the logger will filter out logs below this level

import logging
logging.basicConfig(level=logging.WARNING,  #设置级别,根据等级显示
    format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s')  # 设置输出格式
logging.debug('This is a debug log')
logging.info('This is a info log')
logging.warning('This is a warning log')
logging.error('This is a error log')
logging.critical('This is a critical log')

 2020-09-11 17:39:26,667-WARNING-This is a warning log

2020-09-11 17:39:26,669-ERROR-This is a error log

2020-09-11 17:39:26,669-CRITICAL-This is a critical log

3. Configuration

The basicConfig method supports the following keyword parameters for configuration.

parameter

describe

filename 

Create a FileHandler with the specified filename instead of a StreamHandler. 

filemode

If filename is specified, the file is opened with this mode. The default mode is 'a'. 

format 

Specifies the format string used by the processor. 

datefmt 

Use the specified date/time format, as accepted by time.strftime(). 

style 

If format is specified, this style will be used for the format string. '%', '{' or '$' correspond to printf style, str.format() or string.Template respectively. The default is'%'. 

level 

Sets the root logger level to the specified level.

stream 

Initializes a StreamHandler with the specified stream. Note that this parameter is incompatible with filename - a ValueError is raised if both are present. 

handlers 

 If specified, this should be an iterable containing the created handlers to join the root logger. Any handlers that do not already have a format descriptor set will be set to the default format descriptor created in this function. Note that this parameter is not compatible with filename or stream -- a ValueError is raised if both are present. 

force 

If this keyword argument is specified as true, any existing handlers attached to the root logger will be removed and closed before executing the configuration specified by the other arguments. 

Default type: %(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:%(message)s

import logging
logging.basicConfig(level=logging.DEBUG,  #设置级别,根据等级显示
    format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s')  # 设置输出格式
logging.debug('This is a debug log')

[DEBUG]-2020-09-11 17:36:50,125--4:This is a debug log

5. Log written to file

Just configure the filename parameter

import logging
logging.basicConfig(
    level=logging.WARNING,  #设置级别,根据等级显示
    filename='example.log'
    format='%(asctime)s-[%(filename)s-->line:%(lineno)d]-%(levelname)s:% (message)s')  # 设置输出格式
logging.debug('This is a debug log')
logging.info('This is a info log')
logging.warning('This is a warning log')
logging.error('This is a error log')
logging.critical('This is a critical log')

Note that after fielname is configured, the log will not be output on the console.

2. Advanced usage

Simple code can be used directly through logging. If you want to use it in depth, you need to use logging in an object-oriented manner.

1. Log component

The logging module contains the following components.

components

illustrate

Loggers 

The interface that the provider uses directly 

Handlers (log processor) 

Send recorded logs to the specified location 

Filters (log filter) 

Used to filter specific log records 

Formatters (log formatter) 

Used to control the output format of log information 

2. Steps

2.1 Create a logger

import logging
#  第一步创建一个logger,用来产生日志
logger = logging.getLogger('%s_log' % __name__)
logger.setLevel(logging.DEBUG)  # 设置日志等级

 

A logger can be created through the getLogger method, pay attention to the name or return to the root logger.

Set the logger level by setLevel.

2.2 Create a log processor

# 创建一个文本处理器用来将日志写入到文件
file_handler = logging.FileHandler(filename='py34.log',encoding='utf-8')
file_handler.setLevel('WARNING')  # 设置处理器的日志等级
# 创建一个控制台处理器用来将日志输出到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel('INFO')  # 设置控制台处理器的日志等级

The log processor is to send the log to the specified location.

  • FileHandler to send logs to a file 

  • StreaHandler It can send logging output to a data stream such as sys.stdout, sys.stderr or any file-like object. Default sys.stdout is the console. 

  • RotatingFileHandler supports rotation based on log file size 

  • TimedRotatingFileHandler supports rotating log files based on time 

See official docs for more details

(https://docs.python.org/zh-cn/3/library/logging.handlers.html?utm_source=testingpai.com#module-logging.handlers)

2.3 Create a formatter

formatter = logging.Formatter(fmt='%(levelname)s %(asctime)s [%(filename)s-->line:%(lineno)d]:%(message)s')

The formatter needs to be set on the handler

file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

2.4 Create filter

Filters are used to filter specified logs. The specific use is omitted, generally not used.

See the official documentation for details

(https://docs.python.org/zh-cn/3/howto/logging-cookbook.html?utm_source=testingpai.com#filters-contextual)

2.5 Add the processor to the logger

logger.addHandler(file_handler)
logger.addHandler(console_handler)

2.6 Logging

logger.info('This is a info')

2020-09-11 22:22:44,095-[-->line:1]-INFO:This is a info

logger.warning('This is a warning')

2020-09-11 22:23:20,337-[-->line:1]-WARNING:This is a warning

3. Log module encapsulation

1. Functional analysis

  1. Ability to customize the logger name 

  2. Ability to customize log file name and path 

  3. Ability to customize log file encoding 

  4. Ability to customize log format 

  5. Use a time-round handler, and be able to configure 

2. Encapsulated into a function

Create the module log_handler.py in the common directory and create the following functions in it.


import logging
from logging.handlers import TimedRotatingFileHandler


def get_logger(name, filename, encoding='utf-8', fmt=None, when='d', interval=1, backup_count=7, debug=False):
    """

    :param name: 日志器的名字
    :param filename: 日志文件名(包含路径)
    :param encoding: 字符编码
    :param fmt: 日志格式
    :param when: 日志轮转时间单位
    :param interval: 间隔
    :param backup_count: 日志文件个数
    :param debug: 调试模式
    :return:
    """
    logger = logging.getLogger(name)
    logger.setLevel(logging.DEBUG)
    # 文件处理器的等级一般情况一定比控制台要高
    if debug:
        file_level = logging.DEBUG
        console_level = logging.DEBUG
    else:
        file_level = logging.WARNING
        console_level = logging.INFO

    if fmt is None:
        fmt = '%(levelname)s %(asctime)s [%(filename)s-->line:%(lineno)d]:%(message)s'

    file_handler = TimedRotatingFileHandler(
        filename=filename, when=when, interval=interval, backupCount=backup_count, encoding=encoding)
    file_handler.setLevel(file_level)

    console_handler = logging.StreamHandler()
    console_handler.setLevel(console_level)

    formatter = logging.Formatter(fmt=fmt)
    file_handler.setFormatter(formatter)
    console_handler.setFormatter(formatter)

    logger.addHandler(file_handler)
    logger.addHandler(console_handler)

    return logger


if __name__ == '__main__':
    log = get_logger(name='py41', filename='py41.log', debug=True, when='s')
    log.info('我是普通信息')
    import time
    time.sleep(3)
    log.warning('我是警告信息')

4. Apply to the project

1. Import

The import of the log generator function cannot be imported once in each use case module like the Excel data reading function. Because it returns a logger object, when the logger generation function is called multiple times with the same logger name, multiple log processors will be added to the same logger, resulting in the problem of duplicating loggers.

In order to solve the above problem, create a file named __init__.py under the common folder. When the common module is imported, the code in this file will be executed automatically, and it will only be executed once.

Write the following code in the __init__.py file:

from .log_handler import get_logger
logger = get_logger('py41', 'py38.log')

Then in other modules in the project, you can import it through the following code

from common import logger

This ensures that the get_logger method will only be executed once during project execution.

2. Logging

The role of the log is to record the running status of the program and provide a basis for locating and analyzing errors when the program has problems.

When and what kind of logs need to be recorded depends on everyone's understanding of the program and experience.

In our project, the process of executing use cases is the core, so our logs also revolve around the execution of use cases.

Use logs to record the test data and test results of each use case. The code is as follows:

...
@list_data(*cases)
    def test_login(self, case):
        """
        登陆测试
        """
        logger.info('测试用例【{}】开始测试'.format(case['title']))
        # 1. 测试数据
        # 传入进来的case参数
        logger.info('测试用例【{}】的测试数据是:{}'.format(case['title'], case))
        # 2. 测试步骤
        res = login_check(case['username'], case['password'])
        logger.info('测试用例【{}】的测试结果是:{}'.format(case['title'], res))
        # 3. 断言
        try:
            self.assertEqual(res, case['expect'])
        except AssertionError as e:
            logger.error('测试用例【{}】断言失败'.format(case['title']))
            raise e
        else:
            logger.info('测试用例【{}】断言成功'.format(case['title']))
        finally:
            logger.info('测试用例【{}】测试结束')

Guess you like

Origin blog.csdn.net/m0_73409141/article/details/132410248