#参考官方文档----

一、logging基本使用

1、logging概述

logging是软件运行过程中跟踪一些时间发生的一种手段,软件开发会在软件的一些特定时间发生后在代码中添加log,此时会调用logging。日志里面对这个事件信息进行描述,可以包含一些变量数据,具体信息由自己定义。这些事件也存在一定的严重程度,这些也是由开发者赋给事件的严重性,即会有一个时间的Level表示不同严重级别的事件。

2、什么时候会用到logging

logging提供了一系列的functions供使用,例如debug()、info()、warnning()、error()、critical().下述描述了什么时候用logging.

想要执行的任务 该任务最适合的工具
一个命令行脚本或者程序的一般用法显示在console输出 print()
正常程序执行过程中的一些事件的触发记录 loggiing.info()、logging.debug()
触发了一个报警事件 warnings.warn()-如果需要client对数据进行处理或者改变;loging.waring()-client可以不做任何处理,这是一个告警提醒用户注意
在运行过程中触发error Raise 触发一个异常
报告一个异常error但是不触发 logging.error()、logging.exception()、logging.critcal

logging的方法使用之前需要定义log的严重级别来确定需要跟踪的事件,下述表格描述标准的级别定义。(可以重写方法添加其他级别)

Level 什么时候使用
DEBUG 详细信息,只有诊断问题时才需要,就是一般的调试信息
INFO 当程序运行时期望的一些信息
WARNING 软件运行正常,但是可能会有一些预期之外的事件发生
ERROR 由于一些严重问题导致软件一些功能出现问题
CRITICAL 很严重的错误直接导致软件不能继续运行

默认的级别时WARNING,意味默认的级别下,显示的日志信息必须时>=WARNING的级别才会进行处理。
日志的记录可以由不同的方式,最简单的是把日志直接输出到console,另外一种就是可以把他们写到文件。

举一个简单的例子:

import logging
logging.warning('Watch out!')
logging.info("I told you so")

运行结果:

WARNING:root:Watch out !

打印到console,INFO信息没有打印是因为默认的日志级别是WARNING。当前打印的信息包含级别:loggger 名字:日志信息。(可以设置格式和修改logger name.后面会介绍)

保存日志到文件

比较好的方法是把日志打印在一个文件,后续可以查看。

import logging
logging.basicConfig(filename='example.log',level=logging.DEBUG)
logging.debug("This message should go to the log file")
logging.info("So should this")
logging.warning("And this,too")

运行上述代码可以在当前目录找到example.log文件,文件内容如下:

DEBUG:root:This message should go to the log file
INFO:root:So should this
WARNING:root:And this,too

上述例子告诉我们如何设置日志级别,设置级别为INFO,所以DEBUG和WARNING比它级别高的日志都可以收集到。
如果运行多次上述的脚本,则每一次运行的日志都会追加到example.log,如果想要不记录之前的运行日志,需要设置filemode参数:
logging.basicConfig(filename=‘examle.log’,level=logging.DEBUG,filemode=‘w’)
这样设置后面再运行直接覆盖前面运行的内容。

多个模块的日志处理

如果程序跑了多个模块,需要处理logging,下述一个例子告诉我们怎么处理这种情况:

#myapp.py
import logging
import mylib
def main():logging.basicConfig(filename='myaoo.log',level=logging.INFO)logging.info("start")mylib.do_something()logging.info("Finished!")
if __name__=='__main__':main()
#mylib.py
def do_something()logging.info("Doing something!")

运行myapp.py,mypp.log内容如下:

INFO:root:start
INFO:root:Doing something!
INFO:root:Finished!

添加变量logging

日志记录事件,有时会需要记录一些数据,
import logging
logging.warning(’%s before you %s’,‘Look’,‘leap!’)
日志展现如下:
WARNING:root:Look before you leap!
如上可以看出加入变量的值就用%s即可

修改信息在日志中的格式

修改日志展示的格式:

