.net 单例、日志记录和全局设置-实现的好坏?
我有一个日志类,它需要从应用程序的几乎所有地方调用 但是,它需要在应用程序开始时设置“写入路径”、“日志级别”以及是否“启用” 我不想每次都给这个参数,也不想把Logging类作为参数传递给应用程序中的每个对象,所以我确实使用singleton模式进行日志记录 最近我遭受了很多紧耦合类的痛苦,我不想再犯同样的错误,但考虑到这一点,这似乎是唯一好的解决方案 更新: 我并不真正关心日志记录,我关心的是解决类似的设计问题,我对另一个需要从这么多类中使用的全局设置对象也有同样的困境。但是将它注入到每一个代码中只会产生可怕的开销和可读性较差的代码 您对这个实现有什么看法?当您遇到类似的设计决策时,您会怎么做.net 单例、日志记录和全局设置-实现的好坏?,.net,singleton,scope,settings,global,.net,Singleton,Scope,Settings,Global,我有一个日志类,它需要从应用程序的几乎所有地方调用 但是,它需要在应用程序开始时设置“写入路径”、“日志级别”以及是否“启用” 我不想每次都给这个参数,也不想把Logging类作为参数传递给应用程序中的每个对象,所以我确实使用singleton模式进行日志记录 最近我遭受了很多紧耦合类的痛苦,我不想再犯同样的错误,但考虑到这一点,这似乎是唯一好的解决方案 更新: 我并不真正关心日志记录,我关心的是解决类似的设计问题,我对另一个需要从这么多类中使用的全局设置对象也有同样的困境。但是将它注入到每一个
注意:请不要建议像“使用Log4X库”之类的东西。首先-您可以将日志编写器作为跟踪侦听器编写,并使用
跟踪。从方法中编写等吗
你真的需要一个实例吗?例如,如果您想将其抽象为TextWriter
或类似的,那么这将非常有用,但如果它将是一个独立的单例,那么这些方法是否可以不直接使用静态方法,即Log.Write(…)
(而不是传递日志实例)
这是一个普遍的问题——它取决于正在进行日志记录的类型。对于“Manager”(ETC)类,您可以考虑使用依赖注入(Unity、StultMag等)来实现这一点。不过,我通常不会在DTO中使用注入。即使您不想得到“使用Log4X”的建议(尽管您没有确切说明为什么要重新发明轮子),查看各种日志库做出的设计决策似乎是明智的
根据我的经验,当应用于日志记录时,紧密耦合的问题就没有那么重要了——特别是,我很少想测试我的应用程序的日志记录端,我也不介意它是否在单元测试期间登录到控制台
简言之,“正常”模式是:
private static readonly Logger log = LogManager.GetLogger(...);
(通过适当的名称更改等)在使用静态方法时在美学上没有吸引力,但在实践中效果相当好。至少,这是我的经验。这是ASP.Net吗?如果是这样,您可以使用Global.asax中的错误事件
对于许多依赖项,您是否考虑过使用依赖项注入框架
更新
我不确定性能的含义,也不确定性能与你的应用程序有多相关,但这个框架看起来很有趣:
您还可以利用
如果您使用PostSharp,我会对它的工作原理感兴趣。您可能可以在这里使用singleton。应用程序中的每个类和logger类之间都有紧密的耦合,但如果每个类中都需要logger类和全局设置类,这是可以接受的。日志和设置实际上是以两种不同的方式处理的,因此如果我理解正确,您实际的问题与处理程序集之间的全局设置更相关
关于日志记录,事情很清楚——使用全局单例是很常见的,尽管它确实将您的库与日志库紧密耦合。在IMHO中,使用跟踪侦听器是更好的解决方案
但是,当谈到应用程序设置时,您当然应该避免将其全球化。将所有与应用程序相关的设置仅保留在一个位置(那些应该持久化的设置),但不能静态地供其他库使用。因此,将适当的设置传递给其他程序集必须由调用方负责,反之亦然。在这种情况下,我个人使用静态类。该类具有静态配置字段(用于手动实验)和一些函数,可以使用相应的.config文件节中的configuration填充这些字段
这实际上非常接近于使用DI时的情况,因为您可以“注入”新配置。要将配置更改为新模型,我只需更改保持“活动”配置部分的.config文件字段
这是易于使用,易于维护,每个人都理解它。。。我看不出它有什么特别的缺点 您可以研究的一件事是按功能打包。据称,采用这种技术会解决导致类之间高度耦合的一些问题。更具体地说,这意味着您的应用程序中的每个功能中只有一个类负责与配置提供程序对话(这很可能是配置/设置/安装功能本身的一部分)。耦合级别仍然处于高水平,但由于其定义明确,因此应可管理。类似:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using log4net.Config;
using log4net.Appender;
using System.Reflection;
using System.IO;
using System.Globalization;
using log4net.Core;
using System.Web;
namespace GenApp.Utils
{
///<summary> Wrapper around log4net with dynamically adjustable verbosity</summary>
public class Logger
{
private static Logger inst = new Logger ();
public static Logger Inst ()
{
inst.ConfigureLogging ();
return inst;
}
public enum DebugLevel : int
{
Fatal_Msgs = 0,
Fatal_Error_Msgs = 1,
Fatal_Error_Warn_Msgs = 2,
Fatal_Error_Warn_Info_Msgs = 3,
Fatal_Error_Warn_Info_Debug_Msgs = 4
}
public static void Debug ( GenApp.Bo.User objUser, ILog logger, string msg )
{
DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel;
string strLogLevel = Logger.GetLogTypeString ( debugLevel );
inst.SetLogingLevel ( strLogLevel );
logger.Debug ( msg );
} //eof method
public static void Info ( GenApp.Bo.User objUser, ILog logger, string msg )
{
DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel;
string strLogLevel = Logger.GetLogTypeString ( debugLevel );
inst.SetLogingLevel ( strLogLevel );
logger.Info ( msg );
} //eof method
public static void Warn ( GenApp.Bo.User objUser, ILog logger, string msg )
{
DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel;
string strLogLevel = Logger.GetLogTypeString ( debugLevel );
inst.SetLogingLevel ( strLogLevel );
logger.Warn ( msg );
} //eof method
public static void Error ( GenApp.Bo.User objUser, ILog logger, string msg )
{
DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel;
string strLogLevel = Logger.GetLogTypeString ( debugLevel );
inst.SetLogingLevel ( strLogLevel );
logger.Error ( msg );
} //eof method
public static void Fatal ( GenApp.Bo.User objUser, ILog logger, string msg )
{
DebugLevel debugLevel = (DebugLevel)objUser.UserSettings.LogLevel;
string strLogLevel = Logger.GetLogTypeString ( debugLevel );
inst.SetLogingLevel ( strLogLevel );
logger.Fatal ( msg );
} //eof method
/// <summary>
/// Activates debug level
/// </summary>
/// <sourceurl>http://geekswithblogs.net/rakker/archive/2007/08/22/114900.aspx</sourceurl>
private void SetLogingLevel ( string strLogLevel )
{
this.ConfigureLogging ();
string strChecker = "WARN_INFO_DEBUG_ERROR_FATAL";
if (String.IsNullOrEmpty ( strLogLevel ) == true || strChecker.Contains ( strLogLevel ) == false)
throw new ArgumentOutOfRangeException ( " The strLogLevel should be set to WARN , INFO , DEBUG ," );
log4net.Repository.ILoggerRepository[] repositories = log4net.LogManager.GetAllRepositories ();
//Configure all loggers to be at the debug level.
foreach (log4net.Repository.ILoggerRepository repository in repositories)
{
repository.Threshold = repository.LevelMap[strLogLevel];
log4net.Repository.Hierarchy.Hierarchy hier = (log4net.Repository.Hierarchy.Hierarchy)repository;
log4net.Core.ILogger[] loggers = hier.GetCurrentLoggers ();
foreach (log4net.Core.ILogger logger in loggers)
{
( (log4net.Repository.Hierarchy.Logger)logger ).Level = hier.LevelMap[strLogLevel];
}
}
//Configure the root logger.
log4net.Repository.Hierarchy.Hierarchy h = (log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository ();
log4net.Repository.Hierarchy.Logger rootLogger = h.Root;
rootLogger.Level = h.LevelMap[strLogLevel];
}
///<summary>
///0 -- prints only FATAL messages
///1 -- prints FATAL and ERROR messages
///2 -- prints FATAL , ERROR and WARN messages
///3 -- prints FATAL , ERROR , WARN and INFO messages
///4 -- prints FATAL , ERROR , WARN , INFO and DEBUG messages
///</summary>
private static string GetLogTypeString ( DebugLevel debugLevel )
{
string srtLogLevel = String.Empty;
switch (debugLevel)
{
case DebugLevel.Fatal_Msgs:
srtLogLevel = "FATAL";
break;
case DebugLevel.Fatal_Error_Msgs:
srtLogLevel = "ERROR";
break;
case DebugLevel.Fatal_Error_Warn_Msgs:
srtLogLevel = "WARN";
break;
case DebugLevel.Fatal_Error_Warn_Info_Msgs:
srtLogLevel = "INFO";
break;
case DebugLevel.Fatal_Error_Warn_Info_Debug_Msgs:
srtLogLevel = "DEBUG";
break;
default:
srtLogLevel = "FATAL";
break;
} //eof switch
return srtLogLevel;
} //eof GetLogTypeString
/// <summary>
/// The path where the configuration is read from.
/// This value is set upon a call to ConfigureLogging().
/// </summary>
private string configurationFilePath;
public void ConfigureLogging ()
{
lock (this)
{
bool configured = false;
#region ConfigureByThePathOfTheEntryAssembly
// Tells the logging system the correct path.
Assembly a = Assembly.GetEntryAssembly ();
if (a != null && a.Location != null)
{
string path = a.Location + ".config";
if (File.Exists ( path ))
{
log4net.Config.DOMConfigurator.Configure (
new FileInfo ( path ) );
configurationFilePath = path;
configured = true;
}
else
{
path = FindConfigInPath ( Path.GetDirectoryName ( a.Location ) );
if (File.Exists ( path ))
{
log4net.Config.DOMConfigurator.Configure (
new FileInfo ( path ) );
configurationFilePath = path;
configured = true;
}
}
}
#endregion ConfigureByThePathOfTheEntryAssembly
#region ConfigureByWeb.config
// Also, try web.config.
if (!configured)
{
if (HttpContext.Current != null &&
HttpContext.Current.Server != null &&
HttpContext.Current.Request != null)
{
string path = HttpContext.Current.Server.MapPath (
HttpContext.Current.Request.ApplicationPath );
path = path.TrimEnd ( '\\' ) + "\\Web.config";
if (File.Exists ( path ))
{
log4net.Config.DOMConfigurator.Configure (
new FileInfo ( path ) );
configurationFilePath = path;
configured = true;
}
}
}
#endregion ConfigureByWeb.config
#region ConfigureByThePathOfTheExecutingAssembly
if (!configured)
{
// Tells the logging system the correct path.
a = Assembly.GetExecutingAssembly ();
if (a != null && a.Location != null)
{
string path = a.Location + ".config";
if (File.Exists ( path ))
{
log4net.Config.DOMConfigurator.Configure (
new FileInfo ( path ) );
configurationFilePath = path;
configured = true;
}
else
{
path = FindConfigInPath ( Path.GetDirectoryName ( a.Location ) );
if (File.Exists ( path ))
{
log4net.Config.DOMConfigurator.Configure (
new FileInfo ( path ) );
configurationFilePath = path;
configured = true;
}
}
}
}
#endregion ConfigureByThePathOfTheExecutingAssembly
#region ConfigureByThePathOfTheCallingAssembly
if (!configured)
{
// Tells the logging system the correct path.
a = Assembly.GetCallingAssembly ();
if (a != null && a.Location != null)
{
string path = a.Location + ".config";
if (File.Exists ( path ))
{
log4net.Config.DOMConfigurator.Configure (
new FileInfo ( path ) );
configurationFilePath = path;
configured = true;
}
else
{
path = FindConfigInPath ( Path.GetDirectoryName ( a.Location ) );
if (File.Exists ( path ))
{
log4net.Config.DOMConfigurator.Configure (
new FileInfo ( path ) );
configurationFilePath = path;
configured = true;
}
}
}
}
#endregion ConfigureByThePathOfTheCallingAssembly
#region ConfigureByThePathOfTheLibIsStored
if (!configured)
{
// Look in the path where this library is stored.
a = Assembly.GetAssembly ( typeof ( Logger ) );
if (a != null && a.Location != null)
{
string path = FindConfigInPath ( Path.GetDirectoryName ( a.Location ) );
if (File.Exists ( path ))
{
log4net.Config.DOMConfigurator.Configure (
new FileInfo ( path ) );
configurationFilePath = path;
configured = true;
}
}
}
#endregion ConfigureByThePathOfTheLibIsStored
} //eof lock
} //eof method
/// <summary>
/// Searches for a configuration file in the given path.
/// </summary>
private string FindConfigInPath (
string path )
{
string[] files = Directory.GetFiles ( path );
if (files != null && files.Length > 0)
{
foreach (string file in files)
{
if (Path.GetExtension ( file ).Trim ( '.' ).ToLower (
CultureInfo.CurrentCulture ) == "config")
{
return file;
}
}
}
// Not found.
return string.Empty;
} //eof method
/// <summary>
/// Remove dynamically appenders
/// </summary>
/// <param name="appenderName"></param>
/// <param name="threshold"></param>
public static void SetThreshold ( string appenderName, Level threshold )
{
foreach (AppenderSkeleton appender in LogManager.GetRepository ().GetAppenders ())
{
if (appender.Name == appenderName)
{
appender.Threshold = threshold;
appender.ActivateOptions ();
break;
}
}
} //eof method
} //eof class
} //eof namespace
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用log4net;
使用log4net.Config;
使用log4net.Appender;
运用系统反思;
使用System.IO;
利用制度全球化;
使用log4net.Core;
使用System.Web;
命名空间GenApp.Utils
{
///log4net的包装器,详细程度可动态调整
公共类记录器
{
专用静态记录器inst=新记录器();
公共静态记录器仪器()
{
inst.ConfigureLogging();
返回仪表;
}
公共枚举调试级别:int
{
致命的_Msgs=0,
致命错误\u Msgs=1,
致命错误警告Msgs=2,
致命错误警告信息Msgs=3,
致命错误警告信息调试Msgs=4
}
公共静态void调试(GenApp.Bo.User objUser、ILog记录器、字符串消息