Skip to content

Commit d1e29ea

Browse files
authored
summary: Include APM Labels in forwarded application logs.
notice: feat: New Relic APM language agents now allow you to opt-in to adding your custom tags (labels) to agent-forwarded logs. With custom tags on logs, platform engineers can easily filter, search, and correlate log data for faster and more efficient troubleshooting, improved performance, and optimized resource utilization. To learn more about this feature see the [documentation](https://docs.newrelic.com/docs/logs/logs-context/APM-logs-custom-tags). (#2831) feat: New Relic APM language agents now allow you to opt-in to adding your custom tags (labels) to agent-forwarded logs. With custom tags on logs, platform engineers can easily filter, search, and correlate log data for faster and more efficient troubleshooting, improved performance, and optimized resource utilization. To learn more about this feature see the [documentation](https://docs.newrelic.com/docs/logs/logs-context/APM-logs-custom-tags). (#2831)
1 parent cfc6d6a commit d1e29ea

File tree

28 files changed

+520
-43
lines changed

28 files changed

+520
-43
lines changed

src/Agent/NewRelic/Agent/Core/AgentHealth/AgentHealthReporter.cs

+1
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,7 @@ public void ReportLogForwardingConfiguredValues()
662662
ReportSupportabilityCountMetric(MetricNames.GetSupportabilityLogMetricsConfiguredName(_configuration.LogMetricsCollectorEnabled));
663663
ReportSupportabilityCountMetric(MetricNames.GetSupportabilityLogForwardingConfiguredName(_configuration.LogEventCollectorEnabled));
664664
ReportSupportabilityCountMetric(MetricNames.GetSupportabilityLogDecoratingConfiguredName(_configuration.LogDecoratorEnabled));
665+
ReportSupportabilityCountMetric(MetricNames.GetSupportabilityLogLabelsConfiguredName(_configuration.LabelsEnabled));
665666
}
666667

667668
#endregion

src/Agent/NewRelic/Agent/Core/Aggregators/LogEventAggregator.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using NewRelic.Agent.Extensions.Collections;
1414
using NewRelic.Agent.Extensions.Logging;
1515
using NewRelic.Agent.Core.SharedInterfaces;
16+
using NewRelic.Agent.Core.Labels;
1617

1718
namespace NewRelic.Agent.Core.Aggregators
1819
{
@@ -31,14 +32,17 @@ public class LogEventAggregator : AbstractAggregator<LogEventWireModel>, ILogEve
3132
private const double ReservoirReductionSizeMultiplier = 0.5;
3233

3334
private readonly IAgentHealthReporter _agentHealthReporter;
35+
private readonly ILabelsService _labelsService;
3436

3537
private ConcurrentPriorityQueue<PrioritizedNode<LogEventWireModel>> _logEvents = new ConcurrentPriorityQueue<PrioritizedNode<LogEventWireModel>>(0);
3638
private int _logsDroppedCount;
3739

38-
public LogEventAggregator(IDataTransportService dataTransportService, IScheduler scheduler, IProcessStatic processStatic, IAgentHealthReporter agentHealthReporter)
40+
public LogEventAggregator(IDataTransportService dataTransportService, IScheduler scheduler, IProcessStatic processStatic, IAgentHealthReporter agentHealthReporter,
41+
ILabelsService labelsService)
3942
: base(dataTransportService, scheduler, processStatic)
4043
{
4144
_agentHealthReporter = agentHealthReporter;
45+
_labelsService = labelsService;
4246
ResetCollections(_configuration.LogEventsMaxSamplesStored);
4347
}
4448

@@ -98,6 +102,7 @@ protected void InternalHarvest(string transactionId = null)
98102
_configuration.ApplicationNames.ElementAt(0),
99103
_configuration.EntityGuid,
100104
hostname,
105+
_configuration.LabelsEnabled ? _labelsService.GetFilteredLabels(_configuration.LabelsExclude) : [],
101106
aggregatedEvents);
102107

103108
var responseStatus = DataTransportService.Send(modelsCollection, transactionId);

src/Agent/NewRelic/Agent/Core/Config/Configuration.cs

+74
Original file line numberDiff line numberDiff line change
@@ -5472,6 +5472,8 @@ public partial class configurationApplicationLoggingForwarding
54725472

54735473
private configurationApplicationLoggingForwardingContextData contextDataField;
54745474

5475+
private configurationApplicationLoggingForwardingLabels labelsField;
5476+
54755477
private bool enabledField;
54765478

54775479
private int maxSamplesStoredField;
@@ -5483,6 +5485,7 @@ public partial class configurationApplicationLoggingForwarding
54835485
/// </summary>
54845486
public configurationApplicationLoggingForwarding()
54855487
{
5488+
this.labelsField = new configurationApplicationLoggingForwardingLabels();
54865489
this.contextDataField = new configurationApplicationLoggingForwardingContextData();
54875490
this.enabledField = true;
54885491
this.maxSamplesStoredField = 10000;
@@ -5500,6 +5503,18 @@ public configurationApplicationLoggingForwardingContextData contextData
55005503
}
55015504
}
55025505

