Skip to content

Commit 2b297e4

Browse files
committed
post-review: test syntax errors
1 parent ced88cc commit 2b297e4

File tree

2 files changed

+75
-24
lines changed

2 files changed

+75
-24
lines changed

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

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,18 @@ private List<MetricAttribute> addMetricAttributes(Map<String, Object> metricAttr
138138

139139
// package protected for testing
140140
static MetricAttribute buildMetricAttribute(String key, String target) {
141+
String errorMsg =
142+
String.format("Invalid metric attribute expression for '%s' : '%s'", key, target);
143+
144+
String targetExpr = target;
141145

142146
// an optional modifier may wrap the target
143147
// - lowercase(param(STRING))
144148
boolean lowercase = false;
145-
if (target.startsWith("lowercase(")) {
149+
String lowerCaseExpr = tryParseFunction("lowercase", targetExpr, target);
150+
if (lowerCaseExpr != null) {
146151
lowercase = true;
147-
target = target.substring(10, target.lastIndexOf(')'));
152+
targetExpr = lowerCaseExpr;
148153
}
149154

150155
//
@@ -153,38 +158,62 @@ static MetricAttribute buildMetricAttribute(String key, String target) {
153158
// - beanattr(STRING)
154159
// - const(STRING)
155160
// where STRING is the name of the corresponding parameter key, attribute name,
156-
// or the direct value to use
157-
int k = target.indexOf(')');
161+
// or the constant value to use
158162

159163
MetricAttributeExtractor extractor = null;
160164

161-
// Check for one of the cases as above
162-
if (target.startsWith("param(")) {
163-
if (k > 0) {
164-
String jmxAttribute = target.substring(6, k).trim();
165-
extractor = MetricAttributeExtractor.fromObjectNameParameter(jmxAttribute);
166-
}
167-
} else if (target.startsWith("beanattr(")) {
168-
if (k > 0) {
169-
String jmxAttribute = target.substring(9, k).trim();
170-
extractor = MetricAttributeExtractor.fromBeanAttribute(jmxAttribute);
165+
String paramName = tryParseFunction("param", targetExpr, target);
166+
if (paramName != null) {
167+
extractor = MetricAttributeExtractor.fromObjectNameParameter(paramName);
168+
}
169+
170+
if (extractor == null) {
171+
String attributeName = tryParseFunction("beanattr", targetExpr, target);
172+
if (attributeName != null) {
173+
extractor = MetricAttributeExtractor.fromBeanAttribute(attributeName);
171174
}
172-
} else if (target.startsWith("const(")) {
173-
if (k > 0) {
174-
String constantValue = target.substring(6, k).trim();
175+
}
176+
177+
if (extractor == null) {
178+
String constantValue = tryParseFunction("const", targetExpr, target);
179+
if (constantValue != null) {
175180
extractor = MetricAttributeExtractor.fromConstant(constantValue);
176181
}
177182
}
178-
if (extractor != null) {
179-
if (lowercase) {
180-
extractor = MetricAttributeExtractor.toLowerCase(extractor);
181-
}
182183

183-
return new MetricAttribute(key, extractor);
184+
if (extractor == null) {
185+
// expression did not match any supported syntax
186+
throw new IllegalArgumentException(errorMsg);
187+
}
188+
189+
if (lowercase) {
190+
extractor = MetricAttributeExtractor.toLowerCase(extractor);
184191
}
192+
return new MetricAttribute(key, extractor);
193+
}
185194

186-
String msg = "Invalid metric attribute specification for '" + key + "': " + target;
187-
throw new IllegalArgumentException(msg);
195+
/**
196+
* Parses a function expression for metric attributes
197+
*
198+
* @param function function name to attempt parsing from expression
199+
* @param expression expression to parse
200+
* @param errorMsg error message to use when syntax error is present
201+
* @return {@literal null} if expression does not start with function
202+
* @throws IllegalArgumentException if expression syntax is invalid
203+
*/
204+
private static String tryParseFunction(String function, String expression, String errorMsg) {
205+
if (!expression.startsWith(function)) {
206+
return null;
207+
}
208+
String expr = expression.substring(function.length());
209+
if (expr.charAt(0) != '(' || expr.charAt(expr.length() - 1) != ')') {
210+
throw new IllegalArgumentException(errorMsg);
211+
}
212+
expr = expr.substring(1, expr.length() - 1);
213+
if (expr.isEmpty()) {
214+
throw new IllegalArgumentException(errorMsg);
215+
}
216+
return expr;
188217
}
189218

190219
private MetricAttribute buildStateMetricAttribute(String key, Map<?, ?> stateMap) {

instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/yaml/MetricStructureTest.java

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

88
import static org.assertj.core.api.Assertions.assertThat;
9+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
910
import static org.mockito.Mockito.mock;
1011
import static org.mockito.Mockito.when;
1112

@@ -16,6 +17,7 @@
1617
import javax.management.ObjectName;
1718
import org.junit.jupiter.params.ParameterizedTest;
1819
import org.junit.jupiter.params.provider.CsvSource;
20+
import org.junit.jupiter.params.provider.ValueSource;
1921

2022
public class MetricStructureTest {
2123

@@ -70,4 +72,24 @@ void metricAttribute_beanParam(String target, String value, String expectedValue
7072

7173
assertThat(ma.acquireAttributeValue(mockConnection, objectName)).isEqualTo(expectedValue);
7274
}
75+
76+
@ParameterizedTest
77+
@ValueSource(
78+
strings = {
79+
"missing(name)", // non-existing target
80+
"param()", // missing parameter
81+
"param(name)a", // something after parenthesis
82+
"lowercase()", // misng target in modifier
83+
"lowercase(param(name)", // missing parenthesis for modifier
84+
"lowercase(missing(name))", // non-existing target within modifier
85+
"lowercase(param())", // missing parameter in modifier
86+
"lowercase(param))", // missing parenthesis within modifier
87+
})
88+
void invalidTargetSyntax(String target) {
89+
assertThatThrownBy(() -> MetricStructure.buildMetricAttribute("metric_attribute", target))
90+
.isInstanceOf(IllegalArgumentException.class)
91+
.describedAs(
92+
"exception should be thrown with original expression to help end-user understand the syntax error")
93+
.hasMessageContaining(target);
94+
}
7395
}

0 commit comments

Comments
 (0)