Python 中有好用的 logging 模块,方便我们为程序添加 log 输出,我一直希望能够写个小程序,现在的现在有个小的关于数据提取的小程序,我希望能够提取相关数据,但是用 print 的方式来观察输出实在是忧伤,所以我试着配置了 log ,问题出现了,我希望同时输出到控制台和文件中保存,恩,怎么配置呢?
问题描述
根据 logconfig.yaml 文件,配置 log,测试模块文件。
问题背景
此处引用输出级别描述:
作者:好吃的野菜
链接:http://www.jianshu.com/p/feb86c06c4f4
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
| 输出级别 |
描述 |
| 级别 |
何时使用 |
| DEBUG |
详细信息,典型地调试问题时会感兴趣。 |
| INFO |
证明事情按预期工作。 |
| WARNING |
表明发生了一些意外,或者不久的将来会发生问题(如‘磁盘满了’)。软件还是在正常工作。 |
| ERROR |
由于更严重的问题,软件已不能执行一些功能了。 |
| CRITICAL |
严重错误,表明软件已不能继续运行了。 |
从上往下,信息级别依次升高!
可采用的 format 格式关键词:
| 关键词 |
描述 |
| %(levelno)s |
打印日志级别的数值 |
| %(levelname)s |
打印日志级别名称 |
| %(pathname)s |
打印当前执行程序的路径 |
| %(filename)s |
打印当前执行程序名称 |
| %(funcName)s |
打印日志的当前函数 |
| %(lineno)d |
打印日志的当前行号 |
| %(asctime)s |
打印日志的时间 |
| %(thread)d |
打印线程 id |
| %(threadName)s |
打印线程名称 |
| %(process)d |
打印进程 ID |
| %(message)s |
打印日志信息 |
配置文件
version 键值为 1 ,表示启用这个配置。
1
2
3
|
formatters:
simple:
format: '%(asctime)s - %(filename)s - %(lineno)d - [%(levelname)s] %(message)s'
|
formatters 键值中需要有对应的 format 设置,可自定义 format 。
datefmt 格式采用 time.strftime 标准模式,此处不设置,自动启用默认。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
handlers:
console:
class: logging.StreamHandler
level: WARNING
formatter: simple
stream: ext://sys.stdout
logfile:
class: logging.handlers.RotatingFileHandler
level: DEBUG
formatter: simple
filename: log.log
maxBytes: 10485760
backupCount: 3
|
Handlers 是处理 log 的处理器,主要配置输出模式,这里我设置两种模式,控制台( console )和日志文件( logfile )。
class 设定 log 采用的输出模式,不同的 class 有着不同的参数, lever 指定处理器处理的最小级别。
-
StreamHandler 是流输出模式,此处特殊设置流的去处 stream 为 ext://sys.stdout , sys.stdout 是控制台输出位,加上 ext:// 是为了让 Yaml 识别。
-
RotatingFileHandler 是循环文件模式,此处特殊设置 filename 、 maxBytes 、 backupCount 的参数。其中 filename 指定保存文件的名称, maxBytes 指定文件大小的最大值, backupCount 指定文件保存数目的最大值,以序号标注,如果超过次数目,默认删掉最早的,更新文件名。
1
2
3
4
5
6
7
8
|
loggers:
logger:
level: INFO
handlers: [logfile]
propagate: yes
root:
level: WARNING
handlers: [console]
|
loggers 指定自定义的 log 名称,此处设置名称为 logger ,propagate 指定是否将信息传到其他 loggers ,这里设置为 yes ,这样 logger 接收到的错误信息可以传到 root 那里, 以供显示输出。
完整配置文件( logconfig.yaml )如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
version: 1
formatters:
simple:
format: '%(asctime)s - %(filename)s - %(lineno)d - [%(levelname)s] %(message)s'
handlers:
console:
class: logging.StreamHandler
level: WARNING
formatter: simple
stream: ext://sys.stdout
logfile:
class: logging.handlers.RotatingFileHandler
level: DEBUG
formatter: simple
filename: log.log
maxBytes: 10485760
backupCount: 3
loggers:
logger:
level: INFO
handlers: [logfile]
propagate: yes
root:
level: WARNING
handlers: [console]
|
main.py 文件中导入配置文件
main.py 中使用函数导入文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
import logging
import logging.config
import yaml
def setup_logging(default_path = "./logconfig.yaml",default_level = logging.INFO,env_key = "LOG_CFG"):
path = default_path
value = os.getenv(env_key,None)
if value:
path = value
if os.path.exists(path):
with open(path,"r") as f:
config = yaml.load(f)
logging.config.dictConfig(config)
else:
logging.basicConfig(level = default_level)
if __name__ == '__main__':
setup_logging(default_path = "./logconfig.yaml")
logger = logging.getLogger("logger")
#测试
logger.debug("测试debug")
logger.info("测试info")
logger.warn("测试warn")
logger.error("测试error")
logger.critical("测试critical")
|
控制台输出如下:
1
2
3
|
2017-08-17 20:32:11,723 - main.py - 39 - [WARNING] 测试warn
2017-08-17 20:32:11,724 - main.py - 40 - [ERROR] 测试error
2017-08-17 20:32:11,724 - main.py - 41 - [CRITICAL] 测试critical
|
log.log 文件输出如下:
1
2
3
4
|
2017-08-17 20:32:11,723 - main.py - 38 - [INFO] 测试info
2017-08-17 20:32:11,723 - main.py - 39 - [WARNING] 测试warn
2017-08-17 20:32:11,724 - main.py - 40 - [ERROR] 测试error
2017-08-17 20:32:11,724 - main.py - 41 - [CRITICAL] 测试critical
|
模块引入配置
如果模块文件需要引入 log 配置,
如下使用:
test.py 内容如下:
1
2
3
4
5
6
7
8
9
|
import logging
logger = logging.getLogger("logger")
def test()
logger.debug("测试debug")
logger.info("测试info")
logger.warn("测试warn")
logger.error("测试error")
logger.critical("测试critical")
|
main.py 内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
import test
import logging
import logging.config
import yaml
def setup_logging(default_path = "./logconfig.yaml",default_level = logging.INFO,env_key = "LOG_CFG"):
path = default_path
value = os.getenv(env_key,None)
if value:
path = value
if os.path.exists(path):
with open(path,"r") as f:
config = yaml.load(f)
logging.config.dictConfig(config)
else:
logging.basicConfig(level = default_level)
if __name__ == '__main__':
setup_logging(default_path = "./logconfig.yaml")
logger = logging.getLogger("logger")
#测试
logger.debug("测试debug")
logger.info("测试info")
logger.warn("测试warn")
logger.error("测试error")
logger.critical("测试critical")
test.test()
|
控制台输出如下:
1
2
3
4
5
6
|
2017-08-17 20:45:40,586 - main.py - 40 - [WARNING] 测试warn
2017-08-17 20:45:40,586 - main.py - 41 - [ERROR] 测试error
2017-08-17 20:45:40,587 - main.py - 42 - [CRITICAL] 测试critical
2017-08-17 20:45:40,587 - test.py - 17 - [WARNING] 测试warn
2017-08-17 20:45:40,587 - test.py - 18 - [ERROR] 测试error
2017-08-17 20:45:40,587 - test.py - 19 - [CRITICAL] 测试critical
|
log.log 输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
2017-08-17 20:45:14,225 - main.py - 39 - [INFO] 测试info
2017-08-17 20:45:14,225 - main.py - 40 - [WARNING] 测试warn
2017-08-17 20:45:14,225 - main.py - 41 - [ERROR] 测试error
2017-08-17 20:45:14,225 - main.py - 42 - [CRITICAL] 测试critical
2017-08-17 20:45:40,586 - main.py - 39 - [INFO] 测试info
2017-08-17 20:45:40,586 - main.py - 40 - [WARNING] 测试warn
2017-08-17 20:45:40,586 - main.py - 41 - [ERROR] 测试error
2017-08-17 20:45:40,587 - main.py - 42 - [CRITICAL] 测试critical
2017-08-17 20:45:40,587 - test.py - 16 - [INFO] 测试info
2017-08-17 20:45:40,587 - test.py - 17 - [WARNING] 测试warn
2017-08-17 20:45:40,587 - test.py - 18 - [ERROR] 测试error
2017-08-17 20:45:40,587 - test.py - 19 - [CRITICAL] 测试critical
|
解决模块引入配置的问题
模块引入 log 后,log.log 的信息有重复。
修复方法:
logconfig.yaml 文件添加一条配置
1
|
disable_existing_loggers: False
|
log.log 输出如下:
1
2
3
4
5
6
7
8
|
2017-08-17 20:57:32,535 - main.py - 39 - [INFO] 测试info
2017-08-17 20:57:32,535 - main.py - 40 - [WARNING] 测试warn
2017-08-17 20:57:32,536 - main.py - 41 - [ERROR] 测试error
2017-08-17 20:57:32,536 - main.py - 42 - [CRITICAL] 测试critical
2017-08-17 20:57:32,536 - test.py - 16 - [INFO] 测试info
2017-08-17 20:57:32,536 - test.py - 17 - [WARNING] 测试warn
2017-08-17 20:57:32,536 - test.py - 18 - [ERROR] 测试error
2017-08-17 20:57:32,536 - test.py - 19 - [CRITICAL] 测试critical
|
问题解决,控制台输出同上。
其他补充
-
logconfig.yaml 配置文件对大小写敏感。
-
logconfig.yaml 配置文件使用缩进确认键值包含关系。
-
logconfig.yaml 配置文件中同样的缩进表示同一级别键值。
-
logconfig.yaml 配置文件键值冒号后需要一个空格!
-
本文仅提供配置文件的部分参数介绍,需要了解详情,建议参考 Python 3.5.2 标准库 logging 模块文档(中文) 。