Python日志记录中的准确时间戳
我最近一直在构建一个错误记录应用程序,并且正在使用一种精确地为输入数据加上时间戳的方法。当我说准确时,我的意思是每个时间戳相对于彼此都应该是准确的(不需要同步到原子钟或者类似的时钟)。
我一直在使用datetime.now()作为第一个刺,但这并不完美:
>>> 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毫秒更新一次。当我们来分析数据时,问题就来了,因为按除时间戳以外的其他内容进行排序,然后再次按时间戳进行排序,可能会使数据以错误的顺序(按时间顺序)排列。最好使时间戳准确到任何对时间戳生成器的调用都提供唯一时间戳的程度。
我一直在考虑一些方法,这些方法涉及使用添加到起始datetime的time.clock()调用,但是希望能够在同一台计算机上的各个线程之间准确地工作的解决方案。任何建议将不胜感激。
解决方案
time.clock()仅在Windows上测量挂钟时间。在其他系统上,time.clock()实际上测量CPU时间。在那些系统上,time.time()更适合壁钟时间,并且它具有Python可以管理的高分辨率-这是OS可以管理的高分辨率;通常使用gettimeofday(3)(微秒分辨率)或者ftime(3)(毫秒分辨率)。其他OS限制实际上使实际分辨率比该分辨率高很多。 datetime.datetime.now()使用time.time(),因此time.time()直接不会更好。
作为记录,如果我在循环中使用datetime.datetime.now(),则看到的分辨率约为1/10000秒。通过查看数据,我们可以得到比这大得多的分辨率。我不确定Python是否可以做这样的事情,尽管我们可以说服操作系统通过其他方式做得更好。
我似乎记得在Windows上,time.clock()实际上(略)比time.time()更准确,但是自从首次调用time.clock()以来,它就测量挂钟,因此我们必须记住要"初始化" '首先。
这是有关Python计时精度的主题:
Python time.clock()与time.time()的准确性?
我们不太可能获得足够细粒度的控制,因此无法完全消除这种可能性
对于重复的时间戳,我们需要的分辨率要小于生成datetime对象所需的时间。我们可能还可以使用其他几种方法来处理它:
- 处理它。使时间戳保持不变,但要依靠python的排序稳定来处理重新排序问题。首先按时间戳排序,然后其他方式将保留时间戳排序-我们只需要注意始终每次都从时间戳排序列表开始,而不要对同一列表进行多种排序。
- 添加自己的价值以实现唯一性。例如。包括一个递增的整数值作为键的一部分,或者仅在时间戳不同时才添加此值。例如。
以下内容将保证唯一的时间戳记值:
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值。然后,我们可以绝对确定它们匹配,而不考虑时间差异。
如果我们希望相对顺序完全正确,那么记录器就可以按照接收顺序为每条消息分配一个序列号,这就足够了。
谢谢大家的贡献,他们都非常有用。 Brian的答案似乎与我最终的想法最接近(即处理它,但使用一种独特的标识符,请参见下文),因此我接受了他的答案。我设法将所有各种数据接收器合并到一个线程中,现在使用新的AccurrateTimeStamp类完成时间戳记。只要时间戳是使用时钟的第一件事,我所做的工作就可以正常工作。
正如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.Cage的最后这篇文章。
对于我的工作,跨流程和平台的事件的"合理"时序至关重要。显然有很多地方可以歪斜(时钟漂移,上下文切换等),但是,我认为,这种准确的计时解决方案将有助于确保记录的时间戳足够准确,可以查看其他错误来源。
就是说,我想知道其中有几个细节,这些细节在《微秒问题》中进行了解释。例如,我认为time.clock()最终会包装。我认为,要使它在一个长期运行的过程中起作用,我们可能必须处理该问题。