我正在编写一个应用程序,其中日志记录是我的实际域模型的一部分。它是一个自动化和批处理工具,最终用户可以在实际应用程序中查看批处理作业的日志,而不仅仅是文本日志文件。
所以我的域模型包括一个LogMessage
类:
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
}
我也有一个Result
类,其收藏属性为LogMessages
。最终用户可以使用我的应用程序将结果保存到文件并从中打开。
public class Result
{
public bool Succeeded {get; set;}
public string StatusMessage {get; set;}
public IList<LogMessage> LogMessages {get; set;}
}
我的应用程序还支持第三方开发人员使用也可以编写日志消息的插件扩展应用程序。所以我为插件开发人员定义了一个通用的ILogger
接口。
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);
}
我向插件写了一个ILogger
的实例,写入Result.LogMessages
。
public interface IPlugIn
{
Output DoSomeThing(Input in, ILogger logger);
}
我显然也希望能够从我自己的内部代码登录,并最终希望Result.LogMessages
包含我的内部日志消息和来自插件的日志消息的混合。因此,遇到问题的最终用户可以向我发送一个结果文件,其中包含来自我的内部代码和所使用的任何插件的调试日志。
目前,我有一个使用自定义NLog目标的解决方案。
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);
}
}
此类将消息转发给分配给静态Result
属性的LogResultTarget.CurrentTargetResult
。我的内部代码记录到NLog记录器,并且我有一个ILogger
的实现,它也记录到NLog.Logger
。
这很有效,但感觉非常脆弱。如果CurrentTargetResult
设置不正确或未设置回null,我最终可能会将日志消息存储到不适用的结果中。另外因为只有一个静态CurrentTargetResult
,我无法同时支持处理多个结果。
有没有不同/更好的方法我可以接近这个?或者我正在尝试从根本上做错什么?
我认为你的方法是正确的,但你可以通过使用已经为你做这个抽象的库来节省工作量。 Common Logging图书馆就是你所追求的。
您的域代码仅取决于Common Logging的ILogger
界面。仅当您的域由运行时使用时,例如Web API,然后配置您将要使用的日志记录提供程序。
有许多预构建的提供程序可用作单独的nuget包:
Common.Logging提供支持.NET中所有以下流行的日志记录目标/框架的适配器:
我已经使用了很多年了,很高兴你的域/库代码可以在另一个上下文中重用,但不必对日志框架有一个固定的依赖(我已经从Enterprise Libraries迁移到log4net,最后NLog ...这是一件轻而易举的事。
认为static
CurrentTargetResult确实有点脆弱,但总体方法很好。
我建议进行以下更改:
CurrentTargetResult
非常静止,并且总是初始化,
像这样的东西:
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);
}
}
public class Result
{
public bool Succeeded {get; set;}
public string StatusMessage {get; set;}
public IList<LogMessage> LogMessages {get; set;} = new List<LogMessage>();
}
// 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:你也可以在InitializeTarget
覆盖LogResultTarget
来初始化目标