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

Enable NewRelicMeterRegistry to publish via APM in addition to API (resolves #1761) #1854

Closed
wants to merge 2 commits into from

Conversation

schmidt-galen-heb
Copy link
Contributor

@schmidt-galen-heb schmidt-galen-heb commented Feb 14, 2020

#1761

I've done extensive testing of this with some of my own applications, and everything appears to be working correctly.

The scope of the changes are fairly large, but I've been very careful to ensure that all of the changes should be backwards compatible for existing clients.

Specifically, all existing configurations will continue working as before, and none of the generated JSON payloads to the Insights API should change.

@@ -52,7 +53,8 @@ def VERSIONS = [
'org.hdrhistogram:HdrHistogram:latest.release',
'org.hibernate:hibernate-entitymanager:latest.release',
'org.hsqldb:hsqldb:latest.release',
'org.jooq:jooq:latest.release',
// Pin version temporarily to restore builds. See gh-1852
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Included so the build will pass, can be removed once #1852 is resolved

@@ -2,6 +2,11 @@ dependencies {
api project(':micrometer-core')

implementation 'org.slf4j:slf4j-api'
implementation 'com.newrelic.agent.java:newrelic-api'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding APM support requires the addition of a compile time dependency on the newrelic-api.
The API is fairly light, containing only annotations, interfaces, POJOs, and enums, so I don't expect this to cause any issues for consumers not using the APM integration

* @return The NewRelic integration method
*/
default NewRelicIntegration integration() {
return NewRelicIntegration.fromString(get(prefix() + ".integration")).orElse(NewRelicIntegration.API);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default to API integration to preserve existing behavior

* POJO representation of an Insights event.
*/
// VisibleForTesting
static class NewRelicEvent {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To allow the APM/API integrations to share the majority of the code, a DTO to represent the event was created

@@ -45,16 +47,16 @@ public NewRelicNamingConvention(NamingConvention delegate) {

@Override
public String name(String name, Meter.Type type, @Nullable String baseUnit) {
return StringEscapeUtils.escapeJson(toValidNewRelicString(delegate.name(name, type, baseUnit)));
return toValidNewRelicString(delegate.name(name, type, baseUnit));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the addition of the APM integration, there was a potential issue where attributes would either be double JSON escaped, or never JSON escaped.
Removing the JSON escaping from the NamingConvention and only performing it when the data is actually converted into JSON prevents this from happening.

*/
@SuppressWarnings("ConstantConditions")
class NewRelicMeterRegistryTest {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were previously several code paths which weren't tested by this class, including conversion of various types of Meters into JSON, as well as ensuring global attributes were included.

I've ensured there's now a test for each subclass of Meter, as well as tests to ensure the global tags are applied.

@Test
void eventFor_Gauge() {
eventFor_Gauge(this.meterNameEventTypeEnabledRegistry, ImmutableMap.<String, Object>builder()
.put("eventType", "myGauge")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous test did a simple String comparison between the generated JSON and the expected value. This broke with the addition of NewRelicEvent, because the attributes were now stored in a Map, meaning the keys were written in an non-deterministic order in the JSON.

I've refactored the tests to use Jackson to read the JSON object into a Map, and then use AssertJ's map comparator instead. That way, the tests only care that the JSON has the correct keys, values, and types, making the tests easier to write + less prone to false failures.

@shakuzen shakuzen closed this Apr 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants