Skip to content

Commit ac0f569

Browse files
committed
add watchdog timer
1 parent df3defa commit ac0f569

File tree

1 file changed

+46
-3
lines changed

1 file changed

+46
-3
lines changed

semantickernel-api/src/main/java/com/microsoft/semantickernel/implementation/telemetry/SemanticKernelTelemetrySpan.java

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,59 @@
66
import io.opentelemetry.context.Scope;
77
import io.opentelemetry.instrumentation.reactor.v3_1.ContextPropagationOperator;
88
import java.io.Closeable;
9+
import java.time.Duration;
910
import java.util.concurrent.atomic.AtomicBoolean;
1011
import java.util.function.Function;
1112
import org.slf4j.Logger;
13+
import reactor.core.Disposable;
14+
import reactor.core.publisher.Mono;
1215
import reactor.util.context.ContextView;
1316

1417
public abstract class SemanticKernelTelemetrySpan implements Closeable {
1518

1619
private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(
1720
SemanticKernelTelemetrySpan.class);
1821

22+
private static final long SPAN_TIMEOUT_MS = Long.parseLong((String) System.getProperties()
23+
.getOrDefault("semantickernel.telemetry.span_timeout", "120000"));
24+
1925
private final Span span;
2026
private final Function<reactor.util.context.Context, reactor.util.context.Context> reactorContextModifier;
2127
private final Scope spanScope;
2228
private final Scope contextScope;
2329
private final AtomicBoolean closed = new AtomicBoolean(false);
2430

31+
// Timeout to close the span if it was not closed within the specified time to avoid memory leaks
32+
private final Disposable watchdog;
33+
34+
// This is a finalizer guardian to ensure that the span is closed if it was not closed explicitly
35+
@SuppressWarnings("unused")
36+
private final Object finalizerGuardian = new Object() {
37+
@Override
38+
protected void finalize() {
39+
if (closed.get() == false) {
40+
LOGGER.warn("Span was not closed");
41+
close();
42+
}
43+
}
44+
};
45+
2546
public SemanticKernelTelemetrySpan(Span span,
2647
Function<reactor.util.context.Context, reactor.util.context.Context> reactorContextModifier,
2748
Scope spanScope, Scope contextScope) {
2849
this.span = span;
2950
this.reactorContextModifier = reactorContextModifier;
3051
this.spanScope = spanScope;
3152
this.contextScope = contextScope;
53+
54+
watchdog = Mono.just(1)
55+
.delay(Duration.ofMillis(SPAN_TIMEOUT_MS))
56+
.subscribe(i -> {
57+
if (closed.get() == false) {
58+
LOGGER.warn("Span was not closed, timing out");
59+
close();
60+
}
61+
});
3262
}
3363

3464
public interface SpanConstructor<T extends SemanticKernelTelemetrySpan> {
@@ -71,14 +101,27 @@ public void close() {
71101
if (closed.compareAndSet(false, true)) {
72102
LOGGER.trace("Closing span: {}", span);
73103
if (span.isRecording()) {
74-
span.end();
104+
try {
105+
span.end();
106+
} catch (Exception e) {
107+
LOGGER.error("Error closing span", e);
108+
}
75109
}
76110
if (contextScope != null) {
77-
contextScope.close();
111+
try {
112+
contextScope.close();
113+
} catch (Exception e) {
114+
LOGGER.error("Error closing context scope", e);
115+
}
78116
}
79117
if (spanScope != null) {
80-
spanScope.close();
118+
try {
119+
spanScope.close();
120+
} catch (Exception e) {
121+
LOGGER.error("Error closing span scope", e);
122+
}
81123
}
124+
watchdog.dispose();
82125
}
83126
}
84127

0 commit comments

Comments
 (0)