为 .NET 中简单且与框架无关的日志记录提供外观






4.90/5 (12投票s)
日志记录是每个应用程序的重要组成部分,但您可能不喜欢到处都依赖于特定的日志记录框架。这种日志记录外观为您提供了一个通用接口,将您选择的日志记录框架与您的代码分离。
在开始阅读之前:基于此项目,我们启动(并发布)了 SLF,即 Simple Logging Façade(简单日志记录外观)。SLF 比这个项目大一点,但更灵活,同时保持了简单性的目标。您可以在 http://slf.codeplex.com 阅读有关它的信息。
引言
日志记录是一个重要的方面,但我不喜欢到处依赖特定的日志记录框架。这就是日志记录外观派上用场的地方。基本上,外观只是为您提供了一个通用 接口
,将使用的日志记录框架与您的代码分离。
//ILogger is the facade. Behind the scenes,
//a framework of your choice is used
ILogger logger = LoggerService.Logger;
logger.Log("hello world");
日志记录外观的想法并不新鲜,但我想与您分享这个,原因有几个
- 它非常容易使用。
- 它在日志记录方面提供了相当多的重载。
- 核心库根本不依赖于其他库。
- 有两个外观(独立的 DLL),它们通过 企业库 日志记录块或 BitFactory 日志记录器 进行日志记录。希望有更多!
- 编写您自己的外观就像重写一个方法一样简单。
- 它不属于另一个项目(完全独立),并且没有附加任何许可。您可以随意使用它。
- 它非常容易使用。
这是一种创建基于文件的日志记录器(使用 BitFactory 外观)并使其全局可访问的方法。这只需要几行代码
//create a new logger instance
string file = @"C:\logfile.txt";
ILogger logger = BitFactoryLogger.CreateSingleFileLogger(file);
//use the global LoggerService to store the logger
LoggerService.SetLogger(logger);
...
//this will store the info in the log file
LoggerService.Logger.Log("This is an information");
通过 ILogger 进行日志记录
此项目的全部目的是保护您的库免受实际选择的日志记录框架的影响。因此,您始终通过 ILogger
实例进行日志记录。ILogger
提供了 Log
方法的许多重载,这里有几个
public void LogData(ILogger logger)
{
logger.Log("An information");
logger.Log("Something Happened", TraceEventType.Warning);
//LogItem is the most verbose version
LogItem item = new LogItem();
item.Message = "My Message";
item.EventId = 999;
item.Categories.Add("Foo");
item.Priority = 10;
logger.Log(item);
try
{
DivideByZero();
}
catch(Exception e)
{
logger.Log(e);
logger.Log("Additional message.", e);
logger.Log("Additional message.", e, TraceEventType.Critical);
}
}
初始化 ILogger 实现
在应用程序的初始化过程中,您将需要指定要使用的日志记录器实现。这可能以声明方式或直接在代码中发生。这是来自 NetDrives 的初始化代码,它通过 AutoFac IOC 容器提供了一个日志记录器。
请注意,我为调试构建注册了一个 ConsoleLogger
,而发布构建则写入日志文件。这些是完全不同的类,但这无关紧要 - 它们都实现了 ILogger 接口
//init IOC container builder
var builder = new ContainerBuilder();
//register single logger instance
ILogger logger;
#if (DEBUG)
logger = new ConsoleLogger();
#else
logger = BitFactoryLogger.CreateSingleFileLogger(AppUtil.LogFile);
#endif
//register logger
builder.Register(logger).As<ILogger>();
通过 LoggerService 进行注册和访问
我更喜欢通过 IOC 容器初始化和访问我的日志记录器,但您可以根据自己的喜好进行操作。如果您缺少使您的 ILogger
全局可访问的地方,可以使用 static LoggerService
类
public void InitApp()
{
//create a file logger (use BitFactory facade)
string logFile = @"C:\MyLogFile.txt";
ILogger logger = BitFactoryLogger.CreateSingleFileLogger(logFile);
//register as globally used logger
LoggerService.SetLogger(logger);
}
private void Foo()
{
try
{
DoSomethingWrong();
}
catch(Exception e)
{
//get registered logger and log exception
ILogger logger = LoggerService.Logger;
logger.Log(e);
}
}
关于 LoggerService
的一个好事情:它始终保证您有一个有效的 ILogger 实例
。如果未设置日志记录器,它只会退回到一个 NullLogger
实现,该实现根本不创建任何输出。这是实现
namespace Hardcodet.Util.Logging
{
/// <summary>
/// Provides a global repository for a given <see cref="ILogger"/>
/// instance. This class ensures that the <see cref="Logger"/>
/// property is never nullo - in case no logger is defined, it
/// automatically installs a <see cref="NullLogger"/>
/// instance.
/// </summary>
public static class LoggerService
{
private static ILogger logger = new NullLogger();
/// <summary>
/// Gets the installed <see cref="ILogger"/> implementation.
/// </summary>
/// <remarks>This property always returns a valid
/// logger.</remarks>
public static ILogger Logger
{
get { return logger; }
}
/// <summary>
/// Installs a given logger or resets the <see cref="Logger"/>
/// to a <see cref="NullLogger"/> instance if the
/// <paramref name="loggerImplementation"/> is a null
/// reference.
/// </summary>
/// <param name="loggerImplementation">The logger to be
/// used globally, or a null reference in order to reset
/// the service.</param>
public static void SetLogger(ILogger loggerImplementation)
{
logger = loggerImplementation ?? new NullLogger();
}
}
}
创建一个新的日志记录外观
如果您想使用另一个日志记录框架(例如 NLog 或 Log4Net),创建一个新的外观非常容易。基本上,您创建一个新项目,设置对基本库的引用,并编写一个类,该类
- 直接实现
ILogger
- 或者,更简单的是,派生自
abstract LoggerBase
类。
想分享您自己的外观吗?只需与我联系,我很乐意包含您的实现。
作为一个示例,这是 ConsoleLogger
(核心库的一部分)和企业库外观的代码
using System;
namespace Hardcodet.Util.Logging
{
/// <summary>
/// A very simple implementation of <see cref="ILogger"/>
/// that outputs all messages to the system console.
/// </summary>
public class ConsoleLogger : LoggerBase
{
/// <summary>
/// Logs a given item to the console.
/// </summary>
/// <param name="item">The item to be logged.</param>
/// <exception cref="ArgumentNullException">If <paramref name="item"/>
/// is a null reference.</exception>
public override void Log(LogItem item)
{
if (item == null) throw new ArgumentNullException("item");
Console.Out.WriteLine(item.ToLogMessage());
}
}
}
using Microsoft.Practices.EnterpriseLibrary.Logging;
namespace Hardcodet.Util.Logging.EntLibFacade
{
/// <summary>
/// An implementation of the <see cref="ILogger"/>
/// interface which outputs logged data using
/// the <see cref="Logger"/> of the MS Enterprise
/// Library.
/// </summary>
public class EnterpriseLibraryLogger : LoggerBase
{
/// <summary>
/// Writes a log entry to the Enterprise Library's
/// logging block. Output depends on the logging
/// block's configuration.
/// </summary>
/// <param name="item">An log item which encapsulates
/// information to be logged.</param>
public override void Log(LogItem item)
{
LogEntry entry = ConvertLogItem(item);
Logger.Write(entry);
}
/// <summary>
/// Creates a <c>LogEntry</c> instance which can be processed
/// by the Enterprise Library based on a given log item.
/// </summary>
/// <param name="item">An log item which encapsulates information
/// to be logged.</param>
/// <returns>An Enterprise Library item which corresponds
/// to the submitted <c>LogItem</c>.</returns>
private static LogEntry ConvertLogItem(LogItem item)
{
//assign properties
LogEntry entry = new LogEntry();
entry.Message = item.Message;
entry.Title = item.Title;
entry.AddErrorMessage(item.ErrorMessage);
entry.EventId = item.EventId;
entry.Priority = item.Priority;
entry.Severity = item.Severity;
entry.TimeStamp = item.TimeStamp;
foreach (string category in item.Categories)
{
item.Categories.Add(category);
}
return entry;
}
}
}
下载内容包含核心库、两个外部外观(BitFactory、企业库)和一个示例项目。希望你会喜欢!