2008-10-01 74 views
12

最近我一直在构建一个错误日志记录应用程序,并且正在准确地对传入数据进行时间戳记。当我准确地表达时,我的意思是每个时间戳应该相对于彼此准确(不需要同步到原子钟或类似的东西)。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〜32ms更新一次。当我们分析数据时,问题出现了,因为按照时间戳以外的方式排序,然后再按时间戳排序可能会使数据按错误顺序排序(按时间顺序排列)。如果时间标记精确到对时间标记发生器的任何调用都给出唯一时间标记,那将是很好的做法。

我一直在考虑一些涉及到使用time.clock()调用添加到开始日期时间的方法,但希望能够在同一台机器上的线程间准确工作的解决方案。任何建议将非常感激地收到。

+0

我刚刚在* Windows *中发布了一个新答案,使用Python,您可以使用Windows QPC时钟获得亚微秒级* *分辨率*(不准确性)时间戳,正如我在代码中链接的代码回答。 – 2016-08-09 02:01:58

+0

为什么你要建立自己的日志框架?已经有很多了,时间戳是一个解决的问题(降低到一定的准确度)。万一你有一个没有现有日志框架解决的用例,你能选择最接近的用户并提出问题并提交你的代码吗? – smci 2017-07-10 15:17:52

+0

因为〜8.5年前(当我发布这个时),选项有些更有限。我没有构建错误日志框架,我正在写一些东西来接收UDP数据并从中记录信息。如果有一个可用的库(并且我找到了)可以做到这一点,我已经完全开放使用它;-) – 2017-07-13 15:43:44

回答

7

你是不可能得到充分细致的控制,可以完全消除的可能性重复时间戳的 - 你需要的分辨率较小比生成日期时间对象所花费的时间还要多。您可能需要采取几种其他方法来处理它:

  1. 处理它。让您的时间戳不是唯一的,但依赖于python的排序稳定处理重新排序的问题。首先在时间戳上排序,那么其他内容将保留时间戳排序 - 您必须小心始终始终从时间戳排序列表开始,而不是在同一列表上进行多种排序。

  2. 附加自己的值来强制唯一性。例如。包括一个递增的整数值作为键的一部分,或者仅在时间戳不同时附加这样的值。例如。

下面将保证独特的时间戳值:

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 

多进程(而不是线程),它变得有点棘手,但。

+1

我意识到这有点挑剔,但你的意思是“严格增加整数”而不是“单调增加整数”。单调增加的集合意味着它不会减少,但仍可能具有相同的值。 – 2008-11-12 16:12:32

12

time.clock()仅测量Windows上的挂钟时间。在其他系统上,time.clock()实际上会测量CPU时间。在这些系统上,time.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,所以您必须请记得先“初始化”它。

+0

的确,这是它在Debian/Linux上的外观: datetime .datetime(2008,10,1,17,11,31,875190) datetime.datetime(2008,10,1,17,11,31,875199) datetime.datetime(2008,10,1,17,11 ,31,875207) – bortzmeyer 2008-10-01 15:13:03

+0

我可以确认时钟确实在我尝试过的所有Windows机器上都更加准确。 – 2008-10-01 19:21:36

2

这是一个关于Python定时精度螺纹:

Python - time.clock() vs. time.time() - accuracy?

+0

是的,我已经看到了一个,但那些是相对于一个开始的过程或对时钟的调用而不是绝对(ish)时间。 – 2008-10-01 15:04:10

3

“时间戳应该是相对精确的”

为什么时间?为什么不是序列号?如果它是客户端 - 服务器应用程序的任何客户端,则网络延迟会使时间戳具有随机性。

你是否匹配一些外部信息来源?说另一个应用程序的日志?同样,如果有网络,那些时间不会太近。

如果您必须在不同的应用程序之间进行匹配,请考虑传递GUID以便两个应用程序都记录GUID值。那么你可以绝对确定他们匹配,不管时间差异。

如果您希望相对的顺序完全正确,那么您的记录器可能会按照收到的顺序为每封邮件分配一个序列号就足够了。

5

谢谢大家的贡献 - 他们都非常有用。 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) 
0

我想感谢J.Cage的最后一篇文章。

对于我的工作,跨过程和平台的“合理”事件时间安排至关重要。显然有很多地方可能发生歪斜事件(时钟漂移,上下文切换等),但我认为,这种精确的时序解决方案有助于确保记录的时间戳足够准确,以查看其他误差来源。

这就是说,有几个细节我想知道在When MicroSeconds Matter解释。例如,我认为time.clock()将最终包装。我认为这是为了长时间运行的过程,你可能需要处理它。

1

这个问题已经提出并回答了几年,至少对于Windows上的CPython已经处理了这个问题。使用下面两个Win7的64位和Windows Server 2008 R2中的脚本,我得到了相同的结果:

  • datetime.now()给出了1毫秒的分辨率和抖动小于1ms的
  • time.clock()给出了优于1us的和分辨率抖动远小于1ms的

脚本:

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)) 

结果可视化: enter image description here

0

如果你想microsecond-分辨率(不准确)在Python时间戳,在Windows中,您可以使用Windows的QPC计时器一样,在我的答案在这里证明:How to get millisecond and microsecond-resolution timestamps in Python。我不知道如何在Linux中做到这一点,所以如果有人知道,请在上面的链接中评论或回答。