11
11
* the Apache License, Version 2.0 (the "License"); you may
12
12
* not use this file except in compliance with the License.
13
13
* You may obtain a copy of the License at
14
- *
14
+ *
15
15
* http://www.apache.org/licenses/LICENSE-2.0
16
- *
16
+ *
17
17
* Unless required by applicable law or agreed to in writing,
18
18
* software distributed under the License is distributed on an
19
19
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
26
26
27
27
import co .elastic .apm .agent .impl .ElasticApmTracer ;
28
28
import co .elastic .apm .agent .impl .context .AbstractContext ;
29
+ import co .elastic .apm .agent .matcher .WildcardMatcher ;
30
+ import co .elastic .apm .agent .objectpool .Recyclable ;
31
+ import co .elastic .apm .agent .report .ReporterConfiguration ;
29
32
import org .slf4j .Logger ;
30
33
import org .slf4j .LoggerFactory ;
31
34
32
35
import javax .annotation .Nullable ;
33
36
import java .util .concurrent .Callable ;
34
37
import java .util .concurrent .TimeUnit ;
35
38
import java .util .concurrent .atomic .AtomicInteger ;
39
+ import java .util .concurrent .atomic .AtomicLong ;
36
40
37
41
public abstract class AbstractSpan <T extends AbstractSpan > extends TraceContextHolder <T > {
38
42
private static final Logger logger = LoggerFactory .getLogger (AbstractSpan .class );
@@ -43,37 +47,98 @@ public abstract class AbstractSpan<T extends AbstractSpan> extends TraceContextH
43
47
* Generic designation of a transaction in the scope of a single service (eg: 'GET /users/:id')
44
48
*/
45
49
protected final StringBuilder name = new StringBuilder ();
50
+ protected final boolean collectBreakdownMetrics ;
46
51
private long timestamp ;
47
52
48
- /**
49
- * How long the transaction took to complete, in ms with 3 decimal points
50
- * (Required)
51
- */
52
- protected double duration ;
53
+ // in microseconds
54
+ protected long duration ;
55
+ private ChildDurationTimer childDurations = new ChildDurationTimer ();
53
56
protected AtomicInteger references = new AtomicInteger ();
54
57
protected volatile boolean finished = true ;
55
58
56
59
public int getReferenceCount () {
57
60
return references .get ();
58
61
}
59
62
63
+ private static class ChildDurationTimer implements Recyclable {
64
+
65
+ private AtomicInteger activeChildren = new AtomicInteger ();
66
+ private AtomicLong start = new AtomicLong ();
67
+ private AtomicLong duration = new AtomicLong ();
68
+
69
+ /**
70
+ * Starts the timer if it has not been started already.
71
+ *
72
+ * @param startTimestamp
73
+ */
74
+ void onChildStart (long startTimestamp ) {
75
+ if (activeChildren .incrementAndGet () == 1 ) {
76
+ start .set (startTimestamp );
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Stops the timer and increments the duration if no other direct children are still running
82
+ * @param endTimestamp
83
+ */
84
+ void onChildEnd (long endTimestamp ) {
85
+ if (activeChildren .decrementAndGet () == 0 ) {
86
+ incrementDuration (endTimestamp );
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Stops the timer and increments the duration even if there are direct children which are still running
92
+ *
93
+ * @param endTimestamp
94
+ */
95
+ void onSpanEnd (long endTimestamp ) {
96
+ if (activeChildren .getAndSet (0 ) != 0 ) {
97
+ incrementDuration (endTimestamp );
98
+ }
99
+ }
100
+
101
+ private void incrementDuration (long epochMicros ) {
102
+ duration .addAndGet (epochMicros - start .get ());
103
+ }
104
+
105
+ @ Override
106
+ public void resetState () {
107
+ activeChildren .set (0 );
108
+ start .set (0 );
109
+ duration .set (0 );
110
+ }
111
+
112
+ public long getDuration () {
113
+ return duration .get ();
114
+ }
115
+ }
116
+
60
117
public AbstractSpan (ElasticApmTracer tracer ) {
61
118
super (tracer );
62
119
traceContext = TraceContext .with64BitId (this .tracer );
120
+ collectBreakdownMetrics = !WildcardMatcher .isAnyMatch (tracer .getConfig (ReporterConfiguration .class ).getDisableMetrics (), "span.self_time" );
63
121
}
64
122
65
123
public boolean isReferenced () {
66
124
return references .get () > 0 ;
67
125
}
68
126
69
127
/**
70
- * How long the transaction took to complete, in ms with 3 decimal points
71
- * (Required)
128
+ * How long the transaction took to complete, in µs
72
129
*/
73
- public double getDuration () {
130
+ public long getDuration () {
74
131
return duration ;
75
132
}
76
133
134
+ public long getSelfDuration () {
135
+ return duration - childDurations .getDuration ();
136
+ }
137
+
138
+ public double getDurationMs () {
139
+ return duration / AbstractSpan .MS_IN_MICROS ;
140
+ }
141
+
77
142
/**
78
143
* Generic designation of a transaction in the scope of a single service (eg: 'GET /users/:id')
79
144
*/
@@ -124,6 +189,7 @@ public void resetState() {
124
189
timestamp = 0 ;
125
190
duration = 0 ;
126
191
traceContext .resetState ();
192
+ childDurations .resetState ();
127
193
references .set (0 );
128
194
}
129
195
@@ -177,20 +243,23 @@ public void end() {
177
243
178
244
public final void end (long epochMicros ) {
179
245
if (!finished ) {
180
- this .duration = (epochMicros - timestamp ) / AbstractSpan . MS_IN_MICROS ;
246
+ this .duration = (epochMicros - timestamp );
181
247
if (name .length () == 0 ) {
182
248
name .append ("unnamed" );
183
249
}
184
- doEnd (epochMicros );
185
- // has to be set last so doEnd callbacks don't think it has already been finished
250
+ childDurations . onSpanEnd (epochMicros );
251
+ beforeEnd ( epochMicros );
186
252
this .finished = true ;
253
+ afterEnd ();
187
254
} else {
188
255
logger .warn ("End has already been called: {}" , this );
189
256
assert false ;
190
257
}
191
258
}
192
259
193
- protected abstract void doEnd (long epochMicros );
260
+ protected abstract void beforeEnd (long epochMicros );
261
+
262
+ protected abstract void afterEnd ();
194
263
195
264
@ Override
196
265
public boolean isChildOf (TraceContextHolder other ) {
@@ -242,6 +311,18 @@ public void setStartTimestamp(long epochMicros) {
242
311
timestamp = epochMicros ;
243
312
}
244
313
314
+ void onChildStart (long epochMicros ) {
315
+ if (collectBreakdownMetrics ) {
316
+ childDurations .onChildStart (epochMicros );
317
+ }
318
+ }
319
+
320
+ void onChildEnd (long epochMicros ) {
321
+ if (collectBreakdownMetrics ) {
322
+ childDurations .onChildEnd (epochMicros );
323
+ }
324
+ }
325
+
245
326
public void incrementReferences () {
246
327
references .incrementAndGet ();
247
328
if (logger .isDebugEnabled ()) {
0 commit comments