配置管理器和日志管理器是项目中最为独立的模块。我们可以很方便将其剥离出来供其他Python工程使用。文件的重点将是介绍Python单例和logging模块的使用。(转载请指明出于breaksoftware的csdn博客)

配置管理器

在《码农技术炒股之路——架构和设计》中我们介绍过,配置管理将作为一个单例而存在。我尝试过各种Python单例的实现方法,发现都存在一些问题,不能保证单例的特性。后来对一些方案进行修改,得出下面一种可靠的方式:

instances = {}
def singleton(cls, *args, **kw):global instancesdef _singleton(*args, **kw): if cls.__name__ not in instances:  instances[cls.__name__] = cls(*args, **kw)return instances[cls.__name__]  return _singleton

我们可以使用下面方法进行测试

    @singletonclass singleton_test(object):def __init__(self, s_data):print "init"self._data = s_datadef run(self):print self._dataa = singleton_test("AAAAAAA")print aa.run()b = singleton_test("BBBBBBB")print bb.run()

其结果是

init
<__main__.singleton_test object at 0x021C57D0>
AAAAAAA
<__main__.singleton_test object at 0x021C57D0>
AAAAAAA

可见我们对通过修饰符修饰后的singleton_test类“构造”两次后得到的是同一个对象。

配置管理的实现其实非常简单,它就是一个使用singleton修饰、封装了ConfigParser的单例类

import ConfigParser
from singleton import singleton@singleton
class scheduler_frame_conf_inst():def __init__(self):self._cp = Nonedef load(self, conf_path):print("load frame conf %s" % conf_path)self._cp = ConfigParser.SafeConfigParser()self._cp.read(conf_path)def has_option(self, section_name, option_name):if self._cp:return self._cp.has_option(section_name, option_name)return Falsedef get(self, section_name, option_name):if self._cp:return self._cp.get(section_name, option_name)else:print("get conf %s %s" % (section_name, option_name))

load方法用于从指定路径加载工程配置。因为子模块都有自己的配置,且可能格式不一致,所以如果这些配置都放在一个文件中会显得非常杂乱。故工程的主配置文件保存是一组子模块配置文件路径的信息。子模块通过自己的配置解释规则去解释这些文件。

[frame_job]
conf_path = ./conf/frame_job.conf[frame_log]
conf_path = ./conf/log.conf[strategy_job]
conf_path = ./conf/strategy_job.conf[mysql_manager]
conf_path = ./conf/mysql_manager.conf[regulars]
conf_path = ./conf/regulars_manager.conf

上面配置分别对应于:系统任务管理器配置、日志管理器配置、普通任务管理器配置、数据库管理配置和正则管理器配置。

日志管理器

日志管理是通过封装Python的logging实现的。官方说明并没有对如何配置logging进行详细且准确的说明,所以我在完善这个模块时进行了若干次尝试,才得出正确的使用方法。

一般来说日志可以分为如下五种等级:

  • Info。用于记录一般性日志,如执行流程或者运行中的中间结果。如果线上日志量比较大,这种日志在上线前是可以关闭的。
  • Debug。用于记录辅助调试的信息。如果线上日志量比较大,这种日志在上线前是可以关闭的。
  • Warning。用于记录运行中我们可以接受的错误。一般发生这种错误只是一种预告,预示着某些方面出现了异常。
  • Error。用于记录运行中我们可以勉强接受的错误。这种错误的发生并不代表我们整个工程不可用,而是某些功能已经受限了。
  • Fatal。用于记录运行中我们不可以接收的错误。比如我们发现内存分配失败,就可以打印Fatal错误,并退出程序。因为内存都耗尽了,之后发生的什么事都不好预测,不如记录下错误信息后退出。

Python的logging库也支持上述等级。我们先看下封装后的日志类初始化操作

@singleton
class loggingex():def __init__(self, conf_path):error = 0while True:try:logging.config.fileConfig(conf_path)except IOError as e:if error > 1:raise eif 2 == e.errno:if os.path.isdir(e.filename):os.makedirs(e.filename)else:os.makedirs(os.path.dirname(e.filename))error = error + 1except Exception as e: raise eelse:break

其最核心的就是logging.config.fileConfig(conf_path)这行。它让logging库通过一个配置文件进行初始化,其中包括日志类型、日志格式和日志输出方式等信息。这些配置如何编写,以及如何结合代码使用,将是后文介绍的重点。

为了让封装的日志管理器有更强大的功能。我提出以下设计要求:

  1. Debug等级日志只打印在Console中。
  2. Info等级日志只打印在普通日志文件中。按小时切分。
  3. Warning、Error和Fatal等级日志只打印在错误日志文件中。按小时切分。

我们先看下针对Debug等级日志的配置方式。

打印在Console中的Debug等级日志

