- 8 minutes to read

How to implement Log4Net in a new .NET Application

Package on NuGet.org

Info

This page describes the steps about how to add the Nodinite Log4Net Appender to your Visual Studio Project and Log events using the Log4Net framework to Nodinite.

Tip

You should use the SeriLog if you are using .NET.

Step 1. Add package reference

Start by adding the Nodinite Log4Net Nuget Package to your Visual Studio project.
Add NuGet Package Reference

Step 2. Create handle

Then, add a variable to acquire a handle to the Log4Net instance:

var log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 

.NET Framework.

Step 3. Include namespace

Resolve the issue with the missing namespace by adding a using declaration at the top of the Class:

using log4net;

.NET Framework.

Step 4. Add the Log4Net configuration file

Add the log4net.config file, make sure to set Copy to Output Directory to Copy Always.

Tip

You can override the configuration values in code!

Step 5. Manage Log4Net configuration

Add the following default XML configuration to the log4net.config file (Change values to your liking or even better, overide the run-time values in code).

The fields map 1:1 with the Nodinite JSON Log Event. LogText (Exception object) and LogStatus are implicitly provided in your code.

Note

The message param is the Body (Payload) in Nodinite.

    <log4net>
	<appender name="Log4NetAppender" type="Nodinite.LogAgent.Log4NetAppender.NodiniteLog4NetAppender,Nodinite.LogAgent.Log4NetAppender">
		<!-- Custom Parameters -->
		<OriginalMessageType value="Nodinite.LogAgent.Log4NetAppender/2.0#DefaultMessageType" />
		<MessageTypeExtractFromBody value="false" />
		<LogAgentID value="101"/>
		<EventNumber value="0"/>
		<EventDirection value="66">
		<EndPointDirection value="-2">
		<EndPointName value="Log4Net Unit Test"/>
		<EndPointUri value="VS.local.log4net.test"/>
		<!--<LogText value =""/>--> <!-- Should not be part of the configuration file -->
		<!--<LogStatusId value ="255"/>--> <!-- Should not be part of the configuration file -->
		<ProcessingUser value="Administrator"/>
		<ModuleType value="unit test"/>
		<ProcessName value="unit test"/>
		<ProcessingMachineName value="DEV"/>
		<ProcessingModuleType value="unit test"/>
		<ProcessingModuleName value="VS"/>
		<LogApiServiceURI value="http://localhost/Nodinite/Dev/logapi"/>
		<ApplicationInterchangeId value="RUN-123">
		<LocalInterchangeId value="{E4CDDF18-9925-4A79-8E9F-71BC0D4C5172}"/>
		<ServiceInstanceActivityId value="{E4CDDF18-9925-4A79-8E9F-71BC0D4C5173}"/>
	</appender>
	<root>
		<level value="DEBUG"/>
		<appender-ref ref="Log4NetAppender"/>
	</root>
</log4net>

Important

Ensure the LogApiServiceURI has a valid address (URI) to the Nodinite Log API OR has a folder that exist on disk. Make sure the path ends with a trailing backslash \.

LogApiServiceURI

LogApiServiceURI Comment
Log API http://localhost/Nodinite/Dev/logapi NOTE: The call is synchronous, hence, if service is unavailable, you will lose the log event
Folder New 6.0.8 c:\temp\ Ensure the target folder is highly available. Use the Nodinite Pickup service to consume the files, and use the Nodinite File Monitoring Agent to ensure the files do not stockpile

What's new New 6.0.8

  • LogText can be set unless you are logging an Exception.

    Note

    Internally, the LogText is set to null on each Log. You must set it again for each event to be logged.

  • ApplicationInterchangeId can be a string.

  • EndPointDirection can be set, the default is "None (-2)".

  • EventDirection can be set, default is "Process Outgoing (66)".

  • All config values except MessageTypeExtractFromBody can be overriden in code.

  • LogApiServiceURI can either be a folder, or an address to the Nodinite Log API.

  • SequenceNo can be set programmatically (should not be set in config file).

What's new New 6.0.0.9

You can set the Log4Net Appender to legacy mode, by default, this option is turned off. We built the Logger to Log Business Transactions. If you set this property to true, then Hello World is in the Log Text field if your code is: _logger.LogInformation("Hello World");.

This is a new property to set in the config file

<LogMessageObjectAsLogText value="true"/>

Set the LogMessageObjectAsLogText to true to enable legacy mode.

Note

This settings also affects logging errors!

Step 6. Add Watcher

Add the line: [assembly: log4net.Config.XmlConfigurator(Watch = true)] to the Properties.cs file to make sure Log4Net reflects changes in the configuration- file during run-time.

Step 7. Initialize Log4Net configuration

The code is different depending on the type of .NET run-time:

.NET Framework

Example using a .NET Framework (>4.6.2) solution.

var fileInfo = new System.IO.FileInfo("log4net.config");
XmlConfigurator.Configure(fileInfo);