Import logging
logging.basicConfig(format='%(levelname)s:%(meaasge)s',level=logging.DEBUG)
logging.debug("This message should appear on the console")
logging.info('So should this')
logging.warning('And this,too')

运行结果:

DEBUG:This message should appear on the console
INFO:So should this
WARNING:And this,too

上述的参数可以自己添加,常用参数由:
%(acstime)s 时间
%(filename)s 日志文件名
%(funcName)s 调用日志的函数名
%(levelname)s 日志的级别
%(module)s 调用日志的模块名
%(message)s 日志信息
%(name)s logger的name,不写的话默认是root
-还有一些其他的,用到的话可以参考官方文档

线程安全

logging模块不需要调用它的用户再做其他的操作使线程安全,它自己已经实现了线程锁,每一个模块调用的时候是锁定了共享数据区域,每一个handler也对IO进行加锁操作。这里了解的不多,暂时先这样。

模块级别功能

logging框架中主要由四个部分组成:

Loggers: 可供程序直接调用的接口
Handlers: 决定将日志记录分配至正确的目的地
Filters: 提供更细粒度的日志是否输出的判断
Formatters: 制定最终记录打印的格式布局

logging.getLogger(name=None)返回一个loggger的对象,这个对象是提供所有的log使用的接口,name如果未None则使用的是root logger,否则可自己定义一个自己的名字。
所有调用日志的接口,如果logger name一样则都是同一个logger实例,不需要在不同的应用中传输。
logging.getLoggerClass()
返回一个标准的logger类,或者是传输给setLoggerClass()的类。可以新建logger类,然后继承该类。
logging.basicConfig(**kwargs)
对logging做一些基本的配置,创建StreamHandler,默认的Formatter,增加这些到rootlogger.

支持参数如下:
filename—日志存储文件路径
filemode–写日志的方式,默认是a即追加模式,w为覆盖填写
format–日志格式
datefmt–时间格式
style–format如果是特别的,要用特定的fotmat格式,默认是%
level–日志过滤的级别
stream–用特定的stream初始化StreamHandler,如果有filename该参数无效
handlers–filename or stream和该参数冲突,否则会引起错误

----其他的功能请参考官方文档

使用方法1-logger使用,配置handler

创建一个logger,定义handlers(stream或者file),对stream和file进行格式添加以及logging.level的添加,接着把handler添加到logger。
在需要添加log的地方调用logging.info()、logging.debug()的等加入自己需要的日志。举例如下:

import logging
#创建logger
log=logging.getLogger("example")
log.setLevel(logging.DEBUG)
hander1=logging.StreamHandler()
hander2=logging.FileHandler('haha.log',mode='w')
hander1.setLevel(logging.INFO)
hander2.setLevel(logging.DEBUG)
formatter1=logging.Formatter("%(asctime)s-%(name)s-%(levelname)s-%(message)s")
formatter2=logging.Formatter("%(levelname)s : %(message)s")
hander1.setFormatter(formatter1)
hander2.setFormatter(formatter2)
log.addHandler(hander1)
log.addHandler(hander2)
log.info("这是一个info信息")
log.debug("这是一个debug信息")
log.warning("这是一个warnning信息")
log.info("这个怎么算呢")

运行结果:

C:\Users\18566\AppData\Local\Programs\Python\Python38\python.exe C:/Users/18566/Desktop/APPAutoTest/logginglearn/loggingUse.py
2020-08-07 18:13:46,190-example-INFO-这是一个info信息
2020-08-07 18:13:46,191-example-WARNING-这是一个warnning信息
2020-08-07 18:13:46,191-example-INFO-这个怎么算呢Process finished with exit code 0haha.log内容:
INFO : 这是一个info信息
DEBUG : 这是一个debug信息
WARNING : 这是一个warnning信息
INFO : 这个怎么算呢

查看整体的loggig定义的level级别一定要比handler自己的定义levle低,否则日志会以logging的level进行日志的过滤。

使用方法2—logging.basicConfig()

先看一个程序:

