diff --git a/SimpleLog.Tests/ConfigTest.cs b/SimpleLog.Tests/ConfigTest.cs new file mode 100644 index 0000000..ccb6ef1 --- /dev/null +++ b/SimpleLog.Tests/ConfigTest.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using NUnit.Framework; +using NUnit.Mocks; +using NUnit.Framework.SyntaxHelpers; +using SimpleLog.Framework; + +namespace SimpleLog.Tests { + [TestFixture] + public class ConfigTest { + + [SetUp] + public void SetUp() { + + } + + [Test] + public void TestLoadConfig() { + string configFile = "Test.config"; + Config config = new Config(configFile); + + //verify that logger configs were set up correctly + Assert.That(config.HasLoggerConfig("SimpleLog.DefaultLogger")); + + LoggerConfig loggerConfig = config.GetLoggerConfig("SimpleLog.DefaultLogger"); + Assert.That(loggerConfig, Is.Not.Null); + IDictionary handlerData = loggerConfig.HandlerData; + Assert.That(handlerData, Is.Not.Null); + Assert.That(handlerData.ContainsKey("SimpleLog.ConsoleWindowLogHandler")); + Assert.That(handlerData.ContainsKey("SimpleLog.FileLogHandler")); + + LogHandlerConfig handlerConfig = handlerData["SimpleLog.ConsoleWindowLogHandler"]; + Assert.That(handlerConfig, Is.Not.Null); + Assert.That(handlerConfig.Settings, Is.TypeOf(typeof(Dictionary))); + Assert.That(handlerConfig.Settings.Count, Is.EqualTo(3)); + Assert.That(handlerConfig.HasSetting("DebugForeColor")); + Assert.That(handlerConfig.GetSetting("DebugForeColor"), Is.EqualTo("Cyan")); + Assert.That(handlerConfig.HasSetting("FatalForeColor")); + Assert.That(handlerConfig.GetSetting("FatalForeColor"), Is.EqualTo("White")); + Assert.That(handlerConfig.HasSetting("FatalBackColor")); + Assert.That(handlerConfig.GetSetting("FatalBackColor"), Is.EqualTo("Red")); + + handlerConfig = handlerData["SimpleLog.FileLogHandler"]; + Assert.That(handlerConfig, Is.Not.Null); + Assert.That(handlerConfig.Settings, Is.TypeOf(typeof(Dictionary))); + Assert.That(handlerConfig.Settings.Count, Is.EqualTo(3)); + Assert.That(handlerConfig.HasSetting("LogDirectory")); + Assert.That(handlerConfig.GetSetting("LogDirectory"), Is.EqualTo("c:\\WINDOWS\\Temp")); + Assert.That(handlerConfig.HasSetting("FilePrefix")); + Assert.That(handlerConfig.GetSetting("FilePrefix"), Is.EqualTo("simplelog_")); + Assert.That(handlerConfig.HasSetting("FileSuffix")); + Assert.That(handlerConfig.GetSetting("FileSuffix"), Is.EqualTo(".log")); + } + + [Test] + [ExpectedException("System.Collections.Generic.KeyNotFoundException")] + public void TestLogHandlerConfigGetSetting() { + LogHandlerConfig config = new LogHandlerConfig(); + config.GetSetting("foo"); + } + + [Test] + [ExpectedException("System.Collections.Generic.KeyNotFoundException")] + public void TestConfigGetLoggerConfig() { + Config config = new Config(); + config.GetLoggerConfig("foo"); + } + } +} diff --git a/SimpleLog.Tests/ConsoleColorTest.cs b/SimpleLog.Tests/ConsoleColorTest.cs new file mode 100644 index 0000000..1298f64 --- /dev/null +++ b/SimpleLog.Tests/ConsoleColorTest.cs @@ -0,0 +1,34 @@ +using System; +using SimpleLog; + +namespace SimpleLog.Tests { + /// + /// To run this test, modify the project properties' "Application" + /// section and set the output type to "Console Application", and + /// set the startup object to this class. Then compile, and + /// start a debugging instance of this class. + /// + internal static class ConsoleColorTest { + + static void Main(string[] args) { + DefaultLogger logger = LoggerManager.GetLogger(typeof(DefaultLogger)) as DefaultLogger; + logger.LogLevel = SimpleLog.Framework.LogLevel.All; + + logger.Debug("Debug message"); + logger.Info("Info message"); + logger.Warning("Warning message"); + logger.Error("Error message"); + logger.Fatal("Fatal message"); + + try { + throw new Exception("Oh noes!! an exception"); + } + catch (Exception e) { + logger.Fatal(e); + } + + Console.ReadLine(); + } + + } +} diff --git a/SimpleLog.Tests/ConsoleLogHandlerTest.cs b/SimpleLog.Tests/ConsoleLogHandlerTest.cs index d8737ae..be6581f 100644 --- a/SimpleLog.Tests/ConsoleLogHandlerTest.cs +++ b/SimpleLog.Tests/ConsoleLogHandlerTest.cs @@ -1,7 +1,6 @@ using System; using NUnit.Framework; using NUnit.Framework.SyntaxHelpers; -using SimpleLog.LogHandlers; namespace SimpleLog.Tests { [TestFixture] @@ -16,7 +15,7 @@ namespace SimpleLog.Tests { [Test] public void TestLog() { - Assert.That(this.handler.Log("yay", Framework.LogLevel.Critical)); + Assert.That(this.handler.Log("yay", Framework.LogLevel.Fatal)); Assert.That(this.handler.Log("yay", Framework.LogLevel.Debug)); } @@ -29,7 +28,7 @@ namespace SimpleLog.Tests { [Test] public void TestAccessors() { Assert.That(handler.MessageHandler, Is.TypeOf(typeof(DefaultMessageHandler))); - Assert.That(handler.LogLevel, Is.EqualTo(null)); + Assert.That(handler.LogLevel, Is.EqualTo(SimpleLog.Framework.LogLevel.None)); } } diff --git a/SimpleLog.Tests/DefaultLoggerTest.cs b/SimpleLog.Tests/DefaultLoggerTest.cs index 5e0c3c1..9088b18 100644 --- a/SimpleLog.Tests/DefaultLoggerTest.cs +++ b/SimpleLog.Tests/DefaultLoggerTest.cs @@ -10,20 +10,24 @@ namespace SimpleLog.Tests { private DefaultLogger Logger; private DynamicMock Handler; + private string TempFile; [SetUp] - public void Init() { + public void SetUp() { this.Logger = new DefaultLogger(); this.Logger.MessageHandler = new DefaultMessageHandler(); this.Handler = new DynamicMock(typeof(ILogHandler)); - + this.TempFile = null; } [TearDown] - public void DeInit() { + public void TearDown() { this.Logger.UnregisterAllLogHandlers(); this.Logger.Enabled = true; this.Logger.MessageHandler = new DefaultMessageHandler(); + if (this.TempFile != null && System.IO.File.Exists(this.TempFile)) { + System.IO.File.Delete(this.TempFile); + } } [Test] @@ -34,8 +38,6 @@ namespace SimpleLog.Tests { this.Logger.RegisterLogHandler(handler); Assert.That(this.Logger.LogHandlerCount, Is.EqualTo(1)); - - //this.Logger.UnregisterLogHandlerType("SimpleLog.Framework.ILogHandler"); Assert.That(this.Logger.UnregisterLogHandlerType("SimpleLog.Framework.ILogHandler"), Is.EqualTo(1)); Assert.That(this.Logger.LogHandlerCount, Is.EqualTo(0)); @@ -64,6 +66,8 @@ namespace SimpleLog.Tests { this.Logger.MessageHandler = (IMessageHandler)msgHandler.MockInstance; this.Handler.ExpectAndReturn("Log", true, "foobar", LogLevel.Warning); + //h4x0rz! + this.Handler.ExpectAndReturn("get_LogLevel", LogLevel.None); this.Logger.RegisterLogHandler((ILogHandler)this.Handler.MockInstance); @@ -83,6 +87,8 @@ namespace SimpleLog.Tests { this.Logger.MessageHandler = (IMessageHandler)msgHandler.MockInstance; this.Handler.ExpectAndReturn("Log", false, "foobar", LogLevel.Warning); + //h4x0rz! + this.Handler.ExpectAndReturn("get_LogLevel", LogLevel.None); this.Logger.RegisterLogHandler((ILogHandler)this.Handler.MockInstance); @@ -101,11 +107,21 @@ namespace SimpleLog.Tests { this.Handler.ExpectNoCall("Log"); this.Logger.RegisterLogHandler((ILogHandler)this.Handler.MockInstance); + //h4x0rz! + this.Handler.ExpectAndReturn("get_LogLevel", LogLevel.None); Assert.That(this.Logger.Log("yay", LogLevel.Debug)); this.Handler.Verify(); msgHandler.Verify(); } + [Test] + public void TestLoadConfig() { + this.TempFile = System.IO.Path.GetTempFileName(); + + + + } + } } diff --git a/SimpleLog.Tests/LoggerManagerTest.cs b/SimpleLog.Tests/LoggerManagerTest.cs index 7042aa7..a685fff 100644 --- a/SimpleLog.Tests/LoggerManagerTest.cs +++ b/SimpleLog.Tests/LoggerManagerTest.cs @@ -1,23 +1,27 @@ using NUnit.Framework; using NUnit.Framework.SyntaxHelpers; -using SimpleLog.Framework; namespace SimpleLog.Tests { [TestFixture] public class LoggerManagerTest { + [SetUp] + public void SetUp() { + LoggerManager.LoadConfig(null); + } + [Test] public void TestGetLogger() { - DefaultLogger logger = LoggerManager.GetLogger(); + DefaultLogger logger = LoggerManager.GetLogger(typeof(DefaultLogger)) as DefaultLogger; Assert.That(logger, Is.TypeOf(typeof(DefaultLogger))); - DefaultLogger logger2 = LoggerManager.GetLogger(); + DefaultLogger logger2 = LoggerManager.GetLogger(typeof(DefaultLogger)) as DefaultLogger; Assert.That(logger2, Is.TypeOf(typeof(DefaultLogger))); } [Test] [ExpectedException("System.ArgumentException")] public void TestGetLoggerThrowsArgumentException() { - LoggerManager.GetLogger(); + LoggerManager.GetLogger(typeof(object)); } } diff --git a/SimpleLog.Tests/SimpleLog.Tests.csproj b/SimpleLog.Tests/SimpleLog.Tests.csproj index 232dcf3..64b8072 100644 --- a/SimpleLog.Tests/SimpleLog.Tests.csproj +++ b/SimpleLog.Tests/SimpleLog.Tests.csproj @@ -6,12 +6,13 @@ 9.0.30729 2.0 {4ECCF9B6-8A33-4D17-97EA-BA2D9D9178DD} - Library + Exe Properties SimpleLog.Tests SimpleLog.Tests v3.5 512 + SimpleLog.Tests.ConsoleColorTest true @@ -26,7 +27,8 @@ none true bin\Release\ - + + prompt 4 @@ -34,8 +36,13 @@ + + + + + @@ -47,6 +54,11 @@ SimpleLog + + + Always + + diff --git a/SimpleLog.Tests/Test.config b/SimpleLog.Tests/Test.config new file mode 100644 index 0000000..0c75d4f --- /dev/null +++ b/SimpleLog.Tests/Test.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleLog.targets b/SimpleLog.targets index 2df48db..b6863cc 100644 --- a/SimpleLog.targets +++ b/SimpleLog.targets @@ -6,7 +6,7 @@ Tommy Montgomery Copyright (C) 2009 Tommy Montgomery 1 - 2 + 3 0 0 Properties @@ -56,4 +56,10 @@ - \ No newline at end of file + + + + + + + diff --git a/SimpleLog/SimpleLog.config b/SimpleLog/SimpleLog.config new file mode 100644 index 0000000..ceb6c46 --- /dev/null +++ b/SimpleLog/SimpleLog.config @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SimpleLog/SimpleLog.csproj b/SimpleLog/SimpleLog.csproj index 3b106dc..576067f 100644 --- a/SimpleLog/SimpleLog.csproj +++ b/SimpleLog/SimpleLog.csproj @@ -34,20 +34,39 @@ prompt 4 - bin\Release\SimpleLog.XML + bin\Release\SimpleLog.xml + + + - + + + - - - + + + + + + 3.5 + + + + 3.5 + + + + + + Always + diff --git a/SimpleLog/Src/LogHandlers/ConsoleLogHandler.cs b/SimpleLog/Src/ConsoleLogHandler.cs similarity index 87% rename from SimpleLog/Src/LogHandlers/ConsoleLogHandler.cs rename to SimpleLog/Src/ConsoleLogHandler.cs index 78183f2..b8b4708 100644 --- a/SimpleLog/Src/LogHandlers/ConsoleLogHandler.cs +++ b/SimpleLog/Src/ConsoleLogHandler.cs @@ -1,7 +1,7 @@ using System; using SimpleLog.Framework; -namespace SimpleLog.LogHandlers { +namespace SimpleLog { /// /// Log handler for console output (e.g. use this instead of Console.WriteLine()). @@ -102,13 +102,21 @@ namespace SimpleLog.LogHandlers { /// The log level /// One of the STD_* constants protected virtual uint GetStreamType(LogLevel level) { - if (level >= SimpleLog.Framework.LogLevel.Warning) { + if ((level & SimpleLog.Framework.LogLevel.Default) > 0) { return STD_ERROR_HANDLE; } return STD_OUTPUT_HANDLE; } + /// + /// Applies a configuration object. There is no configuration defined + /// for this log handler. + /// + public virtual void ApplyConfig(LogHandlerConfig config) { + + } + } } diff --git a/SimpleLog/Src/ConsoleWindowLogHandler.cs b/SimpleLog/Src/ConsoleWindowLogHandler.cs new file mode 100644 index 0000000..c0b961b --- /dev/null +++ b/SimpleLog/Src/ConsoleWindowLogHandler.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using SimpleLog.Framework; + +namespace SimpleLog { + + /// + /// Log handler for writing log messages to a console window (e.g. cmd.exe). + /// + /// The colors of the various log levels are defined in GetConsoleColor(). + /// + public class ConsoleWindowLogHandler : ConsoleLogHandler { + + private IDictionary colors; + + #region Constructors + /// + /// Constructs a new ConsoleWindowLogHandler with the default title + /// and width + /// + public ConsoleWindowLogHandler() : this("SimpleLog Console Window") { + + } + + /// + /// Constructs a new ConsoleWindowLogHandler with the specified title + /// and default width + /// + /// Title of the console window + public ConsoleWindowLogHandler(string title) : this(title, 100) { + + } + + /// + /// Constructs a new ConsoleWindowLogHandler with a user defined + /// title and width + /// + /// Title of the console window + /// Width of the console window + public ConsoleWindowLogHandler(string title, int width) : base() { + Win32.AllocConsole(); + Console.Title = title; + Console.WindowWidth = width; + this.Colors = new Dictionary() { + {"Debug", ConsoleColor.DarkGreen}, + {"Info", ConsoleColor.Gray}, + {"Warning", ConsoleColor.Yellow}, + {"Error", ConsoleColor.Red}, + {"Fatal", ConsoleColor.White + ((int)ConsoleColor.Red << 4)} + }; + } + #endregion + + /// + /// Gets the console handler + /// + /// Either stderr or stdout + protected IntPtr GetConsoleHandle(uint streamType) { + return Win32.GetStdHandle(streamType); + } + + /// + /// Gets the colors for the console based on the given log level. + /// + /// Override this method in your own class if you want to modify the + /// colors. Ideally these would be set by some config file, but + /// that can come later. + /// + protected virtual ConsoleColor GetConsoleColors(LogLevel level) { + switch (level) { + case SimpleLog.Framework.LogLevel.Debug: + return this.Colors["Debug"]; + case SimpleLog.Framework.LogLevel.Info: + return this.Colors["Info"]; + case SimpleLog.Framework.LogLevel.Warning: + return this.Colors["Warning"]; + case SimpleLog.Framework.LogLevel.Error: + return this.Colors["Error"]; + case SimpleLog.Framework.LogLevel.Fatal: + return this.Colors["Fatal"]; + default: + throw new IndexOutOfRangeException("Unknown log level"); + } + } + + /// + /// Gets the colors associated with each log level + /// + public IDictionary Colors { + get { + return this.colors; + } + private set { + this.colors = value; + } + } + + #region Overrides + /// + /// Logs a message to the console window + /// + /// The message to log + /// The log level of the message + public override bool Log(string message, LogLevel level) { + uint streamType = this.GetStreamType(level); + System.IO.TextWriter writer = this.GetOutputStream(streamType); + IntPtr consoleHandle = GetConsoleHandle(streamType); + + //get the original colors of the console + Win32.CONSOLE_SCREEN_BUFFER_INFO bufferInfo; + Win32.GetConsoleScreenBufferInfo(consoleHandle, out bufferInfo); + + ushort colorInfo = (ushort)this.GetConsoleColors(level); + + //set the console colors + Win32.SetConsoleTextAttribute(consoleHandle, colorInfo); + + //write the message to the console + writer.Write(message); + + //reset the console colors + Win32.SetConsoleTextAttribute(consoleHandle, bufferInfo.wAttributes); + + return true; + } + + /// + /// Applies the given configuration to the log handler + /// + /// + /// Each setting in the config file maps a log level to one of + /// the System.ConsoleColor constants. The Name attribute should + /// be one of the LogLevel constants (e.g. "Debug") followed by + /// either "BackColor" or "ForeColor". The Value attribute is + /// any one of the System.ConsoleColor enum names (e.g. "DarkGreen"). + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// A log handler configuration + public override void ApplyConfig(LogHandlerConfig config) { + foreach (KeyValuePair setting in config.Settings) { + if (setting.Key.EndsWith("ForeColor")) { + string level = setting.Key.Replace("ForeColor", ""); + if (this.Colors.ContainsKey(level)) { + this.Colors[level] = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), setting.Value); + } + } + else if (setting.Key.EndsWith("BackColor")) { + string level = setting.Key.Replace("BackColor", ""); + if (this.Colors.ContainsKey(level)) { + this.Colors[level] = this.Colors[level] + ((int)(ConsoleColor)Enum.Parse(typeof(ConsoleColor), setting.Value) << 4); + } + } + } + } + #endregion + + } +} diff --git a/SimpleLog/Src/DefaultLogger.cs b/SimpleLog/Src/DefaultLogger.cs index bb718ab..326964d 100644 --- a/SimpleLog/Src/DefaultLogger.cs +++ b/SimpleLog/Src/DefaultLogger.cs @@ -86,7 +86,7 @@ namespace SimpleLog { LogLevel allowedLevel; IMessageHandler messageHandler; foreach (ILogHandler handler in this.logHandlers) { - allowedLevel = (handler.LogLevel == SimpleLog.Framework.LogLevel.None) ? (LogLevel)this.LogLevel : handler.LogLevel; + allowedLevel = (handler.LogLevel == SimpleLog.Framework.LogLevel.None) ? this.LogLevel : handler.LogLevel; if ((level & allowedLevel) > 0) { messageHandler = handler.MessageHandler ?? this.MessageHandler; messageHandler.DateFormat = messageHandler.DateFormat ?? this.DateFormat; @@ -109,20 +109,6 @@ namespace SimpleLog { this.logHandlers.Add(handler); } - /// - /// Unregisters all log handlers that are instances or - /// derived instances of the specified class - /// - /// The fully qualified class name (e.g. SimpleLog.Framework.ILogHandler) - /// The number of log handlers that were removed - public int UnregisterLogHandlerType(string qualifiedClassName) { - return this.logHandlers.RemoveAll( - delegate(ILogHandler handler) { - return (handler.GetType().FullName == qualifiedClassName); - } - ); - } - /// /// Unregisters all log handlers /// @@ -184,6 +170,20 @@ namespace SimpleLog { this.lineTerminator = value; } } + + /// + /// Applies the given configuration to this logger + /// + /// A configuration + public void ApplyConfig(LoggerConfig config) { + foreach (KeyValuePair pair in config.HandlerData) { + string className = pair.Key; + LogHandlerConfig handlerConfig = pair.Value; + ILogHandler handler = (ILogHandler)Activator.CreateInstance(Type.GetType(className)); + handler.ApplyConfig(handlerConfig); + this.RegisterLogHandler(handler); + } + } #endregion #region Accessors @@ -210,6 +210,20 @@ namespace SimpleLog { #endregion #region Convenience methods + /// + /// Unregisters all log handlers that are instances or + /// derived instances of the specified class + /// + /// The fully qualified class name (e.g. SimpleLog.ILogHandler) + /// The number of log handlers that were removed + public int UnregisterLogHandlerType(string qualifiedClassName) { + return this.logHandlers.RemoveAll( + delegate(ILogHandler handler) { + return (handler.GetType().FullName == qualifiedClassName); + } + ); + } + /// /// Logs a debug message /// @@ -241,8 +255,8 @@ namespace SimpleLog { /// /// Logs a critical message /// - public bool Critical(object message) { - return this.Log(message, LogLevel.Critical); + public bool Fatal(object message) { + return this.Log(message, LogLevel.Fatal); } #endregion diff --git a/SimpleLog/Src/LogHandlers/FileLogHandler.cs b/SimpleLog/Src/FileLogHandler.cs similarity index 83% rename from SimpleLog/Src/LogHandlers/FileLogHandler.cs rename to SimpleLog/Src/FileLogHandler.cs index 8ab6efa..e4d6cd5 100644 --- a/SimpleLog/Src/LogHandlers/FileLogHandler.cs +++ b/SimpleLog/Src/FileLogHandler.cs @@ -1,8 +1,9 @@ using System; +using System.Collections.Generic; using System.IO; using SimpleLog.Framework; -namespace SimpleLog.LogHandlers { +namespace SimpleLog { /// /// Log handler for filesystem-based logging /// @@ -23,7 +24,7 @@ namespace SimpleLog.LogHandlers { protected string logDirectory; /// /// The string that prepends each log file name (e.g. - /// in logger_20090417.log, "logger" is the filePrefix) + /// in logger_20090417.log, "logger_" is the filePrefix) /// protected string filePrefix; /// @@ -49,7 +50,7 @@ namespace SimpleLog.LogHandlers { /// The file prefix /// The file suffix (e.g. ".log") public FileLogHandler(string dir, string prefix, string suffix) { - this.messageHandler = new DefaultMessageHandler(); + this.MessageHandler = new DefaultMessageHandler(); this.LogLevel = SimpleLog.Framework.LogLevel.None; this.LogDirectory = dir; this.FilePrefix = prefix; @@ -114,7 +115,7 @@ namespace SimpleLog.LogHandlers { /// protected virtual string BuildFileName() { string fileName = this.logDirectory + Path.DirectorySeparatorChar + this.FilePrefix; - fileName += "_" + string.Format("{0:yyyyMMdd}", DateTime.Now) + this.FileSuffix; + fileName += string.Format("{0:yyyyMMdd}", DateTime.Now) + this.FileSuffix; return fileName; } @@ -153,6 +154,15 @@ namespace SimpleLog.LogHandlers { this.fileSuffix = value; } } + + /// + /// Applies the configuration to this log handler + /// + public void ApplyConfig(LogHandlerConfig config) { + foreach (KeyValuePair setting in config.Settings) { + Util.SetPropertyDynamically(this, setting.Key, setting.Value); + } + } } } diff --git a/SimpleLog/Src/Framework/Config.cs b/SimpleLog/Src/Framework/Config.cs new file mode 100644 index 0000000..3aa8fbf --- /dev/null +++ b/SimpleLog/Src/Framework/Config.cs @@ -0,0 +1,93 @@ +using System; +using System.Xml; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SimpleLog.Framework { + /// + /// Global SimpleLog configuration object + /// + public sealed class Config { + + private IDictionary loggerData; + + #region Constructors + /// + /// Constructs a new LoggerConfig object with no initial data + /// + public Config() { + this.LoggerData = new Dictionary(); + } + + /// + /// Constructs a new LoggerConfig object initialized with + /// the data provided by the given configuration file + /// + /// Configuration file to load + public Config(string configFile) : this() { + this.Load(configFile); + } + #endregion + + /// + /// Loads a configuration file + /// + /// Path to the config file to load + public void Load(string configFile) { + XmlDocument document = new XmlDocument(); + document.Load(configFile); + + XmlNodeList loggers = document.GetElementsByTagName("Logger"); + foreach (XmlElement element in loggers) { + string className = element.GetAttribute("Class"); + this.LoggerData[className] = new LoggerConfig(); + + XmlNodeList h = element.GetElementsByTagName("LogHandlers"); + if (h.Count > 0) { + foreach (XmlElement handler in ((XmlElement)h[0]).GetElementsByTagName("LogHandler")) { + string handlerClass = handler.GetAttribute("Class"); + XmlNodeList settings = handler.GetElementsByTagName("Setting"); + LogHandlerConfig config = new LogHandlerConfig(settings); + this.LoggerData[className].AddHandler(handlerClass, config); + } + } + } + } + + /// + /// Gets a LoggerConfig for the given name + /// + /// Fully qualified name of ILogger implementation + /// + public LoggerConfig GetLoggerConfig(string loggerName) { + if (!this.HasLoggerConfig(loggerName)) { + throw new KeyNotFoundException("The logger " + loggerName + " has no configuration"); + } + + return this.LoggerData[loggerName]; + } + + /// + /// Determines whether a config object has been registered for the specified logger + /// + /// The qualified name of the ILogger implementation + public bool HasLoggerConfig(string loggerName) { + return this.LoggerData.ContainsKey(loggerName); + } + + /// + /// Gets or sets the logger data + /// + private IDictionary LoggerData { + get { + return this.loggerData; + } + set { + this.loggerData = value; + } + } + + } +} diff --git a/SimpleLog/Src/Framework/ILogHandler.cs b/SimpleLog/Src/Framework/ILogHandler.cs index 8f14cde..c767f35 100644 --- a/SimpleLog/Src/Framework/ILogHandler.cs +++ b/SimpleLog/Src/Framework/ILogHandler.cs @@ -30,5 +30,10 @@ /// IMessageHandler MessageHandler { get; set; } + /// + /// Applies a configuration to the log handler + /// + void ApplyConfig(LogHandlerConfig config); + } } diff --git a/SimpleLog/Src/Framework/ILogger.cs b/SimpleLog/Src/Framework/ILogger.cs index 20314ab..353b753 100644 --- a/SimpleLog/Src/Framework/ILogger.cs +++ b/SimpleLog/Src/Framework/ILogger.cs @@ -15,12 +15,6 @@ /// The log handler to register void RegisterLogHandler(ILogHandler handler); - /// - /// Removes all log handlers that match the given assembly name - /// - /// Number of log handlers that were unregistered - int UnregisterLogHandlerType(string assemblyName); - /// /// Unregisters all registered log handlers /// @@ -45,5 +39,11 @@ /// IMessageHandler MessageHandler { get; set; } + /// + /// Applies the given configuration to the logger + /// + /// A logger configuration + void ApplyConfig(LoggerConfig config); + } } diff --git a/SimpleLog/Src/Framework/LogHandlerConfig.cs b/SimpleLog/Src/Framework/LogHandlerConfig.cs new file mode 100644 index 0000000..46e400a --- /dev/null +++ b/SimpleLog/Src/Framework/LogHandlerConfig.cs @@ -0,0 +1,76 @@ +using System.Xml; +using System.Collections.Generic; + +namespace SimpleLog.Framework { + /// + /// Configuration object for log handlers + /// + public sealed class LogHandlerConfig { + + private IDictionary settings; + + /// + /// Constructs a new log handler configuration object + /// with default settings + /// + public LogHandlerConfig() { + this.Settings = new Dictionary(); + } + + /// + /// Constructs a new log handler configuration object initialized + /// with the given settings + /// + /// An XML node list containing settings information + public LogHandlerConfig(XmlNodeList settings) : this() { + this.LoadSettings(settings); + } + + /// + /// Loads settings from XML + /// + /// An XML node list containing settings information + /// If the XML element does not have the "Name" and "Value" attributes + public void LoadSettings(XmlNodeList settings) { + foreach (XmlElement element in settings) { + if (!element.HasAttribute("Name") || !element.HasAttribute("Value")) { + throw new XmlException("LogHandler tag must have Name and Value attributes"); + } + + this.Settings[element.GetAttribute("Name")] = element.GetAttribute("Value"); + } + } + + /// + /// Gets whether the specified setting is defined + /// + public bool HasSetting(string key) { + return this.Settings.ContainsKey(key); + } + + /// + /// Gets the setting specified by + /// + /// + public string GetSetting(string key) { + if (!this.HasSetting(key)) { + throw new KeyNotFoundException("The key " + key + " does not exist"); + } + + return this.Settings[key]; + } + + /// + /// Gets the settings of this configuration + /// + public IDictionary Settings { + get { + return this.settings; + } + private set { + this.settings = value; + } + } + + } +} diff --git a/SimpleLog/Src/Framework/LoggerConfig.cs b/SimpleLog/Src/Framework/LoggerConfig.cs new file mode 100644 index 0000000..3f50858 --- /dev/null +++ b/SimpleLog/Src/Framework/LoggerConfig.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SimpleLog.Framework { + /// + /// Configuratino object for ILogger implementations + /// + public sealed class LoggerConfig { + + private IDictionary handlerData; + + /// + /// Constructs a new LoggerConfig object + /// + public LoggerConfig() { + this.HandlerData = new Dictionary(); + } + + /// + /// Adds a log handler key/value pair + /// + /// Qualified name of the ILogHandler implementation (e.g. "SimpleLog.FileLogHandler") + /// A configuration object + public void AddHandler(string className, LogHandlerConfig config) { + this.HandlerData.Add(className, config); + } + + /// + /// Gets the log handler data associated with this config object + /// + public IDictionary HandlerData { + get { + return this.handlerData; + } + private set { + this.handlerData = value; + } + } + + } +} diff --git a/SimpleLog/Src/Framework/LoggerManager.cs b/SimpleLog/Src/Framework/LoggerManager.cs deleted file mode 100644 index 5a601fd..0000000 --- a/SimpleLog/Src/Framework/LoggerManager.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Reflection; -using System.Collections.Generic; - -namespace SimpleLog.Framework { - /// - /// Manages ILogger instances in a singletonish way and provides - /// a single point of entry to creating loggers. If singleton - /// behavior is not desired, just instantiate each logger class - /// normally. - /// - public static class LoggerManager { - - /// - /// Registry of ILogger instances - /// - private static Dictionary registry = new Dictionary(); - - /// - /// Gets a logger from the registry. - /// This will reuse a logger that has already been instantiated, - /// or create a new instance and stick it in the registry for - /// later use. - /// - /// The specified class must implement the SimpleLog.Framework.ILogger - /// interface - /// - /// - /// DefaultLogger logger = LogManager.GetLogger{DefaultLogger}(); - /// Must implement the SimpleLog.Framework.ILogger interface - /// - public static T GetLogger() { - Type type = typeof(T); - string index = type.FullName; - - if (LoggerManager.registry.ContainsKey(index)) { - ILogger logger; - LoggerManager.registry.TryGetValue(index, out logger); - return (T)logger; - } - - //Type interfaceType = type.GetInterface("ILogger"); - if (type.GetInterface("ILogger") != null && type.IsClass) { - return (T)Activator.CreateInstance(type); - } - - throw new ArgumentException("The type specified (" + index + ") does not implement the " + (typeof(ILogger).FullName) + " interface"); - } - - } -} diff --git a/SimpleLog/Src/Framework/Util.cs b/SimpleLog/Src/Framework/Util.cs index f1f5a8e..503360a 100644 --- a/SimpleLog/Src/Framework/Util.cs +++ b/SimpleLog/Src/Framework/Util.cs @@ -1,12 +1,15 @@ using System; +using System.Reflection; +using System.Runtime.InteropServices; namespace SimpleLog.Framework { + #region Structs and enumerations /// /// Log level enumeration /// [Flags] - public enum LogLevel { + public enum LogLevel : int { /// /// No log level specified /// @@ -30,23 +33,19 @@ namespace SimpleLog.Framework { /// /// Critical death level only /// - Critical = 16, - /// - /// Preferred alias of Critical - /// - OhNoes = 16, + Fatal = 16, /// /// Debug and Info only /// LowPriority = Debug | Info, /// - /// Default log level: Warning, Error and Critical + /// Default log level: Warning, Error and Fatal /// - Default = Warning | Error | Critical, + Default = Warning | Error | Fatal, /// /// All messages are logged /// - All = Debug | Info | Warning | Error | Critical + All = Debug | Info | Warning | Error | Fatal } /// @@ -56,17 +55,120 @@ namespace SimpleLog.Framework { /// /// Windows line terminator (CRLF) /// - public static readonly string Windows = "\r\n"; + public const string Windows = "\r\n"; /// /// Macintosh line terminator (CR) /// - public static readonly string Mac = "\r"; + public const string Mac = "\r"; /// /// Unix line terminator (LF) /// - public static readonly string Unix = "\n"; + public const string Unix = "\n"; } + #endregion + #region Win32 API stuff + /// + /// Win32 API stuff + /// + public static class Win32 { + /// + /// 16-bit coordinate struct + /// + [StructLayout(LayoutKind.Sequential)] + public struct COORD { + /// + /// x-coordinate + /// + public UInt16 x; + /// + /// y-coordinate + /// + public UInt16 y; + } + + /// + /// 16-bit rectangle struct + /// + [StructLayout(LayoutKind.Sequential)] + public struct SMALL_RECT { + /// + /// Left side of the rectangle + /// + public UInt16 Left; + /// + /// Top of the rectangle + /// + public UInt16 Top; + /// + /// Right side of the rectangle + /// + public UInt16 Right; + /// + /// Bottom of the rectangle + /// + public UInt16 Bottom; + } + + /// + /// Console screen buffer info struct + /// + [StructLayout(LayoutKind.Sequential)] + public struct CONSOLE_SCREEN_BUFFER_INFO { + /// + /// Location the console screen + /// + public COORD dwSize; + /// + /// Location of the cursor + /// + public COORD dwCursorPosition; + /// + /// Meta information of the console + /// + public ushort wAttributes; + /// + /// Bounds of the console window + /// + public SMALL_RECT srWindow; + /// + /// Maximum size of the window + /// + public COORD dwMaximumWindowSize; + } + + /// + /// Allocates a console window + /// + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool AllocConsole(); + + /// + /// Gets the output pointer based on the stream type (stderr or stdout) + /// + /// One of the STD_* constants + [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr GetStdHandle(UInt32 type); + + /// + /// Sets the color of the text to be written to the console + /// + /// A console handle returned by GetStdHandle() + /// Bitwise pairing of ConsoleColors + [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool SetConsoleTextAttribute(IntPtr consoleHandle, ushort attributes); + + /// + /// Gets info about the console window screen + /// + /// A console handle return by GetStdHandle() + /// + [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool GetConsoleScreenBufferInfo(IntPtr consoleHandle, out CONSOLE_SCREEN_BUFFER_INFO bufferInfo); + } + #endregion + + #region Utility class /// /// Utility class /// @@ -77,10 +179,35 @@ namespace SimpleLog.Framework { /// public static bool LineTerminatorIsValid(string lineTerminator) { return - lineTerminator == LineTerminator.Unix || + lineTerminator == LineTerminator.Unix || lineTerminator == LineTerminator.Windows || lineTerminator == LineTerminator.Mac; } + /// + /// Sets a property dynamically through reflection, if that property exists + /// + /// The object with the property to set + /// The property name to set + /// The value to set the property to + /// TRUE if successfully set, FALSE otherwise + public static bool SetPropertyDynamically(object source, string propertyName, object value) { + PropertyInfo prop = source.GetType().GetProperty(propertyName); + if (prop != null) { + MethodInfo method = prop.GetSetMethod(false); + if (method != null) { + try { + //value = Convert.ChangeType(value, prop.PropertyType); + method.Invoke(source, new object[] { value }); + return true; + } + catch { } + } + } + return false; + } + } + #endregion + } diff --git a/SimpleLog/Src/LogHandlers/ConsoleWindowLogHandler.cs b/SimpleLog/Src/LogHandlers/ConsoleWindowLogHandler.cs deleted file mode 100644 index ba81261..0000000 --- a/SimpleLog/Src/LogHandlers/ConsoleWindowLogHandler.cs +++ /dev/null @@ -1,202 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using SimpleLog.Framework; - -namespace SimpleLog.LogHandlers { - - /// - /// Colors for console output. Stolen from log4net.ColoredConsoleAppender (verbatim). - /// - [Flags] - public enum ConsoleColor : int { - /// - /// The color blue - /// - Blue = 0x0001, - /// - /// The color green - /// - Green = 0x0002, - /// - /// The color red - /// - Red = 0x0004, - /// - /// The color white (Red, Green and Blue combined) - /// - White = Blue | Green | Red, - /// - /// The color yellow (Red and Green combined) - /// - Yellow = Red | Green, - /// - /// The color purple (Red and Blue combined) - /// - Purple = Red | Blue, - /// - /// The color cyan (Green and Blue combined) - /// - Cyan = Green | Blue, - /// - /// Brightens a color - /// - HighIntensity = 0x0008, - } - - /// - /// Log handler for writing log messages to a console window (e.g. cmd.exe). - /// - /// The colors of the various log levels are defined in GetConsoleColor(). - /// - public class ConsoleWindowLogHandler : ConsoleLogHandler { - - #region Constructors - /// - /// Constructs a new ConsoleWindowLogHandler with the default title - /// and width - /// - public ConsoleWindowLogHandler() : this("SimpleLog Console Window") { - - } - - /// - /// Constructs a new ConsoleWindowLogHandler with the specified title - /// and default width - /// - /// Title of the console window - public ConsoleWindowLogHandler(string title) : this(title, 100) { - - } - - /// - /// Constructs a new ConsoleWindowLogHandler with a user defined - /// title and width - /// - /// Title of the console window - /// Width of the console window - public ConsoleWindowLogHandler(string title, int width) : base() { - ConsoleWindowLogHandler.AllocConsole(); - Console.Title = title; - Console.WindowWidth = width; - } - #endregion - - /// - /// Gets the console handler - /// - /// Either stderr or stdout - protected IntPtr GetConsoleHandle(uint streamType) { - return ConsoleWindowLogHandler.GetStdHandle(streamType); - } - - /// - /// Gets the colors for the console based on the given log level. - /// - /// Override this method in your own class if you want to modify the - /// colors. Ideally these would be set by some config file, but - /// that can come later. - /// - protected virtual ConsoleColor GetConsoleColors(LogLevel level) { - switch (level) { - case SimpleLog.Framework.LogLevel.Debug: - return ConsoleColor.Green; - case SimpleLog.Framework.LogLevel.Info: - return ConsoleColor.White; - case SimpleLog.Framework.LogLevel.Warning: - return ConsoleColor.Yellow | ConsoleColor.HighIntensity; - case SimpleLog.Framework.LogLevel.Error: - return ConsoleColor.Red | ConsoleColor.HighIntensity; - case SimpleLog.Framework.LogLevel.Critical: - return (ConsoleColor.White | ConsoleColor.HighIntensity) + ((int)(ConsoleColor.Red | ConsoleColor.HighIntensity) << 4); - default: - return ConsoleColor.White; - } - } - - #region Overrides - /// - /// Logs a message to the console window - /// - /// The message to log - /// The log level of the message - public override bool Log(string message, LogLevel level) { - uint streamType = this.GetStreamType(level); - System.IO.TextWriter writer = this.GetOutputStream(streamType); - IntPtr consoleHandle = GetConsoleHandle(streamType); - - //get the original colors of the console - CONSOLE_SCREEN_BUFFER_INFO bufferInfo; - GetConsoleScreenBufferInfo(consoleHandle, out bufferInfo); - - - ushort colorInfo = (ushort)this.GetConsoleColors(level); - - //set the console colors - ConsoleWindowLogHandler.SetConsoleTextAttribute(consoleHandle, colorInfo); - - //write the message to the console - writer.Write(message); - - //reset the console colors - ConsoleWindowLogHandler.SetConsoleTextAttribute(consoleHandle, bufferInfo.wAttributes); - - return true; - } - #endregion - - #region Win32 API stuff - [StructLayout(LayoutKind.Sequential)] - private struct COORD { - public UInt16 x; - public UInt16 y; - } - - [StructLayout(LayoutKind.Sequential)] - private struct SMALL_RECT { - public UInt16 Left; - public UInt16 Top; - public UInt16 Right; - public UInt16 Bottom; - } - - [StructLayout(LayoutKind.Sequential)] - private struct CONSOLE_SCREEN_BUFFER_INFO { - public COORD dwSize; - public COORD dwCursorPosition; - public ushort wAttributes; - public SMALL_RECT srWindow; - public COORD dwMaximumWindowSize; - } - - /// - /// Allocates a console window - /// - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool AllocConsole(); - - /// - /// Gets the output pointer based on the stream type (stderr or stdout) - /// - /// One of the STD_* constants - [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] - private static extern IntPtr GetStdHandle(UInt32 type); - - /// - /// Sets the color of the text to be written to the console - /// - /// A console handle returned by GetStdHandle() - /// Bitwise pairing of ConsoleColors - [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] - private static extern bool SetConsoleTextAttribute(IntPtr consoleHandle, ushort attributes); - - /// - /// Gets info about the console window screen - /// - /// A console handle return by GetStdHandle() - /// - [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] - private static extern bool GetConsoleScreenBufferInfo(IntPtr consoleHandle, out CONSOLE_SCREEN_BUFFER_INFO bufferInfo); - #endregion - - } -} diff --git a/SimpleLog/Src/LoggerManager.cs b/SimpleLog/Src/LoggerManager.cs new file mode 100644 index 0000000..2f67ab7 --- /dev/null +++ b/SimpleLog/Src/LoggerManager.cs @@ -0,0 +1,113 @@ +using System; +using System.Xml; +using System.Reflection; +using System.Collections.Generic; +using SimpleLog.Framework; + +namespace SimpleLog { + /// + /// Manages ILogger instances in a singletonish way and provides + /// a single point of entry to creating loggers. If singleton + /// behavior is not desired, just instantiate each logger class + /// normally. + /// + public static class LoggerManager { + + /// + /// Registry of ILogger instances + /// + private static IDictionary Registry = new Dictionary(); + + /// + /// Global configuration object for all loggers + /// + private static Config config = null; + + /// + /// Default location of the configuration file + /// + public const string DEFAULT_CONFIG_LOCATION = "SimpleLog.config"; + + /// + /// Sets up the LoggerConfig if one hasn't already been set by an + /// external source + /// + static LoggerManager() { + if (LoggerManager.Config == null) { + if (System.IO.File.Exists(LoggerManager.DEFAULT_CONFIG_LOCATION)) { + LoggerManager.LoadConfig(LoggerManager.DEFAULT_CONFIG_LOCATION); + } + else { + LoggerManager.LoadConfig(null); + } + } + } + + /// + /// Loads or reloads the global configuration from a file + /// + /// Path to the configuration file, or null + public static void LoadConfig(string configFile) { + if (configFile != null) { + LoggerManager.Config = new Config(configFile); + } + else { + LoggerManager.Config = new Config(); + } + + } + + /// + /// Gets a logger from the registry. + /// This will reuse a logger that has already been instantiated, + /// or create a new instance and stick it in the registry for + /// later use. + /// + /// The specified class must implement the SimpleLog.Framework.ILogger + /// interface + /// + /// + /// DefaultLogger logger = (DefaultLogger)LoggerManager.GetLogger(typeof(DefaultLogger)); + /// The type of logger to create + /// + public static ILogger GetLogger(Type type) { + string loggerName = type.FullName; + + //if the logger has already been instantiated, return it + if (LoggerManager.Registry.ContainsKey(loggerName)) { + return LoggerManager.Registry[loggerName]; + } + + if (type.GetInterface("ILogger") != null) { + if (type.IsClass && !type.IsAbstract) { + ILogger logger = (ILogger)Activator.CreateInstance(type); + + if (LoggerManager.Config.HasLoggerConfig(loggerName)) { + LoggerConfig loggerConfig = LoggerManager.Config.GetLoggerConfig(loggerName); + logger.ApplyConfig(loggerConfig); + } + return logger; + } + else { + throw new ArgumentException("The type specified (" + loggerName + ") is not an instantiable class"); + } + } + else { + throw new ArgumentException("The type specified (" + loggerName + ") does not implement the " + (typeof(ILogger).FullName) + " interface"); + } + } + + /// + /// Gets the global configuration object + /// + public static Config Config { + get { + return LoggerManager.config; + } + private set { + LoggerManager.config = value; + } + } + + } +} diff --git a/build_clean.bat b/build_clean.bat deleted file mode 100644 index 3912b9a..0000000 --- a/build_clean.bat +++ /dev/null @@ -1,20 +0,0 @@ -@echo off -if exist "SimpleLog/bin" ( - echo deleting SimpleLog/bin... - rmdir /S /Q "SimpleLog/bin" -) - -if exist "SimpleLog/obj" ( - echo deleting SimpleLog/obj... - rmdir /S /Q "SimpleLog/obj" -) - -if exist "SimpleLog.Tests/bin" ( - echo deleting SimpleLog.Tests/bin... - rmdir /S /Q "SimpleLog.Tests/bin" -) - -if exist "SimpleLog.Tests/obj" ( - echo deleting SimpleLog.Tests/obj... - rmdir /S /Q "SimpleLog.Tests/obj" -) \ No newline at end of file diff --git a/clean.bat b/clean.bat new file mode 100644 index 0000000..16fbc9f --- /dev/null +++ b/clean.bat @@ -0,0 +1 @@ +%WINDIR%\Microsoft.NET\Framework\v3.5\MSBuild.exe /target:Clean SimpleLog.sln \ No newline at end of file