首先我们需要定义日志输出的格式。我们希望日志可以打印出:时间、等级、进程ID、线程ID和用户自定义消息。这样在配置文件中我们需要加入如下的内容

[formatter_LogFormatter]
format=%(asctime)s ^ %(levelname)s ^ %(process)d ^ %(thread)d ^ %(message)s
datefmt=
class=logging.Formatter

注意一下,这个节的名称是formatter_LogFormatter。但是实际我们之后要使用的名称只有下划线之后一节——LogFormatter。“formatter_”是格式配置名的固有信息,即任何格式配置都要使用它来开头。

然后我们要声明一个叫formatters节,其下keys包含了之前声明的格式配置名称

[formatters]
keys=LogFormatter

下一步我们要声明日志输出方式。因为Debug日志是输出到Console中的,所以我们使用的类是StreamHandler

[handler_ConsoleHandler]
class=StreamHandler
formatter=LogFormatter
level=DEBUG
args=(sys.stdout,)

上面一节记录了日志输出所使用的类名、所使用的格式、日志等级和输出参数。注意一下节的名称——handler_ConsoleHandler,和格式配置节名要以“formatter_”开始类似,输出方式的节名要以“handler_”开头,而实际的名称则是下划线之后的ConsoleHandler。

接下来我们需要声明一个叫handlers的节,其下keys包含了之前声明的输出方式配置名称

[handlers]
keys=ConsoleHandler

最后我们要声明一个叫loggers的节,其下keys字段它包含了日志对象的名称。这些名称用逗号分隔。在定义Debug等级日志对象名称前,我们先要定义一个叫root的日志对象

[loggers]
keys=root,LogDebug

root日志对象的配置要包含所有声明的初始方式信息,当前我们只有ConsoleHandler,于是这样配置

[logger_root]
level=NOTSET
handlers=ConsoleHandler

LogDebug的配置如下

[logger_LogDebug]
handlers=ConsoleHandler
qualname=logger_LogDebug
level=DEBUG
propagate=0

节名是以logger_开头,其后跟着在Loggers中声明的Debug日志对象名称LogDebug。handler指向向Console输出的输出方式名ConsoleHandler;qualname指定为该节节名。level设置为DEBUG。

在Python中,我们可以通过下面的方式使用该日志对象

    def log_debug(self, msg):log_debug = logging.getLogger('logger_LogDebug')        #https://docs.python.org/2/howto/logging.htmllog_debug.debug(msg)

打印在文件中、按时间切分的Info等级日志

数据的内容格式我们还是借用LogFormatter定义。因为这次是要往文件中输出,所以我们需要重新定义一种输出方式——FileNomalHandler。

[handler_FileNomalHandler]
class=logging.handlers.TimedRotatingFileHandler
formatter=LogFormatter
level=INFO
args=('./log/nomal.log.i', 'H', 1, 60)

因为要按时间维度切分,所以这次使用的类是logging.handlers.TimedRotatingFileHandler。然后我们再args中指定文件生成的路径和通用名,以及按多久时间切分一次。上述写法,将导致logging在工程的log目录下生成nomal.log.i.2017-01-01_23这样格式的数据

        别忘记修改handlers下的keys信息,要把新增的handler给加进去

[handlers]
keys=ConsoleHandler,FileNomalHandler,

以及在logger_root加入它

[logger_root]
level=NOTSET
handlers=ConsoleHandler,FileNomalHandler

相应的我们需要定义一个日志对象配置

[logger_LogInfo]
handlers=FileNomalHandler
qualname=logger_LogInfo
level=INFO
propagate=0

并在loggers下的keys中新增该对象名称

[loggers]
keys=root,LogDebug,LogInfo

打印在文件中、按时间切分的Warning、Error和Fatal等级日志

相应的配置修改和上面类似,当时要注意文件名称需要换一下。我把整个配置放在这面区域中

