解决python logging遇到的坑 日志重复打印问题

python 中 logging模块 假如遇到 多线程 或者 多进程 或者在web框架中自定义logging的话(一个请求就是一个独立的线程)非常容易重复打印日志 和造成内存崩溃,所以:

解决方法如下:

重写日志方法 用类:

 class Log():
 import logging
 def __init__(self):
 self.logger = logging.getLogger(__name__)
 # 以下三行为清空上次文件
 # 这为清空当前文件的logging 因为logging会包含所有的文件的logging
 logging.Logger.manager.loggerDict.pop(__name__)
 # 将当前文件的handlers 清空
 self.logger.handlers = []
 # 然后再次移除当前文件logging配置
 self.logger.removeHandler(self.logger.handlers)
 # 这里进行判断,如果logger.handlers列表为空,则添加,否则,直接去写日志
 if not self.logger.handlers:
 # loggger 文件配置路径
 self.handler = logging.FileHandler(os.getcwd() + '/logger/%s_log/%s_score.log' % (str(dt.date.today()), str(dt.date.today())))
 # logger 配置等级
 self.logger.setLevel(logging.DEBUG)
 # logger 输出格式
 formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')
 # 添加输出格式进入handler
 self.handler.setFormatter(formatter)
 # 添加文件设置金如handler
 self.logger.addHandler(self.handler)

 # 以下皆为重写方法 并且每次记录后清除logger
 def info(self,message=None):
 self.__init__()
 self.logger.info(message)
 self.logger.removeHandler(self.logger.handlers)

 def debug(self,message=None):
 self.__init__()
 self.logger.debug(message)
 self.logger.removeHandler(self.logger.handlers)

 def warning(self,message=None):
 self.__init__()
 self.logger.warning(message)
 self.logger.removeHandler(self.logger.handlers)

 def error(self,message=None):
 self.__init__()
 self.logger.error(message)
 self.logger.removeHandler(self.logger.handlers)

 def critical(self, message=None):
 self.__init__()
 self.logger.critical(message)
 self.logger.removeHandler(self.logger.handlers)

亲测有效!

另外 模块尤其注意 例如web请求的时候 在接口处调用 然后引导传参 千万别做全局变量

补充:python中多个文件共用logger,重复打印问题的解决方案

问题背景&现象

最近在项目中,需要用python的logging库来将日志打印到文件中,然后将python脚本放到crontab中执行。所以写了一个logger的简单封装。

如下:

#!/usr/bin/python
# -*- coding:utf-8 -*-
import logging
import time
import os
class Log(object):
 '''
封装后的logging
 '''
 def __init__(self, logger=None, log_cate='search'):
  '''
   指定保存日志的文件路径,日志级别,以及调用文件
   将日志存入到指定的文件中
  '''
  # 创建一个logger
  self.logger = logging.getLogger(logger)
  self.logger.setLevel(logging.DEBUG)
  # 创建一个handler,用于写入日志文件
  self.log_time = time.strftime("%Y_%m_%d")
  file_dir = os.getcwd() + '/../log'
  if not os.path.exists(file_dir):
   os.mkdir(file_dir)
  self.log_path = file_dir
  self.log_name = self.log_path + "/" + log_cate + "." + self.log_time + '.log'
  # print(self.log_name)

  fh = logging.FileHandler(self.log_name, 'a') # 追加模式 这个是python2的
  # fh = logging.FileHandler(self.log_name, 'a', encoding='utf-8') # 这个是python3的
  fh.setLevel(logging.INFO)

  # 再创建一个handler,用于输出到控制台
  ch = logging.StreamHandler()
  ch.setLevel(logging.INFO)

  # 定义handler的输出格式
  formatter = logging.Formatter(
   '[%(asctime)s] %(filename)s->%(funcName)s line:%(lineno)d [%(levelname)s]%(message)s')
  fh.setFormatter(formatter)
  ch.setFormatter(formatter)

  # 给logger添加handler
  self.logger.addHandler(fh)
  self.logger.addHandler(ch)

  # 添加下面一句,在记录日志之后移除句柄
  # self.logger.removeHandler(ch)
  # self.logger.removeHandler(fh)
  # 关闭打开的文件
  fh.close()
  ch.close()

 def getlog(self):
  return self.logger

目的是让所有用到logger的地方,只import这个封装库就行,然后直接调用。比如调用logger的

a.py

#!/usr/bin/python
# -*- coding:utf-8 -*-

from common.log import Log
log = Log().getlog()
log.info("I am a.py")

b.py

#!/usr/bin/python
# -*- coding:utf-8 -*-

from common.log import Log
log = Log().getlog()
log.info("I am b.py")

c.py

#!/usr/bin/python
# -*- coding:utf-8 -*-

import a
import b
from common.log import Log
log = Log().getlog()
log.info("I am c.py")

此时执行c.py的结果如下:

➜ search git:(master) ✗ python c.py

[2019-01-14 15:58:35,807] a.py-><module> line:6 [INFO]I am a.py

