英文:
What is a clean way of setting description for dynamic metrics in Micrometer?
问题
这可能听起来有些愚蠢,也可能有一个非常简单的答案,但是我还没有找到任何答案。
Micrometer 为每种度量类型都有 Builder 类,格式如下:
<MetricType>.builder(<metricName>).description(<description>).tags(<tagsArray>).register(<meterRegistry>)
以计数器度量为例。可以按如下方式递增:
<meterRegistry>.Counter(<counterName>, <标签及其值的成对对> ).increment()
当你想设置描述时就会出现问题。如果标签的值是动态的,即这些值在运行时决定,或者如果标签的可能组合太多,比如超过 10 个,那么如何设置描述呢?
我唯一能想到的解决方案是在启动度量服务时在某处存储描述。然后在递增方法周围创建一个包装器。现在,当用户调用包装器时,调用注册方法(使用存储的描述),然后再调用递增方法。据我所知,如果不存在则注册方法会创建一个新的度量,否则会返回现有的度量。
在 Prometheus 中,相同的问题可以很容易地解决,因为他们有一个注册方法,可以让你设置所有标签(仅限键)和描述。然后递增方法只需要这些标签的值。
Micrometer 是否提供类似的功能?为一个简单的应用创建一个包装器似乎非常不方便。考虑到 Micrometer 已经是 Prometheus(和其他仪表化服务)的一个包装器,我不想再创建一个包装器。
如果问题不清楚,以下示例可能会有所帮助:
我们来看一个带有标签 type
的计数器度量 requests
,其值可以是 alpha,beta,gamma
,其描述为 跟踪来自客户端的请求
。
那么作为解决方案,我可以在启动度量服务时的注册函数中添加以下代码:
Counter.builder("requests")
.description("跟踪来自客户端的请求")
.tags("type", "alpha")
.register(meterRegistry);
然后无论在哪里,我都可以运行以下代码来使用这个度量:
meterRegistry.Counter("request", "type", <valueOfType>).increment()
我的担忧是,在这个解决方案中,我只初始化了alpha
类型,这似乎不正确,因为只有这一种类型。另一个选项是注册所有三种类型,然而,这假设所有可能的值在运行时都是已知的。如果这些值是在运行时发现的,这个解决方案完全失败,现在你必须使用上面描述的包装器解决方案,或者干脆忽略描述。
英文:
This may sound silly and may have a really simple answer however I haven't found any yet.
Micrometer has Builder classes for each of the metric type of the following form:
<MetricType>.builder(<metricName>).description(<description>).tags(<tagsArray>).register(<meterRegistry>)
Lets take Counter metrics as an example. It can be incremented as follows:
<meterRegistry>.Counter(<counterName>, <Pairs of labels and their values>).increment()
The problem arises when you want to set the description. If the values of labels are dynamic in nature i.e the values are decided at run time or if the possible combination of labels is too many like 10+ then how are you supposed to set the description?
The only solution I could think of is to store the description somewhere when starting the metric service. Then create a wrapper around the increment method. Now, when the user calls the wrapper, call the register method (with the stored description) and then the increment method. And afaik the register method creates a new metric if it doesn't exist otherwise returns the existing metric.
The same problem could be easily resolved in Prometheus as they have a register method that lets you set all the labels (i.e keys only) and the description. The increment method then only requires the values of those labels.
Does micrometer provide similar functionality? Creating a wrapper for a simple application seems highly inconvenient. And considering that micrometer is already a wrapper around Prometheus (and other instrumenting services), I don't want to create a wrapper around it as well.
If the question doesn't make sense the following example may help:
Let's consider a Counter metric requests
with label type
whose value can be alpha, beta, gamma
with the description being tracks requests from clients
Then as a solution I can add the following line in register function called when starting the metric service.
Counter.builder("requests")
.description("tracks requests from clients)
.tags("type", "alpha")
.register(meterRegistry);
And then wherever I want to use this metric I can run the following code:
meterRegistry.Counter("request", "type", <valueOfType>).increment()
My concern is that in this solution I am initiating only the alpha
type which doesn't seem right since that is the only type. Another option would be to register all 3 types, however, this is assuming all the possible values are known during runtime. If the values are discovered at runtime, this solution fails completely and now you have to use the solution with wrappers as described above or forget description altogether.
答案1
得分: 2
我遇到了类似的问题,当尝试使用计数器和单个标签的多个值进行工作时。
以下是我的解决方案:
@RestController
public class HelloController {
private final Counter.Builder counter;
private MeterRegistry registry;
public HelloController(MeterRegistry registry) {
this.counter = Counter.builder("counter").description("A description");
this.registry = registry;
}
@GetMapping("/count")
public String count1() {
counter.tag("tag2", "value1").register(registry).increment();
// 我没有在这里提供更多的描述,但是我每次调用时都需要使用 .register('registry')。
return "Count";
}
@GetMapping("/count2")
public String count2() {
counter.tag("tag2", "value2").register(registry).increment();
return "Count";
}
}
- 注意:我必须调用
counter...register(registry)
以便拥有单个描述和多个标签值。
英文:
I came across a similar problem, when trying to work with Counters and multiple value for a single Tag.
Here is my solution:
@RestController
public class HelloController {
private final Counter.Builder counter;
private MeterRegistry registry;
public HelloController(MeterRegistry registry) {
this.counter = Counter.builder("counter").description("A description");
this.registry = registry;
}
@GetMapping("/count")
public String count1() {
counter.tag("tag2", "value1").register(registry).increment();
// I don't provide more description here, but I need to use .register('registry') at each call.
return "Count";
}
@GetMapping("/count2")
public String count2() {
counter.tag("tag2", "value2").register(registry).increment();
return "Count";
}
}
- Note : I have to call
counter...register(registry)
in order to have a single description, and multiple value for tags.
答案2
得分: 1
我曾经和您遇到相同的问题,所以我找到了这篇有用的帖子,但是...
我不喜欢这个解决方案。
我认为更好的方法是使用占位符,就像这个。
所以也许可以定义一个HierarchicalNameMapper
,在标签键与其匹配时替换标签占位符,这是个不错的选择。
现在,动态标记计量的问题似乎已经解决了,但是描述仍然是一个问题。所以我也没有使用这个解决方案。
我的最终解决方案是将Counters
、Gauges
和其他计量器存储在映射中,并通过Id(名称和标签)进行检索。
在测量过程中提供了描述,我在查找映射,如果具有该Id的度量标准已经存在,它将会被增加(或减少),否则将会被创建,其描述基于有意义的度量标准名称和标签键-值。
/**
* 使用输入标签增加{@link Counter}。如果不存在,将会创建。
*/
public void increment(String description, String unit, Tags tags) throws MetricsException {
this.counter(description, unit, tags).increment();
}
/**
* 搜索具有输入标签的计数器。如果不存在,将会创建。
*/
private Counter counter(String description, String unit, Tags tags) {
final String counterId = name.concat(tags.toString());
Counter counter = counters.get(counterId);
if (counter == null) {
counter = Counter.builder(name)
.description(description)
.baseUnit(unit)
.tags(tags)
.register(registry);
counters.put(counterId, counter);
}
return counter;
}
> 其中:
> registry
是 MeterRegistry
字段值
> counters
是一个 Map<String, Counter>
如果您对内存使用感到困扰,我使用了一个Java插装代理来检查Map。这是一个示例:这里。
映射的一个条目大约是1KB,所以您应该有大约1000个标签名称组合才能使用1MB
英文:
I was struggle with your same problem, so I found this helpful post, but...
I didn't liked this solution.
I think te better approach is a placeholder one, like this.
So maybe define a HierarchicalNameMapper
that will replace tag placeholder when tag keys match it, it's good choice.
So now the problem of dynamic tagging meters seems to be solved, but the description still remains a problem. So I didn't use this solution too.
My final solution was to store Counters
Gauges
and other Meters in maps and retrieving by Ids (Name and tags).
The description was provided during measurements, where I lookup the map and if a metric with that Id already exists will be incremented (or decremented) otherwise will be created with a description based on a meaningful metric name and tag keys-values.
/**
* Increment the {@link Counter} with input tags. If it does not exists it will be created.
*/
public void increment(String description, String unit, Tags tags) throws MetricsException {
this.counter(description, unit, tags).increment();
}
/**
* Search for a counter with input tags. If it does not exists it will be created.
*/
private Counter counter(String description, String unit, Tags tags) {
final String counterId = name.concat(tags.toString());
Counter counter = counters.get(counterId);
if (counter == null) {
counter = Counter.builder(name)
.description(description)
.baseUnit(unit)
.tags(tags)
.register(registry);
counters.put(counterId, counter);
}
return counter;
}
> Where:
> registry
is MeterRegistry
field value
> counters
is a Map<String, Counter>
If you are struggling about memory usage, I used a Java instrumentation agent to inspect the Map. This one.
An entry of the map is like 1KB, so you should have like 1000 tag-names combination to use 1MB
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论