123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554 |
- 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;
- namespace Emby.Common.Implementations.Logging
- {
- /// <summary>
- /// Class NlogManager
- /// </summary>
- public class NlogManager : ILogManager
- {
- #region Private Fields
- private LogSeverity _severity = LogSeverity.Debug;
- /// <summary>
- /// Gets or sets the log directory.
- /// </summary>
- /// <value>The log directory.</value>
- private readonly string LogDirectory;
- /// <summary>
- /// Gets or sets the log file prefix.
- /// </summary>
- /// <value>The log file prefix.</value>
- 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>
- /// <value>The log file path.</value>
- public string LogFilePath { get; private set; }
- /// <summary>
- /// Gets or sets the exception message prefix.
- /// </summary>
- /// <value>The exception message prefix.</value>
- public string ExceptionMessagePrefix { get; set; }
- public string NLogConfigurationFilePath { get; set; }
- 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;
- if (changed)
- {
- 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");
- // https://github.com/NLog/NLog/wiki/Performance
- var wrapper = new AsyncTargetWrapper
- {
- OverflowAction = AsyncTargetWrapperOverflowAction.Block,
- QueueLimit = 10000,
- BatchSize = 500,
- TimeToSleepBetweenBatches = 50
- };
- wrapper.Name = "ApplicationLogFileWrapper";
- var logFile = new FileTarget
- {
- FileName = path,
- Layout = "${longdate} ${level} ${logger}: ${message}",
- KeepFileOpen = true,
- ConcurrentWrites = false
- };
- 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;
- foreach (var rule in rules)
- {
- if (!rule.IsLoggingEnabledForLevel(level))
- {
- rule.EnableLoggingForLevel(level);
- }
- foreach (var lev in rule.Levels.ToArray())
- {
- if (lev < level)
- {
- rule.DisableLoggingForLevel(lev);
- }
- }
- }
- }
- private void AddCustomFilters(string defaultLoggerNamePattern, LoggingRule defaultRule)
- {
- DebugFileWriter(
- LogDirectory, String.Format(
- "AddCustomFilters called, defaultLoggerNamePattern = [{0}], defaultRule.LoggerNamePattern = [{1}].",
- defaultLoggerNamePattern,
- defaultRule.LoggerNamePattern
- ));
- try
- {
- var customConfig = new NLog.Config.XmlLoggingConfiguration(NLogConfigurationFilePath);
- DebugFileWriter(
- LogDirectory, String.Format(
- "Custom Configuration Loaded, Rule Count = [{0}].",
- customConfig.LoggingRules.Count.ToString()
- ));
- 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)
- {
- // Intentionally do nothing, prevent issues affecting normal execution.
- DebugFileWriter(
- LogDirectory, String.Format(
- "Exception in AddCustomFilters, ex.Message = [{0}].",
- ex.Message
- )
- );
- }
- }
- #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);
- }
- /// <summary>
- /// Adds the log target.
- /// </summary>
- /// <param name="target">The target.</param>
- /// <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);
- AddCustomFilters(loggerNamePattern, rule);
- config.LoggingRules.Add(rule);
- LogManager.Configuration = config;
- }
- /// <summary>
- /// Removes the target.
- /// </summary>
- /// <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);
- if (target != null)
- {
- foreach (var rule in config.LoggingRules.ToList())
- {
- var contains = rule.Targets.Contains(target);
- rule.Targets.Remove(target);
- if (contains)
- {
- config.LoggingRules.Remove(rule);
- }
- }
- config.RemoveTarget(name);
- LogManager.Configuration = config;
- }
- }
- public void AddConsoleOutput()
- {
- 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>
- /// Reloads the logger, maintaining the current log level.
- /// </summary>
- public void ReloadLogger()
- {
- ReloadLogger(LogSeverity);
- }
- /// <summary>
- /// 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));
- AddFileTarget(LogFilePath, level);
- LogSeverity = level;
- if (LoggerLoaded != null)
- {
- try
- {
- DebugFileWriter(
- LogDirectory, String.Format(
- "ReloadLogger called, raised event LoggerLoaded."
- ));
- LoggerLoaded(this, EventArgs.Empty);
- }
- catch (Exception ex)
- {
- GetLogger("Logger").ErrorException("Error in LoggerLoaded event", ex);
- }
- }
- }
- /// <summary>
- /// Flushes this instance.
- /// </summary>
- public void Flush()
- {
- DebugFileWriter(
- LogDirectory, String.Format(
- "Flush called."
- ));
- LogManager.Flush();
- }
- #endregion
- #region Conditional Debug Methods
- /// <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
- {
- 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
- )
- );
- }
- catch (Exception ex)
- {
- // Intentionally do nothing, prevent issues affecting normal execution.
- }
- #endif
- }
- #endregion
- }
- }
|