[2019-01-14 15:58:35,808] b.py-><module> line:6 [INFO]I am b.py

[2019-01-14 15:58:35,808] b.py-><module> line:6 [INFO]I am b.py

[2019-01-14 15:58:35,809] c.py-><module> line:8 [INFO]I am c.py

[2019-01-14 15:58:35,809] c.py-><module> line:8 [INFO]I am c.py

[2019-01-14 15:58:35,809] c.py-><module> line:8 [INFO]I am c.py

可见,a.py, b.py,c.py的logger共用了,出现了重复打印。

问题原因分析

从现象可以得出,不同文件间的log系统是相互影响的,在a.py,b.py, c.py中,我们的调用方式是log = Log().getlog(), 即self.logger = logging.getLogger(logger),logger参数并未传递 , 所以得到的self.logger是RootLogger。

RootLogger是一个python程序内全局唯一的,所有Logger对象的祖先。所以我们对RootLogger的设定,自然会影响到所有的日志输出。简言之,就是先打开的文件中对log的设置,后打开的文件都会受到影响,都会走一遍logger的继承关系。在这个示例中,b.py在a.py之后被import, 所以b.py会执行一次自己的logger,再执行一次a.py中打开的RootLogger, 以此类推.........

问题解决方式

不用默认的RootLogger, 给每个Logger都加个名字。

a.py

from common.log import Log
log = Log(__name__).getlog()
log.info("I am a.py")

b.py

from common.log import Log
log = Log(__name__).getlog()
log.info("I am b.py")

c.py

import b
import a
from common.log import Log
log = Log(__name__).getlog()
log.info("I am c.py")

c.py的最新执行结果:

➜ search git:(master) ✗ python c.py

[2019-01-14 16:24:12,008] b.py-><module> line:6 [INFO]I am b.py

[2019-01-14 16:24:12,009] a.py-><module> line:6 [INFO]I am a.py

[2019-01-14 16:24:12,009] c.py-><module> line:10 [INFO]I am c.py

