How to Change the time zone in Python logging?

前端 未结 7 1242
耶瑟儿~
耶瑟儿~ 2021-01-31 18:30

I would like to change the timestamp in the log file so that it reflects my current time zone so that i can debug errors at a faster rate,

is it possible that i can cha

相关标签:
7条回答
  • 2021-01-31 19:02

    How to log the timezone

    %Z from strftime format

    Windows

    >>> import logging
    >>> logging.basicConfig(format="%(asctime)s %(message)s", datefmt="%m/%d/%Y %I:%M:%S %p %Z")
    >>> logging.error('test')
    11/03/2017 02:29:54 PM Mountain Daylight Time test
    

    Linux

    >>> import logging
    >>> logging.basicConfig(format="%(asctime)s %(message)s", datefmt="%m/%d/%Y %I:%M:%S %p %Z")
    >>> logging.error('test')
    11/03/2017 02:30:50 PM MDT test
    

    If the question is

    How do I log in a different timezone than the local time on the server?

    part of the answer is logging.Formatter.converter, however, you have to understand naive and aware datetime objects. Unless you want to write your own timezone module, I highly suggest the pytz library (pip install pytz). Python 3 includes a UTC and UTC offset timezone, but there's rules you'll have to implement for daylight saving or other offsets, so I would suggest the pytz library, even for python 3.

    For example,

    >>> import datetime
    >>> utc_now = datetime.datetime.utcnow()
    >>> utc_now.isoformat()
    '2019-05-21T02:30:09.422638'
    >>> utc_now.tzinfo
    (None)
    

    If I apply a timezone to this datetime object, the time won't change (or will issue a ValueError for < python 3.7ish).

    >>> mst_now = utc_now.astimezone(pytz.timezone('America/Denver'))
    >>> mst_now.isoformat()
    '2019-05-21T02:30:09.422638-06:00'
    >>> utc_now.isoformat()
    '2019-05-21T02:30:09.422638'
    

    However, if instead, I do

    >>> import pytz
    >>> utc_now = datetime.datetime.now(tz=pytz.timezone('UTC'))
    >>> utc_now.tzinfo
    <UTC>
    

    now we can create a properly translated datetime object in whatever timezone we wish

    >>> mst_now = utc_now.astimezone(pytz.timezone('America/Denver'))
    >>> mst_now.isoformat()
    '2019-05-20T20:31:44.913939-06:00'
    

    Aha! Now to apply this to the logging module.

    Epoch timestamp to string representation with timezone

    The LogRecord.created attribute is set to the time when the LogRecord was created (as returned by time.time()), from the time module. This returns a timestamp (seconds since the epoch). You can do your own translation to a given timezone, but again, I suggest pytz, by overriding the converter.

    import datetime
    import logging
    import pytz
    
    class Formatter(logging.Formatter):
        """override logging.Formatter to use an aware datetime object"""
        def converter(self, timestamp):
            dt = datetime.datetime.fromtimestamp(timestamp)
            tzinfo = pytz.timezone('America/Denver')
            return tzinfo.localize(dt)
            
        def formatTime(self, record, datefmt=None):
            dt = self.converter(record.created)
            if datefmt:
                s = dt.strftime(datefmt)
            else:
                try:
                    s = dt.isoformat(timespec='milliseconds')
                except TypeError:
                    s = dt.isoformat()
            return s
    

    Python 3.5, 2.7

    >>> logger = logging.root
    >>> handler = logging.StreamHandler()
    >>> handler.setFormatter(Formatter("%(asctime)s %(message)s"))
    >>> logger.addHandler(handler)
    >>> logger.setLevel(logging.DEBUG)
    >>> logger.debug('test')
    2019-05-20T22:25:10.758782-06:00 test
    

    Python 3.7

    >>> logger = logging.root
    >>> handler = logging.StreamHandler()
    >>> handler.setFormatter(Formatter("%(asctime)s %(message)s"))
    >>> logger.addHandler(handler)
    >>> logger.setLevel(logging.DEBUG)
    >>> logger.debug('test')
    2019-05-20T22:29:21.678-06:00 test
    

    Substitute America/Denver with America/Anchorage for the posix timezone as defined by pytz

    >>> next(_ for _ in pytz.common_timezones if 'Alaska' in _)
    'US/Alaska'
    

    US/Alaska is deprecated

    >>> [_ for _ in pytz.all_timezones if 'Anchorage' in _]
    ['America/Anchorage']
    

    Local

    If you got to this question and answers looking for how to log the local timezone, then instead of hardcoding the timezone, get tzlocal (pip install tzlocal) and replace

            tzinfo = pytz.timezone('America/Denver')
    

    with

            tzinfo = tzlocal.get_localzone()
    

    Now it will work on whatever server runs the script, with the timezone on the server.

    Caveat when not logging UTC

    I should add, depending on the application, logging in local time zones can create ambiguity or at least confusion twice a year, where 2 AM is skipped or 1 AM repeats, and possibly others.

    0 讨论(0)
  • 2021-01-31 19:04

    An alternative solution if you want to use logging configuration function:

    import pytz
    import logging
    import logging.config
    from datetime import datetime
    
    tz = pytz.timezone('Asia/Tokyo')
    
    class TokyoFormatter(logging.Formatter):
        converter = lambda *args: datetime.now(tz).timetuple()
    
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'beijing': {
                '()': TokyoFormatter,
                'format': '%(asctime)s %(levelname)s: %(message)s',
                'datefmt': '%Y-%m-%d %H:%M:%S'
            },
        },
        'handlers': {
            'console': {
                'class': 'logging.StreamHandler',
                'formatter': 'beijing'
            },
        },
        'loggers': {
            'foo': {
                'handlers': ['console'],
                'level': 'INFO'
            },
        }
    }
    
    logging.config.dictConfig(LOGGING)
    logger = logging.getLogger('foo')
    logger.info('Just a test.')
    

    For more details, please refer to Customizing handlers with dictConfig().

    0 讨论(0)
  • 2021-01-31 19:15
    import logging, time
    from datetime import datetime, timedelta
    logger = logging.getLogger(__name__)
    converter = lambda x, y: (datetime.utcnow() - timedelta(
        hours=7 if time.localtime().tm_isdst else 6)
    ).timetuple()
    logging.Formatter.converter = converter
    

    Edited as Elias points out the original answer didn't check for DST.

    0 讨论(0)
  • 2021-01-31 19:16

    just add this this pythonic line to your code (using pytz and datetime):

    from pytz import timezone
    from datetime import datetime
    
    logging.Formatter.converter = lambda *args: datetime.now(tz=timezone('tz string name')).timetuple()
    
    # quoting Ryan J McCall: to find the string name for your desired timezone...
    # print(pytz.all_timezones)
    # or print(pytz.common_timezones)
    
    0 讨论(0)
  • 2021-01-31 19:21

    If you know your utc offset, you can define a function to correct the time and then pass it to logging.Formatter.converter.

    For example, you want to convert the time to UTC+8 timezone, then:

    import logging
    import datetime
    
    
    def beijing(sec, what):
        '''sec and what is unused.'''
        beijing_time = datetime.datetime.now() + datetime.timedelta(hours=8)
        return beijing_time.timetuple()
    
    
    logging.Formatter.converter = beijing
    
    logging.basicConfig(
        format="%(asctime)s %(levelname)s: %(message)s",
        level=logging.INFO,
        datefmt="%Y-%m-%d %H:%M:%S",
    )
    

    Just change the hours in datetime.timedelta(hours=8) depending on your situation.

    Reference: https://alanlee.fun/2019/01/06/how-to-change-logging-date-timezone/

    0 讨论(0)
  • 2021-01-31 19:22
    #!/usr/bin/env python
    from datetime import datetime
    import logging
    import time
    
    from pytz import timezone, utc
    
    
    def main():
        logging.basicConfig(format="%(asctime)s %(message)s",
                            datefmt="%Y-%m-%d %H:%M:%S")
        logger = logging.getLogger(__name__)
        logger.error("default")
    
        logging.Formatter.converter = time.localtime
        logger.error("localtime")
    
        logging.Formatter.converter = time.gmtime
        logger.error("gmtime")
    
        def customTime(*args):
            utc_dt = utc.localize(datetime.utcnow())
            my_tz = timezone("US/Eastern")
            converted = utc_dt.astimezone(my_tz)
            return converted.timetuple()
    
        logging.Formatter.converter = customTime
        logger.error("customTime")
    
        # to find the string code for your desired tz...
        # print(pytz.all_timezones)
        # print(pytz.common_timezones)
    
    
    if __name__ == "__main__":
        main()
    
    • Ostensibly the pytz package is the blessed way of converting time zones in Python. So we start with datetime, convert, then get the (immutable) time_tuple to match return type of the time methods
    • Setting the logging.Formatter.converter function is recommended by this answer: (Python logging: How to set time to GMT).
    • Find your favorite TZ code by uncommenting the end lines
    0 讨论(0)
提交回复
热议问题