Saturday, February 04, 2012
 
The best way to analyze your logs! Minimize
 Log4Net Mail archive   

The Log4Net mailing list is a great source of information about using log4Net, in this forum we collect all the messages in the log4net user list and some selected threads from the developer list.

Subject: Wrapping Log4Net
Prev Next
You are not authorized to post a reply.

Author Messages
xalex

07/18/2009 10:53 PM  


Hi forum,

I would like to use log4net in a large .net development. because i have the
requirement to prepare a potential replacement of the log4net framework e.g.
against ms-enterprise library or against a newer version of log4net, i would
like to wrap this. My way to do it is straight forward:
A single assembly references the log4net framework, offes the ILog and
LogManager classes, and all other projects reference only this wrapper (see
below).

This wrapper allows me to restrict the users on only the main functions
which are really needed and allows me to replace this framework,
potentially.

Now my question: When the ILog.Debug() Method is called, the output in the
logfile is wrong, because the LocationInfo used for this output corresponds
to the Wrapper and not to the code from which it is really called :-(

A: Is there an easy way to fix this problem?
B: Is there a better idea to wrap log4net
C: Is it true, that logging is only possible in DEBUG-Builds? When using the
release build, i dont get any output

Thanks
Alex



Wrapper:
------------
    public interface ILog
    {
        bool IsDebugEnabled { get; }
        bool IsErrorEnabled { get; }
        bool IsFatalEnabled { get; }
        bool IsInfoEnabled { get; }
        bool IsWarnEnabled { get; }

        void Debug(object message);
        void Error(object message);
        void Fatal(object message);
        void Info(object message);
        void Warn(object message);
    }

    public static class LogManager
    {
        static LogManager()
        {
            XmlConfigurator.Configure( new
System.IO.FileInfo("c:/logger.xml")) ;
        }

        public static ILog GetLogger(Type type)
        {
            MyLog log = new MyLog(log4net.LogManager.GetLogger(type) );
            return log;
        }
    }

    public class MyLog :ILog
    {
        private log4net.ILog _log;

        public MyLog(log4net.ILog log)
        {
            _log = log;
        }

        #region ILog Members

        public bool IsDebugEnabled
        {
            get { return _log.IsDebugEnabled; }
        }

       
        public void Debug(object message)
        {
            _log.Debug(message);
        }

   ...
}

--
View this message in context: http://www.nabble.com/Wrapping-Log4Net-tp24551728p24551728.html
Sent from the Log4net - Users mailing list archive at Nabble.com.

Peter Drier

07/18/2009 11:09 PM  
Because of "A", wrapping log4net is a bad idea.  It's a noble thought, but in 10+ years of using log4net and having people around and above me demand wrapping, I've never seen log4net replaced.   Given you can always write an appender to output to any other system as necessary, the "need" for wrapping is moot. 

B)  the only way to wrap it "better" would be to walk the stack everytime something is logged to see who the calling method was..  Horribly slow, so I wouldn't call it better.

C) Some part of your config is wrong.  We log in release mode all the time.  

-Peter

Matt Lund

07/18/2009 11:19 PM  

This reminds me of my attempt to wrap our use of NHibernate.  I later realized this was a bad idea for a practical reason and philosophical reason.  The practical challenge was that I had to choose between only exposing simple functionality in my wrapper to keep the coupling loose.  The philosophical epiphany we later had was that there is a difference between services and frameworks.  Loosely coupling your program to services is usually a good idea.  Loosely coupling your program to frameworks is usually a bad idea.

 

In short, I’d reconsider wrapping it.

 

Matt Lund

07/18/2009 11:22 PM  

Oops, I didn’t quite finish one sentence below.  Meant to say “The practical challenge was that I had to choose between only exposing simple functionality in my wrapper to keep the coupling loose or wrapping more of what NHibernate in order to take advantage of what it offers but at the loss of loose coupling.”

 

Daniel Marohn

07/18/2009 11:40 PM  

Hi Alex,

> private log4net.ILog _log;

I think this line is wrong. You use an Implementation of ILog for your
wrapper. This implementation will do the same checks, your
Implementation does and it sets the locationinfo onto your position
(from the point of the _log instance, your code is doing the logging).
Change the type to ILogger and log Messages like this:

private readonly static Type ThisDeclaringType = typeof(MyLog);
                virtual public void Debug(object message, Exception exception)
                {
                        Logger.Log(ThisDeclaringType, m_levelDebug, message, exception);
                }
                virtual public void DebugFormat(string format, params object[] args)
                {
                        if (IsDebugEnabled)
                        {
                                Logger.Log(ThisDeclaringType, m_levelDebug, new
SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
                        }
                }

I copied this code from LogImpl.cs and LoggerWrapperImpl.cs. It's a
damm good peace of code, and it's fun reading.

I hope this helps

Daniel

--
Daniel Marohn - marohn@sipgate.de
Telefon: +49 (0)211-63 55 55-0
Telefax: +49 (0)211-63 55 55-22
sipgate GmbH - Gladbacher Str. 74 - 40219 Düsseldorf
HRB Düsseldorf 39841 - Geschäftsführer: Thilo Salmon, Tim Mois
Steuernummer: 106/5724/7147, Umsatzsteuer-ID: DE219349391
www.sipgate.de - www.sipgate.at - www.sipgate.co.uk

xalex

07/20/2009 7:42 AM  


Hi Peter, thanks for your reply.

A - if wrapping is simply possible it offers flexibility: think of a large
system landscape with hundrets of visual studio projects. If there is a
change in the log4net version, you have to update all these references. If
it is wrapped, you can do this once (if the interface stays stable, and it
should do so!).

B - I think I have to have a deeper look on LoggerWrapperImpl, this should
help. The log4nt internal class LocationInfo walks the stack, so I dont want
do this again, an i hope I dont need it

C - I will tell you what was wrong, after fixing it :-)

-Alex

--
View this message in context: http://www.nabble.com/Wrapping-Log4Net-tp24551728p24564232.html
Sent from the Log4net - Users mailing list archive at Nabble.com.

Bruno Baia

07/20/2009 9:41 AM  
Hi,

Did you look at Common.Logging ?

http://netcommon.sourceforge.net/


- Bruno
James Green

07/20/2009 11:41 AM  

Hi,

Despite what you have been told already I have wrapped log4net, if you
do it right it is fine imho.

I wrapped log4net since we need to consume it in scenarios where we
absoloutely cannot have a plethora of config files kicking around all
the time simply to enable some logging in the consuming components.

For instance, what if you want to log from an SSIS pipeline?  You
absolutely do not want to generate dependencies on config files in such
situations.  This is also why I had a nightmare of a time getting
log4net configured using pure C# because it just doesn't seem to be done
all that often so isn't documented (I'm working on some docs for that
scenario to share).

The way I got around the log source problem was to use method signatures
like this on my wrapper class:

Log.Debug(Type sender, string message)

Then inside the wrapper it is a simple task to request the ILog from
log4net using the Type parameter, no need to walk the stack.

Works for us anyway ...

Cheers,

James.

xalex

07/21/2009 3:16 AM  


Hi Daniel,

first of all, thank you for this hint.
My goal is to use my simple ILog interface (as i sketched) from the
application code and to get the logger by calling ILog logger =
LogManager.GetLogger(typeof( typeof<current class>));
I would like to get this according to the current class because I want to
controll the logging intensity on namespace level. I also would like to
avoid the config-files / or use these only in case of problems for remote
debúgging.

I had a look at ILogger but I did not find out how to get this ILogger.
There is an interface to get a logger according to a string (name?) via
ILoggerRepository. But when I call LogManager.GetRepository and then
rep.GetCurrentLoggers() there are no loggers defined... The documentation
did not help me.

Thank you
Alex


xalex

07/21/2009 7:39 AM  


Hi James,

yes, this works of course, but there are 2 drawbacks: you need to request
the logger to the type for every single call. Instead of doing so, i would
like to instanciate as static variable per type only once like this:
private static readonly ILog logger = LogManager.GetLogger(typeof(Program));
The second (little) drawback is, that you have to provide the type for every
call.
I have a question: the call to the logger itself is done by your wrapper
code. How did you solve the problem, that the log4net.LocationInfo is wrong
in the logfile? I think also in your case the class, sourcefile and line
will be printed out of your wrapper and not the location of the application
code, right?

best regards
Alex

xalex

07/21/2009 7:42 AM  


thanks Bruno,
i think this is exactly what i need, i will have a closer look

Alex

Roy Chastain

07/21/2009 1:12 PM  

I think we have done what you want, plus a little bit.  I have an
Interface
public interface IKmsTraceLog: ILog
{
                void Alert (System.Exception ex);
                void Alert (string message);
                void Alert (string message, System.Exception ex);
                void Alert (object target);
                void Alert (object target, System.Exception ex);
                void Alert (object target, string message);
                void Alert (object target, string message,
System.Exception ex);
                void Alert (object target, string format, object
param1);
                void Alert (object target, string format, params
object[] paramList);
                bool IsAlertEnabled
                {
                        get;
                }
....  repeat above for each 'level' of trace wanted such as trace,
debug, warn etc)
}

It is implemented as
public class KmsTraceLogImpl: log4net.Core.LogImpl, IKmsTraceLog
{
                public void Alert (System.Exception ex)
                {
                        LogItWithCheck(Level.Alert, null, ex, null);
                }               /* method KmsTraceLogImpl Alert */

