设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间01 现在时间01 缓存数据 晚安,心上人,但愿,早日同床枕。

晚安,心上人,但愿,早日同床枕。

查看: 446|回复: 2

python更加灵活的Logger日志详解

[复制链接]

  离线 

TA的专栏

  • 打卡等级:即来则安
  • 打卡总天数:17
  • 打卡月天数:0
  • 打卡总奖励:212
  • 最近打卡:2025-04-17 03:23:01
等级头衔

等級:晓枫资讯-上等兵

在线时间
0 小时

积分成就
威望
0
贡献
322
主题
252
精华
0
金钱
1071
积分
612
注册时间
2023-1-25
最后登录
2025-5-31

发表于 2024-2-27 09:35:53 来自手机 | 显示全部楼层 |阅读模式
用到的4个类:
1、Logger:

打印日志用的对象;
设置日志等级,添加移除handler,添加移除filter,设置下级Logger,使用各种方法打印日志;
创建方式有两种,使用logging.getLogger("mylog")和创建实例logging.Logger("mylog");
推荐使用getLogger()的方式,不传name参数的获得的Logger为root Logger,传name参数的上级为root Logger;
直接实例化的Logger没有上级,日志等级为NOTSET,并且用该实例创建的下级Logger的上级直接为root Logger;
1.png
2.png

使用getLogger(name)传入相同的名字获得是同一个Logger;
3.png
4.png

Logger拥有上下级关系,父子记录器名称之间用“.”分割,Logger拥有有效日志等级概念(getEffectiveLevel()方法返回的是日志等级整数值),直接新建实例创建的Logger日志等级为NOTSET,默认的根记录器为WARNING等级,如果NOTSET等级的Logger为根记录器则处理所有消息,否则会委托给父级记录器,遍历父记录器链,直到找到非NOTSET的父记录器并把该等级作为自己的有效等级,如果直到根记录器也是NOTSET则处理所有消息;
5.png

6.png

2、Formatter:

日志打印格式;
创建方式为logging.Formatter(fmt=None, datefmt=None, style='%', validate=True)
fmt:格式化日志
datefmt:格式化fmt中的%(asctime)s
style:在3.2版本添加
validate:在3.8版本添加,不正确或不匹配的样式和fmt将引发ValueError;
跟上一篇一致python的logging日志模块
7.png

8.png

3、Filter:

过滤日志;
通过继承logging.Filter类,并实现 filter(self, record: LogRecord) -> int 方法,返回0或者False表示不通过,返回非0或者True表示可以通过;(from logging import LogRecord)
LogRecord可以操作的属性大概有这么多:
9.png

同一个Handler可以添加多个Filter,依次过滤,当前Filter通过后传递给下一个Filter;
10.png

11.png

4、Handler:

日志输出方式;
设置日志等级,设置formatter,添加移除filter;
Logger可以添加多个handler,将日志按不同格式和过滤送往不同的地方,同一个Logger只会添加同一个handler一次,多次添加无影响;
子Logger处理完自己的handler后,会将日志传递给父Logger的handler处理,依次向上传递,不要将同一个handler同时添加到父子Logger里,否则父子Logger都会处理会打印多次相同日志;
可以通过设置Logger对象的propagate属性为False关闭传递给父Logger的handler;
12.png

13.png

大概有这么多Handler:
14.png

(from logging import handlers)
15.png

StreamHandler:

构造方法 logging.StreamHandler(stream=None)
将日志发送到像sys.stdout、sys.stderr或类似文件的对象中(支持write()和flush()方法的对象),默认使用sys.stderr,3.2版本后还有个terminator属性,可以设置终止符(默认“\n”);
16.png
17.png

FileHandler:

构造方法 logging.FileHandler(filename, mode='a', encoding=None, delay=False)
将日志记录到文件中,默认文件将无限增长,如果delay为true,打开文件会延迟到第一次调用emit();
18.png
19.png

3.6版本开始,pathlib.Path也可以作为filename的值
20.png

NullHandler:

不做任何格式化和输出,提供给库开发人员使用;
WatchedFileHandler:

这是一个FileHandler,监控正在记录日志的文件,如果文件变动了则关闭文件重新打开;(windows系统不适用)
BaseRotatingHandler:

构造方法 logging.handlers.BaseRotatingHandler(filename, mode, encoding=None, delay=False)
是RotatingFileHandler和TimedRotatingFileHandler的基类,不需要实例化该类;
RotatingFileHandler:

构造方法 logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False)
按照日志文件大小和备份日志文件数量保存日志到文件;
maxBytes或者backupCount为0则不会滚动日志,当日志大小接近maxBytes时会用“.1”“.2”“.3”...后缀保存旧文件,并保持旧文件数量不超过backupCount,当前日志一直是没有后缀的那个文件;
21.png
22.png

TimedRotatingFileHandler:

构造方法 logging.handlers.TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None)
按照时间间隔和备份文件数量保存日志到文件;
when:“S”秒,“M”分钟,“H”小时,“D”天,“W0”-“W6”工作日(W0为周一),“midnight”午夜(不指定atTime的时候午夜12点,否则按照atTime的时间滚动)
interval:时间间隔;
backupCount:保留文件数量上限;
utc:为true时文件名后缀使用UTC时间否则使用本地时间;
atTime:3.4添加,datetime.time类型,指定一天中发生滚动日志的时间,只有when为“midnight”或者“W0”-“W6”时有效;
生成的备份文件文件名后缀格式为%Y-%m-%d_%H-%M-%S,第一次计算滚动时间(程序启动后)时会使用现有日志的最后修改时间或者当前时间计算;
23.png

24.png

SocketHandler:

构造方法 logging.handlers.SocketHandler(host, port)
将日志发送到网络套接字,基类使用的是TCP;
发送的二进制bytes是由编码的,由“内容长度+内容”组成,内容是用pickle序列化的一个dict(LogRecord,可以用logging.makeLogRecord(attrdict)转换),长度是用struct将序列化后的内容打包的一个大端无符号long数值,具体逻辑在SocketHandler类源码的makePickle()方法中有体现:
25.png

搞了半天才搞好的小demo(之前没看到编码方式还以为socket的编码,服务端接收数据解不出):
  1. #!/usr/bin/env python3
  2. # coding=utf-8

  3. import logging
  4. from logging import handlers
  5. import time

  6. log = logging.getLogger("mylog")
  7. log.setLevel(logging.DEBUG)
  8. h = logging.handlers.SocketHandler(host="127.0.0.1", port=8899)
  9. f = logging.Formatter("[%(name)s][%(asctime)s]%(message)s")
  10. h.setFormatter(f)
  11. log.addHandler(h)
  12. for x in range(1, 6):
  13.     log.info("test log %d" % x)
  14.     time.sleep(0.5)
复制代码
服务端demo:
  1. #!/usr/bin/env python3
  2. # coding=utf-8

  3. import socket
  4. import pickle
  5. import struct
  6. import logging


  7. def unPickle(bs: bytes):
  8.     data_len_bytes = bs[0:4]
  9.     data_len = struct.unpack(">L", data_len_bytes)[0]
  10.     pickled_data = bs[4: data_len + 4 + 1]
  11.     return pickle.loads(pickled_data)


  12. socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  13. socket_server.bind(("127.0.0.1", 8899))
  14. socket_server.listen(3)
  15. while True:
  16.     try:
  17.         client, client_addr = socket_server.accept()
  18.         while True:
  19.             data = client.recv(1024 * 10)
  20.             if data != b'':
  21.                 log_dict = unPickle(data)
  22.                 log_record = logging.makeLogRecord(log_dict)
  23.                 print("recv log:", log_record)
  24.     except Exception as e:
  25.         print("Exception:", e)
复制代码
先运行socket服务,再测日志,因为连接不到服务日志会被丢弃,运行结果:
26.png

DatagramHandler:

构造方法 logging.handlers.DatagramHandler(host, port)
继承了SocketHandler,使用UDP发送,类似SocketHandler的使用;
27.png

28.png

29.png

SysLogHandler:

构造方法 logging.handlers.SysLogHandler(address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER, socktype=socket.SOCK_DGRAM)
将日志发送到远程或者本地的Unix系统日志,未指定address则使用('localhost', 514)地址;
NTEventLogHandler:

构造方法 logging.handlers.NTEventLogHandler(appname, dllname=None, logtype='Application')
将日志发送到本地的Windows NT、Windows 2000、Windows XP系统日志,使用时需要Mark Hammond's Win32的python扩展;
SMTPHandler:

构造方法 logging.handlers.SMTPHandler(mailhost, fromaddr, toaddrs, subject, credentials=None, secure=None, timeout=1.0)
将日志发送到电子邮件;
mailhost使用(host, port)元组,toaddrs是一个字符串列表,credentials可以用(username, password)元组指定用户名密码
MemoryHandler:

构造方法 logging.handlers.MemoryHandler(capacity, flushLevel=ERROR, target=None, flushOnClose=True)
支持在内存中缓冲日志,当缓存将满或者出现某种严重情况时把日志发送到目标handler;
是BufferingHandler的子类;
每当向缓冲区添加日志的时候都会调用shouldFlush()判断是否需要刷新,如果需要则会调用flush()刷新;
HTTPHandler:

构造方法 logging.handlers.HTTPHandler(host, url, method='GET', secure=False, credentials=None, context=None)
通过GET或者POST向一个web服务器发送日志;
如果要指定端口,host可以使用host:port值;
secure为true,则使用HTTPS;
对HTTPHandler使用setFormatter()是无效的,HTTPHandler没有调用format()格式化,而是调用了mapLogRecord()方法然后使用urllib.parse.urlencode()编码的;
mapLogRecord()函数很简单(有需要可以重写):
30.png

而LogRecord()里是没有asctime字段的,所以log.asctime是错误的,但是logRecord里有created和msecs字段表时间:
31.png

小小的demo:
  1. #!/usr/bin/env python3
  2. # coding=utf-8

  3. import logging
  4. from logging import handlers
  5. import time

  6. log = logging.getLogger("mylog")
  7. log.setLevel(logging.DEBUG)
  8. h_get = handlers.HTTPHandler(host="127.0.0.1:8080", url="test_http_log", method="GET",
  9.                              secure=False, credentials=None, context=None)
  10. log.addHandler(h_get)
  11. h_post = handlers.HTTPHandler(host="127.0.0.1:8080", url="test_http_log", method="POST",
  12.                               secure=False, credentials=None, context=None)
  13. log.addHandler(h_post)

  14. h = logging.StreamHandler()
  15. f = logging.Formatter("[%(levelname)s][%(asctime)s]%(message)s")
  16. h.setFormatter(f)
  17. log.addHandler(h)
  18. for x in range(1, 3):
  19.     log.info("test HTTP log %d" % x)
  20.     time.sleep(0.5)
