1、解决如果多次添加handlers重复打印的问题。在__add_handlers方法中作出判断。

2、由get_logger_and_add_handlers和get_logger_without_handlers返回logger类型的实例,不再使用代理模式在本类里面添加debug info  warning erro critical方法,解决日志fomatter的lineno显示的是本类的相关方法的行数,而不是具体打印日志地方的代码行数。

# coding=utf8
"""
@author bfzs
"""
import os
import unittest
import logging
from logging.handlers import RotatingFileHandler if os.name == 'posix':
from cloghandler import ConcurrentRotatingFileHandler format_dict = {
1: logging.Formatter('日志时间【%(asctime)s】 - 日志名称【%(name)s】 - 文件【%(filename)s】 - 第【%(lineno)d】行 - 日志等级【%(levelname)s】 - 日志信息【%(message)s】', "%Y-%m-%d %H:%M:%S"),
2: logging.Formatter('日志时间【%(asctime)s】 - 日志名称【%(name)s】 - 文件【%(filename)s】 - 第【%(lineno)d】行 - 日志等级【%(levelname)s】 - 日志信息【%(message)s】', "%Y-%m-%d %H:%M:%S"),
3: logging.Formatter('日志时间【%(asctime)s】 - 日志名称【%(name)s】 - 文件【%(filename)s】 - 第【%(lineno)d】行 - 日志等级【%(levelname)s】 - 日志信息【%(message)s】', "%Y-%m-%d %H:%M:%S"),
4: logging.Formatter('日志时间【%(asctime)s】 - 日志名称【%(name)s】 - 文件【%(filename)s】 - 第【%(lineno)d】行 - 日志等级【%(levelname)s】 - 日志信息【%(message)s】', "%Y-%m-%d %H:%M:%S"),
5: logging.Formatter('日志时间【%(asctime)s】 - 日志名称【%(name)s】 - 文件【%(filename)s】 - 第【%(lineno)d】行 - 日志等级【%(levelname)s】 - 日志信息【%(message)s】', "%Y-%m-%d %H:%M:%S"),
} class LogLevelException(Exception):
def __init__(self, log_level):
err = '设置的日志级别是 {0}, 设置错误,请设置为1 2 3 4 5 范围的数字'.format(log_level)
Exception.__init__(self, err) class LogManager(object):
"""
一个日志类,用于创建和捕获日志,支持将日志打印到控制台打印和写入日志文件。
""" def __init__(self, logger_name=None):
"""
:param logger_name: 日志名称,当为None时候打印所有日志
"""
self.logger = logging.getLogger(logger_name)
self._logger_level = None
self._is_add_stream_handler = None
self._log_path = None
self._log_filename = None
self._log_file_size = None
self._formatter = None def get_logger_and_add_handlers(self, log_level_int=1, is_add_stream_handler=True, log_path=None, log_filename=None, log_file_size=10):
"""
:param log_level_int: 日志输出级别,设置为 1 2 3 4 5,分别对应输出DEBUG,INFO,WARNING,ERROR,CRITICAL日志
:param is_add_stream_handler: 是否打印日志到控制台
:param log_path: 设置存放日志的文件夹路径
:param log_filename: 日志的名字,仅当log_path和log_filename都不为None时候才写入到日志文件。
:param log_file_size :日志大小,单位M,默认10M
:type logger_name :str
:type log_level_int :int
:type is_add_stream_handler :bool
:type log_path :str
:type log_filename :str
:type log_file_size :int
"""
self.__check_log_level(log_level_int)
self._logger_level = self.__transform_logger_level(log_level_int)
self._is_add_stream_handler = is_add_stream_handler
self._log_path = log_path
self._log_filename = log_filename
self._log_file_size = log_file_size
self._formatter = format_dict[log_level_int]
self.__set_logger_level()
self.__add_handlers()
return self.logger def get_logger_without_handlers(self):
"""返回一个不带hanlers的logger"""
return self.logger def __set_logger_level(self):
self.logger.setLevel(self._logger_level) @staticmethod
def __check_log_level(log_level_int):
if log_level_int not in [1, 2, 3, 4, 5]:
raise LogLevelException(log_level_int) @staticmethod
def __transform_logger_level(log_level_int):
logger_level = None
if log_level_int == 1:
logger_level = logging.DEBUG
elif log_level_int == 2:
logger_level = logging.INFO
elif log_level_int == 3:
logger_level = logging.WARNING
elif log_level_int == 4:
logger_level = logging.ERROR
elif log_level_int == 5:
logger_level = logging.CRITICAL
return logger_level def __add_handlers(self):
if self._is_add_stream_handler:
for h in self.logger.handlers:
if isinstance(h, logging.StreamHandler):
break
else:
self.__add_stream_handler()
if all([self._log_path, self._log_filename]):
for h in self.logger.handlers:
if os.name == 'nt':
if isinstance(h, RotatingFileHandler):
break
if os.name == 'posix':
if isinstance(h, (RotatingFileHandler, ConcurrentRotatingFileHandler)):
break else:
self.__add_file_handler() def __add_stream_handler(self):
"""
日志显示到控制台
"""
stream_handler = logging.StreamHandler()
stream_handler.setLevel(self._logger_level)
stream_handler.setFormatter(self._formatter)
self.logger.addHandler(stream_handler) def __add_file_handler(self):
"""
日志写入日志文件
"""
if not os.path.exists(self._log_path):
os.makedirs(self._log_path)
log_file = os.path.join(self._log_path, self._log_filename)
os_name = os.name
rotate_file_handler = None
if os_name == 'nt':
# windows下用这个,非进程安全
rotate_file_handler = RotatingFileHandler(log_file, mode="a", maxBytes=self._log_file_size * 1024 * 1024, backupCount=10,
encoding="utf-8")
if os_name == 'posix':
# linux下可以使用ConcurrentRotatingFileHandler,进程安全的日志方式
rotate_file_handler = ConcurrentRotatingFileHandler(log_file, mode="a", maxBytes=self._log_file_size * 1024 * 1024,
backupCount=10, encoding="utf-8")
rotate_file_handler.setLevel(self._logger_level)
rotate_file_handler.setFormatter(self._formatter)
self.logger.addHandler(rotate_file_handler) import time class Test(unittest.TestCase):
# @unittest.skip
def test_repeat_add_handlers_(self):
"""测试重复添加handlers"""
test_log = LogManager('test').get_logger_and_add_handlers(1, log_path='../logs', log_filename='test.log')
test_log = LogManager('test').get_logger_and_add_handlers(1, log_path='../logs', log_filename='test.log')
test_log = LogManager('test').get_logger_and_add_handlers(1, log_path='../logs', log_filename='test.log')
test_log = LogManager('test').get_logger_and_add_handlers(1, log_path='../logs', log_filename='test.log')
print('下面这一句不会重复打印四次和写入日志四次')
time.sleep(1)
test_log.debug('这一句不会重复打印四次和写入日志四次') def test_get_logger_without_hanlders(self):
"""测试没有handlers的日志"""
log = LogManager('test2').get_logger_without_handlers()
print('下面这一句不会被打印')
time.sleep(1)
log.info('这一句不会被打印') def test_add_handlers(self):
"""这样可以在具体的地方任意写debug和info级别日志,只需要在总闸处规定级别就能过滤,很方便"""
log0 = LogManager('test3').get_logger_and_add_handlers(2)
log1 = LogManager('test3').get_logger_without_handlers()
print('下面这一句是info级别,可以被打印出来')
time.sleep(1)
log1.info('这一句是info级别,可以被打印出来')
print('下面这一句是debug级别,不能被打印出来')
time.sleep(1)
log1.debug('这一句是debug级别,不能被打印出来') def test_only_write_log_to_file(self):
"""只写入日志文件"""
log5 = LogManager('test5').get_logger_and_add_handlers(is_add_stream_handler=False, log_path='../logs', log_filename='test5.log')
print('下面这句话只写入文件')
log5.debug('这句话只写入文件') if __name__ == "__main__":
unittest.main()

