Logging to elmah.io from NLog
NLog is one of the most popular logging frameworks for .NET. With an active history of almost 10 years, the possibilities with NLog are many and it’s easy to find documentation on how to use it.
To start logging messages from NLog to elmah.io, you need to install the Elmah.Io.NLog
NuGet package:
Install-Package Elmah.Io.NLog
dotnet add package Elmah.Io.NLog
<PackageReference Include="Elmah.Io.NLog" Version="5.*" />
paket add Elmah.Io.NLog
Please don't use NLog
4.6.0
since that version contains a bug that causes the elmah.io target to not load correctly.4.5.11
,4.6.1
, or newer.
Configuration in .NET
To configure the elmah.io target, add the following configuration to your app.config/web.config/nlog.config depending on what kind of project you’ve created:
<extensions>
<add assembly="Elmah.Io.NLog"/>
</extensions>
<targets>
<target name="elmahio" type="elmah.io" apiKey="API_KEY" logId="LOG_ID"/>
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="elmahio" />
</rules>
<targets>
<target name="elmahio" type="elmah.io, Elmah.Io.NLog" apiKey="API_KEY" logId="LOG_ID"/>
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="elmahio" />
</rules>
Replace API_KEY
with your API key (Where is my API key?) and LOG_ID
with the ID of the log you want messages sent to (Where is my log ID?).
In the example, we specify the level minimum as Info. This tells NLog to log only information, warning, error, and fatal messages. You may adjust this, but be aware that your elmah.io log may run full pretty fast, especially if you log thousands and thousands of trace and debug messages.
Log messages to elmah.io, just as with every other target and NLog:
log.Warn("This is a warning message");
log.Error(new Exception(), "This is an error message");
Specify API key and log ID in appSettings
If you are already using elmah.io, you may have your API key and log ID in the appSettings
element already. To use these settings from withing the NLog target configuration you can use an NLog layout formatter:
<targets>
<target name="elmahio" type="elmah.io" apiKey="${appsetting:item=apiKey}" logId="${appsetting:item=logId}"/>
</targets>
By using the layout ${appsetting:item=apiKey}
you tell NLog that the value for this attribute is in an appSettings
element named elmahKey
:
<appSettings>
<add key="apiKey" value="API_KEY" />
<add key="logId" value="LOG_ID" />
</appSettings>
The
appSettings
layout formatter only works when targeting .NET Full Framework and requiresElmah.Io.NLog
version 3.3.x or above andNLog
version 4.6.x or above.
IntelliSense
There is support for adding IntelliSense in Visual Studio for the NLog.config
file. Extend the nlog
root element like this:
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:elmahio="http://www.nlog-project.org/schemas/NLog.Targets.Elmah.Io.xsd"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.Targets.Elmah.Io.xsd http://www.nlog-project.org/schemas/NLog.Targets.Elmah.Io.xsd">
<!-- ... -->
</nlog>
Then change the type
attribute in the target to xsi:type
:
<target xsi:type="elmahio:elmah.io" />
Configuration in appsettings.json
.NET 5 (previously Core) and newer switched from declaring XML configuration in app/web/nlog.config
files to JSON configuration in an appsettings.json
file. To configure elmah.io in JSON, install the NLog.Extensions.Logging
NuGet package:
Install-Package NLog.Extensions.Logging
dotnet add package NLog.Extensions.Logging
<PackageReference Include="NLog.Extensions.Logging" Version="1.*" />
paket add NLog.Extensions.Logging
Extend the appsettings.json
file with a new NLog
section:
{
"NLog": {
"throwConfigExceptions": true,
"extensions": [
{ "assembly": "Elmah.Io.NLog" }
],
"targets": {
"elmahio": {
"type": "elmah.io",
"apiKey": "API_KEY",
"logId": "LOG_ID"
}
},
"rules": [
{
"logger": "*",
"minLevel": "Info",
"writeTo": "elmahio"
}
]
}
}
{
"NLog": {
"throwConfigExceptions": true,
"targets": {
"elmahio": {
"type": "elmah.io, Elmah.Io.NLog",
"apiKey": "API_KEY",
"logId": "LOG_ID"
}
},
"rules": [
{
"logger": "*",
"minLevel": "Info",
"writeTo": "elmahio"
}
]
}
}
Replace API_KEY
with your API key (Where is my API key?) and LOG_ID
with the ID of the log you want messages sent to (Where is my log ID?).
If you haven't already loaded the configuration in your application, make sure to do so:
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
Finally, tell NLog how to load the NLog
configuration section:
LogManager.Configuration = new NLogLoggingConfiguration(config.GetSection("NLog"));
elmah.io configuration outside the NLog section
You might not want the elmah.io API key and log Id inside the NLog
section or already have an ElmahIo
section defined and want to reuse that. Splitting up configuration like that is supported through NLog layout renderers:
{
"NLog": {
// ...
"targets": {
"elmahio": {
"type": "elmah.io",
"apiKey": "${configsetting:item=ElmahIo.ApiKey}",
"logId": "${configsetting:item=ElmahIo.LogId}"
}
},
// ...
},
"ElmahIo": {
"ApiKey": "API_KEY",
"LogId": "LOG_ID"
}
}
Notice how the value of the apiKey
and logId
parameters have been replaced with ${configsetting:item=ElmahIo.*}
. At the bottom, the ElmahIo
section wraps the API key and log Id.
To make this work, you will need an additional line of C# when setting up NLog logging:
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
ConfigSettingLayoutRenderer.DefaultConfiguration = config; // 👈 add this line
LogManager.Configuration = new NLogLoggingConfiguration(config.GetSection("NLog"));
IntelliSense
There is support for adding IntelliSense in Visual Studio for the NLog
section in the appsettings.json
file. Copy and paste the following link into the Schema textbox above the file content:
https://nlog-project.org/schemas/appsettings.schema.json
Configuration in code
The elmah.io target can be configured from C# code if you prefer or need to access the built-in events (see more later). The following adds logging to elmah.io:
var config = new LoggingConfiguration();
var elmahIoTarget = new ElmahIoTarget();
elmahIoTarget.Name = "elmahio";
elmahIoTarget.ApiKey = "API_KEY";
elmahIoTarget.LogId = "LOG_ID";
config.AddTarget(elmahIoTarget);
config.AddRuleForAllLevels(elmahIoTarget);
LogManager.Configuration = config;
The example will log all log levels to elmah.io. For more information about how to configure individual log levels, check out the NLog documentation on GitHub.
Logging custom properties
NLog supports logging custom properties in multiple ways. If you want to include a property (like a version number) in all log messages, you might want to look into the OnMessage
feature on Elmah.Io.NLog
.
With custom properties, you can log additional key/value pairs with every log message. The elmah.io target for NLog supports custom properties as well. Properties are persisted alongside every log message in elmah.io and searchable if named correctly.
One way to log custom properties with NLog and elmah.io is to use the overload of each logging-method that takes a LogEventInfo
object as a parameter:
var infoMessage = new LogEventInfo(LogLevel.Info, "", "This is an information message");
infoMessage.Properties.Add("Some Property Key", "Some Property Value");
log.Info(infoMessage);
This saves the information message in elmah.io with a custom property with key Some Property Key
and value Some Property Value
.
As of NLog 4.5, structured logging is supported as well. To log a property as part of the log message, use the new syntax as shown here:
log.Warn("Property named {FirstName}", "Donald");
In the example, NLog will log the message Property named "Donald"
, but the key (FirstName
) and value (Donald
), will also be available in the Data tab inside elmah.io.
Elmah.Io.NLog
provides a range of reserved property names, that can be used to fill in data in the correct fields on the elmah.io UI. Let's say you want to fill the User
field using structured logging only:
log.Info("{Quote} from {User}", "Hasta la vista, baby", "T-800");
This will fill in the value T-800
in the User
field, as well as add two key/value pairs (Quote
and User
) to the Data tab on elmah.io. For a reference of all possible property names, check out the property names on CreateMessage.
NLog also provides a fluent API (available in the NLog.Fluent
namespace) that some might find more readable:
logger.Info()
.Message("I'll be back")
.Property("User", "T-800")
.Write();
If you want to use the normal logging methods like Info
and Error
, you can do so injunction with the MappedDiagnosticsLogicalContext
class, also provided by NLog:
using (MappedDiagnosticsLogicalContext.SetScoped("User", "T-800"))
{
logger.Info("I'll be back");
}
This will create the same result as the example above.
In NLog 5 MappedDiagnosticsLogicalContext
has been deprecated in favor of a scope context:
using (logger.PushScopeProperty("User", "T-800"))
{
logger.Info("I'll be back");
}
Setting application name
The application field on elmah.io can be set globally using NLog's global context:
GlobalDiagnosticsContext.Set("Application", "My application name");
Setting version number
The version field on elmah.io can be set globally using NLog's global context:
GlobalDiagnosticsContext.Set("Version", "1.2.3");
Setting category
elmah.io provide a field named Category to better group log messages by class name, namespace, or similar. Category maps to NLog's logger field automatically when using Elmah.Io.NLog
. The category field can be overwritten using a scoped property (NLog 5):
using (logger.PushScopeProperty("category", "The category"))
{
logger.Info("This is an information message with category");
}
Message hooks
Elmah.Io.NLog
provides message hooks similar to the integrations with ASP.NET and ASP.NET Core. Message hooks need to be implemented in C#. Either configure the elmah.io target in C# or fetch the target already configured in XML:
var elmahIoTarget = (ElmahIoTarget)LogManager.Configuration.FindTargetByName("elmahio");
You also need to install the most recent version of the Elmah.Io.Client
NuGet package to use message hooks.
Decorating log messages
To include additional information on log messages, you can use the OnMessage
action:
elmahIoTarget.OnMessage = msg =>
{
msg.Version = "1.0.0";
};
The example above includes a version number on all errors.
Include source code
You can use the OnMessage
action to include source code to log messages. This will require a stack trace in the Detail
property with filenames and line numbers in it.
There are multiple ways of including source code to log messages. In short, you will need to install the Elmah.Io.Client.Extensions.SourceCode
NuGet package and call the WithSourceCodeFromPdb
method in the OnMessage
action:
elmahIoTarget.OnMessage = msg =>
{
msg.WithSourceCodeFromPdb();
};
Check out How to include source code in log messages for additional requirements to make source code show up on elmah.io.
Including source code on log messages is available in the
Elmah.Io.Client
v4 package and forward.
Handle errors
To handle any errors happening while processing a log message, you can use the OnError event when initializing the elmah.io target:
elmahIoTarget.OnError = (msg, err) =>
{
// Do something here
};
The example implements a callback if logging to elmah.io fails. How you choose to implement this is entirely up to your application and tech stack.
Error filtering
To ignore specific errors based on their content, you can use the OnFilter event when initializing the elmah.io target:
elmahIoTarget.OnFilter = msg =>
{
return msg.Title.Contains("trace");
};
The example above ignores any log messages with the word trace
in the title.
Include HTTP context in ASP.NET and ASP.NET Core
When logging through NLog from a web application, you may want to include HTTP contextual information like the current URL, status codes, server variables, etc. NLog provides two web-packages to include this information. For ASP.NET, MVC, and Web API you can install the NLog.Web
NuGet package and include the following code in web.config
:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="NLog" type="NLog.Web.NLogHttpModule, NLog.Web" />
</modules>
</system.webServer>
For ASP.NET Core you can install the NLog.Web.AspNetCore
NuGet package. When installed, the elmah.io NLog target automatically picks up the HTTP context and fills in all possible fields on messages sent to elmah.io.
Troubleshooting
Here are some things to try out if logging from NLog to elmah.io doesn't work:
- Run the
diagnose
command with the elmah.io CLI as shown here: Diagnose potential problems with an elmah.io installation. - Make sure that you have the newest
Elmah.Io.NLog
andElmah.Io.Client
packages installed. - Make sure to include all of the configuration from the example above. That includes both the
<extensions>
(Only needed inElmah.Io.NLog
3 and 4),<targets>
, and<rules>
element. - Make sure that the API key is valid and allow the Messages | Write permission.
- Make sure to include a valid log ID.
- Make sure that you have sufficient log messages in your subscription and that you didn't disable logging to the log or include any ignore filters/rules.
- Always make sure to call
LogManager.Shutdown()
before exiting the application to make sure that all log messages are flushed. If you are re-using the logger but only want to shut down the thread that is logging messages or similar you can callLogManager.Flush()
which will store all batched messages. - Extend the
nlog
element withinternalLogLevel="Warn" internalLogFile="c:\temp\nlog-internal.log
and inspect that log file for any internal NLog errors.
System.IO.FileLoadException: Could not load file or assembly 'Newtonsoft.Json'
If you see this error in the internal NLog file it means that there's a problem with multiple assemblies referencing different versions of Newtonsoft.Json
(also known as NuGet dependency hell). This can be fixed by redirecting to the installed version in the App.config
or Web.config
file:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-13.0.1.0" newVersion="13.0.1.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
This article was brought to you by the elmah.io team. elmah.io is the best error management system for .NET web applications. We monitor your website, alert you when errors start happening, and help you fix errors fast.
See how we can help you monitor your website for crashes Monitor your website