using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; // Log4Net Declarations using log4net.Appender; using log4net.Core; using log4net.Layout; using log4net.Config; using log4net; using Diag = System.Diagnostics; using BridgeConnector.Lib.Other; namespace BridgeConnector.Lib.File { /// /// Logging Detail /// public enum Logging_Detail { NONE, ERROR, INFO, DEBUG } /// /// Used to Configure a Logger Instance in the Logging Class via AddGlobalLoggerConfiguration() /// public struct Logging_Configuration { public string LogFileNameNPath; public Logging_Detail Detail; public bool UseExclusiveFileLock; public int maxFileSizeInMB; public int numOfBackupLogFiles; public string PatternLayout; } /// /// Is a Wrapper Object around Log4Net's Rolling File Appender. /// Use it by calling AddGlobalLoggerConfiguration() with a valid Logging Configuration. /// You can configure multipe Logger Instances distinguished by Name. /// subsequent calls can call GetLogger() with the named Logger instance to receive a valid logger object /// public class Logging { #region Private Static Members private static Dictionary _loggerConfigurationMap = new Dictionary(); private static Dictionary _loggerObjects = new Dictionary(); private static bool s_IsVSHosted = false; #endregion #region Private Construction private Logging() { s_IsVSHosted = Diag.Process.GetCurrentProcess().ProcessName.Contains("vshost"); } #endregion #region Internal Members internal ILog _Log4NetLog = null; // Initialized by GetLogger() #endregion #region Public Log Methods public void Info(object message) { if (_Log4NetLog != null) _Log4NetLog.Info(MessageHeader() + message); } public void Info(object message, int nPlusMinus) { if (_Log4NetLog != null) _Log4NetLog.Info(MessageHeader(nPlusMinus) + message); } public void Info(object message, Exception exception) { if (_Log4NetLog != null) _Log4NetLog.Info(MessageHeader() + message, exception); } public void Debug(object message) { if (_Log4NetLog != null) _Log4NetLog.Debug(MessageHeader() + message); } public void Debug(object message, int nPlusMinus) { if (_Log4NetLog != null) _Log4NetLog.Debug(MessageHeader(nPlusMinus) + message); } public void Debug(object message, Exception exception) { if (_Log4NetLog != null) _Log4NetLog.Debug(MessageHeader() + message, exception); } public void Error(object message) { if (_Log4NetLog != null) _Log4NetLog.Error(MessageHeader() + message); } public void Error(object message, int nPlusMinus) { if (_Log4NetLog != null) _Log4NetLog.Error(MessageHeader(nPlusMinus) + message); } public void Error(object message, Exception exception) { if (_Log4NetLog != null) _Log4NetLog.Error(MessageHeader() + message, exception); } public void Fatal(object message) { if (_Log4NetLog != null) _Log4NetLog.Fatal(MessageHeader() + message); } public void Fatal(object message, int nPlusMinus) { if (_Log4NetLog != null) _Log4NetLog.Fatal(MessageHeader(nPlusMinus) + message); } public void Fatal(object message, Exception exception) { if (_Log4NetLog != null) _Log4NetLog.Fatal(MessageHeader() + message, exception); } /// /// Message Header to be shown on every log message /// private string MessageHeader() { if (s_IsVSHosted) return MessageHeader(0); else return StackWalker.GetMethodNameFromStack(-2) + "()- "; } /// /// Message Header to be shown on every log message /// /// Use this to add/substract from the base stack level you want to retrieve private string MessageHeader(int nPlusMinus) { // When Running this from via VS it behaves differently then when // running it outside of it, when running it regularly, each foreach loop // is it's own stackframe! ~weird but true. Only use the nPlusMinus when not running // inside VS if(s_IsVSHosted) return StackWalker.GetMethodNameFromStack(nPlusMinus) + "()- "; else return StackWalker.GetMethodNameFromStack(nPlusMinus - 1) + "()- "; } #endregion #region Public Static Configuration and Logger Creation Methods /// /// Used to add a new Configuration and Logger Instance onto a Static Map. /// Will create one logger instance per unique name. /// /// a name for the logger instance /// a valid configuration to use on the instance /// a logging object that can be used to log public static Logging AddGlobalLoggerConfiguration(string Name, Logging_Configuration Configuration) { // Must have a Valid Input if (string.IsNullOrEmpty(Name)) throw new ArgumentException("Name Is Invalid"); if (!_loggerObjects.Keys.Contains(Name.ToLower())) { // Create the Repository log4net.Repository.ILoggerRepository repository = LogManager.CreateRepository(Name.ToLower()); // Create FileAppender Configuration RollingFileAppender appender = RollingFileAppenderCreator(Configuration); // Run the Configuration against the Repository BasicConfigurator.Configure(repository, appender); // Add Configuration to our Static Map _loggerConfigurationMap[Name.ToLower()] = Configuration; // Last, but not least, Create the new Logging Instance Object and Store it _loggerObjects[Name.ToLower()] = LogManager.GetLogger(Name.ToLower(), Name.ToLower()); } // Let the Caller get the Logging Object return GetLogger(Name); } /// /// Used to retrieve a named logging instance that has already been created via a previous call /// to AddGlobalLoggerConfiguration(). /// /// a name for a previously created Logging instance /// a Logging object that can be used to log public static Logging GetLogger(string Name) { if (_loggerObjects.Keys.Contains(Name.ToLower())) { Logging logger = new Logging(); logger._Log4NetLog = _loggerObjects[Name.ToLower()]; return logger; } else throw new ArgumentException("Must call AddGlobalLoggerConfiguration() with a Configuration Before calling this Function"); } #endregion #region Private Static Helper Methods /// /// Creates a Log4Net RollingFileAppender with the specified configuration /// /// a valid configuration /// a Log4Net RollingFileAppender Object private static RollingFileAppender RollingFileAppenderCreator(Logging_Configuration config) { #region Input Validation if (config.maxFileSizeInMB <= 0) throw new ArgumentException("Logging_Configuration - Invalid maxFileSizeInMB"); if (config.numOfBackupLogFiles < 0) throw new ArgumentException("Logging_Configuration - Invalid numOfBackupLogFiles"); if (String.IsNullOrEmpty(config.LogFileNameNPath)) throw new Exception("Logging_Configuration - Invalid LogFileNameNPath"); if (!Directory.Exists(Path.GetDirectoryName(config.LogFileNameNPath))) Directory.CreateDirectory(Path.GetDirectoryName(config.LogFileNameNPath)); #endregion // Create and Set Layout for FileAppender RollingFileAppender rfAppender = new RollingFileAppender(); if (!String.IsNullOrEmpty(config.PatternLayout)) rfAppender.Layout = new PatternLayout(config.PatternLayout); else rfAppender.Layout = new PatternLayout("%date{dd MMM HH:mm:ss,fff} [%thread] %level - %message%newline"); //rfAppender.Layout = new PatternLayout("%date{dd MMM yyyy HH:mm:ss,fff} [%thread] %level %logger - %message%newline"); // Locking Minimal allows us to run Log4Net from multiple processes if (config.UseExclusiveFileLock) rfAppender.LockingModel = new FileAppender.ExclusiveLock(); else rfAppender.LockingModel = new FileAppender.MinimalLock(); // Configure FileName and always set Appent to true rfAppender.File = config.LogFileNameNPath; rfAppender.AppendToFile = true; // According to the listings on the blog site // http://blog.aggregatedintelligence.com/2009/08/log4net-logging-levels-available.html // Error, will log Error, Fatal // Info, will log Info, Error, and Fatal // Debug, will log Info, Error, Fatal and Debug if (config.Detail == Logging_Detail.NONE) rfAppender.Threshold = Level.Off; else if (config.Detail == Logging_Detail.ERROR) rfAppender.Threshold = Level.Error; else if (config.Detail == Logging_Detail.INFO) rfAppender.Threshold = Level.Info; else if (config.Detail == Logging_Detail.DEBUG) rfAppender.Threshold = Level.Debug; rfAppender.MaximumFileSize = String.Format("{0}MB", config.maxFileSizeInMB); rfAppender.MaxSizeRollBackups = config.numOfBackupLogFiles; rfAppender.RollingStyle = RollingFileAppender.RollingMode.Size; // Setting to RollingMode.Size will make MaxSizeRollBackups work rfAppender.ActivateOptions(); return rfAppender; } #endregion } }