logging-log4net-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Anthony Francisco" <a...@recombulator.net>
Subject How to configure log4net when using shared assemblies?
Date Mon, 16 Sep 2013 16:21:36 GMT
(I've also posted this question to StackOverflow
<http://stackoverflow.com/questions/18814385/how-to-configure-log4net-when-u
sing-shared-assemblies> .)

 

How do I configure log4net when I'm using shared assemblies?

I have multiple components which depend on common component. Each of the
components lives in its own assembly. All the components use log4net for
logging. All the components are loaded into a single process space, but the
order of usage of the components varies. All the outfacing components load
their respective log4net configurations on first use to try to send logging
data to their. The common component does not load any configuration.
Additionally, there is a legacy component which does not use the common
component. It also outfacing and loads its configuration on first use. I
cannot touch the code or configuration of this legacy component directly.

The problem I'm facing is that due to the components loading their
configuration on first use, the last person to load the configuration wins.
This results in logging happening, but all the log output ends up going into
that last configuration that loaded. Obviously, it's just ugly to look in
your log file and have some other component's log entries written into it.

I've hit upon a partial solution by using the RepositoryAttribute and
AliasRepositoryAttribute. The lets the outfacing components load their
configuration into their "logger repository", and effectively isolate
themselves from each other. The legacy component now happily writes to its
own logs without an noise from my components, and my components are happily
writing to their own logs without generating noise in the other logs.

I said partial solution because there still exists the situation for logging
for the common component. When using the AliasRepositoryAttribute, the first
component that is loaded and aliases the common component gets all the log
output even if it was another component that was calling into the common
component. This is terrible since I'll be missing important logging
information in the later components, and I'll have non relevant logging
information in the first log.

The following code demonstrates the issue:

Common: (stand in for the common component)

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using log4net;

using log4net.Config;

[assembly: Repository("CommonLib")]

 

namespace CommonLib

{

    public class CommonClass

    {

        static readonly ILog Log =
LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().Declari
ngType);

 

        public void DoCommon(string from)

        {

            Log.Debug("DoCommon:" + from);

        }

    }

}

Library A: (stand in for one of the outfacing components)

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Xml;

using CommonLib;

using log4net;

using log4net.Config;

[assembly: Repository("ALib")]

[assembly: AliasRepository("CommonLib")]

 

namespace ALib

{

    public static class LogPrep

    {

        static bool _loaded = false;

 

        public static void Ensure()

        {

            if (_loaded)

                return;

 

            var doc = new XmlDocument();

            doc.LoadXml(

@"<log4net
xsi:noNamespaceSchemaLocation='http://csharptest.net/downloads/schema/log4ne
t.xsd'

         xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'

         debug='true'>

  <appender name='ConsoleAppender'
type='log4net.Appender.ColoredConsoleAppender'>

    <mapping>

        <level value='DEBUG' />

        <foreColor value='White' />

        <backColor value='Green' />

    </mapping>

    <layout type='log4net.Layout.PatternLayout'>

      <conversionPattern value='ALib: %d{yyyy MMM dd HH:mm:ss} [%p] %c{1}
%mdc - %m%n' />

    </layout>

  </appender>

  <root>

    <level value='DEBUG' />

    <appender-ref ref='ConsoleAppender' />

  </root>

</log4net>");

            XmlConfigurator.Configure(doc.DocumentElement);

 

            _loaded = true;

        }

    }

 

    public class AClass

    {

        static readonly ILog Log =
LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().Declari
ngType);

 

        public void DoA()

       {

            LogPrep.Ensure();

            Log.Debug("DoA");

 

            var common = new CommonClass();

            common.DoCommon("A");

        }

    }

}

Library B: (stand in for another outfacing component)

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Xml;

using CommonLib;

using log4net;

using log4net.Config;

[assembly: Repository("BLib")]

[assembly: AliasRepository("CommonLib")]

 

namespace BLib

{

    public class BClass

    {

        static readonly ILog Log =
LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().Declari
ngType);

 

        public static class LogPrep

        {

            static bool _loaded = false;

 

            public static void Ensure()

            {

               if (_loaded)

                    return;

 

                var doc = new XmlDocument();

                doc.LoadXml(

@"<log4net
xsi:noNamespaceSchemaLocation='http://csharptest.net/downloads/schema/log4ne
t.xsd'

         xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'

         debug='true'>

  <appender name='ConsoleAppender'
type='log4net.Appender.ColoredConsoleAppender'>

    <mapping>

        <level value='DEBUG' />

        <foreColor value='White' />

        <backColor value='Red' />

    </mapping>

    <layout type='log4net.Layout.PatternLayout'>

      <conversionPattern value='BLib: %d{yyyy MMM dd HH:mm:ss} [%p] %c{1}
%mdc - %m%n' />

    </layout>

  </appender>

  <root>

    <level value='DEBUG' />

    <appender-ref ref='ConsoleAppender' />

  </root>

</log4net>");

                XmlConfigurator.Configure(doc.DocumentElement);

 

                _loaded = true;

            }

        }

 

        public void DoB()

        {

            LogPrep.Ensure();

            Log.Debug("DoB");

 

            var common = new CommonClass();

            common.DoCommon("B");

        }

    }

}

Executable container: (stand in for the legacy component)

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Xml;

using System.Xml.Linq;

using ALib;

using BLib;

using log4net;

using log4net.Config;

 

namespace TestLog4NetRepositories

{

    class Program

    {

        private static readonly ILog Log =
LogManager.GetLogger(typeof(Program));

 

       static void Main(string[] args)

        {

 

            // Set up a simple configuration that logs on the console.

 

            var doc = new XmlDocument();

            doc.LoadXml(

@"<log4net
xsi:noNamespaceSchemaLocation='http://csharptest.net/downloads/schema/log4ne
t.xsd'

         xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'

         debug='true'>

  <appender name='ConsoleAppender'
type='log4net.Appender.ColoredConsoleAppender'>

    <mapping>

        <level value='DEBUG' />

        <foreColor value='White' />

        <backColor value='Blue' />

    </mapping>

    <layout type='log4net.Layout.PatternLayout'>

      <conversionPattern value='Main: %d{yyyy MMM dd HH:mm:ss} [%p] %c{1}
%mdc - %m%n' />

    </layout>

  </appender>

  <root>

    <level value='DEBUG' />

    <appender-ref ref='ConsoleAppender' />

  </root>

</log4net>");

            XmlConfigurator.Configure(doc.DocumentElement);

 

            Log.Info("Entering application.");

 

            var a = new AClass();

            a.DoA();

 

           var b = new BClass();

            b.DoB();

 

            Log.Info("Exiting application.");

        }

    }

}

If you run this code, notice that Common's output is printed out in Green,
and never printed out in Red.

log4net: Creating repository [BLib] using type
[log4net.Repository.Hierarchy.Hierarchy]

log4net:ERROR Failed to alias repository [CommonLib]
System.InvalidOperationException: Repository [CommonLib] is already aliased
to repository [ALib]. Aliases cannot be redefined.

    at log4net.Core.DefaultRepositorySelector.AliasRepository(String
repositoryAlias, ILoggerRepository repositoryTarget)

    at log4net.Core.DefaultRepositorySelector.LoadAliases(Assembly assembly,
ILoggerRepository repository)

Although the executable container above shows A before B, in the real
situation, B can be before A.

As I mentioned before, I have more complex hierarchy, but the above
demonstrates the minimal problem I am facing.

 


Mime
View raw message