Python日志记录中的精确时间戳
我最近一直在构建一个错误日志应用程序,并且正在寻找一种准确标记传入数据时间的方法。当我说精确时,我的意思是每个时间戳都应该相对精确(不需要同步到原子钟或类似的东西) 我一直使用datetime.now()作为第一个尝试,但这并不完美:Python日志记录中的精确时间戳,python,logging,timer,timestamp,Python,Logging,Timer,Timestamp,我最近一直在构建一个错误日志应用程序,并且正在寻找一种准确标记传入数据时间的方法。当我说精确时,我的意思是每个时间戳都应该相对精确(不需要同步到原子钟或类似的东西) 我一直使用datetime.now()作为第一个尝试,但这并不完美: >>> for i in range(0,1000): ... datetime.datetime.now() ... datetime.datetime(2008, 10, 1, 13, 17, 27, 562000) datetime
>>> for i in range(0,1000):
... datetime.datetime.now()
...
datetime.datetime(2008, 10, 1, 13, 17, 27, 562000)
datetime.datetime(2008, 10, 1, 13, 17, 27, 562000)
datetime.datetime(2008, 10, 1, 13, 17, 27, 562000)
datetime.datetime(2008, 10, 1, 13, 17, 27, 562000)
datetime.datetime(2008, 10, 1, 13, 17, 27, 578000)
datetime.datetime(2008, 10, 1, 13, 17, 27, 578000)
datetime.datetime(2008, 10, 1, 13, 17, 27, 578000)
datetime.datetime(2008, 10, 1, 13, 17, 27, 578000)
datetime.datetime(2008, 10, 1, 13, 17, 27, 578000)
datetime.datetime(2008, 10, 1, 13, 17, 27, 609000)
datetime.datetime(2008, 10, 1, 13, 17, 27, 609000)
datetime.datetime(2008, 10, 1, 13, 17, 27, 609000)
etc.
第一秒采样的时钟之间的变化如下所示:
uSecs difference
562000
578000 16000
609000 31000
625000 16000
640000 15000
656000 16000
687000 31000
703000 16000
718000 15000
750000 32000
765000 15000
781000 16000
796000 15000
828000 32000
843000 15000
859000 16000
890000 31000
906000 16000
921000 15000
937000 16000
968000 31000
984000 16000
因此,在我的机器上,计时器数据似乎每15-32毫秒更新一次。当我们分析数据时,问题就来了,因为按时间戳以外的东西排序,然后再按时间戳排序,可能会使数据按错误的顺序(按时间顺序)排列。如果时间戳精确到任何对时间戳生成器的调用都会给出唯一的时间戳,那就太好了
我一直在考虑一些方法,包括使用一个添加到起始日期时间的time.clock()调用,但我希望有一个能够在同一台机器上跨线程准确工作的解决方案。如果您有任何建议,我们将不胜感激。time.clock()只测量Windows上的挂钟时间。在其他系统上,time.clock()实际上测量CPU时间。在这些系统上,time.time()更适合于wallclock time,并且它的分辨率与Python所能管理的一样高——这是操作系统所能管理的;通常使用gettimeofday(3)(微秒分辨率)或ftime(3)(毫秒分辨率)。其他操作系统限制实际上使实际分辨率比这高得多。datetime.datetime.now()使用time.time(),因此直接使用time.time()不会更好
作为记录,如果我在循环中使用datetime.datetime.now(),我会看到大约1/10000秒的分辨率。从你的数据来看,你的分辨率比这要粗得多。我不确定Python本身是否能做些什么,尽管您可以通过其他方式说服操作系统做得更好
我似乎还记得,在Windows上,time.clock()实际上(稍微)比time.time()更精确,但自从第一次调用time.clock()以来,它就测量了wallclock,因此您必须记住先“初始化”它。这里有一个关于Python计时准确性的线程:
您不太可能获得足够细粒度的控制,从而完全消除这种可能性 重复时间戳的数量-您需要的分辨率小于生成datetime对象所需的时间。您可能会采取以下几种其他方法来处理此问题:
class TimeStamper(object):
def __init__(self):
self.lock = threading.Lock()
self.prev = None
self.count = 0
def getTimestamp(self):
with self.lock:
ts = str(datetime.now())
if ts == self.prev:
ts +='.%04d' % self.count
self.count += 1
else:
self.prev = ts
self.count = 1
return ts
但是,对于多个进程(而不是线程),它会变得有点棘手。“时间戳相对于彼此来说应该是准确的”
为什么是时间?为什么不是序列号?如果是客户机-服务器应用程序的任何客户机,网络延迟会使时间戳变得随机
您是否匹配外部信息源?比如说另一个应用程序的日志?再说一次,如果有网络,这些时间不会太近
如果你必须在不同的应用程序之间进行匹配,请考虑通过GUID,这样两个应用程序都会记录GUID值。然后你就可以完全确定它们是匹配的,而不考虑时间上的差异
如果您希望相对顺序完全正确,那么您的日志记录器就可以按照收到消息的顺序为每条消息分配一个序列号。感谢大家的贡献,它们都非常有用。布莱恩的答案似乎与我最终的答案最接近(即,处理它,但使用一种唯一的标识符-见下文),因此我接受了他的答案。我设法将所有不同的数据接收器整合到一个线程中,现在使用新的AccurateTimeStamp类完成时间戳。只要时间戳是使用时钟的第一件事,我所做的工作就会起作用 正如S.Lott所说,没有实时操作系统,它们永远不会绝对完美。我真的只想要一些东西,当接收到东西时,它能让我看到每个传入数据块的相对位置,这样我下面得到的东西就可以很好地工作了 再次感谢大家
import time
class AccurateTimeStamp():
"""
A simple class to provide a very accurate means of time stamping some data
"""
# Do the class-wide initial time stamp to synchronise calls to
# time.clock() to a single time stamp
initialTimeStamp = time.time()+ time.clock()
def __init__(self):
"""
Constructor for the AccurateTimeStamp class.
This makes a stamp based on the current time which should be more
accurate than anything you can get out of time.time().
NOTE: This time stamp will only work if nothing has called clock() in
this instance of the Python interpreter.
"""
# Get the time since the first of call to time.clock()
offset = time.clock()
# Get the current (accurate) time
currentTime = AccurateTimeStamp.initialTimeStamp+offset
# Split the time into whole seconds and the portion after the fraction
self.accurateSeconds = int(currentTime)
self.accuratePastSecond = currentTime - self.accurateSeconds
def GetAccurateTimeStampString(timestamp):
"""
Function to produce a timestamp of the form "13:48:01.87123" representing
the time stamp 'timestamp'
"""
# Get a struct_time representing the number of whole seconds since the
# epoch that we can use to format the time stamp
wholeSecondsInTimeStamp = time.localtime(timestamp.accurateSeconds)
# Convert the whole seconds and whatever fraction of a second comes after
# into a couple of strings
wholeSecondsString = time.strftime("%H:%M:%S", wholeSecondsInTimeStamp)
fractionAfterSecondString = str(int(timestamp.accuratePastSecond*1000000))
# Return our shiny new accurate time stamp
return wholeSecondsString+"."+fractionAfterSecondString
if __name__ == '__main__':
for i in range(0,500):
timestamp = AccurateTimeStamp()
print GetAccurateTimeStampString(timestamp)
我想感谢J.凯奇的最后一篇文章 对于我的工作,跨流程和平台的事件“合理”时间安排至关重要。显然,在很多地方,事情可能会发生偏差(时钟漂移、上下文切换等),但是我认为,这种精确的计时解决方案将有助于确保记录的时间戳足够精确,以查看其他错误源
也就是说,有一些细节我想知道,这是解释在。例如,我认为time.clock()最终将结束。我认为,要使这个问题在长时间运行的过程中起作用,您可能必须处理这个问题。几年前,这个问题已经被提出和回答,并且已经解决了,至少对于Windows上的CPython来说是这样。在Win7 64位和Windows Server 2008 R2上使用下面的脚本,我得到了相同的结果:
的分辨率为1ms,抖动小于1msdatetime.now()
的分辨率优于1us,抖动远小于1mstime.clock()
import time
import datetime
t1_0 = time.clock()
t2_0 = datetime.datetime.now()
with open('output.csv', 'w') as f:
for i in xrange(100000):
t1 = time.clock()
t2 = datetime.datetime.now()
td1 = t1-t1_0
td2 = (t2-t2_0).total_seconds()
f.write('%.6f,%.6f\n' % (td1, td2))
结果可视化:
如果您想要微秒分辨率(而不是精度)的时间戳,请使用p