没有重复了,符合预期。问题得以解决。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • python (logging) 日志按日期、大小回滚的操作

    描述: 日志按日期.大小回滚 代码: # -*- coding: utf-8 -*- import os import logging.handlers log_dir = os.path.dirname(os.path.abspath(__file__)) + os.sep + 'logs' if not os.path.isdir(log_dir): os.makedirs(log_dir) # CONSTANT VARIABLES MODULE_NAME = 'my_module' LOG

  • python 实现logging动态变更输出日志文件名

    python作为一门非常容易上手的脚本语言,日志输出更是简单,logging模块,简单的设置配置和属性,就能实现到控制台输出日志,在basicConfig()设置文件名,就能够将日志信息写入文件,简直是简单到不能再简单. 最近在项目中就遇到一个日志问题,使用python编写的服务程序一直运行,连续处理一些任务,每个任务的关键信息都需要输出到文件中,便于维护人员查看,可是对于简单实用logging来说,日志写入文件非常简单,由于服务程序连续运行,一直向一个文件记录日志信息有些不妥,有常识的开发人员

  • Python中使用logging模块打印log日志详解

    学一门新技术或者新语言,我们都要首先学会如何去适应这们新技术,其中在适应过程中,我们必须得学习如何调试程序并打出相应的log信息来,正所谓"只要log打的好,没有bug解不了",在我们熟知的一些信息技术中,log4xxx系列以及开发Android app时的android.util.Log包等等都是为了开发者更好的得到log信息服务的.在Python这门语言中,我们同样可以根据自己的程序需要打出log. log信息不同于使用打桩法打印一定的标记信息,log可以根据程序需要而分出不同的l

  • python 日志模块logging的使用场景及示例

    前言 日志是对于软件执行所发生的事件的一种追踪记录方式.日常使用过程中对代码执行的错误和问题会进行查看日志来分析定位问题所在.平常编写代码以及调试也经常用到.通常的新手的做法是直接print打印,但是打印的结果只在控制台显示.今天我们学习一种高级的日志打印和记录模块logging. logging提供了一系列的函数,它们是debug(), info(), warning(), error(), 和critical(). 他们的使用场景请看下表 你想要执行的任务 此任务的最好的工具 对于命令行或程

  • python logging日志打印过程解析

    一. 基础使用 1.1 logging使用场景 日志是什么?这个不用多解释.百分之九十的程序都需要提供日志功能.Python内置的logging模块,为我们提供了现成的高效好用的日志解决方案.但是,不是所有的场景都需要使用logging模块,下面是Python官方推荐的使用方法: 任务场景 最佳工具 普通情况下,在控制台显示输出 print() 报告正常程序操作过程中发生的事件 logging.info()(或者更详细的logging.debug()) 发出有关特定事件的警告 warnings.

  • Python日志打印里logging.getLogger源码分析详解

    实践环境 WIN 10 Python 3.6.5 函数说明 logging.getLogger(name=None) getLogger函数位于logging/__init__.py脚本 源码分析 _loggerClass = Logger # ...略 root = RootLogger(WARNING) Logger.root = root Logger.manager = Manager(Logger.root) # ...略 def getLogger(name=None): "&quo

  • 解决python logging遇到的坑 日志重复打印问题

    python 中 logging模块 假如遇到 多线程 或者 多进程 或者在web框架中自定义logging的话(一个请求就是一个独立的线程)非常容易重复打印日志 和造成内存崩溃,所以: 解决方法如下: 重写日志方法 用类: class Log(): import logging def __init__(self): self.logger = logging.getLogger(__name__) # 以下三行为清空上次文件 # 这为清空当前文件的logging 因为logging会包含所有

  • 解决Python logging模块无法正常输出日志的问题

    废话少说,先上代码 File:logger.conf [formatters] keys=default [formatter_default] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s class=logging.Formatter [handlers] keys=console, error_file [handler_console] class=logging.StreamHandler formatter=d

  • python logging 日志轮转文件不删除问题的解决方法

    前言 最近在维护项目的python项目代码,项目使用了 python 的日志模块 logging, 设定了保存的日志数目, 不过没有生效,还要通过contab定时清理数据. 分析 项目使用了 logging 的 TimedRotatingFileHandler : #!/user/bin/env python # -*- coding: utf-8 -*- import logging from logging.handlers import TimedRotatingFileHandler l

  • python logging 重复写日志问题解决办法详解

    python logging 重复写日志问题 用Python的logging模块记录日志时,遇到了重复记录日志的问题,第一条记录写一次,第二条记录写两次,第三条记录写三次...很头疼,这样记日志可不行.网上搜索到了原因与解决方案: 原因:没有移除handler 解决:在日志记录完之后removeHandler 修改前示例代码: import logging def log(message): logger = logging.getLogger('testlog') streamhandler

  • python logging日志模块以及多进程日志详解

    本篇文章主要对 python logging 的介绍加深理解.更主要是 讨论在多进程环境下如何使用logging 来输出日志, 如何安全地切分日志文件. 1. logging日志模块介绍 python的logging模块提供了灵活的标准模块,使得任何Python程序都可以使用这个第三方模块来实现日志记录.python logging 官方文档 logging框架中主要由四个部分组成: Loggers: 可供程序直接调用的接口 Handlers: 决定将日志记录分配至正确的目的地 Filters:

  • python logging设置level失败的解决方法

    一.问题描述 在用python开发时经常用到logging这个包,根据官方示例,如果要指定日志级别可以写成如下的方式. import logging logging.basicConfig(level=logging.INFO) 但是在实际应用中,这种方式有时候会设置不成功,导致无法打印出info及以下级别的日志.一种最直接的解决方式是将这两行代码提到文件的最前面,保证在所有其他import语句之前. 示例如下. # encoding=utf8 import logging logging.ba

  • python logging.basicConfig不生效的原因及解决

    最近在写脚本时,明明在脚本里使用logging.basicConfig配置了log目录,可目录文件确实空的 import logging from Logger import logger as log # log.Log_Info('nihaohaohao') # 设置log的存储文件 logging.basicConfig(filename = os.path.join(os.getcwd(), 'logs/report_log.txt'), level = logging.DEBUG) l

  • 详解 python logging日志模块

    目录 1.日志简介 2.日志级别 3.修改日志级别 4.日志记录到文件 5.指定日志格式 6.记录器(logger) 7.处理器(Handler) 8.处理器操作 9.格式器(formatter) 10.logging.basicConfig 11.日志配置 转自微信公众号: Python之禅 1.日志简介 说到日志,无论是写框架代码还是业务代码,都离不开日志的记录,他能给我们定位问题带来极大的帮助. 记录日志最简单的方法就是在你想要记录的地方加上一句 print , 我相信无论是新手还是老鸟都

  • 详解 python logging日志模块

    目录 1.日志简介 2.日志级别 3.修改日志级别 4.日志记录到文件 5.指定日志格式 6.记录器(logger) 7.处理器(Handler) 8.处理器操作 9.格式器(formatter) 10.logging.basicConfig 11.日志配置 转自微信公众号: Python之禅 1.日志简介 说到日志,无论是写框架代码还是业务代码,都离不开日志的记录,他能给我们定位问题带来极大的帮助. 记录日志最简单的方法就是在你想要记录的地方加上一句 print , 我相信无论是新手还是老鸟都

  • python logging日志模块的详解

    python logging日志模块的详解 日志级别 日志一共分成5个等级,从低到高分别是:DEBUG INFO WARNING ERROR CRITICAL. DEBUG:详细的信息,通常只出现在诊断问题上 INFO:确认一切按预期运行 WARNING:一个迹象表明,一些意想不到的事情发生了,或表明一些问题在不久的将来(例如.磁盘空间低").这个软件还能按预期工作. ERROR:更严重的问题,软件没能执行一些功能 CRITICAL:一个严重的错误,这表明程序本身可能无法继续运行 这5个等级,也

随机推荐