3、在__add_handlers中添加判断,如果该日志已经有该类型的handlers,就不再添加。

无论添加多少次日志handler,都不会重复打印。这在把日志实例作为模块里面的全局变量,项目中多个地方反复import这个模块,造成日志重复打印,可以很好解决掉。

修复日志,阻止给日志多次添加handlers时候重复打印的问题的更多相关文章

  1. 大幅度改变celery日志外观,修改成日志可点击跳转和鲜艳五彩日志,解决脚本中已添加handler的logger和框架日志重复记录问题。

    大幅度改变celery日志外观,修改成日志可点击跳转和鲜艳五彩日志,解决脚本中已添加handler的logger和框架日志重复记录问题.打猴子补丁. 先把脚本中的所有logger的handlers全部 ...

  2. 异步调试神器Slog,“从此告别看日志,清日志文件了”

    微信调试.API调试和AJAX的调试的工具,能将日志通过WebSocket输出到Chrome浏览器的console中  — Edit 92 commits 4 branches 3 releases ...

  3. MariaDB——(二) MariaDB 10.0.15 日志文件—undo 日志

          日志的记录和维护是数据库中相当重要的内容,写这篇文章和后面几篇文章作为学习官网文档的笔记.MariaDB数据库日志可分为二进制日志.查询日志.错误日志.myISAM表日志.relay日志和 ...

  4. .NET Core的日志[4]:将日志写入EventLog

    面向Windows的编程人员应该不会对Event Log感到陌生,以至于很多人提到日志,首先想到的就是EventLog.EventLog不仅仅记录了Windows系统自身针对各种事件的日志,我们的应用 ...

  5. .NET Core的日志[3]:将日志写入Debug窗口

    定义在NuGet包"Microsoft.Extensions.Logging.Debug"中的DebugLogger会直接调用Debug的WriteLine方法来写入分发给它的日志 ...

  6. .NET Core的日志[2]:将日志输出到控制台

    对于一个控制台应用,比如采用控制台应用作为宿主的ASP.NET Core应用,我们可以将记录的日志直接输出到控制台上.针对控制台的Logger是一个类型为ConsoleLogger的对象,Consol ...

  7. 【开源】OSharp3.0框架解说系列(6.2):操作日志与数据日志

    OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...

  8. linux查看ssh用户登录日志与操作日志

    linux查看ssh用户登录日志与操作日志 2013-11-01转载   ssh用户登录日志 linux下登录日志在下面的目录里:  代码如下 复制代码 cd /var/log 查看ssh用户的登录日 ...

  9. mysql之 日志体系(错误日志、查询日志、二进制日志、事务日志、中继日志)

    一. mysql错误日志:错误日志记录的事件:a).服务器启动关闭过程中的信息b).服务器运行过程中的错误信息c).事件调试器运行一个事件时间生的信息d).在从服务器上启动从服务器进程时产生的信息lo ...

