C# 复合应用程序的最佳日志记录方法?
我正在创建一个复合WPF(Prism)应用程序,其中包含几个不同的项目(Shell、模块等)。我正在准备使用Log4Net实现日志记录。似乎有两种方法可以设置日志记录:C# 复合应用程序的最佳日志记录方法?,c#,logging,log4net,prism,C#,Logging,Log4net,Prism,我正在创建一个复合WPF(Prism)应用程序,其中包含几个不同的项目(Shell、模块等)。我正在准备使用Log4Net实现日志记录。似乎有两种方法可以设置日志记录: 让Shell项目执行所有实际的日志记录。它获取对Log4Net的引用,其他项目触发复合事件,让Shell知道它需要记录一些东西。这些项目仅在Shell的app.config文件(DEBUG、ERROR等)中打开日志记录的级别触发事件,以免降低性能 为每个项目(包括模块)提供一个Log4Net引用,让项目自己记录到一个公共日志文
- 让Shell项目执行所有实际的日志记录。它获取对Log4Net的引用,其他项目触发复合事件,让Shell知道它需要记录一些东西。这些项目仅在Shell的app.config文件(DEBUG、ERROR等)中打开日志记录的级别触发事件,以免降低性能
- 为每个项目(包括模块)提供一个Log4Net引用,让项目自己记录到一个公共日志文件中,而不是将消息发送到Shell进行记录
哪种方法更好?或者,还有另一种方法我应该考虑吗?感谢您的帮助。为每个模块提供单独的记录器配置可能会在部署时出现问题。请记住,超级用户或管理员可能会完全更改日志记录的目标,重定向到数据库或中央存储库聚合日志记录服务(如的)。如果所有单独的模块都有单独的配置,超级用户/管理员必须为每个模块重复配置(在每个.config文件中,或在主app.config中的每个模块的部分中),并在每次位置/格式发生更改时重复此操作。此外,由于appender是在运行时从配置中添加的,并且可能有一些appender您目前一无所知,因此有人可能会使用一个appender来锁定文件并导致应用程序模块之间的冲突。Hsving一个log4.net配置简化了管理
仍然可以根据每个模块的需要单独配置各个模块(例如,DB层的信息,UI层的错误)。每个模块将通过请求自己的类型来获取记录器:
LogManager.GetLogger(typeof(MyModule);
,但只有Shell将配置记录器(例如调用XmlConfigurator.configure),使用自己的app.config。登录Prism最简单的方法是覆盖引导程序中的LoggerFacade
属性。通过覆盖LoggerFacade
,只要记录器实现了ILoggerFac,您就可以使用所需的任何配置传入任何记录器的实例ade
接口
我发现以下内容对于日志记录非常有效(我使用的是企业库日志记录块,但对Log4Net应用类似的东西应该是直接的):
在Shell中创建Boostrapper:
-My Project
-Shell Module (add a reference to the Infrastructure project)
-Bootstrapper.cs
在基础架构项目中创建日志适配器,即:
-My Project
-Infrastructure Module
-Adapters
-Logging
-MyCustomLoggerAdapter.cs
-MyCustomLoggerAdapterExtendedAdapter.cs
-IFormalLogger.cs
MyCustomLoggerAdapter类将用于覆盖引导程序中的“LoggerFacade”属性。它应该有一个默认的contstructor,用于更新所有内容
注意:通过覆盖引导程序中的LoggerFacade
属性,您为Prism提供了一种用于记录其内部消息的日志机制。您可以在整个应用程序中使用此日志记录程序,也可以扩展日志记录程序以获得功能更全面的日志记录程序。(请参见MyCustomLoggerAdapterExtendedAdapter
/iFormalLoger
)
我认为日志策略不需要单独的模块。通过将日志策略添加到基础架构模块,所有其他模块都将获得所需的引用(假设您将基础架构模块添加为对其他模块的引用)。通过将记录器添加到Boostrapper,您可以让UnityContainer根据需要注入日志策略
CodePlex上的CompositeWPF contrib项目中也有一个
HTH的我终于回到了这个问题上,结果证明答案非常简单。在Shell项目中,将Log4Net配置为自定义记录器。Prism文档(2009年2月)在第287页解释了如何进行此操作。Shell项目是唯一需要引用Log4Net的项目。要访问记录器(假设所有模块都传递了对Prism IOC容器的引用),只需在IOC容器中解析iLogger Facade,这将为您提供对自定义记录器的引用。以正常方式将消息传递给此记录器
因此,不需要任何事件返回到Shell,也不需要模块有Log4Net引用。天哪,我喜欢IOC容器!上面提到的
LoggerFacade
的问题是,应用程序的非棱柱部分不知道它。Logger IMHO需要更低级别和更普遍地访问而不仅仅是在复合框架内
我的建议是,为什么不仅仅依靠标准的
Debug/Trace
并实现您自己的TraceListener
。这样它对Prism/nonPrism两个部分都能很好地工作。您可以用它实现所需的灵活性。即使每个项目都有自己对Log4Net的引用,它们也不会有单独的配置文件——不是吗解决方案中的每个项目都有自己的日志记录(使用自己的Log4Net引用)实际做法似乎是在主应用程序中使用单个配置文件。请看,这个问题仍然存在。我了解到Prism有自己的日志记录功能,它可以作为Log4Net等日志记录程序的接口。但我仍然没有找到日志记录策略的最佳做法。我最终找到了我的答案——请参见下文。我不打算更改此选项Metro Smurf的回答“正确”。他确实给出了一个将Log4Net与Prism接口的好例子。但我下面的回答真正解决了如何设置记录器的核心问题。Metro Smurf的回答比你的回答更“最佳实践”。阿门。我正在开发一个应用程序,开发人员在其中引用了ILogger
facade一些非常低级的代码。它给我一种不好的感觉。是的,但是log4
public class MyCustomLoggerAdapter : ILoggerFacade
{
#region ILoggerFacade Members
/// <summary>
/// Logs an entry using the Enterprise Library logging.
/// For logging a Category.Exception type, it is preferred to use
/// the EnterpriseLibraryLoggerAdapter.Exception methods."
/// </summary>
public void Log( string message, Category category, Priority priority )
{
if( category == Category.Exception )
{
Exception( new Exception( message ), ExceptionPolicies.Default );
return;
}
Logger.Write( message, category.ToString(), ( int )priority );
}
#endregion
/// <summary>
/// Logs an entry using the Enterprise Library Logging.
/// </summary>
/// <param name="entry">the LogEntry object used to log the
/// entry with Enterprise Library.</param>
public void Log( LogEntry entry )
{
Logger.Write( entry );
}
// Other methods if needed, i.e., a default Exception logger.
public void Exception ( Exception ex ) { // do stuff }
}
public class MyCustomLoggerAdapterExtendedAdapter : MyCustomLoggerAdapter, IFormalLogger
{
private readonly ILoggingPolicySection _config;
private LogEntry _infoPolicy;
private LogEntry _debugPolicy;
private LogEntry _warnPolicy;
private LogEntry _errorPolicy;
private LogEntry InfoLog
{
get
{
if( _infoPolicy == null )
{
LogEntry log = GetLogEntryByPolicyName( LogPolicies.Info );
_infoPolicy = log;
}
return _infoPolicy;
}
}
// removed backing code for brevity
private LogEntry DebugLog... WarnLog... ErrorLog
// ILoggingPolicySection is passed via constructor injection in the bootstrapper
// and is used to configure various logging policies.
public MyCustomLoggerAdapterExtendedAdapter ( ILoggingPolicySection loggingPolicySection )
{
_config = loggingPolicySection;
}
#region IFormalLogger Members
/// <summary>
/// Info: informational statements concerning program state,
/// representing program events or behavior tracking.
/// </summary>
/// <param name="message"></param>
public void Info( string message )
{
InfoLog.Message = message;
InfoLog.ExtendedProperties.Clear();
base.Log( InfoLog );
}
/// <summary>
/// Debug: fine-grained statements concerning program state,
/// typically used for debugging.
/// </summary>
/// <param name="message"></param>
public void Debug( string message )
{
DebugLog.Message = message;
DebugLog.ExtendedProperties.Clear();
base.Log( DebugLog );
}
/// <summary>
/// Warn: statements that describe potentially harmful
/// events or states in the program.
/// </summary>
/// <param name="message"></param>
public void Warn( string message )
{
WarnLog.Message = message;
WarnLog.ExtendedProperties.Clear();
base.Log( WarnLog );
}
/// <summary>
/// Error: statements that describe non-fatal errors in the application;
/// sometimes used for handled exceptions. For more defined Exception
/// logging, use the Exception method in this class.
/// </summary>
/// <param name="message"></param>
public void Error( string message )
{
ErrorLog.Message = message;
ErrorLog.ExtendedProperties.Clear();
base.Log( ErrorLog );
}
/// <summary>
/// Logs an Exception using the Default EntLib Exception policy
/// as defined in the Exceptions.config file.
/// </summary>
/// <param name="ex"></param>
public void Exception( Exception ex )
{
base.Exception( ex, ExceptionPolicies.Default );
}
#endregion
/// <summary>
/// Creates a LogEntry object based on the policy name as
/// defined in the logging config file.
/// </summary>
/// <param name="policyName">name of the policy to get.</param>
/// <returns>a new LogEntry object.</returns>
private LogEntry GetLogEntryByPolicyName( string policyName )
{
if( !_config.Policies.Contains( policyName ) )
{
throw new ArgumentException( string.Format(
"The policy '{0}' does not exist in the LoggingPoliciesCollection",
policyName ) );
}
ILoggingPolicyElement policy = _config.Policies[policyName];
var log = new LogEntry();
log.Categories.Add( policy.Category );
log.Title = policy.Title;
log.EventId = policy.EventId;
log.Severity = policy.Severity;
log.Priority = ( int )policy.Priority;
log.ExtendedProperties.Clear();
return log;
}
}
public interface IFormalLogger
{
void Info( string message );
void Debug( string message );
void Warn( string message );
void Error( string message );
void Exception( Exception ex );
}
public class MyProjectBootstrapper : UnityBootstrapper
{
protected override void ConfigureContainer()
{
// ... arbitrary stuff
// create constructor injection for the MyCustomLoggerAdapterExtendedAdapter
var logPolicyConfigSection = ConfigurationManager.GetSection( LogPolicies.CorporateLoggingConfiguration );
var injectedLogPolicy = new InjectionConstructor( logPolicyConfigSection as LoggingPolicySection );
// register the MyCustomLoggerAdapterExtendedAdapter
Container.RegisterType<IFormalLogger, MyCustomLoggerAdapterExtendedAdapter>(
new ContainerControlledLifetimeManager(), injectedLogPolicy );
}
private readonly MyCustomLoggerAdapter _logger = new MyCustomLoggerAdapter();
protected override ILoggerFacade LoggerFacade
{
get
{
return _logger;
}
}
}
public partial class Shell : Window, IShellView
{
private readonly IFormalLogger _logger;
private readonly ILoggerFacade _loggerFacade;
public Shell( IFormalLogger logger, ILoggerFacade loggerFacade )
{
_logger = logger;
_loggerFacade = loggerFacade
_logger.Debug( "Shell: Instantiating the .ctor." );
_loggerFacade.Log( "My Message", Category.Debug, Priority.None );
InitializeComponent();
}
#region IShellView Members
public void ShowView()
{
_logger.Debug( "Shell: Showing the Shell (ShowView)." );
_loggerFacade.Log( "Shell: Showing the Shell (ShowView).", Category.Debug, Priority.None );
this.Show();
}
#endregion
}