Logging Muti-Progress

<center>

Author: Mikoy Date: 2018-08-25

</center>

Introduction:

??Recently, I have tried to record some infomations occurred during progress running automatically. Because the main language is Python, so I choose the logging module to achieve it.

??Firstly, I can record log when I use logging module. But when I tried to write log infomation two times or more, I found that the log file will be re-written with cleaning up all my infomation written last time. And then I have tested it by making two progress to write log infomating using logging module to one log file. This time I found that if the two progresses write infomation at the same time, Python Global Interpreter Lock(GIL) will prevent multiple progress form executing the I/O operations. So the fact is just one progress writted log infomation in log file.

??But why the log file will be cleaned up? I searched my question in google and found the result. Because the logging module include class TimedRotatingFileHandler(BaseRotatingHandler), and we see it sources code, strengthen the reson code:

def doRollover(self):
    ...
    ...
    dfn = self.rotation_filename(self.baseFilename + "." +
                                     time.strftime(self.suffix, timeTuple))
    if os.path.exists(dfn):
        os.remove(dfn)
    self.rotate(self.baseFilename, dfn)
    if self.backupCount > 0:
        for s in self.getFilesToDelete():
            os.remove(s)        
    ...
    ...  

??As you see, it will remove the log file if it existed. So we try to create a class like TimedRotatingFileHandler, but support for Muti-Progress.

The coding thinking:

graph TD
a[Log file Request] --> b{shouldRollover?}
b -->|True| c[Create a new log file]
b -->|False| d[file.stream.close]
d -->f[open log file]
f --> g[if file exists, create a symlink to the currentTime log file and make the I/O stream point it.]

??When a progress request I/O operation, firstly judge it whether should Rollover? if this log file is the currenttime file you want to record it. if is True return True, while False return False. And the doRollover receive the parameter to do rollover. When it rollover, it makes a symlink pointing to the current time file and then record it.

The main code:

...
...
...

def shouldRollover(self, record):
        if self.currentFileName != self._compute_fn():
            return True
        return False


def _open(self):
        if self.encoding is None:
            stream = open(self.currentFileName, self.mode)
        else:
            stream = codecs.open(self.currentFileName, self.mode, self.encoding)
        # simulate file name structure of `logging.TimedRotatingFileHandler`
        if os.path.exists(self.baseFilename):
            try:
                os.remove(self.baseFilename)
            except OSError:
                pass
        try:
            # Create the symlink point the lastest log file.
            os.symlink(self.currentFileName, self.baseFilename)
        except OSError:
            pass
        return stream
...
...
...

The whole source code:

class MultiProcessSafeDailyRotatingFileHandler(BaseRotatingHandler):
    """
    Similar with `logging.TimedRotatingFileHandler`, while this one is
    - Multi process safe
    - Rotate at midnight only
    - Utc not supported
    - This log will create day by day.
    """
    def __init__(self, filename, encoding=None, delay=False, utc=False, **kwargs):
        self.utc = utc
        self.suffix = "%Y%m%d"
        self.baseFilename = os.path.abspath(filename)
        self.currentFileName = self._compute_fn()
        BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)

    def shouldRollover(self, record):
        if self.currentFileName != self._compute_fn():
            return True
        return False

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

    def _compute_fn(self):
        return self.baseFilename + "." + time.strftime(self.suffix, time.localtime())

    def _open(self):
        if self.encoding is None:
            stream = open(self.currentFileName, self.mode)
        else:
            stream = codecs.open(self.currentFileName, self.mode, self.encoding)
        # simulate file name structure of `logging.TimedRotatingFileHandler`
        if os.path.exists(self.baseFilename):
            try:
                os.remove(self.baseFilename)
            except OSError:
                pass
        try:
            # Create the symlink point the lastest log file.
            os.symlink(self.currentFileName, self.baseFilename)
        except OSError:
            pass
        return stream

Finally

??Let me tell you some usages of logging module: This module defines functions and classes which implement a flexible event logging system for applications and libraries. The key benefit of having the logging API provided by a standard library module is that all Python modules can participate in logging, so your application log can include your own messages integrated with messages from third-party modules.

Usage:

logger --> handler --> formatter --> output

  1. Create logger
    logger = logging.getLogger('user')
    logger.setLevel('DEBUG')
  1. Create handler and set leverl
    handler = logging.StreamHandler()
    handler.setLevel('DEBUG')
  1. Create formatter
    handler.setFormatter('%(levelname)s: %(message)s') 
  1. Add to logger
    logger.addHandler(handler)

My simple:

"""
    Author: Mikoy
    Github: github.com/MikoyChinese
    The name of the log file changes automatically based on time.
    LOGGING_CONFIG: the config file about the logging.
    <logging.config.dictConfig>: such like next,
    logger --> handler --> formatter --> output
    # Create logger
    logger = logging.getLogger('user')
    logger.setLevel('DEBUG')
    # Create handler and set leverl
    handler = logging.StreamHandler()
    handler.setLevel('DEBUG')
    # Create formatter
    handler.setFormatter('%(levelname)s: %(message)s')
    # Add to logger
    logger.addHandler(handler)
"""


