一、日志模块类图
1、ILogger接口及实现
2、ILoggerFactory接口及实现
3、其他
二、NullLogger类型
NullLogger类型是实现ILogger接口的空类型。它的 IsEnabled 方法总是返回false;Log 方法是个空实现。在代码中要用到日志的地方设计1个 NullLogger属性 作为占位符,在代码运行时将这个对象替换为真正的日志对象。如果不替换,则代码也能编译成功,运行时也不会出错。
View Code
1 publicclassNullLogger:ILogger 2 { 3 privatestaticreadonlyILogger _instance =newNullLogger(); 4 publicstaticILoggerInstance 5 { 6 get 7 { 8 return _instance; 9 } 10 } 11 #region ILogger 成员 12 13 public bool IsEnabled(LogLevel level) 14 { 15 returnfalse; 16 } 17 18 publicvoidLog(LogLevel level,Exception exception,string format,paramsobject[] args) 19 { 20 21 } 22 23 #endregion 24 }
三、和autofac的结合使用
在orchard中,autofac的使用无处不在。日志模块也不例外。前面说过,NullLogger 类型相当于1个日志类型的占位符,在运行时可以被替换为真实的日志类型。那在运行时如何替换呢?这就要和autofac结合在一起使用
1、LoggingModule类
LoggingModule类继承了Module类型。Module类型是autofac的类型。
View Code
1 publicclassLoggingModule:Module 2 { 3 privatereadonlyConcurrentDictionary<string,ILogger> _loggerCache; 4 5 publicLoggingModule() 6 { 7 _loggerCache =newConcurrentDictionary<string,ILogger>(); 8 } 9 10 protectedoverridevoidLoad(ContainerBuilder moduleBuilder) 11 { 12 // by default, use Orchard's logger that delegates to Castle's logger factory 13 moduleBuilder.RegisterType<CastleLoggerFactory>().As<ILoggerFactory>().InstancePerLifetimeScope(); 14 moduleBuilder.RegisterType<OrchardLog4netFactory>().As<Castle.Core.Logging.ILoggerFactory>().InstancePerLifetimeScope().WithParameter(newNamedParameter("isFullTrust",false)); 15 16 // call CreateLogger in response to the request for an ILogger implementation 17 moduleBuilder.Register(CreateLogger).As<ILogger>().InstancePerDependency(); 18 } 19 20 protectedoverridevoidAttachToComponentRegistration(IComponentRegistry componentRegistry,IComponentRegistration registration) 21 { 22 var implementationType = registration.Activator.LimitType; 23 24 // build an array of actions on this type to assign loggers to member properties 25 var injectors =BuildLoggerInjectors(implementationType).ToArray(); 26 27 // if there are no logger properties, there's no reason to hook the activated event 28 if(!injectors.Any()) 29 return; 30 31 // otherwise, whan an instance of this component is activated, inject the loggers on the instance 32 registration.Activated+=(s, e)=> 33 { 34 foreach(var injector in injectors) 35 injector(e.Context, e.Instance); 36 }; 37 } 38 39 privateIEnumerable<Action<IComponentContext,object>>BuildLoggerInjectors(Type componentType) 40 { 41 // Look for settable properties of type "ILogger" 42 var loggerProperties = componentType 43 .GetProperties(BindingFlags.SetProperty|BindingFlags.Public|BindingFlags.Instance) 44 .Select(p =>new 45 { 46 PropertyInfo= p, 47 p.PropertyType, 48 IndexParameters= p.GetIndexParameters(), 49 Accessors= p.GetAccessors(false) 50 }) 51 .Where(x => x.PropertyType==typeof(ILogger))// must be a logger 52 .Where(x => x.IndexParameters.Count()==0)// must not be an indexer 53 .Where(x => x.Accessors.Length!=1|| x.Accessors[0].ReturnType==typeof(void));//must have get/set, or only set 54 55 // Return an array of actions that resolve a logger and assign the property 56 foreach(var entry in loggerProperties) 57 { 58 var propertyInfo = entry.PropertyInfo; 59 yield return(ctx, instance)=> 60 { 61 string component = componentType.ToString(); 62 if(component != instance.GetType().ToString()) 63 { 64 return; 65 } 66 var logger = _loggerCache.GetOrAdd(component, key => ctx.Resolve<ILogger>(newTypedParameter(typeof(Type), componentType))); 67 propertyInfo.SetValue(instance, logger,null); 68 }; 69 } 70 } 71 72 privatestaticILoggerCreateLogger(IComponentContext context,IEnumerable<Parameter> parameters) 73 { 74 // return an ILogger in response to Resolve<ILogger>(componentTypeParameter) 75 var loggerFactory = context.Resolve<ILoggerFactory>(); 76 var containingType = parameters.TypedAs<Type>(); 77 return loggerFactory.CreateLogger(containingType); 78 } 79 }
1) Load方法在 builder.Build()时会被调用,这里注册了 3个类型
moduleBuilder.RegisterType<CastleLoggerFactory>().As<ILoggerFactory>().InstancePerLifetimeScope();
moduleBuilder.RegisterType<OrchardLog4netFactory>().As<Castle.Core.Logging.ILoggerFactory>().InstancePerLifetimeScope().WithParameter(new NamedParameter("isFullTrust", false));
moduleBuilder.Register(CreateLogger).As<ILogger>().InstancePerDependency();
2) AttachToComponentRegistration方法也会在 builder.Build()时会被调用,它会调用BuildLoggerInjectors 方法。而 BuildLoggerInjectors方法会检查 类型 中的属性,如果属性是 ILogger接口,则会被替换为 CastleLogger或 OrchardLog4netLogger类。当然你也能注册为自定义的其他日志类型。
四、log4net 设置
orchard使用log4net作为日志的具体实现。配置如下
View Code
1 <?xml version="1.0" encoding="utf-8"?> 2 <log4net> 3 <root> 4 5 <priority value="ERROR"/> 6 <appender-refref="error-file"/> 7 </root> 8 9 10 <logger name="NHibernate.Cache"> 11 <priority value="ERROR"/> 12 </logger> 13 14 <logger name="NHibernate.AdoNet.AbstractBatcher"> 15 <priority value="ERROR"/> 16 </logger> 17 18 <logger name="NHibernate.Util.ADOExceptionReporter"> 19 <priority value="ERROR"/> 20 </logger> 21 22 <appender name="error-file" type="Orchard.Logging.OrchardFileAppender"> 23 <file value="orchard-error"/> 24 <appendToFile value="true"/> 25 <immediateFlush value="false"/> 26 <staticLogFileName value="false"/> 27 <rollingStyle value="Date"/> 28 <datepattern value="-yyyy.MM.dd'.log'"/> 29 <lockingModel type="log4net.Appender.FileAppender+MinimalLock"/> 30 <layout type="log4net.Layout.PatternLayout"> 31 <conversionPattern value="%date [%thread] %logger - %P{Tenant} - %message%newline %P{Url}%newline"/> 32 </layout> 33 </appender> 34 </log4net>
五、orchard日志模块使用示例
1、要使用日志的类型
-
1 /// <summary> 2 /// 要使用日志的类型 3 /// </summary> 4 publicclassThing 5 { 6 publicThing() 7 { 8 Logger=NullLogger.Instance; 9 } 10 /// <summary> 11 /// 设置类型为 ILogger 的属性 12 /// </summary> 13 publicILoggerLogger 14 { 15 get; 16 set; 17 } 18 publicvoidTestMethod() 19 { 20 //这使用Log方法则 Logger属性 会被设置为 CastleLogger类;使用Error方法 Logger属性 会被设置为OrchardLog4netLogger类 21 Logger.Log(Orchard.Logging.LogLevel.Error,newException("测试异常"),"测试异常使用CastleLogger类型"); 22 Logger.Error(newException("测试异常"),"测试异常使用OrchardLog4netLogger类型"); 23 } 24 }
2、调用
-
1 publicvoidCreateTest() 2 { 3 var builder =newContainerBuilder();//构建autofac容器 4 builder.RegisterModule(newLoggingModule());//注册LoggingModule 5 builder.RegisterType<Thing>();//注册 Thing类型 6 var container = builder.Build();//Build 容器 7 var thing = container.Resolve<Thing>();//从容器中解析 Thing类型 8 thing.TestMethod();//调用TestMethod方法,则会调用日志 9 }