Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5096144
  • 博文数量: 921
  • 博客积分: 16037
  • 博客等级: 上将
  • 技术积分: 8469
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-05 02:08
文章分类

全部博文(921)

文章存档

2020年(1)

2019年(3)

2018年(3)

2017年(6)

2016年(47)

2015年(72)

2014年(25)

2013年(72)

2012年(125)

2011年(182)

2010年(42)

2009年(14)

2008年(85)

2007年(89)

2006年(155)

分类: Python/Ruby

2015-08-28 11:33:20

以往使用twsited服务器记录log使用的都是按照大小对日志切分,而现在有一个服务需要对log按照天进行切分,于是研究了一下twisted的日志记录方式,最后终于搞定。这里将分析过程记录下,以帮助后面有同样问题的人。
   一 twisted日志记录简介
     Twisted通过twisted.python.log 提供了msg, err和startLogging三个函数来进行日志记录,其中startLogging用来打开文件对象来开始日志,msg用来记录信息,err用来记录错误信息。通过twisted.python.logfile提供了BaseLogFile、LogFile和DailyLogFile三个类来同startLogging协同工作。
   如果使用twisted来启动程序,twisted会自动的使用LogFile来启动startLogging进行日志记录和切割。后面我们的分析都是基于使用twisted来启动程序在后台运行这一条件。
二 按天切分初步探索
   (一) 使用twisted.python.log 
     正如前面的介绍,我们可以自主使用startLogging来启动日志记录将其记录到指定的文件中。然后以一定的周期来判断是否进入到了新的一天,如果是的话,则主动的进行日志的切分。
     该方法需自主进行切分判断、主动切换日志,这显然不是最好的选择了。
   (二) 使用twisted.python.logfile的DailyLogFile
     使用DailyLogFile后,切分的判断与日志的切分都由twisted进行,不需要我们做额外的工作,使用起来比较方便,相对于第一种方法无疑是巨大的进步。但是该方法存在的问题是DailyLog与twisted主动托管的log会同时进行记录,同时如果之前代码已经完工,这里需要对代码进行大量的修改才能够满足要求,因此该方法只能算是下策。

  1. import twisted.python.logfile
  2. f = logfile.DailyLogFile("dailylog", "/log")
  3. log.startLogging(f)

三 按天切分分析
     前面的两种方法都不符合我的要求,这样我只能另觅他路了。让我们先来看看使用twisted启动程序时程序的启动过程。
    程序启动后通过twisted.scripts.twisted.run()函数开始执行,该函数最终执行到_SomeApplicationRunner(config).run()。其中config为程序启动时的传入参数解析后的结果,_SomeApplicationRunner为不同操作系统下twisted启动时使用的Runner,在linux下为UnixApplicationRunner, Windows下为WindowsApplicationRunner。 那么在linux下,

