码农技术炒股之路——配置管理器、日志管理器
配置管理器和日志管理器是项目中最为独立的模块。我们可以很方便将其剥离出来供其他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库通过一个配置文件进行初始化,其中包括日志类型、日志格式和日志输出方式等信息。这些配置如何编写,以及如何结合代码使用,将是后文介绍的重点。
为了让封装的日志管理器有更强大的功能。我提出以下设计要求:
- Debug等级日志只打印在Console中。
- Info等级日志只打印在普通日志文件中。按小时切分。
- 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)做算法优化时,只能现搬书里的算法,遇到不一样的问题,就不会了。2)面试一旦涉及到算法和数据结构,如果数学不行,面试基本就凉凉了。3)算法…

受限列表 队列与栈
2019独角兽企业重金招聘Python工程师标准>>> 队列与栈为受限列表,队列为先入先出型列表,而栈为先入后出型列表,有关列表的实现可以查看 http://my.oschina.net/u/2011113/blog/514713 。 结构图为 Queue实现了IQueue接口ÿ…
码农技术炒股之路——数据库管理器、正则表达式管理器
我选用的数据库是Mysql。选用它是因为其可以满足我的需求,而且资料多。因为作为第三方工具,难免有一些配置问题。所以本文也会讲解一些和Mysql配置及开发相关的问题。(转载请指明出于breaksoftware的csdn博客) 数据库管理器 Mysq…

Overview of ISA and TMG Networking and ISA Networking Case Study (Part 1)
老方说:此篇文章摘自ISASERVER.ORG网站,出自Thomas Shinder达人之手。严重建议ISA爱好者看看。Published: Dec 16, 2008 Updated: Jan 21, 2009 Author: Thomas Shinder What ISA/TMG firewall Networks are about and how the firewall uses these ne…
阿里云免费开放一切AI算力,加速新型冠状病毒新药和疫苗研发
近日,阿里云宣布,为了帮助加速新药和疫苗研发,将向全球公共科研机构免费开放一切AI算力。目前,中国疾控中心已成功分离病毒,疫苗研发和药物筛选仍在争分夺秒地进行。新药和疫苗研发期间,需要进行大量的数据…

