Micrometer: Add support to assign dynamic tag into existing metric

Created on 4 Apr 2018  路  12Comments  路  Source: micrometer-metrics/micrometer

From slack thread it would be nice to have ability to define only dynamic part in metric while metric with static tags can be defined only once:

registry.timer("my.timer", "static", "tag", "response", response.status()).record(1);

->

Timer myTimer = registry.timer("my.timer", "static", "tag")
...
myTimer.withTags("response", response.status()).record(1);

where withTags returns new instance of timer with additional tags attached.

doc-update

Most helpful comment

@zeratul021 It is possible to use dynamic tags now though, simply by registering it as you use it. If the meter already exists, Micrometer will return the existing one.

All 12 comments

Ingenious usability hack.

@jkschneider is there a recommended way of solving this issue?
E.g. I want to have counter for registered users and the dynamic tag would be their country of origin.
TIA

Sorry this won't get done for 1.1.0. It is actually much harder than it would initially seem. We don't habitually maintain a reference to the registry on each meter, so spawning a completely new metric is difficult. Also, for timers we don't tend to preserve all the options (like DistributionSummaryConfig) that we used to create a timer after it exists. Technically there is no way to go back and introduce this without a breaking API change.

@zeratul021 It is possible to use dynamic tags now though, simply by registering it as you use it. If the meter already exists, Micrometer will return the existing one.

@jkschneider thanks for the input. Yeah, I'm using it exactly like that. Seems there is nothing wrong with it for now.

@jkschneider if I am not mistaken, this pattern (register again) cannot be applied to gauges without building a new gauge every time? In opposite to the other meters, the registry returns the gauge value instead of a wrapper representing the gauge (which makes perfect sense imo).

I currently have to migrate a lot of existing, dynamic gauges and came up with this workaround to register dynamic gauges:

private final ConcurrentHashMap<Meter.Id, AtomicLong> dynamicGauges = new ConcurrentHashMap<>();
private void handleDynamicGauge(String meterName, String labelKey, String labelValue, Long snapshot) {
    Meter.Id id = new Meter.Id(meterName, Tags.of(labelKey, labelValue), null, null, GAUGE);

    dynamicGauges.compute(id, (key, current) -> {
        if (current == null) {
            AtomicLong initialValue = new AtomicLong(snapshot);
            registry.gauge(key.getName(), key.getTags(), initialValue);
            return initialValue;
        } else {
            current.set(snapshot);
            return current;
        }
    });
}

Is there any other way, to achieve dynamic gauges with micrometer instead of using my workaround? I am relying a bit on micrometer internals (Meter.Id equals/hashcode methods) and would be happy about any advice.

@u6f6o Version 1.1.0 has a MultiGauge that allows you to register several gauges with a common name and set of tags but that varies by some other set of tags.

@jkschneider thanks for pointing me in the right direction.

I needed to add a dynamic tag for all meters that have already been registered, and I ended up with following workaround - it adds the tag in getConventionTags() which is invoked from publish() of registries (e.g. ElasticMeterRegistry):

public class DynamicValueAddingMeterFilter implements MeterFilter {

    private final DynamicValueProvider dynamicValueProvider;

    @Override
    public Meter.Id map(Meter.Id id) {
        return new DynamicMeterId(id);
    }

    class DynamicMeterId extends Meter.Id {

        DynamicMeterId(Meter.Id delegateId) {
            super(delegateId.getName(),
                  Tags.of(delegateId.getTags()),
                  delegateId.getBaseUnit(),
                  delegateId.getDescription(),
                  delegateId.getType()
            );
        }

        @Override
        public List<Tag> getConventionTags(NamingConvention namingConvention) {
            List<Tag> result = super.getConventionTags(namingConvention);
            result.add(Tag.of("myDynamicTag", dynamicValueProvider.getDynamicValue())));
            return result;
        }
    }
}   

@jkschneider thanks for the input. Yeah, I'm using it exactly like that. Seems there is nothing wrong with it for now.
Hello @zeratul021 @jkschneider , I am not quite understand how to add dynamic tags. Could you kindly sharing code block I could refer to ? My scenario is, we have a set of spring cloud stream applications to process data in kafka, from one system to other. We'd like to record metrics of each batch while most of application are running always to process data. The producer produce the data with batch id. In always running application,we'd record metrics according to batch id. So we are going to change the batch id value according the message. I am not sure if that possible. I did some tests to register new counter, but the counter come with tag of "batch_id", and values of all processed batch id . I expect just get the tag of "batch_id" and value of current batch id.

Can someone post the snippet for adding the dynamic tags to the existing meter? Also, how to register and get the meter from existing list?

Was this page helpful?
0 / 5 - 0 ratings