Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JMX Metrics to OTLP & Breeze #3406

Merged
merged 44 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
3bc5624
experimental changes
harsimar Nov 9, 2023
85867db
modifying dotinjmxmetric test to use mock breeze and otlp, with a mod…
harsimar Nov 9, 2023
1b65c2d
adding thread count metric
harsimar Nov 10, 2023
84afc02
rename and refactor test based on helen's most recent otlp test pr
harsimar Nov 11, 2023
22324ff
pull from upstream and fix merge conflict
harsimar Nov 11, 2023
c189821
removing metricItems list size assertion because it is sometimes 4 in…
harsimar Nov 11, 2023
f92231a
partial restructuring changes, will complete tomorrow
harsimar Nov 16, 2023
6791fc8
restructuring done, figure out why the callback is being called 2x pe…
harsimar Nov 16, 2023
48e6123
minor, may remove some logging later
harsimar Nov 20, 2023
e7ed384
some cleanup and test changes, need to still add check for timestamp
harsimar Nov 22, 2023
e9314e9
adding more test cases
harsimar Nov 28, 2023
1a0320a
fixing tests and cleanup code
harsimar Nov 28, 2023
6136ac2
Merge branch 'main' into harskaur/jmkMetricsToOtlp
harsimar Nov 28, 2023
3720c17
fixing spotless violations
harsimar Nov 29, 2023
3590e55
more spotless apply
harsimar Nov 29, 2023
1427b4e
Fix unexpected metrics smoke test
heyams Nov 29, 2023
1f5d56a
adding schema url to meter and pr comments
harsimar Nov 30, 2023
fd4aedd
spotless
harsimar Nov 30, 2023
5640a81
pr comments
harsimar Nov 30, 2023
4c590d4
changed character replace regex
harsimar Dec 1, 2023
e6af6c7
removing schema url & pr comments
harsimar Dec 1, 2023
5499b39
adding attribute
harsimar Dec 1, 2023
3240814
spotless apply
harsimar Dec 1, 2023
375b36b
rename attribute
harsimar Dec 1, 2023
ea5787f
spotless
harsimar Dec 1, 2023
39b0932
Update internal metric name
heyams Dec 19, 2023
09edf1d
Merge branch 'main' into harskaur/jmkMetricsToOtlp
heyams Dec 19, 2023
3ca9581
Update license
heyams Dec 19, 2023
c7e5977
Merge branch 'harskaur/jmkMetricsToOtlp' of https://github.com/harsim…
heyams Dec 19, 2023
007b387
Update test
heyams Dec 19, 2023
9eebc69
Delete unused import
heyams Dec 19, 2023
18a1e92
Fix test
heyams Dec 19, 2023
ea6be27
Fix ambiguous assertion
heyams Dec 19, 2023
ec303ac
Keep old metric name for breeze endpoint
heyams Jan 3, 2024
c8cadb3
Fix smoke test
heyams Jan 3, 2024
9d21009
Merge branch 'main' into harskaur/jmkMetricsToOtlp
heyams Jan 3, 2024
d500a7f
Fix flaky tests
heyams Jan 3, 2024
3c5c04c
Fix flaky tests
heyams Jan 3, 2024
cb3b5f2
Merge branch 'harskaur/jmkMetricsToOtlp' of https://github.com/harsim…
heyams Jan 3, 2024
2c56a3e
Fix conconurrent CI tests sharing the same SET
heyams Jan 3, 2024
15ca137
Create constants
heyams Jan 10, 2024
fd95eaf
my intended formatting of this comment
harsimar Jan 10, 2024
573523a
Fix spotless
heyams Jan 10, 2024
d498d7d
Remove unnecessary outer try/catch block
heyams Jan 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ public Map<String, String> apply(ConfigProperties otelConfig) {
"applicationinsights.internal.micrometer.step.millis",
Long.toString(SECONDS.toMillis(configuration.metricIntervalSeconds)));

properties.put(
"otel.metric.export.interval",
Long.toString(SECONDS.toMillis(configuration.metricIntervalSeconds)));

enableInstrumentations(otelConfig, configuration, properties);