5506+
public configurationApplicationLoggingForwardingLabels labels
5507+
{
5508+
get
5509+
{
5510+
return this.labelsField;
5511+
}
5512+
set
5513+
{
5514+
this.labelsField = value;
5515+
}
5516+
}
5517+
55035518
[System.Xml.Serialization.XmlAttributeAttribute()]
55045519
[System.ComponentModel.DefaultValueAttribute(true)]
55055520
public bool enabled
@@ -5628,6 +5643,65 @@ public virtual configurationApplicationLoggingForwardingContextData Clone()
56285643
#endregion
56295644
}
56305645

5646+
[System.CodeDom.Compiler.GeneratedCodeAttribute("Xsd2Code", "3.6.0.20097")]
5647+
[System.SerializableAttribute()]
5648+
[System.ComponentModel.DesignerCategoryAttribute("code")]
5649+
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="urn:newrelic-config")]
5650+
public partial class configurationApplicationLoggingForwardingLabels
5651+
{
5652+
5653+
private bool enabledField;
5654+
5655+
private string excludeField;
5656+
5657+
/// <summary>
5658+
/// configurationApplicationLoggingForwardingLabels class constructor
5659+
/// </summary>
5660+
public configurationApplicationLoggingForwardingLabels()
5661+
{
5662+
this.enabledField = false;
5663+
this.excludeField = "";
5664+
}
5665+
5666+
[System.Xml.Serialization.XmlAttributeAttribute()]
5667+
[System.ComponentModel.DefaultValueAttribute(false)]
5668+
public bool enabled
5669+
{
5670+
get
5671+
{
5672+
return this.enabledField;
5673+
}
5674+
set
5675+
{
5676+
this.enabledField = value;
5677+
}
5678+
}
5679+
5680+
[System.Xml.Serialization.XmlAttributeAttribute()]
5681+
[System.ComponentModel.DefaultValueAttribute("")]
5682+
public string exclude
5683+
{
5684+
get
5685+
{
5686+
return this.excludeField;
5687+
}
5688+
set
5689+
{
5690+
this.excludeField = value;
5691+
}
5692+
}
5693+
5694+
#region Clone method
5695+
/// <summary>
5696+
/// Create a clone of this configurationApplicationLoggingForwardingLabels object
5697+
/// </summary>
5698+
public virtual configurationApplicationLoggingForwardingLabels Clone()
5699+
{
5700+
return ((configurationApplicationLoggingForwardingLabels)(this.MemberwiseClone()));
5701+
}
5702+
#endregion
5703+
}
5704+
56315705
[System.CodeDom.Compiler.GeneratedCodeAttribute("Xsd2Code", "3.6.0.20097")]
56325706
[System.SerializableAttribute()]
56335707
[System.ComponentModel.DesignerCategoryAttribute("code")]

src/Agent/NewRelic/Agent/Core/Config/Configuration.xsd

+23
Original file line numberDiff line numberDiff line change
@@ -1816,6 +1816,29 @@
18161816
</xs:attribute>
18171817
</xs:complexType>
18181818
</xs:element>
1819+
<xs:element name="labels" minOccurs="0" maxOccurs="1">
1820+
<xs:annotation>
1821+
<xs:documentation>
1822+
Include configured labels with log records.
1823+
</xs:documentation>
1824+
</xs:annotation>
1825+
<xs:complexType>
1826+
<xs:attribute name="enabled" type="xs:boolean" default="false">
1827+
<xs:annotation>
1828+
<xs:documentation>
1829+
Controls whether or not labels are included with log records. Defaults to false.
1830+
</xs:documentation>
1831+
</xs:annotation>
1832+
</xs:attribute>
1833+
<xs:attribute name="exclude" type="xs:string" default="">
1834+
<xs:annotation>
1835+
<xs:documentation>
1836+
A comma-separated list of case-insensitive strings that define the labels that should NOT be added to log records.
1837+
</xs:documentation>
1838+
</xs:annotation>
1839+
</xs:attribute>
1840+
</xs:complexType>
1841+
</xs:element>
18191842
</xs:sequence>
18201843
<xs:attribute name="enabled" type="xs:boolean" default="true">
18211844
<xs:annotation>

src/Agent/NewRelic/Agent/Core/Configuration/DefaultConfiguration.cs

+27
Original file line numberDiff line numberDiff line change
@@ -2062,6 +2062,33 @@ public virtual HashSet<string> LogLevelDenyList
20622062
}
20632063
}
20642064

