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. [转] 机器学习是什么——周志华

    机器学习现在是一大热门,研究的人特多,越来越多的新人涌进来. 不少人其实并没有真正想过,这是不是自己喜欢搞的东西,只不过看见别人都在搞,觉着跟大伙儿走总不会吃亏吧. 问题是,真有个"大伙儿& ...

  2. SqlServer 自动化分区方案

    本文是我关于数据库分区的方案的一些想法,或许有些问题.仅供大家讨论.SqlServer (SqlServer 2005\SqlServer 2008)实现分区需要在企业版下进行. SqlServer的 ...

  3. Extjs4.x MVC开发模式,效率提高的两大秘诀

    最近做MVC开发的,遇到一个蛋疼的问题,每次加载模块都需要耗时3~4秒钟,才可以显示出完整的页面,通过监控,发现主要还是在Controller里慢,加载js文件等都是非常快的,但一到controlle ...

  4. hbase源码系列(五)Trie单词查找树

    在上一章中提到了编码压缩,讲了一个简单的DataBlockEncoding.PREFIX算法,它用的是前序编码压缩的算法,它搜索到时候,是全扫描的方式搜索的,如此一来,搜索效率实在是不敢恭维,所以在h ...

  5. python 语法最佳实践

    1. 列表推倒 我们知道, 列表类似于数组, 列表里存储的都是对象, 所以列表中可以存储"数字","字符串" 等对象. 列表用中括号扩起, 然后逗号分隔 列表内 ...

  6. 用source函数代替繁冗的R语言打包过程

    用source函数代替繁冗的R语言打包过程 经过初级的学习和使用R语言之后我们渐渐的开始动手写自己的R语言小程序,这些小程序因为和自己的工作非常契合而变得通用性不是那么强.因此,要让它们成为一个独立的 ...

  7. thinkphp中memcache的用法实例

    本文实例讲述了thinkphp中memcache的用法.分享给大家供大家参考.具体分析如下: 1.下载并安装memcache ① window下安装memcache. 下载memcached.exe ...

  8. ResNets和Inception的理解

    ResNets和Inception的理解 ResNet解析

  9. Ogre 编辑器一(MyGUI+Ogre整合与主界面)

    在查看Ogre例子时,想看材质要里的纹理,着色器代码都需要每个去查找,非常麻烦.也想看更新每个Ogre里的对象后有什么效果.然后看到Compositor组件与粒子组件时,想到能实时编辑着色器代码实时更 ...

  10. mysql 5.7.10 启动多实例笔记

    1. 复制配置文件 cp /etc/my.cnf /etc/my3308.cnf 2. 修改配置文件 3. 创建目录, 并赋予权限 4. 初始化数据库 ---> 有报错 2018-01-03T0 ...