Categories
Uncategorized

Adding File Logging to a .NET ScriptLink Project with NLog

Last week, we added NLog to our .NET ScriptLink project to generate debugger log entries. Having NLog now configured and in place we can consider additional targets for the log entries. These targets include: the console, file, mail, databases, and more. Today, we are going to expand our configuration to use a file target, setup filters, and auto-archiving.

Last week, we added NLog to our .NET ScriptLink project to generate debugger log entries. Having NLog now configured and in place we can consider additional targets for the log entries. These targets include: the console, file, mail, databases, and more. Today, we are going to expand our configuration to use a file target, setup filters, and auto-archiving.

A key advantage of this setup is that we can change or add logging targets without having to modify the log entries scattered throughout our project. This means that since we have already created our log entries all we have to do is update our NLog configuration to add our new targets. Let’s go.

The Specification

For this example, we are going to create three file targets to collect the log entries from our Web Services (*.asmx), Commands, and Factory.

  • Api: api.log
  • Commands: api.commands.log
  • Factories: api.factories.log

In other implementations I have also created targets for my repositories and services.

We will also setup these log files to archive daily and keep up to 14 days of logs in the archive.

Add the Targets

First, let’s add our new targets to the web.config file.

    <targets async="true">
      <target name="debugger" xsi:type="Debugger" />
      <target name="file.api" xsi:type="File"
              fileName="\Logs\RS.ScriptLinkDemo\api.log"
              archiveFileName="\Logs\RS.ScriptLinkDemo\Archive\Api\api.{#}.log"
              archiveEvery="Day"
              archiveNumbering="Date"
              maxArchiveFiles="14"
              concurrentWrites="true"
              keepFileOpen="false"
              encoding="UTF8" />
      <target name="file.api.commands" xsi:type="File"
              fileName="\Logs\RS.ScriptLinkDemo\api.commands.log"
              archiveFileName="\Logs\RS.ScriptLinkDemo\Archive\Api\api.commands.{#}.log"
              archiveEvery="Day"
              archiveNumbering="Date"
              maxArchiveFiles="14"
              concurrentWrites="true"
              keepFileOpen="false"
              encoding="UTF8" />
      <target name="file.api.factories" xsi:type="File"
              fileName="\Logs\RS.ScriptLinkDemo\api.factories.log"
              archiveFileName="\Logs\RS.ScriptLinkDemo\Archive\Api\api.factories.{#}.log"
              archiveEvery="Day"
              archiveNumbering="Date"
              maxArchiveFiles="14"
              concurrentWrites="true"
              keepFileOpen="false"
              encoding="UTF8" />
    </targets>

By design I have nested the commands and factories under the api logs. You can see that when they are archived they will also go into a common Api folder. I have found this helpful when I was logging other activities such as those from my repositories and services. Each of which was given their own section.

You can also see the various configurations describe in the specification. I like to use the name of the project in the logging path to keep my logs for conflicting with each other. Typically, this would only apply to you development system, but if you run multiple applications on the same web server this could be helpful as well.

The above configuration can be customized with variables.

Ok. Now we need to setup the rules.

Add the Logging Rules

These rules will allow us to filter all of the log entries being generated and write them to the correct log file.

    <rules>
      <logger name="*" minlevel="Debug" writeTo="debugger" />
      <logger name="RS.ScriptLinkDemo.Soap.api.*" minlevel="Debug" writeTo="file.api" />
      <logger name="RS.ScriptLinkDemo.Soap.Commands.*" minlevel="Debug" writeTo="file.api.commands" />
      <logger name="RS.ScriptLinkDemo.Soap.Factories.*" minlevel="Debug" writeTo="file.api.factories" />
    </rules>

Based on the above configuration we should see all of our log entries (“*”) be written to the debugger logger, but the file loggers will only be written to based on the namespace of the class that is logging.

Testing

As with each of our previous changes, it is time to test. Let’s submit a request from our SoapUI or Postman samples and see if the log entries are entered as expected. I will test my DefaultCommand first by sending in an invalid parameter. This should generate log entries in the debugger and each of the files.

Debugger Target

You should see something similar to below in your debugger log.

If so, then let’s check on our file logs.

File Targets

Let’s check the directory we established to see if our log files are there.

Hopefully, you see the three files where you expected them. Next, let’s open them up and make sure the entries match what we expect to find in them. If so, great!

Next Steps

Test, test, test. Try sending a request to your HelloWorld command. Try sending a request to your v1 or v2 web service, if you made one. Try sending a GetVersion request. You should see all of these entries filtered into their proper locations.

There are other things you can try to make this logging solution more robust.

There are many options to tailor this to your environment and needs. You should now have all of the basics to meet your logging needs. Please let me know how it goes. Are there any other logging configuration examples that you would like to see?