###############################################################################
[loggers]
keys=root,LogDebug,LogInfo,LogWarningErrorCritical,SQL_ERROR[logger_root]
level=NOTSET
handlers=ConsoleHandler,FileNomalHandler,FileErrorHandler[logger_LogDebug]
handlers=ConsoleHandler
qualname=logger_LogDebug
level=DEBUG
propagate=0[logger_LogInfo]
handlers=FileNomalHandler
qualname=logger_LogInfo
level=INFO
propagate=0[logger_LogWarningErrorCritical]
handlers=FileErrorHandler
qualname=logger_LogWarningErrorCritical
level=WARNING
propagate=0[logger_SQL_ERROR]
handlers=SaveErrorSQL_FileHandler
qualname=logger_SQL_ERROR
level=WARNING
propagate=0
##############################################################################################################################################################
[handlers]
keys=ConsoleHandler,FileNomalHandler,FileErrorHandler,SaveErrorSQL_FileHandler[handler_ConsoleHandler]
class=StreamHandler
formatter=LogFormatter
level=DEBUG
args=(sys.stdout,)[handler_FileNomalHandler]
class=logging.handlers.TimedRotatingFileHandler
formatter=LogFormatter
level=INFO
args=('./log/nomal.log.i', 'H', 1, 60)[handler_FileErrorHandler]
class=logging.handlers.TimedRotatingFileHandler
formatter=LogFormatter
level=WARNING
args=('./log/nomal.log.wec', 'H', 1, 60)[handler_SaveErrorSQL_FileHandler]
class=logging.handlers.TimedRotatingFileHandler
formatter=SQLLogFormatter
level=WARNING
args=('./log/sql_error.log', 'H', 1, 60)##############################################################################################################################################################
[formatters]
keys=LogFormatter,SQLLogFormatter[formatter_LogFormatter]
format=%(asctime)s ^ %(levelname)s ^ %(process)d ^ %(thread)d ^ %(message)s
datefmt=
class=logging.Formatter[formatter_SQLLogFormatter]
format=%(asctime)s ^ %(message)s
datefmt=
class=logging.Formatter
###############################################################################

上面配置中,我新增了一个打印SQL的日志对象配置。因为我们之后维护时可能需要把执行失败的SQL重新执行一遍,所以需要一个尽量简洁的文件格式。
        因为在日志中,我需要知道是哪个文件哪行出错,所以需要使用inspect库进行栈回溯。于是在用户自定义消息的基础上,在调用日志方法前,对原消息做些修改(除了SQL日志)以扩充信息。

完整的代码如下:

import os
import sys
import inspect
import logging
import logging.config
from singleton import singleton@singleton
class loggingex():def __init__(self, conf_path):error = 0while True:try:logging.config.fileConfig(conf_path)except IOError as e:if error > 1:raise eif 2 == e.errno:if os.path.isdir(e.filename):os.makedirs(e.filename)else:os.makedirs(os.path.dirname(e.filename))error = error + 1except Exception as e: raise eelse:breakdef log_debug(self, msg):log_debug = logging.getLogger('logger_LogDebug')        #https://docs.python.org/2/howto/logging.htmllog_debug.debug(msg)def log_info(self, msg):log_info = logging.getLogger('logger_LogInfo')log_info.info(msg)def log_warning(self, msg):log_warning_error_critical = logging.getLogger('logger_LogWarningErrorCritical')log_warning_error_critical.warning(msg)def log_error(self, msg):log_warning_error_critical = logging.getLogger('logger_LogWarningErrorCritical')log_warning_error_critical.error(msg)      def log_critical(self, msg):log_warning_error_critical = logging.getLogger('logger_LogWarningErrorCritical')log_warning_error_critical.critical(msg)def log_error_sql(self, msg):log_error_sql = logging.getLogger('logger_SQL_ERROR')log_error_sql.critical(msg)def LOG_INIT(conf_path):global logger_objlogger_obj = loggingex(conf_path)def modify_msg(msg):stack_info = inspect.stack()if len(stack_info) > 2:file_name = inspect.stack()[2][1]line = inspect.stack()[2][2]function_name = inspect.stack()[2][3]new_msg = file_name + " ^ " + function_name + " ^ " + str(line) + " ^ " + msgreturn new_msgdef LOG_DEBUG(msg):new_msg = modify_msg(msg)try:logger_obj.log_debug(new_msg)except Exception as e:print new_msgdef LOG_INFO(msg):new_msg = modify_msg(msg)try:logger_obj.log_info(new_msg)except Exception as e:print new_msgdef LOG_WARNING(msg):new_msg = modify_msg(msg)try:logger_obj.log_warning(new_msg)except Exception as e:print new_msgdef LOG_ERROR(msg):new_msg = modify_msg(msg)try:logger_obj.log_error(new_msg)except Exception as e:print new_msgdef LOG_CRITICAL(msg):new_msg = modify_msg(msg)try:logger_obj.log_critical(new_msg)except Exception as e:print new_msgdef LOG_ERROR_SQL(msg):try:logger_obj.log_error_sql(msg)except Exception as e:print msgif __name__ == "__main__":LOG_INIT("../../conf/log.conf")LOG_DEBUG('LOG_DEBUG')LOG_INFO('LOG_INFO')LOG_WARNING('LOG_WARNING')LOG_ERROR('LOG_ERROR')LOG_CRITICAL('LOG_CRITICAL')LOG_ERROR_SQL("Create XXX Error")

