Skip to content

Commit

Permalink
Merge pull request #718 from newrelic/logging-initiative
Browse files Browse the repository at this point in the history
Logging initiative
  • Loading branch information
jasonjkeller committed Mar 25, 2022
2 parents e220ab5 + 0b79ed2 commit 2d9c2bc
Show file tree
Hide file tree
Showing 83 changed files with 4,919 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

package com.newrelic.agent.bridge;

import com.newrelic.api.agent.Logs;
import com.newrelic.api.agent.NewRelic;

/**
Expand Down Expand Up @@ -87,4 +88,11 @@ public interface Agent extends com.newrelic.api.agent.Agent {
*/
boolean ignoreIfUnstartedAsyncContext(Object activityContext);

/**
* Provides access to the LogSender events API.
*
* @return Object used to add custom events.
*/
Logs getLogSender();

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.newrelic.api.agent.Config;
import com.newrelic.api.agent.Insights;
import com.newrelic.api.agent.Logger;
import com.newrelic.api.agent.Logs;
import com.newrelic.api.agent.MetricAggregator;
import com.newrelic.api.agent.TraceMetadata;

Expand Down Expand Up @@ -63,6 +64,11 @@ public Insights getInsights() {
return NoOpInsights.INSTANCE;
}

@Override
public Logs getLogSender() {
return NoOpLogs.INSTANCE;
}

