修复日志,阻止给日志多次添加handlers时候重复打印的问题
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时候重复打印的问题的更多相关文章
- 大幅度改变celery日志外观,修改成日志可点击跳转和鲜艳五彩日志,解决脚本中已添加handler的logger和框架日志重复记录问题。
大幅度改变celery日志外观,修改成日志可点击跳转和鲜艳五彩日志,解决脚本中已添加handler的logger和框架日志重复记录问题.打猴子补丁. 先把脚本中的所有logger的handlers全部 ...
- 异步调试神器Slog,“从此告别看日志,清日志文件了”
微信调试.API调试和AJAX的调试的工具,能将日志通过WebSocket输出到Chrome浏览器的console中 — Edit 92 commits 4 branches 3 releases ...
- MariaDB——(二) MariaDB 10.0.15 日志文件—undo 日志
日志的记录和维护是数据库中相当重要的内容,写这篇文章和后面几篇文章作为学习官网文档的笔记.MariaDB数据库日志可分为二进制日志.查询日志.错误日志.myISAM表日志.relay日志和 ...
- .NET Core的日志[4]:将日志写入EventLog
面向Windows的编程人员应该不会对Event Log感到陌生,以至于很多人提到日志,首先想到的就是EventLog.EventLog不仅仅记录了Windows系统自身针对各种事件的日志,我们的应用 ...
- .NET Core的日志[3]:将日志写入Debug窗口
定义在NuGet包"Microsoft.Extensions.Logging.Debug"中的DebugLogger会直接调用Debug的WriteLine方法来写入分发给它的日志 ...
- .NET Core的日志[2]:将日志输出到控制台
对于一个控制台应用,比如采用控制台应用作为宿主的ASP.NET Core应用,我们可以将记录的日志直接输出到控制台上.针对控制台的Logger是一个类型为ConsoleLogger的对象,Consol ...
- 【开源】OSharp3.0框架解说系列(6.2):操作日志与数据日志
OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...
- linux查看ssh用户登录日志与操作日志
linux查看ssh用户登录日志与操作日志 2013-11-01转载 ssh用户登录日志 linux下登录日志在下面的目录里: 代码如下 复制代码 cd /var/log 查看ssh用户的登录日 ...
- mysql之 日志体系(错误日志、查询日志、二进制日志、事务日志、中继日志)
一. mysql错误日志:错误日志记录的事件:a).服务器启动关闭过程中的信息b).服务器运行过程中的错误信息c).事件调试器运行一个事件时间生的信息d).在从服务器上启动从服务器进程时产生的信息lo ...
随机推荐
- JDK自带观察者的使用
被观察者 package observerForJDK; import java.util.Observable; /** * 新闻派发器,当有新的新闻时派发给所有的订阅读者(观察者) */ publ ...
- uboot下emmc内容烧写(拷贝)步骤
一.目的:嵌入式开发板,通过emmc上的内核文件加载启动linux操作系统,以及存放其他程序文件.需要将所需文件先写入emmc中. 二.总体步骤是:uboot启动后,进入linux下,将emmc分区并 ...
- .net 定时启动任务
.net winform程序,设置每天的零时启动,执行完后自动关闭. 系统计划启动: 如果要定时启动某个程序,可以通过“任务计划”来安排. 如果是XP系统,在开始 --> 所有程序 --> ...
- javascript弹层
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- PHP高级程序员必学
业务增长,给你的网站带来用户和流量,那随之机器负载就上去了,要不要做监控?要不要做负载均衡?用户复杂了,要不要做多终端兼容?要不要做CDN?数据量大了,要不要做分布?垂直分还是横向分?系统瓶颈在哪里? ...
- Java设计模式(16)中介模式(Mediator模式)
Mediator定义:用一个中介对象来封装一系列关于对象交互行为. 为何使用Mediator模式/中介模式 各个对象之间的交互操作非常多,每个对象的行为操作都依赖彼此对方,修改一个对象的行为,同时会涉 ...
- Python 内置方法new
class Dog(object): def __new__(self): print("i am new .") def __init__(self): print(" ...
- 解决DoubanFM第三方客户端UI线程与工作线程交互问题
最新文章:Virson's Blog 首先要感谢yk000123的慷慨开源,开源地址见:http://doubanfm.codeplex.com/ 最近正好在学习WPF,然后在Codeplex上找到了 ...
- ADB 设置远程调试
Version:0.9 StartHTML:-1 EndHTML:-1 StartFragment:00000099 EndFragment:00016657 相信很多人在android 开发调试的时 ...
- Pandas 的轴向连接 concat
在pandas里面,另一种数据何必运算也被称为连接(concatenation).绑定(binding)或堆叠(stacking). Numpy的轴向连接, concatenation Numpy有一 ...