Skip to content

Commit 7ec5cd3

Browse files
committed
2 parents 3a3a378 + f78c32b commit 7ec5cd3

File tree

6 files changed

+327
-0
lines changed

6 files changed

+327
-0
lines changed

build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ dependencies {
6363

6464
testImplementation(platform("org.junit:junit-bom:5.10.0"))
6565
testImplementation("org.junit.jupiter:junit-jupiter-api")
66+
testImplementation("org.junit.jupiter:junit-jupiter-params")
6667
testImplementation("org.junit.jupiter:junit-jupiter-engine")
6768

6869
testImplementation(platform("org.assertj:assertj-bom:3.24.2"))

buildscripts/templates/SemanticAttributes.java.j2

+25
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ import static io.opentelemetry.api.common.AttributeKey.longKey;
5252
import static io.opentelemetry.api.common.AttributeKey.stringKey;
5353
import static io.opentelemetry.api.common.AttributeKey.stringArrayKey;
5454

55+
import static io.opentelemetry.semconv.AttributeKeyTemplate.stringArrayKeyTemplate;
56+
import static io.opentelemetry.semconv.AttributeKeyTemplate.stringKeyTemplate;
57+
5558
import io.opentelemetry.api.common.AttributeKey;
5659
import java.util.List;
5760

@@ -84,6 +87,28 @@ public final class {{class}} {
8487
{%- endif %}
8588
public static final AttributeKey<{{upFirst(to_java_return_type(attribute.attr_type | string))}}> {{attribute.fqn | to_const_name}} = {{to_java_key_type(attribute.attr_type | string)}}("{{attribute.fqn}}");
8689
{%- endfor %}
90+
{%- for attribute_template in attribute_templates if attribute_template.is_local and not attribute_template.ref %}
91+
92+
/**
93+
* {{attribute_template.brief | render_markdown(code="{{@code {0}}}", paragraph="{0}")}}
94+
{%- if attribute_template.note %}
95+
*
96+
* <p>Notes:
97+
{# NOTE: replace("> ", "") removes the following problematic characters which produce mangled javadoc: #}
98+
{# https://github.com/open-telemetry/semantic-conventions/blob/c83a10a9c33c18a769835e959200d0e24dc708fe/model/resource/k8s.yaml#L34-L38 #}
99+
<ul> {{attribute_template.note | replace("> ", "") | render_markdown(code="{{@code {0}}}", paragraph="<li>{0}</li>", list="{0}")}} </ul>
100+
101+
{%- endif %}
102+
{%- if (attribute_template.stability | string()) == "StabilityLevel.DEPRECATED" %}
103+
*
104+
* @deprecated {{attribute_template.brief | to_doc_brief}}.
105+
{%- endif %}
106+
*/
107+
{%- if (attribute_template.stability | string()) == "StabilityLevel.DEPRECATED" %}
108+
@Deprecated
109+
{%- endif %}
110+
public static final AttributeKeyTemplate<{{upFirst(to_java_return_type(attribute_template.instantiated_type | string))}}> {{attribute_template.fqn | to_const_name}} = {{to_java_key_type(attribute_template.instantiated_type | string)}}Template("{{attribute_template.fqn}}");
111+
{%- endfor %}
87112

88113
// Enum definitions
89114
{%- for attribute in attributes if attribute.is_local and not attribute.ref %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.semconv;
7+
8+
import io.opentelemetry.api.common.AttributeKey;
9+
import io.opentelemetry.api.common.AttributeType;
10+
import java.util.List;
11+
import java.util.concurrent.ConcurrentHashMap;
12+
import java.util.concurrent.ConcurrentMap;
13+
import java.util.function.Function;
14+
15+
/**
16+
* This class provides a handle for creating and caching dynamic / template-type attributes of the
17+
* form <b>&lt;prefix&gt;.&lt;key&gt;</b>. The &lt;prefix&gt; is fixed for a template instance while
18+
* {@link AttributeKey}s can be created and are cached for different values of the &lt;key&gt; part.
19+
*
20+
* <p>An example template-type attribute is the set of attributes for HTTP headers:
21+
* <b>http.request.header.&lt;key&gt;</b>
22+
*
23+
* @param <T> The type of the nested {@link AttributeKey}s.
24+
*/
25+
public final class AttributeKeyTemplate<T> {
26+
27+
private final String prefix;
28+
private final Function<String, AttributeKey<T>> keyBuilder;
29+
private final ConcurrentMap<String, AttributeKey<T>> keysCache = new ConcurrentHashMap<>(1);
30+
31+
private AttributeKeyTemplate(String prefix, Function<String, AttributeKey<T>> keyBuilder) {
32+
this.prefix = prefix;
33+
this.keyBuilder = keyBuilder;
34+
}
35+
36+
/**
37+
* Create an {@link AttributeKeyTemplate} with type {@link AttributeType#STRING} and the given
38+
* {@code prefix}.
39+
*/
40+
public static AttributeKeyTemplate<String> stringKeyTemplate(String prefix) {
41+
return new AttributeKeyTemplate<>(prefix, AttributeKey::stringKey);
42+
}
43+
44+
/**
45+
* Create an {@link AttributeKeyTemplate} with type {@link AttributeType#STRING_ARRAY} and the
46+
* given {@code prefix}.
47+
*/
48+
public static AttributeKeyTemplate<List<String>> stringArrayKeyTemplate(String prefix) {
49+
return new AttributeKeyTemplate<>(prefix, AttributeKey::stringArrayKey);
50+
}
51+
52+
/**
53+
* Create an {@link AttributeKeyTemplate} with type {@link AttributeType#BOOLEAN} and the given
54+
* {@code prefix}.
55+
*/
56+
public static AttributeKeyTemplate<Boolean> booleanKeyTemplate(String prefix) {
57+
return new AttributeKeyTemplate<>(prefix, AttributeKey::booleanKey);
58+
}
59+
60+
/**
61+
* Create an {@link AttributeKeyTemplate} with type {@link AttributeType#BOOLEAN_ARRAY} and the
62+
* given {@code prefix}.
63+
*/
64+
public static AttributeKeyTemplate<List<Boolean>> booleanArrayKeyTemplate(String prefix) {
65+
return new AttributeKeyTemplate<>(prefix, AttributeKey::booleanArrayKey);
66+
}
67+
68+
/**
69+
* Create an {@link AttributeKeyTemplate} with type {@link AttributeType#LONG} and the given
70+
* {@code prefix}.
71+
*/
72+
public static AttributeKeyTemplate<Long> longKeyTemplate(String prefix) {
73+
return new AttributeKeyTemplate<>(prefix, AttributeKey::longKey);
74+
}
75+
76+
/**
77+
* Create an {@link AttributeKeyTemplate} with type {@link AttributeType#LONG_ARRAY} and the given
78+
* {@code prefix}.
79+
*/
80+
public static AttributeKeyTemplate<List<Long>> longArrayKeyTemplate(String prefix) {
81+
return new AttributeKeyTemplate<>(prefix, AttributeKey::longArrayKey);
82+
}
83+
84+
/**
85+
* Create an {@link AttributeKeyTemplate} with type {@link AttributeType#DOUBLE} and the given
86+
* {@code prefix}.
87+
*/
88+
public static AttributeKeyTemplate<Double> doubleKeyTemplate(String prefix) {
89+
return new AttributeKeyTemplate<>(prefix, AttributeKey::doubleKey);
90+
}
91+
92+
/**
93+
* Create an {@link AttributeKeyTemplate} with type {@link AttributeType#DOUBLE_ARRAY} and the
94+
* given {@code prefix}.
95+
*/
96+
public static AttributeKeyTemplate<List<Double>> doubleArrayKeyTemplate(String prefix) {
97+
return new AttributeKeyTemplate<>(prefix, AttributeKey::doubleArrayKey);
98+
}
99+
100+
private AttributeKey<T> createAttributeKey(String keyName) {
101+
String key = prefix + "." + keyName;
102+
return keyBuilder.apply(key);
103+
}
104+
105+
/**
106+
* Returns an {@link AttributeKey} object for the given attribute key whereby the key is the
107+
* variable part of the full attribute name in a template-typed attribute, for example
108+
* <b>http.request.header.&lt;key&gt;</b>.
109+
*
110+
* <p>{@link AttributeKey} objets are being created and cached on the first invocation of this
111+
* method for a certain key. Subsequent invocations of this method with the same key return the
112+
* cached object.
113+
*
114+
* @param key The variable part of the template-typed attribute name.
115+
* @return An {@link AttributeKey} object for the given key.
116+
*/
117+
public AttributeKey<T> getAttributeKey(String key) {
118+
return keysCache.computeIfAbsent(key, this::createAttributeKey);
119+
}
120+
}

src/main/java/io/opentelemetry/semconv/ResourceAttributes.java

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static io.opentelemetry.api.common.AttributeKey.longKey;
1010
import static io.opentelemetry.api.common.AttributeKey.stringArrayKey;
1111
import static io.opentelemetry.api.common.AttributeKey.stringKey;
12+
import static io.opentelemetry.semconv.AttributeKeyTemplate.stringKeyTemplate;
1213

1314
import io.opentelemetry.api.common.AttributeKey;
1415
import java.util.List;
@@ -931,6 +932,10 @@ public final class ResourceAttributes {
931932
@Deprecated
932933
public static final AttributeKey<String> OTEL_LIBRARY_VERSION = stringKey("otel.library.version");
933934

935+
/** Container labels, {@code <key>} being the label name, the value being the label value. */
936+
public static final AttributeKeyTemplate<String> CONTAINER_LABELS =
937+
stringKeyTemplate("container.labels");
938+
934939
// Enum definitions
935940
public static final class CloudPlatformValues {
936941
/** Alibaba Cloud Elastic Compute Service. */

src/main/java/io/opentelemetry/semconv/SemanticAttributes.java

+117
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import static io.opentelemetry.api.common.AttributeKey.longKey;
1111
import static io.opentelemetry.api.common.AttributeKey.stringArrayKey;
1212
import static io.opentelemetry.api.common.AttributeKey.stringKey;
13+
import static io.opentelemetry.semconv.AttributeKeyTemplate.stringArrayKeyTemplate;
14+
import static io.opentelemetry.semconv.AttributeKeyTemplate.stringKeyTemplate;
1315

1416
import io.opentelemetry.api.common.AttributeKey;
1517
import java.util.List;
@@ -1738,6 +1740,121 @@ public final class SemanticAttributes {
17381740
*/
17391741
public static final AttributeKey<Boolean> EXCEPTION_ESCAPED = booleanKey("exception.escaped");
17401742

1743+
/**
1744+
* HTTP request headers, {@code <key>} being the normalized HTTP Header name (lowercase), the
1745+
* value being the header values.
1746+
*
1747+
* <p>Notes:
1748+
*
1749+
* <ul>
1750+
* <li>Instrumentations SHOULD require an explicit configuration of which headers are to be
1751+
* captured. Including all request headers can be a security risk - explicit configuration
1752+
* helps avoid leaking sensitive information. The {@code User-Agent} header is already
1753+
* captured in the {@code user_agent.original} attribute. Users MAY explicitly configure
1754+
* instrumentations to capture them even though it is not recommended. The attribute value
1755+
* MUST consist of either multiple header values as an array of strings or a single-item
1756+
* array containing a possibly comma-concatenated string, depending on the way the HTTP
1757+
* library provides access to headers.
1758+
* </ul>
1759+
*/
1760+
public static final AttributeKeyTemplate<List<String>> HTTP_REQUEST_HEADER =
1761+
stringArrayKeyTemplate("http.request.header");
1762+
1763+
/**
1764+
* HTTP response headers, {@code <key>} being the normalized HTTP Header name (lowercase), the
1765+
* value being the header values.
1766+
*
1767+
* <p>Notes:
1768+
*
1769+
* <ul>
1770+
* <li>Instrumentations SHOULD require an explicit configuration of which headers are to be
1771+
* captured. Including all response headers can be a security risk - explicit configuration
1772+
* helps avoid leaking sensitive information. Users MAY explicitly configure
1773+
* instrumentations to capture them even though it is not recommended. The attribute value
1774+
* MUST consist of either multiple header values as an array of strings or a single-item
1775+
* array containing a possibly comma-concatenated string, depending on the way the HTTP
1776+
* library provides access to headers.
1777+
* </ul>
1778+
*/
1779+
public static final AttributeKeyTemplate<List<String>> HTTP_RESPONSE_HEADER =
1780+
stringArrayKeyTemplate("http.response.header");
1781+
1782+
/**
1783+
* Connect request metadata, {@code <key>} being the normalized Connect Metadata key (lowercase),
1784+
* the value being the metadata values.
1785+
*
1786+
* <p>Notes:
1787+
*
1788+
* <ul>
1789+
* <li>Instrumentations SHOULD require an explicit configuration of which metadata values are to
1790+
* be captured. Including all request metadata values can be a security risk - explicit
1791+
* configuration helps avoid leaking sensitive information.
1792+
* </ul>
1793+
*/
1794+
public static final AttributeKeyTemplate<List<String>> RPC_CONNECT_RPC_REQUEST_METADATA =
1795+
stringArrayKeyTemplate("rpc.connect_rpc.request.metadata");
1796+
1797+
/**
1798+
* Connect response metadata, {@code <key>} being the normalized Connect Metadata key (lowercase),
1799+
* the value being the metadata values.
1800+
*
1801+
* <p>Notes:
1802+
*
1803+
* <ul>
1804+
* <li>Instrumentations SHOULD require an explicit configuration of which metadata values are to
1805+
* be captured. Including all response metadata values can be a security risk - explicit
1806+
* configuration helps avoid leaking sensitive information.
1807+
* </ul>
1808+
*/
1809+
public static final AttributeKeyTemplate<List<String>> RPC_CONNECT_RPC_RESPONSE_METADATA =
1810+
stringArrayKeyTemplate("rpc.connect_rpc.response.metadata");
1811+
1812+
/**
1813+
* gRPC request metadata, {@code <key>} being the normalized gRPC Metadata key (lowercase), the
1814+
* value being the metadata values.
1815+
*
1816+
* <p>Notes:
1817+
*
1818+
* <ul>
1819+
* <li>Instrumentations SHOULD require an explicit configuration of which metadata values are to
1820+
* be captured. Including all request metadata values can be a security risk - explicit
1821+
* configuration helps avoid leaking sensitive information.
1822+
* </ul>
1823+
*/
1824+
public static final AttributeKeyTemplate<List<String>> RPC_GRPC_REQUEST_METADATA =
1825+
stringArrayKeyTemplate("rpc.grpc.request.metadata");
1826+
1827+
/**
1828+
* gRPC response metadata, {@code <key>} being the normalized gRPC Metadata key (lowercase), the
1829+
* value being the metadata values.
1830+
*
1831+
* <p>Notes:
1832+
*
1833+
* <ul>
1834+
* <li>Instrumentations SHOULD require an explicit configuration of which metadata values are to
1835+
* be captured. Including all response metadata values can be a security risk - explicit
1836+
* configuration helps avoid leaking sensitive information.
1837+
* </ul>
1838+
*/
1839+
public static final AttributeKeyTemplate<List<String>> RPC_GRPC_RESPONSE_METADATA =
1840+
stringArrayKeyTemplate("rpc.grpc.response.metadata");
1841+
1842+
/**
1843+
* A dynamic value in the url path.
1844+
*
1845+
* <p>Notes:
1846+
*
1847+
* <ul>
1848+
* <li>Many Elasticsearch url paths allow dynamic values. These SHOULD be recorded in span
1849+
* attributes in the format {@code db.elasticsearch.path_parts.<key>}, where {@code <key>}
1850+
* is the url path part name. The implementation SHOULD reference the <a
1851+
* href="https://raw.githubusercontent.com/elastic/elasticsearch-specification/main/output/schema/schema.json">elasticsearch
1852+
* schema</a> in order to map the path part values to their names.
1853+
* </ul>
1854+
*/
1855+
public static final AttributeKeyTemplate<String> DB_ELASTICSEARCH_PATH_PARTS =
1856+
stringKeyTemplate("db.elasticsearch.path_parts");
1857+
17411858
// Enum definitions
17421859
public static final class ErrorTypeValues {
17431860
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.semconv;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
import io.opentelemetry.api.common.AttributeKey;
11+
import io.opentelemetry.api.common.AttributeType;
12+
import java.util.function.Function;
13+
import java.util.stream.Stream;
14+
import org.junit.jupiter.params.ParameterizedTest;
15+
import org.junit.jupiter.params.provider.Arguments;
16+
import org.junit.jupiter.params.provider.MethodSource;
17+
18+
class AttributeKeyTemplateTest {
19+
20+
@ParameterizedTest
21+
@MethodSource("getAttributeKeyArgs")
22+
void getAttributeKey(
23+
Function<String, AttributeKeyTemplate<?>> createTemplate,
24+
AttributeType expectedAttributeType) {
25+
String prefix = "my.prefix";
26+
AttributeKeyTemplate<?> template = createTemplate.apply(prefix);
27+
28+
AttributeKey<?> attributeKey1 = template.getAttributeKey("key1");
29+
AttributeKey<?> attributeKey2 = template.getAttributeKey("key2");
30+
31+
assertThat(attributeKey1.getType()).isEqualTo(expectedAttributeType);
32+
assertThat(attributeKey1.getKey()).isEqualTo("my.prefix.key1");
33+
assertThat(attributeKey1).isSameAs(template.getAttributeKey("key1"));
34+
35+
assertThat(attributeKey2.getType()).isEqualTo(expectedAttributeType);
36+
assertThat(attributeKey2.getKey()).isEqualTo("my.prefix.key2");
37+
}
38+
39+
private static Stream<Arguments> getAttributeKeyArgs() {
40+
return Stream.of(
41+
Arguments.of(asFunction(AttributeKeyTemplate::stringKeyTemplate), AttributeType.STRING),
42+
Arguments.of(
43+
asFunction(AttributeKeyTemplate::stringArrayKeyTemplate), AttributeType.STRING_ARRAY),
44+
Arguments.of(asFunction(AttributeKeyTemplate::booleanKeyTemplate), AttributeType.BOOLEAN),
45+
Arguments.of(
46+
asFunction(AttributeKeyTemplate::booleanArrayKeyTemplate), AttributeType.BOOLEAN_ARRAY),
47+
Arguments.of(asFunction(AttributeKeyTemplate::longKeyTemplate), AttributeType.LONG),
48+
Arguments.of(
49+
asFunction(AttributeKeyTemplate::longArrayKeyTemplate), AttributeType.LONG_ARRAY),
50+
Arguments.of(asFunction(AttributeKeyTemplate::doubleKeyTemplate), AttributeType.DOUBLE),
51+
Arguments.of(
52+
asFunction(AttributeKeyTemplate::doubleArrayKeyTemplate), AttributeType.DOUBLE_ARRAY));
53+
}
54+
55+
private static Function<String, AttributeKeyTemplate<?>> asFunction(
56+
Function<String, AttributeKeyTemplate<?>> function) {
57+
return function;
58+
}
59+
}

0 commit comments

Comments
 (0)