Skip to content

Commit 376acc5

Browse files
committed
fix state metric detection
1 parent 6551200 commit 376acc5

File tree

3 files changed

+81
-40
lines changed

3 files changed

+81
-40
lines changed

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ AttributeInfo getAttributeInfo(MBeanServerConnection connection, ObjectName obje
151151

152152
// Verify correctness of configuration by attempting to extract the metric value.
153153
// The value will be discarded, but its type will be checked.
154-
Object sampleValue = extractAttributeValue(connection, objectName, logger);
154+
Object sampleValue = getSampleValue(connection, objectName);
155155

156156
// Only numbers can be used to generate metric values
157157
if (sampleValue instanceof Number) {
@@ -194,6 +194,11 @@ AttributeInfo getAttributeInfo(MBeanServerConnection connection, ObjectName obje
194194
return null;
195195
}
196196

197+
@Nullable
198+
protected Object getSampleValue(MBeanServerConnection connection, ObjectName objectName) {
199+
return extractAttributeValue(connection, objectName, logger);
200+
}
201+
197202
/**
198203
* Extracts the specified attribute value. In case the value is a CompositeData, drills down into
199204
* it to find the correct singleton value (usually a Number or a String).
@@ -203,7 +208,7 @@ AttributeInfo getAttributeInfo(MBeanServerConnection connection, ObjectName obje
203208
* pattern
204209
* @param logger the logger to use, may be null. Typically we want to log any issues with the
205210
* attributes during MBean discovery, but once the attribute is successfully detected and
206-
* confirmed to be eligble for metric evaluation, any further attribute extraction
211+
* confirmed to be eligible for metric evaluation, any further attribute extraction
207212
* malfunctions will be silent to avoid flooding the log.
208213
* @return the attribute value, if found, or {@literal null} if an error occurred
209214
*/

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

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import java.util.List;
1919
import java.util.Map;
2020
import java.util.Set;
21-
import java.util.stream.Collectors;
2221
import javax.annotation.Nullable;
2322
import javax.management.MBeanServerConnection;
2423
import javax.management.MalformedObjectNameException;
@@ -173,52 +172,71 @@ public MetricDef buildMetricDef() throws Exception {
173172
StateMapping stateMapping = getEffectiveStateMapping(m, this);
174173

175174
if (stateMapping.isEmpty()) {
176-
MetricExtractor metricExtractor =
175+
metricExtractors.add(
177176
new MetricExtractor(
178-
attrExtractor, metricInfo, attributeList.toArray(new MetricAttribute[0]));
179-
metricExtractors.add(metricExtractor);
177+
attrExtractor, metricInfo, attributeList.toArray(new MetricAttribute[0])));
180178
} else {
181179

182180
// generate one metric extractor per state metric key
183181
// each metric extractor will have the state attribute replaced with a constant
184-
for (String key : stateMapping.getStateKeys()) {
185-
List<MetricAttribute> stateMetricAttributes =
186-
attributeList.stream()
187-
.map(
188-
ma -> {
189-
if (!ma.isStateAttribute()) {
190-
return ma;
191-
} else {
192-
return new MetricAttribute(
193-
ma.getAttributeName(), MetricAttributeExtractor.fromConstant(key));
194-
}
195-
})
196-
.collect(Collectors.toList());
197-
198-
BeanAttributeExtractor stateMetricExtractor =
199-
new BeanAttributeExtractor(attrExtractor.getAttributeName()) {
200-
201-
@Override
202-
protected Number extractNumericalAttribute(
203-
MBeanServerConnection connection, ObjectName objectName) {
204-
String rawStateValue = attrExtractor.extractValue(connection, objectName);
205-
String mappedStateValue = stateMapping.getStateValue(rawStateValue);
206-
return key.equals(mappedStateValue) ? 1 : 0;
207-
}
208-
};
209-
210-
metricExtractors.add(
211-
new MetricExtractor(
212-
stateMetricExtractor,
213-
metricInfo,
214-
stateMetricAttributes.toArray(new MetricAttribute[0])));
215-
}
182+
metricExtractors.addAll(
183+
createStateMappingExtractors(stateMapping, attributeList, attrExtractor, metricInfo));
216184
}
217185
}
218186

219187
return new MetricDef(group, metricExtractors.toArray(new MetricExtractor[0]));
220188
}
221189

