diff --git a/src/Umbraco.Core/Composing/Current.cs b/src/Umbraco.Core/Composing/Current.cs index d0025c0c8b..49bb44db47 100644 --- a/src/Umbraco.Core/Composing/Current.cs +++ b/src/Umbraco.Core/Composing/Current.cs @@ -57,6 +57,7 @@ namespace Umbraco.Core.Composing // resets *everything* that is 'current' internal static void Reset() { + _container?.Dispose(); _container = null; _shortStringHelper = null; diff --git a/src/Umbraco.Core/Logging/DebugDiagnosticsLogger.cs b/src/Umbraco.Core/Logging/DebugDiagnosticsLogger.cs index a8ca9b6c31..d1bde55306 100644 --- a/src/Umbraco.Core/Logging/DebugDiagnosticsLogger.cs +++ b/src/Umbraco.Core/Logging/DebugDiagnosticsLogger.cs @@ -7,6 +7,9 @@ namespace Umbraco.Core.Logging /// public class DebugDiagnosticsLogger : ILogger { + public bool IsEnabled(Type reporting, LogLevel level) + => true; + /// public void Fatal(Type reporting, Exception exception, string message) { @@ -26,15 +29,15 @@ namespace Umbraco.Core.Logging } /// - public void Fatal(Type reporting, Exception exception, string messageTemplate, params object[] args) + public void Fatal(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) { - System.Diagnostics.Debug.WriteLine(string.Format(messageTemplate, args) + Environment.NewLine + exception, reporting.FullName); + System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(messageTemplate, propertyValues) + Environment.NewLine + exception, reporting.FullName); } /// - public void Fatal(Type reporting, string messageTemplate, params object[] args) + public void Fatal(Type reporting, string messageTemplate, params object[] propertyValues) { - System.Diagnostics.Debug.WriteLine(messageTemplate, args); + System.Diagnostics.Debug.WriteLine(messageTemplate, propertyValues); } /// @@ -56,27 +59,27 @@ namespace Umbraco.Core.Logging } /// - public void Error(Type reporting, Exception exception, string messageTemplate, params object[] args) + public void Error(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) { - System.Diagnostics.Debug.WriteLine(string.Format(messageTemplate, args) + Environment.NewLine + exception, reporting.FullName); + System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(messageTemplate, propertyValues) + Environment.NewLine + exception, reporting.FullName); } /// - public void Error(Type reporting, string messageTemplate, params object[] args) + public void Error(Type reporting, string messageTemplate, params object[] propertyValues) { - System.Diagnostics.Debug.WriteLine(messageTemplate, args); + System.Diagnostics.Debug.WriteLine(messageTemplate, propertyValues); } /// - public void Warn(Type reporting, string format) + public void Warn(Type reporting, string message) { - System.Diagnostics.Debug.WriteLine(format, reporting.FullName); + System.Diagnostics.Debug.WriteLine(message, reporting.FullName); } - + /// - public void Warn(Type reporting, string format, params object[] args) + public void Warn(Type reporting, string message, params object[] propertyValues) { - System.Diagnostics.Debug.WriteLine(string.Format(format, args), reporting.FullName); + System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(message, propertyValues), reporting.FullName); } /// @@ -86,9 +89,9 @@ namespace Umbraco.Core.Logging } /// - public void Warn(Type reporting, Exception exception, string format, params object[] args) + public void Warn(Type reporting, Exception exception, string message, params object[] propertyValues) { - System.Diagnostics.Debug.WriteLine(string.Format(format + Environment.NewLine + exception, args), reporting.FullName); + System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(message + Environment.NewLine + exception, propertyValues), reporting.FullName); } /// @@ -98,9 +101,9 @@ namespace Umbraco.Core.Logging } /// - public void Info(Type reporting, string format, params object[] args) + public void Info(Type reporting, string messageTemplate, params object[] propertyValues) { - System.Diagnostics.Debug.WriteLine(string.Format(format, args), reporting.FullName); + System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(messageTemplate, propertyValues), reporting.FullName); } /// @@ -110,9 +113,9 @@ namespace Umbraco.Core.Logging } /// - public void Debug(Type reporting, string format, params object[] args) + public void Debug(Type reporting, string messageTemplate, params object[] propertyValues) { - System.Diagnostics.Debug.WriteLine(string.Format(format, args), reporting.FullName); + System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(messageTemplate, propertyValues), reporting.FullName); } /// @@ -122,10 +125,9 @@ namespace Umbraco.Core.Logging } /// - public void Verbose(Type reporting, string format, params object[] args) + public void Verbose(Type reporting, string messageTemplate, params object[] propertyValues) { - System.Diagnostics.Debug.WriteLine(string.Format(format, args), reporting.FullName); + System.Diagnostics.Debug.WriteLine(MessageTemplates.Render(messageTemplate, propertyValues), reporting.FullName); } - } } diff --git a/src/Umbraco.Core/Logging/DisposableTimer.cs b/src/Umbraco.Core/Logging/DisposableTimer.cs index 869ca2cd44..db530e5339 100644 --- a/src/Umbraco.Core/Logging/DisposableTimer.cs +++ b/src/Umbraco.Core/Logging/DisposableTimer.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Logging public class DisposableTimer : DisposableObjectSlim { private readonly ILogger _logger; - private readonly LogType? _logType; + private readonly LogLevel _level; private readonly Type _loggerType; private readonly int _thresholdMilliseconds; private readonly IDisposable _profilerStep; @@ -19,22 +19,14 @@ namespace Umbraco.Core.Logging private bool _failed; private readonly string _timingId; - internal enum LogType - { - Debug, Info - } - // internal - created by profiling logger - internal DisposableTimer(ILogger logger, LogType logType, IProfiler profiler, Type loggerType, + internal DisposableTimer(ILogger logger, LogLevel level, IProfiler profiler, Type loggerType, string startMessage, string endMessage, string failMessage = null, int thresholdMilliseconds = 0) { - if (logger == null) throw new ArgumentNullException(nameof(logger)); - if (loggerType == null) throw new ArgumentNullException(nameof(loggerType)); - - _logger = logger; - _logType = logType; - _loggerType = loggerType; + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _level = level; + _loggerType = loggerType ?? throw new ArgumentNullException(nameof(loggerType)); _endMessage = endMessage; _failMessage = failMessage; _thresholdMilliseconds = thresholdMilliseconds < 0 ? 0 : thresholdMilliseconds; @@ -42,16 +34,16 @@ namespace Umbraco.Core.Logging if (thresholdMilliseconds == 0) { - switch (logType) + switch (_level) { - case LogType.Debug: + case LogLevel.Debug: logger.Debug(loggerType, "[Timing {TimingId}] {StartMessage}", _timingId, startMessage); break; - case LogType.Info: + case LogLevel.Information: logger.Info(loggerType, "[Timing {TimingId}] {StartMessage}", _timingId, startMessage); break; default: - throw new ArgumentOutOfRangeException(nameof(logType)); + throw new ArgumentOutOfRangeException(nameof(level)); } } @@ -87,19 +79,19 @@ namespace Umbraco.Core.Logging _profilerStep?.Dispose(); if ((Stopwatch.ElapsedMilliseconds >= _thresholdMilliseconds || _failed) - && _logType.HasValue && _loggerType != null && _logger != null + && _loggerType != null && _logger != null && (_endMessage.IsNullOrWhiteSpace() == false || _failed)) { if (_failed) { _logger.Error(_loggerType, _failException, "[Timing {TimingId}] {FailMessage} ({TimingDuration}ms)", _timingId, _failMessage, Stopwatch.ElapsedMilliseconds); } - else switch (_logType) + else switch (_level) { - case LogType.Debug: + case LogLevel.Debug: _logger.Debug(_loggerType, "[Timing {TimingId}] {EndMessage} ({TimingDuration}ms)", _timingId, _endMessage, Stopwatch.ElapsedMilliseconds); break; - case LogType.Info: + case LogLevel.Information: _logger.Info(_loggerType, "[Timing {TimingId}] {EndMessage} ({TimingDuration}ms)", _timingId, _endMessage, Stopwatch.ElapsedMilliseconds); break; // filtered in the ctor diff --git a/src/Umbraco.Core/Logging/ILogger.cs b/src/Umbraco.Core/Logging/ILogger.cs index 2162b10bfe..4f49d0b3b4 100644 --- a/src/Umbraco.Core/Logging/ILogger.cs +++ b/src/Umbraco.Core/Logging/ILogger.cs @@ -14,6 +14,13 @@ namespace Umbraco.Core.Logging /// public interface ILogger { + /// + /// Determines if logging is enabled at a specified level, for a reporting type. + /// + /// The reporting type. + /// The level. + bool IsEnabled(Type reporting, LogLevel level); + /// /// Logs a fatal message with an exception. /// diff --git a/src/Umbraco.Core/Logging/LogLevel.cs b/src/Umbraco.Core/Logging/LogLevel.cs new file mode 100644 index 0000000000..f1b65499d6 --- /dev/null +++ b/src/Umbraco.Core/Logging/LogLevel.cs @@ -0,0 +1,15 @@ +namespace Umbraco.Core.Logging +{ + /// + /// Specifies the level of a log event. + /// + public enum LogLevel + { + Verbose, + Debug, + Information, + Warning, + Error, + Fatal + } +} diff --git a/src/Umbraco.Core/Logging/LoggerExtensions.cs b/src/Umbraco.Core/Logging/LoggerExtensions.cs index c29bcef4f7..3b105ed888 100644 --- a/src/Umbraco.Core/Logging/LoggerExtensions.cs +++ b/src/Umbraco.Core/Logging/LoggerExtensions.cs @@ -8,63 +8,62 @@ namespace Umbraco.Core.Logging public static class LoggerExtensions { /// - /// Logs an error message + /// Determines if logging is enabled at a specified level, for a reporting type. + /// + /// The reporting type. + /// The logger. + /// The level. + public static bool IsEnabled(this ILogger logger, LogLevel level) + => logger.IsEnabled(typeof(T), level); + + /// + /// Logs an error message with an exception. /// /// The reporting type. /// The logger. /// A message. /// An exception. public static void Error(this ILogger logger, Exception exception, string message) - { - logger.Error(typeof(T), exception, message); - } + => logger.Error(typeof(T), exception, message); /// - /// Logs an error message with a structured message template + /// Logs an error message with an exception. /// - /// The reporting type + /// The reporting type. /// The logger. - /// A structured message template - /// An exception - /// Message property values + /// An exception. + /// A message template. + /// Property values. public static void Error(this ILogger logger, Exception exception, string messageTemplate, params object[] propertyValues) - { - logger.Error(typeof(T), exception, messageTemplate, propertyValues); - } + => logger.Error(typeof(T), exception, messageTemplate, propertyValues); /// - /// Logs an error message NOTE: This will log an empty message string + /// Logs an error exception. /// - /// The reporting type + /// The reporting type. /// The logger. - /// An exception + /// An exception. public static void Error(this ILogger logger, Exception exception) - { - logger.Error(typeof(T), exception); - } + => logger.Error(typeof(T), exception); /// - /// Logs an error message WITHOUT EX + /// Logs an error message. /// - /// - /// - /// - public static void Error(this ILogger logger, string message) - { - logger.Error(typeof(T), message); - } - - /// - /// Logs an error message - using a structured log message - /// - /// The reporting type + /// The reporting type. /// The logger. - /// A structured message template - /// Message property values + /// A message. + public static void Error(this ILogger logger, string message) + => logger.Error(typeof(T), message); + + /// + /// Logs an error message. + /// + /// The reporting type. + /// The logger. + /// A message template. + /// Property values. public static void Error(this ILogger logger, string messageTemplate, params object[] propertyValues) - { - logger.Error(typeof(T), messageTemplate, propertyValues); - } + => logger.Error(typeof(T), messageTemplate, propertyValues); /// /// Logs a warning message. @@ -73,46 +72,38 @@ namespace Umbraco.Core.Logging /// The logger. /// A message. public static void Warn(this ILogger logger, string message) - { - logger.Warn(typeof(T), message); - } + => logger.Warn(typeof(T), message); /// - /// Logs a warning message with a structured message template + /// Logs a warning message. /// - /// The reporting type + /// The reporting type. /// The logger. - /// A structured message template - /// Message property values + /// A message template. + /// Property values. public static void Warn(this ILogger logger, string messageTemplate, params object[] propertyValues) - { - logger.Warn(typeof(T), messageTemplate, propertyValues); - } + => logger.Warn(typeof(T), messageTemplate, propertyValues); /// - /// Logs a formatted warning message with an exception. + /// Logs a warning message with an exception. /// /// The reporting type. /// The logger. /// An exception. /// A message. public static void Warn(this ILogger logger, Exception exception, string message) - { - logger.Warn(typeof(T), exception, message); - } + => logger.Warn(typeof(T), exception, message); /// - /// Logs a warning message with an exception with a structured message template + /// Logs a warning message with an exception. /// - /// The reporting type + /// The reporting type. /// The logger. - /// An exception - /// A structured message template - /// Message property values + /// An exception. + /// A message template. + /// Property values. public static void Warn(this ILogger logger, Exception exception, string messageTemplate, params object[] propertyValues) - { - logger.Warn(typeof(T), exception, messageTemplate, propertyValues); - } + => logger.Warn(typeof(T), exception, messageTemplate, propertyValues); /// /// Logs an information message. @@ -121,21 +112,17 @@ namespace Umbraco.Core.Logging /// The logger. /// A message. public static void Info(this ILogger logger, string message) - { - logger.Info(typeof(T), message); - } + => logger.Info(typeof(T), message); /// - /// Logs a information message with a structured message template + /// Logs a information message. /// /// The reporting type /// The logger. - /// A structured message template - /// Message property values + /// A message template. + /// Property values. public static void Info(this ILogger logger, string messageTemplate, params object[] propertyValues) - { - logger.Info(typeof(T), messageTemplate, propertyValues); - } + => logger.Info(typeof(T), messageTemplate, propertyValues); /// /// Logs a debugging message. @@ -144,21 +131,17 @@ namespace Umbraco.Core.Logging /// The logger. /// A message. public static void Debug(this ILogger logger, string message) - { - logger.Debug(typeof(T), message); - } + => logger.Debug(typeof(T), message); /// - /// Logs a debugging message with a structured message template + /// Logs a debugging message. /// /// The reporting type /// The logger. - /// A structured message template - /// Message property values + /// A message template. + /// Property values. public static void Debug(this ILogger logger, string messageTemplate, params object[] propertyValues) - { - logger.Debug(typeof(T), messageTemplate, propertyValues); - } + => logger.Debug(typeof(T), messageTemplate, propertyValues); /// /// Logs a verbose message. @@ -167,22 +150,17 @@ namespace Umbraco.Core.Logging /// The logger. /// A message. public static void Verbose(this ILogger logger, string message) - { - logger.Verbose(typeof(T), message); - } + => logger.Verbose(typeof(T), message); /// - /// Logs a Verbose message with a structured message template + /// Logs a verbose message. /// - /// The reporting type + /// The reporting type. /// The logger. - /// A structured message template - /// Message property values + /// A message template. + /// Property values. public static void Verbose(this ILogger logger, string messageTemplate, params object[] propertyValues) - { - logger.Verbose(typeof(T), messageTemplate, propertyValues); - } - + => logger.Verbose(typeof(T), messageTemplate, propertyValues); /// /// Logs a fatal message. @@ -192,23 +170,17 @@ namespace Umbraco.Core.Logging /// An exception. /// A message. public static void Fatal(this ILogger logger, Exception exception, string message) - { - logger.Fatal(typeof(T), exception, message); - } - + => logger.Fatal(typeof(T), exception, message); /// - /// Logs a fatal message with a structured message template + /// Logs a fatal message. /// /// The reporting type. /// The logger. /// An exception. - /// A structured message template - /// Message property values + /// A message template. + /// Property values. public static void Fatal(this ILogger logger, Exception exception, string messageTemplate, params object[] propertyValues) - { - logger.Fatal(typeof(T), exception, messageTemplate, propertyValues); - } - + => logger.Fatal(typeof(T), exception, messageTemplate, propertyValues); } } diff --git a/src/Umbraco.Core/Logging/MessageTemplates.cs b/src/Umbraco.Core/Logging/MessageTemplates.cs new file mode 100644 index 0000000000..194d47c339 --- /dev/null +++ b/src/Umbraco.Core/Logging/MessageTemplates.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using Serilog; + +namespace Umbraco.Core.Logging +{ + /// + /// Provides tools to support message templates. + /// + public static class MessageTemplates + { + // Umbraco now uses Message Templates (https://messagetemplates.org/) for logging, which means + // we cannot plainly use string.Format() to format them. There is a work-in-progress C# lib, + // derived from Serilog, which should help (https://github.com/messagetemplates/messagetemplates-csharp) + // but it only has a pre-release NuGet package. So, we've got to use Serilog's code, which + // means we cannot get rid of Serilog entirely. We may want to revisit this at some point. + + private static readonly Lazy MinimalLogger = new Lazy(() => new LoggerConfiguration().CreateLogger()); + + public static string Render(string messageTemplate, params object[] args) + { + // by default, unless initialized otherwise, Log.Logger is SilentLogger which cannot bind message + // templates. Log.Logger is set to a true Logger when initializing Umbraco's logger, but in case + // that has not been done already - use a temp minimal logger (eg for tests). + var logger = Log.Logger as global::Serilog.Core.Logger ?? MinimalLogger.Value; + + var bound = logger.BindMessageTemplate(messageTemplate, args, out var parsedTemplate, out var boundProperties); + + if (!bound) + throw new FormatException($"Could not format message \"{messageTemplate}\" with {args.Length} args."); + + return parsedTemplate.Render(boundProperties.ToDictionary(x => x.Name, x => x.Value)); + } + } +} diff --git a/src/Umbraco.Core/Logging/ProfilingLogger.cs b/src/Umbraco.Core/Logging/ProfilingLogger.cs index 28d2494b0d..80560e923a 100644 --- a/src/Umbraco.Core/Logging/ProfilingLogger.cs +++ b/src/Umbraco.Core/Logging/ProfilingLogger.cs @@ -13,10 +13,8 @@ namespace Umbraco.Core.Logging public ProfilingLogger(ILogger logger, IProfiler profiler) { - if (logger == null) throw new ArgumentNullException(nameof(logger)); - if (profiler == null) throw new ArgumentNullException(nameof(profiler)); - Logger = logger; - Profiler = profiler; + Logger = logger ?? throw new ArgumentNullException(nameof(logger)); + Profiler = profiler ?? throw new ArgumentNullException(nameof(profiler)); } public DisposableTimer TraceDuration(string startMessage) @@ -26,27 +24,33 @@ namespace Umbraco.Core.Logging public DisposableTimer TraceDuration(string startMessage, string completeMessage, string failMessage = null) { - return new DisposableTimer(Logger, DisposableTimer.LogType.Info, Profiler, typeof(T), startMessage, completeMessage, failMessage); + return new DisposableTimer(Logger, LogLevel.Information, Profiler, typeof(T), startMessage, completeMessage, failMessage); } public DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null) { - return new DisposableTimer(Logger, DisposableTimer.LogType.Info, Profiler, loggerType, startMessage, completeMessage, failMessage); + return new DisposableTimer(Logger, LogLevel.Information, Profiler, loggerType, startMessage, completeMessage, failMessage); } public DisposableTimer DebugDuration(string startMessage) { - return DebugDuration(startMessage, "Completed."); + return Logger.IsEnabled(LogLevel.Debug) + ? DebugDuration(startMessage, "Completed.") + : null; } public DisposableTimer DebugDuration(string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0) { - return new DisposableTimer(Logger, DisposableTimer.LogType.Debug, Profiler, typeof(T), startMessage, completeMessage, failMessage, thresholdMilliseconds); + return Logger.IsEnabled(LogLevel.Debug) + ? new DisposableTimer(Logger, LogLevel.Debug, Profiler, typeof(T), startMessage, completeMessage, failMessage, thresholdMilliseconds) + : null; } public DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage, string failMessage = null, int thresholdMilliseconds = 0) { - return new DisposableTimer(Logger, DisposableTimer.LogType.Debug, Profiler, loggerType, startMessage, completeMessage, failMessage, thresholdMilliseconds); + return Logger.IsEnabled(loggerType, LogLevel.Debug) + ? new DisposableTimer(Logger, LogLevel.Debug, Profiler, loggerType, startMessage, completeMessage, failMessage, thresholdMilliseconds) + : null; } } } diff --git a/src/Umbraco.Core/Logging/SerilogExtensions/Log4NetLevelMapperEnricher.cs b/src/Umbraco.Core/Logging/Serilog/Log4NetLevelMapperEnricher.cs similarity index 96% rename from src/Umbraco.Core/Logging/SerilogExtensions/Log4NetLevelMapperEnricher.cs rename to src/Umbraco.Core/Logging/Serilog/Log4NetLevelMapperEnricher.cs index 681f7b4936..1424fa0b55 100644 --- a/src/Umbraco.Core/Logging/SerilogExtensions/Log4NetLevelMapperEnricher.cs +++ b/src/Umbraco.Core/Logging/Serilog/Log4NetLevelMapperEnricher.cs @@ -1,7 +1,7 @@ using Serilog.Core; using Serilog.Events; -namespace Umbraco.Core.Logging.SerilogExtensions +namespace Umbraco.Core.Logging.Serilog { /// /// This is used to create a new property in Logs called 'Log4NetLevel' diff --git a/src/Umbraco.Core/Logging/SerilogExtensions/LoggerConfigExtensions.cs b/src/Umbraco.Core/Logging/Serilog/LoggerConfigExtensions.cs similarity index 97% rename from src/Umbraco.Core/Logging/SerilogExtensions/LoggerConfigExtensions.cs rename to src/Umbraco.Core/Logging/Serilog/LoggerConfigExtensions.cs index 150fb0395c..8861c808df 100644 --- a/src/Umbraco.Core/Logging/SerilogExtensions/LoggerConfigExtensions.cs +++ b/src/Umbraco.Core/Logging/Serilog/LoggerConfigExtensions.cs @@ -4,7 +4,7 @@ using Serilog; using Serilog.Events; using Serilog.Formatting.Compact; -namespace Umbraco.Core.Logging.SerilogExtensions +namespace Umbraco.Core.Logging.Serilog { public static class LoggerConfigExtensions { @@ -16,7 +16,7 @@ namespace Umbraco.Core.Logging.SerilogExtensions /// A Serilog LoggerConfiguration public static LoggerConfiguration MinimalConfiguration(this LoggerConfiguration logConfig) { - Serilog.Debugging.SelfLog.Enable(msg => System.Diagnostics.Debug.WriteLine(msg)); + global::Serilog.Debugging.SelfLog.Enable(msg => System.Diagnostics.Debug.WriteLine(msg)); //Set this environment variable - so that it can be used in external config file //add key="serilog:write-to:RollingFile.pathFormat" value="%BASEDIR%\logs\log.txt" /> diff --git a/src/Umbraco.Core/Logging/Logger.cs b/src/Umbraco.Core/Logging/Serilog/SerilogLogger.cs similarity index 57% rename from src/Umbraco.Core/Logging/Logger.cs rename to src/Umbraco.Core/Logging/Serilog/SerilogLogger.cs index 0a20022e93..6d3b2b28b3 100644 --- a/src/Umbraco.Core/Logging/Logger.cs +++ b/src/Umbraco.Core/Logging/Serilog/SerilogLogger.cs @@ -2,41 +2,40 @@ using System.IO; using System.Reflection; using System.Threading; -using Umbraco.Core.Configuration; -using Umbraco.Core.Diagnostics; using Serilog; using Serilog.Events; -using Umbraco.Core.Logging.SerilogExtensions; +using Umbraco.Core.Configuration; +using Umbraco.Core.Diagnostics; -namespace Umbraco.Core.Logging +namespace Umbraco.Core.Logging.Serilog { /// /// Implements on top of Serilog. /// - public class Logger : ILogger + public class SerilogLogger : ILogger { /// - /// Initialize a new instance of the class with a configuration file. + /// Initialize a new instance of the class with a configuration file. /// /// - public Logger(FileInfo logConfigFile) + public SerilogLogger(FileInfo logConfigFile) { Log.Logger = new LoggerConfiguration() .ReadFrom.AppSettings(filePath: AppDomain.CurrentDomain.BaseDirectory + logConfigFile) .CreateLogger(); } - public Logger(LoggerConfiguration logConfig) + public SerilogLogger(LoggerConfiguration logConfig) { //Configure Serilog static global logger with config passed in Log.Logger = logConfig.CreateLogger(); } /// - /// Creates a logger with some pre-definied configuration and remainder from config file + /// Creates a logger with some pre-defined configuration and remainder from config file /// /// Used by UmbracoApplicationBase to get its logger. - public static Logger CreateWithDefaultConfiguration() + public static SerilogLogger CreateWithDefaultConfiguration() { var loggerConfig = new LoggerConfiguration(); loggerConfig @@ -46,78 +45,118 @@ namespace Umbraco.Core.Logging .ReadFromConfigFile() .ReadFromUserConfigFile(); - return new Logger(loggerConfig); + return new SerilogLogger(loggerConfig); } + /// + /// Gets a contextualized logger. + /// + private global::Serilog.ILogger LoggerFor(Type reporting) + => Log.Logger.ForContext(reporting); + + /// + /// Maps Umbraco's log level to Serilog's. + /// + private LogEventLevel MapLevel(LogLevel level) + { + switch (level) + { + case LogLevel.Debug: + return LogEventLevel.Debug; + case LogLevel.Error: + return LogEventLevel.Error; + case LogLevel.Fatal: + return LogEventLevel.Fatal; + case LogLevel.Information: + return LogEventLevel.Information; + case LogLevel.Verbose: + return LogEventLevel.Verbose; + case LogLevel.Warning: + return LogEventLevel.Warning; + } + + throw new NotSupportedException($"LogLevel \"{level}\" is not supported."); + } + + /// + public bool IsEnabled(Type reporting, LogLevel level) + => LoggerFor(reporting).IsEnabled(MapLevel(level)); + /// public void Fatal(Type reporting, Exception exception, string message) { - Fatal(reporting, exception, message, null); + var logger = LoggerFor(reporting); + DumpThreadAborts(logger, LogEventLevel.Fatal, exception, ref message); + logger.Fatal(exception, message); } /// public void Fatal(Type reporting, Exception exception) { - Fatal(reporting, exception, string.Empty); + var logger = LoggerFor(reporting); + var message = "Exception."; + DumpThreadAborts(logger, LogEventLevel.Fatal, exception, ref message); + logger.Fatal(exception, message); } /// public void Fatal(Type reporting, string message) { - //Sometimes we need to throw an error without an ex - Fatal(reporting, null, message); + LoggerFor(reporting).Fatal(message); } /// public void Fatal(Type reporting, string messageTemplate, params object[] propertyValues) { - //Log a structured message WITHOUT an ex - Fatal(reporting, null, messageTemplate, propertyValues); + LoggerFor(reporting).Fatal(messageTemplate, propertyValues); } /// public void Fatal(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) { - ErrorOrFatal(Fatal, exception, ref messageTemplate); - var logger = Log.Logger; - logger?.ForContext(reporting).Fatal(exception, messageTemplate, propertyValues); + var logger = LoggerFor(reporting); + DumpThreadAborts(logger, LogEventLevel.Fatal, exception, ref messageTemplate); + logger.Fatal(exception, messageTemplate, propertyValues); } /// public void Error(Type reporting, Exception exception, string message) { - Error(reporting, exception, message, null); + var logger = LoggerFor(reporting); + DumpThreadAborts(logger, LogEventLevel.Error, exception, ref message); + logger.Error(exception, message); } /// public void Error(Type reporting, Exception exception) { - Error(reporting, exception, string.Empty); + var logger = LoggerFor(reporting); + var message = "Exception"; + DumpThreadAborts(logger, LogEventLevel.Error, exception, ref message); + logger.Error(exception, message); } /// public void Error(Type reporting, string message) { - //Sometimes we need to throw an error without an ex - Error(reporting, null, message); + LoggerFor(reporting).Error(message); } /// public void Error(Type reporting, string messageTemplate, params object[] propertyValues) { - //Log a structured message WITHOUT an ex - Error(reporting, null, messageTemplate, propertyValues); + LoggerFor(reporting).Error(messageTemplate, propertyValues); } /// public void Error(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) { - ErrorOrFatal(Error, exception, ref messageTemplate); - var logger = Log.Logger; - logger?.ForContext(reporting).Error(exception, messageTemplate, propertyValues); + var logger = LoggerFor(reporting); + DumpThreadAborts(logger, LogEventLevel.Error, exception, ref messageTemplate); + logger.Error(exception, messageTemplate, propertyValues); } - private static void ErrorOrFatal(Action logAction, Exception exception, ref string messageTemplate) + private static void DumpThreadAborts(global::Serilog.ILogger logger, LogEventLevel level, Exception exception, ref string messageTemplate) { var dump = false; @@ -143,8 +182,10 @@ namespace Umbraco.Core.Logging } catch (Exception ex) { + messageTemplate += "\r\nFailed to create a minidump"; + //Log a new entry (as opposed to appending to same log entry) - logAction(ex.GetType(), ex, "Failed to create a minidump at App_Data/MiniDump ({ExType}: {ExMessage}", + logger.Write(level, ex, "Failed to create a minidump ({ExType}: {ExMessage})", new object[]{ ex.GetType().FullName, ex.Message }); } } @@ -174,69 +215,63 @@ namespace Umbraco.Core.Logging } /// - public void Warn(Type reporting, string format) + public void Warn(Type reporting, string message) { - Warn(reporting, null, format); + LoggerFor(reporting).Warning(message); } /// - public void Warn(Type reporting, string messageTemplate, params object[] propertyValues) + public void Warn(Type reporting, string message, params object[] propertyValues) { - Warn(reporting, null, messageTemplate, propertyValues); + LoggerFor(reporting).Warning(message, propertyValues); } /// public void Warn(Type reporting, Exception exception, string message) { - Warn(reporting, exception, message, Array.Empty()); + LoggerFor(reporting).Warning(exception, message); } /// public void Warn(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) { - var logger = Log.Logger; - logger?.ForContext(reporting).Warning(exception, messageTemplate, propertyValues); + LoggerFor(reporting).Warning(exception, messageTemplate, propertyValues); } /// public void Info(Type reporting, string message) { - Info(reporting, message, Array.Empty()); + LoggerFor(reporting).Information(message); } /// public void Info(Type reporting, string messageTemplate, params object[] propertyValues) { - var logger = Log.Logger; - logger?.ForContext(reporting).Information(messageTemplate, propertyValues); + LoggerFor(reporting).Information(messageTemplate, propertyValues); } /// public void Debug(Type reporting, string message) { - Debug(reporting, message, Array.Empty()); + LoggerFor(reporting).Debug(message); } /// public void Debug(Type reporting, string messageTemplate, params object[] propertyValues) { - var logger = Log.Logger; - logger?.ForContext(reporting).Debug(messageTemplate, propertyValues); + LoggerFor(reporting).Debug(messageTemplate, propertyValues); } /// public void Verbose(Type reporting, string message) { - Verbose(reporting, message, Array.Empty()); + LoggerFor(reporting).Verbose(message); } /// public void Verbose(Type reporting, string messageTemplate, params object[] propertyValues) { - var logger = Log.Logger; - logger?.ForContext(reporting).Verbose(messageTemplate, propertyValues); + LoggerFor(reporting).Verbose(messageTemplate, propertyValues); } - - } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 099664ecde..00ef58e0a2 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -336,8 +336,10 @@ - - + + + + @@ -576,7 +578,7 @@ - + diff --git a/src/Umbraco.Core/UmbracoApplicationBase.cs b/src/Umbraco.Core/UmbracoApplicationBase.cs index 14b94fe65c..979bc380ff 100644 --- a/src/Umbraco.Core/UmbracoApplicationBase.cs +++ b/src/Umbraco.Core/UmbracoApplicationBase.cs @@ -3,10 +3,9 @@ using System.Reflection; using System.Threading; using System.Web; using System.Web.Hosting; -using Serilog; using Umbraco.Core.Composing; using Umbraco.Core.Logging; -using ILogger = Umbraco.Core.Logging.ILogger; +using Umbraco.Core.Logging.Serilog; namespace Umbraco.Core { @@ -27,7 +26,7 @@ namespace Umbraco.Core /// protected virtual ILogger GetLogger() { - return Logger.CreateWithDefaultConfiguration(); + return SerilogLogger.CreateWithDefaultConfiguration(); } /// @@ -170,13 +169,6 @@ namespace Umbraco.Core _runtime = null; } - // dispose the container and everything - // but first, capture the looger! - var logger = Current.Logger; - Current.Reset(); - - if (SystemUtilities.GetCurrentTrustLevel() != AspNetHostingPermissionLevel.Unrestricted) return; - // try to log the detailed shutdown message (typical asp.net hack: http://weblogs.asp.net/scottgu/433194) try { @@ -194,7 +186,7 @@ namespace Umbraco.Core BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, null, runtime, null); - logger.Info("Application shutdown. Details: {ShutdownReason}\r\n\r\n_shutDownMessage={ShutdownMessage}\r\n\r\n_shutDownStack={ShutdownStack}", + Current.Logger.Info("Application shutdown. Details: {ShutdownReason}\r\n\r\n_shutDownMessage={ShutdownMessage}\r\n\r\n_shutDownStack={ShutdownStack}", HostingEnvironment.ShutdownReason, shutDownMessage, shutDownStack); @@ -202,20 +194,19 @@ namespace Umbraco.Core catch (Exception) { //if for some reason that fails, then log the normal output - logger.Info("Application shutdown. Reason: {ShutdownReason}", HostingEnvironment.ShutdownReason); + Current.Logger.Info("Application shutdown. Reason: {ShutdownReason}", HostingEnvironment.ShutdownReason); } + + // dispose the container and everything + Current.Reset(); } // called by ASP.NET (auto event wireup) once per app domain // sender is System.Web.HttpApplicationFactory, evargs is EventArgs.Empty protected void Application_End(object sender, EventArgs evargs) { - HandleApplicationEnd(); OnApplicationEnd(sender, evargs); - - //Not sure if we need to do this - as my POC approach I never had to deal with this - //As the LightInject container when tearing down will dispose of Serilog AFAIK - Log.CloseAndFlush(); + HandleApplicationEnd(); } #endregion diff --git a/src/Umbraco.Tests/Models/VariationTests.cs b/src/Umbraco.Tests/Models/VariationTests.cs index 6fdda7c1ff..278d8408a0 100644 --- a/src/Umbraco.Tests/Models/VariationTests.cs +++ b/src/Umbraco.Tests/Models/VariationTests.cs @@ -429,14 +429,15 @@ namespace Umbraco.Tests.Models Assert.IsTrue(content.IsCultureAvailable(langUk)); Assert.IsFalse(content.IsCulturePublished(langUk)); Assert.IsNull(content.GetPublishName(langUk)); - Assert.IsNull(content.GetPublishDate(langUk)); // not published - Assert.IsTrue(content.IsCultureEdited(langEs)); // not published, so... edited + Assert.IsNull(content.GetPublishDate(langUk)); // not published Assert.IsFalse(content.IsCultureAvailable(langEs)); + Assert.IsFalse(content.IsCultureEdited(langEs)); // not avail, so... not edited Assert.IsFalse(content.IsCulturePublished(langEs)); + + // not published! Assert.IsNull(content.GetPublishName(langEs)); - Assert.IsNull(content.GetPublishDate(langEs)); // not published! - Assert.IsTrue(content.IsCultureEdited(langEs)); // not published, so... edited + Assert.IsNull(content.GetPublishDate(langEs)); // cannot test IsCultureEdited here - as that requires the content service and repository // see: ContentServiceTests.Can_SaveRead_Variations diff --git a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs index 6edf85718d..7be52c7212 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs @@ -45,7 +45,7 @@ namespace Umbraco.Tests.Persistence.Repositories // Assert Assert.That(language, Is.Not.Null); Assert.That(language.HasIdentity, Is.True); - Assert.That(language.CultureName, Is.EqualTo("en-US")); + Assert.That(language.CultureName, Is.EqualTo("English (United States)")); Assert.That(language.IsoCode, Is.EqualTo("en-US")); } } @@ -222,7 +222,7 @@ namespace Umbraco.Tests.Persistence.Repositories using (var scope = provider.CreateScope()) { var repository = CreateRepository(provider); - + var languageBR = (ILanguage)new Language("pt-BR") { CultureName = "pt-BR", IsDefaultVariantLanguage = true, Mandatory = true }; repository.Save(languageBR); var languageEN = new Language("en-AU") { CultureName = "en-AU" }; diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index d321e9f5e7..2f1cb74e80 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -2621,9 +2621,9 @@ namespace Umbraco.Tests.Services AssertPerCulture(content, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, false), (langDe, false)); AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, false), (langDe, false)); - // not published => must be edited - AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); - AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + // not published => must be edited, if available + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false)); // act @@ -2663,8 +2663,8 @@ namespace Umbraco.Tests.Services AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false)); // fr and uk, published without changes, not edited - AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false), (langDe, true)); - AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false), (langDe, true)); + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, false), (langUk, false), (langDe, false)); AssertPerCulture(content, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw AssertPerCulture(content2, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw @@ -2723,8 +2723,8 @@ namespace Umbraco.Tests.Services AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, true), (langUk, true), (langDe, false)); // we have changed values so now fr and uk are edited - AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); - AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false)); AssertPerCulture(content, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw AssertPerCulture(content2, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langFr, false), (langUk, false)); // DE would throw @@ -2767,8 +2767,8 @@ namespace Umbraco.Tests.Services AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); // and so, fr has to be edited - AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); - AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false)); AssertPerCulture(content, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw AssertPerCulture(content2, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw @@ -2814,8 +2814,8 @@ namespace Umbraco.Tests.Services AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); // and so, fr has to be edited - uk still is - AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); - AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false)); AssertPerCulture(content, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw AssertPerCulture(content2, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw @@ -2856,8 +2856,8 @@ namespace Umbraco.Tests.Services AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); // no change, back to published - AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); - AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, true)); + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, true), (langDe, false)); AssertPerCulture(content, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw AssertPerCulture(content2, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw @@ -2879,8 +2879,8 @@ namespace Umbraco.Tests.Services AssertPerCulture(content2, (x, c) => x.IsCulturePublished(c), (langFr, false), (langUk, true), (langDe, false)); // now, uk is no more edited - AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false), (langDe, true)); - AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false), (langDe, true)); + AssertPerCulture(content, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false), (langDe, false)); + AssertPerCulture(content2, (x, c) => x.IsCultureEdited(c), (langFr, true), (langUk, false), (langDe, false)); AssertPerCulture(content, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw AssertPerCulture(content2, (x, c) => x.GetPublishDate(c) == DateTime.MinValue, (langUk, false)); // FR, DE would throw diff --git a/src/Umbraco.Tests/TestHelpers/ConsoleLogger.cs b/src/Umbraco.Tests/TestHelpers/ConsoleLogger.cs index 9caa4b2aa1..a3e36db363 100644 --- a/src/Umbraco.Tests/TestHelpers/ConsoleLogger.cs +++ b/src/Umbraco.Tests/TestHelpers/ConsoleLogger.cs @@ -5,6 +5,9 @@ namespace Umbraco.Tests.TestHelpers { public class ConsoleLogger : ILogger { + public bool IsEnabled(Type reporting, LogLevel level) + => true; + public void Fatal(Type reporting, Exception exception, string message) { Console.WriteLine("FATAL {0} - {1}", reporting.Name, message); @@ -22,15 +25,15 @@ namespace Umbraco.Tests.TestHelpers Console.WriteLine("FATAL {0} - {1}", reporting.Name, message); } - public void Fatal(Type reporting, Exception exception, string format, params object[] args) + public void Fatal(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) { - Console.WriteLine("FATAL {0} - {1}", reporting.Name, string.Format(format, args)); + Console.WriteLine("FATAL {0} - {1}", reporting.Name, MessageTemplates.Render(messageTemplate, propertyValues)); Console.WriteLine(exception); } - public void Fatal(Type reporting, string format, params object[] args) + public void Fatal(Type reporting, string messageTemplate, params object[] propertyValues) { - Console.WriteLine("FATAL {0} - {1}", reporting.Name, string.Format(format, args)); + Console.WriteLine("FATAL {0} - {1}", reporting.Name, MessageTemplates.Render(messageTemplate, propertyValues)); } public void Error(Type reporting, Exception exception, string message) @@ -50,15 +53,15 @@ namespace Umbraco.Tests.TestHelpers Console.WriteLine("ERROR {0} - {1}", reporting.Name, message); } - public void Error(Type reporting, Exception exception, string format, params object[] args) + public void Error(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues) { - Console.WriteLine("ERROR {0} - {1}", reporting.Name, string.Format(format, args)); + Console.WriteLine("ERROR {0} - {1}", reporting.Name, MessageTemplates.Render(messageTemplate, propertyValues)); Console.WriteLine(exception); } - public void Error(Type reporting, string format, params object[] args) + public void Error(Type reporting, string messageTemplate, params object[] propertyValues) { - Console.WriteLine("ERROR {0} - {1}", reporting.Name, string.Format(format, args)); + Console.WriteLine("ERROR {0} - {1}", reporting.Name, MessageTemplates.Render(messageTemplate, propertyValues)); } public void Warn(Type reporting, string message) @@ -66,9 +69,9 @@ namespace Umbraco.Tests.TestHelpers Console.WriteLine("WARN {0} - {1}", reporting.Name, message); } - public void Warn(Type reporting, string format, params object[] args) + public void Warn(Type reporting, string message, params object[] propertyValues) { - Console.WriteLine("WARN {0} - {1}", reporting.Name, string.Format(format, args)); + Console.WriteLine("WARN {0} - {1}", reporting.Name, MessageTemplates.Render(message, propertyValues)); } public void Warn(Type reporting, Exception exception, string message) @@ -76,16 +79,16 @@ namespace Umbraco.Tests.TestHelpers Console.WriteLine("WARN {0} - {1}", reporting.Name, message); Console.WriteLine(exception); } - - public void Warn(Type reporting, Exception exception, string format, params object[] args) + + public void Warn(Type reporting, Exception exception, string message, params object[] propertyValues) { - Console.WriteLine("WARN {0} - {1}", reporting.Name, string.Format(format, args)); + Console.WriteLine("WARN {0} - {1}", reporting.Name, MessageTemplates.Render(message, propertyValues)); Console.WriteLine(exception); } - public void Info(Type reporting, string format, params object[] args) + public void Info(Type reporting, string messageTemplate, params object[] propertyValues) { - Console.WriteLine("INFO {0} - {1}", reporting.Name, string.Format(format, args)); + Console.WriteLine("INFO {0} - {1}", reporting.Name, MessageTemplates.Render(messageTemplate, propertyValues)); } public void Info(Type reporting, string message) @@ -98,9 +101,9 @@ namespace Umbraco.Tests.TestHelpers Console.WriteLine("DEBUG {0} - {1}", reporting.Name, message); } - public void Debug(Type reporting, string format, params object[] args) + public void Debug(Type reporting, string messageTemplate, params object[] propertyValues) { - Console.WriteLine("DEBUG {0} - {1}", reporting.Name, string.Format(format, args)); + Console.WriteLine("DEBUG {0} - {1}", reporting.Name, MessageTemplates.Render(messageTemplate, propertyValues)); } public void Verbose(Type reporting, string message) @@ -108,9 +111,9 @@ namespace Umbraco.Tests.TestHelpers Console.WriteLine("VERBOSE {0} - {1}", reporting.Name, message); } - public void Verbose(Type reporting, string format, params object[] args) + public void Verbose(Type reporting, string messageTemplate, params object[] propertyValues) { - Console.WriteLine("VERBOSE {0} - {1}", reporting.Name, string.Format(format, args)); + Console.WriteLine("VERBOSE {0} - {1}", reporting.Name, MessageTemplates.Render(messageTemplate, propertyValues)); } } } diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index 7dcb1df77c..1fe814e4c6 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -127,6 +127,11 @@ namespace Umbraco.Tests.TestHelpers { if (!(expected is string) && expected is IEnumerable) { + // sort property collection by alias, not by property ids + // on members, built-in properties don't have ids (always zero) + if (expected is PropertyCollection) + sorter = e => ((PropertyCollection) e).OrderBy(x => x.Alias); + // compare lists AssertListsAreEqual(property, (IEnumerable) actual, (IEnumerable) expected, sorter, dateDeltaMilliseconds); } @@ -168,6 +173,8 @@ namespace Umbraco.Tests.TestHelpers private static void AssertListsAreEqual(PropertyInfo property, IEnumerable expected, IEnumerable actual, Func sorter = null, int dateDeltaMilliseconds = 0) { + + if (sorter == null) { // this is pretty hackerific but saves us some code to write diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 7d0f53d404..5951a87d77 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.IO.MediaPathSchemes; using Umbraco.Core.Logging; +using Umbraco.Core.Logging.Serilog; using Umbraco.Core.Manifest; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; @@ -164,7 +165,7 @@ namespace Umbraco.Tests.Testing } else if (option == UmbracoTestOptions.Logger.Serilog) { - Container.RegisterSingleton(f => new Logger(new FileInfo(TestHelper.MapPathForTest("~/unit-test.config")))); + Container.RegisterSingleton(f => new SerilogLogger(new FileInfo(TestHelper.MapPathForTest("~/unit-test.config")))); Container.RegisterSingleton(f => new LogProfiler(f.GetInstance())); } diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs index d512e53b9c..5ca195849b 100644 --- a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs @@ -1,8 +1,8 @@ using System.IO; using NUnit.Framework; -using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Logging; +using Umbraco.Core.Logging.Serilog; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; @@ -14,18 +14,12 @@ namespace Umbraco.Tests.UmbracoExamine [OneTimeSetUp] public void InitializeFixture() { - var logger = new Logger(new FileInfo(TestHelper.MapPathForTest("~/unit-test.config"))); + var logger = new SerilogLogger(new FileInfo(TestHelper.MapPathForTest("~/unit-test.config"))); _profilingLogger = new ProfilingLogger(logger, new LogProfiler(logger)); } private ProfilingLogger _profilingLogger; - protected override ProfilingLogger ProfilingLogger - { - get - { - return _profilingLogger; - } - } + protected override ProfilingLogger ProfilingLogger => _profilingLogger; /// /// sets up resolvers before resolution is frozen