Why is logging so important during the application development? Well, while your application is in the developing stage it is very easy to debug the code and to find out what went wrong. But, can you debug in the production environment?
Of course not.
That is why logging messages are a great way to find out what went wrong and where did errors happen in your code in the production environment. .NET Core has its own implementation of logging messages, but in all my projects, I prefer to create my own custom logger service.
If you want to see all the basic instructions and complete navigation for this series, please follow the following link: Introduction page for this tutorial.
For the previous part check out: Creating .NET Core WebApi project – Basic code preparations
So, let’s start.
Creating Required Projects
Let’s create two new projects. Name the first one Contracts
. We are going to store interfaces inside this project. Name the second one LoggerService
. We are going to use it for the logger logic.
To create a new project:
- Right-click on the solution window,
- Choose
Add
and thenNewProject
. We are going to choose the Class Library and name itContracts
:
Do the same thing for the second project, just name it LoggerService
.
With these two new projects in place, we need to reference the LoggerService
project to the main project.
To do that, we have to:
- Navigate to the main project inside solution explorer
- Right-click on the Dependences and choose the Add Project Reference
- Under the Projects click the Solution and choose the LoggerService
Before we proceed to the implementation of the LoggerService, let’s do one more thing.
In the LoggerService project right click on the Dependencies and then click on the Add Project Reference. Inside check the Contracts checkbox to import its reference inside the LoggerService project. This would automatically add the Contracts reference to the main project because we already have a LoggerService referenced inside.
Creating the Interface and Installing NLog
Our logger service will contain four methods for logging:
- Info messages
- Debug messages
- Warning messages
- And error messages
Additionally, we are going to create the interface ILoggerManager
inside the Contracts
project containing those four method definitions.
So, let’s create the ILoggerManager
interface and modify it:
namespace Contracts { public interface ILoggerManager { void LogInfo(string message); void LogWarn(string message); void LogDebug(string message); void LogError(string message); } }
Before we implement this interface inside the LoggerService
project, we need to install the NLog
library in our LoggerService
project. NLog
is a logging platform for the .NET which will help us create and log our messages.
We are going to show you two ways to add the NLog
library to our project.
For the first way, in the LoggerService
project, right-click on the Dependences
, and choose Manage NuGet Packages
. After the NuGet window appears, we have to install the required library:
The second option – from the View menu choose the Other Windows and then click on the Package Manager Console. After the console appears just follow the leads:
Install-Package NLog.Extensions.Logging
After a couple of seconds, NLog
is up and running in your app.
Implementing Interface and Nlog.Config File
In the LoggerService
project, let’s create a new LoggerManager
class.
Then, let’s modify that class:
using Contracts; using NLog; namespace LoggerService { public class LoggerManager : ILoggerManager { private static ILogger logger = LogManager.GetCurrentClassLogger(); public void LogDebug(string message) => logger.Debug(message); public void LogError(string message) => logger.Error(message); public void LogInfo(string message) => logger.Info(message); public void LogWarn(string message) => logger.Warn(message); } }
NLog needs to have information about the folder to create log files in, what the name of these files will be, and what a minimum level of logging is. Therefore, we need to create one text file in the main project with the name nlog.config
and populate it. You need to change the path of the internal log
and filename
parameters to your paths:
<?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" autoReload="true" internalLogLevel="Trace" internalLogFile="d:Projects\Blog-AccountOwner\Project\internal_logs\internallog.txt"> <targets> <target name="logfile" xsi:type="File" fileName="d:/Projects/Blog-AccountOwner/Project/logs/${shortdate}_logfile.txt" layout="${longdate} ${level:uppercase=true} ${message}"/> </targets> <rules> <logger name="*" minlevel="Debug" writeTo="logfile" /> </rules> </nlog>
Configuring Logger Service for Logging Messages
Setting up the configuration for a logger service is quite easy.
We have to modify the Program
class:
using NLog; var builder = WebApplication.CreateBuilder(args); LogManager.LoadConfiguration(string.Concat(Directory.GetCurrentDirectory(), "/nlog.config"));
For the .NET 5 version, we can modify the Startup
constructor:
public Startup(IConfiguration configuration) { LogManager.LoadConfiguration(String.Concat(Directory.GetCurrentDirectory(), "/nlog.config")); Configuration = configuration; }
Secondly, we need to add the logger service inside the .NET Core’s IOC container. There are three ways to do that:
- By calling
builder.Services.AddSingleton
will create the service the first time you request it and then every subsequent request is calling the same instance of the service. This means that all components are sharing the same service every time they need it. You are using the same instance always - By calling
builder.Services.AddScoped
will create the service once per request. That means whenever we send the HTTP request towards the application, a new instance of the service is created - By calling
builder.Services.AddTransient
will create the service each time the application request it. This means that if during one request towards our application, multiple components need the service, this service will be created again for every single component which needs it
So, in the ServiceExtensions
class we are going to add a new method:
public static void ConfigureLoggerService(this IServiceCollection services) { services.AddSingleton<ILoggerManager, LoggerManager>(); }
Lastly, in the Program
class, we have to invoke this extension method, right above builder.Services.AddControllers()
:
builder.Services.ConfigureLoggerService();
Every time we want to use a logger service, all we need to do is to inject it into the constructor of the class that is going to use that service. .NET Core will serve that service from the IOC container and all of its features will be available to use. This type of injecting object is called Dependency Injection.
DI, IOC, and Logger Service Testing
What is exactly Dependency Injection(DI) and what is IOC (Inversion of Control)?
Dependency injection is a technique for achieving loose coupling between objects and their dependencies. It means that rather than instantiating an object every time it is needed, we can instantiate it once and then serve it in a class, most often, through a constructor method. This specific approach we utilize is also known as the Constructor Injection.
In a system that is designed to use DI, you may find many classes requesting their dependencies via their constructor. In that case, it is helpful to have a class that will provide all instances to classes through the constructor. These classes are referred to as containers or more specific Inversion of Control containers. An IOC container is essentially a factory that is responsible for providing instances of types that are requested from it.
For the logger service testing purpose, we could use WeatherForecastController
. We are going to find it in the main project in the Controllers
folder.
So, let’s modify it:
[ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private readonly ILoggerManager _logger; public WeatherForecastController(ILoggerManager logger) { _logger = logger; } [HttpGet] public IEnumerable<string> Get() { _logger.LogInfo("Here is info message from the controller."); _logger.LogDebug("Here is debug message from the controller."); _logger.LogWarn("Here is warn message from the controller."); _logger.LogError("Here is error message from the controller."); return new string[] { "value1", "value2" }; } }
After all, we can start the application, and browse to http://localhost:5000/weatherforecast
. As a result, we will see an array of two strings. Now, let’s go to the folder that we have specified in the nlog.config
file, and check out the result. We should see four lines logged inside that file.
Conclusion
Now you have the working logger service, you will use through an entire process of our application development.
By reading this post you’ve learned:
- Why logging is important
- How to create external service
- How to use the NLog library and how to configure it
- What are DI and IOC
- And how to use them for service injection
Thank you for reading and see you soon in the next blog post where we will talk about repository pattern with entity framework core.
For NLog 5.1.3 and Asp.Net Core 6 I was forced to add these lines to the “Program.cs” file, but I’m not sure if it was necessary:
var builder = WebApplication.CreateBuilder(args);
LogManager.Setup().LoadConfigurationFromAppSettings();
builder.Logging.ClearProviders();
builder.Host.UseNLog();
builder.Services.AddSingleton<ILogService, LogService>();
Hi Geo. I am not sure why you had to do that. In our book (both versions 6 and 7) we are using this approach and everything is working fine.
Hi,
The concept about DI is very easy to understand. Thanks for helping us understand all the topics in a crystal and clear manner
You are most welcome.
In nLog.Config from where i get Internal log file and File Name. internalLogFile=”d:Projects\Blog-AccountOwner\Project\internal_logs\internallog.txt”>
Or Simply I copy-paste the code of your nlog.config in my nlog.config file.
If you have D partition, you can copy-paste it, if not, just modify to C.
I have a Project in E drive and the Path of My project is E:\VS Projects\Net Core Projects\AccountOwnerServer what is the code of the Internal log file and File Name.
Just modify the path from the example from D to E and you should be good to go.
Hi,
Thank you very much for the tutorial. It is really well done.
I have a question regarding populating the nlog.config file. In the directions you tell us to provide our own paths for internalLogFile and fileName(picture attached). I am unsure what my path should be.
My solution is located here(inside AccountOwnerServer): /Users/craigreedgileswilliams/Desktop/workspace/aspDotNetCoreWebAPI/AccountOwnerServer
What should my path for these files be?
Thank you for you help!
-Craig
Hello Craig.
Try following paths:
internalLogFile=”.\internal_logs\internallog.txt”>
fileName=”.\logs\${shortdate}_logfile.txt”
With these, you can’t miss.
Thanks, Marinko!
The type ‘LoggerService.LoggerManager’ cannot be used as type parameter ‘TImplementation’ in the generic type or method ‘ServiceCollectionServiceExtensions.AddScoped<TService, TImplementation>(IServiceCollection)’. There is no implicit reference conversion from ‘LoggerService.LoggerManager’ to ‘Library_ASP.NET.Extensions.ILoggerManager’.
What can be the cause of this problem?
Well, LoggerService.LoggerManager is a custom implementation from the NLog namespace, and obviously, you can’t register it with the ILoggerManager that comes from the ASP.NET.Extensions namespace.
Hello,
Can you please make a tutorial on unit testing .Net Core web api using NUnit ?
Hello. We don’t have the article using NUnit but maybe this one with XUnit can help you https://code-maze.com/unit-testing-aspnetcore-web-api/. We will cover the NUnit as well soon.
I dont find
ServiceExtensions
classhttps://github.com/CodeMazeBlog/.NET-Core-Series/tree/master/Part%203/AccountOwnerServer/Extensions
Hi,
Kindly show us logging using Serilog as well. It would be great to understand how to use Serilog for logging details in file as well in a database table.
Thanks
Hi Rohan. These are great topics and we will cover them for sure.
Thanks Marinko.
I don’t see any log files in specified path. I’ve already added log file path and file name similar to your nlog.config file. When running it, it calls successfully controller action and log writing code lines. but, I don’t see any log file in specified path. Thanks in advance.
As much as I would like to help, I don’t know where to start since I have no code or anything to work with. The best solution would be to download our source code, and compare it line by line with yours. Probably you have missed something, that happens a lot. You can start our project and verify that it is logging messages. If it does, then you missed something for sure.
Thank for reply. Can I get your source code link to download? Thank in advance
It is okay now. I remove {{urvanov-syntax-highlighter-internal:0}} in nlog.config file.
I am glad you solved the problem. By the way, the link for the source code is always at the beginning of the article.
Never a fan of local text files. Always a problem with directory resolution as I ran into right off trying to find the nlog configuration file. When I did write my own log files they went to a table in a database and all I had to do was insert a row.
This really depends on the project. If you have just a simple logging than the table would be sufficient. But if you have a large logging activity or if you have 10 microservices where each of them logs own activity, then having the logs in the database could be overload with additional tables and a lots and lots of logging rows. Of course, NLog provides support for the database logging as well.
Hi,
I have applied ASP.NET CORE 3.1 in my project.
I cannot add services.ConfigureLoggerService() to my startup.cs file
Hello Jahan. You are doing something wrong with the Logger IOC registration. I don’t have your code and can’t tell what is the problem. You can always compare your code with ours and find the differences.
Hello Marinko,
I have just ignored it and it works fine!
Thank you very much for your article. 🙂
Hi, good article. Will you make its continuation? I’ve followed all the steps, but I don’t know how to read NLog files or how to make it elsewhere on the Web Api website. Thanks
Hello. Thank you for reading this article. Well this is the ASP.NET Core Web API series of articles and we have continuation, just not related to logging actions. Of course there is a lot more to write about NLog, there is no doubt with that. You can read log files, if you need that, by using Directory and File classes from C#, there is nothing special about that (if I understood your question correctly). And I am not sure what you mean by “make it elsewhere on the Web Api website”. Right now your logging service is registered with IOC and you can use it whereever you like, just inject it through DI.
Best regards.
Thank you, your tutorials are really helpful.
Question: In the XML, I noticed that you use absolute path, won’t this approach be a problem during production?
Hello mate. I saw you comment on our GitHub repository and replied you there, didn’t you see it?
Haha silly me, No I did not see it. Let me quickly check it. Thanks for the quick response.
Hi Marinko. First of all, a big thank you for your tremendous effort for a detailed tutorial of this level. I bow in front of you.
I saw where we used DI in the ValuesController, however, I couldnt quite get where was the IOC that .NET Core was using to serve the service? Is it the ConfigureServices() method in the Startup.cs, that adds services to the container (more specifically, the line services.ConfigureLoggerService();?) Or is it another place? (I actually had no clue at first, but while I was writing this question, I just went back to the text, then to the code, and now it all makes sense.. we put a Singleton class in that method into the services IOC.) Thanks ^_^
Thank you very much. Hope you will enjoy the rest of the tutorial as well. Best regards.
First of all thanks for writing such a great tutorial , though I have few questions
Q1. For Asp.net core , NLog.Web.AspNetCore suggested ? Why we are using Nlog.Extension.Logging ? Actually I don’t know the difference. Please help me to understand ?
Q2. In .Net Core webapi project , I amusing Entity Framework(for core) to scaffold classes, Shall I need to refere to the same classes or shall I create different models as well.
Please Help. Thanks & Regards, A New .Net Core Developer. 🙂
Hello Deepak. We are using NLog because it is easy to use and it allows you better control of logging information. Of course you can use default logging solution integrated inside .NET Core, there is no problem with that at all, but I like to have better control of what content I em logging.
For you second question, if you are using Scaffolding, than stick to what has been created for you.
All the best.
The first question was regarding choice between
1) NLog.Web.AspNetCore
2) Nlog.Extension.Logging
Any difference between them , for .net core when u install package they prefer (1) ( as written in NLog website) but you implemented package num(2) , so I was asking any specific reason ?..
Regards & Thanks.
Ok I get it now, but I haven’t been using the first choice at all. This is from the official NLog site:
https://uploads.disquscdn.com/images/ff39e2e2d403c56cdb20e8a96aa1e8cd9f9fa1d670803414cc545537e9b5ba20.png
and as you can see they are stating that this library has a .net core support, so I didn’t search to much around, just saw that and used it 😀
Thanks for this step by step tutorial series. God bless you
You are very welcome. Glad to hear you like it and find it worth reading.
Hey thanks for the article. I’m just diving in to back-end development and I’m finding this very easy to understand. However, there’s a small issue that I need help with.
public Startup(IConfiguration configuration)
{
LogManager.LoadConfiguration(String.Concat(Directory.GetCurrentDirectory(), "/nlog.config"));
Configuration = configuration;
}
LogManager resides in NLog package if I’m not mistaken hence cannot be accessed from the Startup class I think since we added the NLog dependency to LoggerService and not AccountOwnerServer or am I missing something? Any sort of help is appreciated.
Also could you elaborate as to why we need a Contracts project in between when we could directly use the interface in LoggerService?
Hello Shahid, thank you for your comment. You are right that LogManager resides in NLog package and we have installed it inside the LoggerService project, but we have referenced that project to our main project thus we have a reference towards the NLog library. You can check that by expanding Dependencies > Projects > LoggerService.
We need the Contracts project due to the interface reusability mainly. Now if you want to implement another logger with some other library, but you want to log the same level messages, all you have to do is to create another project and just reference the Contracts project. Then you would have access to the ILoggerManager interface.
I hope this helps. All the best.
I was getting the same error and to fix this issue I have removed the reference of LoggerService from our main project and readded it and it works.
Thanks for your nice article! If I want to have a custom file name, how can I do it?
For example, when an error occurred, I produce a number for user then developer can figure out where the error occurred.
I want to change my file name error to this number.
Hello Jahan. Thank you for reading this article, I am glad you like it. About your question. Developer will always know where the error started due to the stack trace message. You can modify the name of your error log file, but all the modifications exist in the nlog.config file. I am not sure how to do it in the code. You can check this link https://github.com/nlog/NLog/wiki/File-target for more information about target files and how to archive them and name them.
Hi,
I want use DI in EF class library project, Is there any example available for class library?
I have many projects so i want to create separate class library and want to use DI in those class library. There are many examples but not how to use DI and many thing in separate EF core class library project like transaction management to execute opeartion in single block without raising deadlock etc. Is there any example available?
I came for the error handling stuff but have found so much more. I just want to say thanks heaps for this series, especially for making the amendments such as the nlog code update.
Thank you very much for your kind words. Don’t forget to subscribe, to get updated about our new posts. One more time thank you a lot.
Hello Martin. Thank you very much for reading the post and for your comment as well. I am so glad if you could find it useful. Regarding your questions:
1) As much as I know, NLog is not built in inside .NET Core Web API project. It is a third party, open source library. Please check this link: http://nlog-project.org/. NET Core has its own logging logic but I always like to use my way of controlling and logging messages. If you know something different or something has changed in the relation between .NET Core and NLog please share it with me.
2) Your implementation is very acceptable but there are two reasons for me to do it like I did. Firstly, the Startup constructor is going to fire up only once (when you start the server) and that is it, every other request won’t trigger this constructor therefore making NLog dependency on a single place as you mentioned and configured just once. The second reason and Main reason is that I had to extend the ILoggerFactory, which is built in in .NET Core, because without this extension NLog didn’t work when application was deployed on Linux environment (at least I had that problem).
I hope you are satisfied with my answers, and one more time, thank you very much for reading and making your suggestions. It is always a pleasure to talk with .NET skillful people 😀
One more time thank you so much mate. It is always a pleasure to talk with people like you. Hope I will see you around. All the best mate.
Best regards,
Marinko.
Thanks for the post. For project reference, you have “Also, add a reference from the Contracts project to the LoggerService project.” This should be the other way around. Right? We should be referencing LoggerService project from Contract project to make use of the interface. When I added a reference from Contracts project to LoggerService project, I got some circular reference error. other then that, everything works fine. I was able to test the logging service in the end.
Hi Sumu (again 😀 ) Well you just misunderstood me or I wrote it in the wrong way. But to explain my self: With the sentence “Also, add a reference from the Contracts project to the LoggerService project.” I meant in the LoggerService right click on the Dependencies and click AddReference, then choose Contracts and import it’s reference inside LoggerService project. Other way around you are going to get circular reference, as you mentioned. I am sorry for misleading sentence, I am going to make it more clear for understanding. Thank you very much for the suggestion. And thank you for reading our posts.
Ok. Thanks!
Hello Marinko, i was reading the comments as i was facing the same problem. Do you mean import LoggerService as reference in Contracts project, or import Contracts as reference in LoggerService project?
Based on your tutorial above, it seems like you are saying we have to do both. But i encounter the circular error when i try to do it that way.
Please advise.
Hi James. Could you please point where is stated to do both? I have to fix that. You need to import Contract reference to the Logger project. The main interface that Logger is using exists in the Contracts project .