Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1ecaa96
Add Azure Durable Functions worker instrumentation
jcstorms1 Sep 23, 2026
f6cb9cf
Add Azure Functions worker code owner
jcstorms1 Sep 23, 2026
376c24d
Convert Azure Functions worker tests to JUnit
jcstorms1 Sep 23, 2026
12dec94
Fix Durable Functions replay edge cases
jcstorms1 Sep 23, 2026
8cd74a3
Fix Azure Functions replay error handling
jcstorms1 Sep 23, 2026
e159a3d
Address Azure Functions review feedback
jcstorms1 Sep 23, 2026
ab2c863
Sort Azure Functions helper classes
jcstorms1 Sep 23, 2026
868f14c
Scope Azure sampling workaround to durable functions
jcstorms1 Sep 24, 2026
675b1ca
Align Azure worker test fixture with SPI
jcstorms1 Sep 24, 2026
96637fd
Harden durable function replay tracing
jcstorms1 Sep 24, 2026
39331a5
Merge remote-tracking branch 'origin/master' into storms/azure-durabl…
jcstorms1 Sep 29, 2026
c56b3ed
Remove Azure Functions sampling override
jcstorms1 Sep 29, 2026
9b6fc61
Avoid rescanning Azure Durable orchestration history
jcstorms1 Sep 29, 2026
b706092
Run Azure worker v0 tests with latest dependencies
jcstorms1 Sep 30, 2026
05516ca
Avoid activating scope for Azure replay error span
jcstorms1 Sep 30, 2026
06e2965
Scope Azure durable orchestration tracing to worker invocations
jcstorms1 Sep 30, 2026
5bd0b58
Share Azure durable orchestration state across classloaders
jcstorms1 Sep 30, 2026
374e866
Harden Azure Durable Functions tracing and classloader isolation
jcstorms1 Oct 1, 2026
fb59635
Simplify Azure Durable Functions muzzle and error span finish
jcstorms1 Oct 2, 2026
7fc25cc
Merge branch 'master' into storms/azure-durable-fx-support
jcstorms1 Oct 9, 2026
8186859
Merge branch 'master' into storms/azure-durable-fx-support
jcstorms1 Oct 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,15 @@
/utils/flare-utils/ @DataDog/apm-sdk-capabilities

# @DataDog/apm-serverless
/dd-trace-core/src/*/*/datadog/trace/lambda/ @DataDog/apm-serverless
/utils/container-utils/ @DataDog/apm-serverless
**/InferredProxy* @DataDog/apm-serverless
/dd-java-agent/instrumentation/**/*InferredProxy* @DataDog/apm-serverless
/dd-java-agent/instrumentation/aws-java/ @DataDog/apm-serverless
/dd-java-agent/instrumentation/aws-java/aws-java-lambda-handler-1.2/ @DataDog/apm-serverless
/dd-java-agent/instrumentation/azure-functions/ @DataDog/apm-serverless
/dd-java-agent/instrumentation/azure-functions-1.2.2/ @DataDog/apm-serverless
/dd-trace-core/src/*/*/datadog/trace/lambda/ @DataDog/apm-serverless
/utils/container-utils/ @DataDog/apm-serverless
**/InferredProxy* @DataDog/apm-serverless
/dd-java-agent/instrumentation/**/*InferredProxy* @DataDog/apm-serverless
/dd-java-agent/instrumentation/aws-java/ @DataDog/apm-serverless
/dd-java-agent/instrumentation/aws-java/aws-java-lambda-handler-1.2/ @DataDog/apm-serverless
/dd-java-agent/instrumentation/azure-functions/ @DataDog/apm-serverless
/dd-java-agent/instrumentation/azure-functions-1.2.2/ @DataDog/apm-serverless
/dd-java-agent/instrumentation/azure-functions-worker-2.7/ @DataDog/apm-serverless

# @DataDog/apm-lang-platform-java
/.agents/skills/fix-continuation-leakage/ @DataDog/apm-lang-platform-java
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package datadog.trace.bootstrap.instrumentation.azure;

