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
Related
Here we are developing an email client software which has a specific UI that we need to teach that to the user.
There are some classes which handle the logic of the application. for example there is function like this in the one of main classes of project:
public void ComposeMessage(string username,string message)
{
MessageComposer.ComposeMessage(username, message);
}
so, in the above function we send a message to a user.
but in the another side of the application; there is a tutorial phase . so when user goes into tutorial side of the application , he can interact with application's buttons which we tell him , but we don't want to send real messages. so we have changed the function above like this:
public void ComposeMessage(string username,string message)
{
if(!Global.IsTutorial)
MessageComposer.ComposeMessage(username, message);
}
So there are many functions which we are adding if(!Global.IsTutorial) in their body. How can we avoid this situation? We don't want to change the body of the functions in the tutorial section and we don't want to add more codes and classes into our project. We want to keep changes low as much as possible.
There is a lot of ways to achieve what you want but all of them imply that, to start with, you don't use ComposeMessage directly but create interface for it and refactor callers to inject it as a dependency:
public interface IMessageComposer
{
void ComposeMessage(string username, string message);
}
public class MyApp
{
IMessageComposer messageComposer;
public MyApp(IMessageComposer messageComposer)
{
this.messageComposer = messageComposer;
}
public void Foo()
{
messageComposer.ComposeMessage(username, message);
}
}
When you don't rely on concrete implementation and inject dependencies you may change implementation of IMessageComposer to whatever you like without actually touching original MessageComposer's or MyApp's code.
For example you can create TutorialMessageComposer like this
public class TutorialMessageComposer : IMessageComposer
{
public void ComposeMessage(string username, string password)
{
Console.WriteLine("Tutorial mode!");
}
}
And RoutingMessageComposer decorator which will check current mode and call right composer
public class RoutingMessageComposer : IMessageComposer
{
IMessageComposer composer;
IMessageComposer tutorialComposer;
public RoutingMessageComposer(IMessageComposer composer, IMessageComposer tutorialComposer)
{
this.composer = composer;
this.tutorialComposer = tutorialComposer;
}
public void ComposeMessage(string username, string message)
{
if (Global.IsTutorial)
tutorialComposer.ComposeMessage(username, message);
else
composer.ComposeMessage(username, message);
}
}
After all preparations completed, you just need to stick RoutingMessageComposer into MyApp
var app = new MyApp(new RoutingMessageComposer(new MessageComposer(), new TutorialMessageComposer()));
Hello c# experts,
I'm writing an AWS lambda function in c# which requires external logging (I have to write all my logs to an external endpoint). I have been reading many articles about the best practices. Also I did some research on c# libraries such as NLog, Log4Net etc (Some libraries aren't compatible with .Net Core).
My main requirement is that I do not want to pass a Logger object around everywhere in my code. Instead, I should be able to write an external log more like a static method call (Ex: Logger.sendLog("log message");).
Considering the above requirement and the threading issues, I have decided to implement my Logger as a singleton which works fine at the moment. But, I'm curious to know if there are any particular issues with this design. Please let me know if there is a better way to implement a custom Logger class which writes external logs.
Thank you.
interface ILogger
{
void init();
void setUserInfo(UserInfo userInfo);
void sendLog(Dictionary<LogKey, object> payload);
}
public sealed class Logger : ILogger
{
private static readonly Logger instance = new Logger();
private static LogObject logObject;
public static Logger Instance
{
get
{
return instance;
}
}
public void init()
{
logObject = new LogObject();
logObject.appId = AppConfig.appId;
logObject.application = AppConfig.appName;
logObject.version = AppConfig.appVersion;
logObject.environment = EnvConfig.Instance.clientConfig.environment;
logObject.clientName = EnvConfig.Instance.clientConfig.clientName;
}
public void setUserInfo(UserInfo userInfo)
{
logObject.userId = userInfo.userId;
logObject.userName = userInfo.userName;
}
public void sendLog(Dictionary<LogKey, object> payload)
{
setTimeStamp();
logObject.log_level = LogLevel.INFO.ToString();
logObject.payload = payload;
deliverLog(JsonConvert.SerializeObject(logObject));
resetLogObj();
}
private void deliverLog(string logStr)
{
// External API call here
}
private void setTimeStamp()
{
logObject.timestamp = DateTime.UtcNow;
}
private void resetLogObj()
{
logObject.payload = null;
}
}
I need to serialize some objects before NLog sends it to its targets. I could write custom a target, but then I will have to write a custom target for each possible log target.
What I need to know is if NLog will be logging the message(based on the level) to any target and if so, serialize the data. If the logdata states a level that is not to be logged according to the NLog configuration, then I want to avoid serializing the objects because this takes time.
Is there any way to prepare data before NLog sends it to targets or will I have to solve this in custom target classes?
You can get a Logger and check the IsXXXEnabled property.
For example:
class MyExpensiveClass
{
public void string Serialize()
{
return SomethingExpensive;
}
}
class ThisNeedsLogging
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private MyExpensiveClass expensive = new MyExpensiveClass();
public void TraceSomething()
{
if (logger.IsDebugEnabled)
logger.Debug(expensive.Serialize());
}
}
I'm in the process of implementing a notification service. Essentially, customers can get notified in a number of ways, such as via email, text message, fax etc. Below is a rough implementation that is not wired together.
public class NotificationService
{
private readonly INotification _notification;
private readonly INotificationFormatter _formatter;
public NotificationService(
INotificationMethod notification,
INotificationFormatter formatter)
{
_notification = notification;
_formatter = formatter;
}
public void Notify(SomeParameterObject obj)
{
var formattedMessage = _formatter.Format(obj);
_notification.SendNotification(formattedMessage);
}
}
public interface INotificationFormatter
{
NotificationMessage Format(SomeParameterObject obj);
}
public interface INotification
{
void SendNotification();
}
public EmailNotification : INotification
{
public void SendNotification(NotificationMessage message)
{
// Use Exchange Web Services to send email
}
}
The NotificationService class essentially takes in a method of notification and a formatter. Obviously, each method of notification requires different formatting.
Based on business criteria, how do I select which implementation of INotification and NotificationFormatter I wish to use? Note that within the lifetime of the user using the application each notification will most likely be used. I say this because it's not as simple as instructing my container to inject implementation Foobar as it will change while the user is using the application.
I've thought of creating some sort of class that could handle pairs because it seems to makes sense to me that you wouldn't want use a text message notification formatter for a fax notification. However, I can't seem to wrap my head around a decent implementation of this.
I also own the book Dependency Injection in .NET by Mark Seemann. Did I perhaps miss something obvious?
Thank you.
How is it that you decide what kind of notification a user wants? If it can change while they're using your app, it seems like the NotificationService for that user msut be created anew for each notification you want to send them. That's ok - just use some sort of lookup to select a INotification impelmentation with an IoC container.
IoC's (I use AutoFac) let you use string-indexes to select a specific implementation. That string could come from a DB or whatever to represent the user's preference. Then you'd pass it to your IoC asking for an INotification 'decorated' with your string-choice. Upon startup, all the various implementations are registered with thier choice-strings.
I think you may be on to something with your 'pairs' comment - if INotificationFormat is closely tied to INotification and there is a possiblity of mixing them up then perhaps the INotification implementation itself should select its formatter.
What you need to do is to provide some kind of configuration infrastructure. For example, assuming that you want to keep the service just the way you've defined it, I would implement a factory returning an instance of NotificationService according to your model:
public struct NotificaitonSettings<T>
{
public Predicate<T> Predicate;
public NotificationService Service;
}
public class NotificationServiceFactory<T> : INotificationServiceFactory<T>
{
protected static List<NotificaitonSettings<T>> settings = new List<NotificaitonSettings<T>>();
static NotificationServiceFactory()
{
settings.Add(new NotificaitonSettings<T>
{
Predicate = m => !String.IsNullOrEmpty(m.Email),
Service = new NotificationService(new EmailNotification(), new EmailFormatter())
});
settings.Add(new NotificaitonSettings<T>
{
Predicate = m => !String.IsNullOrEmpty(m.Fax),
Service = new NotificationService(new FaxNotification(), new FaxFormatter())
});
}
public NotificationService Create(T model)
{
return settings.FirstOrDefault(s => s.Predicate(model)).Service;
}
}
This implementation configures the factory using static list, you could use a IoC container if it supports this kind of operations.
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.