import logging
print(logging._handlerList)
logging.basicConfig(level=logging.INFO,format='%(asctime)s:%(name)s:%(levelname)s:%(message)s')
print(logging._handlerList)
logger1=logging.FileHandler(filename='now.log',mode='w')
print(logger1.setFormatter())
logger2=logging.StreamHandler()
logger1.setLevel(logging.WARNING)
logger2.setLevel(logging.DEBUG)
logging.getLogger().addHandler(logger1)
logging.getLogger().addHandler(logger2)
print(logging._handlerList)
print("+++++++++++++++++++")
logging.info("这是一个logging")
logging.warning("这个号码")
logging.error("error级别日志应该都可以获取")
logger1_log=logging.getLogger("logger1_log")
logger2_log=logging.getLogger("logger2_log")
print("+++++++++++++++++++++++")
print(logging._handlerList)
logger1_log.info("这个时logger1 info")
logger2_log.debug("这个时logger2 debug")
logger2_log.error("这个时logger2 error")
logging.error("放在最后的logging error")

运行结果如下:

C:\Users\18566\AppData\Local\Programs\Python\Python38\python.exe C:/Users/18566/Desktop/APPAutoTest/logginglearn/loggingUse.py
2020-08-08 10:25:20,403:root:INFO:这是一个logging
[<weakref at 0x00000255A9D888B0; to '_StderrHandler' at 0x00000255A9D7A970>]
这是一个logging
2020-08-08 10:25:20,403:root:WARNING:这个号码
[<weakref at 0x00000255A9D888B0; to '_StderrHandler' at 0x00000255A9D7A970>, <weakref at 0x00000255A9CC0CC0; to 'StreamHandler' at 0x00000255A9A3B6D0>]
这个号码
None
2020-08-08 10:25:20,403:root:ERROR:error级别日志应该都可以获取
error级别日志应该都可以获取
[<weakref at 0x00000255A9D888B0; to '_StderrHandler' at 0x00000255A9D7A970>, <weakref at 0x00000255A9CC0CC0; to 'StreamHandler' at 0x00000255A9A3B6D0>, <weakref at 0x00000255A9CC8DB0; to 'FileHandler' at 0x00000255A9CC1BB0>, <weakref at 0x00000255A9CDE860; to 'StreamHandler' at 0x00000255A9CC1EE0>]
2020-08-08 10:25:20,403:logger1_log:INFO:这个时logger1 info
+++++++++++++++++++
+++++++++++++++++++++++
这个时logger1 info
[<weakref at 0x00000255A9D888B0; to '_StderrHandler' at 0x00000255A9D7A970>, <weakref at 0x00000255A9CC0CC0; to 'StreamHandler' at 0x00000255A9A3B6D0>, <weakref at 0x00000255A9CC8DB0; to 'FileHandler' at 0x00000255A9CC1BB0>, <weakref at 0x00000255A9CDE860; to 'StreamHandler' at 0x00000255A9CC1EE0>]
2020-08-08 10:25:20,403:logger2_log:ERROR:这个时logger2 error
这个时logger2 error
2020-08-08 10:25:20,403:root:ERROR:放在最后的logging error
放在最后的logging errorProcess finished with exit code 0now.log内容:
这个号码
error级别日志应该都可以获取
这个时logger2 error
放在最后的logging error

程序分析:
1、关于输出格式,如果对handler没有进行格式的设置,则有一个默认格式,查看logging里面的源码信息

class Formatter(object):"""Formatter instances are used to convert a LogRecord to text.Formatters need to know how a LogRecord is constructed. They areresponsible for converting a LogRecord to (usually) a string which canbe interpreted by either a human or an external system. The base Formatterallows a formatting string to be specified. If none is supplied, thethe style-dependent default value, "%(message)s", "{message}", or"${message}", is used.The Formatter can be initialized with a format string which makes use ofknowledge of the LogRecord attributes - e.g. the default value mentionedabove makes use of the fact that the user's message and arguments are pre-formatted into a LogRecord's message attribute. Currently, the usefulattributes 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 loggingcall 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()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)%(process)d         Process ID (if available)%(message)s         The result of record.getMessage(), computed just asthe record is emitted"""

