Python日志中的准确时间戳

12 投票
8 回答
10460 浏览
提问于 2025-04-11 09:21

最近我在开发一个错误日志记录的应用程序,想要找到一种方法来准确地给接收到的数据加上时间戳。这里的“准确”是指每个时间戳之间的关系要准确(不需要和原子钟之类的东西同步)。

我最开始使用的是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毫秒才更新一次。问题在于,当我们分析数据时,如果先按其他东西排序,再按时间戳排序,可能会导致数据的顺序(时间上)错乱。如果时间戳能做到每次调用时间戳生成器都能得到一个独特的时间戳,那就太好了。

我曾考虑过一些方法,比如用time.clock()加上一个起始的datetime,但我希望能找到一种在同一台机器上跨线程都能准确工作的解决方案。如果有任何建议,我会非常感激。

8 个回答

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

在Windows系统上,time.clock()只测量实际经过的时间。而在其他系统上,time.clock()测量的是CPU的使用时间。在这些系统中,time.time()更适合用来测量实际经过的时间,它的精度是Python能做到的最高水平,通常是操作系统能支持的最高精度;一般是通过gettimeofday(3)(微秒级精度)或ftime(3)(毫秒级精度)来实现的。实际上,其他操作系统的限制让真实的精度比这更高。

顺便说一下,如果我在循环中使用datetime.datetime.now(),我能看到大约1/10000秒的精度。而从你的数据来看,你的精度要粗糙得多。我不确定Python能做什么,不过你可能可以通过其他方式让操作系统表现得更好。

我记得在Windows上,time.clock()的精度(稍微)比time.time()更高,但它测量的是从第一次调用time.clock()开始的实际经过时间,所以你得记得先“初始化”它。

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

不过对于多个进程(而不是线程)来说,这就有点复杂了。

撰写回答