-
Notifications
You must be signed in to change notification settings - Fork 0
Log4Net
Kota edited this page Sep 21, 2018
·
24 revisions
Nuget: install-package log4net
Link: https://logging.apache.org/log4net/
For Java: log4j
Common Logging Framework: https://github.com/net-commons/common-logging
// Configure
log4net.Config.BasicConfigurator.Configure();
// Get a Logger
var log = log4net.LogManager.GetLogger(typeof(Program));
// Log : Default is Console Log
log.Info("Hello from log4net!");
- Logging Event: can be stored anywhere (more than one storage)
- Storage: Application Event Log (Appender) , SQLServer Database (Appender), Local Text File (Appender)
- Log Levels: DEBUG, INFO, WARN, ERROR, FATAL
- By default it looks for app.config / web.config for XML Path
<configuration>
<configSections>
<section name="log4net" type="log4net.config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="Console" type="log4net.Appender.ConsoleAppender" >
<layout type="log4net.Layout.SimpleLayout" />
</appender>
<appender name="File" type="log4net.Appender.FileAppender" >
<file value="GuiApplicationLog.txt" />
<layout type="log4net.Layout.SimpleLayout" />
</appender>
<root>
<level value="All" />
<appender-ref ref="Console" />
</root>
</log4net>
</configuration>
var layout = new SimpleLayout();
layout.ActivateOptions();
var appender = new ConsoleAppender();
appender.Layout = layout;
appender.ActivateOptions();
var hierarchy = (Hierarchy)LogManager.GetRepository();
Logger root = hierarchy.Root;
root.Level = log4net.Core.Level.All;
BasicConfiguration.Configure(appender);
// Usage
ILog log = LogManager.GetLogger(typeof(Program));
log.Info("Info!");
- (No error and No log so ensure below points)
- Forgetting to Initialize
log4net.Config.<basicConfigurator>.Configure(); - Ensure Logging Level is not OFF
<level value="All" /> - Look for Configuration errors in debugger output window of Visual Studio
- Make sure Layout is set for Appenders
<layout type="log4net.Layout.SimpleLayout" /> - Make sure Appenders is set for Loggers
<appender-ref ref="Console" />
- Log Level Order: Debug > Info > Warn > Error > Fatal
- At root Levl
<root> <level value="All" /> <appender-ref ref="Console" /> </root>- Logger Object Properties
<logger name="Sample.Program" > <level value="INFO" /> <appender-ref ref="File" /> // prints twice on Console. AS the root one also gets copied <appender-ref ref="Console" /> </logger>
- Decides where to store the log message. File, Console, DB, Events etc...
- Two appenders of same type can also be used by multiple refs (also can be done using Forwarding Appender)
- Common Properties: Level, Layout, Filter
- Console Appender
- Colored Console Appender
<appender name="Console" type="log4net.Appender.ColoredConsoleAppender" >
<target value="Console.Out" />
<mapping>
<level value="DEBUG" />
<foreColor value="Green" />
</mapping>
<mapping>
<level value="ERROR" />
<foreColor value="Red, HighIntensity" />
</mapping>
<mapping>
<level value="FATAL" />
<foreColor value="Red" />
<backColor value="White" />
</mapping>
<layout type="log4net.Layout.SimpleLayout" />
</appender>
- Debug Appender (works on release binaries too :( )
- Sends output to [System.Diagnostics.Debug.Write]
- Trace Appender
- Sends output to [System.Diagnostics.Trace.Write]
- EventLog Appender (requires Administrator Access)
- To show in Event Logs (eventvwr)
- Properties: Log Name (default-Application), Application Name (Source), Category, EventId, Mapping
- File Appender (Not to use - as the logs will increase)
- Properties: Path, Encoding, AppendToFile (false-rewrites after app restart), LocakingModel (Exclusive(default), Minimal, InterProcess)
- Rolling File Appender
- Same as File Appender but creates new Log file once it reaches certain size / after certain time. Also no of files can be configured (deletes the old log files if count increases)
- +Properties: RollingStyle (size, date), MaxFileSize, MaxFileSizeRollBackups, CountDirection, PreserveLogFileExtension
- ADO.NET Appender (Buffering)
- ConnectionString, ConnectionStringName, AppSettingsKey (connectionstringtouse), ConnectionType, ReconnectOnError,
- CommandType, CommandText, UseTransactions, Parameters (Name, DBType, Size)
- ASP.NET Trace Appender
- P: Category. DEBUG, INFO => System.Web.Trace.Write | WARN, ERROR, FATAL => System.Web.Trace.Warn
- Remoting Appender (Buffering)
- P: Sink (Sink URL, IRemoteLoggingSink)
- Telnet Appender
- Port (TCP/IP - default 23)
- UDP Appender
- Encoding, LocalPort, RemoteAddress, RemotePort
- SMTP and SMTPPickupDir Appender (Buffering) (Emails)
- SmtpHost, Port, Authentication, Username, Password, EnableSSL, Other(From, To, CC/BCC, Priority, ReplyTo, Subject)
- SMTPPickupDir Appender: Stores Email formatted Logs for later (Buffering)
- PickupDir, From, To, Subject
- Forwarding Appender (Forwards message to other Appender)
- Appender-ref
- Can be set with a different Threshold than To Appender
- Buffering Appender (Buffering)
- Appender-ref
- Collects message till buffer is reached and forwards to other Appender
- Appenders : Helps in Storage
- Layouts: Helps in formatting the content. Contains Header, Footer
- Simple Layout
- Console doesnt shows header & footer, File appender shows
<appender>
<layout type="log4net.Layout.SimpleLayout">
<header value="==== BEGIN LOG ===== " >
<footer value="==== END LOG ===== " >
</layout>
</appender>
- XML Layout (Prefix, Encode etc..)
<layout type="log4net.Layout.XmlLayout">
- Pattern Layout
- Conversion Pattern (Format Specifiers => http://bit.ly/12YuBgE)
Example Setup// %<> is called as format specifier LOG:[%logger]%-5level-%message-%newline LOG:[MyApp.Program]DEBUG-Program Starting<layout type="log4net.Layout.PatternLayout" > <conversionPattern value="%timestamp [%thread] %level %logger - %message%newline%exception" /> </layout>- Format Modifier => %number
%5level means the no of characters are limited to 5 character%-5level means the minimum no of characters is 5 character%.5level means the no of characters is limited to 5 character and excess is truncated from beginning
- Raw Layout
- RawTimeStampLayout , RawUtcTimeStampLayout
- Custom Logging properties. Ex: CPU Utilization, No of threads etc..
log4net.GlobalContext.Properties["WindowTitle"] = Process.GetCurrentProcess().MainWindowTitle;
logger.Info("Info log");
- Accesing this property in Layout. Ex: Pattern Layout
%property{WindowTitle} - Property Contexts
- Global Context
- Thread Context
- Logical Thread Context (http://bit.ly/13Dwu66)
- Context Property Stacks
ThreadContext.Properties["name"] = "A"
ThreadContext.Properties["name"] = "B" // overrides
=> %property{name} gets "B"
ThreadContext.Stacks["name"].Push("A")
ThreadContext.Stacks["name"].Push("B")
=> %property{name} gets both "A" and "B"
- Properties can be of any type: string, new Object();. Conversion calls ToString() on that object for logging
- Filters log message before it reaches the appender
- Filters can be chained (in order of setup)
- Types
- Level Match Filter. LevelToMatch, AcceptOnMatch
<appender>
<filter type="log4net.Filter.LevelMatchFilter" >
<levelToMatch value="ERROR" />
<acceptOnMatch value="true" />
</filter>
<filter type="log4net.Filter.DenyAllFilter" />
</appender>
- Level Range Filter. LevelMin, LevelMax, AcceptOnMatch
- Logger Match Filter. LoggerToMatch, AcceptOnMatch
String.StartsWith() - String Match Filter. RegexToMatch, StringToMatch, AcceptOnMatch
- RegexToMatch takes 1st priority if both regex & string is used
- Property Match Filter. Key, RegexToMatch, StringToMatch, AcceptOnMatch
- Can be Property on Global/Thread/LogicalThread Contexts
- Deny All Filter
<filter type="log4net.Filter.DenyAllFilter" /> - Filters can forward to another Filter OR Reject the logs. ( Accept / Deny / Neutral )
- LevelRangeFilter denies message if it doesn't match based on config (acceptOnMatch)
- Logging Code is Still Code
- Logging uses Resources
- Lossy Logging | Lossy Appender are Buffering Appender
- Keeps the logs in the buffer. New logs deletes the old logs and stores in the buffer. Once there is a FATAL (can be configured - LogLevel/Exception/Time) occurs, the logs are processed and stored
- Properties: Lossy, BufferSize, LevelEvaluator, ExceptionEvaluator, TimeEvaluator
<evaluator type="log4net.Core.LevelEvaluator" > <threshold value="ERROR" /> </evaluator>
- Lossy Logging | Lossy Appender are Buffering Appender
- Log Now
- Tips
- Surround values with [] so that if it's null it will show the square braces
- Logging is not Exception Handling
- Logging Unhandled Exceptions
AppDomain.CurrentDomain.UnhandledException += LogUnhandledException; void LogUnhandledException(object sender, UnhandledExceptionEventArgs e) { Log.Error( String.Format("A {0}fatal Unhandled exception occured", e.IsTerminating ? string.Empty : "non-" ), e.ExceptionObject as Exception ); }
- Generics
class LogManager
{
static readonly ILogManager _logManager;
static LogManager()
{
log4net.Config.XmlConfigurator.Configure(new FileInfo("log4net.config"));
_logManager = new LogManager();
}
public static ILogger GetLogger<T>()
{
return _logManager.GetLogger(typeof(T));
}
}
// usage
var logger = LogManager.GetLogger<Program>();
- Extension methods :: Log what is required
// Existing :: In client code
if(Log.IsDebugEnabled)
{
Log.DebugFormat("Value is {0}", value);
}
// NEW
static class LoggerExtensions
{
public static void Debug(this ILogger log, Func<string> getMessage)
{
if(! log.IsDebugEnabled)
{
return;
}
var logMessage = getMessage();
log.Debug(logMessage);
}
}
// usage
private static Ilogger Log = LogManager.GetLogger<Program>();
Log.Debug(() => String.Format("Value is {0}", value));
- Extension methods :: Logger On-demand
static class GenericLoggingExtensions
{
public static ILogger Log<T>(this T thing)
{
var log = LogManager.GetLogger<T>();
return log;
}
}
// usage
this.Log().Debug("Debugging!");
- Disposable Activity Pattern
static class LogActivityExtensions
{
static IDisposable Activity(this ILogger log, string format, params object[] args)
{
return new LoggingActivity(log, String.Format(format, args));
}
}
class LoggingActivity : IDisposable
{
private readonly ILogger _log, string _activityName, IDisposable _scope;
public LoggingActivity(ILogger log, string activityName)
{
_log = log;
_activityName = activityName;
_scope = _log.PushActivity(activityName);
log.DebugFormat(">> Entering activity [{0}]", activityName);
}
public void Dispose()
{
_log.DebugFormat(">> Leavingactivity [{0}]", _activityName);
_scope.Dispose();
}
}
//LoggerAdapter .cs
public bool IsDebugEnabled { get { return _log.IsDebugEnabled; } }
public IDisposable PushActivity(string activityName)
{
return ThreadContext.Stacks["activity"].Push(activityName);
}
// usage
this.Log().Debug("Preparing to Run.");
using (this.Log().Activity("Run))
{
this.Log().Debug("We are running now.");
}
// Patter Layout
<conversionPattern value="%level [Current Activity: %property{activity}] - %message%newline" />
// output
DEBUG [Current Activity: <null>] - Preparing to Run.
DEBUG [Current Activity: Run] - >> Entering activity [Run]
DEBUG [Current Activity: Run] - We are running now.
DEBUG [Current Activity: Run] - << Leaving activity [Run]
- Object Patterns (Decorator)
- Decorating an actual object to Log info and then proceed with its normal behavior
- PostSharp
- using log4net, PostSharp.Aspects, PostSharp.Extensibility
- [MulticastAttributeUsage(MulticastTragets.Method ......)], [AttributeUsage(AttributeTargets.Assembly)]
- LogMethodAspect : OnMethodBoundaryAspect
- Implement: OnEntry() { .... logging ... }, OnExit(), OnException()
- Use it [assembly: LogMethodAspect] namespace { ... // logs every method call (entry,exit,exception) ... }
- Custom Layout : inherit LayoutSkeleton
class SerializableLogEvent
{
......
}
class JsonLayout : LayoutSkeleton
{
private JavaScriptSerializer _serializer;
public override void ActivateOptions()
{
_serializer = new JavaScriptSerializer();
}
public override void Format(TextWriter writer, LoggingEvent loggingEvent)
{
var logEvent = new SerializableLogEvent(loggingEvent);
var json = _serializer.Serialize(logEvent);
writer.WriteLine(json);
}
}
- Custom Filter : inherit FilterSkeleton (Accept / Deny / Neutral)
class QueryStringfilter : FilterSkeleton
{
public override FilerDecision Decide(LoggingEvent loggingEvent)
{
if (null == HttpContext.Current)
{
return FilterDecision.Neutral;
}
if (!HttpContext.Current.Request.QueryString.Get("log"))
{
return FilterDecision.Neutral;
}
var logQueryStringValue = HttpContext.Current.Request.QueryString.Get("log");
var values = new[] {"1", "yes", "true"};
if (values.Contains(logQueryStringValue.ToLowerInvariant()))
{
return FilterDecision.Accept;
}
return FilterDecision.Deny;
}
}
- Custom Appender : AppenderSkeleton, BufferingAppenderSkeleton (this.BufferSize, Append => SendBuffer())
using Twilio;
public class TwilioAppender : AppenderSkeleton
{
TwilioRestclient _twilio;
public string AccountSid { get; set; }
public string AuthToken { get; set; }
public string From { get; set; }
public string To { get; set; }
public override void ActivatOptions()
{
_twilio = new TwilioRestclient(AccountSid, AuthToken);
base.ActivatOptions();
}
protected override void Append(LoggingEvent loggingEvent)
{
var message = this.RenderLoggingEvent(loggingEvent);
_twilio.SendSmsMessage(From, To, message);
}
}
// In config
<appender name="Twilio" type="namespace.TwilioAppender, namespace" >
<accountSid value="AccountSid" />
<authToken value="authToken" />
<from value="143" />
<to value="864" />
<filter />
<layout />
</appender>
- Custom Object Renderer (like Layouts but focuses on specific type of objects) : IObjectRenderer
public class RequestRenderer : IObjectRenderer
{
public void RenderObject(RendererMap rendererMap, object obj, TextWriter writer)
{
HttpRequestBase requestBase = obj as HttpRequestBase;
if (null == requestBase)
{
return;
}
var renderedRequest = FormatRequest(requestBase);
writer.WriteLine(renderedRequest);
}
private static string FormatRequest()
{
var renderedRequest = String.Format(
"([{0}] [{1}] from browser [{2}])",
request.HttpMethod,
request.RawUrl,
request.Browser.Browser
);
return renderedRequest;
}
}
// config
<log4net>
<appender>
<layout />
</appender>
<renderer
renderingClass="nampespace.RequestRenderer, namespace"
renderedClass="System.Web.HttprequestBase" />
</log4net>
- Custom Plugins : PluginSkeleton
public class AutoregisterRenderersPlugin : PluginSkeleton
{
public AutoregisterRenderersPlugin() : base("AutoregisterRenderers")
{
}
public override void Attach(log4net.Repository.ILoggerRepository repository)
{
var renderMap = log4net.LogManager.GetRepository().RendererMap;
var renderers = from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
from attr in type.GetCustomAttributes(typeof(RendersAttribute), true)
let renderAttr = attr as RendersAttribute
let renderer = Activator.CreateInstance(type) as IObjectrenderer
select new {RenderType = renderAttr.rendersType, Renderer= renderer };
renderers.ToList().ForEach( r = renderMap.Put(r,RenderType, r.Renderer) );
base.Attach(repository);
}
public override void Shutdown()
{
bsae.Shutdown();
}
}
class RendersAttribute : Attribute
{
...
}
[Renders(typeof(HttpRequestBase))]
class RequestRenderer : IObjectRenderer
{
...
}
// usage :: Register as below in startup
[assembly: log4net.Config.Plugin(typeof(namespace.AutoregisterRenderersPlugin))]
namespace
{
class Application
{
protected void Application_Start(){}
}
}
// config - no need to add renderers