解释语句有一句话:the style-dependent default value, “%(message)s”, “{message}”, or
“${message}”, is used.
所以在上述输出有一个单独的message信息,这是因为不管是logger1还是logger2都没有对格式做设置,默认的格式就是只输出message.
2、为什么还输出了一个带格式的日志呢?
看到程序中输出的handlersList,最开始没有添加任何handler时,默认时有一个标准的错误输出的:[<weakref at 0x00000255A9D888B0; to ‘_StderrHandler’ at 0x00000255A9D7A970>]

配置了logging.basciConfig(),没有定义任何的handler,logging.basciConfig如果没有说明handler时console还是file则默认是streamHandler,所以此时的handlerList变为:
[<weakref at 0x00000255A9D888B0; to ‘_StderrHandler’ at 0x00000255A9D7A970>, <weakref at 0x00000255A9CC0CC0; to ‘StreamHandler’ at 0x00000255A9A3B6D0>]
3、增加了2个handler之后,HandlerList变为:
[<weakref at 0x00000255A9D888B0; to ‘_StderrHandler’ at 0x00000255A9D7A970>, <weakref at 0x00000255A9CC0CC0; to ‘StreamHandler’ at 0x00000255A9A3B6D0>, <weakref at 0x00000255A9CC8DB0; to ‘FileHandler’ at 0x00000255A9CC1BB0>, <weakref at 0x00000255A9CDE860; to ‘StreamHandler’ at 0x00000255A9CC1EE0>]
分别添加了一个streamHandler和一个FileHandler,所以fie里面会写一次不带格式的日志。因为handlerList只添加了一个fileHandler且没有定义格式。有2个streamHandler,一个是在logging.BasicConfig中定义了格式,logger2 streamHandler采用默认格式,所以日志输入格式:

2020-08-08 10:46:21,428:root:ERROR:放在最后的logging error
放在最后的logging error

4、增加了2个logger的名字,可以看出所有的handler会接受所有的logger的日志进行track,所以root,logger1_log,logger2-log都会写到log和cosole

日志参数采用文件配置

导入库文件logging.config

def fileConfig(fname, defaults=None, disable_existing_loggers=True):"""Read the logging configuration from a ConfigParser-format file.This can be called several times from an application, allowing an end userthe ability to select from various pre-canned configurations (if thedeveloper provides a mechanism to present the choices and load the chosenconfiguration)."""import configparserif isinstance(fname, configparser.RawConfigParser):cp = fnameelse:cp = configparser.ConfigParser(defaults)if hasattr(fname, 'readline'):cp.read_file(fname)else:cp.read(fname)formatters = _create_formatters(cp)# critical sectionlogging._acquireLock()try:_clearExistingHandlers()# Handlers add themselves to logging._handlershandlers = _install_handlers(cp, formatters)_install_loggers(cp, handlers, disable_existing_loggers)finally:logging._releaseLock()

用configparser解析ini文件,ini文件的写法如下:

logging.ini
[loggers]
keys=root,example
[handlers]
keys=consoleHandler,FileHandler
[formatters]
keys=consoleFmt,fileFmt
[formatter_consoleFmt]
format=%(asctime)s::%(levelname)s::%(name)s::%(message)s
[formatter_fileFmt]
format=%(name)s::%(levelname)s::%(message)s
[logger_root]
level=DEBUG
handlers=consoleHandler,FileHandler
[logger_example]
handlers=consoleHandler
qualname=example
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=consoleFmt
args=(sys.stdout,)
[handler_FileHandler]
class=FileHandler
level=DEBUG
formatter=fileFmt
args=('./wwxxyy.log','w')

脚本如下:

import logging.config
logging.config.fileConfig('./logging.ini')
logging.info("this is a info log")
logging.debug("this is s debug log")
logging.warning("this is a warning log")
logging.error("this is a error log")
#
logger=logging.getLogger("example")
logger.error("hahhahhah")

