1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 package org.spf4j.base;
33
34 import com.google.common.annotations.Beta;
35 import edu.umd.cs.findbugs.annotations.CleanupObligation;
36 import edu.umd.cs.findbugs.annotations.DischargesObligation;
37 import java.util.ArrayList;
38 import java.util.Collection;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.concurrent.TimeUnit;
42 import java.util.concurrent.TimeoutException;
43 import java.util.function.BiFunction;
44 import java.util.function.Consumer;
45 import javax.annotation.Nonnegative;
46 import javax.annotation.Nonnull;
47 import javax.annotation.Nullable;
48 import javax.annotation.ParametersAreNonnullByDefault;
49 import javax.annotation.Signed;
50 import org.spf4j.base.avro.Converters;
51 import org.spf4j.base.avro.DebugDetail;
52 import org.spf4j.base.avro.StackSampleElement;
53 import org.spf4j.log.Level;
54 import org.spf4j.log.Slf4jLogRecord;
55
56
57
58
59
60
61
62
63
64
65 @CleanupObligation
66 @ParametersAreNonnullByDefault
67 public interface ExecutionContext extends AutoCloseable, JsonWriteable {
68
69 public interface SimpleTag<T> extends Tag<T, Void> {
70 }
71
72 public interface Tag<T, A> {
73
74 String toString();
75
76
77
78
79
80 default boolean isInherited(final Relation relation) {
81 return true;
82 }
83
84
85
86
87
88 default boolean pushOnClose() {
89 return false;
90 }
91
92
93 default T accumulate(@Nullable final T existing, final T newVal) {
94 return newVal;
95 }
96
97 default T accumulateComponent(@Nullable final T existing, final A component) {
98 throw new UnsupportedOperationException();
99 }
100
101 }
102
103 enum Relation {
104 CHILD_OF, FOLLOWS
105 }
106
107 @DischargesObligation
108 void close();
109
110 @Nonnull
111 String getName();
112
113 CharSequence getId();
114
115 long getStartTimeNanos();
116
117 long getDeadlineNanos();
118
119 @Nullable
120 ExecutionContext getSource();
121
122
123
124
125
126 default ExecutionContext getRoot() {
127 ExecutionContext curr = this;
128 ExecutionContext parent;
129 while ((parent = curr.getSource()) != null) {
130 curr = parent;
131 }
132 return curr;
133 }
134
135
136
137
138
139 default ExecutionContext getRootParent() {
140 ExecutionContext curr = this;
141 ExecutionContext parent;
142 while (curr.getRelationToSource() == Relation.CHILD_OF && (parent = curr.getSource()) != null) {
143 curr = parent;
144 }
145 return curr;
146 }
147
148
149
150
151 @Nullable
152 default ExecutionContext getNotClosedParent() {
153 ExecutionContext curr = this;
154 ExecutionContext parent;
155 do {
156 if (curr.getRelationToSource() != Relation.CHILD_OF) {
157 return null;
158 }
159 parent = curr.getSource();
160 if (parent == null || !parent.isClosed()) {
161 break;
162 }
163 curr = parent;
164 } while (true);
165 return parent;
166 }
167
168 @Beta
169 void addLog(Slf4jLogRecord log);
170
171 @Beta
172 void addLogs(Collection<Slf4jLogRecord> log);
173
174
175
176
177
178
179 @Beta
180 void addCloseable(AutoCloseable closeable);
181
182
183
184
185
186
187 @Beta
188 Level getContextMinLogLevel(String loggerName);
189
190 default Level getContextMinLogLevel() {
191 return getContextMinLogLevel("");
192 }
193
194
195
196
197
198
199 @Beta
200 @Nullable
201 Level getBackendMinLogLevel(String loggerName);
202
203 @Nullable
204 default Level getBackendMinLogLevel() {
205 return getBackendMinLogLevel("");
206 }
207
208 @Beta
209 @Nullable
210 Level setBackendMinLogLevel(String loggerName, Level level);
211
212 @Nullable
213 default Level setBackendMinLogLevel(Level level) {
214 return setBackendMinLogLevel("", level);
215 }
216
217 @Beta
218 void streamLogs(Consumer<Slf4jLogRecord> to);
219
220 @Beta
221 void streamLogs(Consumer<Slf4jLogRecord> to, int nrLogs);
222
223
224
225
226 void detach();
227
228
229
230
231
232 void attach();
233
234
235
236
237 boolean isAttached();
238
239 @Nonnegative
240 default long getTimeToDeadline(final TimeUnit unit) throws TimeoutException {
241 long result = getTimeRelativeToDeadline(unit);
242 if (result <= 0) {
243 throw new TimeoutException("Deadline exceeded by " + (-result) + ' ' + unit);
244 }
245 return result;
246 }
247
248 @Nonnegative
249 default long getUncheckedTimeToDeadline(final TimeUnit unit) {
250 long result = getTimeRelativeToDeadline(unit);
251 if (result <= 0) {
252 throw new UncheckedTimeoutException("Deadline exceeded by " + (-result) + ' ' + unit);
253 }
254 return result;
255 }
256
257 @Signed
258 default long getTimeRelativeToDeadline(final TimeUnit unit) {
259 return unit.convert(getDeadlineNanos() - TimeSource.nanoTime(), TimeUnit.NANOSECONDS);
260 }
261
262 @Nonnegative
263 default long getMillisToDeadline() throws TimeoutException {
264 return getTimeToDeadline(TimeUnit.MILLISECONDS);
265 }
266
267 @Nonnegative
268 default int getSecondsToDeadline() throws TimeoutException {
269 long secondsToDeadline = getTimeToDeadline(TimeUnit.SECONDS);
270 if (secondsToDeadline > Integer.MAX_VALUE) {
271 return Integer.MAX_VALUE;
272 } else {
273 return (int) secondsToDeadline;
274 }
275 }
276
277
278
279
280
281
282
283
284
285 @Nullable
286 @Beta
287 <T> T get(Tag<T, ?> key);
288
289
290
291
292
293
294
295
296
297
298 @Nullable
299 @Beta
300 <T> ContextValue<T> getContextAndValue(Tag<T, ?> key);
301
302
303
304
305
306
307
308
309 @Nullable
310 @Beta
311 <T> T getLocal(Tag<T, ?> key);
312
313
314
315
316
317
318
319
320
321 @Nullable
322 @Beta
323 <T> T put(Tag<T, ?> tag, T data);
324
325
326
327
328 @Deprecated
329 default <T> void combine(Tag<T, ?> tag, T data) {
330 accumulate(tag, data);
331 }
332
333 @Beta
334 default <T> void accumulate(Tag<T, ?> tag, T data) {
335 compute(tag, (t, v) -> t.accumulate(v, data));
336 }
337
338 @Beta
339 default <T, A> void accumulateComponent(Tag<T, A> tag, A data) {
340 compute(tag, (t, v) -> t.accumulateComponent(v, data));
341 }
342
343
344
345
346
347
348
349
350 @Nullable
351 @Beta
352 default <T> T putToRootParent(final Tag<T, ?> key, final T data) {
353 return getRootParent().put(key, data);
354 }
355
356
357
358
359
360
361
362
363
364 @Beta
365 @Nullable
366 <V, A> V compute(Tag<V, A> key, BiFunction<Tag<V, A>, V, V> compute);
367
368
369 @Nullable
370 @Beta
371 default <T> List<T> addToRootParent(final Tag<List<T>, T> tag, final T data) {
372 ExecutionContext ctx = getRootParent();
373 return ctx.compute(tag, (k, v) -> {
374 if (v == null) {
375 v = new ArrayList<>(2);
376 }
377 v.add(data);
378 return v;
379 });
380 }
381
382 default ExecutionContext startChild(final String operationName,
383 final long timeout, final TimeUnit tu) {
384 return ExecutionContexts.start(operationName, this, timeout, tu);
385 }
386
387 default ExecutionContext startChild(final String operationName) {
388 return ExecutionContexts.start(operationName, this);
389 }
390
391 default ExecutionContext detachedChild(final String operationName,
392 final long timeout, final TimeUnit tu) {
393 return ExecutionContexts.createDetached(operationName, this, timeout, tu);
394 }
395
396 default ExecutionContext detachedChild(final String operationName) {
397 return ExecutionContexts.createDetached(operationName, this, TimeSource.nanoTime(), this.getDeadlineNanos());
398 }
399
400 long nextChildId();
401
402 void add(StackTraceElement[] sample);
403
404 void add(StackSamples samples);
405
406 @Nullable
407 StackSamples getAndClearStackSamples();
408
409 @Nullable
410 StackSamples getStackSamples();
411
412
413 boolean isClosed();
414
415 Relation getRelationToSource();
416
417
418 default DebugDetail getDebugDetail(final String origin, @Nullable final Throwable throwable) {
419 return getDebugDetail(origin, throwable, true);
420 }
421
422 default DebugDetail getDebugDetail(final String origin, @Nullable final Throwable throwable,
423 boolean addStackSamples) {
424 return getDebugDetail(origin, throwable, addStackSamples, 100);
425 }
426
427 default DebugDetail getDebugDetail(final String origin, @Nullable final Throwable throwable,
428 boolean addStackSamples, final int maxNrLogs) {
429 List<Slf4jLogRecord> ctxLogs = new ArrayList<>();
430 ExecutionContext curr = this;
431 while (curr != null) {
432 curr.streamLogs((log) -> {
433 ctxLogs.add(log);
434 }, maxNrLogs);
435 curr = curr.getSource();
436 }
437 Collections.sort(ctxLogs, Slf4jLogRecord::compareByTimestamp);
438 if (addStackSamples) {
439 StackSamples ss = this.getAndClearStackSamples();
440 List<StackSampleElement> sses = Converters.convert(ss);
441 return new DebugDetail(origin + '/' + this.getName(),
442 Converters.convert("", this.getId().toString(), ctxLogs),
443 throwable == null ? null : Converters.convert(throwable),
444 sses);
445 } else {
446 return new DebugDetail(origin + '/' + this.getName(),
447 Converters.convert("", this.getId().toString(), ctxLogs),
448 throwable == null ? null : Converters.convert(throwable),
449 Collections.emptyList());
450 }
451
452 }
453
454 }