logging:多线程调试时用来代替print和单步调试
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了logging:多线程调试时用来代替print和单步调试相关的知识,希望对你有一定的参考价值。
参考技术A 当你要写多线程项目时,不免要调试错误,要debug。一般debug的工具就是打印函数print, 调试工具gdb进行单步调试,但是多线程时,单步调试就很鸡肋了,这时就需要打印日志了
没错,打印日志无疑是调试多线程工程的高效工具了。
在python中开发,就要用到logging日志库了
logging库已经封装好日志需要的基本功能,能够实现在文件里,在命令行等写日志
还能输出日志信息的类型,如debug,warning,error等
细节在这里有所介绍:
https://docs.python.org/3/howto/logging.html
文本介绍一下,第一次使用logging时,要熟悉logging时,需要用一个非常简单的例子
先看一个最简单的例子:
运行以上代码:
printed out on the console. The INFO message doesn’t appear because the default level is WARNING . The printed message includes the indication of the level and the description of the event provided in the logging call, i.e. ‘Watch out!’. Don’t worry about the ‘root’ part for now: it will be explained later. The actual output can be formatted quite flexibly if you need that; formatting options will also be explained later.
输出结果为:
如果你想每次都在一个新的日志文件中写日志,那么使用filemode='w'参数:
分别在main 函数里,在mylib.py里写日志
运行后的输出:
以上代码会显示:
该 format参数的值
以上代码会显示:
还是该format:
以上代码会输出
如果想自己设定时间的格式:
会这样显示:
logging库中用了模块化的思路,把日志的整体功能用了4个基本的模块来完成:
Loggers,Handlers,Filters,Formatters
其中,Handlers,主要配置将信息写到命令行,还是写到文件里。
Filters,是对信息本身的过滤,决定那些信息不写,那些信息写。
Formatters决定信息输出的格式。比如是否输出时间,是否输出logger本身的名字等,决定那些信息在前,那些信息在后等。
Logger就是Handler,Filter,Formatter配置的一个日志对象了。
下面我们逐个说一下这4个类:
它有三个功能,1 提供分级日志的输出,比如 WARNING,ERROR,INFO等不同等级。2. 它可以决定哪些信息输出,哪些信息不输出。3 它可以将一条信息发给命令行和文件,可以把一条信息发给多个handler去处理。
logger 最常用的成员方法大概分两类:配置和信息发送
以下是常用的配置函数:
Logger.setLevel() 设定信息记录的等级。如果一条信息的等级比我们设定的低,那么就不对此条信息进行处理。信息一共分为:DEBUG,INFO,WARNING,ERROR,CRITICAL 五个级别,其中DEBUG是最低的等级,CRITICAL是最高的等级。
Logger.addHandler() 和 Logger.removeHandler(),添加或者删除信息处理器Handler。这个信息处理器就是定义了将日志写入命令行,还是写入文件,或者写入邮件等。
Logger.addFilter() 和 Logger.removeFilter() .添加或者删除信息过滤器Filter。这个信息过滤器,决定了哪些信息不显示。
可以看到,信息过滤器,信息处理器,信息等级共同配置了logger.
并不是每个logger你都需要去配置一遍,你可以利用logger的继承机制,只配置父logger.
一旦配置好logger之后,就可以用以下函数来在你自己代码的任意位置来记录日志了。
Logger.debug(),Logger.info(),Logger.warning(),Logger.error()和Logger.critical.这些函数的功能都是建立了日志记录信息,不同的是,函数名字就代表了其建立的日志信息的等级。
Logger.exeception()建立一个与Logger.error()比较相似的信息。但是Logger.exception()是放入一个追踪盏里面的。所以,只有在exception handler处理器中,才能使用它。
Logger.log()发送一个LOG 等级的信息,使用LOG等级的信息稍微繁琐些,因为使用LOG等级可以自定义等级。
getLogger() 返回一个logger的引用,如果指定了名字,那么返回特定名字对应的logger,如果没有指定名字,那就返回一个名字为root的loger的引用名字root,或者你指定的名字是一种级连结构。用同样的名字去调用getLogger(),会得到同样的值。logger的名字也决定了logger在树结构中的层级。例如:这里有一个logger的名字为foo,那么foo.bar,foo.bar.baz,还有foo.bam都是foo的子孙,logger还有一个有效等级level的概念。这个有效等级其实就是决定debug,info,warning等不同类型的消息是否进行记录。因为logger本身有了父子那样的继承关系,所以有效等级level也是可以继承的。如果子logger没有设定了自身的level,那么就把父logger的level继承过来使用。如果子logger本身设定了level,就用自身这个level.如果父logger仍然没有设定level,那就看父logger的父logger,一直这么追述下去,就会追述到root上,所以,我们必须给root设定一个level,或者默认一个level,方便root的子logger去继承。我们给root设定的默认levle为WARNING.如果有些日志的level,相比我们设定的WARNING低,那么它就不会被传递给Handler去处理,就不会被打印出来,或者记录进日志文件。另外logger的属性也是可继承的,所以就只配置一下root logger即可,没有root logger时,只用配置一个相对的那个根logger就行了
handler 信息处理器是负责信息分发给不同的目的地的,这个目的地可能是命令行,也可能是文件,或者邮件。分发时,同样要检查信息本身的等级severity.一个logger可以用addHandler()函数添加0个或者多个handlers。比如有这样一个场景,希望发送所有的log等级的信息到一个log文件内。所有的等级为错误的信息到stdout 标准输出上,发送所有的critical信息到邮件上。这样的场景需要3个不同的处理器,每个处理器负责发送相应等级的信息到相应的目的地。
标准库里包含一些处理器类型,本教程主要使用StreamHandler 和 FileHandler两种信息处理器。
处理器中的成员函数非常少,我们用来配置处理器的成员函数大概有这个几个:
setLevel(),用来设置处理器处理的信息等级。注意到logger中有setLevel(),而处理器中也有setLevel(),也就是说,logger把信息通过信息等级过滤一遍后,logger内的处理器需要根据处理器自身的功能设定,再根据信息等级来过滤一遍。
setFormatter() 为处理器设定一种信心记录的格式Formatter
addFilter() removeFilter()函数添加信息过滤器或者删除信息过滤器的函数。
到这里,我们发现,logger类中,将信息发送到不同的目的地就依赖Handlers实现的,
所有Handler就需要用Formatter和Filter来配置一下。接下来,我们看看Formatter和Filter.
它决定了信息显示的顺序,结构和内容。可以直接操作formatter的相关类,也可以自己继承 formatter类,去完成自己的设定。formatter的构造函数需要3个可选的参数:字符串格式的信息,日期,一个符号。
logging.Formatter.__init__(fmt=None,datafmt=None,style='%')
如果这里没有设定参数,就使用默认参数。对于fmt来说,就显示原来信息,对于datafmt来说,就以年-月-日 时:分:秒的合适显示。
下面的fmt就定义了一个按照 时间-信息等级-信息本身 来记录日志的方法:
这里有三种方法:
写代码配置logging的例子:
运行结果为:
写配置文件配置logging的例子:
代码中所需要的配置文件ogging.conf的具体内容为 :
以上代码的输出为:
显然,配置文件修改起来相对容易很多。
还有更方便的配置方式,使用字典来配置:
运行结果与上面一样。
到这里,基本完成了logging库的比较常用的使用方式。
比如在什么情况下,需要使用一个什么都不做的处理器NullHandler(),
还有关于信息等级的更详细的解释:
以及如何自定义信息等级。
还有关于异常的处理
还有关于信息的,信息都是字符串的。不过你也可以直接把某个类作为信息抛出来,因为类会自动调用其__str__()函数,返回一个类的字符串回来。
还有就是优化性能的一些小技巧
这里就编写expensive_func1 和 expensive_func2 来完成设定数据格式。
以下表格也显示了如何收集运行log的代码文件信息,线程信息,进程信息,处理器信息
以及如何操作才能收集代码文件信息,线程信息,进程信息,以及处理器信息。
我们一般会使用收集代码文件,线程,进程等信息,因为这样大大方便了多线程多进程工程的调试。
js异步和单线程
异步结果:100 300(不做等待) 200
同步结果:100 200(等待) 300 (会阻塞后面代码的运行)
对比alert();
console.log(100);
alert(200);
console.log(300);
结果:// 100 200 点击确认 300
5、什么时候需要异步?
a>可能发生等待的情况
b>不能像alert一样阻塞程序运行
换言之,所有的“等待的情况”都需要异步
6、前端使用异步的场景:
a>定时任务
b>ajax
demo1:ajax请求
console.log(‘start‘);
$.get(‘XXX‘, function(data) {
console.log(data);
});
console.log(end);
执行结果: // start end object
分析:
ajax请求需要等待
过程:a》打印start
b》执行 $.get 后,函数 data1 会被暂存起来,不会立即执行(单线程的特点,不能同时干两件事)
c》执行最后一行,打印300
d》待所有程序执行完,处于空闲状态时,会立马看有没有暂存起来的要执行
e》发现暂存起来的 $.get 中的 data1 未执行,待请求 data1.json返回 后,立即执行 data1
demo2:动态图片的加载
console.log(‘start‘);
const img = document.getElementById(‘img’);
img.onLoad = function() {
console.log(‘loadImg‘);
};
console.log(end);
执行结果: // start loadImg end 不知道图片何时完成加载,需要等待
demo3:事件绑定
console.log(‘start‘);
document.getElementById(‘box‘).on(click, function() {cosnole.log(‘click);});
console.log(‘end‘);
// start end (点击)click 不知道用户何时点击,需要等待
7、异步和单线程:
JS 是单线程的语言,所谓“单线程”就是一根筋,对于拿到的程序,一行一行的执行,直到上面的执行为完成,只能做这一件事
以上是关于logging:多线程调试时用来代替print和单步调试的主要内容,如果未能解决你的问题,请参考以下文章