c# windows-services - How do I handle logging exceptions? - c#

I am creating a Windows service. When an exception occurrs, I handle it appropriately and create a log. I am using the decorator pattern, as there are many different ways people will be looking at these logs. I have an email logger, a file logger, and a windows event logger, all which inherit from LoggingDecorator, which implements ILogger. So, no logger knows about any other logger.
My question is: How should I handle logging exceptions?
If writing to a file fails, or sending an email fails, what should I do? I want to log the initial log content with the other loggers, but what do I do with the logging exception? Doesn't it also depend on the order of the loggers in the constructor?
Right now, I'm just wrapping try/catch blocks with empty catch(Exception) statements, which just feels dirty and makes FxCop yell at me. However, is this one of those "it depends" moments?
[Flags]
public enum LoggingCategories
{
None = 0,
ServiceEvents = 1,
ProcessingInformation = 2,
ProcessingErrors = 4,
UnexpectedErrors = 8
}
public interface ILogger
{
void LogMessage(LoggingCategories category, string message);
}
public abstract class LoggerDecorator : ILogger
{
private ILogger _decoratedLogger;
private LoggingCategories _categories;
protected LoggerDecorator(ILogger logger, LoggingCategories categories)
{
this._decoratedLogger = logger;
this._categories = categories;
}
protected bool ShouldLogCategory(LoggingCategories category)
{
return ((this._categories & category) == category);
}
public virtual void LogMessage(LoggingCategories category, string message)
{
_decoratedLogger.LogMessage(category, message);
}
}
public class ControlLogger : ILogger
{
public ControlLogger()
{
}
public void LogMessage(LoggingCategories category, string message)
{
Console.WriteLine(LoggingHelper.ConstructLog(category, message));
}
}
(questionable code in WindowsEventLogger)
try
{
this._eventLog.WriteEntry(log, type);
}
catch (Exception)
{
//Even if this logging fails, we do not want to halt any further logging/processing.
}
(code in service constructor)
ILogger controlLogger = new ControlLogger();
ILogger windowsEventLogger = new WindowsEventLogger(controlLogger, windowsEventLogCategories, windowsEventLogSource);
ILogger emailLogger = new EmailLogger(windowsEventLogger, emailCategories, emailSubject, emailAddresses);
ILogger fileLogger = new FileLogger(emailLogger, fileCategories, logDirectory, logFileNamePrefix, logFileExtension);
this._logger = fileLogger;

Why don't put it in the actual Windows Event log if logger fails?

Its not an answer, but curious why did you choose to not utilize the existing System.Diagnostics.Trace methods. You could implement some type of categorization of log types on top it it perhaps?

Create separate ping service on a well behaving machine which you trust to be very reliable. If your primary service fails then ping also fails and control service then should send you e-mail with warning.

Related

.NET DI with runtime implementation resolvers

