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

Check it out!
On the demonstration site you can try it with live data.
|