Skip to content

Commit 1bc15f0

Browse files
SylvainJugejaydelucalauritrenovate[bot]AlixBa
authored
jmx metrics: require explicit unit in yaml (open-telemetry#13796)
Co-authored-by: Jay DeLuca <[email protected]> Co-authored-by: Lauri Tulmin <[email protected]> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Alix <[email protected]> Co-authored-by: Steve Rao <[email protected]> Co-authored-by: Gregor Zeitlinger <[email protected]>
1 parent b658c65 commit 1bc15f0

File tree

8 files changed

+141
-96
lines changed

8 files changed

+141
-96
lines changed

instrumentation/jmx-metrics/README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ In the particular case where only two values are defined, we can simplify furthe
287287
off: '*'
288288
```
289289

290+
State metrics do not have a unit (nor source unit) and use an empty string `""` as unit.
291+
290292
### Metric attributes modifiers
291293

292294
JMX attributes values may require modification or normalization in order to fit semantic conventions.
@@ -382,21 +384,21 @@ rules: # start of list of configuration rules
382384
<ATTRIBUTE2>: beanattr(<ATTR>) # <ATTR> is used as the MBean attribute name to extract the value
383385
<ATTRIBUTE3>: const(<CONST>) # <CONST> is used as a constant
384386
prefix: <METRIC_NAME_PREFIX> # optional, useful for avoiding specifying metric names below
385-
unit: <UNIT> # optional, redefines the default unit for the whole rule
387+
unit: <UNIT> # optional, defines the default unit for the whole rule. must be defined at metric level if not set here
386388
type: <TYPE> # optional, redefines the default type for the whole rule
387389
dropNegativeValues: <BOOL> # optional, redefines if negative values are dropped for the whole rule
388390
mapping:
389391
<BEANATTR1>: # an MBean attribute name defining the metric value
390392
metric: <METRIC_NAME1> # metric name will be <METRIC_NAME_PREFIX><METRIC_NAME1>
391393
type: <TYPE> # optional, the default type is gauge
392394
desc: <DESCRIPTION1> # optional
393-
unit: <UNIT1> # optional
395+
unit: <UNIT1> # optional if unit is already defined on rule level (redefines it in such a case), required otherwise
394396
dropNegativeValues: <BOOL> # optional, defines if negative values are dropped for the metric
395397
metricAttribute: # optional, will be used in addition to the shared metric attributes above
396398
<ATTRIBUTE3>: const(<STR>) # direct value for the metric attribute
397399
<BEANATTR2>: # use a.b to get access into CompositeData
398400
metric: <METRIC_NAME2> # optional, the default is the MBean attribute name
399-
unit: <UNIT2> # optional
401+
unit: <UNIT2> # optional if unit is already defined on rule level (redefines it in such a case), required otherwise
400402
<BEANATTR3>: # metric name will be <METRIC_NAME_PREFIX><BEANATTR3>
401403
<BEANATTR4>: # metric name will be <METRIC_NAME_PREFIX><BEANATTR4>
402404
- beans: # alternatively, if multiple object names are needed

instrumentation/jmx-metrics/library/src/main/java/io/opentelemetry/instrumentation/jmx/engine/BeanGroup.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
package io.opentelemetry.instrumentation.jmx.engine;
77

88
import java.util.ArrayList;
9-
import java.util.Collections;
109
import java.util.List;
1110
import javax.annotation.Nullable;
1211
import javax.management.MalformedObjectNameException;
@@ -34,10 +33,6 @@ private BeanGroup(@Nullable QueryExp queryExp, List<ObjectName> namePatterns) {
3433
this.namePatterns = namePatterns;
3534
}
3635

37-
public static BeanGroup forSingleBean(String bean) throws MalformedObjectNameException {
38-
return new BeanGroup(null, Collections.singletonList(new ObjectName(bean)));
39-
}
40-
4136
public static BeanGroup forBeans(List<String> beans) throws MalformedObjectNameException {
4237
List<ObjectName> list = new ArrayList<>();
4338
for (String name : beans) {

instrumentation/jmx-metrics/library/src/main/java/io/opentelemetry/instrumentation/jmx/yaml/JmxRule.java

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,22 @@ public class JmxRule extends MetricStructure {
4141
// ATTRIBUTE3:
4242
// METRIC_FIELDS3
4343
// The parser never calls setters for these fields with null arguments
44-
@Nullable private String bean;
44+
4545
private List<String> beans;
4646
@Nullable private String prefix;
4747
private Map<String, Metric> mapping;
4848

49-
@Nullable
50-
public String getBean() {
51-
return bean;
52-
}
53-
54-
public void setBean(String bean) {
55-
this.bean = validateBean(bean);
56-
}
57-
5849
public List<String> getBeans() {
5950
return beans;
6051
}
6152

53+
public void addBean(String bean) {
54+
if (beans == null) {
55+
beans = new ArrayList<>();
56+
}
57+
beans.add(validateBean(bean));
58+
}
59+
6260
private static String validateBean(String name) {
6361
try {
6462
String trimmed = name.trim();
@@ -70,14 +68,6 @@ private static String validateBean(String name) {
7068
}
7169
}
7270

73-
public void setBeans(List<String> beans) {
74-
List<String> list = new ArrayList<>();
75-
for (String name : beans) {
76-
list.add(validateBean(name));
77-
}
78-
this.beans = list;
79-
}
80-
8171
public void setPrefix(String prefix) {
8272
this.prefix = validatePrefix(prefix.trim());
8373
}
@@ -128,13 +118,10 @@ private static Map<String, Metric> validateAttributeMapping(Map<String, Metric>
128118
*/
129119
public MetricDef buildMetricDef() throws Exception {
130120
BeanGroup group;
131-
if (bean != null) {
132-
group = BeanGroup.forSingleBean(bean);
133-
} else if (beans != null && !beans.isEmpty()) {
134-
group = BeanGroup.forBeans(beans);
135-
} else {
121+
if (beans == null || beans.isEmpty()) {
136122
throw new IllegalStateException("No ObjectName specified");
137123
}
124+
group = BeanGroup.forBeans(beans);
138125

139126
if (mapping == null || mapping.isEmpty()) {
140127
throw new IllegalStateException("No MBean attributes specified");
@@ -157,9 +144,7 @@ public MetricDef buildMetricDef() throws Exception {
157144
getUnit(),
158145
getMetricType());
159146
} else {
160-
metricInfo =
161-
m.buildMetricInfo(
162-
prefix, niceAttributeName, getSourceUnit(), getUnit(), getMetricType());
147+
metricInfo = m.buildMetricInfo(niceAttributeName, this);
163148
}
164149

165150
List<MetricAttribute> ownAttributes = getAttributeList();
@@ -216,13 +201,13 @@ private static List<MetricExtractor> createStateMappingExtractors(
216201
BeanAttributeExtractor extractor =
217202
BeanAttributeExtractor.forStateMetric(attrExtractor, key, stateMapping);
218203

219-
// state metric are always up/down counters
204+
// state metric are always up/down counters, empty '' unit and no source unit
220205
MetricInfo stateMetricInfo =
221206
new MetricInfo(
222207
metricInfo.getMetricName(),
223208
metricInfo.getDescription(),
224-
metricInfo.getSourceUnit(),
225-
metricInfo.getUnit(),
209+
null,
210+
"",
226211
MetricInfo.Type.UPDOWNCOUNTER);
227212

228213
extractors.add(new MetricExtractor(extractor, stateMetricInfo, stateMetricAttributes));

instrumentation/jmx-metrics/library/src/main/java/io/opentelemetry/instrumentation/jmx/yaml/Metric.java

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ public class Metric extends MetricStructure {
2121
@Nullable private String metric;
2222
@Nullable private String desc;
2323

24+
public Metric() {}
25+
2426
@Nullable
2527
public String getMetric() {
2628
return metric;
@@ -46,13 +48,16 @@ public void setDesc(String desc) {
4648
this.desc = desc.trim();
4749
}
4850

49-
MetricInfo buildMetricInfo(
50-
@Nullable String prefix,
51-
String attributeName,
52-
String defaultSourceUnit,
53-
String defaultUnit,
54-
MetricInfo.Type defaultType) {
51+
/**
52+
* @param attributeName attribute name
53+
* @param parentJmxRule parent JMX rule where metric is defined
54+
* @return metric info
55+
* @throws IllegalStateException when effective metric definition is invalid
56+
*/
57+
MetricInfo buildMetricInfo(String attributeName, JmxRule parentJmxRule) {
5558
String metricName;
59+
60+
String prefix = parentJmxRule.getPrefix();
5661
if (metric == null) {
5762
metricName = prefix == null ? attributeName : (prefix + attributeName);
5863
} else {
@@ -61,17 +66,30 @@ MetricInfo buildMetricInfo(
6166

6267
MetricInfo.Type metricType = getMetricType();
6368
if (metricType == null) {
64-
metricType = defaultType;
69+
metricType = parentJmxRule.getMetricType();
70+
}
71+
if (metricType == null) {
72+
metricType = MetricInfo.Type.GAUGE;
6573
}
6674

6775
String sourceUnit = getSourceUnit();
6876
if (sourceUnit == null) {
69-
sourceUnit = defaultSourceUnit;
77+
sourceUnit = parentJmxRule.getSourceUnit();
7078
}
7179

72-
String unit = getUnit();
73-
if (unit == null) {
74-
unit = defaultUnit;
80+
String unit;
81+
if (!getStateMapping().isEmpty()) {
82+
// state metrics do not have a unit, use empty string
83+
unit = "";
84+
} else {
85+
unit = getUnit();
86+
if (unit == null) {
87+
unit = parentJmxRule.getUnit();
88+
}
89+
if (unit == null) {
90+
throw new IllegalStateException(
91+
String.format("Metric unit is required for metric '%s'", metricName));
92+
}
7593
}
7694

7795
return new MetricInfo(metricName, desc, sourceUnit, unit, metricType);

instrumentation/jmx-metrics/library/src/main/java/io/opentelemetry/instrumentation/jmx/yaml/MetricStructure.java

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
*
2525
* <p>Known subclasses are {@link JmxRule} and {@link Metric}.
2626
*/
27-
abstract class MetricStructure {
27+
public abstract class MetricStructure {
2828

2929
// Used by the YAML parser
3030
//
@@ -47,8 +47,9 @@ abstract class MetricStructure {
4747
private Map<String, Object> metricAttribute;
4848
private StateMapping stateMapping = StateMapping.empty();
4949
private static final String STATE_MAPPING_DEFAULT = "*";
50-
private String sourceUnit;
51-
private String unit;
50+
51+
@Nullable private String sourceUnit;
52+
@Nullable private String unit;
5253
@Nullable private Boolean dropNegativeValues;
5354

5455
private MetricInfo.Type metricType;
@@ -59,9 +60,14 @@ abstract class MetricStructure {
5960
void setType(String t) {
6061
// Do not complain about case variations
6162
t = t.trim().toUpperCase(Locale.ROOT);
62-
this.metricType = MetricInfo.Type.valueOf(t);
63+
try {
64+
this.metricType = MetricInfo.Type.valueOf(t);
65+
} catch (IllegalArgumentException e) {
66+
throw new IllegalArgumentException("Invalid metric type: " + t, e);
67+
}
6368
}
6469

70+
@Nullable
6571
public String getSourceUnit() {
6672
return sourceUnit;
6773
}
@@ -70,12 +76,13 @@ public void setSourceUnit(String unit) {
7076
this.sourceUnit = validateUnit(unit.trim());
7177
}
7278

79+
@Nullable
7380
public String getUnit() {
7481
return unit;
7582
}
7683

7784
public void setUnit(String unit) {
78-
this.unit = validateUnit(unit.trim());
85+
this.unit = unit.trim();
7986
}
8087

8188
public void setDropNegativeValues(Boolean dropNegativeValues) {
@@ -167,7 +174,7 @@ static MetricAttribute buildMetricAttribute(String key, String target) {
167174
// an optional modifier may wrap the target
168175
// - lowercase(param(STRING))
169176
boolean lowercase = false;
170-
String lowerCaseExpr = tryParseFunction("lowercase", targetExpr, target);
177+
String lowerCaseExpr = tryParseFunction("lowercase", targetExpr, errorMsg);
171178
if (lowerCaseExpr != null) {
172179
lowercase = true;
173180
targetExpr = lowerCaseExpr;
@@ -183,20 +190,20 @@ static MetricAttribute buildMetricAttribute(String key, String target) {
183190

184191
MetricAttributeExtractor extractor = null;
185192

186-
String paramName = tryParseFunction("param", targetExpr, target);
193+
String paramName = tryParseFunction("param", targetExpr, errorMsg);
187194
if (paramName != null) {
188195
extractor = MetricAttributeExtractor.fromObjectNameParameter(paramName);
189196
}
190197

191198
if (extractor == null) {
192-
String attributeName = tryParseFunction("beanattr", targetExpr, target);
199+
String attributeName = tryParseFunction("beanattr", targetExpr, errorMsg);
193200
if (attributeName != null) {
194201
extractor = MetricAttributeExtractor.fromBeanAttribute(attributeName);
195202
}
196203
}
197204

198205
if (extractor == null) {
199-
String constantValue = tryParseFunction("const", targetExpr, target);
206+
String constantValue = tryParseFunction("const", targetExpr, errorMsg);
200207
if (constantValue != null) {
201208
extractor = MetricAttributeExtractor.fromConstant(constantValue);
202209
}

instrumentation/jmx-metrics/library/src/main/java/io/opentelemetry/instrumentation/jmx/yaml/RuleParser.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,11 @@ private static JmxRule parseJmxRule(Map<String, Object> ruleYaml) {
7575

7676
String bean = (String) ruleYaml.remove("bean");
7777
if (bean != null) {
78-
jmxRule.setBean(bean);
78+
jmxRule.addBean(bean);
7979
}
8080
List<String> beans = (List<String>) ruleYaml.remove("beans");
8181
if (beans != null) {
82-
jmxRule.setBeans(beans);
82+
beans.forEach(jmxRule::addBean);
8383
}
8484
String prefix = (String) ruleYaml.remove("prefix");
8585
if (prefix != null) {
@@ -153,7 +153,7 @@ private static void parseMetricStructure(
153153

154154
private static void failOnExtraKeys(Map<String, Object> yaml) {
155155
if (!yaml.isEmpty()) {
156-
throw new IllegalArgumentException("Unrecognized keys found: " + yaml.keySet());
156+
throw new IllegalArgumentException("Unrecognized key(s) found: " + yaml.keySet());
157157
}
158158
}
159159

0 commit comments

Comments
 (0)