python logging默认情况下打印_Python中logging在多进程环境下打印日志

2023-10-27

因为涉及到进程间互斥与通信问题,因此默认情况下Python中的logging无法在多进程环境下打印日志。但是查询了官方文档可以发现,推荐了一种利用logging.SocketHandler的方案来实现多进程日志打印。

其原理很简单,概括一句话就是说:多个进程将各自环境下的日志通过Socket发送给一个专门打印日志的进程,这样就可以防止多进程打印的冲突与混乱情况。

本文主要记录下SocketHandler真实的用法情况:

1 时序图

简单说明下逻辑:主进程(MainProcess)启动一个专门打印日志的进程(LogReceiverProcess),并且将自己(主进程)环境下的日志都“重定向”给LogReceiverProcess。同理,在后续逻辑中启动的所有工作子进程(WorkerProcess)都做一样的操作,把自己环境下的日志都“重定向”给日志进程去打印。

2 实现代码

2.1 日志进程

日志进程的代码核心在于要建立一个TCP Server来接收并处理Log record,代码如下:

1 importos2 importlogging3 importlogging.handlers4 importtraceback5 importcPickle6 importstruct7 importSocketServer8 from multiprocessing importProcess9

10 classLogRecordStreamHandler(SocketServer.StreamRequestHandler):11 defhandle(self):12 whileTrue:13 try:14 chunk = self.connection.recv(4)15 if len(chunk) < 4:16 break

17 slen = struct.unpack(">L", chunk)[0]18 chunk =self.connection.recv(slen)19 while len(chunk) <20 chunk="chunk" self.connection.recv obj="self.unpickle(chunk)22" record="logging.makeLogRecord(obj)23" self.handle_log_record>

25 except:26 break

27

28 @classmethod29 defunpickle(cls, data):30 returncPickle.loads(data)31

32 defhandle_log_record(self, record):33 if self.server.logname is notNone:34 name =self.server.logname35 else:36 name =record.name37 logger =logging.getLogger(name)38 logger.handle(record)39

40

41 classLogRecordSocketReceiver(SocketServer.ThreadingTCPServer):42 allow_reuse_address = 1

43

44 def __init__(self, host='localhost', port=logging.handlers.DEFAULT_TCP_LOGGING_PORT, handler=LogRecordStreamHandler):45 SocketServer.ThreadingTCPServer.__init__(self, (host, port), handler)46 self.abort =047 self.timeout = 1

48 self.logname =None49

50 defserve_until_stopped(self):51 importselect52 abort =053 while notabort:54 rd, wr, ex =select.select([self.socket.fileno()], [], [], self.timeout)55 ifrd:56 self.handle_request()57 abort =self.abort58

59

60 def_log_listener_process(log_format, log_time_format, log_file):61 log_file =os.path.realpath(log_file)62 logging.basicConfig(level=logging.DEBUG, format=log_format, datefmt=log_time_format, filename=log_file, filemode='a+')63

64 #Console log

65 console =logging.StreamHandler()66 console.setLevel(logging.INFO)67 console.setFormatter(logging.Formatter(fmt=log_format, datefmt=log_time_format))68 logging.getLogger().addHandler(console)69

70 tcp_server =LogRecordSocketReceiver()71

72 logging.debug('Log listener process started ...')73 tcp_server.serve_until_stopped()

View Code

关键点:

(1)TCPServer的构建逻辑,拆包还原Log记录;

(2)在日志进程中设定好logging记录级别和打印方式,这里除了指定文件存储还添加了Console打印。

2.2 其他进程

除了日志进程之外的进程,设置logging都“重定向”给日志进程,并且要关闭当前进程的日志在Console打印(默认会显示Warning级别及以上的日志到Console),否则Console上日志展示会有重复凌乱的感觉。

1 classLogHelper:2 #默认日志存储路径(相对于当前文件路径)

3 default_log_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'logs')4

5 #记录当前实际的日志所在目录

6 current_log_path = ''

7

8 #记录当前实际的日志完整路径