// enable "io.opentelemetry.sdk.autoconfigure.internal.EnvironmentResourceProvider" only. It
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@
import com.microsoft.applicationinsights.agent.internal.perfcounter.FreeMemoryPerformanceCounter;
import com.microsoft.applicationinsights.agent.internal.perfcounter.GcPerformanceCounter;
import com.microsoft.applicationinsights.agent.internal.perfcounter.JmxAttributeData;
import com.microsoft.applicationinsights.agent.internal.perfcounter.JmxMetricPerformanceCounter;
import com.microsoft.applicationinsights.agent.internal.perfcounter.JmxDataFetcher;
import com.microsoft.applicationinsights.agent.internal.perfcounter.JvmHeapMemoryUsedPerformanceCounter;
import com.microsoft.applicationinsights.agent.internal.perfcounter.OshiPerformanceCounter;
import com.microsoft.applicationinsights.agent.internal.perfcounter.PerformanceCounterContainer;
import com.microsoft.applicationinsights.agent.internal.perfcounter.ProcessCpuPerformanceCounter;
import com.microsoft.applicationinsights.agent.internal.perfcounter.ProcessMemoryPerformanceCounter;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.ObservableDoubleMeasurement;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.ArrayList;
Expand All @@ -32,6 +36,8 @@
public class PerformanceCounterInitializer {

private static final Logger logger = LoggerFactory.getLogger(PerformanceCounterInitializer.class);
private static final String METRIC_NAME_REGEXP = "[a-zA-z0-9_.-/]+";
private static final String INVALID_CHARACTER_REGEXP = "[^a-zA-z0-9_.-/]";

public static void initialize(Configuration configuration) {

Expand Down Expand Up @@ -73,59 +79,119 @@ private static boolean isAgentRunningInSandboxEnvWindows() {
* a map where the key is the Jmx object name and the value is a list of requested attributes. 2.
* Go through all the requested Jmx counters: a. If the object name is not in the map, add it with
* an empty list Else get the list b. Add the attribute to the list. 3. Go through the map For
* every entry (object name and attributes) Build a {@link JmxMetricPerformanceCounter} Register
* the Performance Counter in the {@link PerformanceCounterContainer}
* every entry (object name and attributes) to build a meter per attribute & for each meter
* register a callback to report the metric value.
*/
private static void loadCustomJmxPerfCounters(List<Configuration.JmxMetric> jmxXmlElements) {
try {
HashMap<String, Collection<JmxAttributeData>> data = new HashMap<>();
HashMap<String, Collection<JmxAttributeData>> data = new HashMap<>();

// Build a map of object name to its requested attributes
for (Configuration.JmxMetric jmxElement : jmxXmlElements) {
Collection<JmxAttributeData> collection =
data.computeIfAbsent(jmxElement.objectName, k -> new ArrayList<>());
// Build a map of object name to its requested attributes
for (Configuration.JmxMetric jmxElement : jmxXmlElements) {
Collection<JmxAttributeData> collection =
data.computeIfAbsent(jmxElement.objectName, k -> new ArrayList<>());

if (Strings.isNullOrEmpty(jmxElement.objectName)) {
try (MDC.MDCCloseable ignored = CUSTOM_JMX_METRIC_ERROR.makeActive()) {
logger.error("JMX object name is empty, will be ignored");
}
continue;
if (Strings.isNullOrEmpty(jmxElement.objectName)) {
try (MDC.MDCCloseable ignored = CUSTOM_JMX_METRIC_ERROR.makeActive()) {
logger.error("JMX object name is empty, will be ignored");
}
continue;
}

if (Strings.isNullOrEmpty(jmxElement.attribute)) {
try (MDC.MDCCloseable ignored = CUSTOM_JMX_METRIC_ERROR.makeActive()) {
logger.error("JMX attribute is empty for '{}'", jmxElement.objectName);
}
continue;
if (Strings.isNullOrEmpty(jmxElement.attribute)) {
try (MDC.MDCCloseable ignored = CUSTOM_JMX_METRIC_ERROR.makeActive()) {
logger.error("JMX attribute is empty for '{}'", jmxElement.objectName);
}
continue;
}

if (Strings.isNullOrEmpty(jmxElement.name)) {
try (MDC.MDCCloseable ignored = CUSTOM_JMX_METRIC_ERROR.makeActive()) {
logger.error("JMX name is empty for '{}', will be ignored", jmxElement.objectName);
}
continue;
if (Strings.isNullOrEmpty(jmxElement.name)) {
try (MDC.MDCCloseable ignored = CUSTOM_JMX_METRIC_ERROR.makeActive()) {
logger.error("JMX name is empty for '{}', will be ignored", jmxElement.objectName);
}
continue;
}

collection.add(new JmxAttributeData(jmxElement.name, jmxElement.attribute));
}

createMeterPerAttribute(data);
}

collection.add(new JmxAttributeData(jmxElement.name, jmxElement.attribute));
// Create a meter for each attribute & declare the callback that reports the metric in the meter.
private static void createMeterPerAttribute(
Map<String, Collection<JmxAttributeData>> objectAndAttributesMap) {
for (Map.Entry<String, Collection<JmxAttributeData>> entry :
objectAndAttributesMap.entrySet()) {
String objectName = entry.getKey();

for (JmxAttributeData jmxAttributeData : entry.getValue()) {

String otelMetricName;
if (jmxAttributeData.metricName.matches(METRIC_NAME_REGEXP)) {
otelMetricName = jmxAttributeData.metricName;
} else {
otelMetricName = jmxAttributeData.metricName.replaceAll(INVALID_CHARACTER_REGEXP, "_");
}

GlobalOpenTelemetry.getMeter("com.microsoft.applicationinsights.jmx")
.gaugeBuilder(otelMetricName)
.buildWithCallback(
observableDoubleMeasurement -> {
calculateAndRecordValueForAttribute(
observableDoubleMeasurement, objectName, jmxAttributeData);
});
}
}
}

// Register each entry in the performance container
for (Map.Entry<String, Collection<JmxAttributeData>> entry : data.entrySet()) {
private static void calculateAndRecordValueForAttribute(
ObservableDoubleMeasurement observableDoubleMeasurement,
String objectName,
JmxAttributeData jmxAttributeData) {
try {
List<Object> result =
JmxDataFetcher.fetch(
objectName, jmxAttributeData.attribute); // should return the [val, ...] here

logger.trace(
"Size of the JmxDataFetcher.fetch result: {}, for objectName:{} and metricName:{}",
result.size(),
objectName,
jmxAttributeData.metricName);

boolean ok = true;
double value = 0.0;
for (Object obj : result) {
try {
PerformanceCounterContainer.INSTANCE.register(
new JmxMetricPerformanceCounter(entry.getKey(), entry.getValue()));
} catch (RuntimeException e) {
try (MDC.MDCCloseable ignored = CUSTOM_JMX_METRIC_ERROR.makeActive()) {
logger.error(
"Failed to register JMX performance counter: '{}' : '{}'",
entry.getKey(),
e.toString());
if (obj instanceof Boolean) {
value = ((Boolean) obj).booleanValue() ? 1 : 0;
} else {
value += Double.parseDouble(String.valueOf(obj));
}
} catch (RuntimeException e) {
ok = false;
break;
}
}
} catch (RuntimeException e) {
if (ok) {
logger.trace(
"value {} for objectName:{} and metricName{}",
value,
objectName,
jmxAttributeData.metricName);
observableDoubleMeasurement.record(
value,
Attributes.of(
AttributeKey.stringKey("applicationinsights.internal.metric_name"),
jmxAttributeData.metricName));
}
} catch (Exception e) {
try (MDC.MDCCloseable ignored = CUSTOM_JMX_METRIC_ERROR.makeActive()) {
logger.error("Failed to register JMX performance counters: '{}'", e.toString());
logger.error(
"Failed to calculate the metric value for objectName {} and metric name {}",
objectName,
jmxAttributeData.metricName);
logger.error("Exception: {}", e.toString());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,37 @@ public static Map<String, Collection<Object>> fetch(
return result;
}

/**
* Gets an object name and an attribute to fetch and will return the data.
*
* @param objectName The object name to search.
* @param attribute An attribute that belongs to the object name
* @return A List of values found for that attribute
* @throws Exception In case the object name is not found.
*/
public static List<Object> fetch(String objectName, String attribute) throws Exception {
List<Object> resultForAttribute = new ArrayList<>();

MBeanServer server = ManagementFactory.getPlatformMBeanServer();
Set<ObjectName> objects = server.queryNames(new ObjectName(objectName), null);
logger.trace("Matching object names for pattern {}: {}", objectName, objects.toString());
if (objects.isEmpty()) {
String errorMsg = String.format("Cannot find object name '%s'", objectName);
throw new IllegalArgumentException(errorMsg);
}

try {
resultForAttribute = fetch(server, objects, attribute);
} catch (Exception e) {
try (MDC.MDCCloseable ignored = CUSTOM_JMX_METRIC_ERROR.makeActive()) {
logger.warn("Failed to fetch JMX object '{}' with attribute '{}': ", objectName, attribute);
}
throw e;
}

return resultForAttribute;
}

private static List<Object> fetch(
MBeanServer server, Set<ObjectName> objects, String attributeName)
throws AttributeNotFoundException,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ void testBadAttributeName() throws Exception {

assertThatThrownBy(() -> JmxDataFetcher.fetch("JSDKTests:type=TestStub3", attributes))
.isInstanceOf(Exception.class);

// Test the other fetch method
for (JmxAttributeData attribute : attributes) {
assertThatThrownBy(
() -> JmxDataFetcher.fetch("JSDKTests:type=TestStub3", attribute.attribute))
.isInstanceOf(Exception.class);
}
}

@Test
Expand All @@ -81,6 +88,11 @@ void testBadName() throws Exception {

assertThatThrownBy(() -> JmxDataFetcher.fetch("JSDKTests:type=TestStub", attributes))
.isInstanceOf(IllegalArgumentException.class);

// Test the other fetch method
assertThatThrownBy(
() -> JmxDataFetcher.fetch("JSDKTests:type=TestStub", attributes.get(0).attribute))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
Expand Down Expand Up @@ -119,6 +131,27 @@ private static void performTest(
verify(result, "Int", expectedInt);
verify(result, "Double", expectedDouble);
verify(result, "Long", expectedLong);

// verify the other fetch
for (int i = 0; i < attributes.size(); i++) {
List<Object> singleAttributeResult =
JmxDataFetcher.fetch("JSDKTests:type=TestStub", attributes.get(i).attribute);
assertThat(singleAttributeResult).isNotNull();
assertThat(singleAttributeResult.size()).isEqualTo(1);

double value = 0.0;
for (Object obj : singleAttributeResult) {
value += Double.parseDouble(String.valueOf(obj));
}

if (i == 0) {
assertThat(value).isEqualTo(expectedInt);
} else if (i == 1) {
assertThat(value).isEqualTo(expectedDouble);
} else {
assertThat(value).isEqualTo(expectedLong);
}
}
}

private static void verify(
Expand Down
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ hideFromDependabot(":smoke-tests:apps:Diagnostics")
hideFromDependabot(":smoke-tests:apps:Diagnostics:JfrFileReader")
hideFromDependabot(":smoke-tests:apps:DiagnosticExtension:MockExtension")
hideFromDependabot(":smoke-tests:apps:DiagnosticExtension")
hideFromDependabot(":smoke-tests:apps:DotInJmxMetric")
hideFromDependabot(":smoke-tests:apps:JmxMetric")
hideFromDependabot(":smoke-tests:apps:gRPC")
hideFromDependabot(":smoke-tests:apps:HeartBeat")
hideFromDependabot(":smoke-tests:apps:HttpClients")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.WILDFLY_13_JAVA_8_OPENJ9;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
Expand All @@ -23,14 +25,22 @@ abstract class DetectUnexpectedOtelMetricsTest {

@RegisterExtension static final SmokeTestExtension testing = SmokeTestExtension.create();

private static final List<String> EXPECTED_METRIC_NAMES = new ArrayList<>();

static {
EXPECTED_METRIC_NAMES.add("_OTELRESOURCE_");
EXPECTED_METRIC_NAMES.add("Current Thread Count");
EXPECTED_METRIC_NAMES.add("Loaded Class Count");
trask marked this conversation as resolved.
Show resolved Hide resolved
}

@Test
@TargetUri("/app")
void testApp() throws Exception {
// verify no unexpected otel metrics, expect an TimeoutException being thrown
assertThatThrownBy(
() ->
testing.mockedIngestion.waitForItemsUnexpectedOtelMetric(
"MetricData", envelope -> true))
"MetricData", envelope -> true, EXPECTED_METRIC_NAMES))
.isInstanceOf(TimeoutException.class);
}

Expand Down

This file was deleted.