While lurking and skulking in the shadows of various technical .Net sites, I've noticed many developers discussing log4net in their blogs and posts; log4net is an extremely popular tool for logging .Net Applications. So, I decided to try it out. After the initial complexities of getting it up and running, I was suitably impressed. Could it be…logging is fun? Well, I don't know if I'd go that far…at least I'd never admit it.
One of the great features of log4net is how easy it is to route the logs to multiple outputs. Here is an incomplete list of outputs…or 'Appenders' in the log4net vernacular:
AdoNetAppender | Logs to databases |
ConsoleAppender | Logs to a console window |
EventLogAppender | Logs to Windows event log |
FileAppender | Logs to a file |
RollingFileAppender | Logs to multiple files |
SmtpAppender | Logs to an email address |
TraceAppender | Logs to the .Net trace system |
AspNetTraceAppender | Logs to the Asp.Net page trace |
MemoryAppender | Logs to a memory buffer |
Etc., etc., etc. | |
Wow.
I've been doing a lot of work with jQuery/JavaScript and it dawned on me that seeing server side logging strings in a JavaScript Console could be useful.
So I wrote a log4net JavaScript Console Appender. Strings logged at the server will show up in the browser's console window. Note: For IE, you need to have the "Developer Tools" window active.
I'm not going to describe how to setup log4net in an Asp.Net web site; there are many step-by-step tutorials around. But I'll give you some hints:
- Each step must be followed or it will not work (duh).
- Putting the log4net settings in a separate configuration file is more complicated than having the settings in web.config: Start with the settings in web.config.
- The name in this line of code: LogManager.GetLogger("MyLogger");
...refers to this section in the configuration file: <logger name="MyLogger">
I built the Appender and a test Asp.Net site in .Net Framework 4.0. Here's the jsConsoleAppender.cs file:
using System;
using System.Collections.Generic;
using System.Text;
using log4net;
using log4net.Core;
using log4net.Appender;
using log4net.Layout;
using System.Web;
using System.Web.UI;
namespace log4net.Appender
{
public class JSConsoleAppender : AppenderSkeleton
{
private int m_IDCounter = 0;
private bool m_ExceptionOnNoHttpContext = true;
public bool ExceptionOnNoHttpContext
{
get { return m_ExceptionOnNoHttpContext; }
set { m_ExceptionOnNoHttpContext = value; }
}
override protected void Append(LoggingEvent loggingEvent)
{
if (ExceptionOnNoHttpContext == true)
{
if (HttpContext.Current == null)
{
ErrorHandler.Error("JSConsoleAppender: No HttpContext to write javascript to.");
return;
}
}
PatternLayout Layout = this.Layout as PatternLayout;
if (Layout.ConversionPattern.Contains("%newline"))
{
ErrorHandler.Error("JSConsoleAppender: Pattern may not contain %newline.");
return;
}
String LogStr = this.RenderLoggingEvent(loggingEvent);
LogStr = LogStr.Replace("'", "\\'");
String OutputScript = String.Format("if (window.console) console.log('{0}');", LogStr);
Page page = HttpContext.Current.CurrentHandler as Page;
page.ClientScript.RegisterStartupScript(page.GetType(), m_IDCounter++.ToString(), OutputScript, true);
}
override protected bool RequiresLayout
{
get { return true; }
}
}
}
From the ASP.NET test application, here's the web.config file. In the pattern for the JSConsoleAppender, I added the word SERVER: to differentiate the lines from client logging. Note there are two other Appenders in the log…just for fun!
="1.0"
<configuration>
<configSections >
<section name="log4net"
type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
<appender name="LogFileAppender"
type="log4net.Appender.FileAppender">
<param name="File" value="C:\Log4Net.log"/>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d %-5p %c %m%n"/>
</layout>
</appender>
<appender name="TraceAppender"
type="log4net.Appender.TraceAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level [%property{NDC}] - %message%newline" />
</layout>
</appender>
<appender name="JSConsoleAppender"
type="log4net.Appender.JSConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="SERVER: %date %-5level %logger: %message SRC: %location" />
</layout>
</appender>
<logger name="MyLogger">
<level value="ALL" />
<appender-ref ref="LogFileAppender" />
<appender-ref ref="TraceAppender" />
<appender-ref ref="JSConsoleAppender" />
</logger>
</log4net>
<system.web>
<compilation debug="true"
targetFramework="4.0"/>
</system.web>
</configuration>
Here's the default.aspx file from the test program, I added a bit of JavaScript and jQuery:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Log4Net Test</title>
<script src="Scripts/jquery-1.7.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(DocReady);
function DocReady()
{
if (window.console)
console.log("CLIENT: Doc Ready!");
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<h5>Log4Net Test</h5>
<asp:Button ID="ButtonLog" runat="server" Text="Do Some Logging!"
onclick="ButtonLog_Click" />
</div>
</form>
</body>
</html>
Here's the default.cs file from the test program with some logging strings:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using log4net;
public partial class _Default : System.Web.UI.Page
{
private static readonly ILog log = LogManager.GetLogger("MyLogger");
protected void Page_Load(object sender, EventArgs e)
{
log.Info("Page_Load!");
}
protected void ButtonLog_Click(object sender, EventArgs e)
{
log.Info("Soft kitty, warm kitty");
log.Warn("Little ball of fur");
log.Error("Happy kitty, sleepy kitty");
log.Fatal("Purr, purr, purr.");
}
}
And finally, here are the three output logs:
From the JSConsoleAppender (IE Developer Tools), it includes a client log call with the server log calls:
From the TraceAppender, the Visual Studio Output window:
From the LogFileAppender (using Notepad):
You can download the solution/projects/code here.
I hope someone finds this useful.
Steve Wellens