logging-log4net-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Anthony Francisco" <a...@recombulator.net>
Subject RE: How to configure log4net when using shared assemblies?
Date Tue, 17 Sep 2013 13:04:35 GMT
SharePoint when you come down to it is simply an ASP.NET application.

 

There are techniques to get loaded up into the ASP.NET application by
setting up a global.asax or an IHttpModule, but this kind of change is a
farm wide change  wide change. Much like I don't have the privileges to
change the legacy component or its configuration, I have even less
privileges to make a farm wide change.

 

But let's say for a moment that I did have the rights to install a
global.asax or IHttpModule, how does that help me? When the legacy component
loads up its configuration, won't it just go and override the configuration
I setup in my global.asax or IHttpModule? Or is there a way to lock down a
hierarchy once it's been configured once?

 

Another way to think of my general problem is that you are writing two
addins for Word (ABC.Xena.dll and ABC.Gabrielle.dll) which can be either
bought by customers together or separately. Your two addins depend on a
common utility assembly (ABC.Joxer.dll) . All your assemblies use log4net.
Additionally, the customer also has another addin they bought from a 3rd
party (XYZ.Calisto.dll) that also uses log4net, but the 3rd party had
configured theirs to use the root level logger. So obviously you don't want
your log output going into the 3rd party's log file, nor do you want the 3rd
party's log output to go into your logfile. Additionally, you'll want the
log output for the two addins to be complete so that if there is an issue,
the customer only needs to email just one log file (eg. ABC.Xena.log
contains output from ABC.Xena and ABC.Joxer, and ABC.Gabrielle.log contains
output from ABC.Gabrielle and ABC.Joxer.)

 

-          Ants

 

From: Dominik Psenner [mailto:dpsenner@gmail.com] 
Sent: Tuesday, 17 September, 2013 02:52
To: 'Log4NET User'
Subject: AW: How to configure log4net when using shared assemblies?

 

There is always a main entry point. That entry point (assembly) knows which
assemblies it may will load during runtime and that entry point is usually
the place where logging is configured.

 

In case of a console application that place would be the first line in
Main(string[] argv), in case of a WPF application it would be App.xaml.cs or
the MainWindow constructor etc.

 

I'm not familiar enough with SharePoint to know what that entry point will
be.

 

Von: Anthony Francisco [mailto:ants@recombulator.net] 
Gesendet: Dienstag, 17. September 2013 08:31
An: 'Log4NET User'
Betreff: RE: How to configure log4net when using shared assemblies?

 

There are three reasons:

1. I cannot touch the configuration of the legacy component.

2. I can't predict ahead of time which assembly will be the entry assembly.

3. The hosting application is actually SharePoint, I can't touch that code
ited.

 

- Anthony

 

From: Dominik Psenner [mailto:dpsenner@gmail.com] 
Sent: Monday, 16 September, 2013 18:07
To: Log4NET User
Subject: Re: How to configure log4net when using shared assemblies?

 

Why don't you configure logging only in the entry assembly?

 

2013/9/16 Anthony Francisco <ants@recombulator.net>

(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.

 





 

-- 

Dominik Psenner


Mime
View raw message