from logging.handlers import BaseRotatingHandler
import os, time, codecs, logging, logging.config


class MyLogging(object):
    def __init__(self, logger_name='user'):
        # 1.The configuration below has no file to output.
        self.LOGGING_CONFIG = {
            'version': 1,
            'disable_existing_loggers': False,
            'formatters': {
                'standard': {
                    'format': '[%(levelname)s] %(asctime)s %(module)s %('
                              'process)d %(thread)d: %(message)s',
                    'datefmt': '%H:%M:%S'
                },
                'simple': {
                    'format': '[%(levelname)s]: %(message)s'
                }
            },
            'handlers': {
                'console': {
                    'level': 'DEBUG',
                    'class': 'logging.StreamHandler',
                    'formatter': 'simple'
                },
                'default': {
                    'level': 'DEBUG',
                    'class': 'loggingModule.MultiProcessSafeDailyRotatingFileHandler',
                    'formatter': 'standard',
                    'filename': './log/info.log'
                }
            },
            'loggers': {
                'user': {
                    'level': 'DEBUG',
                    'handlers': ['console', 'default']
                }
            }
        }
        # 2. To get the absolute path of the log file.
        # Log file is stored under the 'log' folder. If it not existed,
        # create it auto.
        dir_path = os.path.join(os.path.dirname(__file__), 'log')
        if not os.path.exists(dir_path):
            os.makedirs(dir_path)
        # 3. Loading the config, and make it effect.
        # This command can not be executed after the logging.getLogger function
        # because there is no specific logger name.
        logging.config.dictConfig(self.LOGGING_CONFIG)
        self.logger = logging.getLogger(logger_name)  # return a logger


class MultiProcessSafeDailyRotatingFileHandler(BaseRotatingHandler):
    """
    Similar with `logging.TimedRotatingFileHandler`, while this one is
    - Multi process safe
    - Rotate at midnight only
    - Utc not supported
    - This log will create day by day.
    """
    def __init__(self, filename, encoding=None, delay=False, utc=False, **kwargs):
        self.utc = utc
        self.suffix = "%Y%m%d"
        self.baseFilename = os.path.abspath(filename)
        self.currentFileName = self._compute_fn()
        BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)

    def shouldRollover(self, record):
        if self.currentFileName != self._compute_fn():
            return True
        return False

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

    def _compute_fn(self):
        return self.baseFilename + "." + time.strftime(self.suffix, time.localtime())

    def _open(self):
        if self.encoding is None:
            stream = open(self.currentFileName, self.mode)
        else:
            stream = codecs.open(self.currentFileName, self.mode, self.encoding)
        # simulate file name structure of `logging.TimedRotatingFileHandler`
        if os.path.exists(self.baseFilename):
            try:
                os.remove(self.baseFilename)
            except OSError:
                pass
        try:
            # Create the symlink point the lastest log file.
            os.symlink(self.currentFileName, self.baseFilename)
        except OSError:
            pass
        return stream


if __name__ == '__main__':
    mylogging = MyLogging(logger_name='user')  # This is a customized class;
    while 1:
        mylogging.logger.debug('%s' % time.time())
        time.sleep(5)
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市棋嘲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖裆馒,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異在讶,居然都是意外死亡抢肛,警方通過查閱死者的電腦和手機狼钮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捡絮,“玉大人熬芜,你說我怎么就攤上這事〗踉” “怎么了猛蔽?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長灵寺。 經(jīng)常有香客問我曼库,道長,這世上最難降的妖魔是什么略板? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任毁枯,我火速辦了婚禮,結果婚禮上叮称,老公的妹妹穿的比我還像新娘种玛。我一直安慰自己,他們只是感情好瓤檐,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布赂韵。 她就那樣靜靜地躺著,像睡著了一般挠蛉。 火紅的嫁衣襯著肌膚如雪祭示。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天谴古,我揣著相機與錄音质涛,去河邊找鬼。 笑死掰担,一個胖子當著我的面吹牛汇陆,可吹牛的內容都是我干的。 我是一名探鬼主播带饱,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼毡代,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起月趟,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤灯蝴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后孝宗,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡耕肩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年因妇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猿诸。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡婚被,死狀恐怖,靈堂內的尸體忽然破棺而出梳虽,到底是詐尸還是另有隱情址芯,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布窜觉,位于F島的核電站谷炸,受9級特大地震影響,放射性物質發(fā)生泄漏禀挫。R本人自食惡果不足惜旬陡,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望语婴。 院中可真熱鬧描孟,春花似錦、人聲如沸砰左。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缠导。三九已至廉羔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間酬核,已是汗流浹背蜜另。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嫡意,地道東北人举瑰。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像蔬螟,于是被迫代替她去往敵國和親此迅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內容