ASP.net(C#)批量上传图片(完整版)
来自:http://blog.itpub.net/9869521/viewspace-667955/ 这篇关于ASP.Net批量上传图片的文章写得非常好,偶尔在网上看到想转载到这里,却费劲了周折。为了更新这篇文章,我用了近半个小时,网上的转载都残缺不全ÿ…

码农技术炒股之路——任务管理器
系统任务和普通任务都是通过任务管理器调度的。它们的区别是:系统任务在程序运行后即不会被修改,而普通任务则会被修改。(转载请指明出于breaksoftware的csdn博客) 为什么要有这样的设计?因为我希望它是一个可以不用停…
面对新型肺炎疫情,AI能做什么?
作者 | 马超出品 | AI科技大本营(ID:rgznai100)根据最新的新型冠状病毒疫情通报,截至1月30日24时,国家卫生健康委公布确诊病例9692例,重症病例1527例,累计死亡病例213例,另有疑似病例15238例。为…

大家帮忙.谢谢!..(急急急急急)
大家帮忙.谢谢!..(急急急急急) Delphi / Windows SDK/APIhttp://www.delphi2007.net/DelphiDB/html/delphi_20061218224617231.htmlprocedure TForm1.Button4Click(Sender: TObject); var P : pstring; i, j : integer; begin GetMem(p, sizeof(stri…

HDU4866 Shooting (要持久段树)
意甲冠军: 给你一些并行x行轴。总是询问坐标x的顶部之前,k一个段高度,。标题是必须在线。思路: 首先要会可持久化线段树(又称主席树和函数式线段树)。不会的能够去做下POJ 2104。 把全部线段高度离散化,作为结点建线段…
C++过去的这一年
作者 | Bartek译者 | 苏本如,责编 | 屠敏出品 | CSDN(ID:CSDNnews)【导读】本文旨在让我们回顾 C 2019年里的变化和发展!我们将重点关注本年度里 C 上发生的重大事件,标准的发展,工具的变化等等…
码农技术炒股之路——抓取股票基本信息、实时交易信息、主力动向信息
从本节开始,我们开始介绍各个抓取和备份业务。(转载请指明出于breaksoftware的csdn博客) 因为我们数据库很多,数据库中表也很多,所以我们需要一个自动检测并创建数据库和表的功能。在《码农技术炒股之路——数据库管理…

TemplateBuilder
http://msdn.microsoft.com/zh-cn/vstudio/system.web.ui.templatebuilder_members(VS.85).aspx TemplateBuilder 成员TemplateBuilder 成员支持在生成模板及其包含的子控件时使用的页分析器。 下表列出了由 TemplateBuilder 类型公开的成员。 公共构造函数 名称 说明 Templat…

【iOS UI】iOS 9 GUI 资源分享
分享的内容包括一个【DesignCode-iOS-9-GUI】Sketch 文件, 和苹果官方释出的【SF-UI、SF-Compact】两种字体的安装包。 以上内容是正版、免费的 <a href "https://itunes.apple.com/cn/app/sketch-3/id852320343?mt12">Sketch</a> 是收费软…
反向R?削弱显著特征为细粒度分类带来提升 | AAAI 2020
作者 | VincentLee来源 | 晓飞的算法工程笔记导读:论文提出了类似于dropout作用的diversification block,通过抑制特征图的高响应区域来反向提高模型的特征提取能力,在损失函数方面,提出专注于top-k类别的gradient-boosting loss来…

C#初学——doWhile
继续上面的学习,这次的是流程控制,用dowhile,代码如下,还是用语言选择来作为事例的。using System; using System.Collections.Generic; using System.Text; namespace ConsoleApplication9 { class Program { static void Main(s…

码农技术炒股之路——实时交易信息、主力动向信息分库备份
一般来说,一个股票信息应该保存在一张表中。但是由于我机器资源限制,且我希望尽快频率的抓取数据。所以每天我将所有股票的实时交易信息放在daily_temp库中的一个以日期命名的表中。主力动向信息也是如此。但是盘后分析股票时,我们会以单只股…

数据预处理(完整步骤)
原文:http://dataunion.org/5009.html 一:为什么要预处理数据?(1)现实世界的数据是肮脏的(不完整,含噪声,不一致)(2)没有高质量的数据,…

码农技术炒股之路——抓取日线数据、计算均线和除权数据
日线数据是股票每日收盘后的信息。这块数据不用实时抓取,所以并不占用宝贵的交易时间的资源。于是我们抓取完数据后直接往切片后的数据库中保存。(转载请指明出于breaksoftware的csdn博客) 抓取日线数据 我们先要获取今天有交易信息的股票代…

茫茫碌碌的日子
一连很好多天,都在为公司数据库基础构架升级的事情忙活着。升级的事情还是比较棘手的。需要升级硬件服务器,相关的存储,操作系统,数据库产品,涉及面非常多。当然烦心的事情就很多。作为线上生产系统,升级和…
Python PK C++,究竟谁更胜一筹?
作者 | Farhad Malik译者 | 弯月,编辑 | 屠敏来源 | CSDN(ID:CSDNnews)在编程生涯的早期阶段,我参与过一款C数学优化应用程序的开发,这个程序对性能的要求很高。至今我依然记得那段艰难的经历。在那个项目中…

oracle--查看表空间大小以及修改表空间大小
为什么80%的码农都做不了架构师?>>> 一.修改表空间大小 解决以上问题的办法:通过增大表空间即可解决,如下: Sql代码 使用dba用户登陆 sqlplus / as sysdba; 执行如下命令: SQL >…
同步、异步、堵塞、非堵塞和函数调用及I/O之间的组合概念
在我们工作和学习中,经常会接触到“同步”、“异步”、“堵塞”和“非堵塞”这些概念,但是并不是每个人都能将它们的关系和区别说清楚。本文将对这些基本概念进行讨论,以期让大家有更清楚的认识。(转载请指明出于breaksoftware的c…
“抗击”新型肺炎!阿里达摩院研发AI算法,半小时完成疑似病例基因分析
利用技术辅助抗击疫情,阿里巴巴、百度等科技巨头各显身手。此前,AI科技大本营采访报道了阿里达摩院《数十名工程师作战5天,阿里达摩院连夜研发智能疫情机器人》一文,后者为了解决客服人力不足的局面,快速响应政府需求开…

反编译工具jad简单用法
反编译工具jad简单用法 下载地址:[url]http://58.251.57.206/down1?cidB99584EFA6154A13E5C0B273C3876BD4CC8CE672&t2&fmt&usrinput[/url]反编译工具jad &dt2002000一. 不用安装,只要解压就行(有这样两个文件jad.exe&#x…
ubuntu 系统设置bugzilla制
随着时间的推移。在大脑中形成的记忆总会慢慢的淡去。人的记忆力就是这样。所以最好的办法就是形成博客去记录下来,一方面给自己以后回想用。一方面也算是自己的一个积累。所以一旦选择了一个行业,最好不要轻 易转行,由于非常多知识须要不断的…
静态分析C语言生成函数调用关系的利器——cflow
除了《静态分析C语言生成函数调用关系的利器——calltree》一文中介绍的calltree,我们还可以借助cflow辅助我们阅读理解代码。(转载请指明出于breaksoftware的csdn博客) cflow的说明和安装cflow是一款静态分析C语言代码的工具,通过…
我在MongoDB年终大会上获二等奖文章:由数据迁移至MongoDB导致的数据不一致问题及解决方案...
作者 | 上海小胖来源 | Python专栏(ID:xpchuiit)故事背景企业现状2019年年初,我接到了一个神秘电话,电话那头竟然准确的说出了我的昵称:上海小胖。我想这事情不简单,就回了句:您好,我是小胖&…

注意String.Split的几个重载形式
String.Split应该是经常用到的一个函数了,经常的有下面两种形式 public string[] Split(char[] separator, StringSplitOptions options); public string[] Split(string[] separator, StringSplitOptions options); 1. 多数情况下我们会使用第一种,代码里可能这…

如何让猎头找到你
如何让猎头找到你