如何更改 Python 日志中的时区?
How to Change the time zone in Python logging?
我想更改日志文件中的时间戳,使其反映我当前的时区,以便我可以更快地调试错误,
我可以更改日志文件中的时区吗?
目前我的配置是:
logging.basicConfig(filename='audit.log',
filemode='w',
level=logging.INFO,
format='%(asctime)s %(message)s',
datefmt='%m/%d/%Y %I:%M:%S %p')
#!/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()
- 从表面上看,
pytz
包是在 Python 中转换时区的好方法。所以我们从 datetime
开始,转换,然后得到(不可变的)time_tuple
来匹配 time
方法 的 return 类型
- 这个答案推荐设置
logging.Formatter.converter
函数:(Python logging: How to set time to GMT).
- 通过取消注释结尾行找到您最喜欢的 TZ 代码
如何记录时区
%Z
来自 strftime 格式
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
如果问题是
How do I log in a different timezone than the local time on the server?
部分答案是 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 库 (pip install pytz
)。 Python 3 包含 UTC 和 UTC 偏移时区,但是对于夏令时或其他偏移,您必须执行一些规则,所以我建议使用 pytz 库,即使对于 python 3.
例如,
>>> import datetime
>>> utc_now = datetime.datetime.utcnow()
>>> utc_now.isoformat()
'2019-05-21T02:30:09.422638'
>>> utc_now.tzinfo
(None)
如果我将时区应用到这个日期时间对象,时间将不会改变(或者将发出 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'
但是,如果相反,我会
>>> import pytz
>>> utc_now = datetime.datetime.now(tz=pytz.timezone('UTC'))
>>> utc_now.tzinfo
<UTC>
现在我们可以在我们希望的任何时区创建一个正确翻译的 datetime
对象
>>> mst_now = utc_now.astimezone(pytz.timezone('America/Denver'))
>>> mst_now.isoformat()
'2019-05-20T20:31:44.913939-06:00'
啊哈!现在将其应用于日志记录模块。
带时区的字符串表示的纪元时间戳
LogRecord.created
属性设置为创建 LogRecord
的时间(由 time.time()
), from the time
module. This returns a timestamp (seconds since the epoch 返回)。您可以对给定时区进行自己的翻译,但我再次建议 pytz
,通过覆盖转换器。
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
用 America/Anchorage
替换 America/Denver
为 pytz
定义的 posix 时区
>>> next(_ for _ in pytz.common_timezones if 'Alaska' in _)
'US/Alaska'
>>> [_ for _ in pytz.all_timezones if 'Anchorage' in _]
['America/Anchorage']
本地
如果您遇到这个问题并寻找如何记录本地时区的答案,那么不要对时区进行硬编码,而是获取 tzlocal
(pip install tzlocal
) 并替换
tzinfo = pytz.timezone('America/Denver')
和
tzinfo = tzlocal.get_localzone()
现在它可以在任何运行该脚本的服务器上运行,并使用服务器上的时区。
不记录 UTC 时的注意事项
我应该补充一点,根据应用程序,在当地时区登录可能会产生歧义或至少每年两次造成混淆,其中跳过凌晨 2 点或重复凌晨 1 点,可能还有其他情况。
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
编辑为 Elias 指出原始答案没有检查夏令时。
如果你知道你的utc offset,你可以定义一个函数来校正时间,然后将它传递给logging.Formatter.converter
。
比如你想把时间转换成UTC+8时区,那么:
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",
)
只需根据您的情况更改 datetime.timedelta(hours=8)
中的时间。
参考:https://alanlee.fun/2019/01/06/how-to-change-logging-date-timezone/
#!/usr/bin/python
from datetime import datetime
from pytz import timezone
import logging
def timetz(*args):
return datetime.now(tz).timetuple()
tz = timezone('Asia/Shanghai') # UTC, Asia/Shanghai, Europe/Berlin
logging.Formatter.converter = timetz
logging.basicConfig(
format="%(asctime)s %(levelname)s: %(message)s",
level=logging.INFO,
datefmt="%Y-%m-%d %H:%M:%S",
)
logging.info('Timezone: ' + str(tz))
使用 pytz 定义相对于 UTC 的时区。
基于示例:secsilm
只需将此 pythonic 行添加到您的代码中(使用 pytz 和日期时间):
from pytz import timezone
from datetime import datetime
import logging
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)
如果要使用日志配置功能的替代解决方案:
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': {
'Tokyo': {
'()': TokyoFormatter,
'format': '%(asctime)s %(levelname)s: %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'Tokyo'
},
},
'loggers': {
'foo': {
'handlers': ['console'],
'level': 'INFO'
},
}
}
logging.config.dictConfig(LOGGING)
logger = logging.getLogger('foo')
logger.info('Just a test.')
定义日志格式化程序,例如“TokyoFormatter”。它有一个属性“converter”,完成时区转换的工作。
详情请参考Customizing handlers with dictConfig().
我想更改日志文件中的时间戳,使其反映我当前的时区,以便我可以更快地调试错误,
我可以更改日志文件中的时区吗?
目前我的配置是:
logging.basicConfig(filename='audit.log',
filemode='w',
level=logging.INFO,
format='%(asctime)s %(message)s',
datefmt='%m/%d/%Y %I:%M:%S %p')
#!/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()
- 从表面上看,
pytz
包是在 Python 中转换时区的好方法。所以我们从datetime
开始,转换,然后得到(不可变的)time_tuple
来匹配time
方法 的 return 类型
- 这个答案推荐设置
logging.Formatter.converter
函数:(Python logging: How to set time to GMT). - 通过取消注释结尾行找到您最喜欢的 TZ 代码
如何记录时区
%Z
来自 strftime 格式
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
如果问题是
How do I log in a different timezone than the local time on the server?
部分答案是 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 库 (pip install pytz
)。 Python 3 包含 UTC 和 UTC 偏移时区,但是对于夏令时或其他偏移,您必须执行一些规则,所以我建议使用 pytz 库,即使对于 python 3.
例如,
>>> import datetime
>>> utc_now = datetime.datetime.utcnow()
>>> utc_now.isoformat()
'2019-05-21T02:30:09.422638'
>>> utc_now.tzinfo
(None)
如果我将时区应用到这个日期时间对象,时间将不会改变(或者将发出 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'
但是,如果相反,我会
>>> import pytz
>>> utc_now = datetime.datetime.now(tz=pytz.timezone('UTC'))
>>> utc_now.tzinfo
<UTC>
现在我们可以在我们希望的任何时区创建一个正确翻译的 datetime
对象
>>> mst_now = utc_now.astimezone(pytz.timezone('America/Denver'))
>>> mst_now.isoformat()
'2019-05-20T20:31:44.913939-06:00'
啊哈!现在将其应用于日志记录模块。
带时区的字符串表示的纪元时间戳
LogRecord.created
属性设置为创建 LogRecord
的时间(由 time.time()
), from the time
module. This returns a timestamp (seconds since the epoch 返回)。您可以对给定时区进行自己的翻译,但我再次建议 pytz
,通过覆盖转换器。
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
用 America/Anchorage
替换 America/Denver
为 pytz
>>> next(_ for _ in pytz.common_timezones if 'Alaska' in _)
'US/Alaska'
>>> [_ for _ in pytz.all_timezones if 'Anchorage' in _]
['America/Anchorage']
本地
如果您遇到这个问题并寻找如何记录本地时区的答案,那么不要对时区进行硬编码,而是获取 tzlocal
(pip install tzlocal
) 并替换
tzinfo = pytz.timezone('America/Denver')
和
tzinfo = tzlocal.get_localzone()
现在它可以在任何运行该脚本的服务器上运行,并使用服务器上的时区。
不记录 UTC 时的注意事项
我应该补充一点,根据应用程序,在当地时区登录可能会产生歧义或至少每年两次造成混淆,其中跳过凌晨 2 点或重复凌晨 1 点,可能还有其他情况。
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
编辑为 Elias 指出原始答案没有检查夏令时。
如果你知道你的utc offset,你可以定义一个函数来校正时间,然后将它传递给logging.Formatter.converter
。
比如你想把时间转换成UTC+8时区,那么:
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",
)
只需根据您的情况更改 datetime.timedelta(hours=8)
中的时间。
参考:https://alanlee.fun/2019/01/06/how-to-change-logging-date-timezone/
#!/usr/bin/python
from datetime import datetime
from pytz import timezone
import logging
def timetz(*args):
return datetime.now(tz).timetuple()
tz = timezone('Asia/Shanghai') # UTC, Asia/Shanghai, Europe/Berlin
logging.Formatter.converter = timetz
logging.basicConfig(
format="%(asctime)s %(levelname)s: %(message)s",
level=logging.INFO,
datefmt="%Y-%m-%d %H:%M:%S",
)
logging.info('Timezone: ' + str(tz))
使用 pytz 定义相对于 UTC 的时区。
基于示例:secsilm
只需将此 pythonic 行添加到您的代码中(使用 pytz 和日期时间):
from pytz import timezone
from datetime import datetime
import logging
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)
如果要使用日志配置功能的替代解决方案:
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': {
'Tokyo': {
'()': TokyoFormatter,
'format': '%(asctime)s %(levelname)s: %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'Tokyo'
},
},
'loggers': {
'foo': {
'handlers': ['console'],
'level': 'INFO'
},
}
}
logging.config.dictConfig(LOGGING)
logger = logging.getLogger('foo')
logger.info('Just a test.')
定义日志格式化程序,例如“TokyoFormatter”。它有一个属性“converter”,完成时区转换的工作。 详情请参考Customizing handlers with dictConfig().