9 current_log_file = ''

10

11 #日志文件内容格式

12 log_format = '[%(asctime)s.%(msecs)03d][%(processName)s][%(levelname)s][%(filename)s:%(lineno)d] %(message)s'

13

14 #日志中时间格式

15 log_time_format = '%Y%m%d %H:%M:%S'

16

17 #日志进程

18 log_process =None19

20 def __init__(self):21 pass

22

23 @staticmethod24 defprint_console_log(level, message):25 print '--------------------------------------------------'

26 if level ==logging.WARN:27 level_str = '[WARN]'

28 elif level ==logging.ERROR:29 level_str = '[ERROR]'

30 elif level ==logging.FATAL:31 level_str = '[FATAL]'

32 else:33 level_str = '[INFO]'

34 print '\t%s %s' %(level_str, message)35 print '--------------------------------------------------'

36

37 @staticmethod38 def init(clear_logs=True, log_path=''):39 #40 console =logging.StreamHandler()41 console.setLevel(logging.FATAL)42 logging.getLogger().addHandler(console)43

44 try:45 #如果外部没有指定日志存储路径则默认在common同级路径存储

46 if log_path == '':47 log_path =LogHelper.default_log_path48 if notos.path.exists(log_path):49 os.makedirs(log_path)50 LogHelper.current_log_path =log_path51

52 #清理旧的日志并初始化当前日志路径

53 ifclear_logs:54 LogHelper.clear_old_log_files()55 LogHelper.current_log_file =LogHelper._get_latest_log_file()56

57 socket_handler = logging.handlers.SocketHandler('localhost', logging.handlers.DEFAULT_TCP_LOGGING_PORT)58 logging.getLogger().setLevel(logging.DEBUG)59 logging.getLogger().addHandler(socket_handler)60

61 #62 LogHelper.start()63

64 exceptException, ex:65 LogHelper.print_console_log(logging.FATAL, 'init() exception: %s' %str(ex))66 traceback.print_exc()67

68 @staticmethod69 defstart():70 if LogHelper.log_process isNone:71 LogHelper.log_process = Process(target=_log_listener_process, name='LogRecorder', args=(LogHelper.log_format, LogHelper.log_time_format, LogHelper.current_log_file))72 LogHelper.log_process.start()73 else:74 pass

75

76 @staticmethod77 defstop():78 if LogHelper.log_process isNone:79 pass

80 else:81 LogHelper.log_process.terminate()82 LogHelper.log_process.join()83

84 @staticmethod85 def_get_latest_log_file():86 latest_log_file = ''

87 try:88 ifos.path.exists(LogHelper.current_log_path):89 for maindir, subdir, file_name_list inos.walk(LogHelper.current_log_path):90 for file_name infile_name_list:91 apath =os.path.join(maindir, file_name)92 if apath >latest_log_file:93 latest_log_file =apath94

95 if latest_log_file == '':96 latest_log_file = LogHelper.current_log_path + os.sep + 'system_'

97 latest_log_file += time.strftime("%Y%m%d_%H%M%S", time.localtime(time.time())) + '.log'

98

99 exceptException, ex:100 logging.error('EXCEPTION: %s' %str(ex))101 traceback.print_exc()102

103 finally:104 returnlatest_log_file105

106 @staticmethod107 defget_log_file():108 returnLogHelper.current_log_file109

110 @staticmethod111 defclear_old_log_files():112 if notos.path.exists(LogHelper.current_log_path):113 logging.warning('clear_old_log_files() Not exist: %s' %LogHelper.current_log_path)114 return

115

116 try:117 for maindir, subdir, file_name_list inos.walk(LogHelper.current_log_path):118 for file_name infile_name_list:119 apath =os.path.join(maindir, file_name)120 if apath !=LogHelper.current_log_file:121 logging.info('DEL -> %s' %str(apath))122 os.remove(apath)123 else:124 with open(LogHelper.current_log_file, 'w') as f:125 f.write('')126

127 logging.debug('Clear log done.')128