                public void Alert (string message)
                {
                        LogItWithCheck(Level.Alert, message, null,
null);
                }               /* method KmsTraceLogImpl Alert */

                public void Alert (string message, System.Exception ex)
                {
                        LogItWithCheck(Level.Alert, message, ex, null);
                }               /* method KmsTraceLogImpl Alert */

                public void Alert (object target)
                {
                        LogItWithCheck(Level.Alert, null, null, target);
                }               /* method KmsTraceLogImpl Alert */

                public void Alert (object target, System.Exception ex)
                {
                        LogItWithCheck(Level.Alert, null, ex, target);
                }               /* method KmsTraceLogImpl Alert */

                public void Alert (object target, string message)
                {
                        LogItWithCheck(Level.Alert, message, null,
target);
                }               /* method KmsTraceLogImpl Alert */

                public void Alert (object target, string message,
System.Exception ex)
                {
                        LogItWithCheck(Level.Alert, message, ex,
target);
                }               /* method KmsTraceLogImpl Alert */

                public void Alert (object target, string format, object
param1)
                {
                        if (IsAlertEnabled)
                                LogItNoCheck(Level.Alert,
string.Format(FormatterInstance, format, param1), null, target);
                }               /* method KmsTraceLogImpl Alert */

                public void Alert (object target, string format, params
object[] paramList)
                {
                        if (IsAlertEnabled)
                                LogItNoCheck(Level.Alert,
string.Format(FormatterInstance, format, paramList), null, target);
                }               /* method KmsTraceLogImpl Alert */

                public bool IsAlertEnabled
                {
                        get
                        {
                                return
Logger.IsEnabledFor(log4net.Core.Level.Alert);
                        }
                }               /* property KmsTraceLogImpl
IsAlertEnabled */}
... repeats for other levels

/// two helper methods used by each of the above routines.

                private void LogItNoCheck (Level logLevel, object
message, Exception ex, object target)
                {
                        LoggingEvent le;

                        le = new LoggingEvent(my_type,
this.Logger.Repository, this.Logger.Name, logLevel, message, ex);
                        if (target != null)
                                le.Properties["instance"] =
target.ToString();
                        Logger.Log(le);
                }               /* method KmsTraceLogUtil LogItNoCheck
*/

/// <summary>
/// Internal log method.  All externally visible trace/verbose should go
through here.
/// </summary>
/// <param name="level">The level of the message to be logged.</param>
/// <param name="message">The message object to log.</param>
/// <param name="t">the exception to log, including its stack trace.
Pass <c>null</c> to not log an exception.</param>
/// <remarks>
/// Generates a logging event for the specified <paramref name="level"/>
using
/// the <paramref name="message"/> and <paramref name="t"/>.
/// <para>Code is duplicated from LogItNoCheck for performance
reasons</para>
/// </remarks>
private void LogItWithCheck (Level level, object message, Exception ex,
object target)
{
      LoggingEvent                                      le;
       
        if (Logger.IsEnabledFor(level))
        {
                le = new LoggingEvent(my_type, this.Logger.Repository,
this.Logger.Name, level, message, ex);
                if (target != null)
                      le.Properties["instance"] = target.ToString();
                  Logger.Log(le);
        }
}
}

Then to use this each class that need unique trace info has a static
member such as

static private KMS.Core.Log4Net.KmsLogger               LogTrace =
KMS.Core.Log4Net.LoggerManager.GetLogger(System.Reflection.MethodBase.Ge
tCurrentMethod().DeclaringType);

Sometime I even define a virtual method called LogTrace with a get
accessor in a base class that has overriding classes.  Then each class
in the tree gets its own static instance of KmsLogger that is returned
by the property so that the correct class, not the base class shows in
the trace.

Then the calls to log4Net are done as

LogrTrace.Alert(this,"string and format info",p1,p2,p3...):
Where of course Alert could be Trace, Debug Warn etc.

This results in an output that looks like with the Full Qualified class
name and method name inserted into the trace.  In the example below, I
have the Day, Time Threadid class name and finally my text "- Entered".
Of couse the formatting is done via the config as normal.

27 14:11:57.916 [ServiceThread]
KMS.CertMinder.Service.CertMinderService::OnStart - Entered

One note, is that doing all this does not meet your 2nd requirement of
removing the references to log4Net from the calling projects.  They
still care.

----------------------------------------------------------------------
Roy Chastain

steven higgan

07/22/2009 10:38 AM  

awesome, can you by any chance post your ILoggerRepository
implementation (KMS.Core.Log4Net.KmsLogger) ?

Roy Chastain

07/22/2009 1:25 PM  

