Skip to content

Commit

Permalink
summary: Log Forwarding Updates
Browse files Browse the repository at this point in the history
feat: Allow forwarding of logs where context data is present but the message and exception are missing. Previously, logs with no message or exception were not forwarded, even if they held context data.

feat: Add a supportability metric (Supportability/Logging/Forwarding/Empty) when an "empty" log event is not forwarded. Resolves #1472
  • Loading branch information
tippmar-nr authored Oct 20, 2023
1 parent 8fe8d63 commit 1db5335
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 5 deletions.
7 changes: 4 additions & 3 deletions src/Agent/NewRelic/Agent/Core/Agent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -439,14 +439,15 @@ public void RecordLogMessage(string frameworkName, object logEvent, Func<object,

var logMessage = getLogMessage(logEvent);
var logException = getLogException(logEvent);
var logContextData = _configurationService.Configuration.ContextDataEnabled ? getContextData(logEvent) : null;

// exit quickly if the message and exception are missing
if (string.IsNullOrWhiteSpace(logMessage) && logException is null)
// exit quickly if the message, exception and context data are missing
if (string.IsNullOrWhiteSpace(logMessage) && logException is null && (logContextData is null || logContextData.Count == 0))
{
_agentHealthReporter.ReportLoggingEventsEmpty();
return;
}

var logContextData = _configurationService.Configuration.ContextDataEnabled ? getContextData(logEvent) : null;
var timestamp = getTimestamp(logEvent).ToUnixTimeMilliseconds();

LogEventWireModel logEventWireModel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,11 @@ public void ReportLogForwardingEnabledWithFramework(string logFramework)
_loggingForwardingEnabledWithFrameworksReported.TryAdd(logFramework, false);
}

public void ReportLoggingEventsEmpty(int count = 1)
{
ReportSupportabilityCountMetric(MetricNames.SupportabilityLoggingEventEmpty);
}

public void CollectLoggingMetrics()
{
var totalCount = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,6 @@ public interface IAgentHealthReporter : IOutOfBandMetricSource
void ReportLoggingEventsDropped(int droppedCount);
void ReportLogForwardingFramework(string logFramework);
void ReportLogForwardingEnabledWithFramework(string logFramework);
void ReportLoggingEventsEmpty(int count = 1);
}
}
2 changes: 2 additions & 0 deletions src/Agent/NewRelic/Agent/Core/Metrics/MetricNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ static MetricNames()
});
}