码农技术炒股之路——配置管理器、日志管理器相关推荐

  1. 码农技术炒股之路——任务管理器

    系统任务和普通任务都是通过任务管理器调度的.它们的区别是:系统任务在程序运行后即不会被修改,而普通任务则会被修改.(转载请指明出于breaksoftware的csdn博客) 为什么要有这样的设计?因为 ...

  2. 码农技术炒股之路——数据库管理器、正则表达式管理器

    我选用的数据库是Mysql.选用它是因为其可以满足我的需求,而且资料多.因为作为第三方工具,难免有一些配置问题.所以本文也会讲解一些和Mysql配置及开发相关的问题.(转载请指明出于breaksoft ...

  3. 码农技术炒股之路——抓取股票基本信息、实时交易信息、主力动向信息

    从本节开始,我们开始介绍各个抓取和备份业务.(转载请指明出于breaksoftware的csdn博客) 因为我们数据库很多,数据库中表也很多,所以我们需要一个自动检测并创建数据库和表的功能.在< ...

  4. 码农技术炒股之路——抓取日线数据、计算均线和除权数据

    日线数据是股票每日收盘后的信息.这块数据不用实时抓取,所以并不占用宝贵的交易时间的资源.于是我们抓取完数据后直接往切片后的数据库中保存.(转载请指明出于breaksoftware的csdn博客) 抓取 ...

  5. 码农技术炒股之路——实时交易信息、主力动向信息分库备份

    一般来说,一个股票信息应该保存在一张表中.但是由于我机器资源限制,且我希望尽快频率的抓取数据.所以每天我将所有股票的实时交易信息放在daily_temp库中的一个以日期命名的表中.主力动向信息也是如此 ...

  6. KBMMW 的日志管理器

    kbmmw 4.82 最大的新特性就是增加了 日志管理器. 新的日志管理器实现了不同类型的日志.断言.异常处理.计时等功能. 首先.引用kbmMWLog.pas 单元后,系统就默认生成一个IkbmMW ...

  7. Canal binlog 日志管理器与GTID简介

    本节目录 1.Canal 位点管理(日志管理器) 1.1 类图 1.2 日志管理器使用方法 2.MySQL GTID 扫盲 正如上文提到的那样,在 Canal Instance 启动的时候,首先会查询 ...

  8. java日志——修改日志管理器配置+日志本地化

    [0]README 0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java日志--修改日志管理器配置+日志本地化 的相关知识: [1]修改日志管理器配置 1.1 ...

  9. PostgreSQL multixact日志管理器说明

    PostgreSQL multixact日志管理器说明 MultiXactID日志是uxdb系统用来记录组合事务ID的一种日志.由于uxdb采用了多版本并发控制,因此同一个元组相关联的事务ID可能有多 ...

最新文章

  1. C# WebAPI中DateTime类型字段在使用微软自带的方法转json格式后默认含T的解决办法...
  2. Unable to generate a temporary class (result=1)解决方法
  3. 【转载】浅谈React编程思想
  4. 互联网日报 | 3月12日 星期五 | 京东全年净增1.1亿活跃用户;百度计划3月23日登陆港交所;中国联通首次公布5G用户数...
  5. 利用 assistant_如何使用Dialogflow对Google Assistant操作实施本地履行
  6. 穿越鸿蒙电视剧,电视剧大泼猴剧情介绍(1-45全集)
  7. 想成为大数据分析师必须知道的这些事儿(文末福利)
  8. python字符串及基本运算
  9. ORA-01501: CREATE DATABASE failed
  10. java私塾初级_java私塾 Java初级视频教程
  11. python画spc控制图_如何选择最适合我们的SPC控制图?
  12. VMware系列序列号
  13. 关于2020计算机考研。
  14. python 谷歌翻译接口_使用python调用谷歌翻译接口实现英文到中文的翻译
  15. 三七互娱秋招web前端笔试题编程题(使用原生JS实现一个英雄类Hero, 可以按照以下方式调用正确输出)
  16. 经济学人The Economist学习(笔记词汇)Day1
  17. 机器人运动控制-水下机器人
  18. 【arcpy批量制图】符号系统的色带、小数点设置
  19. 坐标旋转公式-- 角度和弧度
  20. CSS雪碧图demo(含雪碧代码)

热门文章

  1. keras 的 example 文件 mnist_denoising_autoencoder.py 解析
  2. OpenCV(基础补充)颜色空间HSV *args与**args(滑动条传参问题)
  3. hibernate连接mysql数据库步骤_Hibernate (操作步骤)
  4. mysql替换开头_如何在MySQL的字符串开头搜索和替换特定字符?
  5. python 去除str的引号 去除括号_Python基础教程:运算符以及数据类型解析
  6. boost::asio中的C/S同步实例源码
  7. 3dsMax插件V-Ray渲染与合成学习课程 3ds Max: Rendering for Compositing in V-Ray Next
  8. 通过IP地址和子网掩码与运算计算相关地址
  9. 磁盘I:O 性能指标 以及 如何通过 fio 对nvme ssd,optane ssd, pmem 性能摸底
  10. linux进程间通信:FIFO应用 /var/log/ 系统日志的模拟实现