2065+
public virtual bool LabelsEnabled
2066+
{
2067+
get
2068+
{
2069+
return LogEventCollectorEnabled &&
2070+
EnvironmentOverrides(_localConfiguration.applicationLogging.forwarding.labels.enabled, "NEW_RELIC_APPLICATION_LOGGING_FORWARDING_LABELS_ENABLED");
2071+
}
2072+
}
2073+
2074+
private HashSet<string> _labelsExclude;
2075+
public virtual IEnumerable<string> LabelsExclude
2076+
{
2077+
get
2078+
{
2079+
if (_labelsExclude == null)
2080+
{
2081+
_labelsExclude = new HashSet<string>(
2082+
EnvironmentOverrides(_localConfiguration.applicationLogging.forwarding.labels.exclude,
2083+
"NEW_RELIC_APPLICATION_LOGGING_FORWARDING_LABELS_EXCLUDE")
2084+
?.Split(new[] { StringSeparators.CommaChar, ' ' }, StringSplitOptions.RemoveEmptyEntries)
2085+
?? Enumerable.Empty<string>());
2086+
}
2087+
2088+
return _labelsExclude;
2089+
}
2090+
}
2091+
20652092
#endregion
20662093

20672094
private IEnumerable<IDictionary<string, string>> _ignoredInstrumentation;

src/Agent/NewRelic/Agent/Core/Configuration/ReportedConfiguration.cs

+6
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,12 @@ public ReportedConfiguration(IConfiguration configuration)
638638
[JsonProperty("application_logging.forwarding.context_data.exclude")]
639639
public IEnumerable<string> ContextDataExclude => _configuration.ContextDataExclude;
640640

641+
[JsonProperty("application_logging.forwarding.labels.enabled")]
642+
public bool LabelsEnabled => _configuration.LabelsEnabled;
643+
644+
[JsonProperty("application_logging.forwarding.labels.exclude")]
645+
public IEnumerable<string> LabelsExclude => _configuration.LabelsExclude;
646+
641647
[JsonProperty("metrics.harvest_cycle")]
642648
public TimeSpan MetricsHarvestCycle => _configuration.MetricsHarvestCycle;
643649

src/Agent/NewRelic/Agent/Core/JsonConverters/LogEventWireModelCollectionJsonConverter.cs

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2020 New Relic, Inc. All rights reserved.
1+
// Copyright 2020 New Relic, Inc. All rights reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

44

@@ -25,6 +25,7 @@ public class LogEventWireModelCollectionJsonConverter : JsonConverter<LogEventWi
2525
private const string ErrorMessage = "error.message";
2626
private const string ErrorClass = "error.class";
2727
private const string Context = "context";
28+
private const string LabelPrefix = "tags.";
2829