190+
private static List<MetricExtractor> createStateMappingExtractors(
191+
StateMapping stateMapping,
192+
List<MetricAttribute> attributeList,
193+
BeanAttributeExtractor attrExtractor,
194+
MetricInfo metricInfo) {
195+
196+
List<MetricExtractor> extractors = new ArrayList<>();
197+
for (String key : stateMapping.getStateKeys()) {
198+
List<MetricAttribute> stateMetricAttributes = new ArrayList<>();
199+
200+
for (MetricAttribute ma : attributeList) {
201+
// replace state metric attribute with constant key value
202+
if (!ma.isStateAttribute()) {
203+
stateMetricAttributes.add(ma);
204+
} else {
205+
stateMetricAttributes.add(
206+
new MetricAttribute(
207+
ma.getAttributeName(), MetricAttributeExtractor.fromConstant(key)));
208+
}
209+
}
210+
211+
BeanAttributeExtractor stateMetricExtractor =
212+
new BeanAttributeExtractor(attrExtractor.getAttributeName()) {
213+
214+
@Override
215+
protected Object getSampleValue(
216+
MBeanServerConnection connection, ObjectName objectName) {
217+
// metric actual type is sampled in the discovery process, so we have to
218+
// make this extractor as extracting integers.
219+
return 0;
220+
}
221+
222+
@Override
223+
protected Number extractNumericalAttribute(
224+
MBeanServerConnection connection, ObjectName objectName) {
225+
String rawStateValue = attrExtractor.extractValue(connection, objectName);
226+
String mappedStateValue = stateMapping.getStateValue(rawStateValue);
227+
return key.equals(mappedStateValue) ? 1 : 0;
228+
}
229+
};
230+
231+
extractors.add(
232+
new MetricExtractor(
233+
stateMetricExtractor,
234+
metricInfo,
235+
stateMetricAttributes.toArray(new MetricAttribute[0])));
236+
}
237+
return extractors;
238+
}
239+
222240
private static List<MetricAttribute> combineMetricAttributes(
223241
List<MetricAttribute> ownAttributes, List<MetricAttribute> metricAttributes) {
224242

instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/engine/RuleParserTest.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import static org.assertj.core.api.Assertions.assertThat;
1212
import static org.assertj.core.api.Assertions.entry;
13+
import static org.mockito.Mockito.mock;
1314
import static org.mockito.Mockito.when;
1415

1516
import io.opentelemetry.instrumentation.jmx.yaml.JmxConfig;
@@ -21,12 +22,13 @@
2122
import java.nio.charset.StandardCharsets;
2223
import java.util.List;
2324
import java.util.Map;
25+
import javax.management.MBeanAttributeInfo;
26+
import javax.management.MBeanInfo;
2427
import javax.management.MBeanServerConnection;
2528
import javax.management.ObjectName;
2629
import org.junit.jupiter.api.Assertions;
2730
import org.junit.jupiter.api.BeforeAll;
2831
import org.junit.jupiter.api.Test;
29-
import org.mockito.Mockito;
3032

3133
class RuleParserTest {
3234
private static RuleParser parser;
@@ -399,9 +401,17 @@ void testStateMetricConf() throws Exception {
399401
assertThat(metric.getMetricAttribute()).containsEntry("state", "statekey()");
400402

401403
ObjectName objectName = new ObjectName(jmxRule.getBean());
402-
MBeanServerConnection mockConnection = Mockito.mock(MBeanServerConnection.class);
404+
MBeanServerConnection mockConnection = mock(MBeanServerConnection.class);
405+
406+
// mock attribute value
403407
when(mockConnection.getAttribute(objectName, "jmxStateAttribute")).thenReturn("STOPPED");
404408

409+
// mock attribute discovery
410+
MBeanInfo mockBeanInfo = mock(MBeanInfo.class);
411+
when(mockBeanInfo.getAttributes()).thenReturn(new MBeanAttributeInfo[] {
412+
new MBeanAttributeInfo("jmxStateAttribute", "java.lang.String", "", true, false, false)});
413+
when(mockConnection.getMBeanInfo(objectName)).thenReturn(mockBeanInfo);
414+
405415
MetricDef metricDef = jmxRule.buildMetricDef();
406416
assertThat(metricDef.getMetricExtractors())
407417
.hasSize(3)
@@ -418,6 +428,14 @@ void testStateMetricConf() throws Exception {
418428

419429
BeanAttributeExtractor attributeExtractor = me.getMetricValueExtractor();
420430
assertThat(attributeExtractor).isNotNull();
431+
assertThat(attributeExtractor.getSampleValue(null, null))
432+
.describedAs("sampled value must be an integer")
433+
.isInstanceOf(Integer.class);
434+
435+
assertThat(attributeExtractor.getAttributeInfo(mockConnection, objectName))
436+
.describedAs("attribute info must be provided as a regular int metric")
437+
.isNotNull();
438+
421439
int expectedValue = stateAttributeValue.equals("failed") ? 1 : 0;
422440
Number extractedValue =
423441
attributeExtractor.extractNumericalAttribute(mockConnection, objectName);

0 commit comments

Comments
 (0)