micrometer-metrics / micrometer-metrics/micrometer

Alternative approach to build metric names from tags in Dropwizard and other path-based backends

Open
#620 9 comments 8 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Java
Stars
4.9k
Forks
1.2k
Avg merge
8h 56m
Merged PRs (30d)
87

Description

Hello, I'd like to propose an alternative way to create metric names in backends like Dropwizard, which don't support tags natively. Either to consider implementing it in Micrometer or as a suggestion for other developers on how to solve this problem in their systems.

We've been using Dropwizard metrics reported to Graphite in our company for a couple of years. Now we're moving to Micrometer as part of Spring Boot 2 migration, also adding Prometheus as an alternative storage. I describe problems we faced and our solution.

Issues when migrating to Micrometer

One of our concerns during migration was not to change Graphite metric paths when migrating from Spring Boot 1 on Dropwizard to Spring Boot 2 on Micrometer. Some exemplary metric paths were:

# at Graphite before Micrometer
process.jvm.memory.heap.used
process.jvm.memory.non-heap.used
api-requests.SomeController.someHandler.GET.200
api-requests.SomeController.someHandler.GET.500

Introducing tags in vanilla Micrometer results in the following:

# at Prometheus on Micrometer, great
process_jvm_memory_used_bytes {area=heap|non-heap}
api_requests_seconds {controller=SomeController, handler=someHandler, method=GET, code=200|500}
# at Graphite on Micrometer, totally different tree
process.jvm.memory.area.heap.used
process.jvm.memory.area.non-heap.used
api-requests.code.200.controller.SomeController.handler.someHandler.method.GET
api-requests.code.500.controller.SomeController.handler.someHandler.method.GET

Which brings a couple of problems:

  1. Graphite metric tree is different, preventing a smooth migration to Spring Boot 2.
  2. Metric tree structure emerges from tags alphabetic order, no longer follows the logical order of controller -> handler -> method -> code (captured in #595).
  3. Metric paths are much longer – api-requests.SomeController.someHandler.GET.200 is clear enough, we don't need controller, handler, method and code segments.
  4. Whenever a new tag is added the metric tree can get changed depending on alphabetic order.

Controlling path encoding with placeholders

Our approach is to give control over creating the metric path to the developer instead of relying on encoding logic in Micrometer. We provide our own HierarchicalNameMapper and PrometheusNamingConvention implementations which support placeholders in metric names:

meterRegistry.gauge("process.jvm.memory.{area}.used", Tags.of("area", "heap")), ...);
meterRegistry.gauge("process.jvm.memory.{area}.used", Tags.of("area", "non-heap")), ...);

metricRegistry.timer(
    "api-requests.{controller}.{handler}.{method}.{code}", 
    "controller", "SomeController", "handler", "someHandler", "method", "GET", "code", "200"
).record(...);

Our HierarchicalNameMapper implementation replaces all placeholders with their matching tags, mapping to our previous Graphite structure:

# at Graphite on Micrometer, with placeholders resolved
process.jvm.memory.heap.used
process.jvm.memory.non-heap.used
api-requests.SomeController.someHandler.GET.200

Our PrometheusNamingConvention removes all placeholders from metric name, mapping to the same name as just after introducing Micrometer:

# at Prometheus on Micrometer, with placeholders stripped
process_jvm_memory_used_bytes {area=heap|non-heap}
api_requests_seconds {controller=SomeController, handler=someHandler, method=GET, code=200}

Where to use placeholders

We use placeholders in all our code that is expected to support both Graphite and Prometheus. Either:

  • Library code, e.g. memory metrics.
  • Application code in process of migration from Graphite to Prometheus.

If an application doesn't need to report to Graphite and Prometheus simultaneously – either it's not using Prometheus yet or it's already completely migrated from Graphite – then placeholders are not required. When using Graphite only the application can define its metric names explicitly and ignore the tags argument. When using Prometheus only the application can use Micrometer API as it was designed.

Adoption

We started adopting this solution in our ~400 microservices stack. We register all our metrics from internal libraries using the placeholders mechanism and have a few services in the process of migration from Graphite to Prometheus also registering their metrics this way.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Review the proposed HierarchicalNameMapper and PrometheusNamingConvention implementations and the placeholder examples in the issue. Determine whether placeholder-based naming should be implemented in Micrometer or documented as an external approach, and consider the Graphite and Prometheus naming behavior shown as the completion criteria.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
observability-sre
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.