public static MetricName GetCustom(string suffix)
{
return MetricName.Create(Custom, suffix);
Expand Down Expand Up @@ -1045,6 +1046,7 @@ public static string GetPerDestinationAreaDataUsageMetricName(string destination
public const string SupportabilityLoggingEventsSent = SupportabilityLoggingEventsPs + Forwarding + PathSeparator + "Sent";
public const string SupportabilityLoggingEventsCollected = SupportabilityLoggingEventsPs + Forwarding + PathSeparator + "Seen";
public const string SupportabilityLoggingEventsDropped = SupportabilityLoggingEventsPs + Forwarding + PathSeparator + "Dropped";
public const string SupportabilityLoggingEventEmpty = SupportabilityLoggingEventsPs + Forwarding + PathSeparator + "Empty";

public static string GetLoggingMetricsLinesBySeverityName(string logLevel)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ public void ReportLoggingSupportabilityMetrics()
_agentHealthReporter.ReportLoggingEventCollected();
_agentHealthReporter.ReportLoggingEventsSent(2);
_agentHealthReporter.ReportLoggingEventsDropped(3);
_agentHealthReporter.ReportLoggingEventsEmpty();
_agentHealthReporter.ReportLogForwardingFramework("log4net");

_agentHealthReporter.ReportLogForwardingEnabledWithFramework("Framework1");
Expand All @@ -325,6 +326,7 @@ public void ReportLoggingSupportabilityMetrics()
{ "Supportability/Logging/Forwarding/Seen", 1 },
{ "Supportability/Logging/Forwarding/Sent", 2 },
{ "Supportability/Logging/Forwarding/Dropped", 3 },
{ "Supportability/Logging/Forwarding/Empty", 1 },
{ "Supportability/Logging/Metrics/DotNET/enabled", 1 },
{ "Supportability/Logging/Forwarding/DotNET/enabled", 1 },
{ "Supportability/Logging/LocalDecorating/DotNET/enabled", 1 },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1351,7 +1351,50 @@ public void RecordLogMessage_NoTransaction_NoMessage_WithException_Success()
}

[Test]
public void RecordLogMessage_NoTransaction_NoMessage_NoException_DropsEvent()
public void RecordLogMessage_NoTransaction_NoMessage_NoException_WithContextData_Success()
{
Mock.Arrange(() => _configurationService.Configuration.LogEventCollectorEnabled)
.Returns(true);
Mock.Arrange(() => _configurationService.Configuration.ContextDataEnabled)
.Returns(true);

var timestamp = DateTime.Now;
var timestampUnix = timestamp.ToUnixTimeMilliseconds();
var level = "DEBUG";
string message = null;
var contextData = new Dictionary<string, object>() { { "key1", "value1" }, { "key2", 1 } };

Func<object, string> getLevelFunc = (l) => level;
Func<object, DateTime> getTimestampFunc = (l) => timestamp;
Func<object, string> getMessageFunc = (l) => message;
Func<object, Exception> getLogExceptionFunc = (l) => null;
Func<object, Dictionary<string, object>> getContextDataFunc = (l) => contextData;

var spanId = "spanid";
var traceId = "traceid";
var loggingFramework = "testFramework";

var xapi = _agent as IAgentExperimental;
xapi.RecordLogMessage(loggingFramework, new object(), getTimestampFunc, getLevelFunc, getMessageFunc, getLogExceptionFunc, getContextDataFunc, spanId, traceId);

// Access the private collection of events to get the number of add attempts.
var privateAccessor = new PrivateAccessor(_logEventAggregator);
var logEvents = privateAccessor.GetField("_logEvents") as ConcurrentPriorityQueue<PrioritizedNode<LogEventWireModel>>;

var logEvent = logEvents?.FirstOrDefault()?.Data;
Assert.AreEqual(1, logEvents.Count);
Assert.IsNotNull(logEvent);
Assert.AreEqual(timestampUnix, logEvent.TimeStamp);
Assert.AreEqual(level, logEvent.Level);
Assert.AreEqual(message, logEvent.Message);
Assert.AreEqual(spanId, logEvent.SpanId);
Assert.AreEqual(traceId, logEvent.TraceId);
Assert.AreEqual(contextData, logEvent.ContextData);
Assert.IsNotNull(logEvent.Priority);
}

[Test]
public void RecordLogMessage_NoTransaction_NoMessage_NoException_NoContextData_DropsEvent()
{
Mock.Arrange(() => _configurationService.Configuration.LogEventCollectorEnabled)
.Returns(true);
Expand Down Expand Up @@ -1473,7 +1516,51 @@ public void RecordLogMessage_WithTransaction_NoMessage_WithException_Success()
}

[Test]
public void RecordLogMessage_WithTransaction_NoMessage_NoException_DropsEvent()
public void RecordLogMessage_WithTransaction_NoMessage_NoException_WithContextData_Success()
{
Mock.Arrange(() => _configurationService.Configuration.LogEventCollectorEnabled)
.Returns(true);
Mock.Arrange(() => _configurationService.Configuration.ContextDataEnabled)
.Returns(true);

var timestamp = DateTime.Now;
var timestampUnix = timestamp.ToUnixTimeMilliseconds();
var level = "DEBUG";
string message = null;
var contextData = new Dictionary<string, object>() { { "key1", "value1" }, { "key2", 1 } };

Func<object, string> getLevelFunc = (l) => level;
Func<object, DateTime> getTimestampFunc = (l) => timestamp;
Func<object, string> getMessageFunc = (l) => message;
Func<object, Exception> getLogExceptionFunc = (l) => null;
Func<object, Dictionary<string, object>> getContextDataFunc = (l) => contextData;

var spanId = "spanid";
var traceId = "traceid";
var loggingFramework = "testFramework";

SetupTransaction();
var transaction = _transactionService.GetCurrentInternalTransaction();
var priority = transaction.Priority;

var xapi = _agent as IAgentExperimental;
xapi.RecordLogMessage(loggingFramework, new object(), getTimestampFunc, getLevelFunc, getMessageFunc, getLogExceptionFunc, getContextDataFunc, spanId, traceId);

var harvestedLogEvents = transaction.HarvestLogEvents();
var logEvent = harvestedLogEvents.FirstOrDefault();
Assert.AreEqual(1, harvestedLogEvents.Count);
Assert.IsNotNull(logEvent);
Assert.AreEqual(timestampUnix, logEvent.TimeStamp);
Assert.AreEqual(level, logEvent.Level);
Assert.AreEqual(message, logEvent.Message);
Assert.AreEqual(spanId, logEvent.SpanId);
Assert.AreEqual(traceId, logEvent.TraceId);
Assert.AreEqual(contextData, logEvent.ContextData);
Assert.AreEqual(priority, logEvent.Priority);
}

[Test]
public void RecordLogMessage_WithTransaction_NoMessage_NoException_NoContextData_DropsEvent()
{
Mock.Arrange(() => _configurationService.Configuration.LogEventCollectorEnabled)
.Returns(true);
Expand Down

0 comments on commit 1db5335

Please sign in to comment.