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.
サンプルの最初の1秒のクロック間の変更は次のようになります。
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()呼び出しを使用する方法を検討していましたが、同じマシンのスレッド間で正確に動作するソリューションを評価したいと思います。どんな提案も非常に感謝されます。
解決
可能性を完全に排除できるほど十分にきめの細かい制御を得る可能性は低い タイムスタンプの重複-日時オブジェクトを生成するのにかかる時間よりも小さい解像度が必要です。あなたはそれに対処するために取るかもしれない他のいくつかのアプローチがあります:
-
それに対処します。タイムスタンプはそのままにしておきますが、並べ替えの問題に対処するには、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
(スレッドではなく)複数のプロセスの場合、少し複雑になります。
他のヒント
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秒の解像度が表示されます。データを見ると、それよりもはるかに粗い解像度が得られます。 OSが他の方法でより良い結果を出すように説得できるかもしれませんが、Pythonでできることは何かはわかりません。
Windowsでは、time.clock()は実際にはtime.time()よりも(わずかに)正確ですが、time.clock()を最初に呼び出してからwallclockを測定することを思い出すようです。最初に「初期化」します。
あなたの貢献に感謝します-それらはすべて非常に有用です。ブライアンの答えは、最終的に私が行ったものに最も近いようです(つまり、それに対処しますが、一意の識別子の種類を使用します-以下を参照)ので、私は彼の答えを受け入れました。新しい AccurrateTimeStamp クラスを使用してタイムスタンプが行われる単一のスレッドに、さまざまなデータレシーバーをすべて統合することができました。タイムスタンプが時計を使用する最初のものである限り、私がやったことは機能します。
S.Lottが規定しているように、リアルタイムOSがなければ、完全に完璧になることはありません。物事が受信されたときに、入ってくるデータの各チャンクに関連して見えるものだけが本当に欲しかったので、下にあるものがうまく機能します。
みんなありがとう!
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)
"タイムスタンプは相互に正確である必要があります"
なぜですか?なぜシーケンス番号ではないのですか?クライアントサーバーアプリケーションのクライアントである場合、ネットワーク遅延によりタイムスタンプがランダムになります。
外部の情報源と一致していますか?別のアプリケーションでログを作成しますか?繰り返しますが、ネットワークがあれば、それらの時間は近づきすぎません。
別々のアプリ間で物事を一致させる必要がある場合は、両方のアプリがGUID値を記録するようにGUIDを渡すことを検討してください。そうすれば、タイミングの違いに関係なく、それらが確実に一致することを確実にすることができます。
相対の順序を正確にしたい場合は、ロガーが受信した順序で各メッセージにシーケンス番号を割り当てるだけで十分かもしれません。
Pythonのタイミング精度に関するスレッドです:
Python-time.clock()vs. time.time()-精度?
少なくともWindows上のCPythonについては、質問が尋ねられてから数年が経ち、これは対処されました。 Win7 64ビットとWindows Server 2008 R2の両方で以下のスクリプトを使用すると、同じ結果が得られました。
-
datetime.now()
は、1msの分解能と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))
視覚化された結果:
この最後の投稿についてJ. Cageに感謝したかった。
私の仕事では、「合理的な」プロセスおよびプラットフォーム全体のイベントのタイミングが不可欠です。物事が傾く可能性のある場所(クロックドリフト、コンテキストスイッチングなど)は明らかにありますが、この正確なタイミングソリューションは、記録されたタイムスタンプが他のエラーの原因を確認するのに十分正確であることを保証するのに役立つと思います。
とはいえ、マイクロ秒が重要な場合。たとえば、time.clock()は最終的にラップすると思います。これが長時間実行されるプロセスで機能するには、それを処理する必要があるかもしれません。
Pythonでマイクロ秒-解像度(精度ではない)タイムスタンプが必要な場合、 Windows では、私の回答で示されているように、WindowsのQPCタイマーを使用できます: Pythonでミリ秒とマイクロ秒の解像度のタイムスタンプを取得する方法。 Linuxでこれを行う方法はまだわかりません。だれかが知っている場合は、上のリンクでコメントまたは回答してください。