@Override
public boolean startAsyncActivity(Object activityContext) {
return false;
Expand Down
24 changes: 24 additions & 0 deletions agent-bridge/src/main/java/com/newrelic/agent/bridge/NoOpLogs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
*
* * Copyright 2022 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.newrelic.agent.bridge;

import com.newrelic.api.agent.Logs;

import java.util.Map;

class NoOpLogs implements Logs {
static final Logs INSTANCE = new NoOpLogs();

private NoOpLogs() {
}

@Override
public void recordLogEvent(Map<String, ?> attributes) {
}

}
26 changes: 26 additions & 0 deletions agent-bridge/src/main/java/com/newrelic/api/agent/Logs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
*
* * Copyright 2022 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.newrelic.api.agent;

import java.util.Map;

/**
* Used to send LogEvents to New Relic. Each LogEvent represents a single log line.
*/
public interface Logs {

/**
* Sends a LogEvent for the current application.
*
* @param attributes A map of log event data (e.g. log message, log timestamp, log level)
* Each key should be a String and each value should be a String, Number, or Boolean.
* For map values that are not String, Number, or Boolean object types the toString value will be used.
* @since 7.6.0
*/
void recordLogEvent(Map<String, ?> attributes);
}
43 changes: 43 additions & 0 deletions agent-model/src/main/java/com/newrelic/agent/model/LogEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
*
* * Copyright 2022 New Relic Corporation. All rights reserved.
* * SPDX-License-Identifier: Apache-2.0
*
*/

package com.newrelic.agent.model;

import org.json.simple.JSONObject;
import org.json.simple.JSONStreamAware;

import java.io.IOException;
import java.io.Writer;
import java.util.Map;

public class LogEvent extends AnalyticsEvent implements JSONStreamAware {

public static final String LOG_EVENT_TYPE = "LogEvent";

private volatile float mutablePriority;

public LogEvent(Map<String, Object> attributes, float priority) {
super(LOG_EVENT_TYPE, System.currentTimeMillis(), priority, attributes);
this.mutablePriority = priority;
}

@Override
public float getPriority() {
return mutablePriority;
}

public void setPriority(float priority) {
this.mutablePriority = priority;
}

@SuppressWarnings("unchecked")
@Override
public void writeJSONString(Writer out) throws IOException {
JSONObject.writeJSONString(getMutableUserAttributes(), out);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.newrelic.agent.model;

import org.junit.Test;

import java.io.IOException;
import java.io.StringWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.*;

public class LogEventTest {

@Test
public void testConstructor() {
LogEvent logEvent = new LogEvent(Collections.emptyMap(), 0);

assertEquals(logEvent.getPriority(), 0, 0);
assertNotNull(logEvent.getMutableUserAttributes());
assertTrue(logEvent.getMutableUserAttributes().isEmpty());
}

@Test
public void testPriorityAccessors() {
LogEvent logEvent = new LogEvent(Collections.emptyMap(), 0);

assertEquals(0, logEvent.getPriority(), 0);

logEvent.setPriority(1);

assertEquals(1, logEvent.getPriority(),0);
}

@Test
public void testJsonString() throws IOException {
Map<String, Object> attributes = new HashMap<>();
attributes.put("key", "value");

LogEvent logEvent = new LogEvent(attributes, 0);
StringWriter writer = new StringWriter();

logEvent.writeJSONString(writer);

assertEquals("{\"key\":\"value\"}", writer.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.newrelic.api.agent.Config;
import com.newrelic.api.agent.Insights;
import com.newrelic.api.agent.Logger;
import com.newrelic.api.agent.Logs;
import com.newrelic.api.agent.MetricAggregator;
import com.newrelic.api.agent.TraceMetadata;

Expand Down Expand Up @@ -59,4 +60,9 @@ public Logger getLogger() {

@Override
public boolean ignoreIfUnstartedAsyncContext(Object activityContext) { throw new RuntimeException(); }

@Override
public Logs getLogSender() {
throw new RuntimeException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package test.newrelic.test.agent;

import com.newrelic.agent.Transaction;
import com.newrelic.agent.stats.SimpleStatsEngine;
import com.newrelic.agent.stats.TransactionStats;
import com.newrelic.api.agent.Trace;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
* Test for com.newrelic.instrumentation.java.logging-jdk8 instrumentation
*/
public class JavaUtilLoggerTest {

private static final String CAPTURED = "This log message should be captured";
private static final String NOT_CAPTURED = "This message should NOT be captured";

@Before
public void setup() {
Transaction.clearTransaction();
}

@Trace(dispatcher = true)
@Test
public void shouldIncrementEmittedLogsCountersIndependentlyIfLogLevelEnabled() {
// Given
final Logger logger = Logger.getLogger(JavaUtilLoggerTest.class.getName());
logger.setLevel(Level.INFO);

// When
logger.finest(NOT_CAPTURED);
logger.finer(NOT_CAPTURED);
logger.fine(NOT_CAPTURED);
logger.config(NOT_CAPTURED);
logger.info(CAPTURED);
logger.info(CAPTURED);
logger.info(CAPTURED);
logger.warning(CAPTURED);
logger.warning(CAPTURED);
logger.warning(CAPTURED);
logger.warning(CAPTURED);
logger.severe(CAPTURED);

// Then
Map<String, Integer> metrics = getLogMetricsCounts();
Assert.assertEquals(8, (int) metrics.get("Logging/lines"));
Assert.assertEquals(0, (int) metrics.get("Logging/lines/FINEST"));
Assert.assertEquals(0, (int) metrics.get("Logging/lines/FINER"));
Assert.assertEquals(0, (int) metrics.get("Logging/lines/FINE"));
Assert.assertEquals(0, (int) metrics.get("Logging/lines/CONFIG"));
Assert.assertEquals(3, (int) metrics.get("Logging/lines/INFO"));
Assert.assertEquals(4, (int) metrics.get("Logging/lines/WARNING"));
Assert.assertEquals(1, (int) metrics.get("Logging/lines/SEVERE"));
}

@Trace(dispatcher = true)
@Test
public void shouldIncrementAllEmittedLogCountersIfLogLevelIsSetToFinest() {
// Given
final Logger logger = Logger.getLogger(JavaUtilLoggerTest.class.getName());
logger.setLevel(Level.FINEST);

// When
logger.finest(CAPTURED);
logger.finer(CAPTURED);
logger.fine(CAPTURED);
logger.config(CAPTURED);
logger.info(CAPTURED);
logger.warning(CAPTURED);
logger.severe(CAPTURED);

// Then
Map<String, Integer> metrics = getLogMetricsCounts();
Assert.assertEquals(7, (int) metrics.get("Logging/lines"));
Assert.assertEquals(1, (int) metrics.get("Logging/lines/FINEST"));
Assert.assertEquals(1, (int) metrics.get("Logging/lines/FINER"));
Assert.assertEquals(1, (int) metrics.get("Logging/lines/FINE"));
Assert.assertEquals(1, (int) metrics.get("Logging/lines/CONFIG"));
Assert.assertEquals(1, (int) metrics.get("Logging/lines/INFO"));
Assert.assertEquals(1, (int) metrics.get("Logging/lines/WARNING"));
Assert.assertEquals(1, (int) metrics.get("Logging/lines/SEVERE"));
}

@Trace(dispatcher = true)
@Test
public void shouldIncrementEmittedLogsCountersIndependentlyIfLogLevelEnabledEvenLoggingLogRecordsDirectly() {
// Given
final Logger logger = Logger.getLogger(JavaUtilLoggerTest.class.getName());
logger.setLevel(Level.INFO);

// When
logger.log(new LogRecord(Level.FINEST, NOT_CAPTURED));
logger.log(new LogRecord(Level.FINER, NOT_CAPTURED));
logger.log(new LogRecord(Level.FINE, NOT_CAPTURED));
logger.log(new LogRecord(Level.CONFIG, NOT_CAPTURED));
logger.log(new LogRecord(Level.INFO, CAPTURED));
logger.log(new LogRecord(Level.INFO, CAPTURED));
logger.log(new LogRecord(Level.INFO, CAPTURED));
logger.log(new LogRecord(Level.WARNING, CAPTURED));
logger.log(new LogRecord(Level.WARNING, CAPTURED));
logger.log(new LogRecord(Level.WARNING, CAPTURED));
logger.log(new LogRecord(Level.WARNING, CAPTURED));
logger.log(new LogRecord(Level.SEVERE, CAPTURED));

// Then
Map<String, Integer> metrics = getLogMetricsCounts();
Assert.assertEquals(8, (int) metrics.get("Logging/lines"));
Assert.assertEquals(0, (int) metrics.get("Logging/lines/FINEST"));
Assert.assertEquals(0, (int) metrics.get("Logging/lines/FINER"));
Assert.assertEquals(0, (int) metrics.get("Logging/lines/FINE"));
Assert.assertEquals(0, (int) metrics.get("Logging/lines/CONFIG"));
Assert.assertEquals(3, (int) metrics.get("Logging/lines/INFO"));
Assert.assertEquals(4, (int) metrics.get("Logging/lines/WARNING"));
Assert.assertEquals(1, (int) metrics.get("Logging/lines/SEVERE"));
}

private Map<String, Integer> getLogMetricsCounts() {
Transaction transaction = Transaction.getTransaction();
TransactionStats transactionStats = transaction.getTransactionActivity().getTransactionStats();
SimpleStatsEngine engine = transactionStats.getUnscopedStats();
final Map<String, Integer> metrics = new HashMap<>();
metrics.put("Logging/lines", engine.getStats("Logging/lines").getCallCount());
metrics.put("Logging/lines/FINEST", engine.getStats("Logging/lines/FINEST").getCallCount());
metrics.put("Logging/lines/FINER", engine.getStats("Logging/lines/FINER").getCallCount());
metrics.put("Logging/lines/FINE", engine.getStats("Logging/lines/FINE").getCallCount());
metrics.put("Logging/lines/CONFIG", engine.getStats("Logging/lines/CONFIG").getCallCount());
metrics.put("Logging/lines/INFO", engine.getStats("Logging/lines/INFO").getCallCount());
metrics.put("Logging/lines/WARNING", engine.getStats("Logging/lines/WARNING").getCallCount());
metrics.put("Logging/lines/SEVERE", engine.getStats("Logging/lines/SEVERE").getCallCount());
return metrics;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

package com.newrelic.agent.introspec;

import com.newrelic.agent.model.LogEvent;

import java.util.Collection;
import java.util.Map;

Expand Down Expand Up @@ -159,10 +161,30 @@ public interface Introspector {
*/
String getDispatcherVersion();

/**
* Returns all span events that were collected since this was initialized or cleared.
*
* @return collection of SpanEvents or null if there are none
*/
Collection<SpanEvent> getSpanEvents();

/**
* Clear all existing SpanEvents
*/
void clearSpanEvents();

/**
* Returns all log events that were collected since this was initialized or cleared.
*
* @return collection of LogEvents or null if there are none
*/
Collection<LogEvent> getLogEvents();

/**
* Clear all existing LogEvents
*/
void clearLogEvents();

/**
* Return random port available
*
Expand Down
Loading

0 comments on commit 2d9c2bc

Please sign in to comment.