|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Services
Chapters
Feature Zones
|
IntroductionThis article describes how to use the
PublicDomainThe PublicDomain package is a completely free, public domain collection of oft-needed code and packages for .NET. There is no license, so the code (or any part of it) may be included even in corporate applications. Public domain code has no authority and is provided 'AS-IS'. High Level ArchitectureThe architecture was designed with simplicity and extensibility in mind. Some of the concepts include: Logger ClassEverything is based off the abstract The basic API of the Properties
Methods
Configuration ParserEvery application requires a very granular use of logging, which can be dynamically enabled and disabled. Most often, this is specified with a string that has a delimited list of For example, let's say I have class using System;
using System.Collections.Generic;
using System.Text;
using PublicDomain;
using PublicDomain.Logging;
namespace play
{
class Program
{
public static readonly LoggingConfig Loggers;
static Program()
{
// Create the actual logger which is a rolling file logger
Logger fileLogger = new RollingFileLogger("mylog{0}.log");
fileLogger.Formatter = new SimpleLogFormatter();
// This configuration may be loaded from a resource or
// from a config file:
string config = "all=off";
// Create the logging config. This creates a delegate
// which will be called any time another part of the code
// asks for a Logger instance.
Loggers = new LoggingConfig(config, delegate
(string className, LoggerSeverity threshold)
{
Logger result = new SimpleCompositeLogger
(fileLogger, className);
result.Threshold = threshold;
return result;
}, null);
}
static void Main(string[] args)
{
}
}
class A
{
/// <summary>
/// The Logger instance for all methods within class A
/// </summary>
internal static readonly Logger Log =
Program.Loggers.CreateLogger(typeof(A));
public void foo()
{
Log.LogDebug10("calling foo");
}
}
class B
{
/// <summary>
/// The Logger instance for all methods within class B
/// </summary>
internal static readonly Logger Log =
Program.Loggers.CreateLogger(typeof(A));
public void foo()
{
Log.LogInfo20("calling foo {0}", typeof(B).Name);
}
}
}
Some ideas from the code above regardless of programming language (in this case C#):
Now that I have various public static void UpdateLoggers(string config)
{
Loggers.Load(config);
}
We may call this from something that reads input from the user or re-reads a configuration file. Let's say we called it with: Program.UpdateLoggers("play.A=Debug,play.B");
In this case, we will be setting the threshold of the Also, when we ask for a Program.Loggers.CreateLogger(typeof(A));
In this case, we used the name of the class as the Program.Loggers.CreateLogger("mylogger", "Database");
This way, we can update class Of course, the value of this is that you can "share" configuration across multiple loggers. If certain classes may deal with database access, you can add this string to all of them, and when you turn on database logging, all of them will log. EfficiencyLogging efficiency is very important. The two major efficiency improvements in this log package are log guards, and logging on a background thread. Log GuardsThe concept of log guards are pretty simple but very effective. Let's say, I have the following method, which logs entry/exit, and also some debug within the method: public int foo(int x, int y)
{
Log.Entry("foo {0},{1},{2}", x, y, x*y);
// Do some calculation
int result = x * y * 1984;
// Log some debug
Log.LogDebug10("The result mod 7 * 10000 is: {0}", (result % 7) * 10000);
// Log the exit
Log.Exit("foo result={0}", result);
return result;
}
Now, in this case, let's say the However, the .NET JIT and the compiler have no idea that Log.Entry("foo {0},{1},{2}", x, y, x*y);
Log.LogDebug10("The result mod 7 * 10000 is: {0}", (result % 7) * 10000);
Both of these methods have non-trivial calculation in their calls. You could imagine that this is very common, and if the To the uninitiated, public int foo(int x, int y)
{
if (Log.Enabled) Log.Entry("foo {0},{1},{2}", x, y, x*y);
// Do some calculation
int result = x * y * 1984;
// Log some debug
if (Log.Enabled) Log.LogDebug10("The result mod 7 * 10000 is: {0}",
(result % 7) * 10000);
// Log the exit
if (Log.Enabled) Log.Exit("foo result={0}", result);
return result;
}
Notice that each log statement is now "guarded" with " Background Thread LoggingLogging is often turned off in critical sections of the code. Many times, it is needed only to debug a problem. However, often problems are related to race conditions or may not be reproduced if the program slows down significantly. Therefore, when a log is turned on, it is important that it is as efficient as possible. A naïve implementation of a Therefore, we don't want to log synchronously on the thread that called the ApplicationLoggerMost of the above conversation has been about writing highly scalable, extensible, configurable, and performant logging. That's all wonderful, but what if I'm writing a pretty small program? I want a quick and dirty logging solution: ApplicationLoggerusing System;
using System.Collections.Generic;
using System.Text;
using PublicDomain;
using PublicDomain.Logging;
namespace play
{
class Program
{
static void Main(string[] args)
{
if (ApplicationLogger.Current.Enabled)
ApplicationLogger.Current.Threshold = LoggerSeverity.Debug10;
if (ApplicationLogger.Current.Enabled)
ApplicationLogger.Current.LogDebug10("starting program");
Console.WriteLine(A.foo(5, 6));
if (ApplicationLogger.Current.Enabled)
ApplicationLogger.Current.LogDebug10("ending program");
}
}
class A
{
public static int foo(int x, int y)
{
if (ApplicationLogger.Current.Enabled)
ApplicationLogger.Current.Entry("foo {0},{1}", x, y);
int result = x * y;
if (ApplicationLogger.Current.Enabled)
ApplicationLogger.Current.Exit("foo {0}", result);
return result;
}
}
}
The The log guards are of course optional if you don't really care about performance and want "nicer" code.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||