// **************************************************************** // Copyright 2008, Charlie Poole // This is free software licensed under the NUnit license. You may // obtain a copy of the license at http://nunit.org/?p=license&r=2.4 // **************************************************************** using System; using System.IO; using System.Reflection; using BF = System.Reflection.BindingFlags; namespace NUnit.Core { /// /// Proxy class for operations on a real log4net appender, /// allowing NUnit to work with multiple versions of log4net /// and to fail gracefully if no log4net assembly is present. /// public class Log4NetCapture : LogCapture { private Assembly log4netAssembly; private Type appenderType; private Type basicConfiguratorType; private object appender; private bool isInitialized; // Layout codes that work for versions from // log4net 1.2.0.30714 to 1.2.10: // // %a = domain friendly name // %c = logger name (%c{1} = last component ) // %d = date and time // %d{ABSOLUTE} = time only // %l = source location of the error // %m = message // %n = newline // %p = level // %r = elapsed milliseconds since program start // %t = thread // %x = nested diagnostic content (NDC) private static readonly string logFormat = "%d{ABSOLUTE} %-5p [%4t] %c{1} [%x]- %m%n"; protected override void StartCapture() { if ( IsInitialized ) { string threshold = DefaultThreshold; if ( !SetLoggingThreshold( threshold ) ) SetLoggingThreshold( "Error" ); SetAppenderTextWriter( this.Writer ); ConfigureAppender(); } } protected override void StopCapture() { if ( appender != null ) { SetLoggingThreshold( "Off" ); SetAppenderTextWriter( null ); } } #region Helpers private bool IsInitialized { get { if ( isInitialized ) return true; try { log4netAssembly = Assembly.Load( "log4net" ); if ( log4netAssembly == null ) return false; appenderType = log4netAssembly.GetType( "log4net.Appender.TextWriterAppender", false, false ); if ( appenderType == null ) return false; basicConfiguratorType = log4netAssembly.GetType( "log4net.Config.BasicConfigurator", false, false ); if ( basicConfiguratorType == null ) return false; appender = TryCreateAppender(); if ( appender == null ) return false; SetAppenderLogFormat( logFormat ); isInitialized = true; } catch { } return isInitialized; } } private Assembly TryLoadLog4NetAssembly() { Assembly assembly = null; try { assembly = Assembly.Load( "log4net" ); } catch { return null; } return assembly; } /// /// Attempt to create a TextWriterAppender using reflection, /// failing silently if it is not possible. /// private object TryCreateAppender() { ConstructorInfo ctor = appenderType.GetConstructor( Type.EmptyTypes ); object appender = ctor.Invoke( new object[0] ); return appender; } private void SetAppenderLogFormat( string logFormat ) { Type patternLayoutType = log4netAssembly.GetType( "log4net.Layout.PatternLayout", false, false ); if ( patternLayoutType == null ) return; ConstructorInfo ctor = patternLayoutType.GetConstructor( new Type[] { typeof(string) } ); if ( ctor != null ) { object patternLayout = ctor.Invoke( new object[] { logFormat } ); if ( patternLayout != null ) { PropertyInfo prop = appenderType.GetProperty( "Layout", BF.Public | BF.Instance | BF.SetProperty ); if ( prop != null ) prop.SetValue( appender, patternLayout, null ); } } } private bool SetLoggingThreshold( string threshold ) { PropertyInfo prop = appenderType.GetProperty( "Threshold", BF.Public | BF.Instance | BF.SetProperty ); if ( prop == null ) return false; Type levelType = prop.PropertyType; FieldInfo levelField = levelType.GetField( threshold, BF.Public | BF.Static | BF.IgnoreCase ); if ( levelField == null ) return false; object level = levelField.GetValue( null ); prop.SetValue( appender, level, null ); return true; } private void SetAppenderTextWriter( TextWriter writer ) { PropertyInfo prop = appenderType.GetProperty( "Writer", BF.Instance | BF.Public | BF.SetProperty ); if ( prop != null ) prop.SetValue( appender, writer, null ); } private void ConfigureAppender() { MethodInfo configureMethod = basicConfiguratorType.GetMethod( "Configure", new Type[] { appenderType } ); if ( configureMethod != null ) configureMethod.Invoke( null, new object[] { appender } ); } #endregion } }