Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public final class ChatCompletionClientTracerTest {
private static final String AZ_NAMESPACE_NAME = "Microsoft.CognitiveServices";

private static final AttributeKey<String> AZ_NAMESPACE = AttributeKey.stringKey("az.namespace");
private static final AttributeKey<String> AZURE_RESOURCE_PROVIDER_NAMESPACE
= AttributeKey.stringKey("azure.resource_provider.namespace");
private static final AttributeKey<String> GEN_AI_SYSTEM = AttributeKey.stringKey("gen_ai.system");
private static final AttributeKey<String> GEN_AI_OPERATION_NAME = AttributeKey.stringKey("gen_ai.operation.name");
private static final AttributeKey<Double> GEN_AI_REQUEST_TOP_P = AttributeKey.doubleKey("gen_ai.request.top_p");
Expand Down Expand Up @@ -181,6 +183,7 @@ static ReadableSpan getChatSpan(List<ReadableSpan> spans, ChatCompletionsOptions

static void assertChatSpanRequestAttributes(Attributes chatAttributes, ChatCompletionsOptions completionRequest) {
assertEquals(AZ_NAMESPACE_NAME, chatAttributes.get(AZ_NAMESPACE));
assertEquals(AZ_NAMESPACE_NAME, chatAttributes.get(AZURE_RESOURCE_PROVIDER_NAMESPACE));
assertEquals(INFERENCE_GEN_AI_SYSTEM_NAME, chatAttributes.get(GEN_AI_SYSTEM));
assertEquals(GEN_AI_CHAT_OPERATION_NAME, chatAttributes.get(GEN_AI_OPERATION_NAME));
final String modelId = completionRequest.getModel();
Expand Down
6 changes: 6 additions & 0 deletions sdk/core/azure-core-tracing-opentelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@

### Bugs Fixed

- Added additional attributes according to OpenTelemetry semantic conventions changes:
- `az.namespace` -> `azure.resource_provider.namespace`
- `az.service_request_id` -> `azure.service.request.id`
These attributes are added in addition to the existing ones and will be reported together for now to allow users to
transition to the new attribute names. The old attributes will be removed in a future release.

### Other Changes

## 1.0.0-beta.62 (2026-01-15)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.azure.core.util.tracing.TracingLink;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.SpanContext;
Expand All @@ -31,7 +32,7 @@
import java.util.function.BiConsumer;
import java.util.function.Function;

import static java.util.Collections.singletonMap;
import static com.azure.core.tracing.opentelemetry.OpenTelemetryUtils.addAttribute;

/**
* Basic tracing implementation class for use with REST and AMQP Service Clients to create {@link Span} and in-process
Expand All @@ -51,6 +52,7 @@ public class OpenTelemetryTracer implements com.azure.core.util.tracing.Tracer {
private static final String SUPPRESSED_SPAN_FLAG = "suppressed-span-flag";
private static final String CLIENT_METHOD_CALL_FLAG = "client-method-call-flag";
private static final String AZ_TRACING_NAMESPACE_KEY = "az.namespace";
private static final String AZURE_RESOURCE_PROVIDER_NAMESPACE = "azure.resource_provider.namespace";
private final Tracer tracer;
private final boolean isEnabled;

Expand Down Expand Up @@ -142,7 +144,7 @@ public Context start(String spanName, StartSpanOptions options, Context context)
context = context.addData(CLIENT_METHOD_CALL_FLAG, true);
}

io.opentelemetry.context.Context traceContext = getTraceContextOrDefault(context, null);
io.opentelemetry.context.Context traceContext = getTraceContextOrNull(context);

SpanBuilder spanBuilder = createSpanBuilder(spanName, options, traceContext);

Expand All @@ -152,9 +154,9 @@ public Context start(String spanName, StartSpanOptions options, Context context)

String tracingNamespace = getAzNamespace(context);
if (tracingNamespace != null) {
Attributes attributes
= OpenTelemetryUtils.convert(singletonMap(AZ_TRACING_NAMESPACE_KEY, tracingNamespace));
spanBuilder.setAllAttributes(attributes);
AttributesBuilder builder = Attributes.builder();
addAttribute(builder, AZ_TRACING_NAMESPACE_KEY, tracingNamespace);
spanBuilder.setAllAttributes(builder.build());
}

Span span = spanBuilder.startSpan();
Expand Down Expand Up @@ -224,7 +226,7 @@ private SpanBuilder createSpanBuilder(String spanName, StartSpanOptions options,
*/
@Override
public void injectContext(BiConsumer<String, String> headerSetter, Context context) {
io.opentelemetry.context.Context otelContext = getTraceContextOrDefault(context, null);
io.opentelemetry.context.Context otelContext = getTraceContextOrNull(context);
if (otelContext != null) {
TRACE_CONTEXT_FORMAT.inject(otelContext, null, (ignored, key, value) -> headerSetter.accept(key, value));
}
Expand All @@ -246,7 +248,7 @@ public void setAttribute(String key, long value, Context context) {
}

if (span.isRecording()) {
OpenTelemetryUtils.addAttribute(span, key, value);
addAttribute(span, key, value);
}
}

Expand All @@ -272,7 +274,7 @@ public void setAttribute(String key, String value, Context context) {
}

if (span.isRecording()) {
OpenTelemetryUtils.addAttribute(span, key, value);
addAttribute(span, key, value);
}
}

Expand All @@ -294,7 +296,7 @@ public void setAttribute(String key, Object value, Context context) {
}

if (span.isRecording()) {
OpenTelemetryUtils.addAttribute(span, key, value);
addAttribute(span, key, value);
}
}

Expand Down Expand Up @@ -383,7 +385,7 @@ public AutoCloseable makeSpanCurrent(Context context) {
return NOOP_CLOSEABLE;
}

io.opentelemetry.context.Context traceContext = getTraceContextOrDefault(context, null);
io.opentelemetry.context.Context traceContext = getTraceContextOrNull(context);
if (traceContext == null) {
return NOOP_CLOSEABLE;
}
Expand Down Expand Up @@ -447,11 +449,10 @@ private static <T> T getOrNull(Context context, String key, Class<T> clazz) {
* Returns OpenTelemetry trace context from given com.azure.core.Context under PARENT_TRACE_CONTEXT_KEY
* or default value.
*/
private static io.opentelemetry.context.Context getTraceContextOrDefault(Context azContext,
io.opentelemetry.context.Context defaultContext) {
private static io.opentelemetry.context.Context getTraceContextOrNull(Context azContext) {
final Object data = azContext.getData(PARENT_TRACE_CONTEXT_KEY).orElse(null);
if (data == null) {
return defaultContext;
return null;
}

if (data instanceof io.opentelemetry.context.Context) {
Expand All @@ -474,7 +475,7 @@ private static io.opentelemetry.context.Context getTraceContextOrDefault(Context
}
}

return defaultContext;
return null;
}

/**
Expand All @@ -485,7 +486,7 @@ private Span getSpanOrNull(Context azContext) {
return null;
}

io.opentelemetry.context.Context traceContext = getTraceContextOrDefault(azContext, null);
io.opentelemetry.context.Context traceContext = getTraceContextOrNull(azContext);

return traceContext == null ? null : Span.fromContext(traceContext);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;

import static com.azure.core.tracing.opentelemetry.ExceptionUtils.unwrapError;
import static com.azure.core.util.tracing.Tracer.ENTITY_PATH_KEY;
Expand All @@ -21,6 +22,7 @@ class OpenTelemetryUtils {
private static final ClientLogger LOGGER = new ClientLogger(OpenTelemetryUtils.class);

static final String SERVICE_REQUEST_ID_ATTRIBUTE = "serviceRequestId";
static final String AZ_SERVICE_REQUEST_ID_ATTRIBUTE = "az.service_request_id";
static final String CLIENT_REQUEST_ID_ATTRIBUTE = "requestId";
static final AttributeKey<String> ERROR_TYPE_ATTRIBUTE = AttributeKey.stringKey("error.type");

Expand All @@ -35,36 +37,35 @@ public static Attributes convert(Map<String, Object> attributeMap) {
continue;
}

addAttribute(builder, mapAttributeName(kvp.getKey()), kvp.getValue());
addAttribute(builder, kvp.getKey(), kvp.getValue());
}

return builder.build();
}

private static String mapAttributeName(String name) {
private static void mapKeyAndConsume(String key, Consumer<String> mappedKey) {
// TODO (limolkova) remove all these mappings prior to plugin stability
if ("http.method".equals(name)) {
return "http.request.method";
}
if ("http.status_code".equals(name)) {
return "http.response.status_code";
}
if ("http.url".equals(name)) {
return "url.full";
}
if (ENTITY_PATH_KEY.equals(name)) {
return "messaging.destination.name";
}
if (HOST_NAME_KEY.equals(name)) {
return "server.address";
}
if (CLIENT_REQUEST_ID_ATTRIBUTE.equals(name)) {
return "az.client_request_id";
}
if (SERVICE_REQUEST_ID_ATTRIBUTE.equals(name)) {
return "az.service_request_id";
if ("http.method".equals(key)) {
mappedKey.accept("http.request.method");
} else if ("http.status_code".equals(key)) {
mappedKey.accept("http.response.status_code");
} else if ("http.url".equals(key)) {
mappedKey.accept("url.full");
} else if (ENTITY_PATH_KEY.equals(key)) {
mappedKey.accept("messaging.destination.name");
} else if (HOST_NAME_KEY.equals(key)) {
mappedKey.accept("server.address");
} else if (CLIENT_REQUEST_ID_ATTRIBUTE.equals(key)) {
mappedKey.accept("az.client_request_id");
} else if (SERVICE_REQUEST_ID_ATTRIBUTE.equals(key) || AZ_SERVICE_REQUEST_ID_ATTRIBUTE.equals(key)) {
mappedKey.accept("az.service_request_id");
mappedKey.accept("azure.service.request.id");
} else if ("az.namespace".equals(key)) {
mappedKey.accept("az.namespace");
mappedKey.accept("azure.resource_provider.namespace");
} else {
mappedKey.accept(key);
}
return name;
}

/**
Expand All @@ -75,24 +76,28 @@ private static String mapAttributeName(String name) {
* @param key key of the attribute to be added
* @param value value of the attribute to be added
*/
private static void addAttribute(AttributesBuilder attributesBuilder, String key, Object value) {
static void addAttribute(AttributesBuilder attributesBuilder, String key, Object value) {
Objects.requireNonNull(key, "OpenTelemetry attribute name cannot be null.");
if (value instanceof String) {
attributesBuilder.put(AttributeKey.stringKey(key), (String) value);
mapKeyAndConsume(key,
mappedKey -> attributesBuilder.put(AttributeKey.stringKey(mappedKey), (String) value));
} else if (value instanceof Long) {
attributesBuilder.put(AttributeKey.longKey(key), (Long) value);
mapKeyAndConsume(key, mappedKey -> attributesBuilder.put(AttributeKey.longKey(mappedKey), (Long) value));
} else if (value instanceof Integer) {
attributesBuilder.put(AttributeKey.longKey(key), (Integer) value);
mapKeyAndConsume(key, mappedKey -> attributesBuilder.put(AttributeKey.longKey(mappedKey), (Integer) value));
} else if (value instanceof Boolean) {
attributesBuilder.put(AttributeKey.booleanKey(key), (Boolean) value);
mapKeyAndConsume(key,
mappedKey -> attributesBuilder.put(AttributeKey.booleanKey(mappedKey), (Boolean) value));
} else if (value instanceof Double) {
attributesBuilder.put(AttributeKey.doubleKey(key), (Double) value);
mapKeyAndConsume(key,
mappedKey -> attributesBuilder.put(AttributeKey.doubleKey(mappedKey), (Double) value));
} else if (value instanceof Float) {
attributesBuilder.put(AttributeKey.doubleKey(key), ((Float) value).doubleValue());
mapKeyAndConsume(key,
mappedKey -> attributesBuilder.put(AttributeKey.doubleKey(mappedKey), ((Float) value).doubleValue()));
} else if (value instanceof Short) {
attributesBuilder.put(AttributeKey.longKey(key), (Short) value);
mapKeyAndConsume(key, mappedKey -> attributesBuilder.put(AttributeKey.longKey(mappedKey), (Short) value));
} else if (value instanceof Byte) {
attributesBuilder.put(AttributeKey.longKey(key), (Byte) value);
mapKeyAndConsume(key, mappedKey -> attributesBuilder.put(AttributeKey.longKey(mappedKey), (Byte) value));
} else {
LOGGER.warning("Could not populate attribute with key '{}', type {} is not supported.", key,
value.getClass().getName());
Expand All @@ -110,23 +115,23 @@ private static void addAttribute(AttributesBuilder attributesBuilder, String key
static void addAttribute(Span span, String key, Object value) {
Objects.requireNonNull(key, "OpenTelemetry attribute name cannot be null.");

key = mapAttributeName(key);
if (value instanceof String) {
span.setAttribute(AttributeKey.stringKey(key), (String) value);
mapKeyAndConsume(key, mappedKey -> span.setAttribute(AttributeKey.stringKey(mappedKey), (String) value));
} else if (value instanceof Long) {
span.setAttribute(AttributeKey.longKey(key), (Long) value);
mapKeyAndConsume(key, mappedKey -> span.setAttribute(AttributeKey.longKey(mappedKey), (Long) value));
} else if (value instanceof Integer) {
span.setAttribute(AttributeKey.longKey(key), (Integer) value);
mapKeyAndConsume(key, mappedKey -> span.setAttribute(AttributeKey.longKey(mappedKey), (Integer) value));
} else if (value instanceof Boolean) {
span.setAttribute(AttributeKey.booleanKey(key), (Boolean) value);
mapKeyAndConsume(key, mappedKey -> span.setAttribute(AttributeKey.booleanKey(mappedKey), (Boolean) value));
} else if (value instanceof Double) {
span.setAttribute(AttributeKey.doubleKey(key), (Double) value);
mapKeyAndConsume(key, mappedKey -> span.setAttribute(AttributeKey.doubleKey(mappedKey), (Double) value));
} else if (value instanceof Float) {
span.setAttribute(AttributeKey.doubleKey(key), ((Float) value).doubleValue());
mapKeyAndConsume(key,
mappedKey -> span.setAttribute(AttributeKey.doubleKey(mappedKey), ((Float) value).doubleValue()));
} else if (value instanceof Short) {
span.setAttribute(AttributeKey.longKey(key), (Short) value);
mapKeyAndConsume(key, mappedKey -> span.setAttribute(AttributeKey.longKey(mappedKey), (Short) value));
} else if (value instanceof Byte) {
span.setAttribute(AttributeKey.longKey(key), (Byte) value);
mapKeyAndConsume(key, mappedKey -> span.setAttribute(AttributeKey.longKey(mappedKey), (Byte) value));
} else {
LOGGER.warning("Could not populate attribute with key '{}', type {} is not supported.", key,
value.getClass().getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public class CreateTracerTests {
.addSpanProcessor(SimpleSpanProcessor.create(InMemorySpanExporter.create()))
.build();

private static final AttributeKey<String> AZ_NAMESPACE = AttributeKey.stringKey("az.namespace");
private static final AttributeKey<String> AZURE_RESOURCE_PROVIDER_NAMESPACE
= AttributeKey.stringKey("azure.resource_provider.namespace");

private final OpenTelemetry openTelemetry = OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).build();

@Test
Expand Down Expand Up @@ -79,8 +83,9 @@ public void createTracerWithAzNamespace() {

Context span = tracer.start("test", Context.NONE);
SpanData data = getSpanData(span);
assertEquals(1, data.getAttributes().size());
assertEquals("namespace", data.getAttributes().get(AttributeKey.stringKey("az.namespace")));
assertEquals(2, data.getAttributes().size());
assertEquals("namespace", data.getAttributes().get(AZ_NAMESPACE));
assertEquals("namespace", data.getAttributes().get(AZURE_RESOURCE_PROVIDER_NAMESPACE));
}

@Test
Expand All @@ -89,10 +94,12 @@ public void createTracerWithAzNamespaceInContext() {

Tracer tracer = TracerProvider.getDefaultProvider().createTracer("test", null, "namespace", options);

Context span = tracer.start("test", new Context("az.namespace", "another"));
Context span = tracer.start("test", new Context(AZ_NAMESPACE.getKey(), "another")
.addData(AZURE_RESOURCE_PROVIDER_NAMESPACE.getKey(), "andAnother"));
SpanData data = getSpanData(span);
assertEquals(1, data.getAttributes().size());
assertEquals("namespace", data.getAttributes().get(AttributeKey.stringKey("az.namespace")));
assertEquals(2, data.getAttributes().size());
assertEquals("namespace", data.getAttributes().get(AZ_NAMESPACE));
assertEquals("namespace", data.getAttributes().get(AZURE_RESOURCE_PROVIDER_NAMESPACE));
}

@Test
Expand Down Expand Up @@ -151,7 +158,8 @@ public void testSdkOptions() {
assertEquals("test", readableSpan.getInstrumentationScopeInfo().getName());
assertEquals("https://aka.ms/az/sdk/schema:1.42.0", readableSpan.getInstrumentationScopeInfo().getSchemaUrl());
assertEquals("1.2.3-beta.45", readableSpan.getInstrumentationScopeInfo().getVersion());
assertEquals("namespace", readableSpan.getAttributes().get(AttributeKey.stringKey("az.namespace")));
assertEquals("namespace", readableSpan.getAttributes().get(AZ_NAMESPACE));
assertEquals("namespace", readableSpan.getAttributes().get(AZURE_RESOURCE_PROVIDER_NAMESPACE));
}

private static SpanData getSpanData(Context context) {
Expand Down
Loading
Loading