import datadog.context.Context;
import datadog.context.ContextKey;
import datadog.context.ContextScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

/** Shares an Azure Durable orchestration invocation and its span across worker classloaders. */
Comment thread
mcculls marked this conversation as resolved.
public final class DurableOrchestrationState {
private static final ContextKey<DurableOrchestrationState> KEY =
ContextKey.named("azure-durable-orchestration");

private final AgentSpan parentSpan;
private final String functionName;
private final AtomicReference<AgentSpan> span = new AtomicReference<>();
private final AtomicBoolean errorRecorded = new AtomicBoolean();
Comment thread
jcstorms1 marked this conversation as resolved.

private DurableOrchestrationState(AgentSpan parentSpan, String functionName) {
this.parentSpan = parentSpan;
this.functionName = functionName;
}

public static ContextScope activate(AgentSpan remoteSpan, String functionName) {
Context context = Context.current();
final AgentSpan parentSpan = remoteSpan != null && remoteSpan.isValid() ? remoteSpan : null;
if (parentSpan != null) {
context = context.with(parentSpan);
}
return context.with(KEY, new DurableOrchestrationState(parentSpan, functionName)).attach();
Comment thread
mcculls marked this conversation as resolved.
}

public static DurableOrchestrationState current() {
return Context.current().get(KEY);
}

public AgentSpan span() {
return span.get();
}

public AgentSpan parentSpan() {
return parentSpan;
}

public String functionName() {
return functionName;
}

public boolean errorRecorded() {
return errorRecorded.get();
}

public void markErrorRecorded() {
errorRecorded.set(true);
}

public AgentSpan setSpan(AgentSpan span) {
return this.span.getAndSet(span);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package datadog.trace.bootstrap.instrumentation.azure;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import datadog.context.Context;
import datadog.context.ContextScope;
import datadog.trace.api.DDTraceId;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext;
import org.junit.jupiter.api.Test;

class DurableOrchestrationStateTest {

@Test
void activatesRemoteParentAndRestoresPreviousContext() {
AgentSpanContext spanContext = mock(AgentSpanContext.class);
when(spanContext.getTraceId()).thenReturn(DDTraceId.from(42));
when(spanContext.getSpanId()).thenReturn(43L);
AgentSpan parent = AgentSpan.fromSpanContext(spanContext);
AgentSpan orchestration = mock(AgentSpan.class);

assertNull(DurableOrchestrationState.current());
try (ContextScope scope = DurableOrchestrationState.activate(parent, "Orchestrator")) {
DurableOrchestrationState state = DurableOrchestrationState.current();
assertSame(parent, state.parentSpan());
assertSame("Orchestrator", state.functionName());
assertSame(parent, AgentSpan.fromContext(Context.current()));
assertFalse(state.errorRecorded());
state.markErrorRecorded();
assertTrue(state.errorRecorded());
assertNull(state.span());
assertNull(state.setSpan(orchestration));
assertSame(orchestration, state.span());
assertSame(orchestration, state.setSpan(null));
assertNull(state.span());
}
assertNull(DurableOrchestrationState.current());
}

@Test
void ignoresMissingAndInvalidRemoteParents() {
AgentSpanContext invalidContext = mock(AgentSpanContext.class);
when(invalidContext.getTraceId()).thenReturn(DDTraceId.ZERO);
when(invalidContext.getSpanId()).thenReturn(0L);
AgentSpan invalidParent = AgentSpan.fromSpanContext(invalidContext);
assertFalse(invalidParent.isValid());
try (ContextScope scope = DurableOrchestrationState.activate(null, null)) {
assertNull(DurableOrchestrationState.current().parentSpan());
}
try (ContextScope scope = DurableOrchestrationState.activate(invalidParent, null)) {
assertNull(DurableOrchestrationState.current().parentSpan());
assertNull(AgentSpan.fromContext(Context.current()));
}
assertNull(DurableOrchestrationState.current());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Azure Durable Functions Java tracing

For correlated host and Java worker traces, enable OpenTelemetry in the Function App's
`host.json`:

```json
{
"version": "2.0",
"telemetryMode": "OpenTelemetry"
}
```

The instrumentation honors the incoming W3C `traceparent` sampled flag and Datadog
`tracestate` sampling decision. It does not force-keep Durable invocations. If the host or an
upstream service sends a drop decision, the corresponding Durable spans may not be ingested.
Keep the application's sampling configuration consistent across the HTTP starter and Durable
worker invocations; enabling OpenTelemetry does not override an intentional upstream drop.

See the [Azure Functions OpenTelemetry setup guide](https://learn.microsoft.com/en-us/azure/azure-functions/opentelemetry-howto)
for host configuration details.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import groovy.lang.Closure

plugins {
id("dd-trace-java.module.instrumentation")
}

muzzle {
// FunctionExecutionMiddleware lives in the unpublished Azure Functions Java worker.
// The SPI pass validates its public middleware contract, but cannot check the worker's
// internal class name or invoke signature. The test stub exercises the advice behavior;
// worker compatibility also requires validation against a running Function App.
pass {
group = "com.microsoft.azure.functions"
module = "azure-functions-java-spi"
Comment thread
jcstorms1 marked this conversation as resolved.
versions = "[1.0.0,)"
Comment thread
jcstorms1 marked this conversation as resolved.
extraDependency("com.microsoft.azure.functions:azure-functions-java-core-library:1.2.0")
extraDependency("com.microsoft:durabletask-azure-functions:1.0.1")
extraDependency("com.microsoft:durabletask-client:1.0.0")
}
pass {
group = "com.microsoft"
module = "durabletask-client"
versions = "[1.0.0,)"
extraDependency("com.microsoft.azure.functions:azure-functions-java-core-library:1.2.0")
extraDependency("com.microsoft.azure.functions:azure-functions-java-spi:1.0.0")
extraDependency("com.microsoft:durabletask-azure-functions:1.0.1")
}
pass {
group = "com.microsoft"
module = "durabletask-azure-functions"
versions = "[1.0.1,)"
extraDependency("com.microsoft.azure.functions:azure-functions-java-core-library:1.2.0")
extraDependency("com.microsoft.azure.functions:azure-functions-java-spi:1.0.0")
extraDependency("com.microsoft:durabletask-client:1.0.0")
}
}

fun addTestSuiteForDir(name: String, directory: String) {
(project.extra["addTestSuiteForDir"] as Closure<*>).call(name, directory)
}

addTestSuiteForDir("latestDepTest", "test")
Comment thread
jcstorms1 marked this conversation as resolved.
(project.extra["addForkedTestTask"] as Closure<*>).call("latestDepTest")

dependencies {
compileOnly("com.google.protobuf:protobuf-java:3.19.2")
compileOnly("com.microsoft:durabletask-client:1.0.0")
compileOnly("com.microsoft.azure.functions:azure-functions-java-core-library:1.2.0")
compileOnly("com.microsoft.azure.functions:azure-functions-java-spi:1.0.0")

testImplementation("com.microsoft:durabletask-azure-functions:1.0.1")
testImplementation("com.microsoft:durabletask-client:1.0.0")
testImplementation("com.google.protobuf:protobuf-java:3.19.2")
testImplementation("com.microsoft.azure.functions:azure-functions-java-core-library:1.2.0")
testImplementation("com.microsoft.azure.functions:azure-functions-java-spi:1.0.0")
testImplementation(libs.bundles.mockito)

add("latestDepTestImplementation", "com.microsoft:durabletask-azure-functions:+")
add("latestDepTestImplementation", "com.microsoft:durabletask-client:+")
add("latestDepTestImplementation", "com.google.protobuf:protobuf-java:+")
add(
"latestDepTestImplementation",
"com.microsoft.azure.functions:azure-functions-java-core-library:+"
)
add(
"latestDepTestImplementation",
"com.microsoft.azure.functions:azure-functions-java-spi:+"
)
}
Loading
Loading