运行结果是:

C:\Users\18566\AppData\Local\Programs\Python\Python38\python.exe C:/Users/18566/Desktop/APPAutoTest/logginglearn/loggingUse.py
2020-08-08 18:48:55,391::INFO::root::this is a info log
2020-08-08 18:48:55,391::WARNING::root::this is a warning log
2020-08-08 18:48:55,391::ERROR::root::this is a error log
2020-08-08 18:48:55,391::ERROR::example::hahhahhahProcess finished with exit code 0

wwwxxxyyy.log内容如下:

root::INFO::this is a info log
root::DEBUG::this is s debug log
root::WARNING::this is a warning log
root::ERROR::this is a error log

具体实现还要继续看源码,目前还没看明白源码,先记录下怎么实现把。
ini配置文件是用configParser读取出section option然后做一堆处理的。
ini文件包含的内容分如下几部分:
1、先要写你要的logger,section='loggers ’ option=‘keys’,keys的内容写你需要传入的logger name.

[loggers]
keys=root,example

2、要写传入的数据的目的地,目前用的多的streamHandler ,fileHandler,其他的请参考官方文档。handlers这里的名字可以随便写,为了方便读写当然最好是见词知意。

[handlers]
keys=consoleHandler,fileHandler

3、像之前采用basicConfig也是需要加fmt的,当然不加的话就是默认格式

[formatters]
keys=fmt1,fmt2

4、上述指定的数据,下述对数据进行配置,先配置fmt,其实不分先后,只要在文件能找到即可

[formatter_fmt1]
format=(%asctime)s::::%(levelname)s::%(name)s:%(message)s
[formatter_fmt2]
format=(%levelname)s:%(name)s:%(message)s

5、指定handler配置,指定每个handler的参数

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=fmt1
args=(sys.stdout,)
[handler_fileHandler]
class=FileHandler
level=INFO
formatter=fmt2
args=("log.log","w")

6、添加到logger,添加handler到logger

[logger_root]
handlers=consoleHandler,fileHandler
level=DEBUG
[logger_example]
handlers=condoleHandler
level=INFO
qualname=example
propagate=1

qualname=example
propagate=0
加2个参数的原因:
对于跟记录器以外的记录器要添加qualname和propagate零个参数。
级别和处理程序条目解释为跟记录器,除非如果非根记录器的级别被指定为NOTSET,则系统会在层次结构更高的层次上咨询记录器以确定记录器的有效级别。传播条目设置为1(propagate)以指示消息必须传播到记录器层次结构中自此记录器的处理程序,或者0表示消息不传播到层次结构中的处理程序。质量名qualname是记录器的分层通道名称,也就是应用程序用来获取记录器的名称。

这一段解释感觉非常拗口,个人理解:
如果不设置logger或者设置为‘’(实际也是root),记录器相当于没有分层,就只记录root 名字的logger的内容。如果设置了其他的logger name,记录器需要分层处理,设置的其他logger name配置区分不同的分层,当然还带了一个分层不分层的标记propagate标记,1为分层,0不分层。–个人理解,仅供参考

先写到这里把,后续有进步再追加!!!!!!!!

