在现代软件开发中,日志记录是一个不可或缺的功能,它帮助开发者追踪程序的执行过程,定位问题和优化性能。本文将介绍一种高效日志记录系统的设计与实现方法,该系统不仅支持C#语言,还可以在Razor视图中使用,具有高度的灵活性和易用性。
该日志系统的核心思想是使用静态方法和Razor视图来创建和设置日志记录。它的优势在于,如果没有设置日志引擎,系统将不会执行任何日志记录操作。此外,它支持在代码库的任何地方使用(只要可以编写C#代码),包括Razor视图。系统采用延迟执行机制,这意味着在测试过程中不需要模拟日志记录,从而避免了测试失败的风险。同时,系统还支持轻松模拟日志记录,以便于测试。更重要的是,代码库中没有对实际日志引擎的任何引用,这意味着更换或升级日志引擎只需在提供适配器的一个类中进行,大大简化了维护工作。
该日志系统采用了JP Boodhoo在开发者训练营中展示的静态日志网关的概念,并将其进一步发展。在传统的静态日志网关中,每次调用日志记录器时,如果使用的不是log4net或NLog,它都会构建一个对象。虽然在内部可能继续重用同一个对象,但在代码库层面上,这并不明显。为了解决这个问题,该系统在项目根命名空间中实现了以下扩展:
public static class Log
{
private static Type _logType = typeof(NullLog);
private static ILog _logger;
public static void InitializeWith() where T : ILog, new()
{
_logType = typeof(T);
}
public static void InitializeWith(ILog loggerType)
{
_logType = loggerType.GetType();
_logger = loggerType;
}
public static ILog GetLoggerFor(string objectName)
{
var logger = _logger;
if (_logger == null)
{
logger = Activator.CreateInstance(_logType) as ILog;
if (logger != null)
{
logger.InitializeFor(objectName);
}
}
return logger;
}
}
在上述代码中,当调用InitializeFor方法时,会得到类似于以下实现:
_logger = LogManager.GetLogger(loggerName);
为了进一步优化性能,系统使用了并发字典来确保每个类型的日志记录器只有一个实例。这在第一次添加对象时会带来初始性能开销,但从那以后,性能将非常快。虽然每次都会进行反射调用,但这是可以接受的,因为大多数日志引擎都会这样做。
为了使日志记录更加便捷,系统还提供了以下扩展功能:
public static class LogExtensions
{
private static readonly Lazy> _dictionary = new Lazy>(() => new ConcurrentDictionary());
public static ILog Log(this T type)
{
string objectName = typeof(T).FullName;
return Log(objectName);
}
public static ILog Log(this string objectName)
{
return _dictionary.Value.GetOrAdd(objectName, Infrastructure.Logging.Log.GetLoggerFor);
}
}
通过使用并发字典,系统大大加快了获取日志记录器的操作速度。虽然在第一次添加对象时会有性能开销,但之后的性能非常快。这种设计使得日志记录系统既高效又易于使用。