Solve Logging as a Cross-Cutting Concern with MediatR

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we're talking about the logging in our applications I'm going to show you how you can create a mediator pipeline Behavior to solve logging as a cross-cutting concern I'm going to discuss what and how much you want to log and also how to configure the log level through application settings let's start off by creating a mediator pipeline behavior in the application layer I'm going to create a behaviors folder and inside of it I'm going to add a new class which is going to represent our logging pipeline behavior and that's what I'm going to name it to make this class into a pipeline Behavior I need to make it a generic class so I'm going to add two generic arguments which are going to represent my request and response so I'm going to name them T request add the response now I need to implement the ipipeline behavior interface and I'm going to specify the theory request and T response generic arguments so let me go ahead and add that because I have custom command and query interface definitions I'm going to add a few generic constraints the first generic constraint is going to be for T request that it has to be an instance of I request and a generic one returning at the response and now I'm going to add a generic constraint for the T response generic argument and I'm going to say that the response has to be a result which is coming from my domain project this completes the definition for the logging pipeline behavior and now I need to implement the interface as you can see the interface has has one handle method that we need to implement and we get the request and request Handler delegate as the argument the simplest implementation is going to be to await the request Handler delegate execution store that into a result and just return that from the handle method I'm going to start off by making the handle method asynchronous and then I'm going to execute our request Handler delegate I'm going to say VAR result and I'm just going to await the execution of the request Handler delegate and we can return that result and complete our handle method so how do we introduce logging here we're going to start off by injecting an ilogger instance so I'm going to create a private read-only field that is going to represent our logger we're going to use the I logger interface which is coming from the Microsoft extensions logging namespace and I'm going to specify the login pipeline Behavior as the generic argument so let's create the logger instance and we're going to inject it from The Constructor so now that we have our I logger instance we can introduce logging inside of the handle method so what are the things that would be meaningful to log inside of the handle method for example we would want to add a log before executing the request Handler and also after executing the request Handler that way we are going to be able to track our mediator requests through the pipeline and have log statements showing us what was going on during that request so let's start off by adding an information log before executing the request I'm going to say log information and let's add the message inside I'm going to stay starting request and now I'm going to define a few placeholder variables and I'm going to explain what I'm going to do in a moment I'm going to open curly brackets I'm going to add a placeholder inside of the brackets and I'm going to call it request name and I'm going to define a few additional placeholders one thing that would be interesting to log would be the current date and time so let's add that I'm going to say date time UTC I think this is good enough to start out now I need to specify the values for these placeholders so we're going to start off from the request name I'm going to say type of T request and I'm just going to pass in the name of this type as the request name and for the current date time I'm going to say date time UTC now you can see that when I select the argument the proper placeholder inside of the log message is also highlighted what I'm doing here is called structured logging I'm not only formatting the log message as a string I'm also adding the request name and the current date and time as additional metadata on the log message and if I were using a logging provider that supported structured logging an example of that would be serilog which I'm going to cover in another video is going to be able to take that metadata and serialize it into a Json value and store that as the log message this is going to allow me to search my log messages much more efficiently for example we would be able to filter the logs by the request name and see what information we can gather from that let's also add another log statement after we complete our request Handler I'm going to say completed request as the message and we're going to have the request name and the current date time inside of the metadata this is good enough to start out so let's configure our login pipeline Behavior with dependency injection right here where I'm configuring mediator I'm going to say Builder services and I'm going to add a scope service which is going to be our pipeline behavior I'm going to say type of ipipeline behavior and we're going to specify our login pipeline Behavior as the implementation so this is going to configure our login pipeline Behavior as a valid pipeline Behavior with mediator and now we can start our application and see how logging is working I'm going to send a few requests to our API from Postman and you're going to see that we're getting a few log messages in the output console I want to highlight a few things you can see that we have the information log level defined here then you can see the source of the log which is our logging pipeline behavior and then you get our log message which contains the request name which is the get products query in this instance and the time when the request was started and then we have the log below that representing when the request was completed and at what time so at the moment we're only logging when our request was started and when it was completed but because we are returning our result instance here we can check if the result was a success or a failure and log some additional information based on that so I can say if result is failure so if the result is a failure I can decide I want to add a log statement and I'm not sure what log level would be appropriate I can choose from information critical debug error trace or warning let's say we want to log an error when we have a failure result so we would do the same thing as we did before and let's copy this from over here where we log the information statement so instead of starting request we can say request failure we'll leave the request name as it is we'll also leave the date and time and I'm going to add another placeholder here which is going to represent our error instance that occurred in this situation and let's pass in that error instance to our log message by accessing the result error property so now when we encounter a failure in our request pipeline we're going to be logging an error message and it's going to contain an additional metadata property which is going to represent our log I'm going to show you an example of that I'm going to send a pulse request to our API to try to update a product but I'm going to use an ID that does not exist in the database and you can see that we get a failure level log message right here telling us that the request failed for the update product command the error was that the product was not found and this is the date and time when the error occurred if you are enjoying this video about logging make sure that you smash that like button and also subscribe to my channel so that you don't miss any of my future videos one thing that could be problematic if your application has a high number of requests is that you're going to be generating a lot of log statements not all of those log statements are going to have value so you want to be able to control what is the minimum log level that is going to be recorded in your logs you can do that by setting the log level in your application settings we want to set the default log level and let's change it from information to warning and now if we run our application and try to log information logs you're going to see they're not going to show up I added a breakpoint at the start of the logging pipeline behavior and I'm going to send a boot request for a non-existing product watch what's going to happen in the output window when we try to add our information log you can see that it's not added and now we execute our request we get a failure response and this time we're going to log an error level log and as you can see it's going to show up in the output window and let's try for our last log which is after our request completes this is an information level log but the minimum log level is warning and if I execute that statement you can see that we don't get the log in the output window so this is how you can solve logging as a cross-cutting concern inside your application if you enjoyed this video leave it a like and also subscribe to my channel and until next time stay awesome
Info
Channel: Milan Jovanović
Views: 25,207
Rating: undefined out of 5
Keywords: c# logging, c# logging best practices, c# logging tutorial, c# logging framework, c# logging serilog, c# logging with serilog and seq, c# logging levels, c# logging exceptions, ilogger, logging, serilog, seq, structured logging, structured logging c#, c# structured logging, mediatr logging, mediatr logging pipeline behavior, asp.net core logging, asp.net core structured logging, c# logging asp.net core, c# logging mediatr, clean architecture, c# logging clean architecture
Id: JVX9MMpO6pE
Channel Id: undefined
Length: 9min 41sec (581 seconds)
Published: Fri Jan 13 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.