.NET

Worker service example.

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddLogging(loggingBuilder =>
        {
            loggingBuilder.AddLog4Net();
        });
        services.AddHostedService<Worker>();
    })
    .Build();

Step 8. Perform Logging

Perform the logging by adding code depending on the different Log levels, view examples below:

Log using the .NET Framework

// Message is payload (Body)
log.Info("<Orders><Order><Id>123</Id></Order></Orders>");
log.Error("<Orders><Order><Id>123</Id></Order></Orders>", new Exception("Guru Meditation"));

Here's an example of a simple .NET Framework-based Logging example.

Log using .NET

public class Worker
 {
     private readonly ILogger<Worker> _logger;

     public Worker(ILogger<Worker> logger)
     {
         _logger = logger;
     }
     
     public void DoLogging()
     {
		_logger.LogInformation("<Orders><Order><Id>{OrderId}</Id></Order></Orders>", 123);
		_logger.LogError(ex, "<Orders><Order><Id>{OrderId}</Id></Order></Orders>", 123);
     }
}

Here's an example of a simple .NET-based Logging example.

Error example (.NET)

Override Configuration Values

New 6.0.8 All configuration values can be overriden using the name as in the example below:

log4net.GlobalContext.Properties["ApplicationInterchangeId"] = "RUN-456";
log4net.GlobalContext.Properties["SequenceNo"] = 1;
log.Info("<Orders><Order><Id>123</Id></Order></Orders>");

.NET Framework example.

log4net.GlobalContext.Properties["ApplicationInterchangeId"] = "RUN-456";
log4net.GlobalContext.Properties["SequenceNo"] = 2;
log4net.GlobalContext.Properties["LogText"] = "Finished Processing";
_logger.LogInformation(""); //NOTE: LogText is set on previous row

.NET Framework example.

Add any Context

New 6.0.8 You can add arbitrary Key/Values as to the Context. These will appear as Context Properties in the Nodinite Log Event. Then, you can create Search Fields and use the Message Context Key plugin to have Nodinite extract the values. Use this in Nodinite Log Views and allow your business users to gain access, and search for data.

log4net.GlobalContext.Properties["Context"] = new Dictionary<string, string>
 {
     { "Key1", "Value1" },
     { "Key2", "Value2" }
 };

Info

The code above applies to .NET and .NET Framework.

Example overriding all config values

log4net.GlobalContext.Properties["LogText"] = "SAMPLE:LogText"; // NOTE: This value is reset after EACH log event
log4net.GlobalContext.Properties["LogApiServiceURI"] = "C:\\Code\\Nodinite.LogAgent.Log4NetAppender\\Nodinite.LogAgent.Log4NetAppender.TestClient\\Logs\\"; // Should "only" be set in config file
log4net.GlobalContext.Properties["SequenceNo"] = "0"; // This value should be set +1 before any Log operation

log4net.GlobalContext.Properties["ApplicationInterchangeId"] = "SAMPLE:ApplicationInterchangeId";
log4net.GlobalContext.Properties["OriginalMessageType"] = "SAMPLE:OriginalMessageType";
log4net.GlobalContext.Properties["LogAgentID"] = "42";
log4net.GlobalContext.Properties["EventDirection"] = "1";
log4net.GlobalContext.Properties["EventNumber"] = "1337";
log4net.GlobalContext.Properties["EndPointDirection"] = "1";
log4net.GlobalContext.Properties["EndPointName"] = "SAMPLE:EndPointName";
log4net.GlobalContext.Properties["EndPointUri"] = "SAMPLE:EndPointUri";
log4net.GlobalContext.Properties["ProcessingUser"] = "SAMPLE:ProcessingUser";
log4net.GlobalContext.Properties["ModuleType"] = "SAMPLE:ModuleType";
log4net.GlobalContext.Properties["ProcessName"] = "SAMPLE:ProcessName";
log4net.GlobalContext.Properties["ProcessingMachineName"] = "SAMPLE:ProcessingMachineName";
log4net.GlobalContext.Properties["ProcessingModuleType"] = "SAMPLE:ProcessingModuleType";
log4net.GlobalContext.Properties["ProcessingModuleName"] = "SAMPLE:ProcessingModuleName";
log4net.GlobalContext.Properties["ApplicationInterchangeId"] = "SAMPLE:ApplicationInterchangeId";
log4net.GlobalContext.Properties["LocalInterchangeId"] = "SAMPLE:LocalInterchangeId";
log4net.GlobalContext.Properties["ServiceInstanceActivityId"] = "SAMPLE:ServiceInstanceActivityId";

log4net.GlobalContext.Properties["Context"] = new Dictionary<string, string>
{
    { "Key1", "Value1" },
    { "Key2", "Value2" }
};

Please review the JSON Log Event user guide for details about each entry.


Next step

Log API