随机推荐

  1. MVC的JavaScriptResult使用

    JavaScriptResult的使用有两个必要的前提: 1.Ajax 2.jquery.unobtrusive-ajax.js 使用代码示例 controller public ActionResu ...

  2. log4写完日志不会自动释放

    今天想做个日志记录功能.网上查了下.决定使用log4net 来做. 但是最发现在写日志的时候.会一直占用当前的日志文件.当你想查看的时候会提示另一个进程占用了该文件. 只有到他创建下一个日志文件.然后 ...

  3. 【oneday_onepage】——Microsoft to acquire Nokia's mobile phone business

    Nokia will focus on network infrastructure, mapping and locations services and technology developmen ...

  4. linux刻录iso到u盘

    需要的工具:Linux系统.U盘.ISO镜像文件.首先在Linux系统中打开终端,使用dd命令,格式如下:sudo dd if=xxx.iso of=/dev/sdb命令中xxx.iso是你的ISO镜 ...

  5. USB学习笔记连载(七):CY7C68013A 无法识别的可能原因

    最近一直在调试视频 采集卡,和PC端连接的是USB接口,使用的是cypress的CY7C68013A-56PVXC. //======================================= ...

  6. android下通过app名字打开程序(activity)链接

    Version:0.9 StartHTML:-1 EndHTML:-1 StartFragment:00000099 EndFragment:00004599 1.手机遥控器模拟快捷键启动app 刚开 ...

  7. Android R.java:10: “duplicate class”

    在使用mmm命令编译android的app时出现错误error : R.java:10: "duplicate class" 参考方法: http://www.xuebuyuan. ...

  8. 【总结】牛客职播第十期:程盟有你,way来可期

    一.介绍 携程旅行2018校园春招流程介绍&面试答疑 讲师:宋涛,李响 二.答疑 1,技术都是先通过笔试进行优胜劣汰.论真英雄还是靠技术! 2,英语四级必须过! 3,国外大公司招人,对他们的文 ...

  9. SQL数据查询之——单表查询

    一.SQL数据查询的一般格式 数据查询是数据库的核心操作.SQL提供了SELECT语句进行数据查询,其一般格式为: SELECT [ALL | DISTINCT]<目标列表达式>[,< ...

  10. HTML5实现摇一摇的功能(实测后)--转

    eviceMotionEvent(设备运动事件)返回设备有关于加速度和旋转的相关信息.加速度的数据将包含三个轴:x,y和z(示意如下图所 示,x轴横向贯穿手机屏幕或者笔记本键盘,y轴纵向贯穿手机屏幕或 ...