asp.net core + log4net+exceptionles+DI
参考了ABP的代码,我也用依赖注入的原则,设计了日志模块。
与abp不同之处在于:1)DI容器使用的是.net core自带的注入容器,2)集成了excetpionless日志模块,3)不依赖于abp
主要的思想就是,1)定义日志操作接口 2)使用log4net实现日志接口,3)实现注入 4)使用日志
1)定义日志接口,这里直接使用了castle的日志接口
2)使用log4net实现日志接口
[Serializable] public class Log4NetLogger : MarshalByRefObject, ILogger { private static readonly Type DeclaringType = typeof(Log4NetLogger); private log4net.Core.ILogger _logger; private Log4NetLoggerFactory _factory; public Log4NetLogger() { _factory = new Log4NetLoggerFactory(); _logger = _factory.GetLogger("cislog"); } public bool IsDebugEnabled { get { return _logger.IsEnabledFor(Level.Debug); } } public bool IsErrorEnabled { get { return _logger.IsEnabledFor(Level.Error); } } public bool IsFatalEnabled { get { return _logger.IsEnabledFor(Level.Fatal); } } public bool IsInfoEnabled { get { return _logger.IsEnabledFor(Level.Info); } } public bool IsWarnEnabled { get { return _logger.IsEnabledFor(Level.Warn); } } public override string ToString() { return _logger.ToString(); } public void Debug(string message) { if (IsDebugEnabled) { _logger.Log(DeclaringType, Level.Debug, message, null); } } public void Debug(Func<string> messageFactory) { if (IsDebugEnabled) { _logger.Log(DeclaringType, Level.Debug, messageFactory.Invoke(), null); } } public void Debug(string message, Exception exception) { if (IsDebugEnabled) { _logger.Log(DeclaringType, Level.Debug, message, exception); } } public void DebugFormat(string format, params Object[] args) { if (IsDebugEnabled) { _logger.Log(DeclaringType, Level.Debug, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null); } } public void DebugFormat(Exception exception, string format, params Object[] args) { if (IsDebugEnabled) { _logger.Log(DeclaringType, Level.Debug, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception); } } public void DebugFormat(IFormatProvider formatProvider, string format, params Object[] args) { if (IsDebugEnabled) { _logger.Log(DeclaringType, Level.Debug, new SystemStringFormat(formatProvider, format, args), null); } } public void DebugFormat(Exception exception, IFormatProvider formatProvider, string format, params Object[] args) { if (IsDebugEnabled) { _logger.Log(DeclaringType, Level.Debug, new SystemStringFormat(formatProvider, format, args), exception); } } public void Error(string message) { if (IsErrorEnabled) { _logger.Log(DeclaringType, Level.Error, message, null); } } public void Error(Func<string> messageFactory) { if (IsErrorEnabled) { _logger.Log(DeclaringType, Level.Error, messageFactory.Invoke(), null); } } public void Error(string message, Exception exception) { if (IsErrorEnabled) { _logger.Log(DeclaringType, Level.Error, message, exception); } } public void ErrorFormat(string format, params Object[] args) { if (IsErrorEnabled) { _logger.Log(DeclaringType, Level.Error, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null); } } public void ErrorFormat(Exception exception, string format, params Object[] args) { if (IsErrorEnabled) { _logger.Log(DeclaringType, Level.Error, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception); } } public void ErrorFormat(IFormatProvider formatProvider, string format, params Object[] args) { if (IsErrorEnabled) { _logger.Log(DeclaringType, Level.Error, new SystemStringFormat(formatProvider, format, args), null); } } public void ErrorFormat(Exception exception, IFormatProvider formatProvider, string format, params Object[] args) { if (IsErrorEnabled) { _logger.Log(DeclaringType, Level.Error, new SystemStringFormat(formatProvider, format, args), exception); } } public void Fatal(string message) { if (IsFatalEnabled) { _logger.Log(DeclaringType, Level.Fatal, message, null); } } public void Fatal(Func<string> messageFactory) { if (IsFatalEnabled) { _logger.Log(DeclaringType, Level.Fatal, messageFactory.Invoke(), null); } } public void Fatal(string message, Exception exception) { if (IsFatalEnabled) { _logger.Log(DeclaringType, Level.Fatal, message, exception); } } public void FatalFormat(string format, params Object[] args) { if (IsFatalEnabled) { _logger.Log(DeclaringType, Level.Fatal, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null); } } public void FatalFormat(Exception exception, string format, params Object[] args) { if (IsFatalEnabled) { _logger.Log(DeclaringType, Level.Fatal, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception); } } public void FatalFormat(IFormatProvider formatProvider, string format, params Object[] args) { if (IsFatalEnabled) { _logger.Log(DeclaringType, Level.Fatal, new SystemStringFormat(formatProvider, format, args), null); } } public void FatalFormat(Exception exception, IFormatProvider formatProvider, string format, params Object[] args) { if (IsFatalEnabled) { _logger.Log(DeclaringType, Level.Fatal, new SystemStringFormat(formatProvider, format, args), exception); } } public void Info(string message) { if (IsInfoEnabled) { _logger.Log(DeclaringType, Level.Info, message, null); } } public void Info(Func<string> messageFactory) { if (IsInfoEnabled) { _logger.Log(DeclaringType, Level.Info, messageFactory.Invoke(), null); } } public void Info(string message, Exception exception) { if (IsInfoEnabled) { _logger.Log(DeclaringType, Level.Info, message, exception); } } public void InfoFormat(string format, params Object[] args) { if (IsInfoEnabled) { _logger.Log(DeclaringType, Level.Info, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null); } } public void InfoFormat(Exception exception, string format, params Object[] args) { if (IsInfoEnabled) { _logger.Log(DeclaringType, Level.Info, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception); } } public void InfoFormat(IFormatProvider formatProvider, string format, params Object[] args) { if (IsInfoEnabled) { _logger.Log(DeclaringType, Level.Info, new SystemStringFormat(formatProvider, format, args), null); } } public void InfoFormat(Exception exception, IFormatProvider formatProvider, string format, params Object[] args) { if (IsInfoEnabled) { _logger.Log(DeclaringType, Level.Info, new SystemStringFormat(formatProvider, format, args), exception); } } public void Warn(string message) { if (IsWarnEnabled) { _logger.Log(DeclaringType, Level.Warn, message, null); } } public void Warn(Func<string> messageFactory) { if (IsWarnEnabled) { _logger.Log(DeclaringType, Level.Warn, messageFactory.Invoke(), null); } } public void Warn(string message, Exception exception) { if (IsWarnEnabled) { _logger.Log(DeclaringType, Level.Warn, message, exception); } } public void WarnFormat(string format, params Object[] args) { if (IsWarnEnabled) { _logger.Log(DeclaringType, Level.Warn, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null); } } public void WarnFormat(Exception exception, string format, params Object[] args) { if (IsWarnEnabled) { _logger.Log(DeclaringType, Level.Warn, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception); } } public void WarnFormat(IFormatProvider formatProvider, string format, params Object[] args) { if (IsWarnEnabled) { _logger.Log(DeclaringType, Level.Warn, new SystemStringFormat(formatProvider, format, args), null); } } public void WarnFormat(Exception exception, IFormatProvider formatProvider, string format, params Object[] args) { if (IsWarnEnabled) { _logger.Log(DeclaringType, Level.Warn, new SystemStringFormat(formatProvider, format, args), exception); } } public ILogger CreateChildLogger(string loggerName) { throw new NotImplementedException(); } }
public class Log4NetLoggerFactory : AbstractLoggerFactory { internal const string DefaultConfigFileName = "log4net.config"; private readonly ILoggerRepository _loggerRepository; public Log4NetLoggerFactory() : this(DefaultConfigFileName) { } public Log4NetLoggerFactory(string configFileName) { _loggerRepository = LogManager.CreateRepository( typeof(Log4NetLoggerFactory).GetAssembly(), typeof(log4net.Repository.Hierarchy.Hierarchy) ); XmlConfigurator.Configure(_loggerRepository, new FileInfo("log4net.config")); } public log4net.Core.ILogger GetLogger(string name) { return LogManager.GetLogger(_loggerRepository.Name, name).Logger; } public override ILogger Create(string name) { if (name == null) { throw new ArgumentNullException(nameof(name)); } return new Log4NetLogger(); } public override ILogger Create(string name, LoggerLevel level) { throw new NotSupportedException("Logger levels cannot be set at runtime. Please review your configuration file."); } } public static class TypeExtensions { public static Assembly GetAssembly(this Type type) { return type.GetTypeInfo().Assembly; } }
下面是扩展方法,这里实现注入:
public static class Log4NetLoggerExtensions { public static IServiceCollection UseExceptionless(this IServiceCollection services) { services.AddTransient<ILogger, Log4NetLogger>(); return services; } }
然后就可以在startup中使用了
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.UseExceptionless(); }
比如我们在HomeController中使用:
public class HomeController : Controller { private ILogger _logger; public HomeController(ILogger logger) { _logger = logger; } public IActionResult Index() { // throw new Exception("快点来!!!!!!!!!!!!!!!!!!"); _logger.Info("index view"); _logger.Debug("出错了!,快点来,!!"); _logger.Error("Controller Error"); return View(); } }
最后是配置文件:
最重要的是注意exceptionless的配置
<?xml version="1.0" encoding="utf-8" ?> <configuration> <!-- This section contains the log4net configuration settings --> <log4net> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout" value="%date [%thread] %-5level %logger - %message%newline" /> </appender> <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender"> <file value="Logs/" /> <appendToFile value="true" /> <rollingStyle value="Composite" /> <staticLogFileName value="false" /> <datePattern value="yyyy-MM-dd'.log'" /> <maxSizeRollBackups value="10" /> <maximumFileSize value="1MB" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" /> </layout> </appender> <appender name="Exceptionless" type="Exceptionless.Log4net.ExceptionlessAppender,Exceptionless.Log4net"> <apiKey value="d5qna1t9qTUVwrRzLFz1T3z8wSUeO3xNtxOGMHLf" /> <serverUrl value="http://10.117.130.150:9001" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%-4timestamp [%thread] %-5level %logger %ndc - %message%newline"/> </layout> </appender> <!-- Setup the root category, add the appenders and set the default level --> <root> <level value="ALL" /> <appender-ref ref="ConsoleAppender" /> <appender-ref ref="RollingLogFileAppender" /> <appender-ref ref="Exceptionless"/> </root> </log4net> </configuration>
还有一点非常重要,记得引用,否则在exceptionless中也看不到日志
效果如图:
最后,提一点:
现在容器技术很火,如果把这套代码放在容器中运行肯定是不行,因为在配置文件中配置了apikey和url, 所以要稍做修改如下:
添加一个类:
public class ExceptionlessOption { /// <summary> /// 日志应用标识,从Exceptionless面板获取 /// </summary> public string ApiKey { get; set; } /// <summary> /// 日志应用接口地址 /// </summary> public string Url { get; set; } }
services.AddMvc(); services.UseExceptionless(() => { return new ExceptionlessOption { ApiKey = "234na1t9qTU234234234z8wSUeO3x234234HLf", Url = "http://100.11.13.10:9001" }; });
public Log4NetLoggerFactory(ExceptionlessOption _option, string configFileName) { _loggerRepository = LogManager.CreateRepository( typeof(Log4NetLoggerFactory).GetAssembly(), typeof(log4net.Repository.Hierarchy.Hierarchy) ); //读取配置 XmlConfigurator.Configure(_loggerRepository, new FileInfo("log4net.config")); //TODO这里修改 ExceptionlessClient.Default.Configuration.ApiKey = _option.ApiKey ?? "d5qna1t9qTUVwrRzLFz1T3z8wSUeO3xNtxOGMHLf"; ExceptionlessClient.Default.Configuration.ServerUrl = _option.Url ?? "http://10.117.130.150:9001"; }
然后你就可以在startup中通过读取配置文件,实现参数的传入了。