_SomeApplicationRunner(config).run()可以理解为,首先实例化一个UnixApplicationRunner对象,然后执行该对象的run方法。在UnixApplicationRunner(该类继承至application.app.ApplicationRunner)的初始化函数中有这么一句话:
    self.logger = self.loggerFactory(config) (loggerFactory为类的静态对象)
    这说明self.loggerFactory决定了日志的实际处理方法,在unix下loggerFactory也就是UnixAppLogger(该类继承至application.app.AppLogger)。UnixAppLogger的_getLogObserver方法里我们可以看到这么一句话logFile = logfile.LogFile.fromFullPath(self._logfilename),而正是由于这句话,twisted默认采用的才是按大小切分日志。
   如果说我们不在乎其他应用程序,那么我们直接修改Twisted源码中的这句话为:logFile = logfile.DailyLogFile.fromFullPath(self._logfilename) 即可达到按天切分日志的要求。但不幸的是我们往往不能也不应该直接修改其源码,所以我们还要继续努力来寻找解决方法。
    (一)重写loggerFactory
     我们新编写一个继承自_twistd_unix.UnixAppLogger的类,然后重写其_getLogObserver, 最后更新UnixApplicationRunner.loggerFactory为新类。从表面上来看该方法是完美的,即不用修改twisted源码,也不用修改我们的代码就能实现按天切分日志。但不幸的是该方法最终并不能奏效。因为从twisted启动的那一刻开始,UnixApplicationRunner就已经实例化并实例化了其loggerFactory,而此时连程序都尚未开始加载,所以loggerFactor修改也无法进行。
    (二) 设置ILogObserver
      通过twistd.application.AppLogger的start函数可以发现,其会先尝试从application从获取ILogObserver, 如果不存在的话才调用self._getLogObserver来新建一个observer。 这也就是说我们可以在程序中手工设置application的ILogObserver来实现按天切分日志。

  1. from twisted.python.log import ILogObserver, FileLogObserver
  2. from twisted.application import internet, service
  3. from twisted.python import logfile

  4. _logfilename = 'dailylog_test.log'
  5. _logfile = logfile.DailyLogFile.fromFullPath(_logfilename)
  6. application.setComponent(ILogObserver, FileLogObserver(_logfile).emit)

  7. application = service.Application('DailylogTest')
  8. DailylogTestService = internet.TCPServer(12345, DailylogTestFactory())
  9. DailylogTestService .setServiceParent(application)
 经验证,通过该方法的确能够达到不修改程序和twisted的源码而进行按天切分日志,但该方法的问题是日志文件名是在程序中指定的,没有如以前一样通过命令行指定日志名。因此该方法只能算是中策。
   (三) 使用serveroption设置ILogObserver 
       我们只需要将方法二中的log文件名改为解析得到的log日志名即可使用参数来指定日志名。不过由于twsited并未提供任何变量来存储解析参数,所以我们需要自己对参数进行解析。另外方法二中直接使用DailyLogFile来代替了twisted默认的observer,这当中忽略了写日志时应考虑的其他一些因素,在某些时候可能会出现问题。因此我们需模拟UnixApplicationRunner.loggerFactory来提observer。


  1. import sys, os
  2. from twisted.application import app
  3. from twisted.python import logfile, log, syslog
  4. from twisted.scripts import _twistd_unix
  5. from twisted.python.log import ILogObserver, FileLogObserver

  6. class MyAppLogger(_twistd_unix.UnixAppLogger):
  7.     def getLogObserver(self):
  8.         if self._syslog:
  9.             return syslog.SyslogObserver(self._syslogPrefix).emit

  10.         if self._logfilename == '-':
  11.             if not self._nodaemon:
  12.                 sys.exit('Daemons cannot log to stdout, exiting!')
  13.             logFile = sys.stdout
  14.         elif self._nodaemon and not self._logfilename:
  15.             logFile = sys.stdout
  16.         else:
  17.             if not self._logfilename:
  18.                 self._logfilename = 'twistd.log'
  19.             logFile = logfile.DailyLogFile.fromFullPath(self._logfilename)
  20.             try:
  21.                 import signal
  22.             except ImportError:
  23.                 pass
  24.             else:
  25.                 if not signal.getsignal(signal.SIGUSR1):
  26.                     def rotateLog(signal, frame):
  27.                         from twisted.internet import reactor
  28.                         reactor.callFromThread(logFile.rotate)
  29.                     signal.signal(signal.SIGUSR1, rotateLog)
  30.         return log.FileLogObserver(logFile).emit

  31. class MyServerOptions(_twistd_unix.ServerOptions):
  32.     def parseOptions(self, options=None):
  33.         if options is None:
  34.             options = sys.argv[1:] or ["--help"]
  35.         try:
  36.             opts, args = getopt.getopt(options,
  37.                                        self.shortOpt, self.longOpt)
  38.         except getopt.error, e:
  39.             raise UsageError(str(e))

  40.         for opt, arg in opts:
  41.             if opt[1] == '-':
  42.                 opt = opt[2:]
  43.             else:
  44.                 opt = opt[1:]
  45.             optMangled = opt
  46.             if optMangled not in self.synonyms:
  47.                 optMangled = opt.replace("-", "_")
  48.                 if optMangled not in self.synonyms:
  49.                     raise UsageError("No such option '%s'" % (opt,))

  50.             optMangled = self.synonyms[optMangled]
  51.             if isinstance(self._dispatch[optMangled], usage.CoerceParameter):
  52.                 self._dispatch[optMangled].dispatch(optMangled, arg)
  53.             else:
  54.                 if optMangled != 'reactor':
  55.                     self._dispatch[optMangled](optMangled, arg)

  56.         if (getattr(self, 'subCommands', None)
  57.             and (args or self.defaultSubCommand is not None)):
  58.             if not args:
  59.                 args = [self.defaultSubCommand]
  60.             sub, rest = args[0], args[1:]
  61.             for (cmd, short, parser, doc) in self.subCommands:
  62.                 if sub == cmd or sub == short:
  63.                     self.subCommand = cmd
  64.                     self.subOptions = parser()
  65.                     self.subOptions.parent = self
  66.                     self.subOptions.parseOptions(rest)
  67.                     break
  68.             else:
  69.                 raise UsageError("Unknown command: %s" % sub)
  70.         else:
  71.             try:
  72.                 self.parseArgs(*args)
  73.             except TypeError:
  74.                 raise UsageError("Wrong number of arguments.")

  75.         self.postOptions()

  76. def install_dailylog(application):
  77.     Myconfig = MyServerOptions()
  78.     Myconfig.parseOptions()
  79.     application.setComponent(ILogObserver, MyAppLogger(Myconfig).getLogObserver())

四 结束语
    本文就twisted下如何进行按天的日志切分进行了探讨,通过分析twisted的日志记录机制、程序启动方式最终给出了一个比较好的实现方案,并将该方案封装为一个函数,以\方便大家的使用。


原文链接

阅读(1434) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~