6

TimedRotatingFileHandlerを使用して、毎日のログを別のログ ファイルに保存しようとしています。ローテーションは意図したとおりに完全に機能しますが、その方法が気に入らないのはファイルの命名です。

ログ ファイルを my_log_file.log として設定すると、これが「今日の」ログ ファイルになり、午前 0 時に日が変わるとmy_log_file.log.2014-07-08、最後に .log 拡張子のない名前に変更され、新しいログ ファイルmy_log_file.logが作成されます。新しい日。

私が取得したいのは、古いファイルの名前がmy_log_file.2014-07-08.logまたはに変更されていることですmy_log_file-2014-07-08.log。主に、途中ではなく最後に.logがあります。また、「今日の」ログ ファイルには、古いものと同じように、今日の日付で既に名前が付けられていることを望みます。

そうする方法はありますか?

次の方法で接尾辞をパーソナライズできることがわかりました。

handler.suffix = "%Y-%m-%d"

しかし、内部の .log 部分を削除し、現在のログ ファイルに強制的にサフィックスを追加する方法がわかりません。

4

4 に答える 4

5

ParallelTimedRotatingFileHandler クラスを作成しました。主な目的は、複数のプロセスが並行してログ ファイルに書き込みできるようにすることです。このクラスによって解決される並列プロセスの問題は次のとおりです。

  • すべてのプロセスが同時に同じファイルをコピーまたは名前変更しようとするロールオーバーの瞬間に、エラーが発生します。
  • この問題の解決策は、提案した命名規則とまったく同じでした。そのため、ハンドラーで指定したファイル名Serviceの場合、ログは eg ではなくService.log、today toService.2014-08-18.logおよび Tomorrowに記録されService.2014-08-19.logます。
  • 別の解決策は、並列書き込みを許可するa代わりに、(追加) モードでファイルを開くことです。w
  • 複数の並列プロセスが同じファイルを同時に削除しているため、バックアップ ファイルの削除も注意して行う必要があります。
  • この実装ではうるう秒は考慮されていません (これは Unix では問題になりません)。他の OS では、ロールオーバーの時点でまだ 30/6/2008 23:59:60 である可能性があるため、日付は変更されていないため、昨日と同じファイル名を使用します。
  • 標準的な Python の推奨事項は、loggingモジュールが並列プロセスに対して予見されないことであり、SocketHandler を使用する必要があることを知っていますが、少なくとも私の環境では、これは機能します。

このコードは、標準の Python handlers.py モジュールのコードをわずかに変更したものです。もちろん著作権は著作権者にあります。

コードは次のとおりです。

import logging
import logging.handlers
import os
import time
import re

class ParallelTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
    def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, postfix = ".log"):

        self.origFileName = filename
        self.when = when.upper()
        self.interval = interval
        self.backupCount = backupCount
        self.utc = utc
        self.postfix = postfix

        if self.when == 'S':
            self.interval = 1 # one second
            self.suffix = "%Y-%m-%d_%H-%M-%S"
            self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$"
        elif self.when == 'M':
            self.interval = 60 # one minute
            self.suffix = "%Y-%m-%d_%H-%M"
            self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}$"
        elif self.when == 'H':
            self.interval = 60 * 60 # one hour
            self.suffix = "%Y-%m-%d_%H"
            self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}$"
        elif self.when == 'D' or self.when == 'MIDNIGHT':
            self.interval = 60 * 60 * 24 # one day
            self.suffix = "%Y-%m-%d"
            self.extMatch = r"^\d{4}-\d{2}-\d{2}$"
        elif self.when.startswith('W'):
            self.interval = 60 * 60 * 24 * 7 # one week
            if len(self.when) != 2:
                raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
            if self.when[1] < '0' or self.when[1] > '6':
                 raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
            self.dayOfWeek = int(self.when[1])
            self.suffix = "%Y-%m-%d"
            self.extMatch = r"^\d{4}-\d{2}-\d{2}$"
        else:
            raise ValueError("Invalid rollover interval specified: %s" % self.when)

        currenttime = int(time.time())
        logging.handlers.BaseRotatingHandler.__init__(self, self.calculateFileName(currenttime), 'a', encoding, delay)

        self.extMatch = re.compile(self.extMatch)
        self.interval = self.interval * interval # multiply by units requested

        self.rolloverAt = self.computeRollover(currenttime)

    def calculateFileName(self, currenttime):
        if self.utc:
             timeTuple = time.gmtime(currenttime)
        else:
             timeTuple = time.localtime(currenttime)

        return self.origFileName + "." + time.strftime(self.suffix, timeTuple) + self.postfix

    def getFilesToDelete(self, newFileName):
        dirName, fName = os.path.split(self.origFileName)
        dName, newFileName = os.path.split(newFileName)

        fileNames = os.listdir(dirName)
        result = []
        prefix = fName + "."
        postfix = self.postfix
        prelen = len(prefix)
        postlen = len(postfix)
        for fileName in fileNames:
            if fileName[:prelen] == prefix and fileName[-postlen:] == postfix and len(fileName)-postlen > prelen and fileName != newFileName:
                 suffix = fileName[prelen:len(fileName)-postlen]
                 if self.extMatch.match(suffix):
                     result.append(os.path.join(dirName, fileName))
        result.sort()
        if len(result) < self.backupCount:
            result = []
        else:
            result = result[:len(result) - self.backupCount]
        return result

     def doRollover(self):
         if self.stream:
            self.stream.close()
            self.stream = None

         currentTime = self.rolloverAt
         newFileName = self.calculateFileName(currentTime)
         newBaseFileName = os.path.abspath(newFileName)
         self.baseFilename = newBaseFileName
         self.mode = 'a'
         self.stream = self._open()

         if self.backupCount > 0:
             for s in self.getFilesToDelete(newFileName):
                 try:
                     os.remove(s)
                 except:
                     pass

         newRolloverAt = self.computeRollover(currentTime)
         while newRolloverAt <= currentTime:
             newRolloverAt = newRolloverAt + self.interval

         #If DST changes and midnight or weekly rollover, adjust for this.
         if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
             dstNow = time.localtime(currentTime)[-1]
             dstAtRollover = time.localtime(newRolloverAt)[-1]
             if dstNow != dstAtRollover:
                 if not dstNow:  # DST kicks in before next rollover, so we need to deduct an hour
                     newRolloverAt = newRolloverAt - 3600
                 else:           # DST bows out before next rollover, so we need to add an hour
                     newRolloverAt = newRolloverAt + 3600
         self.rolloverAt = newRolloverAt
于 2014-08-19T15:21:25.773 に答える
4

私の知る限り、これを直接達成する方法はありません。

試すことができる解決策の 1 つは、デフォルトの動作をオーバーライドすることです。

  • 独自のものTimedRotatingFileHandler classを作成し、doRollover() function.
  • Pythonインストールのソースを確認してください<PythonInstallDir>/Lib/logging/handlers.py

このようなもの:

class MyTimedRotatingFileHandler(TimedRotatingFileHandler):
    def __init__(self, **kwargs):
        TimedRotatingFileHandler.__init__(self, **kwargs)

    def doRollover(self):
        # Do your stuff, rename the file as you want 
于 2014-07-09T09:43:56.260 に答える
0

Python 3.7 でソリューションhttps://stackoverflow.com/a/25387192/6619512を使用しましたが、これは優れたソリューションです。

しかし、「真夜中」のwhenパラメータと「W」で始まる when パラメータでは、atTime パラメータが導入され、TimedRotatingFileHandler クラスで使用されたため、機能しませんでし

このソリューションを利用するには、次の __init__ 行を使用します。

def __init__(self, filename, when='h', interval=1, backupCount=0,
             encoding=None, delay=False, utc=False, atTime=None, postfix = ".log"):

また、__init__ 宣言の内容に以下を追加します。

self.postfix = postfix
于 2019-11-22T09:15:00.473 に答える