1
- import datetime
2
1
import functools
3
2
import logging
4
3
import random
5
4
import re
6
- import threading
5
+ import time
7
6
import timeit
8
7
9
8
from elasticapm .conf import constants
28
27
29
28
30
29
class Transaction (object ):
31
- def __init__ (self , store , transaction_type = "custom" , trace_parent = None , is_sampled = True ):
30
+ def __init__ (self , tracer , transaction_type = "custom" , trace_parent = None , is_sampled = True ):
32
31
self .id = "%016x" % random .getrandbits (64 )
33
32
self .trace_parent = trace_parent
34
- self .timestamp = datetime .datetime .utcnow ()
35
- self .start_time = _time_func ()
33
+ self .timestamp , self .start_time = time .time (), _time_func ()
36
34
self .name = None
37
35
self .duration = None
38
36
self .result = None
39
37
self .transaction_type = transaction_type
40
- self ._store = store
38
+ self ._tracer = tracer
41
39
42
40
self .spans = []
43
41
self .dropped_spans = 0
@@ -47,22 +45,20 @@ def __init__(self, store, transaction_type="custom", trace_parent=None, is_sampl
47
45
self .is_sampled = is_sampled
48
46
self ._span_counter = 0
49
47
50
- def end_transaction (self , skip_frames = 8 ):
48
+ def end_transaction (self ):
51
49
self .duration = _time_func () - self .start_time
52
50
53
51
def begin_span (self , name , span_type , context = None , leaf = False ):
54
52
parent_span = get_span ()
55
- store = self ._store
53
+ store = self ._tracer
56
54
if parent_span and parent_span .leaf :
57
55
span = DroppedSpan (parent_span , leaf = True )
58
56
elif store .max_spans and self ._span_counter > store .max_spans - 1 :
59
57
self .dropped_spans += 1
60
58
span = DroppedSpan (parent_span )
61
59
self ._span_counter += 1
62
60
else :
63
- start = _time_func () - self .start_time
64
- span_id = "%016x" % random .getrandbits (64 ) if self .trace_parent else self ._span_counter - 1
65
- span = Span (span_id , self .id , self .trace_parent .trace_id , name , span_type , start , context , leaf = leaf )
61
+ span = Span (transaction = self , name = name , span_type = span_type , context = context , leaf = leaf )
66
62
span .frames = store .frames_collector_func ()
67
63
span .parent = parent_span
68
64
self ._span_counter += 1
@@ -77,15 +73,15 @@ def end_span(self, skip_frames):
77
73
set_span (span .parent )
78
74
return
79
75
80
- span .duration = _time_func () - span .start_time - self . start_time
76
+ span .duration = _time_func () - span .start_time
81
77
82
- if not self ._store .span_frames_min_duration or span .duration >= self ._store .span_frames_min_duration :
83
- span .frames = self ._store .frames_processing_func (span .frames )[skip_frames :]
78
+ if not self ._tracer .span_frames_min_duration or span .duration >= self ._tracer .span_frames_min_duration :
79
+ span .frames = self ._tracer .frames_processing_func (span .frames )[skip_frames :]
84
80
else :
85
81
span .frames = None
86
82
self .spans .append (span )
87
83
set_span (span .parent )
88
- self ._store .queue_func (SPAN , span .to_dict ())
84
+ self ._tracer .queue_func (SPAN , span .to_dict ())
89
85
return span
90
86
91
87
def to_dict (self ):
@@ -97,7 +93,7 @@ def to_dict(self):
97
93
"type" : encoding .keyword_field (self .transaction_type ),
98
94
"duration" : self .duration * 1000 , # milliseconds
99
95
"result" : encoding .keyword_field (str (self .result )),
100
- "timestamp" : self .timestamp . strftime ( constants . TIMESTAMP_FORMAT ),
96
+ "timestamp" : int ( self .timestamp * 1000000 ), # microseconds
101
97
"sampled" : self .is_sampled ,
102
98
"span_count" : {"started" : self ._span_counter - self .dropped_spans , "dropped" : self .dropped_spans },
103
99
}
@@ -112,53 +108,54 @@ def to_dict(self):
112
108
113
109
class Span (object ):
114
110
__slots__ = (
115
- "idx" ,
116
- "transaction_id" ,
117
- "trace_id" ,
111
+ "id" ,
112
+ "transaction" ,
118
113
"name" ,
119
114
"type" ,
120
115
"context" ,
121
116
"leaf" ,
117
+ "timestamp" ,
122
118
"start_time" ,
123
119
"duration" ,
124
120
"parent" ,
125
121
"frames" ,
126
122
)
127
123
128
- def __init__ (self , idx , transaction_id , trace_id , name , span_type , start_time , context = None , leaf = False ):
124
+ def __init__ (self , transaction , name , span_type , context = None , leaf = False ):
129
125
"""
130
126
Create a new Span
131
127
132
- :param idx: Index of this span
128
+ :param transaction: transaction object that this span relates to
133
129
:param name: Generic name of the span
134
130
:param span_type: type of the span
135
- :param start_time: start time relative to the transaction
136
131
:param context: context dictionary
137
- :param leaf: is this transaction a leaf transaction ?
132
+ :param leaf: is this span a leaf span ?
138
133
"""
139
- self .idx = idx
140
- self .transaction_id = transaction_id
141
- self .trace_id = trace_id
134
+ self .start_time = _time_func ()
135
+ self .id = "%016x" % random . getrandbits ( 64 )
136
+ self .transaction = transaction
142
137
self .name = name
143
138
self .type = span_type
144
139
self .context = context
145
140
self .leaf = leaf
146
- self .start_time = start_time
141
+ # timestamp is bit of a mix of monotonic and non-monotonic time sources.
142
+ # we take the (non-monotonic) transaction timestamp, and add the (monotonic) difference of span
143
+ # start time and transaction start time. In this respect, the span timestamp is guaranteed to grow
144
+ # monotonically with respect to the transaction timestamp
145
+ self .timestamp = transaction .timestamp + (self .start_time - transaction .start_time )
147
146
self .duration = None
148
147
self .parent = None
149
148
self .frames = None
150
- self .trace_id = trace_id
151
- self .transaction_id = transaction_id
152
149
153
150
def to_dict (self ):
154
151
result = {
155
- "id" : self .idx ,
156
- "transaction_id" : self .transaction_id ,
157
- "trace_id" : self .trace_id ,
158
- "parent_id" : self .parent .idx if self .parent else self .transaction_id ,
152
+ "id" : self .id ,
153
+ "transaction_id" : self .transaction . id ,
154
+ "trace_id" : self .transaction . trace_parent . trace_id ,
155
+ "parent_id" : self .parent .id if self .parent else self .transaction . id ,
159
156
"name" : encoding .keyword_field (self .name ),
160
157
"type" : encoding .keyword_field (self .type ),
161
- "start " : self .start_time * 1000 , # milliseconds
158
+ "timestamp " : int ( self .timestamp * 1000000 ) , # microseconds
162
159
"duration" : self .duration * 1000 , # milliseconds
163
160
"context" : self .context ,
164
161
}
@@ -175,7 +172,7 @@ def __init__(self, parent, leaf=False):
175
172
self .leaf = leaf
176
173
177
174
178
- class TransactionsStore (object ):
175
+ class Tracer (object ):
179
176
def __init__ (
180
177
self ,
181
178
frames_collector_func ,
@@ -186,13 +183,10 @@ def __init__(
186
183
span_frames_min_duration = None ,
187
184
ignore_patterns = None ,
188
185
):
189
- self .cond = threading .Condition ()
190
186
self .max_spans = max_spans
191
187
self .queue_func = queue_func
192
188
self .frames_processing_func = frames_processing_func
193
189
self .frames_collector_func = frames_collector_func
194
- self ._transactions = []
195
- self ._last_collect = _time_func ()
196
190
self ._ignore_patterns = [re .compile (p ) for p in ignore_patterns or []]
197
191
self ._sample_rate = sample_rate
198
192
if span_frames_min_duration in (- 1 , None ):
@@ -201,24 +195,6 @@ def __init__(
201
195
else :
202
196
self .span_frames_min_duration = span_frames_min_duration / 1000.0
203
197
204
- def add_transaction (self , transaction ):
205
- with self .cond :
206
- self ._transactions .append (transaction )
207
- self .cond .notify ()
208
-
209
- def get_all (self , blocking = False ):
210
- with self .cond :
211
- # If blocking is true, always return at least 1 item
212
- while blocking and len (self ._transactions ) == 0 :
213
- self .cond .wait ()
214
- transactions , self ._transactions = self ._transactions , []
215
- self ._last_collect = _time_func ()
216
- return transactions
217
-
218
- def __len__ (self ):
219
- with self .cond :
220
- return len (self ._transactions )
221
-
222
198
def begin_transaction (self , transaction_type , trace_parent = None ):
223
199
"""
224
200
Start a new transactions and bind it in a thread-local variable
0 commit comments