2930
public override LogEventWireModelCollection ReadJson(JsonReader reader, Type objectType, LogEventWireModelCollection existingValue, bool hasExistingValue, JsonSerializer serializer)
3031
{
@@ -50,6 +51,16 @@ private static void WriteJsonImpl(JsonWriter jsonWriter, LogEventWireModelCollec
5051
jsonWriter.WriteValue(value.EntityGuid);
5152
jsonWriter.WritePropertyName(Hostname);
5253
jsonWriter.WriteValue(value.Hostname);
54+
55+
if (value.Labels != null)
56+
{
57+
foreach (var label in value.Labels)
58+
{
59+
jsonWriter.WritePropertyName(LabelPrefix + label.Type);
60+
jsonWriter.WriteValue(label.Value);
61+
}
62+
}
63+
5364
jsonWriter.WriteEndObject();
5465
jsonWriter.WriteEndObject();
5566

src/Agent/NewRelic/Agent/Core/Labels/ILabelsService.cs

+2
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,7 @@ namespace NewRelic.Agent.Core.Labels
99
public interface ILabelsService : IDisposable
1010
{
1111
IEnumerable<Label> Labels { get; }
12+
13+
IEnumerable<Label> GetFilteredLabels(IEnumerable<string> labelsToExclude);
1214
}
1315
}

src/Agent/NewRelic/Agent/Core/Labels/LabelsService.cs

+10-2
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,34 @@ public class LabelsService : ILabelsService
1919

2020
private readonly IConfigurationService _configurationService;
2121

22-
public IEnumerable<Label> Labels { get { return GetLabelsFromConfiguration(); } }
22+
public IEnumerable<Label> Labels { get { return GetLabelsFromConfiguration([]); } }
23+
24+
public IEnumerable<Label> GetFilteredLabels(IEnumerable<string> labelsToExclude)
25+
{
26+
return GetLabelsFromConfiguration(labelsToExclude);
27+
}
2328

2429
public LabelsService(IConfigurationService configurationService)
2530
{
2631
_configurationService = configurationService;
2732
}
2833

29-
private IEnumerable<Label> GetLabelsFromConfiguration()
34+
private IEnumerable<Label> GetLabelsFromConfiguration(IEnumerable<string> labelsToExclude)
3035
{
3136
var labelsString = _configurationService.Configuration.Labels;
3237
if (string.IsNullOrEmpty(labelsString))
3338
return Enumerable.Empty<Label>();
3439

40+
labelsToExclude ??= [];
41+
3542
try
3643
{
3744
var labels = labelsString
3845
.Trim()
3946
.Trim(StringSeparators.SemiColon)
4047
.Split(StringSeparators.SemiColon)
4148
.Select(CreateLabelFromString)
49+
.Where(label => !labelsToExclude.Contains(label.Type, StringComparer.OrdinalIgnoreCase))
4250
.GroupBy(label => label.Type)
4351
.Select(labelGrouping => labelGrouping.Last())
4452
.Take(MaxLabels)

src/Agent/NewRelic/Agent/Core/Metrics/MetricNames.cs

+7
Original file line numberDiff line numberDiff line change
@@ -1111,11 +1111,13 @@ public static string GetLoggingMetricsDeniedName()
11111111
private const string Metrics = "Metrics";
11121112
private const string Forwarding = "Forwarding";
11131113
private const string LocalDecorating = "LocalDecorating";
1114+
private const string Labels = "Labels";
11141115
private const string DotNet = "DotNET";
11151116

11161117
private const string SupportabilityLogMetricsConfigPs = SupportabilityLoggingEventsPs + Metrics + PathSeparator + DotNet + PathSeparator;
11171118
private const string SupportabilityLogForwardingConfigPs = SupportabilityLoggingEventsPs + Forwarding + PathSeparator + DotNet + PathSeparator;
11181119
private const string SupportabilityLogDecoratingConfigPs = SupportabilityLoggingEventsPs + LocalDecorating + PathSeparator + DotNet + PathSeparator;
1120+
private const string SupportabilityLogLabelsConfigPs = SupportabilityLoggingEventsPs + Labels + PathSeparator + DotNet + PathSeparator;
11191121

11201122
public static string GetSupportabilityLogMetricsConfiguredName(bool enabled)
11211123
{
@@ -1127,6 +1129,11 @@ public static string GetSupportabilityLogForwardingConfiguredName(bool enabled)
11271129
return SupportabilityLogForwardingConfigPs + (enabled ? Enabled : Disabled);
11281130
}
11291131

1132+
public static string GetSupportabilityLogLabelsConfiguredName(bool enabled)
1133+
{
1134+
return SupportabilityLogLabelsConfigPs + (enabled ? Enabled : Disabled);
1135+
}
1136+
11301137
public static string GetSupportabilityLogDecoratingConfiguredName(bool enabled)
11311138
{
11321139
return SupportabilityLogDecoratingConfigPs + (enabled ? Enabled : Disabled);

src/Agent/NewRelic/Agent/Core/WireModels/LogEventWireModelCollection.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
// Copyright 2020 New Relic, Inc. All rights reserved.
1+
// Copyright 2020 New Relic, Inc. All rights reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

44
using NewRelic.Agent.Core.JsonConverters;
5+
using NewRelic.Agent.Core.Labels;
56
using Newtonsoft.Json;
67
using System.Collections.Generic;
78

@@ -13,14 +14,17 @@ public class LogEventWireModelCollection
1314
public string EntityName { get; }
1415
public string EntityGuid { get; }
1516
public string Hostname { get; }
17+
public IEnumerable<Label> Labels { get; }
1618

1719
public IList<LogEventWireModel> LoggingEvents { get; }
1820

19-
public LogEventWireModelCollection(string entityName, string entityGuid, string hostname, IList<LogEventWireModel> loggingEvents)
21+
public LogEventWireModelCollection(string entityName, string entityGuid, string hostname,
22+
IEnumerable<Label> labels, IList<LogEventWireModel> loggingEvents)
2023
{
2124
EntityName = entityName;
2225
EntityGuid = entityGuid;
2326
Hostname = hostname;
27+
Labels = labels;
2428
LoggingEvents = loggingEvents;
2529
}
2630
}

src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Configuration/IConfiguration.cs

+2
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,10 @@ public interface IConfiguration
198198
bool LogDecoratorEnabled { get; }
199199
HashSet<string> LogLevelDenyList { get; }
200200
bool ContextDataEnabled { get; }
201+
bool LabelsEnabled { get; }
201202
IEnumerable<string> ContextDataInclude { get; }
202203
IEnumerable<string> ContextDataExclude { get; }
204+
IEnumerable<string> LabelsExclude { get; }
203205
bool AppDomainCachingDisabled { get; }
204206
bool ForceNewTransactionOnNewThread { get; }
205207
bool CodeLevelMetricsEnabled { get; }

0 commit comments

Comments
 (0)