复制代码
http服务端:
  1. #!/usr/bin/env python3
  2. # coding=utf-8

  3. from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
  4. from urllib import parse
  5. import logging
  6. import time


  7. class MyHttpServer(BaseHTTPRequestHandler):
  8.     def do_GET(self):
  9.         url = parse.urlparse(self.path)
  10.         q = parse.parse_qs(url.query)
  11.         log = logging.makeLogRecord(q)
  12.         log_created = time.localtime(float(log.created[0]))
  13.         format_time = time.strftime("%Y/%m/%d %H:%M:%S", log_created)
  14.         print("-GET:", log.name, log.levelname, log.msg, format_time+","+str(int(float(log.msecs[0])//1)))
  15.         self.send_response(200)
  16.         self.end_headers()
  17.         self.wfile.flush()

  18.     def do_POST(self):
  19.         length = int(self.headers["content-length"])
  20.         data = self.rfile.read(length)
  21.         data = data.decode(encoding="utf-8")
  22.         data_dict = parse.parse_qs(data)
  23.         data = logging.makeLogRecord(data_dict)
  24.         print("=POST:", data)
  25.         self.send_response(200)
  26.         self.end_headers()
  27.         self.wfile.flush()


  28. httpserver = ThreadingHTTPServer(("127.0.0.1", 8080), MyHttpServer)
  29. httpserver.serve_forever()
复制代码
先运行服务端再跑日志demo,运行结果:
32.png

33.png

QueueHandler/QueueListener:

构造方法 logging.handlers.QueueHandler(queue)
logging.handlers.QueueListener(queue, *handlers, respect_handler_level=False)
将日志发送到队列,用于处理队列或者多线程模块情况;
queue可以是类队列的任何对象(原样传给dequeue()函数),也可以用queue.SimpleQueue代替queue;
QueueHandler的小demo:
  1. #!/usr/bin/env python3
  2. # coding=utf-8

  3. import logging
  4. from logging import handlers
  5. from logging import LogRecord
  6. import queue


  7. class MyLogHandler(logging.Handler):
  8.     def handle(self, record: LogRecord) -> None:
  9.         print(record)


  10. log = logging.getLogger("mylog")
  11. log.setLevel(logging.DEBUG)
  12. q = queue.SimpleQueue()
  13. h = handlers.QueueHandler(q)
  14. f = logging.Formatter("[%(levelname)s]%(message)s")
  15. h.setFormatter(f)
  16. log.addHandler(h)

  17. listener = handlers.QueueListener(q, MyLogHandler(), respect_handler_level=False)

  18. listener.start()
  19. print("listener started")
  20. log.info("test queue log1 info")
  21. log.error("test queue log2 error")

  22. listener.stop()
  23. print("listener stopted")
  24. log.info("test queue log3 info")
  25. log.error("test queue log4 error")
  26. print("test end")
复制代码
执行结果:
34.png

常用Demo:
  1. #!/usr/bin/env python3
  2. # coding=utf-8

  3. import logging
  4. from logging import LogRecord
  5. from logging import handlers
  6. import sys

  7. # log1 = logging.Logger("a", logging.DEBUG)
  8. log1 = logging.getLogger("a")
  9. _log1 = logging.getLogger("a")
  10. print(log1 is _log1, id(log1), id(_log1))
  11. log1.setLevel(logging.DEBUG)
  12. log2 = log1.getChild("b")
  13. log3 = log1.getChild("c")
  14. print(log1)
  15. print(log2)
  16. print(log3)

  17. h1 = logging.StreamHandler()
  18. format1 = logging.Formatter("[H1] [%(levelname)-8s] %(message)s")
  19. h1.setFormatter(format1)
  20. log1.addHandler(h1)

  21. h2 = logging.StreamHandler()
  22. format2 = logging.Formatter(fmt="[H2][%(levelname)7s][%(asctime)s]%(message)s", datefmt="%c")
  23. h2.setFormatter(format2)


  24. class myFilter(logging.Filter):
  25.     def filter(self, record: LogRecord) -> int:
  26.         print("record:" + repr(record))
  27.         if "HELLO" in record.msg:
  28.             return True
  29.         else:
  30.             return False


  31. h2.addFilter(myFilter())
  32. log2.addHandler(h2)
  33. # log2.addFilter(myFilter())

  34. h3 = logging.StreamHandler()
  35. format3 = logging.Formatter("[H3]{%(levelname)s}{%(name)s}%(message)s")
  36. h3.setFormatter(format3)
  37. h3.setStream(sys.stdout)
  38. h3.setLevel(logging.INFO)
  39. log3.addHandler(h3)
  40. #
  41. h3_file = logging.FileHandler(filename="test_Logging2.log", mode="w")
  42. h3_file.setFormatter(logging.Formatter("[H3File][%(levelname)8s][%(asctime)s]%(message)s"))
  43. log3.addHandler(h3_file)
  44. #
  45. h3_rotatingfile = handlers.RotatingFileHandler(filename="test_Logging2_rotating.log", mode="a",
  46.                                                maxBytes=256, backupCount=3,
  47.                                                encoding="utf-8", delay=False)
  48. h3_rotatingfile.setFormatter(logging.Formatter("[h3_rotating][%(levelname)s] %(message)s"))
  49. log3.addHandler(h3_rotatingfile)
  50. #
  51. h3_timedrotationgfile = handlers.TimedRotatingFileHandler(filename="test_Logging2_timedrotating.log",
  52.                                                           when="S", interval=5,
  53.                                                           backupCount=5, encoding="utf-8",
  54.                                                           delay=False, utc=False,
  55.                                                           # atTime=
  56.                                                           )
  57. h3_timedrotationgfile.setFormatter(logging.Formatter("[H3timed][%(levelname)s]%(message)s"))
  58. log3.addHandler(h3_timedrotationgfile)
  59. log3.propagate = False

  60. log1.debug("test log1")
  61. log1.warning("warn log1")
  62. log2.debug("test log2 HELLO")
  63. log2.info("info log2")
  64. log2.warning("warn log2 HELLO")
  65. log2.error("error log2")
  66. log3.debug("test log3")
  67. log3.warning("warn log3")
复制代码
多次执行结果中的一次:
35.png

生成的日志文件:
36.png

参考:
logging — Logging facility for Python — Python 3.8.12 documentation
logging.handlers — Logging handlers — Python 3.8.12 documentation
Python3 日志(内置logging模块) - 天马行宇 - 博客园
到此这篇关于python更加灵活的Logger日志的文章就介绍到这了,更多相关python更加灵活的Logger日志内容请搜索晓枫资讯以前的文章或继续浏览下面的相关文章希望大家以后多多支持晓枫资讯!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
17
积分
14
注册时间
2022-12-25
最后登录
2022-12-25

发表于 2024-9-19 00:58:25 | 显示全部楼层
感谢楼主分享。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
18
积分
16
注册时间
2022-12-25
最后登录
2022-12-25

发表于 3 天前 | 显示全部楼层
路过,支持一下
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1楼
2楼
3楼

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表