I have a bit of a weird case involving DI, specifically in resolving implementation at runtime from within the same service. I'm aware that I could inject a service provider, but that would seemingly violate the dependency inversion principle.
Also, apologies if this ends up being more of a architectural/design question; I've recently switched from .NET Framework development and still getting acquainted with the limitations of DI. Note that I've simplified & changed the business context for obvious reasons, so keep in mind that the hierarchy/structure is the important part... For this question, I've decided to go with the classic example of an online retailer.
Project Overview/Example:
core library (.NET Class Library)
- IRetailerService: public service consumed by client apps
└ IOrderService: facade/aggregate services injected into ^
├ IInventoryManager: internal components injected into facade/aggregate services as well as other components
├ IProductRespository
└ IPriceEstimator
Aggregate/Façade Services
public class RetailerService : IRetailerService
{
private readonly IOrderService _orderService;
public OrderService( IOrderService orderService, ... ) { //... set injected components }
async Task IRetailerService.Execute( Guid id )
{
await _orderService.Get( id );
}
async Task IRetailerService.Execute( Guid id, User user )
{
await _orderService.Get( id, user );
}
}
internal class OrderService : IOrderService
{
public OrderService( IInventoryManager inventoryManager, IProductRespository productRepo, ... ) { }
async Task<object> IOrderService.Get( Guid id )
{
//... do stuff with the injected components
await _inventoryManager.Execute( ...args );
await _productRepo.Execute( ...args );
}
async Task<object> IOrderService.Get( Guid id, User user ) { }
}
The Problem:
Lets say I want to log IOrderService.Get( Guid id, User user ), but only when this override with the User is provided - this includes logging inside the injected components (InventoryManager, IProductRepository, etc.) as well.
The only solutions I can see at the moment are to either:
Add an additional layer to this hierarchy & use named registration with scope lifetimes to determine if a null vs logging implementation is passed down.
Inject the service provider into the public facing service IRetailerService, and somehow pass down the correct implementation.
I think my ideal solution would be some type of decorator/middleware to control this... I've only given the core library code; but there is also a WebApi project within the solution that references this library. Any ideas/guidance would be greatly appreciated.
I would recommend using a factory to create the order service, and any downstream dependencies that need the logger. Here is a fully worked example:
void Main()
{
var serviceProvider = new ServiceCollection()
.AddScoped<IRetailerService, RetailerService>()
.AddScoped<IInventoryManager, InventoryManager>()
.AddScoped<IOrderServiceFactory, OrderServiceFactory>()
.BuildServiceProvider();
var retailerService = serviceProvider.GetRequiredService<IRetailerService>();
Console.WriteLine("Running without user");
retailerService.Execute(Guid.NewGuid());
Console.WriteLine("Running with user");
retailerService.Execute(Guid.NewGuid(), new User());
}
public enum OrderMode
{
WithUser,
WithoutUser
}
public interface IOrderServiceFactory
{
IOrderService Get(OrderMode mode);
}
public class OrderServiceFactory : IOrderServiceFactory
{
private readonly IServiceProvider _provider;
public OrderServiceFactory(IServiceProvider provider)
{
_provider = provider;
}
public IOrderService Get(OrderMode mode)
{
// Create the right sort of order service - resolve dependencies either by new-ing them up (if they need the
// logger) or by asking the service provider (if they don't need the logger).
return mode switch
{
OrderMode.WithUser => new OrderService(new UserLogger(), _provider.GetRequiredService<IInventoryManager>()),
OrderMode.WithoutUser => new OrderService(new NullLogger(), _provider.GetRequiredService<IInventoryManager>())
};
}
}
public interface IRetailerService
{
Task Execute(Guid id);
Task Execute(Guid id, User user);
}
public interface IOrderService
{
Task Get(Guid id);
Task Get(Guid id, User user);
}
public class User { }
public class RetailerService : IRetailerService
{
private readonly IOrderServiceFactory _orderServiceFactory;
public RetailerService(
IOrderServiceFactory orderServiceFactory)
{
_orderServiceFactory = orderServiceFactory;
}
async Task IRetailerService.Execute(Guid id)
{
var orderService = _orderServiceFactory.Get(OrderMode.WithoutUser);
await orderService.Get(id);
}
async Task IRetailerService.Execute(Guid id, User user)
{
var orderService = _orderServiceFactory.Get(OrderMode.WithUser);
await orderService.Get(id, user);
}
}
public interface ISpecialLogger
{
public void Log(string message);
}
public class UserLogger : ISpecialLogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
public class NullLogger : ISpecialLogger
{
public void Log(string message)
{
// Do nothing.
}
}
public interface IInventoryManager { }
public class InventoryManager : IInventoryManager { }
internal class OrderService : IOrderService
{
private readonly ISpecialLogger _logger;
public OrderService(ISpecialLogger logger, IInventoryManager inventoryManager)
{
_logger = logger;
}
public async Task Get(Guid id)
{
_logger.Log("This is the 'id-only' method");
}
public async Task Get(Guid id, User user)
{
_logger.Log("This is the 'id-and-user' method");
}
}
Using this, you get the following output:
Running without user
Running with user
This is the 'id-and-user' method
The factory lets you have complete control of how the downstream components are generated, so you can get as complicated as you want.
You can resolve your dependencies in the IOrderService.Get method at runtime so that each method has its own dependencies. Nevertheless this doesn't fully resolve your problem. Nested dependencies IInventoryManager inventoryManager, IProductRespository productRepo, ... should be able to enable logging as well.
So instead you may use:
internal class OrderService : IOrderService
{
public OrderService( IServiceProvider serviceProvider) { }
async Task<object> IOrderService.Get( Guid id )
{
var inventoryManager = (IInventoryManager)serviceProvider.GetService(typeof(IInventoryManager));
inventoryManager.Logging = false;
var productRepo = (IProductRespository)serviceProvider.GetService(typeof(IProductRespository));
productRepo.Logging = false;
//... do stuff with the injected components
await inventoryManager.Execute( ...args );
await productRepo.Execute( ...args );
}
async Task<object> IOrderService.Get( Guid id, User user ) {
var inventoryManager = (IInventoryManager)serviceProvider.GetService(typeof(IInventoryManager));
inventoryManager.Logging = false;
var productRepo = (IProductRespository)serviceProvider.GetService(typeof(IProductRespository));
productRepo.Logging = true;
//... do stuff with the injected components
await inventoryManager.Execute( ...args );
await productRepo.Execute( ...args );
}
}
You may also provide a Factory / Builder with a parameter to enable logging.
But in any case because you want a different behavior in nested classes starting from a same root class, this may be complicated.
Another option is to provide 2 implementations of IOrderService, one that include logging, and the other not. But I'm not sure this may help you because you had probably good reasons to provide an overload to the method and not split them into separate services. And this doesn't resolve the issue for nested injections.
Last option may be to use a singleton LoggingOptions class.
Each dependency has a dependency on this class and because this is a singleton, each time you enter your overload you set it to true and so all classes are informed of your intent to log. Nevertheless this highly depends of your architecture. If both methods may be called nearly on the same time, this may break the nested dependencies logging behavior or interrupt the logging at any time.
Take a look at this question this may help. By considering this question, you may provide a Factory for each of your dependency (including nested ones) that would set logging behavior on each call to the overload method.

Including logging as part of my domain model

I'm writing an application where logging is part of my actual domain model. It's an automation and batch processing tool where end users will be able to view the logs of a batch processing job in the actual application and not just text log files.
So my domain model includes a LogMessage class:
public sealed class LogMessage
{
public string Message { get; }
public DateTime TimestampUtc { get; }
public LogLevel Level { get; }
}
public enum LogLevel
{
Fatal = 5,
Error = 4,
Warn = 3,
Info = 2,
Debug = 1,
Trace = 0
}
I also have a Result class which has a collection property of LogMessages. Results can be saved to and opened from files with my application by end users.
public class Result
{
public bool Succeeded {get; set;}
public string StatusMessage {get; set;}
public IList<LogMessage> LogMessages {get; set;}
}
My application also supports third party developers extending the application with plug-ins that can also write log messages. So I've defined a generic ILogger interface for the plug-in developers.
public interface ILogger
{
void Debug(string message);
void Error(string message);
void Fatal(string message);
void Info(string message);
void Log(LogLevel level, string message);
void Trace(string message);
void Warn(string message);
}
I provide an instance of an ILogger to the plug-ins which writes to Result.LogMessages.
public interface IPlugIn
{
Output DoSomeThing(Input in, ILogger logger);
}
I obviously also want to be able to log from my own internal code and ultimately want Result.LogMessages to contain a mixture of my internal log messages and log messages from plug-ins. So an end user having trouble could send me a Result file that would contain debug logs both from my internal code, and any plug-ins used.
Currently, I have a solution working using a custom NLog target.
public class LogResultTarget : NLog.Targets.Target
{
public static Result CurrentTargetResult { get; set; }
protected override void Write(NLog.LogEventInfo logEvent)
{
if (CurrentTargetResult != null)
{
//Convert NLog logEvent to LogMessage
LogLevel level = (LogLevel)Enum.Parse(typeof(LogLevel), logEvent.Level.Name);
LogMessage lm = new LogMessage(logEvent.TimeStamp.ToUniversalTime(), level, logEvent.Message);
CurrentTargetResult.LogMessages.Add(lm);
}
}
protected override void Write(NLog.Common.AsyncLogEventInfo logEvent)
{
Write(logEvent.LogEvent);
}
}
This class forwards message to the Result assigned to the static LogResultTarget.CurrentTargetResult property. My internal code logs to NLog loggers, and and I have a implementation of ILogger that logs to an NLog.Logger as well.
This is working, but feels really fragile. If CurrentTargetResult is not set correctly or not set back to null I can end up with log messages being stored to results that they do not apply to. Also because there is only one static CurrentTargetResult there's no way I could support processing multiple results simultaneously.
Is there a different/better way I could approach this? Or is what I'm trying to do fundamentally wrong?
I think your approach is the right one, but you could save effort by using a library which already does this abstraction for you. The Common Logging library is what you're after.
Your domain code will depend only on the ILogger interface from Common Logging. Only when your domain is used by a runtime e.g. Web API, do you then configure what logging provider you're going to use.
There are a number of pre-built providers available as separate nuget packages:
Common.Logging provides adapters that support all of the following popular logging targets/frameworks in .NET:
Log4Net (v1.2.9 - v1.2.15)
NLog (v1.0 - v4.4.1)
SeriLog (v1.5.14)
Microsoft Enterprise Library Logging Application Block (v3.1 - v6.0)
Microsoft AppInsights (2.4.0)
Microsoft Event Tracing for Windows (ETW)
Log to STDOUT
Log to DEBUG OUT
I've used this for a number of years and it's been great to have your domain/library code be reused in another context, but not have to have a fixed dependency on a logging framework (I've moved from Enterprise Libraries to log4net, to finally NLog ... it was a breeze).
In think the static CurrentTargetResult is indeed a bit fragile, but the overal approach is fine.
I propose the following changes:
unstatic the CurrentTargetResult, and always make it initialized,
Something like this:
public class LogResultTarget : NLog.Targets.Target
{
public Result CurrentTargetResult { get; } = new Result();
protected override void Write(NLog.LogEventInfo logEvent)
{
//Convert NLog logEvent to LogMessage
LogLevel level = (LogLevel)Enum.Parse(typeof(LogLevel), logEvent.Level.Name);
LogMessage lm = new LogMessage(logEvent.TimeStamp.ToUniversalTime(), level, logEvent.Message);
CurrentTargetResult.LogMessages.Add(lm);
}
protected override void Write(NLog.Common.AsyncLogEventInfo logEvent)
{
Write(logEvent.LogEvent);
}
}
Always initialize the LogMessages:
public class Result
{
public bool Succeeded {get; set;}
public string StatusMessage {get; set;}
public IList<LogMessage> LogMessages {get; set;} = new List<LogMessage>();
}
Retrieve the messages - when needed - with the following calls:
// find target by name
var logResultTarget1 = LogManager.Configuration.FindTargetByName<LogResultTarget>("target1");
var results = logResultTarget1.CurrentTargetResult;
// or multiple targets
var logResultTargets = LogManager.Configuration.AllTargets.OfType<LogResultTarget>();
var allResults = logResultTargets.Select(t => t.CurrentTargetResult);
PS: You could also overwrite InitializeTarget in LogResultTarget for initialing the target

Asp.net Core get instance of Dependency Injection

I am adding a global error handler filter in Startup.cs like this:
services.AddMvc(o =>
{
o.Filters.Add(new GlobalExceptionFilter());
});
However, I need to pass in my Email Service which is also being injected. How can I retrieve it from these services in the filter?
public class GlobalExceptionFilter : IExceptionFilter
{
private readonly IEmailService _emailService;
public GlobalExceptionFilter()
{
}
public void OnException(ExceptionContext context)
{
}
}
I use to be able to use DependencyResolver Class to do that in MVC5. Is there a way to accomplish this in core? Or is there a way for me to force instantiation of the service in the Startup so I can pass it as part of the constructor?
I tried looking it up in the services and then looking at ImplementationInstance, but its null at this point so I can't grab it from there it appears. Also keep in mind that my EmailService requires a parameter of IOptions<Settings> so that it can get email settings it needs.
You can use constructor injection.
public class GlobalExceptionFilter : IExceptionFilter
{
private readonly IEmailService emailService;
public GlobalExceptionFilter(IEmailService emailService)
{
this.emailService = emailService;
}
public void OnException(ExceptionContext context)
{
//do something with this.emailService
}
}
But you have to change the way you are registering the global filter in ConfigureServices method. You should use the Add overload which takes a Type
services.AddMvc(o =>
{
o.Filters.Add(typeof(GlobalExceptionFilter));
});
Another option is, explicitly resolving the dependency inside the OnException method by calling the GetService method on HttpContext.RequestServices.
using Microsoft.Extensions.DependencyInjection;
public void OnException(ExceptionContext context)
{
var emailService = context.HttpContext.RequestServices.GetService<IEmailService>();
// use emailService
}
But you should be fine with the first approach. Let the framework resolve it for you and inject to your constructor instead of you trying to do it.
I think best practice for a Global Exception Handler is actually to create a custom middleware step for it. From the documentation
Exception filters are good for trapping exceptions that occur within MVC actions, but they're not as flexible as error handling middleware. Prefer middleware for the general case, and use filters only where you need to do error handling differently based on which MVC action was chosen.
Then you register you classes in the ConfigureServices method:
services.AddTransient<IEmailService, EmailService>();
Then in your Configure method, you register your customer global exception handler. You will want this to be the first thing you do in the Configure method so you catch any exceptions following it in other middleware.
app.UseMiddleware<MyGlobalExceptionHandler>();
And any services you registered will be available for your middleware constructor which might look something like this:
public sealed class MyGlobalExceptionHandler
{
private readonly RequestDelegate _next;
private readonly IEmailService _emailService;
public NlogExceptionHandler(
RequestDelegate next,
IEmailService emailService)
{
_next = next;
_emailService = emailService;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
try
{
_emailService.SendEmail(ex.ToString());
}
catch (Exception ex2)
{
//Its good practice to have a second catch block for simple logging in case the email fails too
}
throw;
}
}
}

Implementation and usage of logger wrapper for Serilog

This question is related to Steven’s answer - here. He proposed a very good logger wrapper. I will paste his code below:
public interface ILogger
{
void Log(LogEntry entry);
}
public static class LoggerExtensions
{
public static void Log(this ILogger logger, string message)
{
logger.Log(new LogEntry(LoggingEventType.Information,
message, null));
}
public static void Log(this ILogger logger, Exception exception)
{
logger.Log(new LogEntry(LoggingEventType.Error,
exception.Message, exception));
}
// More methods here.
}
So, my question is what is the proper way to create implementation that proxies to Serilog?
Note: this question is related to this question about log4net but now specific to Serilog.
So, my question is what is the proper way to create implementation that proxies to Serilog?
you should create something like:
public class SerilogAdapter : ILogger
{
private readonly Serilog.ILogger m_Adaptee;
public SerilogAdapter(Serilog.ILogger adaptee)
{
m_Adaptee = adaptee;
}
public void Log(LogEntry entry)
{
if (entry.Severity == LoggingEventType.Debug)
m_Adaptee.Debug(entry.Exception, entry.Message);
if (entry.Severity == LoggingEventType.Information)
m_Adaptee.Information(entry.Exception, entry.Message);
else if (entry.Severity == LoggingEventType.Warning)
m_Adaptee.Warning(entry.Message, entry.Exception);
else if (entry.Severity == LoggingEventType.Error)
m_Adaptee.Error(entry.Message, entry.Exception);
else
m_Adaptee.Fatal(entry.Message, entry.Exception);
}
}
Does that mean that every class that will log sth (so basically every), should have ILogger in its constructor?
As I understand from Stevens answer: Yes, you should do this.
what is the best way to use it later in the code?
If you are using a DI container, then just use the DI container to map ILogger to SerilogAdapter. You also need to register Serilog.ILogger, or just give an instance of Serilog logger to the DI container to inject it to the SerilogAdapter constructor.
If you don't use a DI container, i.e., you use Pure DI, then you do something like this:
Serilog.ILogger log = Serilog.Log.Logger.ForContext("MyClass");
ILogger logging_adapter = new SerilogAdapter(log);
var myobject = new MyClass(other_dependencies_here, logging_adapter);

Using Ninject to fill Log4Net Dependency

I use Ninject as a DI Container in my application. In order to loosely couple to my logging library, I use an interface like this:
public interface ILogger
{
void Debug(string message);
void Debug(string message, Exception exception);
void Debug(Exception exception);
void Info(string message);
...you get the idea
And my implementation looks like this
public class Log4NetLogger : ILogger
{
private ILog _log;
public Log4NetLogger(ILog log)
{
_log = log;
}
public void Debug(string message)
{
_log.Debug(message);
}
... etc etc
A sample class with a logging dependency
public partial class HomeController
{
private ILogger _logger;
public HomeController(ILogger logger)
{
_logger = logger;
}
When instantiating an instance of Log4Net, you should give it the name of the class for which it will be logging. This is proving to be a challenge with Ninject.
The goal is that when instantiating HomeController, Ninject should instantiate ILog with a "name" of "HomeController"
Here is what I have for config
public class LoggingModule : NinjectModule
{
public override void Load()
{
Bind<ILog>().ToMethod(x => LogManager.GetLogger(GetParentTypeName(x)))
.InSingletonScope();
Bind<ILogger>().To<Log4NetLogger>()
.InSingletonScope();
}
private string GetParentTypeName(IContext context)
{
return context.Request.ParentContext.Request.ParentContext.Request.Service.FullName;
}
}
However the "Name" that is being passed to ILog is not what I'm expecting. I can't figure out any rhyme or reason either, sometimes it's right, most of the time it's not. The Names that I'm seeing are names of OTHER classes which also have dependencies on the ILogger.
I personally have no interest in abstracting away my logger, so my implementation modules reference log4net.dll directly and my constructors request an ILog as desired.
To achieve this, a one line registration using Ninject v3 looks like this at the end of my static void RegisterServices( IKernel kernel ):
kernel.Bind<ILog>().ToMethod( context=>
LogManager.GetLogger( context.Request.Target.Member.ReflectedType ) );
kernel.Get<LogCanary>();
}
class LogCanary
{
public LogCanary(ILog log)
{
log.Debug( "Debug Logging Canary message" );
log.Info( "Logging Canary message" );
}
}
For ease of diagnosing logging issues, I stick the following at the start to get a non-DI driven message too:
public static class NinjectWebCommon
{
public static void Start()
{
LogManager.GetLogger( typeof( NinjectWebCommon ) ).Info( "Start" );
Which yields the following on starting of the app:
<datetime> INFO MeApp.App_Start.NinjectWebCommon - Start
<datetime> DEBUG MeApp.App_Start.NinjectWebCommon+LogCanary - Debug Logging Canary message
<datetime> INFO MeApp.App_Start.NinjectWebCommon+LogCanary - Logging Canary message
The Ninject.Extension.Logging extension already provides all you are implementing yourself. Including support for log4net, NLog and NLog2.
https://github.com/ninject/ninject.extensions.logging
Also you want to use the following as logger type:
context.Request.ParentRequest.ParentRequest.Target.Member.DeclaringType
Otherwise you will get the logger for the service type instead of the implementation type.
The Scope of ILog and ILogger needs to be Transient, otherwise it will just reuse the first logger that it creates. Thanks to #Meryln Morgan-Graham for helping me find that.
Bind<ILog>().ToMethod(x => LogManager.GetLogger(GetParentTypeName(x)))
.InSingletonScope();
You are currently binding in Singleton scope, so only one logger is created which will use the name of the first one created. Instead use InTransientScope()
maybe my answer is late but I'm using this format:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ILog>()
.ToMethod(c => LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType))
.InSingletonScope();
}
For all of you that are still looking for the correct answer, the correct implementation is :
public class LoggingModule : NinjectModule
{
public override void Load()
{
Bind<ILog>().ToMethod(x => LogManager.GetLogger(x.Request.Target.Member.DeclaringType));
Bind<ILogger>().To<Log4NetLogger>()
.InSingletonScope();
}
}
Emphasis on:
x.Request.Target.Member.DeclaringType
I do like the idea of wrapping the Log4Net in my own interfaces. I don't want to be dependent on Ninjects implementation, because to me that just means I take a dependency on Ninject throughout my application and I thought that was the exact opposite of what dependency injection is for. Decouple from third party services. So I took the original posters code but I changed the following code to make it work.
private string GetParentTypeName(IContext context)
{
var res = context.Request.ParentRequest.ParentRequest.Service.FullName;
return res.ToString();
}
I have to call ParentRequest.ParentRequest so that when I print the layout %logger it will print the class that calls the Log4Net log method instead of the Log4Net class of the method that called the Log method.

Categories