|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionI've recently begun seriously working with the .NET framework. I found a need for a .NET logging framework, something offering more robust features and better scalability than the classes in the BackgroundMy background for the last seven or eight years has mostly been in Java development; however, when the release of the .NET framework somewhat coincided with the buyout of my shop by a Microsoft-centered company, I was determined to learn everything I could about the competition. I spent some time getting my MCSD .NET, and learned a lot along the way about how Microsoft likes to do things. Well, I've been pleasantly surprised so far! All in all, I find the .NET Framework to offer a pleasing blend of performance and features. However (and I suspect that this may be mostly due to the youth of the platform) it is still missing crucial features, logging support among them. What's so important about logging?First off, I'm not saying that You may skip the next section and go right to the programming samples if you like, then download the .zip file. It contains extensive HTML documentation, including a programming sample illustrating the use of each logger, event formatter, and data formatter type in detail. The documentation has actually taken me longer to write than the code. Logging package featuresThe new logging package offers the following features, among others: High performance: Classes Stability: Every class in the logging package is thread-safe and performance-tuned for use in a multithreaded environment. Also, logging-failure support can be provided via classes implementing the Ease of use: Loggers provided in this package are easy to understand and to use. Each one represents a different form of output; many different pre-written loggers are provided, covering everything from email logging to database logging to various forms of file-based logging, and most of them can be easily configured by calls to their constructor methods. Care has been taken to make each interface as intuitively easy-to-use as possible. Ease of extension: Users can define their own loggers, filters, exception handlers, event formatters, data formatters, and log levels with ease, usually by just instantiating an object or implementing one or two methods. For instance, a custom Levels: Each log message ( Flexible filtering: A hallmark of good logging support is the ability to selectively discard logged data based on more in-depth criteria than can be provided by the use of levels. Not only are many prebuilt filters provided in the Flexible formatting: Useful types of log-record formatting are supplied via the classes in the Data support: Each log event can carry an object data "payload". Support is built in for correct output of all arrays, lists and dictionary classes, together with the ability to supply format strings for each primitive type, as well as such structures as Partitioned access: Naming: Loggers can be registered with one or more names using a Composition: Loggers in this framework can be added to each other as children, receiving hand-me-down events. Different logging structures can be created for different needs, all organized under one easy-to-use name. Filters can also be composed of other filters using the Event categorization: Support for existing code: The logging package may be used with existing code that depends on Programming sample #1: Logging to files(This and the following two examples were taken at random from the project documentation.) Instances of The logger will create any directories necessary to write to any file. Where it doesn't involve a large memory commitment, File output may be "scattered" to multiple files by the use of a formatting pattern in the file path. This may be used, for example, to implement "rolling" log files, where output is redirected on a periodic (say, hourly) basis. Performance in scattering mode is still excellent, as the logger holds multiple streams open in buffering mode (unless the path contains often-changing information such as the event ID or any time-based token value containing the millisecond. Use of such volatile paths still works just fine, but it results in the opening and closing of a stream for each event, resulting in slower performance). Archiving is supported through the use of the The event formatter used by default is an instance of using System;
using NSpring.Logging;
public class FileLoggerExample {
public static void Main(string[] args) {
// The first parameter here is the file path; the second, the output
// pattern
Logger logger = Logger.CreateFileLogger("c:\\temp\\nspring.log", +
"{ts}{z} [{ln:1w}] {msg}");
logger.IsBufferingEnabled = true;
logger.BufferSize = 1000;
logger.Open();
logger.Log(Level.Debug, "My hamster's name is Wilhelmina");
logger.Log(Level.Verbose, "She has two tails");
logger.Log(Level.Config, "She wears a green mohawk");
logger.Log(Level.Info, "She's studying to be an accountant");
logger.Log(Level.Warning, "Chrysanthemums are her favorite food");
logger.Log(Level.Exception, "Underneath that tough biker exterior," +
" she's just a sweet hamster");
logger.Close();
}
}
Output written to c:\temp\nspring.log: 2003-10-13 12:25:25.895-04:00 [D] My hamster's name is Wilhelmina
2003-10-13 12:25:25.895-04:00 [V] She has two tails
2003-10-13 12:25:25.895-04:00 [C] She wears a green mohawk
2003-10-13 12:25:25.895-04:00 [I] She's studying to be an accountant
2003-10-13 12:25:25.895-04:00 [W] Chrysanthemums are her favorite food
2003-10-13 12:25:25.895-04:00 [E] Underneath that tough biker exterior,
she's just a sweet hamster
Programming sample #2: Using logger compositionThis shows a simple example of the use of the using System;
using NSpring.Logging;
public class CompositeLoggerExample {
public static void Main(string[] args) {
// This very simple pattern will help differentiate output
Logger cl1 = Logger.CreateConsoleLogger("Logger 1: {message}");
// of the two child loggers
Logger cl2 = Logger.CreateConsoleLogger("Logger 2: {message}");
Logger logger = Logger.CreateCompositeLogger(cl1, cl2);
logger.IsBufferingEnabled = true;
// If the buffer reaches this size, the logger's thread will
// automatically awaken
logger.BufferSize = 10000;
logger.AutoFlushInterval = 15000; // This value is in milliseconds
logger.Open(); // This causes the opening of the children
logger.Log("My hamster's name is Wilhelmina");
logger.Log("She drives a tiny dump truck");
logger.Log("She always wins at arm-wrestling");
logger.Close(); // This causes the closing of the children
}
}
Output: Logger 1: My hamster's name is Wilhelmina
Logger 1: She drives a tiny dump truck
Logger 1: She always wins at arm-wrestling
Logger 2: My hamster's name is Wilhelmina
Logger 2: She drives a tiny dump truck
Logger 2: She always wins at arm-wrestling
Programming sample #3: An XMLDataFormatter exampleEach Casing and separator options used in constructing XML names can be set using the using System;
using System.Collections;
using NSpring.Logging;
using NSpring.Logging.Loggers;
using NSpring.Logging.DataFormatters;
using NSpring.Logging.EventFormatters;
public class XMLDataFormatterExample {
public static void Main(string[] args) {
ConsoleLogger logger = new ConsoleLogger();
EventFormatter eventFormatter =
new PatternEventFormatter("{message}\n{data}\n");
eventFormatter.DataFormatter = new XMLDataFormatter();
eventFormatter.IsIndentationEnabled = true;
logger.EventFormatter = eventFormatter;
string s = "schmaltz";
int i = 5;
float f = 1.234567F;
TimeSpan ts = new TimeSpan(1, 2, 3, 4, 555);
DateTime dt = DateTime.Now;
Hashtable h = new Hashtable();
h["key1"] = "value1";
h["key2"] = "value2";
object[] oa = {s, i, f, ts, dt, h};
logger.Log("A single string value:", (object)s);
logger.Log("A single integer value:", i);
logger.Log("A single float value:", f);
logger.Log("A single TimeSpan value:", ts);
logger.Log("A single DateTime value:", dt);
logger.Log("A Hashtable value:", h);
logger.Log("An array of the above:", oa);
}
}
Output: A single string value:
<data type="string">schmaltz</data>
A single integer value:
<data type="integer">5</data>
A single float value:
<data type="float">1.234567</data>
A single TimeSpan value:
<data type="duration">P1DT2H3M4.555S</data>
A single DateTime value:
<data type="dateTime">2003-10-09T18:47:05.181-04:00</data>
A Hashtable value:
<data type="map">
<entry>
<key type="string">key1</key>
<value type="string">value1</value>
</entry>
<entry>
<key type="string">key2</key>
<value type="string">value2</value>
</entry>
</data>
An array of the above:
<data type="list">
<value type="string">schmaltz</value>
<value type="integer">5</value>
<value type="float">1.234567</value>
<value type="duration">P1DT2H3M4.555S</value>
<value type="dateTime">2003-10-09T18:47:05.181-04:00</value>
<value type="map">
<entry>
<key type="string">key1</key>
<value type="string">value1</value>
</entry>
<entry>
<key type="string">key2</key>
<value type="string">value2</value>
</entry>
</value>
</data>
Points of InterestThe main reasons for the speed of the logging framework are twofold: avoidance, wherever possible, of both object creation and synchronization. The bulk of the time quoted above (about 500 nanoseconds for logging a message in buffering mode on a laptop machine) is for constructing the single Failing a further reduction (to less than one!) in per-message object creation overhead, I plan to at least defer the creation of this object. So, instead of an application thread driving the creation of an One thing that helped a lot in reducing synchronization overhead was use of object copies and immutable objects; if something can't be changed by another thread, there's no need for synchronization. For instance, the internals of an Pattern evaluation turns out to be quite speedy. In fact, testing the formatting of date pattern strings versus .NET date format strings shows that the .NET built-in formatting is slower. I guess it's true, sometimes, what they say-- the more generic the code, the slower the code. For a complete reference on the mini-formatting language, look in the appendix of the HTML docs. I highly recommend NDoc, a utility put out by the NDoc project on sourceforge.net. I would've never turned out such professional-looking API docs without it. In the works...A configuration framework, centralized logging application, and several other enhancements. I'd welcome help on this and other planned features, if you have the time. History
| ||||||||||||||||||||||