Disclaimer:
- This is not great code but is super simple to use
- Performance was not a consideration
- I don't claim to have written it, just cobbled the bits together into one atomic class
- Lots of my blogs don't make it publish for the typical fear of retribution as it's not up to standard (I'd probably ridicule my own code in a week) So this time I'm sticking it out there warts and all and will update it as time goes by to make the blurb and code cleaner
Summary
Cut and paste this into a class in your project then start writing to Logger instead of Console and you get Console/File/UDP (Log2Console) output.
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Reflection;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Threading;
namespace My.Project.Namespace
{
public static class Logger
{
public enum LogLevel {Error,Warning,Info,Degug}
private static Socket _socket;
private static IPEndPoint _socketEndPoint;
private static FileStream _logFileStream;
private static readonly string logFile = string.Format("{0}/{1}.log", AppDomain.CurrentDomain.BaseDirectory, Assembly.GetExecutingAssembly().GetName().Name);
private const int MaxFileSizeInMb = 10;
private static readonly IPAddress UdpIp = IPAddress.Loopback;
private const int UdpPort = 7071;
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
static Logger()
{
Level = LogLevel.Info;
InitialiseShutdownAction();
InitialiseFileWriter();
InitialiseUdpBroadcaster();
InitialiseTraceListeners();
}
public static LogLevel Level { get; set; }
public static void Error(string message)
{
WriteEntry("error", message);
}
public static void Error(Exception ex)
{
WriteEntry("error", ex.Message);
}
public static void Warning(string message)
{
if(Level < LogLevel.Warning) return;
WriteEntry("warning", message);
}
public static void Info(string message)
{
if (Level < LogLevel.Info) return;
WriteEntry("info", message);
}
public static void Debug(string message)
{
if (Level < LogLevel.Degug) return;
WriteEntry("debug", message);
}
private static void InitialiseShutdownAction()
{
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
}
private static void InitialiseTraceListeners()
{
var traceListener = new TextWriterTraceListener(_logFileStream);
Trace.Listeners.Add(traceListener);
}
private static void InitialiseFileWriter()
{
if (File.Exists(logFile))
{
RolloverLogFile();
}
_logFileStream = new FileStream(logFile, FileMode.OpenOrCreate);
}
private static void RolloverLogFile()
{
var rollingFileName = logFile.Replace(".log",
string.Format(".{0:yyyyMMddHHmmss}.log", File.GetLastWriteTime(logFile)));
File.Move(logFile, rollingFileName);
}
private static void WriteEntry(string level, string message)
{
var module = GetCallingClass();
Console.WriteLine(message);
UdpBroadcast(module, level, message);
Trace.WriteLine(string.Format(
"{0}|{1}|{2}|{3}",
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
level,
module,
message));
Trace.Flush();
_logFileStream.Flush();
if (new FileInfo(logFile).Length <= MaxFileSizeInMb*1000000)
return;
RolloverLogFile();
_logFileStream = new FileStream(logFile, FileMode.OpenOrCreate);
}
private static string GetCallingClass()
{
var stackTrace = new StackTrace();
var stackFrames = stackTrace.GetFrames();
if (stackFrames == null)
return "Unknown";
var parentStackFrame = stackFrames.First(x => x.GetMethod().DeclaringType != typeof (Logger));
var declaringType = parentStackFrame.GetMethod().DeclaringType;
return declaringType == null ? "Unknown" : declaringType.FullName;
}
private static void InitialiseUdpBroadcaster()
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
_socketEndPoint = new IPEndPoint(UdpIp, UdpPort);
}
private static void UdpBroadcast(string module, string level, string message)
{
var xmlMessage = System.Security.SecurityElement.Escape(message);
var log4jMessage = string.Format(log4JXmlMessageXml, module, (long)DateTime.Now.ToUniversalTime().Subtract(Epoch).TotalMilliseconds, level, Thread.CurrentThread.ManagedThreadId , xmlMessage,
Environment.MachineName, Environment.UserDomainName, Environment.UserName, AppDomain.CurrentDomain.FriendlyName);
var buffer = Encoding.ASCII.GetBytes(log4jMessage);
_socket.SendTo(buffer, _socketEndPoint);
}
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
_logFileStream.Close();
}
private static string log4JXmlMessageXml =
@"<log4j:event logger=""{0}"" timestamp=""{1}"" level=""{2}"" thread=""1"">
<log4j:message>{3}</log4j:message> <log4j:properties> <log4j:data name=""log4jmachinename"" value=""{4}"" />
<log4j:data name=""log4net:HostName"" value=""{4}"" /> <log4j:data name=""log4net:UserName"" value=""{5}"" />
<log4j:data name=""log4japp"" value=""{6}"" /> </log4j:properties> </log4j:event>";
///Sample as provided in Log2Console source code
/// <log4j:event logger="Statyk7.Another.Name.DummyManager" timestamp="1184286222308" level="ERROR" thread="1">
/// <log4j:message>This is an Message</log4j:message>
/// <log4j:properties>
/// <log4j:data name="log4jmachinename" value="remserver" />
/// <log4j:data name="log4net:HostName" value="remserver" />
/// <log4j:data name="log4net:UserName" value="REMSERVER\Statyk7" />
/// <log4j:data name="log4japp" value="Test.exe" />
/// </log4j:properties>
/// </log4j:event>
}
Usage
Now anywhere in you project just log using the Logger static object - no setup need, its all defaulted.
If you want you to configure it hack the class to be what you want (It's your projects code now)
Logger.Info("My Info message");
Credit
http://www.codeguru.com/csharp/.net/article.php/c19405/Tracing-in-NET-and-Implementing-Your-Own-Trace-Listeners.htm
https://log2console.codeplex.com
Update: 2016/01/28 - Updated code styling
- Update to check log level