The .NET Extensions for Serilog will add contextual information from the .NET Agent to Application Log Messages, output them in the New Relic's expected JSON format, and send them to the New Relic Logging endpoint.
- Microsoft .NET Framework 4.6.2+ or .NET 6.0+
- New Relic .NET Agent 8.21+
- New Relic .NET Agent API 8.21+
- Serilog 2.5+
- Serilog File Sinks v4.0+
When application code logs a message, Serilog creates a LogEvent
. Serilog can add additional information to Log Events using Enrichers. Sinks and Formatters format and output Log Events for downstream consumption and/or viewing. The New Relic Log Forwarder monitors an output folder and incrementally sends log information to New Relic.
The NewRelicEnricher
adds contextual information from the .NET Agent (using the API) to the Log Events generated by the application. This contextual information, known as Linking Metadata, is used by New Relic to link log messages to the transactions and spans from which they were created.
The NewRelicFormatter
translates enriched Log Events into the JSON format expected by New Relic. A Sink
instructs Serilog to output the JSON to a file in the location that the Log Forwarder expects.
The Log Forwarder monitors an output folder and incrementally sends New Relic formatted Log information to the New Relic Logging Endpoint. There are many log-forwarders available. For our examples, we will use fluentd.
Configure the NewRelicEnricher
by adding the NewRelicEnricher
to the Serilog configuration as described below. There are no configuration options for the NewRelicEnricher
.
var loggerConfig = new LoggerConfiguration()
loggerConfig
.Enrich.WithNewRelicLogsInContext();
It is common to have multiple enrichers adding information to log messages. In this example, two additional enrichers append Thread Id and Name to the Log Events generated by the application code. When adding additional enrichers, it is recommended to configure the NewRelicEnricher as the last enricher.
var loggerConfig = new LoggerConfiguration()
loggerConfig
.Enrich.WithThreadName()
.Enrich.WithThreadId()
.Enrich.WithNewRelicLogsInContext();
Below is an example configuration using a basic Serilog File Output Sink with the NewRelicFormatter
.
var loggerConfig = new LoggerConfiguration()
loggerConfig
.Enrich.WithThreadName()
.Enrich.WithThreadId()
.Enrich.WithNewRelicLogsInContext()
.WriteTo.File(
formatter: new NewRelicFormatter(),
path: @"C:\logs\SerilogExample.log.json");
var log = loggerConfig.CreateLogger();
Since the JSON files are written to disk, some of these configuration options may be useful in managing the amount of disk space used and/or the performance of the Sink.
restrictedToMinimumLevel
buffered
rollingInterval
rollOnFileSizeLimit
retainedFileCountLimit
Though not required, using the Serilog Asynchronous Sink Wrapper may help improve the performance by performing formatting and output of log files to a different thread.
Based on the Fornatter and Sink Configuration described above, the following Fluentd configuration can be used to send logs to New Relic.
<!--NewRelicLoggingExample.conf-->
<source>
@type tail
path C:\logs\SerilogExample.log.json
pos_file C:\logs\SerilogExample.log.json.pos
tag logfile.*
<parse>
@type json
</parse>
</source>
<match **>
@type newrelic
license_key <YOUR NEW_RELIC_LICENSE_KEY>
base_uri https://log-api.newrelic.com/log/v1
</match>
In addition to the linking metadata obtained from the .NET Agent, the NewRelicFormatter
automatically adds the following properties, if applicable, to each LogEvent
.
- Timestamp
- Error Message
- Error Class
- Error Stack Trace
- Message Text
- Message Template
- Log Level
Other enrichers may be used to provide additional contextual information. For enrichers that provide the following fields, the New Relic Formatter should be configured to identify these properties.
- Thread Id
- Thread Name
- File Name
- Namespace
- Class Name
- Method Name
- Line Number
In this example configuration, the ThreadId and ThreadName enrichers add the additional properties ThreadId
and ThreadName
to each LogEvent
. Correspondingly, the NewRelicFormatter
is configured to identify these fields.
var loggerConfig = new LoggerConfiguration()
loggerConfig
.Enrich.WithThreadName() //Captures the Thread Id and adds it as the "ThreadId" property to the LogEvent
.Enrich.WithThreadId() //Captures the Thread Name and adds it as the "ThreadName" property to the LogEvent
.Enrich.WithNewRelicLogsInContext()
.WriteTo.File(
path: @"C:\logs\SerilogExample.log.json",
formatter: new NewRelicFormatter()
.WithPropertyMapping("ThreadId", NewRelicLoggingProperty.ThreadId) //Identifies the "ThreadId" Property on the Log Event as the Thread Id
.WithPropertyMapping("ThreadName", NewRelicLoggingProperty.ThreadName) //Identifies the "ThreadName" Property on the Log Event as the Thread Name
);
var log = loggerConfig.CreateLogger();
The NewRelicFormatter
automatically adds all custom properties to Log Events. These properties are visible in New Relic Logging under the "Message Properties" section. There are two methods of adding custom properties: (1) a custom enricher; or (2) a message template.
For exmple, the following log message template will result in the custom properties of FirstName
and nbr
log.Debug("Hello {FirstName}, you are number {nbr} on my list", "Bob", 32);
Configuration of the New Relic extensions for Serilog may be accomplished with file based configuration providers.
The example code below creates a logger based on settings contained in an appSettings.json
file.
The following NuGet Packages are required:
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
var configuration = builder.Build();
var logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console","Serilog.Sinks.File","NewRelic.LogEnrichers.Serilog" ],
"MinimumLevel": "Debug",
"Enrich": [ "WithNewRelicLogsInContext" ],
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "C:\\Logs\\SerilogExample.log.json",
"formatter": "NewRelic.LogEnrichers.Serilog.NewRelicFormatter, NewRelic.LogEnrichers.Serilog"
}
}
],
"Properties": {
"Application": "NewRelic Logging Serilog Example"
}
}
}
The example code below creates a logger based on settings contained in a .config
file.
The following NuGet Package is required:
var logger = new LoggerConfiguration()
.ReadFrom.AppSettings()
.CreateLogger();
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="serilog:using:NewRelic" value="NewRelic.LogEnrichers.Serilog" />
<add key="serilog:using:File" value="Serilog.Sinks.File" />
<!--Add other enrichers here-->
<add key="serilog:enrich:WithNewRelicLogsInContext" />
<add key="serilog:write-to:File.path" value="C:\logs\SerilogExample.log.json" />
<add key="serilog:write-to:File.formatter" value="NewRelic.LogEnrichers.Serilog.NewRelicFormatter, NewRelic.LogEnrichers.Serilog" />
</appSettings>