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
@@ -0,0 +1,38 @@
/*
* Copyright 2017-2026 original authors
*
* 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.micronaut.configuration.metrics.common.tags;

import io.micronaut.context.condition.Condition;
import io.micronaut.context.condition.ConditionContext;

import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_COMMON_TAGS;
import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_TAGS;

/**
* Activates common tag configuration when either supported property prefix is configured.
*/
public final class CommonTagsCondition implements Condition {

@Override
public boolean matches(ConditionContext context) {
return containsConfiguredTags(context, MICRONAUT_METRICS_TAGS)
|| containsConfiguredTags(context, MICRONAUT_METRICS_COMMON_TAGS);
}

private static boolean containsConfiguredTags(ConditionContext<?> context, String property) {
return context.containsProperty(property) || context.containsProperties(property);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 original authors
* Copyright 2017-2026 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,30 +19,41 @@
import io.micrometer.core.instrument.Tag;
import io.micronaut.configuration.metrics.aggregator.MeterRegistryConfigurer;
import io.micronaut.configuration.metrics.annotation.RequiresMetrics;
import io.micronaut.configuration.metrics.micrometer.ExportConfigurationProperties;
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Requires;
import io.micronaut.context.env.Environment;
import io.micronaut.core.naming.conventions.StringConvention;
import io.micronaut.core.type.Argument;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Properties;
import java.util.Map;
import java.util.TreeMap;

import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_COMMON_TAGS;
import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_TAGS;

/**
* Configures every MeterRegistry with common tags.
*/
@Factory
@RequiresMetrics
@Requires(property = MICRONAUT_METRICS_COMMON_TAGS)
@Requires(condition = CommonTagsCondition.class)
public class CommonTagsConfigurer implements MeterRegistryConfigurer<MeterRegistry> {

private static final Argument<Map<String, Object>> STRING_OBJECT_MAP = Argument.mapOf(String.class, Object.class);

private final List<Tag> commonTags = new ArrayList<>();

public CommonTagsConfigurer(ExportConfigurationProperties configuration) {
Properties tags = configuration.getTags();
for (String key : tags.stringPropertyNames()) {
commonTags.add(Tag.of(key, tags.getProperty(key)));
public CommonTagsConfigurer(Environment environment) {
Map<String, Object> tags = new TreeMap<>(readConfiguredTags(environment, MICRONAUT_METRICS_TAGS));
tags.putAll(readConfiguredTags(environment, MICRONAUT_METRICS_COMMON_TAGS));
for (Map.Entry<String, Object> entry : tags.entrySet()) {
Object value = entry.getValue();
if (value != null) {
commonTags.add(Tag.of(entry.getKey(), String.valueOf(value)));
}
}
}

Expand All @@ -60,4 +71,14 @@ public Class<MeterRegistry> getType() {
public int getOrder() {
return HIGHEST_PRECEDENCE;
}

private static Map<String, Object> readConfiguredTags(Environment environment, String property) {
if (environment.containsProperties(property)) {
return new LinkedHashMap<>(environment.getProperties(property, StringConvention.RAW));
}
if (environment.containsProperty(property)) {
return new LinkedHashMap<>(environment.getProperty(property, STRING_OBJECT_MAP).orElse(Map.of()));
}
return new LinkedHashMap<>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ public class MeterRegistryFactory {

public static final String MICRONAUT_METRICS = "micronaut.metrics.";
public static final String MICRONAUT_METRICS_BINDERS = MICRONAUT_METRICS + "binders";
public static final String MICRONAUT_METRICS_COMMON_TAGS = MICRONAUT_METRICS + "tags";
public static final String MICRONAUT_METRICS_TAGS = MICRONAUT_METRICS + "tags";
public static final String MICRONAUT_METRICS_COMMON_TAGS = MICRONAUT_METRICS + "common-tags";
public static final String MICRONAUT_METRICS_ENABLED = MICRONAUT_METRICS + "enabled";
public static final String MICRONAUT_METRICS_EXPORT = MICRONAUT_METRICS + "export";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package io.micronaut.configuration.metrics.common.tags

import io.micrometer.core.instrument.simple.SimpleMeterRegistry
import io.micronaut.context.ApplicationContext
import io.micronaut.context.env.Environment
import io.micronaut.core.naming.conventions.StringConvention
import spock.lang.Specification

import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_COMMON_TAGS
import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_TAGS

class CommonTagsConfigurerSpec extends Specification {

void "common-tags override legacy tags for duplicate keys"() {
given:
ApplicationContext context = ApplicationContext.run([
"micronaut.metrics.tags.application" : "legacy",
"micronaut.metrics.tags.region" : "us-east-1",
"micronaut.metrics.common-tags.application": "common-tags",
"micronaut.metrics.common-tags.cluster" : "primary"
])
SimpleMeterRegistry registry = new SimpleMeterRegistry()

when:
context.getBean(CommonTagsConfigurer).configure(registry)
def counter = registry.counter("test.counter")

then:
counter.id.getTag("application") == "common-tags"
counter.id.getTag("region") == "us-east-1"
counter.id.getTag("cluster") == "primary"

cleanup:
registry.close()
context.close()
}

void "null common tag values are ignored"() {
given:
Environment environment = Stub() {
containsProperties(MICRONAUT_METRICS_TAGS) >> false
containsProperty(MICRONAUT_METRICS_TAGS) >> false
containsProperties(MICRONAUT_METRICS_COMMON_TAGS) >> true
getProperties(MICRONAUT_METRICS_COMMON_TAGS, StringConvention.RAW) >> [application: null, region: "us-east-1"]
}
SimpleMeterRegistry registry = new SimpleMeterRegistry()

when:
new CommonTagsConfigurer(environment).configure(registry)
def counter = registry.counter("test.counter")

then:
counter.id.getTag("application") == null
counter.id.getTag("region") == "us-east-1"

cleanup:
registry.close()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -378,12 +378,15 @@ class MetricsEndpointSpec extends Specification {
}

@Unroll
void "test metrics endpoint with common tags"() {
void "test metrics endpoint #name with common tags from #configurationStyle #propertyName"() {
given:
run('endpoints.metrics.sensitive' : false,
(MICRONAUT_METRICS_ENABLED) : true,
"micronaut.metrics.binders.web.enabled": true,
"micronaut.metrics.tags" : ["test1": "test1-val", "test2": "test2-val"])
Map<String, Object> config = [
'endpoints.metrics.sensitive' : false,
(MICRONAUT_METRICS_ENABLED) : true,
"micronaut.metrics.binders.web.enabled": true
]
config.putAll(tagsConfig)
run(config)

expect:
100.times {
Expand All @@ -400,7 +403,15 @@ class MetricsEndpointSpec extends Specification {
}

where:
name << ["process.files.open", "process.files.max"]
configurationStyle | propertyName | name | tagsConfig
"dotted" | "micronaut.metrics.tags" | "process.files.open" | ["micronaut.metrics.tags.test1" : "test1-val", "micronaut.metrics.tags.test2" : "test2-val"]
"dotted" | "micronaut.metrics.tags" | "process.files.max" | ["micronaut.metrics.tags.test1" : "test1-val", "micronaut.metrics.tags.test2" : "test2-val"]
"dotted" | "micronaut.metrics.common-tags" | "process.files.open" | ["micronaut.metrics.common-tags.test1": "test1-val", "micronaut.metrics.common-tags.test2": "test2-val"]
"dotted" | "micronaut.metrics.common-tags" | "process.files.max" | ["micronaut.metrics.common-tags.test1": "test1-val", "micronaut.metrics.common-tags.test2": "test2-val"]
"map" | "micronaut.metrics.tags" | "process.files.open" | ["micronaut.metrics.tags" : [test1: "test1-val", test2: "test2-val"]]
"map" | "micronaut.metrics.tags" | "process.files.max" | ["micronaut.metrics.tags" : [test1: "test1-val", test2: "test2-val"]]
"map" | "micronaut.metrics.common-tags" | "process.files.open" | ["micronaut.metrics.common-tags" : [test1: "test1-val", test2: "test2-val"]]
"map" | "micronaut.metrics.common-tags" | "process.files.max" | ["micronaut.metrics.common-tags" : [test1: "test1-val", test2: "test2-val"]]
}

@Unroll
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ class MeterRegistryConfigurerOrderSpec extends Specification {
void "verify beans created by in correct order"() {
when:
ApplicationContext ctx = ApplicationContext.run([
"test.properties.enabled": true,
"micronaut.metrics.tags.test": "test"
"test.properties.enabled": true
])

List<MeterRegistryConfigurer> configurerList = ctx.getBeansOfType(MeterRegistryConfigurer)

then:
configurerList.size() == 3
configurerList.any { it.getClass() == CompositeMeterRegistryConfigurer.class }
configurerList.last().getClass() == CompositeMeterRegistryConfigurer.class

cleanup:
Expand Down
12 changes: 12 additions & 0 deletions src/main/docs/guide/metricsConcepts.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ include::{metricscore}/aggregator/MeterRegistryConfigurer.java[]
include::{testsmetricscore}/SimpleMeterRegistryConfigurer.java[]
----

Micronaut can also add common tags to every meter via configuration. The legacy `micronaut.metrics.tags.*` prefix remains supported, and `micronaut.metrics.common-tags.*` is available as a clearer alias.
When both prefixes configure the same tag key, `micronaut.metrics.common-tags.*` takes precedence.

[configuration]
----
micronaut:
metrics:
common-tags:
application: ${micronaut.application.name}
region: us-east-1
----

=== Meter Filter

* A https://micrometer.io/docs/concepts#_meter_filters[meter filter] can be used to determine if a Meter is to be added to the registry.
Expand Down
Loading