diff --git a/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpConfig.java b/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpConfig.java
index 56bed994d6..01ff6d9185 100644
--- a/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpConfig.java
+++ b/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpConfig.java
@@ -18,8 +18,12 @@
import io.micrometer.core.instrument.config.validate.Validated;
import io.micrometer.core.instrument.push.PushRegistryConfig;
-import static io.micrometer.core.instrument.config.MeterRegistryConfigValidator.checkAll;
-import static io.micrometer.core.instrument.config.MeterRegistryConfigValidator.checkRequired;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static io.micrometer.core.instrument.config.MeterRegistryConfigValidator.*;
+import static io.micrometer.core.instrument.config.validate.PropertyValidator.getString;
import static io.micrometer.core.instrument.config.validate.PropertyValidator.getUrlString;
/**
@@ -48,9 +52,44 @@ default String url() {
return getUrlString(this, "url").orElse("http://localhost:4318/v1/metrics");
}
+ /**
+ * Attributes to set on the Resource that will be used for all metrics published. This
+ * should include a {@code service.name} attribute that identifies your service.
+ *
+ * By default, resource attributes will load using the {@link #get(String)} method,
+ * extracting key values from a comma-separated list in the format
+ * {@code key1=value1,key2=value2}. Resource attributes will be loaded from the
+ * {@code OTEL_RESOURCE_ATTRIBUTES} environment variable and the service name from the
+ * {@code OTEL_SERVICE_NAME} environment variable if they are set and
+ * {@link #get(String)} does not return a value.
+ * @return map of key value pairs to use as resource attributes
+ * @see OpenTelemetry
+ * Resource Semantic Conventions
+ */
+ default Map resourceAttributes() {
+ Map env = System.getenv();
+ String resourceAttributesConfig = getString(this, "resourceAttributes")
+ .orElse(env.get("OTEL_RESOURCE_ATTRIBUTES"));
+ String[] splitResourceAttributesString = resourceAttributesConfig == null ? new String[] {}
+ : resourceAttributesConfig.trim().split(",");
+
+ Map resourceAttributes = Arrays.stream(splitResourceAttributesString).map(String::trim)
+ .filter(keyvalue -> keyvalue.length() > 2 && keyvalue.indexOf('=') > 0)
+ .collect(Collectors.toMap(keyvalue -> keyvalue.substring(0, keyvalue.indexOf('=')).trim(),
+ keyvalue -> keyvalue.substring(keyvalue.indexOf('=') + 1).trim()));
+
+ if (env.containsKey("OTEL_SERVICE_NAME") && !resourceAttributes.containsKey("service.name")) {
+ resourceAttributes.put("service.name", env.get("OTEL_SERVICE_NAME"));
+ }
+
+ return resourceAttributes;
+ }
+
@Override
default Validated> validate() {
- return checkAll(this, c -> PushRegistryConfig.validate(c), checkRequired("url", OtlpConfig::url));
+ return checkAll(this, c -> PushRegistryConfig.validate(c), checkRequired("url", OtlpConfig::url),
+ check("resourceAttributes", OtlpConfig::resourceAttributes));
}
}
diff --git a/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java b/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java
index 46faae19a9..29d627480b 100644
--- a/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java
+++ b/implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java
@@ -40,6 +40,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.function.DoubleSupplier;
@@ -277,20 +278,30 @@ private Iterable extends KeyValue> getTagsForId(Meter.Id id) {
.collect(Collectors.toList());
}
- private KeyValue createKeyValue(String key, String value) {
+ // VisibleForTesting
+ static KeyValue createKeyValue(String key, String value) {
return KeyValue.newBuilder().setKey(key).setValue(AnyValue.newBuilder().setStringValue(value)).build();
}
- private Iterable getResourceAttributes() {
+ // VisibleForTesting
+ Iterable getResourceAttributes() {
+ boolean serviceNameProvided = false;
List attributes = new ArrayList<>();
- // TODO How to expose configuration of the service.name
- attributes.add(createKeyValue("service.name", "unknown_service"));
attributes.add(createKeyValue("telemetry.sdk.name", "io.micrometer"));
attributes.add(createKeyValue("telemetry.sdk.language", "java"));
String micrometerCoreVersion = MeterRegistry.class.getPackage().getImplementationVersion();
if (micrometerCoreVersion != null) {
attributes.add(createKeyValue("telemetry.sdk.version", micrometerCoreVersion));
}
+ for (Map.Entry keyValue : this.config.resourceAttributes().entrySet()) {
+ if ("service.name".equals(keyValue.getKey())) {
+ serviceNameProvided = true;
+ }
+ attributes.add(createKeyValue(keyValue.getKey(), keyValue.getValue()));
+ }
+ if (!serviceNameProvided) {
+ attributes.add(createKeyValue("service.name", "unknown_service"));
+ }
return attributes;
}
diff --git a/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpConfigTest.java b/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpConfigTest.java
new file mode 100644
index 0000000000..d05a9cd59e
--- /dev/null
+++ b/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpConfigTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.micrometer.registry.otlp;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class OtlpConfigTest {
+
+ @Test
+ void resourceAttributesInputParsing() {
+ OtlpConfig config = k -> "key1=value1,";
+ assertThat(config.resourceAttributes()).containsEntry("key1", "value1").hasSize(1);
+ config = k -> "k=v,a";
+ assertThat(config.resourceAttributes()).containsEntry("k", "v").hasSize(1);
+ config = k -> "k=v,a==";
+ assertThat(config.resourceAttributes()).containsEntry("k", "v").containsEntry("a", "=").hasSize(2);
+ config = k -> " k = v, a= b ";
+ assertThat(config.resourceAttributes()).containsEntry("k", "v").containsEntry("a", "b").hasSize(2);
+ }
+
+}
diff --git a/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpMeterRegistryTest.java b/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpMeterRegistryTest.java
index d34f455e1d..e4e4aca190 100644
--- a/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpMeterRegistryTest.java
+++ b/implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpMeterRegistryTest.java
@@ -18,9 +18,13 @@
import io.micrometer.core.instrument.*;
import org.junit.jupiter.api.Test;
+import java.io.IOException;
import java.lang.management.CompilationMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Properties;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
@@ -466,4 +470,39 @@ void longTaskTimer() {
+ " }\n" + " aggregation_temporality: AGGREGATION_TEMPORALITY_CUMULATIVE\n" + "}\n");
}
+ // If the service.name was not specified, SDKs MUST fallback to 'unknown_service'
+ @Test
+ void unknownServiceByDefault() {
+ assertThat(registry.getResourceAttributes())
+ .contains(OtlpMeterRegistry.createKeyValue("service.name", "unknown_service"));
+ }
+
+ @Test
+ void setServiceNameOverrideMethod() {
+ registry = new OtlpMeterRegistry(new OtlpConfig() {
+ @Override
+ public String get(String key) {
+ return null;
+ }
+
+ @Override
+ public Map resourceAttributes() {
+ return Collections.singletonMap("service.name", "myService");
+ }
+ }, Clock.SYSTEM);
+
+ assertThat(registry.getResourceAttributes())
+ .contains(OtlpMeterRegistry.createKeyValue("service.name", "myService"));
+ }
+
+ // can't test environment variables easily in an isolated way
+ @Test
+ void setResourceAttributesAsString() throws IOException {
+ Properties propertiesConfig = new Properties();
+ propertiesConfig.load(this.getClass().getResourceAsStream("/otlp-config.properties"));
+ registry = new OtlpMeterRegistry(key -> (String) propertiesConfig.get(key), Clock.SYSTEM);
+ assertThat(registry.getResourceAttributes()).contains(OtlpMeterRegistry.createKeyValue("key1", "value1"),
+ OtlpMeterRegistry.createKeyValue("key2", "value2"));
+ }
+
}
diff --git a/implementations/micrometer-registry-otlp/src/test/resources/otlp-config.properties b/implementations/micrometer-registry-otlp/src/test/resources/otlp-config.properties
new file mode 100644
index 0000000000..3760ed4e95
--- /dev/null
+++ b/implementations/micrometer-registry-otlp/src/test/resources/otlp-config.properties
@@ -0,0 +1,17 @@
+#
+# Copyright 2022 VMware, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+otlp.resourceAttributes=key1=value1,key2=value2