https://docs.python.org/3.5/library/logging.html,先3.5是因为我当前的python 版本是3.5
之所以要来详细的写是因为之前学django时也有这个,不是很理解,所以这里就多了解下。
写到后面发现整个文章一点条理都没有,但由于内容比较多,就不重新整理了
logging框架中主要由四个部分组成:
1
2
3
4
|
Loggers expose the interface that application code directly uses.
Handlers send the log records (created by loggers) to the appropriate destination.
Filters provide a finer grained facility
for
determining which log records to output.
Formatters specify the layout of log records
in
the final output.
|
- Loggers: 提供应用直接调用的接口
- Handlers: 决定将日志记录发送至正确的目的地
- Filters: 提供更精细的决定哪些日志输出的能力,简单点说就是决定哪些输出哪些不输出
- Formatters: 制定最终输出的格式。
logger对象
事实上logger是不直接实例化的,但是我们可以通过模块级别的函数logging.getLogger(name)
来调用。如果多次通过同一个名字调用getLogger()将只能得到对同一个对象的引用。
注意: logging.getLogger(name) 中的name是一个支持通过“.”(点号)分层级的值。
例如: 你定义一个logger: foo,那么foo.bar, foo.baz,就都算作它的子代,就是继承,有父子关系。
logger名字的层级与python中包的层级是一致的。所以为了区分它,如果你基于模块来组织logger,
那么建议你使用:logging.getLogger(__name__)
这种形式,因为对于模块而言,在python
包的命名空间中__name__的值就是模块的名字。
常用方法:
Logger.
setLevel
(lvl):
将logger的门限设置为lvl,低于此等级的信息将被忽略,当一个Logger被创造出来时,它的等级被设置为:
NOTSET(未设置) 这样的结果就是: 如果它是一个root logger,它将处理所有的信息,如果没有root logger
它将继承父级的logger等级。Note: root logger可通过level waring来创建,注意这里是个坑,
事实上默认root logger等级为warning,不要搞错了
级别设定:前面是名字,后面是对应的数字值
Logger.
exception
(msg, *args, **kwargs)
以error级别来记录信息,异常信息也会被添加到信息(message),需要exception handler调用才行
Logger.
addFilter
(filt)
添加指定的过滤器
Logger.
removeFilter
(filt)
移除指定的过滤器
这里的方法多得让人想撞死在屏幕上,以后慢慢更新吧。
Handler
- Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
- Handler.setFormatter():给这个handler选择一个格式
- Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象
handler有多达15种,这里只说下常见的几种:
1.logging.StreamHandler
发送信息到流,类文件对象即可,如终端,文件等
2.logging.FileHandler
发送信息到硬盘文件
3.logging.RotatingFileHandler
这个Handler类似于上面的FileHandler,但是它可以管理文件大小,轮询日志文件(不知道是不是检测日志文件大小)。
当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建一个新的同名日志文件继续输出。
4.logging.handlers.TimedRotatingFileHandler
这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,
而是间隔一定时间就 自动创建新的日志文件。
Formatter
Formatters 决定了记录格式。Formatter会将传递来的信息拼接成一条具体的字符串,
默认情况下Format只会将信息%(message)s
直接打印出来。Format中有一些自带的LogRecord属性可以使用,如下表格:
其中lineno,pathname,经过实测了,并不是有些博客里说的什么当前日志记录的行号。后面会有实例。
这里有一个简单的应用 的例子,记录出错信息:
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
28
|
#!/usr/bin/env python
#coding:utf-8
# Created by Andy @ 2017/6/22
import
logging
logging.basicConfig(level
=
logging.DEBUG,
format
=
'%(asctime)s %(pathname)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s '
,
datefmt
=
'%a, %d %b %Y %H:%M:%S'
,
filename
=
r
'D:\Coding\oldboy\day13 module\test.log'
,
filemode
=
'w'
)
def
login():
while
True
:
try
:
name
=
input
(
'Input user name:'
)
password
=
int
(
input
(
'Input password:'
))
# 这里故意转成整型,触发异常
if
name
=
=
'andy'
and
password
=
=
'nopasswd'
:
print
(
'logging succeed!'
)
except
ValueError as e:
logging.debug(e)
break
if
__name__
=
=
'__main__'
:
login()
|
终端验证
1
2
3
4
|
Input
user name:andy
Input
password:nopasswd
Process finished with exit code
0
|
打开test.log:
1
|
Fri,
23
Jun
2017
09
:
56
:
40
D:
/
Coding
/
oldboy
/
day13 module
/
log.py log.py[line:
24
] DEBUG invalid literal
for
int
() with base
10
:
'nopasswd'
|
注意看,lineno 并不是在日志里面的行号,而是你代码的行号,在24行调用了logging.debug(e)
这里现定义一个一simple_logger:
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
28
29
30
31
32
33
34
35
36
37
|
#!/usr/bin/env python
#coding:utf-8
# Created by Andy @ 2017/6/22
import
logging
import
os
log_path
=
os.path.join(os.getcwd(),
'test.log'
)
logging.basicConfig(level
=
logging.DEBUG,
format
=
'%(asctime)s %(pathname)s line:%(lineno)d %(message)s '
,
datefmt
=
'%Y-%m-%d %H:%M:%S'
,
# 将日期格式化成正常模式
filename
=
log_path,
filemode
=
'a'
)
# 指定成追加模式,默认就是追加,所以这句非必须
simple_logger
=
logging.getLogger(
'simple'
)
fh
=
logging.StreamHandler()
#输出到终端
fh.setLevel(level
=
logging.WARNING)
#等级比默认的高,但调用时必须一致
formatter
=
logging.Formatter(
"%(asctime)s %(message)s %(levelname)s"
)
fh.setFormatter(formatter)
simple_logger.addHandler(fh)
def
login():
while
True
:
try
:
name
=
input
(
'Input user name:'
)
password
=
int
(
input
(
'Input password:'
))
# 这里故意转成整型,触发异常
if
name
=
=
'andy'
and
password
=
=
'nopasswd'
:
print
(
'logging succeed!'
)
except
ValueError as e:
simple_logger.warning(e)
break
if
__name__
=
=
'__main__'
:
login()
|
运行:
终端输出:
1
2
3
|
Input
user name:andy
Input
password:andy
2017
-
06
-
24
09
:
55
:
57
,
395
invalid literal
for
int
() with base
10
:
'andy'
WARNING
|
test.log文件:
1
|
2017
-
06
-
24
09
:
55
:
57
D:
/
Coding
/
oldboy
/
day13 module
/
log.py line:
33
invalid literal
for
int
() with base
10
:
'andy'
|
这晨需要 注意的是:因为默认的logger等级是debug,所以它什么消息都会输出,而simple_logger则只输出比自身高等级的信息,
并且:调用simple_logger时必须比定义的等级高于或者等于定义的等级,这句话什么意思呢?看上面的例子,simple_logger
定义时的等级为warning,那么你调用时最少得warning等级,或者比它高的critical才能正常打印消息(它规定就是这样),否则你是看不到任何效果的
(这里坑了好久,一直没消息打印而找不到原因,root logger默认等级是warning,而非Noset),但是如果你调用的等级比较低,
并不影响root 这个logger打印日志信息。
填坑来了:
如果没指定默认的logger的等级,那么默认的等级warning就会生效,所以才出现上面的情况,如果需要将debug等级的信息也输入,那么,这里需要加一条:
1
2
|
simple_logger.setLevel(logging.DEBUG)
# 如果不设置此logger的级别,那么handler的记录是从logger传过来的,也就是默认从logger的warning来的
|
下面是一个使用配置文件 的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#!/usr/bin/env python
#coding:utf-8
# Created by Andy @ 2017/6/25
import
logging
import
logging.config
logging.config.fileConfig(
'logging.conf'
)
# create logger
logger
=
logging.getLogger(
'simpleExample'
)
# 'application' code
logger.debug(
'debug message'
)
logger.info(
'info message'
)
logger.warn(
'warn message'
)
logger.error(
'error message'
)
logger.critical(
'critical message'
)
|
配置文件:
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
28
29
30
31
32
33
34
35
36
37
38
|
[loggers]
keys
=
root,simpleExample
[handlers]
keys
=
consoleHandler,simpleHandler
[formatters]
keys
=
consoleFormatter,simpleFormatter
[logger_root]
level
=
DEBUG
handlers
=
consoleHandler
[logger_simpleExample]
level
=
INFO
handlers
=
simpleHandler
qualname
=
simpleExample
propagate
=
1
[handler_consoleHandler]
class
=
StreamHandler
level
=
DEBUG
formatter
=
consoleFormatter
args
=
(sys.stdout,)
[handler_simpleHandler]
class
=
FileHandler
level
=
INFO
formatter
=
simpleFormatter
args
=
(
'simple.log'
,
'w'
)
[formatter_consoleFormatter]
format
=
%
(levelname)s :
%
(message)s
[formatter_simpleFormatter]
format
=
%
(asctime)s
-
%
(name)s
-
%
(levelname)s
-
%
(message)s
datefmt
=
|
之前的例子没已经删除了,找到原因:propagate=1 ,只有当propagate 为True时,logger的信息才能传到上一级logger,或者说父logger,如果你设置为0
那么,就只有文件中有写入,而终端不会有信息打印出来。