Now that the underlying structure has been created, it’s time to create a simple (very simple) application class to test that the microservice (or mini application at this stage) is working.
This post is going to concentrate on the following three items.
- Creating a mini application class
- Setting up the Program.cs
- Setting up logging in the appsettings.json file
1. Creating a simple output class.
This class is simply going to output “Hello Console/Debug Azure!” to the various logging outputs as setup in the appsetting.json file.
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
namespace Microservice.Application
{
public class MyApplication
{
private readonly ILogger<MyApplication> _logger;
private readonly IConfiguration _configuration;
public MyApplication(ILogger<MyApplication> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
public void Run()
{
try
{
//This is output to the console
_logger.LogInformation("Hello Console Azure");
//This is output to the file
_logger.LogDebug("Hello Debug Azure");
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
}
}
}
}
In the above I’ve created private properties for IConfiguration (Which I’ll use in a later post for getting some values from Azure AppConfiguration) and the Microsoft ILogger extension, which will be used to output the Run method to the log file.
In the constructor, I’m injecting an ILogger for the class and an instance of IConfiguration and setting the previous private properties to these parameters.
Finally, a method Run is created that uses the injected Logger to write to the log as an Information entry and to Console.Writeline, so I can see it’s working in Visual Studio Code at a glance.
I may refactor this at a later point.
2. Setting up Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using NLog.Extensions.Logging;
using Microservice.Application;
using NLog;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
public class Program
{
private static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile(Path.Combine(AppContext.BaseDirectory, "appsettings.json"), optional: false, reloadOnChange: true)
.Build();
var logger = LogManager.Setup()
.SetupExtensions(s => s.RegisterConfigSettings(configuration))
.LoadConfigurationFromSection(configuration)
.GetCurrentClassLogger();
ServiceCollection services = new ();
services.AddTransient<MyApplication>();
services.AddLogging(loggingBuilder =>
{
loggingBuilder.ClearProviders();
loggingBuilder.AddNLog();
});
services.AddSingleton<IConfiguration>(configuration);
var provider = services.BuildServiceProvider();
var app = provider.GetRequiredService<MyApplication>();
app.Run();
}
}
3. Setting up logging in the appsettings.json file
In this file I’ve setup layouts, targets and rules for logging. This enables me to do something a little more involved than just writing Console outputs.
{
"NLog": {
"throwConfigExceptions": true,
"internalLogLevel": "Info",
"internalLogFile": "${tempDir}/nlog-internal-${shortdate}.log",
"targets": {
"logfile": {
"type": "File",
"fileName": "${basedir}/logs/nlog-${level}-${shortdate}.log",
"layout": {
"type": "JsonLayout",
"Attributes": [
{ "name": "timestamp", "layout": "${date:format=o}" },
{ "name": "level", "layout": "${level}" },
{ "name": "logger", "layout": "${logger}" },
{ "name": "message", "layout": "${message:raw=true}" },
{ "name": "properties", "encode": false, "layout": { "type": "JsonLayout", "includeallproperties": "true" } }
]
}
},
"logconsole": {
"type": "Console",
"layout": "${date}|${level:uppercase=true}|${message} ${exception:format=tostring}|${logger}|${all-event-properties}"
}
},
"rules": [
{
"logger": "*",
"maxLevel": "Debug",
"finalMinLevel":"Debug",
"writeTo": "logfile"
},
{
"logger": "*",
"minLevel": "Info",
"writeTo": "logconsole"
}
]
}
}
In this file I’ve setup 2 targets (one for logging info, warn, error and fatal to the consoles and one to log any debugging levels to a file) in order to show how to direct outputs to different areas (targets).
I’ve also set the formatting to demonstrate different levels of information that can be logged.
Caveat : NLog
For me, NLog had a few things that weren’t clear at first (second, or third!) read of the documentation.
Firstly, the casing in the preset variables. I saw ${baseDir}
described as ${BaseDir}
and ${basedir}
Secondly, whether I needed to use async:true
, or use an asyncwrapper target in a console application – I didn’t. I experimented as I wasn’t getting the correct (or any) outputs to the target, this turned out to be….
Thirdly, the rules, I eventually found the correct rule order, and sorted out the confusing finalMinLevel
, (which acts as maxLevel
and final:true
). This enables me to direct _logger.LogDebug
to a file and stop going through the rules, and any other LogInformation
/Etc to the next target.
In the next part, I’ll be setting some variables in Azure AppConfiguration and accessing them from within the application