diff options
Diffstat (limited to 'Emby.Common.Implementations/Logging/NlogManager.cs')
| -rw-r--r-- | Emby.Common.Implementations/Logging/NlogManager.cs | 450 |
1 files changed, 365 insertions, 85 deletions
diff --git a/Emby.Common.Implementations/Logging/NlogManager.cs b/Emby.Common.Implementations/Logging/NlogManager.cs index e38b87bd1..f7b723e8b 100644 --- a/Emby.Common.Implementations/Logging/NlogManager.cs +++ b/Emby.Common.Implementations/Logging/NlogManager.cs @@ -1,8 +1,10 @@ using System; using System.IO; using System.Linq; +using System.Xml; using NLog; using NLog.Config; +using NLog.Filters; using NLog.Targets; using NLog.Targets.Wrappers; using MediaBrowser.Model.Logging; @@ -14,20 +16,35 @@ namespace Emby.Common.Implementations.Logging /// </summary> public class NlogManager : ILogManager { - /// <summary> - /// Occurs when [logger loaded]. - /// </summary> - public event EventHandler LoggerLoaded; + #region Private Fields + + private LogSeverity _severity = LogSeverity.Debug; + /// <summary> /// Gets or sets the log directory. /// </summary> /// <value>The log directory.</value> - private string LogDirectory { get; set; } + private readonly string LogDirectory; + /// <summary> /// Gets or sets the log file prefix. /// </summary> /// <value>The log file prefix.</value> - private string LogFilePrefix { get; set; } + private readonly string LogFilePrefix; + + #endregion + + #region Event Declarations + + /// <summary> + /// Occurs when [logger loaded]. + /// </summary> + public event EventHandler LoggerLoaded; + + #endregion + + #region Public Properties + /// <summary> /// Gets the log file path. /// </summary> @@ -40,28 +57,25 @@ namespace Emby.Common.Implementations.Logging /// <value>The exception message prefix.</value> public string ExceptionMessagePrefix { get; set; } - /// <summary> - /// Initializes a new instance of the <see cref="NlogManager" /> class. - /// </summary> - /// <param name="logDirectory">The log directory.</param> - /// <param name="logFileNamePrefix">The log file name prefix.</param> - public NlogManager(string logDirectory, string logFileNamePrefix) - { - LogDirectory = logDirectory; - LogFilePrefix = logFileNamePrefix; + public string NLogConfigurationFilePath { get; set; } - LogManager.Configuration = new LoggingConfiguration (); - } - - private LogSeverity _severity = LogSeverity.Debug; public LogSeverity LogSeverity { + get { return _severity; } + set { + DebugFileWriter( + LogDirectory, String.Format( + "SET LogSeverity, _severity = [{0}], value = [{1}]", + _severity.ToString(), + value.ToString() + )); + var changed = _severity != value; _severity = value; @@ -70,11 +84,124 @@ namespace Emby.Common.Implementations.Logging { UpdateLogLevel(value); } + + } + } + + #endregion + + #region Constructor(s) + + /// <summary> + /// Initializes a new instance of the <see cref="NlogManager" /> class. + /// </summary> + /// <param name="logDirectory">The log directory.</param> + /// <param name="logFileNamePrefix">The log file name prefix.</param> + public NlogManager(string logDirectory, string logFileNamePrefix) + { + DebugFileWriter( + logDirectory, String.Format( + "NlogManager constructor called, logDirectory is [{0}], logFileNamePrefix is [{1}], _severity is [{2}].", + logDirectory, + logFileNamePrefix, + _severity.ToString() + )); + + LogDirectory = logDirectory; + LogFilePrefix = logFileNamePrefix; + + LogManager.Configuration = new LoggingConfiguration(); + } + + /// <summary> + /// Initializes a new instance of the <see cref="NlogManager" /> class. + /// </summary> + /// <param name="logDirectory">The log directory.</param> + /// <param name="logFileNamePrefix">The log file name prefix.</param> + public NlogManager(string logDirectory, string logFileNamePrefix, LogSeverity initialSeverity) : this(logDirectory, logFileNamePrefix) + { + _severity = initialSeverity; + + DebugFileWriter( + logDirectory, String.Format( + "NlogManager constructor called, logDirectory is [{0}], logFileNamePrefix is [{1}], _severity is [{2}].", + logDirectory, + logFileNamePrefix, + _severity.ToString() + )); + } + + #endregion + + #region Private Methods + + /// <summary> + /// Adds the file target. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="level">The level.</param> + private void AddFileTarget(string path, LogSeverity level) + { + + DebugFileWriter( + LogDirectory, String.Format( + "AddFileTarget called, path = [{0}], level = [{1}].", + path, + level.ToString() + )); + + RemoveTarget("ApplicationLogFileWrapper"); + + var wrapper = new AsyncTargetWrapper(); + wrapper.Name = "ApplicationLogFileWrapper"; + + var logFile = new FileTarget + { + FileName = path, + Layout = "${longdate} ${level} ${logger}: ${message}" + }; + + logFile.Name = "ApplicationLogFile"; + + wrapper.WrappedTarget = logFile; + + AddLogTarget(wrapper, level); + + } + + /// <summary> + /// Gets the log level. + /// </summary> + /// <param name="severity">The severity.</param> + /// <returns>LogLevel.</returns> + /// <exception cref="System.ArgumentException">Unrecognized LogSeverity</exception> + private LogLevel GetLogLevel(LogSeverity severity) + { + switch (severity) + { + case LogSeverity.Debug: + return LogLevel.Debug; + case LogSeverity.Error: + return LogLevel.Error; + case LogSeverity.Fatal: + return LogLevel.Fatal; + case LogSeverity.Info: + return LogLevel.Info; + case LogSeverity.Warn: + return LogLevel.Warn; + default: + throw new ArgumentException("Unrecognized LogSeverity"); } } private void UpdateLogLevel(LogSeverity newLevel) { + DebugFileWriter( + LogDirectory, String.Format( + "UpdateLogLevel called, newLevel = [{0}].", + newLevel.ToString() + )); + var level = GetLogLevel(newLevel); var rules = LogManager.Configuration.LoggingRules; @@ -95,29 +222,117 @@ namespace Emby.Common.Implementations.Logging } } - /// <summary> - /// Adds the file target. - /// </summary> - /// <param name="path">The path.</param> - /// <param name="level">The level.</param> - private void AddFileTarget(string path, LogSeverity level) + private void AddCustomFilters(string defaultLoggerNamePattern, LoggingRule defaultRule) { - RemoveTarget("ApplicationLogFileWrapper"); + DebugFileWriter( + LogDirectory, String.Format( + "AddCustomFilters called, defaultLoggerNamePattern = [{0}], defaultRule.LoggerNamePattern = [{1}].", + defaultLoggerNamePattern, + defaultRule.LoggerNamePattern + )); + + try + { + var customConfig = new NLog.Config.XmlLoggingConfiguration(NLogConfigurationFilePath); - var wrapper = new AsyncTargetWrapper (); - wrapper.Name = "ApplicationLogFileWrapper"; + DebugFileWriter( + LogDirectory, String.Format( + "Custom Configuration Loaded, Rule Count = [{0}].", + customConfig.LoggingRules.Count.ToString() + )); - var logFile = new FileTarget + foreach (var customRule in customConfig.LoggingRules) + { + + DebugFileWriter( + LogDirectory, String.Format( + "Read Custom Rule, LoggerNamePattern = [{0}], Targets = [{1}].", + customRule.LoggerNamePattern, + string.Join(",", customRule.Targets.Select(x => x.Name).ToList()) + )); + + if (customRule.LoggerNamePattern.Equals(defaultLoggerNamePattern)) + { + + if (customRule.Targets.Any((arg) => arg.Name.Equals(defaultRule.Targets.First().Name))) + { + + DebugFileWriter( + LogDirectory, String.Format( + "Custom rule filters can be applied to this target, Filter Count = [{0}].", + customRule.Filters.Count.ToString() + )); + + foreach (ConditionBasedFilter customFilter in customRule.Filters) + { + + DebugFileWriter( + LogDirectory, String.Format( + "Read Custom Filter, Filter = [{0}], Action = [{1}], Type = [{2}].", + customFilter.Condition.ToString(), + customFilter.Action.ToString(), + customFilter.GetType().ToString() + )); + + defaultRule.Filters.Add(customFilter); + + } + } + else + { + + DebugFileWriter( + LogDirectory, String.Format( + "Ignoring custom rule as [Target] does not match." + )); + + } + + } + else + { + + DebugFileWriter( + LogDirectory, String.Format( + "Ignoring custom rule as [LoggerNamePattern] does not match." + )); + + } + } + } + catch (Exception ex) { - FileName = path, - Layout = "${longdate} ${level} ${logger}: ${message}" - }; + // Intentionally do nothing, prevent issues affecting normal execution. + DebugFileWriter( + LogDirectory, String.Format( + "Exception in AddCustomFilters, ex.Message = [{0}].", + ex.Message + ) + ); - logFile.Name = "ApplicationLogFile"; + } + } - wrapper.WrappedTarget = logFile; + #endregion + + #region Public Methods + + /// <summary> + /// Gets the logger. + /// </summary> + /// <param name="name">The name.</param> + /// <returns>ILogger.</returns> + public MediaBrowser.Model.Logging.ILogger GetLogger(string name) + { + + DebugFileWriter( + LogDirectory, String.Format( + "GetLogger called, name = [{0}].", + name + )); + + return new NLogger(name, this); - AddLogTarget(wrapper, level); } /// <summary> @@ -127,13 +342,26 @@ namespace Emby.Common.Implementations.Logging /// <param name="level">The level.</param> public void AddLogTarget(Target target, LogSeverity level) { + + DebugFileWriter( + LogDirectory, String.Format( + "AddLogTarget called, target.Name = [{0}], level = [{1}].", + target.Name, + level.ToString() + )); + + string loggerNamePattern = "*"; var config = LogManager.Configuration; + var rule = new LoggingRule(loggerNamePattern, GetLogLevel(level), target); + config.AddTarget(target.Name, target); - var rule = new LoggingRule("*", GetLogLevel(level), target); + AddCustomFilters(loggerNamePattern, rule); + config.LoggingRules.Add(rule); LogManager.Configuration = config; + } /// <summary> @@ -142,6 +370,13 @@ namespace Emby.Common.Implementations.Logging /// <param name="name">The name.</param> public void RemoveTarget(string name) { + + DebugFileWriter( + LogDirectory, String.Format( + "RemoveTarget called, name = [{0}].", + name + )); + var config = LogManager.Configuration; var target = config.FindTargetByName(name); @@ -165,50 +400,70 @@ namespace Emby.Common.Implementations.Logging } } - /// <summary> - /// Gets the logger. - /// </summary> - /// <param name="name">The name.</param> - /// <returns>ILogger.</returns> - public MediaBrowser.Model.Logging.ILogger GetLogger(string name) + public void AddConsoleOutput() { - return new NLogger(name, this); + + DebugFileWriter( + LogDirectory, String.Format( + "AddConsoleOutput called." + )); + + RemoveTarget("ConsoleTargetWrapper"); + + var wrapper = new AsyncTargetWrapper(); + wrapper.Name = "ConsoleTargetWrapper"; + + var target = new ConsoleTarget() + { + Layout = "${level}, ${logger}, ${message}", + Error = false + }; + + target.Name = "ConsoleTarget"; + + wrapper.WrappedTarget = target; + + AddLogTarget(wrapper, LogSeverity); + + } + + public void RemoveConsoleOutput() + { + + DebugFileWriter( + LogDirectory, String.Format( + "RemoveConsoleOutput called." + )); + + RemoveTarget("ConsoleTargetWrapper"); + } /// <summary> - /// Gets the log level. + /// Reloads the logger, maintaining the current log level. /// </summary> - /// <param name="severity">The severity.</param> - /// <returns>LogLevel.</returns> - /// <exception cref="System.ArgumentException">Unrecognized LogSeverity</exception> - private LogLevel GetLogLevel(LogSeverity severity) + public void ReloadLogger() { - switch (severity) - { - case LogSeverity.Debug: - return LogLevel.Debug; - case LogSeverity.Error: - return LogLevel.Error; - case LogSeverity.Fatal: - return LogLevel.Fatal; - case LogSeverity.Info: - return LogLevel.Info; - case LogSeverity.Warn: - return LogLevel.Warn; - default: - throw new ArgumentException("Unrecognized LogSeverity"); - } + ReloadLogger(LogSeverity); } /// <summary> - /// Reloads the logger. + /// Reloads the logger, using the specified logging level. /// </summary> /// <param name="level">The level.</param> public void ReloadLogger(LogSeverity level) { + + DebugFileWriter( + LogDirectory, String.Format( + "ReloadLogger called, level = [{0}], LogFilePath (existing) = [{1}].", + level.ToString(), + LogFilePath + )); + LogFilePath = Path.Combine(LogDirectory, LogFilePrefix + "-" + decimal.Floor(DateTime.Now.Ticks / 10000000) + ".txt"); - Directory.CreateDirectory(Path.GetDirectoryName(LogFilePath)); + Directory.CreateDirectory(Path.GetDirectoryName(LogFilePath)); AddFileTarget(LogFilePath, level); @@ -218,7 +473,14 @@ namespace Emby.Common.Implementations.Logging { try { + + DebugFileWriter( + LogDirectory, String.Format( + "ReloadLogger called, raised event LoggerLoaded." + )); + LoggerLoaded(this, EventArgs.Empty); + } catch (Exception ex) { @@ -232,33 +494,51 @@ namespace Emby.Common.Implementations.Logging /// </summary> public void Flush() { - LogManager.Flush(); - } + DebugFileWriter( + LogDirectory, String.Format( + "Flush called." + )); - public void AddConsoleOutput() - { - RemoveTarget("ConsoleTargetWrapper"); + LogManager.Flush(); - var wrapper = new AsyncTargetWrapper (); - wrapper.Name = "ConsoleTargetWrapper"; + } - var target = new ConsoleTarget() - { - Layout = "${level}, ${logger}, ${message}", - Error = false - }; + #endregion - target.Name = "ConsoleTarget"; + #region Conditional Debug Methods - wrapper.WrappedTarget = target; + /// <summary> + /// DEBUG: Standalone method to write out debug to assist with logger development/troubleshooting. + /// <list type="bullet"> + /// <item><description>The output file will be written to the server's log directory.</description></item> + /// <item><description>Calls to the method are safe and will never throw any exceptions.</description></item> + /// <item><description>Method calls will be omitted unless the library is compiled with DEBUG defined.</description></item> + /// </list> + /// </summary> + private static void DebugFileWriter(string logDirectory, string message) + { +#if DEBUG + try + { - AddLogTarget(wrapper, LogSeverity); - } + System.IO.File.AppendAllText( + Path.Combine(logDirectory, "NlogManager.txt"), + String.Format( + "{0} : {1}{2}", + System.DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), + message, + System.Environment.NewLine + ) + ); - public void RemoveConsoleOutput() - { - RemoveTarget("ConsoleTargetWrapper"); + } + catch (Exception ex) + { + // Intentionally do nothing, prevent issues affecting normal execution. + } +#endif } + #endregion } -} +}
\ No newline at end of file |
