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
This post is divided into several sections:
- Creating Required Projects
- Creating the Interface and Installing NLog
- Implementing Interface and Nlog.Config File
- Configuring Logger Service
- DI, IOC and Logger Service Testing
- Conclusion
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 then NewProject
. We are going to choose the Class Library (.NET Core) and name it Contracts
:
Do the same thing for the second project, just name it LoggerService
.
With these two projects in place, we need to reference the LoggerService project to the main project. In the main project inside solution explorer, right-click on the Dependences and choose the AddReference. Under the Projects click the Solution and check this project:
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 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 massages.
We are going to show you two ways to add the NLog
library to our project.
- In the
LoggerService
project, right-click on the Dependences and choose Manage NuGet Packages. After the NuGet window appears, just follow the steps:
- 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 -Version 1.6.1
After a couple of seconds, NLog
is up and running in your app.
Implementing Interface and Nlog.Config File
In the LoggerService
project create the new class LoggerManager
.
Let’s modify the LoggerManager
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); } } }
Now, we need to configure it and inject it into the Startup
class in the ConfigureServices
method.
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 to populate it as in the example below. 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 constructor of the Startup class:
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
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
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
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 ConfigureServices
method invoke this extension method, right above services.AddControllers()
:
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 objects 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, start the application, and browse to http://localhost:5000/weatherforecast
. As a result, you will see an array of two strings. Now go to the folder that you have specified in the nlog.config
file, and check out the result. You 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 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.
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 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.
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.
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?
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.
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.
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.
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 😀
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.
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, 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.
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. 🙂
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.
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.