Sorry, I mixed code from two different versions in what I sent you.  Forget that you saw KmsTraceLogImpl, it is really KmsLogger, which is NOT a ILoggerRepository, but instead derives from log4net.Core.LogImpl.  I also removed the IKMSTraceLog interface and just defined the methods within KmsLogger.  I could find no advantage to having the interface.

So, in the code below where you see
public class KmsTraceLogImpl: log4net.Core.LogImpl, IKmsTraceLog
it should read
public class KmsLogger: log4net.Core.LogImpl

Since getting from what I have given you to the full implementation is just a lot of tedious code, I am posting the code on my SkyDrive account at
http://cid-08858f983336f8ec.skydrive.live.com/self.aspx/.Public/CoreLog4Net.zip
Note that there is code in there to handle Event Log logging, but it does not fully work.
Also take a look at the LTUniqueId class.  Classes can derive from it or implement ILTUniqueId to provide unique instance tracking in the log entries.

----------------------------------------------------------------------
Roy Chastain

xalex

07/23/2009 9:07 AM  


Hi,

first of all thanks to all the interesting input!

I want to clearify my requirements and intentions:
- the exchange of log4net is not really planned, it should be only possible
- references to the log4net assembly must be set to a single assembly which
is used in all of our projects
- the use of the logger should be extremely easy, the new interface should
expose only a subset of log4net methods
- we need to configure the levels of different appenders different depending
on the namespace

As shown before using my ILog and MyLog this is possible.
I had just one problem: the %method %line output was wrong, because the call
to the log4net logger was made by my wrapper. I have now implemented an
additional stack-grab which corrects this issue.

- Alex


xalex

07/23/2009 5:44 PM  


Hi,

i did not find it in the documentation:
Is it possible to configure appenders by coding without configfiles?
How do i get the appenders which are currently configured? I would like to
change the output destination file of a FileAppender.
And last question: is it possible to configure a FileAppender so that the
newest log entry is at top of the file and not at the bottom?

Thank you in advance!
Alex

Radovan Raszka

07/24/2009 7:57 AM  

Yes, it is possible to configure logging programatically. It was discussed here, see the archive:
http://mail-archives.apache.org/mod_mbox/logging-log4net-user/200805.mbox/%3c2AD7ECA75635F84A87792C0B2F8692487DDC03@EXCH3.ads.bruker.de%3e

How to get appenders (this example adds or removes file appender):
private static void enableDebugLog(bool enable)
{
        RollingFileAppender rfa = null;
        log4net.Repository.Hierarchy.Logger root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
        foreach (AppenderSkeleton appender in LogManager.GetRepository().GetAppenders())
        {
                rfa = appender as RollingFileAppender;
                if (rfa != null) //file appender found!
                        break;
        }
        if (enable)
        {
                if (rfa != null) return; // already exists, no need to add
                RollingFileAppender debugLog = new RollingFileAppender();
                debugLog.AppendToFile = true;
                debugLog.File = "Log/Service.log";
                debugLog.Layout = new log4net.Layout.PatternLayout("%date{dd-MM-yyyy HH:mm:ss,fff} %5level [%2thread] %message (%logger{1}:%line)%n");
                debugLog.RollingStyle = RollingFileAppender.RollingMode.Date;
                debugLog.Threshold = log4net.Core.Level.Debug;
                debugLog.ActivateOptions();
               
                root.AddAppender(debugLog);
        }
        else
        {
                if (rfa == null) return; // already doesn't exist, no need to delete
                root.RemoveAppender(rfa);
                rfa.Close();
        }
}

I think new log entries can not be added to the top of file.
Radovan Raszka

Michael Schall

07/24/2009 3:10 PM  

If configuring logging programatically is your need it is possible,
but you do loose the ability to change it on in a running process.  I
wouldn't give up the ability to modify loggers/appenders/log levels of
a running process without serious consideration.

You are not authorized to post a reply.
Forums > Log4Net > Log4Net Mail archive > Wrapping Log4Net



ActiveForums 3.7

 

 

 

 

 

 

 

 

Log4Net Dashboard

Log analysis and monitoring made easy!

Log4Net Dashboard is a log viewer that can read log statements from a variety of logging output targets.

You can download a free developer version.

  

Check it out!

On the demonstration site you can try it  with live data.demo.l4ndash.com - Try Log4Net Dashboard with live data

The mail archive is a copy of all the mail sent to the mail address: log4net-user@logging.apache.org, organized as a forum.

If you would like to participate in the mail list, send a mail to log4net-user-subscribe@logging.apache.org.

More information about the mailing list is available on: http://logging.apache.org/log4net/support.html

 

A complete topic list is available and can be viewed here (warning, it takes some time to load)

 

Copyright 2005-2008 by FaktNet AS Terms Of Use Privacy Statement