Mercurial > projects > ldc
diff tango/example/logging/context.d @ 132:1700239cab2e trunk
[svn r136] MAJOR UNSTABLE UPDATE!!!
Initial commit after moving to Tango instead of Phobos.
Lots of bugfixes...
This build is not suitable for most things.
author | lindquist |
---|---|
date | Fri, 11 Jan 2008 17:57:40 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tango/example/logging/context.d Fri Jan 11 17:57:40 2008 +0100 @@ -0,0 +1,322 @@ +/******************************************************************************* + + copyright: Copyright (c) 2007 Stonecobra. All rights reserved + + license: BSD style: $(LICENSE) + + version: Initial release: December 2007 + + author: stonecobra + +*******************************************************************************/ + +module context; + +import tango.core.Thread, + tango.util.log.Log, + tango.util.log.Event, + tango.util.log.EventLayout, + tango.util.log.ConsoleAppender; + +import tango.util.log.model.IHierarchy; + +/******************************************************************************* + + Allows the dynamic setting of log levels on a per-thread basis. + Imagine that a user request comes into your production threaded + server. You can't afford to turn logging up to trace for the sake + of debugging this one users problem, but you also can't afford to + find the problem and fix it. So now you just set the override log + level to TRACE for the thread the user is on, and you get full trace + output for only that user. + +*******************************************************************************/ + +class ThreadLocalDiagnosticContext : IHierarchy.Context +{ + private ThreadLocal!(DCData) dcData; + private char[128] tmp; + + /*********************************************************************** + + ***********************************************************************/ + + public this() + { + dcData = new ThreadLocal!(DCData); + } + + /*********************************************************************** + + set the 'diagnostic' Level for logging. This overrides + the Level in the current Logger. The default level starts + at NONE, so as not to modify the behavior of existing clients + of tango.util.log + + ***********************************************************************/ + + void setLevel (ILevel.Level level) + { + auto data = dcData.val; + data.level = level; + dcData.val = data; + } + + /*********************************************************************** + + All log appends will be checked against this to see if a + log level needs to be temporarily adjusted. + + ***********************************************************************/ + + bool isEnabled (ILevel.Level setting, ILevel.Level level = ILevel.Level.Trace) + { + return level >= setting || level >= dcData.val.level; + } + + /*********************************************************************** + + Return the label to use for the current log message. Usually + called by the Layout. This implementation returns "{}". + + ***********************************************************************/ + + char[] label () + { + return dcData.val.getLabel; + } + + /*********************************************************************** + + Push another string into the 'stack'. This strings will be + appened together when getLabel is called. + + ***********************************************************************/ + + void push (char[] label) + { + auto data = dcData.val; + data.push(label); + dcData.val = data; + } + + /*********************************************************************** + + pop the current label off the stack. + + ***********************************************************************/ + + void pop () + { + auto data = dcData.val; + data.pop; + dcData.val = data; + } + + /*********************************************************************** + + Clear the label stack. + + ***********************************************************************/ + + void clear() + { + auto data = dcData.val; + data.clear; + dcData.val = data; + } +} + + +/******************************************************************************* + + The thread locally stored struct to hold the logging level and + the label stack. + +*******************************************************************************/ + +private struct DCData { + + ILevel.Level level = ILevel.Level.None; + char[][8] stack; + bool shouldUpdate = true; + int stackIndex = 0; + uint labelLength; + char[256] labelContent; + + + char[] getLabel() { + if (shouldUpdate) { + labelLength = 0; + append(" {"); + for (int i = 0; i < stackIndex; i++) { + append(stack[i]); + if (i < stackIndex - 1) { + append(" "); + } + } + append("}"); + shouldUpdate = false; + } + return labelContent[0..labelLength]; + } + + void append(char[] x) { + uint addition = x.length; + uint newLength = labelLength + x.length; + + if (newLength < labelContent.length) + { + labelContent [labelLength..newLength] = x[0..addition]; + labelLength = newLength; + } + } + + void push(char[] label) { + shouldUpdate = true; + stack[stackIndex] = label.dup; + stackIndex++; + } + + void pop() { + shouldUpdate = true; + if (stackIndex > 0) { + stack[stackIndex] = null; + stackIndex--; + } + } + + void clear() { + shouldUpdate = true; + for (int i = 0; i < stack.length; i++) { + stack[i] = null; + } + } +} + + +/******************************************************************************* + + Simple console appender that counts the number of log lines it + has written. + +*******************************************************************************/ + +class TestingConsoleAppender : ConsoleAppender { + + int events = 0; + + this (EventLayout layout = null) + { + super(layout); + } + + override void append (Event event) + { + events++; + super.append(event); + } +} + + +/******************************************************************************* + + Testing harness for the DiagnosticContext functionality. + +*******************************************************************************/ + +void main(char[][] args) +{ + //set up our appender that counts the log output. This is the configuration + //equivalent of importing tango.util.log.Configurator. + auto appender = new TestingConsoleAppender(new SimpleTimerLayout); + Log.getRootLogger.addAppender(appender); + + char[128] tmp = 0; + auto log = Log.getLogger("context"); + log.setLevel(log.Level.Info); + + //first test, use all defaults, validating it is working. None of the trace() + //calls should count in the test. + for (int i=0;i < 10; i++) { + log.info(log.format(tmp, "test1 {}", i)); + log.trace(log.format(tmp, "test1 {}", i)); + } + if (appender.events !is 10) { + log.error(log.format(tmp, "events:{}", appender.events)); + throw new Exception("Incorrect Number of events in normal mode"); + } + + appender.events = 0; + + //test the thread local implementation without any threads, as a baseline. + //should be same result as test1 + auto context = new ThreadLocalDiagnosticContext; + Log.getHierarchy.context(context); + for (int i=0;i < 10; i++) { + log.info(log.format(tmp, "test2 {}", i)); + log.trace(log.format(tmp, "test2 {}", i)); + } + if (appender.events !is 10) { + log.error(log.format(tmp, "events:{}", appender.events)); + throw new Exception("Incorrect Number of events in TLS single thread mode"); + } + + appender.events = 0; + + //test the thread local implementation without any threads, as a baseline. + //This should count all logging requests, because the DiagnosticContext has + //'overridden' the logging level on ALL loggers up to TRACE. + context.setLevel(log.Level.Trace); + for (int i=0;i < 10; i++) { + log.info(log.format(tmp, "test3 {}", i)); + log.trace(log.format(tmp, "test3 {}", i)); + } + if (appender.events !is 20) { + log.error(log.format(tmp, "events:{}", appender.events)); + throw new Exception("Incorrect Number of events in TLS single thread mode with level set"); + } + + appender.events = 0; + + //test the thread local implementation without any threads, as a baseline. + context.setLevel(log.Level.None); + for (int i=0;i < 10; i++) { + log.info(log.format(tmp, "test4 {}", i)); + log.trace(log.format(tmp, "test4 {}", i)); + } + if (appender.events !is 10) { + log.error(log.format(tmp, "events:{}", appender.events)); + throw new Exception("Incorrect Number of events in TLS single thread mode after level reset"); + } + + //Now test threading. set up a trace context in one thread, with a label, while + //keeping the second thread at the normal configuration. + appender.events = 0; + ThreadGroup tg = new ThreadGroup(); + tg.create({ + char[128] tmp = 0; + context.setLevel(log.Level.Trace); + context.push("specialthread"); + context.push("2ndlevel"); + for (int i=0;i < 10; i++) { + log.info(log.format(tmp, "test5 {}", i)); + log.trace(log.format(tmp, "test5 {}", i)); + } + }); + tg.create({ + char[128] tmp = 0; + context.setLevel(log.Level.None); + for (int i=0;i < 10; i++) { + log.info(log.format(tmp, "test6 {}", i)); + log.trace(log.format(tmp, "test6 {}", i)); + } + }); + tg.joinAll(); + + if (appender.events !is 30) { + log.error(log.format(tmp, "events:{}", appender.events)); + throw new Exception("Incorrect Number of events in TLS multi thread mode"); + } +} +