129 exceptException, ex:130 logging.error('EXCEPTION: %s' %str(ex))131 traceback.print_exc()

View Code

20>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python logging默认情况下打印_Python中logging在多进程环境下打印日志 的相关文章

  • springbootweb请求响应

    web三层架构流程 先是前端传送一个请求给后端 然后controller层接受数据进行处理 传给service层进行逻辑处理再交给dao层 dao层访问数据库 数据库再返回数据给dao层 然后再传给service层处理 最后回到contro
  • ResourceUtils

    ResourceUtils org springframework util ResourceUtils 从资源路径获取文件 判断字符串是否是一个合法的 URL 字符串 static boolean isUrl String resourc
  • Java自动装箱和自动拆箱

    基本类型的包装类 在java中 每一种基本类型都有一个对应的包装类 这些类都在java lang包中 8种基本数据类型所对应的包装类是byte Byte short Short int Integter long Long char Cha
  • 【金融系列】使用Python分析债券,画零息利率曲线,对债券进行精确定价,计算债券的麦考利久期、修正久期和凸度,并进行价格敏感性分析

    目前许多代码和资源在进行债券定价时 大多以债券发行日作为贴现值的时间点 但在实际应用中我们常常需要对早就发行的债券进行定价 这就需要计算准确的现金流 现金流日距离当前贴现时间点的时间距离和不同时间距离的零息利率 这一过程会较多使用插值法 还
  • css中的渐变的属性,CSS3 渐变属性(Gradients)-线性渐变(linearGradient)

    通过使用 CSS3 渐变 gradients 你可以减少下载的事件和宽带的使用 此外 渐变效果的元素在放大时看起来效果更好 因为渐变 gradient 是由浏览器生成的 丛本质上来说 既然 background image 属性中的渐变是浏
  • element框架table多选分页时保留原有选中项(vue2和vue3均适用)

    element中table表格多选 分页 1 表格多选 手动添加一个 el table column 设 type 属性为 selection 即可
  • DLT(Direct Linear Transform)算法

    一 定义 直接线性变换解法是建立像点的 坐标仪坐标 和相应物点的物方空间坐标直接的线性关系的解法 直接线性变换解法的特点 不归心 不定项 不需要内外方位元素的起始值 物方空间需布置一组控制点 特别适合于处理非量测相机所摄影像 本质是一种空间
  • 数据结构常用的算法

    一 排序算法 本质上就是按照某种顺序将一组数排好 分多次重复进行 每次只负责把一个数字放到合适的位置上 两种思路 先确定一个数字 然后根据数据找合适的位置 先确定一个位置 根据位置找合适的数字 1 冒泡排序算法 先确定位置 选最前面或者最后
  • 解决protobuf内存泄漏的3种办法

    1 protobuf对象是如何释放 数组 内存的 毫无疑问是 通过调用析构函数 只要让protobuf定义的对象调用析构函数 无论嵌套了多少层数据 包含了多少个数组都可以释放new出来的内存 2 protobuf对象Clear 接口和STL
  • clickhouse系列第三篇之clickhouse客户端使用教程(1.命令行客户端连接)

    文章目录 一 客户端支持的配置项 1 配置项的配置方式 1 1 从命令行传入 1 2 配置文件 2 配置项列表 二 客户端用法 1 非交互模式 1 1 query 参数指定执行语句 1 2 query 参数和发送到stdin的数据联合使用
  • JVM -- 类加载(七)

    一 加载 将类的字节码载入方法区中 内部采用 C 的 instanceKlass 描述 java 类 它的重要 field 有 java mirror 即 java 的类镜像 如对 String 来说 就是 String class 作用是
  • 前端 报错处理(长期更新 2023.9.06)

    目录 一 ECharts 相关 1 1 Error yAxis 0 not found 1 2 Cannot read properties of undefined reading 0 1 3 Cannot read properties
  • Python编程:从入门到实践 动手试一试之8-6

    根据之前学习的返回字典先写了一版 定义函数city countyr 并定义两个形参guojia和city def city country guojia city 定义函数国家和对应的城市 C city guo guojia chen ci
  • 推荐微信小程序常用的几个UI组

    1 WeUI WeUI 是一套同微信原生视觉体验一致的基础样式库 由微信官方设计团队为微信 Web 开发量身设计 可以令用户的使用感知更加统一 包含button cell dialog progress toast article acti
  • ubuntu下安装软件的方法

    一 ubuntu下软件安装 ubuntu系统下 软件安装方法有几种 常用的方法如下 1 使用 apt 工具安装 2 deb 软件包安装 3 自己下载程序源码编译安装 4 通过 ubuntu系统自带的软件中心安装 这种方法不常用 因为一直不太
  • python中SSL/TLS认证失败的解决方案

    重装了ubuntu22 04版本的系统后 跑yolov5的train py脚本时出现以下报错 原因是本地计算机上缺少了需要的根证书 File home anaconda3 envs py37 lib python3 7 urllib req
  • PaddleGAN

    产品动态 人脸编辑神器 StyleGAN V2人脸属性编辑之年龄变换 时光穿梭机 一键实现变老变年轻 完整在线教程 视频超分SOTA算法PP MSVSR 一行命令从 马赛克 到 高清影像 完整在线教程 人脸编辑神器 StyleGAN V2人
  • 爬虫日常练习-艾图网单页面图片爬取

    文章目录 爬虫练习 分析网站 代码设计 下载图片 完整代码 爬虫练习 hello 大家好 好久不见了 无聊的网友今天开始更新关于爬虫的一些日常练习 每次学习完一个新的知识后没有多的案例给自己练习真的很不舒服 希望该系列文章能够让刚刚开始学习
  • 【Linux】调试器---gdb的使用

    文章目录 一 背景知识 二 安装gdb 三 gdb的用法 使用须知 gdb的常用指令 1 进入调试 2 退出调试操作 3 显示源代码 4 设置断点breakPoint 5 查看断点信息 禁用断点 开启断点 删除断点 6 运行程序 开始调试r

