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. 简析TCP的三次握手与四次分手<转>

    TCP是什么? 具体的关于TCP是什么,我不打算详细的说了:当你看到这篇文章时,我想你也知道TCP的概念了,想要更深入的了解TCP的工作,我们就继续.它只是一个超级麻烦的协议,而它又是互联网的基础,也 ...

  2. if 语句练习 身高体重问题

    public class d { /** * @param args */ public static void main(String[] args) { // TODO 自动生成的方法存根 int ...

  3. win10里如何在中文输入法里添加美国键盘

    在控制面板打开“时钟.语言和区域”设置界面,选中“语言”设置   “语言”设置里点击“添加语言”   在添加语言设置里选择“英语”,并点击“打开”按钮,在“区域变量”设置页面里选择“英语(美国)” , ...

  4. 【程序练习】——ini格式转换为xml格式

    ;Configuration of http [http] doamin=www.mysite.com port= cgihome=/cgi-bin   ;Configuration of db [d ...

  5. linq操作符:元素操作符

    元素操作符仅返回一个元素. 一.Fitst操作符 First操作符将返回序列中的第一个元素.如果序列中不包含任何元素,则First<T>方法将引发异常.来看看First()方法的定义: 从 ...

  6. Graph-DFS-图的深度优先遍历

    #include <iostream> using namespace std; /* 5 5 1 2 1 3 1 5 2 4 3 5 1 2 4 3 5 ---------------- ...

  7. 2012Google校园招聘笔试题

    1.已知两个数字为1~30之间的数字,甲知道两数之和,乙知道两数之积,甲问乙:“你知道是哪两个数吗?”乙说:“不知道”.乙问甲:“你知道是哪两个数吗?”甲说:“也不知道”.于是,乙说:“那我知道了”, ...

  8. Html5學習重點清單

    SVG webSQL 數據庫 SSE 服務推送 MathML 基於xml語法 Web 存储 webSockets通信 canvas 畫布操作 音頻和視頻 地理位置 Geolocation API We ...

  9. Apache Storm内部原理分析

    转自:http://shiyanjun.cn/archives/1472.html 本文算是个人对Storm应用和学习的一个总结,由于不太懂Clojure语言,所以无法更多地从源码分析,但是参考了官网 ...

  10. nextcloud私有云盘的部署

    nextcloud在centos系统下搭建自己的私有云盘 搭建一套自己的私有云盘,让数据存储更加方便.可靠.自己搭建的云存储,首先没有什么容量.下载速度的限制,而且本地访问速度很快.一开始以为Next ...