在twisted中按天進行日誌切分

以往使用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會同時進行記錄,同時若是以前代碼已經完工,這裏須要對代碼進行大量的修改纔可以知足要求,所以該方法只能算是下策。
     import twisted.python.logfile
     f = logfile.DailyLogFile("dailylog", "/log")
     log.startLogging(f) 
三 按天切分分析
     前面的兩種方法都不符合個人要求,這樣我只能另覓他路了。讓咱們先來看看使用twisted啓動程序時程序的啓動過程。
    程序啓動後經過twisted.scripts.twisted.run()函數開始執行,該函數最終執行到_SomeApplicationRunner(config).run()。其中config爲程序啓動時的傳入參數解析後的結果,_SomeApplicationRunner爲不一樣操做系統下twisted啓動時使用的Runner,在linux下爲UnixApplicationRunner, Windows下爲WindowsApplicationRunner。 那麼在linux下,python

_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來實現按天切分日誌。react

from twisted.python.log import ILogObserver, FileLogObserver
from twisted.application import internet, service
from twisted.python import logfilelinux

_logfilename = 'dailylog_test.log'
_logfile = logfile.DailyLogFile.fromFullPath(_logfilename)
application.setComponent(ILogObserver, FileLogObserver(_logfile).emit)服務器

application = service.Application('DailylogTest')
DailylogTestService = internet.TCPServer(12345, DailylogTestFactory())
DailylogTestService .setServiceParent(application)
      
    經驗證,經過該方法的確可以達到不修改程序和twisted的源碼而進行按天切分日誌,但該方法的問題是日誌文件名是在程序中指定的,沒有如之前同樣經過命令行指定日誌名。所以該方法只能算是中策。
   (三) 使用serveroption設置ILogObserver 
       咱們只須要將方法二中的log文件名改成解析獲得的log日誌名便可使用參數來指定日誌名。不過因爲twsited並未提供任何變量來存儲解析參數,因此咱們須要本身對參數進行解析。另外方法二中直接使用DailyLogFile來代替了twisted默認的observer,這當中忽略了寫日誌時應考慮的其餘一些因素,在某些時候可能會出現問題。所以咱們需模擬UnixApplicationRunner.loggerFactory來提observer。app

import sys, os
from twisted.application import app
from twisted.python import logfile, log, syslog
from twisted.scripts import _twistd_unix
from twisted.python.log import ILogObserver, FileLogObserver函數

class MyAppLogger(_twistd_unix.UnixAppLogger):
    def getLogObserver(self):
        if self._syslog:
            return syslog.SyslogObserver(self._syslogPrefix).emitpost

        if self._logfilename == '-':
            if not self._nodaemon:
                sys.exit('Daemons cannot log to stdout, exiting!')
            logFile = sys.stdout
        elif self._nodaemon and not self._logfilename:
            logFile = sys.stdout
        else:
            if not self._logfilename:
                self._logfilename = 'twistd.log'
            logFile = logfile.DailyLogFile.fromFullPath(self._logfilename)
            try:
                import signal
            except ImportError:
                pass
            else:
                if not signal.getsignal(signal.SIGUSR1):
                    def rotateLog(signal, frame):
                        from twisted.internet import reactor
                        reactor.callFromThread(logFile.rotate)
                    signal.signal(signal.SIGUSR1, rotateLog)
        return log.FileLogObserver(logFile).emitspa

class MyServerOptions(_twistd_unix.ServerOptions):
    def parseOptions(self, options=None):
        if options is None:
            options = sys.argv[1:] or ["--help"]
        try:
            opts, args = getopt.getopt(options,
                                       self.shortOpt, self.longOpt)
        except getopt.error, e:
            raise UsageError(str(e))操作系統

        for opt, arg in opts:
            if opt[1] == '-':
                opt = opt[2:]
            else:
                opt = opt[1:]
            optMangled = opt
            if optMangled not in self.synonyms:
                optMangled = opt.replace("-", "_")
                if optMangled not in self.synonyms:
                    raise UsageError("No such option '%s'" % (opt,))命令行

            optMangled = self.synonyms[optMangled]
            if isinstance(self._dispatch[optMangled], usage.CoerceParameter):
                self._dispatch[optMangled].dispatch(optMangled, arg)
            else:
                if optMangled != 'reactor':
                    self._dispatch[optMangled](optMangled, arg)

        if (getattr(self, 'subCommands', None)
            and (args or self.defaultSubCommand is not None)):
            if not args:
                args = [self.defaultSubCommand]
            sub, rest = args[0], args[1:]
            for (cmd, short, parser, doc) in self.subCommands:
                if sub == cmd or sub == short:
                    self.subCommand = cmd
                    self.subOptions = parser()
                    self.subOptions.parent = self
                    self.subOptions.parseOptions(rest)
                    break
            else:
                raise UsageError("Unknown command: %s" % sub)
        else:
            try:
                self.parseArgs(*args)
            except TypeError:
                raise UsageError("Wrong number of arguments.")

        self.postOptions()

def install_dailylog(application):    Myconfig = MyServerOptions()    Myconfig.parseOptions()    application.setComponent(ILogObserver, MyAppLogger(Myconfig).getLogObserver())四 結束語    本文就twisted下如何進行按天的日誌切分進行了探討,經過分析twisted的日誌記錄機制、程序啓動方式最終給出了一個比較好的實現方案,並將該方案封裝爲一個函數,以\方便你們的使用。

相關文章
相關標籤/搜索