随机推荐

  • 开发者自述:我是怎样理解支持向量机(SVM)与神经网络的

    https www leiphone com news 201705 v10u2BOvGHEbzBpV html 写在前面 囫囵吞枣看完SVM 个人感觉如果不好好理解一些概念 或说如果知其然而不知其所以然的话 不如不看 因此我想随便写一写
  • 03MySQL数据库表练习

    第一题 1 创建数据库 mysql gt create database Market 2 创建数据表customers 在c num字段上添加主键约束和自增约束 在c birth字段上添加非空约束 mysql gt use Market
  • 二进制,八进制,十进制,十六进制相互转换的快速记忆法

    1 十进制转换为R进制 都是使用除数取余法来转换 结果按倒序来 a 十进制转换为二进制 就一直除以2 直到余数比2小 商为0为止 b 十进制转换为八进制 就一直除以8 直到余数比8小 商为0为止 c 十进制转换为十六进制 就一直除以16 直
  • 2023/09/19 qt day3

    头文件 ifndef WIDGET H define WIDGET H include
  • java String转数组

    java String转数组 String转集合 将字符串转化为数组 如果你和我一样你们公司有人这样存数据的话 这就很气人 如果用分割的方法的话 还需要去除前后两个中括号 还有两个双引号要分割 气死人 所以想要转数组例如 arr a b c
  • 解决fastjson解析List对象出现{“$ref“:“$.data[0]“}的问题

    返回数据问题 例子 List
  • Linux 实验六

    编写 段bash shell程序 保存为 program sh 完成以下输出 可循环执 please input a number 5 回 5 4 3 2 1 4 3 2 1 3 2 1 2 1 1 bin bash echo please
  • Linux网络编程:多进程实现TCP通信

    服务器端代码 TCP 通信的服务器端 多进程实现并发服务器 父进程accept 子进程用于通信 include
  • linux如何运行ipynb文件_怎么在Jupyter里打开ipynb文件

    方法一 1 在使用Anaconda集成环境安装TensorFlow时 里面自带安装 Jupyter 安装完成后 打开开始菜单找到Anaconda3 64 bit 点击Anaconda Prompt 类似windows的命令行工具 2 在命令
  • QT怎么发送带结构体数据的信号?

    当发送的信号是结构体时 第一步 定义一个结构体 并在包含该结构体的类里面注册该结构体 通过此方法Q DECLARE METATYPE T 第二步 作为信号输出时 不能直接传结构体 要先包装一下结构再传出去 接收时 也要拆开包装 才能拿到数据
  • docker 镜像/容器的打包、导出、导入

    目录 一 将变动过的容器打包生成新的镜像 二 对镜像进行导出导入 1 将镜像导出为一个镜像img文件 2 将img镜像文件导入 复制出一个完全一样镜像 三 对容器进行导入导出 1 将容器导出为一个镜像tar文件 2 将镜像tar文件导入 生
  • vuejs项目纯js导出word、在线下载富文本内容或者网页另存为word文件

    所有前端导入导出方法集合 前端必备技能知识 JS导出Blob流文件为Excel表格 Vue js使用Blob的方式实现excel表格的下载 流文件下载 勤动手多动脑少说多做厚积薄发 CSDN博客 js文件流导出excel表格效果 重点 a
  • 【有奖调研】

    2022年 区块链价值被不断挖掘 成为Web3 元宇宙 数字藏品等众多产业的基石 我们面向广大开发者以及区块链爱好者发起本次调研 以了解大家对这项极具潜力的新技术的认知 为感谢大家的认真作答 我们将从有效问卷中随机抽取30名幸运者 赠送精品
  • 使用gsoap由.h文件生成wsdl相关问题

    生成wsdl文件步骤 头文件如下 ws interface h ifndef WS INTERFACE H define WS INTERFACE H 注意 以下注释是必要的 gsoap ns service name ws interfa
  • win7、win10进程pid4占用80端口的解决办法

    https jingyan baidu com article 7e4409533ffe092fc1e2ef10 html 今天想用wamp架设服务器 但是程序启动不起来 查看系统端口 80端口被占用 进程PID是4 我的系统是WIN10
  • 9-基于STM32无刷直流电机控制器的设计仿真与实现(原理图+源码+仿真工程+论文+PPT+参考英文文献)

    基于STM32无刷直流电机控制器的设计仿真与实现 原理图 源码 仿真工程 论文 PPT 参考英文文献 文章目录 基于STM32无刷直流电机控制器的设计仿真与实现 原理图 源码 仿真工程 论文 PPT 参考英文文献 资料 任务书 设计说明书
  • 【详解C语言指针】我真的让C指针给我唱征服了~乌拉

    文章目录 前言 一 字符指针 1 字符指针的定义 2 字符指针的作用 3 字符指针的特点 二 指针数组 1 指针数组的定义 2 指针数组的使用 三 数组指针 1 数组指针的定义 2 细说指针 2 1 指针类型 2 2 指针所指向的类型 2
  • Qt 多线程中的信号/槽

    Qt 多线程中的信号 槽 connect函数的五个参数表示的意义依次为 sender signal receiver slot connectionTpye 其中槽可以是receiver的成员函数 或者是任意可访问的静态函数 在多线程的情形
  • HTML2CANVAS使用总结

    前言 最近遇到了个功能 要把表单转化成图片来保存 这让我想到了一个插件HTML2CANVAS 这里给大家分享下它的用法和我使用的过程 html2canvas 能够实现在用户浏览器端直接对整个或部分页面进行截屏 这个html2canvas脚本
  • python logging默认情况下打印_Python中logging在多进程环境下打印日志

    因为涉及到进程间互斥与通信问题 因此默认情况下Python中的logging无法在多进程环境下打印日志 但是查询了官方文档可以发现 推荐了一种利用logging SocketHandler的方案来实现多进程日志打印 其原理很简单 概括一句话