logging的理解和使用一相关推荐

  1. python log文件如何不写入syslog_python 自动化之路 logging日志模块

    logging 日志模块 http://python.usyiyi.cn/python_278/library/logging.html 中文官方 http://blog.csdn.net/zyz51 ...

  2. oracle日志保存时间设置,关于日志设置的详细介绍

    这篇文章主要介绍了MySQL日志设置及查看方法,需要的朋友可以参考下MySQL有以下几种日志:错误日志: -log-err查询日志: -log慢查询日志: -log-slow-queries更新日志: ...

  3. jar包中的声明文件

    manifest.mf的一点理解 今天发现自己项目下存在commons-logging-1.1.jar和commons-logging.jar两个类包,就想看看commons-logging.jar包 ...

  4. logbook日志系统

    python中替代logging的日志系统. 不过比之前的logging难理解. 先上打印到屏幕上的代码和存到日志文件中的代码: #!/usr/bin/env python3 # -*- coding ...

  5. java怎么表示log2_Java程序员修炼之道 之 Logging(2/3) - 怎么写Log

    1. 一个最基本的例子 使用Logging框架写Log基本上就三个步骤 引入loggerg类和logger工厂类声明logger记录日志 下面看一个例子 //1. 引入slf4j接口的Logger和L ...

  6. 深入理解 wpa_supplicant(二)

    本文为<深入理解Android Wi-Fi.NFC和GPS卷>读书笔记,Android源码为Android 5.1 struct wpa_global * wpa_supplicant_i ...

  7. Python中logging.config配置文件解读

    Python中logging.config配置文件解读 下面的函数用于配置logging模块,它们位于logging.config模块中.你可以使用这些函数来配置,也可以在logging或是loggi ...

  8. 如何设计一个复杂的业务系统?从对领域设计、云原生、微服务、中台的理解开始...

    欢迎关注方志朋的博客,回复"666"获面试宝典 01 如何解决复杂业务设计 Aliware 软件架构设计本身就是一个复杂的事情,但其实业界已有一个共识,那就是"通过组件化 ...

  9. 【java】java自带的java.util.logging.Logger日志功能

    偶然翻阅到一篇文章,注意到Java自带的Logger日志功能,特地来细细的看一看,记录一下. 1.Java自带的日志功能,默认的配置 ①Logger的默认配置,位置在JRE安装目录下lib中的logg ...

最新文章

  1. 编译u-boot时候,make distclean 出现rm:无法删除,****是一个目录
  2. pandas数据预处理(字段筛选、query函数进行数据筛选、缺失值删除)、seaborn可视化分面图(facet)、seaborn使用Catplot可视化分面箱图(Faceted Boxplot)
  3. golang image.image 转文件流_Kuiper 1.0.2 正式发布 超轻量 IoT 边缘流处理
  4. 计算机考试不用输入扩展名吗,本周末的 计算机等级考试,如果不想考0分,一定要花2分钟看完...
  5. 基于Hadoop的数据分析平台搭建
  6. 00002-两数之和-leetcode-1.暴力法(枚举法),2.哈希表法,目前更新了枚举法
  7. 68.营救问题(广搜)
  8. 学校图书借阅管理系统(MySQL)
  9. 恩智浦智能车大赛----笔记
  10. C++:关于类以及h/cpp文件的一些实用知识
  11. GOD IS A GIRL 创作背后感人的故事
  12. 不定式和动名词复合结构是什么
  13. 【渝粤教育】广东开放大学 中国文化与中国文学 形成性考核 (46)
  14. AndroidStudio:The number of method references in a .dex file cannot exceed 64K错误
  15. 阿里云小福利!水杯免费送
  16. Dijkstra最短路径算法——java代码实现
  17. 流言终结者 1080P全高清都等于高画质?
  18. 基于FPGA的智力抢答器设计
  19. 光标和元素的关系:mouseEnter、mouseLeave、mouseOver、mouseOut
  20. oracle在指定列后添加列,ORACLE中文排序及在table中指定位置增加字段

热门文章

  1. 面向对象-----6(javaSE基础知识完结篇)
  2. 设置静态路由 不同网段可以互相访问
  3. Linux基本命令(学习笔记)零基础入门linux系统运维
  4. 李逸轩-5.31黄金还将继续下探,原油还有高点!黄金白银走势分析
  5. 【linux】linux中fork()详解(实例讲解)|fork的运行机制
  6. C# PropertyGrid控件应用心得
  7. html框架详解和布局
  8. 用计算机接口充手机,电脑接口也可给手机充电
  9. VBA中的正则表达式(二)
  10. LaTex中对\begin{itemize}或\begin{enumerate}中的各项新的段落增加缩进