From 21d330116b53cf1b6753f994ec4e3c037bec752e Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 2 Oct 2024 19:20:28 +0200 Subject: [PATCH 001/224] Removed NLog.Fluent since obsolete (#5617) --- src/NLog/Fluent/Log.cs | 179 ------ src/NLog/Fluent/LogBuilder.cs | 389 ------------ src/NLog/Fluent/LoggerExtensions.cs | 156 ----- .../NLog.UnitTests/Fluent/LogBuilderTests.cs | 582 ------------------ 4 files changed, 1306 deletions(-) delete mode 100644 src/NLog/Fluent/Log.cs delete mode 100644 src/NLog/Fluent/LogBuilder.cs delete mode 100644 src/NLog/Fluent/LoggerExtensions.cs delete mode 100644 tests/NLog.UnitTests/Fluent/LogBuilderTests.cs diff --git a/src/NLog/Fluent/Log.cs b/src/NLog/Fluent/Log.cs deleted file mode 100644 index 07e2a14450..0000000000 --- a/src/NLog/Fluent/Log.cs +++ /dev/null @@ -1,179 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -#if !NET35 && !NET40 - -namespace NLog.Fluent -{ - using System; - using System.ComponentModel; - using System.IO; - using System.Runtime.CompilerServices; - using NLog.Common; - - /// - /// A global logging class using caller info to find the logger. - /// - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.3")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static class Log - { - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.0")] - private static readonly ILogger _logger = LogManager.GetCurrentClassLogger(); - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event with the specified . - /// - /// The log level. - /// The full path of the source file that contains the caller. This is the file path at the time of compile. - /// An instance of the fluent . - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Level(LogLevel logLevel, [CallerFilePath] string callerFilePath = null) - { - return Create(logLevel, callerFilePath); - } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event at the Trace level. - /// - /// The full path of the source file that contains the caller. This is the file path at the time of compile. - /// An instance of the fluent . - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Trace([CallerFilePath] string callerFilePath = null) - { - return Create(LogLevel.Trace, callerFilePath); - } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event at the Debug level. - /// - /// The full path of the source file that contains the caller. This is the file path at the time of compile. - /// An instance of the fluent . - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Debug([CallerFilePath] string callerFilePath = null) - { - return Create(LogLevel.Debug, callerFilePath); - } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event at the Info level. - /// - /// The full path of the source file that contains the caller. This is the file path at the time of compile. - /// An instance of the fluent . - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Info([CallerFilePath] string callerFilePath = null) - { - return Create(LogLevel.Info, callerFilePath); - } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event at the Warn level. - /// - /// The full path of the source file that contains the caller. This is the file path at the time of compile. - /// An instance of the fluent . - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Warn([CallerFilePath] string callerFilePath = null) - { - return Create(LogLevel.Warn, callerFilePath); - } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event at the Error level. - /// - /// The full path of the source file that contains the caller. This is the file path at the time of compile. - /// An instance of the fluent . - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Error([CallerFilePath] string callerFilePath = null) - { - return Create(LogLevel.Error, callerFilePath); - } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event at the Fatal level. - /// - /// The full path of the source file that contains the caller. This is the file path at the time of compile. - /// An instance of the fluent . - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Fatal([CallerFilePath] string callerFilePath = null) - { - return Create(LogLevel.Fatal, callerFilePath); - } - - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - private static LogBuilder Create(LogLevel logLevel, string callerFilePath) - { - var logger = GetLogger(callerFilePath); - var builder = new LogBuilder(logger, logLevel); - return builder; - } - - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.0")] - private static ILogger GetLogger(string callerFilePath) - { - try - { - string name = !string.IsNullOrWhiteSpace(callerFilePath) ? Path.GetFileNameWithoutExtension(callerFilePath) : null; - return string.IsNullOrWhiteSpace(name) ? _logger : LogManager.GetLogger(name); - } - catch (Exception ex) - { - InternalLogger.Warn(ex, "Error when converting CallerFilePath to logger name."); - return _logger; - } - } - } -} - -#endif diff --git a/src/NLog/Fluent/LogBuilder.cs b/src/NLog/Fluent/LogBuilder.cs deleted file mode 100644 index a6bbe8dcb6..0000000000 --- a/src/NLog/Fluent/LogBuilder.cs +++ /dev/null @@ -1,389 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -using System; -using System.Collections; -using System.ComponentModel; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using JetBrains.Annotations; -using NLog.Internal; - -namespace NLog.Fluent -{ - /// - /// Obsolete and replaced by with NLog v5. - /// - /// A fluent class to build log events for NLog. - /// - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public class LogBuilder - { - private readonly LogEventInfo _logEvent; - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - /// The to send the log event. - [CLSCompliant(false)] - public LogBuilder(ILogger logger) - : this(logger, LogLevel.Debug) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The to send the log event. - /// The for the log event. - [CLSCompliant(false)] - public LogBuilder(ILogger logger, LogLevel logLevel) - { - Guard.ThrowIfNull(logger); - Guard.ThrowIfNull(logLevel); - - _logger = logger; - _logEvent = new LogEventInfo() { LoggerName = logger.Name, Level = logLevel }; - } - - /// - /// Gets the created by the builder. - /// - public LogEventInfo LogEventInfo => _logEvent; - - /// - /// Sets the information of the logging event. - /// - /// The exception information of the logging event. - /// current for chaining calls. - public LogBuilder Exception(Exception exception) - { - _logEvent.Exception = exception; - return this; - } - - /// - /// Sets the level of the logging event. - /// - /// The level of the logging event. - /// current for chaining calls. - public LogBuilder Level(LogLevel logLevel) - { - Guard.ThrowIfNull(logLevel); - - _logEvent.Level = logLevel; - return this; - } - - /// - /// Sets the logger name of the logging event. - /// - /// The logger name of the logging event. - /// current for chaining calls. - public LogBuilder LoggerName([Localizable(false)] string loggerName) - { - _logEvent.LoggerName = loggerName; - return this; - } - - /// - /// Sets the log message on the logging event. - /// - /// The log message for the logging event. - /// current for chaining calls. - public LogBuilder Message([Localizable(false)] string message) - { - _logEvent.Message = message; - - return this; - } - - /// - /// Sets the log message and parameters for formatting on the logging event. - /// - /// A composite format string. - /// The object to format. - /// current for chaining calls. - [MessageTemplateFormatMethod("format")] - public LogBuilder Message([Localizable(false)][StructuredMessageTemplate] string format, object arg0) - { - _logEvent.Message = format; - _logEvent.Parameters = new[] { arg0 }; - - return this; - } - - /// - /// Sets the log message and parameters for formatting on the logging event. - /// - /// A composite format string. - /// The first object to format. - /// The second object to format. - /// current for chaining calls. - [MessageTemplateFormatMethod("format")] - public LogBuilder Message([Localizable(false)][StructuredMessageTemplate] string format, object arg0, object arg1) - { - _logEvent.Message = format; - _logEvent.Parameters = new[] { arg0, arg1 }; - - return this; - } - - /// - /// Sets the log message and parameters for formatting on the logging event. - /// - /// A composite format string. - /// The first object to format. - /// The second object to format. - /// The third object to format. - /// current for chaining calls. - [MessageTemplateFormatMethod("format")] - public LogBuilder Message([Localizable(false)][StructuredMessageTemplate] string format, object arg0, object arg1, object arg2) - { - _logEvent.Message = format; - _logEvent.Parameters = new[] { arg0, arg1, arg2 }; - - return this; - } - - /// - /// Sets the log message and parameters for formatting on the logging event. - /// - /// A composite format string. - /// The first object to format. - /// The second object to format. - /// The third object to format. - /// The fourth object to format. - /// current for chaining calls. - [MessageTemplateFormatMethod("format")] - public LogBuilder Message([Localizable(false)][StructuredMessageTemplate] string format, object arg0, object arg1, object arg2, object arg3) - { - _logEvent.Message = format; - _logEvent.Parameters = new[] { arg0, arg1, arg2, arg3 }; - - return this; - } - - /// - /// Sets the log message and parameters for formatting on the logging event. - /// - /// A composite format string. - /// An object array that contains zero or more objects to format. - /// current for chaining calls. - [MessageTemplateFormatMethod("format")] - public LogBuilder Message([Localizable(false)][StructuredMessageTemplate] string format, params object[] args) - { - _logEvent.Message = format; - _logEvent.Parameters = args; - - return this; - } - - /// - /// Sets the log message and parameters for formatting on the logging event. - /// - /// An object that supplies culture-specific formatting information. - /// A composite format string. - /// An object array that contains zero or more objects to format. - /// current for chaining calls. - [MessageTemplateFormatMethod("format")] - public LogBuilder Message(IFormatProvider provider, [Localizable(false)][StructuredMessageTemplate] string format, params object[] args) - { - _logEvent.FormatProvider = provider; - _logEvent.Message = format; - _logEvent.Parameters = args; - - return this; - } - - /// - /// Sets a per-event context property on the logging event. - /// - /// The name of the context property. - /// The value of the context property. - /// current for chaining calls. - public LogBuilder Property(object name, object value) - { - Guard.ThrowIfNull(name); - - _logEvent.Properties[name] = value; - return this; - } - - /// - /// Sets multiple per-event context properties on the logging event. - /// - /// The properties to set. - /// current for chaining calls. - public LogBuilder Properties(IDictionary properties) - { - Guard.ThrowIfNull(properties); - - foreach (var key in properties.Keys) - { - _logEvent.Properties[key] = properties[key]; - } - return this; - } - - /// - /// Sets the timestamp of the logging event. - /// - /// The timestamp of the logging event. - /// current for chaining calls. - public LogBuilder TimeStamp(DateTime timeStamp) - { - _logEvent.TimeStamp = timeStamp; - return this; - } - - /// - /// Sets the stack trace for the event info. - /// - /// The stack trace. - /// Index of the first user stack frame within the stack trace. - /// current for chaining calls. - public LogBuilder StackTrace(StackTrace stackTrace, int userStackFrame) - { - _logEvent.SetStackTrace(stackTrace, userStackFrame); - return this; - } - - /// - /// Writes the log event to the underlying logger. - /// - /// The method or property name of the caller to the method. This is set at by the compiler. - /// The full path of the source file that contains the caller. This is set at by the compiler. - /// The line number in the source file at which the method is called. This is set at by the compiler. -#if !NET35 && !NET40 - public void Write( - [CallerMemberName] string callerMemberName = null, - [CallerFilePath] string callerFilePath = null, - [CallerLineNumber] int callerLineNumber = 0) - { - if (!_logger.IsEnabled(_logEvent.Level)) - return; - - SetCallerInfo(callerMemberName, callerFilePath, callerLineNumber); - - _logger.Log(_logEvent); - } -#else - public void Write( - string callerMemberName = null, - string callerFilePath = null, - int callerLineNumber = 0) - { - _logger.Log(_logEvent); - } -#endif - - /// - /// Writes the log event to the underlying logger if the condition delegate is true. - /// - /// If condition is true, write log event; otherwise ignore event. - /// The method or property name of the caller to the method. This is set at by the compiler. - /// The full path of the source file that contains the caller. This is set at by the compiler. - /// The line number in the source file at which the method is called. This is set at by the compiler. -#if !NET35 && !NET40 - public void WriteIf( - Func condition, - [CallerMemberName] string callerMemberName = null, - [CallerFilePath] string callerFilePath = null, - [CallerLineNumber] int callerLineNumber = 0) - { - if (condition is null || !condition() || !_logger.IsEnabled(_logEvent.Level)) - return; - - SetCallerInfo(callerMemberName, callerFilePath, callerLineNumber); - - _logger.Log(_logEvent); - } -#else - public void WriteIf( - Func condition, - string callerMemberName = null, - string callerFilePath = null, - int callerLineNumber = 0) - { - if (condition is null || !condition() || !_logger.IsEnabled(_logEvent.Level)) - return; - - _logger.Log(_logEvent); - } -#endif - - /// - /// Writes the log event to the underlying logger if the condition is true. - /// - /// If condition is true, write log event; otherwise ignore event. - /// The method or property name of the caller to the method. This is set at by the compiler. - /// The full path of the source file that contains the caller. This is set at by the compiler. - /// The line number in the source file at which the method is called. This is set at by the compiler. -#if !NET35 && !NET40 - public void WriteIf( - bool condition, - [CallerMemberName] string callerMemberName = null, - [CallerFilePath] string callerFilePath = null, - [CallerLineNumber] int callerLineNumber = 0) - { - if (!condition || !_logger.IsEnabled(_logEvent.Level)) - return; - - SetCallerInfo(callerMemberName, callerFilePath, callerLineNumber); - - _logger.Log(_logEvent); - } -#else - public void WriteIf( - bool condition, - string callerMemberName = null, - string callerFilePath = null, - int callerLineNumber = 0) - { - if (!condition || !_logger.IsEnabled(_logEvent.Level)) - return; - - _logger.Log(_logEvent); - } -#endif - - private void SetCallerInfo(string callerMethodName, string callerFilePath, int callerLineNumber) - { - if (callerMethodName != null || callerFilePath != null || callerLineNumber != 0) - _logEvent.SetCallerInfo(null, callerMethodName, callerFilePath, callerLineNumber); - } - } -} diff --git a/src/NLog/Fluent/LoggerExtensions.cs b/src/NLog/Fluent/LoggerExtensions.cs deleted file mode 100644 index 61faad7bdd..0000000000 --- a/src/NLog/Fluent/LoggerExtensions.cs +++ /dev/null @@ -1,156 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -using System; -using System.ComponentModel; - -namespace NLog.Fluent -{ - /// - /// Extension methods for NLog . - /// - public static class LoggerExtensions - { - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event with the specified . - /// - /// The logger to write the log event to. - /// The log level. - /// current for chaining calls. - [CLSCompliant(false)] - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Log(this ILogger logger, LogLevel logLevel) - { - var builder = new LogBuilder(logger, logLevel); - return builder; - } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event at the Trace level. - /// - /// The logger to write the log event to. - /// current for chaining calls. - [CLSCompliant(false)] - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForTraceEvent() and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Trace(this ILogger logger) - { - var builder = new LogBuilder(logger, LogLevel.Trace); - return builder; - } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event at the Debug level. - /// - /// The logger to write the log event to. - /// current for chaining calls. - [CLSCompliant(false)] - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForDebugEvent() and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Debug(this ILogger logger) - { - var builder = new LogBuilder(logger, LogLevel.Debug); - return builder; - } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event at the Info level. - /// - /// The logger to write the log event to. - /// current for chaining calls. - [CLSCompliant(false)] - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForInfoEvent() and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Info(this ILogger logger) - { - var builder = new LogBuilder(logger, LogLevel.Info); - return builder; - } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event at the Warn level. - /// - /// The logger to write the log event to. - /// current for chaining calls. - [CLSCompliant(false)] - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForWarnEvent() and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Warn(this ILogger logger) - { - var builder = new LogBuilder(logger, LogLevel.Warn); - return builder; - } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event at the Error level. - /// - /// The logger to write the log event to. - /// current for chaining calls. - [CLSCompliant(false)] - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForErrorEvent() and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Error(this ILogger logger) - { - var builder = new LogBuilder(logger, LogLevel.Error); - return builder; - } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Starts building a log event at the Fatal level. - /// - /// The logger to write the log event to. - /// current for chaining calls. - [CLSCompliant(false)] - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForFatalEvent() and LogEventBuilder. Obsoleted in NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static LogBuilder Fatal(this ILogger logger) - { - var builder = new LogBuilder(logger, LogLevel.Fatal); - return builder; - } - } -} diff --git a/tests/NLog.UnitTests/Fluent/LogBuilderTests.cs b/tests/NLog.UnitTests/Fluent/LogBuilderTests.cs deleted file mode 100644 index 20bfda8296..0000000000 --- a/tests/NLog.UnitTests/Fluent/LogBuilderTests.cs +++ /dev/null @@ -1,582 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.UnitTests.Fluent -{ - using System; - using System.Collections.Generic; - using System.IO; - using NLog.Config; - using NLog.Fluent; - using NLog.Targets; - using Xunit; - - [Obsolete("Obsoleted since it allocates unnecessary. Instead use ILogger.ForLogEvent and LogEventBuilder. Obsoleted in NLog 5.0")] - public class LogBuilderTests : NLogTestBase - { - private static readonly Logger _logger = LogManager.GetLogger("logger1"); - - private LogEventInfo _lastLogEventInfo; - - public LogBuilderTests() - { - var configuration = new LoggingConfiguration(); - - var t1 = new MethodCallTarget("t1", (l, parms) => _lastLogEventInfo = l); - t1.Parameters.Add(new MethodCallParameter("CallSite", "${callsite}")); - var t2 = new DebugTarget { Name = "t2", Layout = "${message}" }; - configuration.AddTarget(t1); - configuration.AddTarget(t2); - configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, t1)); - configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, t2)); - - LogManager.Configuration = configuration; - } - - [Fact] - public void TraceWrite() - { - TraceWrite_internal(() => _logger.Trace()); - } - -#if !NET35 && !NET40 - [Fact] - public void TraceWrite_static_builder() - { - TraceWrite_internal(() => Log.Trace(), true); - } -#endif - - /// - /// func because 1 logbuilder creates 1 message - /// - /// Caution: don't use overloading, that will break xUnit: - /// CATASTROPHIC ERROR OCCURRED: - /// System.ArgumentException: Ambiguous method named TraceWrite in type NLog.UnitTests.Fluent.LogBuilderTests - /// - private void TraceWrite_internal(Func logBuilder, bool isStatic = false) - { - logBuilder() - .Message("This is a test fluent message.") - .Property("Test", "TraceWrite") - .Write(); - - - var loggerName = isStatic ? "LogBuilderTests" : "logger1"; - { - var expectedEvent = new LogEventInfo(LogLevel.Trace, loggerName, "This is a test fluent message."); - expectedEvent.Properties["Test"] = "TraceWrite"; - AssertLastLogEventTarget(expectedEvent); - } - - var ticks = DateTime.Now.Ticks; - logBuilder() - .Message("This is a test fluent message '{0}'.", ticks) - .Property("Test", "TraceWrite") - .Write(); - - { - var rendered = $"This is a test fluent message '{ticks}'."; - var expectedEvent = new LogEventInfo(LogLevel.Trace, loggerName, "This is a test fluent message '{0}'."); - expectedEvent.Properties["Test"] = "TraceWrite"; - AssertLastLogEventTarget(expectedEvent); - AssertDebugLastMessage("t2", rendered); - } - } - - [Fact] - public void TraceWriteProperties() - { - var props = new Dictionary - { - {"prop1", "1"}, - {"prop2", "2"}, - - }; - - _logger.Trace() - .Message("This is a test fluent message.") - .Properties(props).Write(); - - { - var expectedEvent = new LogEventInfo(LogLevel.Trace, "logger1", "This is a test fluent message."); - expectedEvent.Properties["prop1"] = "1"; - expectedEvent.Properties["prop2"] = "2"; - AssertLastLogEventTarget(expectedEvent); - } - } - - [Fact] - public void WarnWriteProperties() - { - var props = new Dictionary - { - {"prop1", "1"}, - {"prop2", "2"}, - - }; - - _logger.Warn() - .Message("This is a test fluent message.") - .Properties(props).Write(); - - { - var expectedEvent = new LogEventInfo(LogLevel.Warn, "logger1", "This is a test fluent message."); - expectedEvent.Properties["prop1"] = "1"; - expectedEvent.Properties["prop2"] = "2"; - AssertLastLogEventTarget(expectedEvent); - } - } - - [Fact] - public void LogWriteProperties() - { - var props = new Dictionary - { - {"prop1", "1"}, - {"prop2", "2"}, - }; - - // Loop to verify caller-attribute-caching-lookup - for (int i = 0; i < 2; ++i) - { - _logger.Log(LogLevel.Fatal) - .Message("This is a test fluent message.") - .Properties(props).Write(); - - var expectedEvent = new LogEventInfo(LogLevel.Fatal, "logger1", "This is a test fluent message."); - expectedEvent.Properties["prop1"] = "1"; - expectedEvent.Properties["prop2"] = "2"; - AssertLastLogEventTarget(expectedEvent); - -#if !NET35 && !NET40 - Assert.Equal(GetType().ToString(), _lastLogEventInfo.CallerClassName); -#endif - } - } - - [Fact] - public void LogOffWriteProperties() - { - var props = new Dictionary - { - {"prop1", "1"}, - {"prop2", "2"}, - - }; - var props2 = new Dictionary - { - {"prop1", "4"}, - {"prop2", "5"}, - - }; - - _logger.Log(LogLevel.Fatal) - .Message("This is a test fluent message.") - .Properties(props).Write(); - - _logger.Log(LogLevel.Off) - .Message("dont log this.") - .Properties(props2).Write(); - - { - var expectedEvent = new LogEventInfo(LogLevel.Fatal, "logger1", "This is a test fluent message."); - expectedEvent.Properties["prop1"] = "1"; - expectedEvent.Properties["prop2"] = "2"; - AssertLastLogEventTarget(expectedEvent); - } - } - -#if !NET35 && !NET40 - [Fact] - public void LevelWriteProperties() - { - var props = new Dictionary - { - {"prop1", "1"}, - {"prop2", "2"}, - - }; - - Log.Level(LogLevel.Fatal) - .Message("This is a test fluent message.") - .Properties(props).Write(); - - { - var expectedEvent = new LogEventInfo(LogLevel.Fatal, "LogBuilderTests", "This is a test fluent message."); - expectedEvent.Properties["prop1"] = "1"; - expectedEvent.Properties["prop2"] = "2"; - AssertLastLogEventTarget(expectedEvent); - } - } -#endif - - [Fact] - public void TraceIfWrite() - { - _logger.Trace() - .Message("This is a test fluent message.1") - .Property("Test", "TraceWrite") - .Write(); - - { - var expectedEvent = new LogEventInfo(LogLevel.Trace, "logger1", "This is a test fluent message.1"); - expectedEvent.Properties["Test"] = "TraceWrite"; - AssertLastLogEventTarget(expectedEvent); - } - - int v = 1; - _logger.Trace() - .Message("This is a test fluent WriteIf message '{0}'.", DateTime.Now.Ticks) - .Property("Test", "TraceWrite") - .WriteIf(() => v == 1); - - - { - var expectedEvent = new LogEventInfo(LogLevel.Trace, "logger1", "This is a test fluent WriteIf message '{0}'."); - expectedEvent.Properties["Test"] = "TraceWrite"; - AssertLastLogEventTarget(expectedEvent); - AssertDebugLastMessageContains("t2", "This is a test fluent WriteIf message "); - } - - _logger.Trace() - .Message("dont write this! '{0}'.", DateTime.Now.Ticks) - .Property("Test", "TraceWrite") - .WriteIf(() => { return false; }); - - { - var expectedEvent = new LogEventInfo(LogLevel.Trace, "logger1", "This is a test fluent WriteIf message '{0}'."); - expectedEvent.Properties["Test"] = "TraceWrite"; - AssertLastLogEventTarget(expectedEvent); - AssertDebugLastMessageContains("t2", "This is a test fluent WriteIf message "); - } - - _logger.Trace() - .Message("This is a test fluent WriteIf message '{0}'.", DateTime.Now.Ticks) - .Property("Test", "TraceWrite") - .WriteIf(v == 1); - - - { - var expectedEvent = new LogEventInfo(LogLevel.Trace, "logger1", "This is a test fluent WriteIf message '{0}'."); - expectedEvent.Properties["Test"] = "TraceWrite"; - AssertLastLogEventTarget(expectedEvent); - AssertDebugLastMessageContains("t2", "This is a test fluent WriteIf message "); - } - - _logger.Trace() - .Message("Should Not WriteIf message '{0}'.", DateTime.Now.Ticks) - .Property("Test", "TraceWrite") - .WriteIf(v > 1); - - - { - //previous - var expectedEvent = new LogEventInfo(LogLevel.Trace, "logger1", "This is a test fluent WriteIf message '{0}'."); - expectedEvent.Properties["Test"] = "TraceWrite"; - AssertLastLogEventTarget(expectedEvent); - AssertDebugLastMessageContains("t2", "This is a test fluent WriteIf message "); - } - } - - [Fact] - public void InfoWrite() - { - InfoWrite_internal(() => _logger.Info()); - } - -#if !NET35 && !NET40 - [Fact] - public void InfoWrite_static_builder() - { - InfoWrite_internal(() => Log.Info(), true); - } -#endif - - /// - /// func because 1 logbuilder creates 1 message - /// - /// Caution: don't use overloading, that will break xUnit: - /// CATASTROPHIC ERROR OCCURRED: - /// System.ArgumentException: Ambiguous method named TraceWrite in type NLog.UnitTests.Fluent.LogBuilderTests - /// - private void InfoWrite_internal(Func logBuilder, bool isStatic = false) - { - logBuilder() - .Message("This is a test fluent message.") - .Property("Test", "InfoWrite") - .Write(); - - var loggerName = isStatic ? "LogBuilderTests" : "logger1"; - { - //previous - var expectedEvent = new LogEventInfo(LogLevel.Info, loggerName, "This is a test fluent message."); - expectedEvent.Properties["Test"] = "InfoWrite"; - AssertLastLogEventTarget(expectedEvent); - } - - logBuilder() - .Message("This is a test fluent message '{0}'.", DateTime.Now.Ticks) - .Property("Test", "InfoWrite") - .Write(); - - { - //previous - var expectedEvent = new LogEventInfo(LogLevel.Info, loggerName, "This is a test fluent message '{0}'."); - expectedEvent.Properties["Test"] = "InfoWrite"; - AssertLastLogEventTarget(expectedEvent); - AssertDebugLastMessageContains("t2", "This is a test fluent message '"); - } - } - - [Fact] - public void DebugWrite() - { - ErrorWrite_internal(() => _logger.Debug(), LogLevel.Debug); - } - -#if !NET35 && !NET40 - [Fact] - public void DebugWrite_static_builder() - { - ErrorWrite_internal(() => Log.Debug(), LogLevel.Debug, true); - } -#endif - - [Fact] - public void FatalWrite() - { - ErrorWrite_internal(() => _logger.Fatal(), LogLevel.Fatal); - } - -#if !NET35 && !NET40 - [Fact] - public void FatalWrite_static_builder() - { - ErrorWrite_internal(() => Log.Fatal(), LogLevel.Fatal, true); - } -#endif - - [Fact] - public void ErrorWrite() - { - ErrorWrite_internal(() => _logger.Error(), LogLevel.Error); - } - -#if !NET35 && !NET40 - [Fact] - public void ErrorWrite_static_builder() - { - ErrorWrite_internal(() => Log.Error(), LogLevel.Error, true); - } -#endif - - [Fact] - public void LogBuilder_null_lead_to_ArgumentNullException() - { - var logger = LogManager.GetLogger("a"); - Assert.Throws(() => new LogBuilder(null, LogLevel.Debug)); - Assert.Throws(() => new LogBuilder(null)); - Assert.Throws(() => new LogBuilder(logger, null)); - - var logBuilder = new LogBuilder(logger); - Assert.Throws(() => logBuilder.Properties(null)); - Assert.Throws(() => logBuilder.Property(null, "b")); - } - - [Fact] - public void LogBuilder_nLogEventInfo() - { - var d = new DateTime(2015, 01, 30, 14, 30, 5); - var logEventInfo = new LogBuilder(LogManager.GetLogger("a")).LoggerName("b").Level(LogLevel.Fatal).TimeStamp(d).LogEventInfo; - - Assert.Equal("b", logEventInfo.LoggerName); - Assert.Equal(LogLevel.Fatal, logEventInfo.Level); - Assert.Equal(d, logEventInfo.TimeStamp); - } - - [Fact] - public void LogBuilder_exception_only() - { - var ex = new Exception("Exception message1"); - - _logger.Error() - .Exception(ex) - .Write(); - - var expectedEvent = new LogEventInfo(LogLevel.Error, "logger1", null) { Exception = ex }; - AssertLastLogEventTarget(expectedEvent); - } - - [Fact] - public void LogBuilder_null_logLevel() - { - Assert.Throws(() => _logger.Error().Level(null)); - } - - [Fact] - public void LogBuilder_message_overloadsTest() - { - LogManager.ThrowExceptions = true; - - _logger.Debug() - .Message("Message with {0} arg", 1) - .Write(); - AssertDebugLastMessage("t2", "Message with 1 arg"); - - _logger.Debug() - .Message("Message with {0} args. {1}", 2, "YES") - .Write(); - AssertDebugLastMessage("t2", "Message with 2 args. YES"); - - _logger.Debug() - .Message("Message with {0} args. {1} {2}", 3, ":) ", 2) - .Write(); - AssertDebugLastMessage("t2", "Message with 3 args. :) 2"); - - _logger.Debug() - .Message("Message with {0} args. {1} {2}{3}", "more", ":) ", 2, "b") - .Write(); - AssertDebugLastMessage("t2", "Message with more args. :) 2b"); - } - - [Fact] - public void LogBuilder_message_cultureTest() - { - if (IsLinux()) - { - Console.WriteLine("[SKIP] LogBuilderTests.LogBuilder_message_cultureTest because we are running in Travis"); - return; - } - - LogManager.Configuration.DefaultCultureInfo = GetCultureInfo("en-US"); - - _logger.Debug() - .Message("Message with {0} {1} {2} {3}", 4.1, 4.001, new DateTime(2016, 12, 31), true) - .Write(); - AssertDebugLastMessage("t2", "Message with 4.1 4.001 12/31/2016 12:00:00 AM True"); - - _logger.Debug() - .Message(GetCultureInfo("nl-nl"), "Message with {0} {1} {2} {3}", 4.1, 4.001, new DateTime(2016, 12, 31), true) - .Write(); - AssertDebugLastMessage("t2", "Message with 4,1 4,001 31-12-2016 00:00:00 True"); - } - - [Fact] - public void LogBuilder_Structured_Logging_Test() - { - var logEvent = _logger.Info().Property("Property1Key", "Property1Value").Message("{@message}", "My custom message").LogEventInfo; - Assert.NotEmpty(logEvent.Properties); - Assert.Contains("message", logEvent.Properties.Keys); - Assert.Contains("Property1Key", logEvent.Properties.Keys); - } - - /// - /// func because 1 logbuilder creates 1 message - /// - /// Caution: don't use overloading, that will break xUnit: - /// CATASTROPHIC ERROR OCCURRED: - /// System.ArgumentException: Ambiguous method named TraceWrite in type NLog.UnitTests.Fluent.LogBuilderTests - /// - private void ErrorWrite_internal(Func logBuilder, LogLevel logLevel, bool isStatic = false) - { - Exception catchedException = null; - string path = "blah.txt"; - - try - { - string text = File.ReadAllText(path); - } - catch (Exception ex) - { - catchedException = ex; - logBuilder() - .Message("Error reading file '{0}'.", path) - .Exception(ex) - .Property("Test", "ErrorWrite") - .Write(); - } - - var loggerName = isStatic ? "LogBuilderTests" : "logger1"; - { - var expectedEvent = new LogEventInfo(logLevel, loggerName, "Error reading file '{0}'."); - expectedEvent.Properties["Test"] = "ErrorWrite"; - expectedEvent.Exception = catchedException; - AssertLastLogEventTarget(expectedEvent); - AssertDebugLastMessageContains("t2", "Error reading file '"); - } - logBuilder() - .Message("This is a test fluent message.") - .Property("Test", "ErrorWrite") - .Write(); - - { - var expectedEvent = new LogEventInfo(logLevel, loggerName, "This is a test fluent message."); - expectedEvent.Properties["Test"] = "ErrorWrite"; - AssertLastLogEventTarget(expectedEvent); - } - - logBuilder() - .Message("This is a test fluent message '{0}'.", DateTime.Now.Ticks) - .Property("Test", "ErrorWrite") - .Write(); - - { - var expectedEvent = new LogEventInfo(logLevel, loggerName, "This is a test fluent message '{0}'."); - expectedEvent.Properties["Test"] = "ErrorWrite"; - AssertLastLogEventTarget(expectedEvent); - AssertDebugLastMessageContains("t2", "This is a test fluent message '"); - } - } - - /// - /// Test the written logevent - /// - /// exptected event to be logged. - void AssertLastLogEventTarget(LogEventInfo expected) - { - Assert.NotNull(_lastLogEventInfo); - Assert.Equal(expected.Message, _lastLogEventInfo.Message); - - Assert.NotNull(_lastLogEventInfo.Properties); - Assert.Equal(expected.Properties.Count, _lastLogEventInfo.Properties.Count); -#if !MONO - Assert.Equal(expected.Properties, _lastLogEventInfo.Properties); -#endif - Assert.Equal(expected.LoggerName, _lastLogEventInfo.LoggerName); - Assert.Equal(expected.Level, _lastLogEventInfo.Level); - Assert.Equal(expected.Exception, _lastLogEventInfo.Exception); - Assert.Equal(expected.FormatProvider, _lastLogEventInfo.FormatProvider); - } - } -} From 3c854498ec6a1a8ab23144b6e947e30488abc4f9 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 2 Oct 2024 20:29:59 +0200 Subject: [PATCH 002/224] Remove obsolete MDLC + NDLC from TargetWithContext (#5620) --- src/NLog/Targets/TargetWithContext.cs | 229 -------------------------- 1 file changed, 229 deletions(-) diff --git a/src/NLog/Targets/TargetWithContext.cs b/src/NLog/Targets/TargetWithContext.cs index f318c3b939..c9a19d8ddf 100644 --- a/src/NLog/Targets/TargetWithContext.cs +++ b/src/NLog/Targets/TargetWithContext.cs @@ -334,23 +334,6 @@ private void AddContextProperty(LogEventInfo logEvent, string propertyName, obje combinedProperties[propertyName] = propertyValue; } - /// - /// Obsolete and replaced by with NLog v5. - /// Returns the captured snapshot of for the - /// - /// - /// Dictionary with MDC context if any, else null - [Obsolete("Replaced by GetScopeContextProperties. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected IDictionary GetContextMdc(LogEventInfo logEvent) - { - if (logEvent.TryGetCachedLayoutValue(_contextLayout.ScopeContextPropertiesLayout, out object value)) - { - return value as IDictionary; - } - return CaptureContextMdc(logEvent, null); - } - /// /// Returns the captured snapshot of dictionary for the /// @@ -365,40 +348,6 @@ protected IDictionary GetScopeContextProperties(LogEventInfo log return CaptureScopeContextProperties(logEvent, null); } - /// - /// Obsolete and replaced by with NLog v5. - /// Returns the captured snapshot of for the - /// - /// - /// Dictionary with MDLC context if any, else null - [Obsolete("Replaced by GetScopeContextProperties. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected IDictionary GetContextMdlc(LogEventInfo logEvent) - { - if (logEvent.TryGetCachedLayoutValue(_contextLayout.ScopeContextPropertiesLayout, out object value)) - { - return value as IDictionary; - } - return CaptureContextMdlc(logEvent, null); - } - - /// - /// Obsolete and replaced by with NLog v5. - /// Returns the captured snapshot of for the - /// - /// - /// Collection with NDC context if any, else null - [Obsolete("Replaced by GetScopeContextNested. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected IList GetContextNdc(LogEventInfo logEvent) - { - if (logEvent.TryGetCachedLayoutValue(_contextLayout.ScopeContextNestedStatesLayout, out object value)) - { - return value as IList; - } - return CaptureContextNdc(logEvent); - } - /// /// Returns the captured snapshot of nested states from for the /// @@ -413,23 +362,6 @@ protected IList GetScopeContextNested(LogEventInfo logEvent) return CaptureScopeContextNested(logEvent); } - /// - /// Obsolete and replaced by with NLog v5. - /// Returns the captured snapshot of for the - /// - /// - /// Collection with NDLC context if any, else null - [Obsolete("Replaced by GetScopeContextNested. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected IList GetContextNdlc(LogEventInfo logEvent) - { - if (logEvent.TryGetCachedLayoutValue(_contextLayout.ScopeContextNestedStatesLayout, out object value)) - { - return value as IList; - } - return CaptureContextNdlc(logEvent); - } - private IDictionary CaptureContextProperties(LogEventInfo logEvent, IDictionary combinedProperties) { combinedProperties = combinedProperties ?? CreateNewDictionary(ContextProperties.Count); @@ -502,70 +434,6 @@ protected virtual IDictionary CaptureContextGdc(LogEventInfo log return contextProperties; } - /// - /// Obsolete and replaced by with NLog v5. - /// Takes snapshot of for the - /// - /// - /// Optional pre-allocated dictionary for the snapshot - /// Dictionary with MDC context if any, else null - [Obsolete("Replaced by CaptureScopeContextProperties. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected virtual IDictionary CaptureContextMdc(LogEventInfo logEvent, IDictionary contextProperties) - { - var names = MappedDiagnosticsContext.GetNames(); - if (names.Count == 0) - return contextProperties; - - contextProperties = contextProperties ?? CreateNewDictionary(names.Count); - bool checkForDuplicates = contextProperties.Count > 0; - foreach (var name in names) - { - object value = MappedDiagnosticsContext.GetObject(name); - if (SerializeMdcItem(logEvent, name, value, out var serializedValue)) - { - AddContextProperty(logEvent, name, serializedValue, checkForDuplicates, contextProperties); - } - } - return contextProperties; - } - - /// - /// Obsolete and replaced by with NLog v5. - /// Take snapshot of a single object value from - /// - /// Log event - /// MDC key - /// MDC value - /// Snapshot of MDC value - /// Include object value in snapshot - [Obsolete("Replaced by SerializeScopeContextProperty. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected virtual bool SerializeMdcItem(LogEventInfo logEvent, string name, object value, out object serializedValue) - { - if (string.IsNullOrEmpty(name)) - { - serializedValue = null; - return false; - } - - return SerializeItemValue(logEvent, name, value, out serializedValue); - } - - /// - /// Obsolete and replaced by with NLog v5. - /// Takes snapshot of for the - /// - /// - /// Optional pre-allocated dictionary for the snapshot - /// Dictionary with MDLC context if any, else null - [Obsolete("Replaced by CaptureScopeContextProperties. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected virtual IDictionary CaptureContextMdlc(LogEventInfo logEvent, IDictionary contextProperties) - { - return CaptureScopeContextProperties(logEvent, contextProperties); - } - /// /// Takes snapshot of dictionary for the /// @@ -601,22 +469,6 @@ protected virtual IDictionary CaptureScopeContextProperties(LogE return contextProperties; } - /// - /// Obsolete and replaced by with NLog v5. - /// Take snapshot of a single object value from - /// - /// Log event - /// MDLC key - /// MDLC value - /// Snapshot of MDLC value - /// Include object value in snapshot - [Obsolete("Replaced by SerializeScopeContextProperty. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected bool SerializeMdlcItem(LogEventInfo logEvent, string name, object value, out object serializedValue) - { - return SerializeScopeContextProperty(logEvent, name, value, out serializedValue); - } - /// /// Take snapshot of a single object value from dictionary /// @@ -636,72 +488,6 @@ protected virtual bool SerializeScopeContextProperty(LogEventInfo logEvent, stri return SerializeItemValue(logEvent, name, value, out serializedValue); } - /// - /// Obsolete and replaced by with NLog v5. - /// Takes snapshot of for the - /// - /// - /// Collection with NDC context if any, else null - [Obsolete("Replaced by CaptureScopeContextNested. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected virtual IList CaptureContextNdc(LogEventInfo logEvent) - { - var stack = NestedDiagnosticsContext.GetAllObjects(); - if (stack.Length == 0) - return stack; - - IList filteredStack = null; - for (int i = 0; i < stack.Length; ++i) - { - var ndcValue = stack[i]; - if (SerializeNdcItem(logEvent, ndcValue, out var serializedValue)) - { - if (filteredStack != null) - filteredStack.Add(serializedValue); - else - stack[i] = serializedValue; - } - else - { - if (filteredStack is null) - { - filteredStack = new List(stack.Length); - for (int j = 0; j < i; ++j) - filteredStack.Add(stack[j]); - } - } - } - return filteredStack ?? stack; - } - - /// - /// Obsolete and replaced by with NLog v5. - /// Take snapshot of a single object value from - /// - /// Log event - /// NDC value - /// Snapshot of NDC value - /// Include object value in snapshot - [Obsolete("Replaced by SerializeScopeContextNestedState. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected virtual bool SerializeNdcItem(LogEventInfo logEvent, object value, out object serializedValue) - { - return SerializeItemValue(logEvent, null, value, out serializedValue); - } - - /// - /// Obsolete and replaced by with NLog v5. - /// Takes snapshot of for the - /// - /// - /// Collection with NDLC context if any, else null - [Obsolete("Replaced by CaptureScopeContextNested. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected virtual IList CaptureContextNdlc(LogEventInfo logEvent) - { - return CaptureScopeContextNested(logEvent); - } - /// /// Takes snapshot of nested states from for the /// @@ -737,21 +523,6 @@ protected virtual IList CaptureScopeContextNested(LogEventInfo logEvent) return filteredStack ?? stack; } - /// - /// Obsolete and replaced by with NLog v5. - /// Take snapshot of a single object value from - /// - /// Log event - /// NDLC value - /// Snapshot of NDLC value - /// Include object value in snapshot - [Obsolete("Replaced by SerializeScopeContextNestedState. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected virtual bool SerializeNdlcItem(LogEventInfo logEvent, object value, out object serializedValue) - { - return SerializeScopeContextNestedState(logEvent, value, out serializedValue); - } - /// /// Take snapshot of a single object value from nested states /// From cec85e38b5c500536cafd8a7ff75dd699fca6e0e Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 2 Oct 2024 22:14:23 +0200 Subject: [PATCH 003/224] Remove obsolete LogToTrace from InternalLogger (#5619) --- src/NLog/Common/IInternalLoggerContext.cs | 2 +- src/NLog/Common/InternalLogger.cs | 182 +-------- .../Common/InternalLoggerMessageEventArgs.cs | 86 ----- src/NLog/Config/LoggingConfigurationParser.cs | 5 - .../SetupInternalLoggerBuilderExtensions.cs | 63 ---- .../Common/InternalLoggerTests.cs | 20 +- .../Common/InternalLoggerTests_Trace.cs | 356 ------------------ .../Config/InternalLoggingTests.cs | 20 +- .../Config/LogFactorySetupTests.cs | 46 --- 9 files changed, 20 insertions(+), 760 deletions(-) delete mode 100644 src/NLog/Common/InternalLoggerMessageEventArgs.cs delete mode 100644 tests/NLog.UnitTests/Common/InternalLoggerTests_Trace.cs diff --git a/src/NLog/Common/IInternalLoggerContext.cs b/src/NLog/Common/IInternalLoggerContext.cs index fe053a0797..eb454502e8 100644 --- a/src/NLog/Common/IInternalLoggerContext.cs +++ b/src/NLog/Common/IInternalLoggerContext.cs @@ -36,7 +36,7 @@ namespace NLog.Common { /// - /// Enables to extract extra context details for + /// Enables to extract extra context details for /// internal interface IInternalLoggerContext { diff --git a/src/NLog/Common/InternalLogger.cs b/src/NLog/Common/InternalLogger.cs index b93c934a37..5a7adef2c4 100644 --- a/src/NLog/Common/InternalLogger.cs +++ b/src/NLog/Common/InternalLogger.cs @@ -62,17 +62,11 @@ public static void Reset() ExceptionThrowWhenWriting = false; LogWriter = null; InternalEventOccurred = null; - -#pragma warning disable CS0618 // Type or member is obsolete - _logMessageReceived = null; - LogLevel = GetSetting("nlog.internalLogLevel", "NLOG_INTERNAL_LOG_LEVEL", LogLevel.Off); - IncludeTimestamp = GetSetting("nlog.internalLogIncludeTimestamp", "NLOG_INTERNAL_INCLUDE_TIMESTAMP", true); - LogToConsole = GetSetting("nlog.internalLogToConsole", "NLOG_INTERNAL_LOG_TO_CONSOLE", false); - LogToConsoleError = GetSetting("nlog.internalLogToConsoleError", "NLOG_INTERNAL_LOG_TO_CONSOLE_ERROR", false); - LogFile = GetSetting("nlog.internalLogFile", "NLOG_INTERNAL_LOG_FILE", string.Empty); - LogToTrace = GetSetting("nlog.internalLogToTrace", "NLOG_INTERNAL_LOG_TO_TRACE", false); -#pragma warning restore CS0618 // Type or member is obsolete - Info("NLog internal logger initialized."); + LogLevel = LogLevel.Off; + IncludeTimestamp = true; + LogToConsole = false; + LogToConsoleError = false; + LogFile = string.Empty; } /// @@ -122,27 +116,6 @@ public static bool LogToConsoleError } private static bool _logToConsoleError; - /// - /// Obsolete and replaced by with NLog v5.3. - /// Gets or sets a value indicating whether internal messages should be written to the .Trace - /// - [Obsolete("Instead use InternalLogger.LogWriter. Marked obsolete with NLog v5.3")] - public static bool LogToTrace - { - get => _logToTrace; - set - { - if (_logToTrace != value) - { - InternalEventOccurred -= LogToTraceSubscription; - if (value) - InternalEventOccurred += LogToTraceSubscription; - _logToTrace = value; - } - } - } - private static bool _logToTrace; - /// /// Gets or sets the file path of the internal log file. /// @@ -178,34 +151,6 @@ public static string LogFile /// public static TextWriter LogWriter { get; set; } - /// - /// Obsolete and replaced by with NLog 5.3. - /// Event written to the internal log. - /// - /// - /// EventHandler will only be triggered for events, where severity matches the configured . - /// - /// Avoid using/calling NLog Logger-objects when handling these internal events, as it will lead to deadlock / stackoverflow. - /// - [Obsolete("Instead use InternalEventOccurred. Marked obsolete with NLog v5.3")] - public static event EventHandler LogMessageReceived - { - add - { - if (_logMessageReceived == null) - InternalEventOccurred += LogToMessageReceived; - _logMessageReceived += value; - } - remove - { - _logMessageReceived -= value; - if (_logMessageReceived == null) - InternalEventOccurred -= LogToMessageReceived; - } - } - [Obsolete("Instead use InternalEventOccurred. Marked obsolete with NLog v5.3")] - private static event EventHandler _logMessageReceived; - /// /// Internal LogEvent written to the InternalLogger /// @@ -467,108 +412,6 @@ public static void LogAssemblyVersion(Assembly assembly) Error(ex, "Error logging version of assembly {0}.", assembly?.FullName); } } - - [Obsolete("InternalLogger should be minimal. Marked obsolete with NLog v5.3")] - private static string GetAppSettings(string configName) - { -#if NETFRAMEWORK - try - { - return System.Configuration.ConfigurationManager.AppSettings[configName]; - } - catch (Exception ex) - { - if (ex.MustBeRethrownImmediately()) - { - throw; - } - } -#endif - return null; - } - - [Obsolete("InternalLogger should be minimal. Marked obsolete with NLog v5.3")] - private static string GetSettingString(string configName, string envName) - { - try - { - string settingValue = GetAppSettings(configName); - if (settingValue != null) - return settingValue; - } - catch (Exception ex) - { - if (ex.MustBeRethrownImmediately()) - { - throw; - } - } - - try - { - string settingValue = EnvironmentHelper.GetSafeEnvironmentVariable(envName); - if (!string.IsNullOrEmpty(settingValue)) - return settingValue; - } - catch (Exception ex) - { - if (ex.MustBeRethrownImmediately()) - { - throw; - } - } - - return null; - } - - [Obsolete("InternalLogger should be minimal. Marked obsolete with NLog v5.3")] - private static LogLevel GetSetting(string configName, string envName, LogLevel defaultValue) - { - string value = GetSettingString(configName, envName); - if (value is null) - { - return defaultValue; - } - - try - { - return LogLevel.FromString(value); - } - catch (Exception exception) - { - if (exception.MustBeRethrownImmediately()) - { - throw; - } - - return defaultValue; - } - } - - [Obsolete("InternalLogger should be minimal. Marked obsolete with NLog v5.3")] - private static T GetSetting(string configName, string envName, T defaultValue) - { - string value = GetSettingString(configName, envName); - if (value is null) - { - return defaultValue; - } - - try - { - return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture); - } - catch (Exception exception) - { - if (exception.MustBeRethrownImmediately()) - { - throw; - } - - return defaultValue; - } - } - private static void CreateDirectoriesIfNeeded(string filename) { try @@ -650,15 +493,6 @@ private static void LogToConsoleErrorSubscription(object sender, InternalLogEven #endif } - [Obsolete("Instead use InternalLogger.LogWriter. Marked obsolete with NLog v5.3")] - private static void LogToTraceSubscription(object sender, InternalLogEventArgs eventArgs) - { -#if !NETSTANDARD1_3 - var logLine = CreateLogLine(eventArgs.Exception, eventArgs.Level, eventArgs.Message); - System.Diagnostics.Trace.WriteLine(logLine, "NLog"); -#endif - } - private static void LogToFileSubscription(object sender, InternalLogEventArgs eventArgs) { var logLine = CreateLogLine(eventArgs.Exception, eventArgs.Level, eventArgs.Message); @@ -677,11 +511,5 @@ private static void LogToFileSubscription(object sender, InternalLogEventArgs ev } } } - - [Obsolete("Instead use InternalEventOccurred and InternalLogEventArgs. Marked obsolete with NLog v5.3")] - private static void LogToMessageReceived(object sender, InternalLogEventArgs eventArgs) - { - _logMessageReceived?.Invoke(null, new InternalLoggerMessageEventArgs(eventArgs.Message, eventArgs.Level, eventArgs.Exception, eventArgs.SenderType, eventArgs.SenderName)); - } } } diff --git a/src/NLog/Common/InternalLoggerMessageEventArgs.cs b/src/NLog/Common/InternalLoggerMessageEventArgs.cs deleted file mode 100644 index 7ad59db851..0000000000 --- a/src/NLog/Common/InternalLoggerMessageEventArgs.cs +++ /dev/null @@ -1,86 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.Common -{ - using System; - using System.ComponentModel; - using JetBrains.Annotations; - - /// - /// A message has been written to the internal logger - /// - [Obsolete("Instead use InternalEventOccurred and InternalLogEventArgs. Marked obsolete with NLog v5.3")] - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class InternalLoggerMessageEventArgs : EventArgs - { - /// - /// The rendered message - /// - public string Message { get; } - - /// - /// The log level - /// - public LogLevel Level { get; } - - /// - /// The exception. Could be null. - /// - [CanBeNull] - public Exception Exception { get; } - - /// - /// The type that triggered this internal log event, for example the FileTarget. - /// This property is not always populated. - /// - [CanBeNull] - public Type SenderType { get; } - - /// - /// The context name that triggered this internal log event, for example the name of the Target. - /// This property is not always populated. - /// - [CanBeNull] - public string SenderName { get; } - - internal InternalLoggerMessageEventArgs(string message, LogLevel level, [CanBeNull] Exception exception, [CanBeNull] Type senderType, [CanBeNull] string senderName) - { - Message = message; - Level = level; - Exception = exception; - SenderType = senderType; - SenderName = senderName; - } - } -} diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index bf31d35e98..3c6e269007 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -154,11 +154,6 @@ private void SetNLogElementSettings(ILoggingConfigurationElement nlogConfig) case "INTERNALLOGFILE": InternalLogger.LogFile = configItem.Value?.Trim(); break; - case "INTERNALLOGTOTRACE": -#pragma warning disable CS0618 // Type or member is obsolete - InternalLogger.LogToTrace = ParseBooleanValue(configItem.Key, configItem.Value, InternalLogger.LogToTrace); -#pragma warning restore CS0618 // Type or member is obsolete - break; case "INTERNALLOGINCLUDETIMESTAMP": InternalLogger.IncludeTimestamp = ParseBooleanValue(configItem.Key, configItem.Value, InternalLogger.IncludeTimestamp); break; diff --git a/src/NLog/SetupInternalLoggerBuilderExtensions.cs b/src/NLog/SetupInternalLoggerBuilderExtensions.cs index 64db149807..5c757d9aea 100644 --- a/src/NLog/SetupInternalLoggerBuilderExtensions.cs +++ b/src/NLog/SetupInternalLoggerBuilderExtensions.cs @@ -80,19 +80,6 @@ public static ISetupInternalLoggerBuilder LogToConsole(this ISetupInternalLogger return setupBuilder; } -#if !NETSTANDARD1_3 - /// - /// Configures - /// - public static ISetupInternalLoggerBuilder LogToTrace(this ISetupInternalLoggerBuilder setupBuilder, bool enabled) - { -#pragma warning disable CS0618 // Type or member is obsolete - InternalLogger.LogToTrace = enabled; -#pragma warning restore CS0618 // Type or member is obsolete - return setupBuilder; - } -#endif - /// /// Configures /// @@ -102,28 +89,6 @@ public static ISetupInternalLoggerBuilder LogToWriter(this ISetupInternalLoggerB return setupBuilder; } - /// - /// Configures - /// - [Obsolete("Instead use AddEventSubscription. Marked obsolete with NLog v5.3")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static ISetupInternalLoggerBuilder AddLogSubscription(this ISetupInternalLoggerBuilder setupBuilder, EventHandler eventSubscriber) - { - InternalLogger.LogMessageReceived += eventSubscriber; - return setupBuilder; - } - - /// - /// Configures - /// - [Obsolete("Instead use RemoveEventSubscription. Marked obsolete with NLog v5.3")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static ISetupInternalLoggerBuilder RemoveLogSubscription(this ISetupInternalLoggerBuilder setupBuilder, EventHandler eventSubscriber) - { - InternalLogger.LogMessageReceived -= eventSubscriber; - return setupBuilder; - } - /// /// Configures /// @@ -141,33 +106,5 @@ public static ISetupInternalLoggerBuilder RemoveEventSubscription(this ISetupInt InternalLogger.InternalEventOccurred -= eventSubscriber; return setupBuilder; } - - /// - /// Configure the InternalLogger properties from Environment-variables and App.config using - /// - /// - /// Recognizes the following environment-variables: - /// - /// - NLOG_INTERNAL_LOG_LEVEL - /// - NLOG_INTERNAL_LOG_FILE - /// - NLOG_INTERNAL_LOG_TO_CONSOLE - /// - NLOG_INTERNAL_LOG_TO_CONSOLE_ERROR - /// - NLOG_INTERNAL_LOG_TO_TRACE - /// - NLOG_INTERNAL_INCLUDE_TIMESTAMP - /// - /// Legacy .NetFramework platform will also recognizes the following app.config settings: - /// - /// - nlog.internalLogLevel - /// - nlog.internalLogFile - /// - nlog.internalLogToConsole - /// - nlog.internalLogToConsoleError - /// - nlog.internalLogToTrace - /// - nlog.internalLogIncludeTimestamp - /// - public static ISetupInternalLoggerBuilder SetupFromEnvironmentVariables(this ISetupInternalLoggerBuilder setupBuilder) - { - InternalLogger.Reset(); - return setupBuilder; - } } } diff --git a/tests/NLog.UnitTests/Common/InternalLoggerTests.cs b/tests/NLog.UnitTests/Common/InternalLoggerTests.cs index 1ffa6180a6..408c3ee500 100644 --- a/tests/NLog.UnitTests/Common/InternalLoggerTests.cs +++ b/tests/NLog.UnitTests/Common/InternalLoggerTests.cs @@ -803,14 +803,14 @@ public void TestReceivedLogEventTest() using (var loggerScope = new InternalLoggerScope()) { // Arrange - var receivedArgs = new List(); + var receivedArgs = new List(); var logFactory = new LogFactory(); logFactory.Setup().SetupInternalLogger(s => { - EventHandler eventHandler = (sender, e) => receivedArgs.Add(e); - s.AddLogSubscription(eventHandler); - s.RemoveLogSubscription(eventHandler); - s.AddLogSubscription(eventHandler); + InternalEventOccurredHandler eventHandler = (sender, e) => receivedArgs.Add(e); + s.AddEventSubscription(eventHandler); + s.RemoveEventSubscription(eventHandler); + s.AddEventSubscription(eventHandler); }); var exception = new Exception(); @@ -827,14 +827,13 @@ public void TestReceivedLogEventTest() } [Fact] - [Obsolete("Instead use InternalEventOccurred. Marked obsolete with NLog v5.3")] public void TestReceivedLogEventThrowingTest() { using (var loggerScope = new InternalLoggerScope()) { // Arrange - var receivedArgs = new List(); - InternalLogger.LogMessageReceived += (sender, e) => + var receivedArgs = new List(); + InternalLogger.InternalEventOccurred += (sender, e) => { receivedArgs.Add(e); throw new ApplicationException("I'm a bad programmer"); @@ -854,15 +853,14 @@ public void TestReceivedLogEventThrowingTest() } [Fact] - [Obsolete("Instead use InternalEventOccurred. Marked obsolete with NLog v5.3")] public void TestReceivedLogEventContextTest() { using (var loggerScope = new InternalLoggerScope()) { // Arrange var targetContext = new NLog.Targets.DebugTarget() { Name = "Ugly" }; - var receivedArgs = new List(); - InternalLogger.LogMessageReceived += (sender, e) => + var receivedArgs = new List(); + InternalLogger.InternalEventOccurred += (sender, e) => { receivedArgs.Add(e); }; diff --git a/tests/NLog.UnitTests/Common/InternalLoggerTests_Trace.cs b/tests/NLog.UnitTests/Common/InternalLoggerTests_Trace.cs deleted file mode 100644 index 5585e8f4da..0000000000 --- a/tests/NLog.UnitTests/Common/InternalLoggerTests_Trace.cs +++ /dev/null @@ -1,356 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -#define DEBUG - -namespace NLog.UnitTests.Common -{ - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Linq; - using NLog.Common; - using NLog.Config; - using Xunit; - - public class InternalLoggerTests_Trace : NLogTestBase - { - - [Theory] - [InlineData(null, null)] - [InlineData(false, null)] - [InlineData(null, false)] - [InlineData(false, false)] - public void ShouldNotLogInternalWhenLogToTraceIsDisabled(bool? internalLogToTrace, bool? logToTrace) - { - var mockTraceListener = SetupTestConfiguration(LogLevel.Trace, internalLogToTrace, logToTrace); - - InternalLogger.Trace("Logger1 Hello"); - - Assert.Empty(mockTraceListener.Messages); - } - - [Theory] - [InlineData(null, null)] - [InlineData(false, null)] - [InlineData(null, false)] - [InlineData(false, false)] - [InlineData(true, null)] - [InlineData(null, true)] - [InlineData(true, true)] - public void ShouldNotLogInternalWhenLogLevelIsOff(bool? internalLogToTrace, bool? logToTrace) - { - var mockTraceListener = SetupTestConfiguration(LogLevel.Off, internalLogToTrace, logToTrace); - - InternalLogger.Trace("Logger1 Hello"); - - Assert.Empty(mockTraceListener.Messages); - } - - [Theory] - [InlineData(true, null)] - [InlineData(null, true)] - [InlineData(true, true)] - public void VerifyInternalLoggerLevelFilter(bool? internalLogToTrace, bool? logToTrace) - { - foreach (LogLevel logLevelConfig in LogLevel.AllLevels) - { - var mockTraceListener = SetupTestConfiguration(logLevelConfig, internalLogToTrace, logToTrace); - - List expected = new List(); - - string input = "Logger1 Hello"; - expected.Add(input); - InternalLogger.Trace(input); - InternalLogger.Debug(input); - InternalLogger.Info(input); - InternalLogger.Warn(input); - InternalLogger.Error(input); - InternalLogger.Fatal(input); - input += "No.{0}"; - expected.Add(string.Format(input, 1)); - InternalLogger.Trace(input, 1); - InternalLogger.Debug(input, 1); - InternalLogger.Info(input, 1); - InternalLogger.Warn(input, 1); - InternalLogger.Error(input, 1); - InternalLogger.Fatal(input, 1); - input += ", We come in {1}"; - expected.Add(string.Format(input, 1, "Peace")); - InternalLogger.Trace(input, 1, "Peace"); - InternalLogger.Debug(input, 1, "Peace"); - InternalLogger.Info(input, 1, "Peace"); - InternalLogger.Warn(input, 1, "Peace"); - InternalLogger.Error(input, 1, "Peace"); - InternalLogger.Fatal(input, 1, "Peace"); - input += " and we are {2} to god"; - expected.Add(string.Format(input, 1, "Peace", true)); - InternalLogger.Trace(input, 1, "Peace", true); - InternalLogger.Debug(input, 1, "Peace", true); - InternalLogger.Info(input, 1, "Peace", true); - InternalLogger.Warn(input, 1, "Peace", true); - InternalLogger.Error(input, 1, "Peace", true); - InternalLogger.Fatal(input, 1, "Peace", true); - input += ", Please don't {3}"; - expected.Add(string.Format(input, 1, "Peace", true, null)); - InternalLogger.Trace(input, 1, "Peace", true, null); - InternalLogger.Debug(input, 1, "Peace", true, null); - InternalLogger.Info(input, 1, "Peace", true, null); - InternalLogger.Warn(input, 1, "Peace", true, null); - InternalLogger.Error(input, 1, "Peace", true, null); - InternalLogger.Fatal(input, 1, "Peace", true, null); - input += " the {4}"; - expected.Add(string.Format(input, 1, "Peace", true, null, "Messenger")); - InternalLogger.Trace(input, 1, "Peace", true, null, "Messenger"); - InternalLogger.Debug(input, 1, "Peace", true, null, "Messenger"); - InternalLogger.Info(input, 1, "Peace", true, null, "Messenger"); - InternalLogger.Warn(input, 1, "Peace", true, null, "Messenger"); - InternalLogger.Error(input, 1, "Peace", true, null, "Messenger"); - InternalLogger.Fatal(input, 1, "Peace", true, null, "Messenger"); - Assert.Equal(expected.Count * (LogLevel.Fatal.Ordinal - logLevelConfig.Ordinal + 1), mockTraceListener.Messages.Count); - for (int i = 0; i < expected.Count; ++i) - { - int msgCount = LogLevel.Fatal.Ordinal - logLevelConfig.Ordinal + 1; - for (int j = 0; j < msgCount; ++j) - { - Assert.Contains(expected[i], mockTraceListener.Messages.First()); - mockTraceListener.Messages.RemoveAt(0); - } - } - Assert.Empty(mockTraceListener.Messages); - } - } - - [Theory] - [InlineData(true, null)] - [InlineData(null, true)] - [InlineData(true, true)] - public void ShouldLogToTraceWhenInternalLogToTraceIsOnAndLogLevelIsTrace(bool? internalLogToTrace, bool? logToTrace) - { - var mockTraceListener = SetupTestConfiguration(LogLevel.Trace, internalLogToTrace, logToTrace); - - InternalLogger.Trace("Logger1 Hello"); - - Assert.Single(mockTraceListener.Messages); - Assert.Equal("NLog: Trace Logger1 Hello" + Environment.NewLine, mockTraceListener.Messages.First()); - } - - [Theory] - [InlineData(true, null)] - [InlineData(null, true)] - [InlineData(true, true)] - public void ShouldLogToTraceWhenInternalLogToTraceIsOnAndLogLevelIsDebug(bool? internalLogToTrace, bool? logToTrace) - { - var mockTraceListener = SetupTestConfiguration(LogLevel.Debug, internalLogToTrace, logToTrace); - - InternalLogger.Debug("Logger1 Hello"); - - Assert.Single(mockTraceListener.Messages); - Assert.Equal("NLog: Debug Logger1 Hello" + Environment.NewLine, mockTraceListener.Messages.First()); - } - - [Theory] - [InlineData(true, null)] - [InlineData(null, true)] - [InlineData(true, true)] - public void ShouldLogToTraceWhenInternalLogToTraceIsOnAndLogLevelIsInfo(bool? internalLogToTrace, bool? logToTrace) - { - var mockTraceListener = SetupTestConfiguration(LogLevel.Info, internalLogToTrace, logToTrace); - - InternalLogger.Info("Logger1 Hello"); - - Assert.Single(mockTraceListener.Messages); - Assert.Equal("NLog: Info Logger1 Hello" + Environment.NewLine, mockTraceListener.Messages.First()); - } - - [Theory] - [InlineData(true, null)] - [InlineData(null, true)] - [InlineData(true, true)] - public void ShouldLogToTraceWhenInternalLogToTraceIsOnAndLogLevelIsWarn(bool? internalLogToTrace, bool? logToTrace) - { - var mockTraceListener = SetupTestConfiguration(LogLevel.Warn, internalLogToTrace, logToTrace); - - InternalLogger.Warn("Logger1 Hello"); - - Assert.Single(mockTraceListener.Messages); - Assert.Equal("NLog: Warn Logger1 Hello" + Environment.NewLine, mockTraceListener.Messages.First()); - } - - [Theory] - [InlineData(true, null)] - [InlineData(null, true)] - [InlineData(true, true)] - public void ShouldLogToTraceWhenInternalLogToTraceIsOnAndLogLevelIsError(bool? internalLogToTrace, bool? logToTrace) - { - var mockTraceListener = SetupTestConfiguration(LogLevel.Error, internalLogToTrace, logToTrace); - - InternalLogger.Error("Logger1 Hello"); - - Assert.Single(mockTraceListener.Messages); - Assert.Equal("NLog: Error Logger1 Hello" + Environment.NewLine, mockTraceListener.Messages.First()); - } - - [Theory] - [InlineData(true, null)] - [InlineData(null, true)] - [InlineData(true, true)] - public void ShouldLogToTraceWhenInternalLogToTraceIsOnAndLogLevelIsFatal(bool? internalLogToTrace, bool? logToTrace) - { - var mockTraceListener = SetupTestConfiguration(LogLevel.Fatal, internalLogToTrace, logToTrace); - - InternalLogger.Fatal("Logger1 Hello"); - - Assert.Single(mockTraceListener.Messages); - Assert.Equal("NLog: Fatal Logger1 Hello" + Environment.NewLine, mockTraceListener.Messages.First()); - } - -#if !NETSTANDARD1_5 - [Fact(Skip = "This test's not working - explenation is in documentation: https://msdn.microsoft.com/pl-pl/library/system.stackoverflowexception(v=vs.110).aspx#Anchor_5. To clarify if StackOverflowException should be thrown.")] - public void ShouldThrowStackOverFlowExceptionWhenUsingNLogTraceListener() - { - SetupTestConfiguration(LogLevel.Trace, true, null); - - Assert.Throws(() => Trace.WriteLine("StackOverFlowException")); - } -#endif - - /// - /// Helper method to setup tests configuration - /// - /// The for the log event. - /// internalLogToTrace XML attribute value. If null attribute is omitted. - /// Value of property. If null property is not set. - /// instance. - private static T SetupTestConfiguration(LogLevel logLevel, bool? internalLogToTrace, bool? logToTrace) where T : TraceListener - { - var internalLogToTraceAttribute = ""; - if (internalLogToTrace.HasValue) - { - internalLogToTraceAttribute = $" internalLogToTrace='{internalLogToTrace.Value}'"; - } - - var xmlConfiguration = string.Format(XmlConfigurationFormat, logLevel, internalLogToTraceAttribute); - - LogManager.Configuration = XmlLoggingConfiguration.CreateFromXmlString(xmlConfiguration); - - InternalLogger.IncludeTimestamp = false; - - if (logToTrace.HasValue) - { -#pragma warning disable CS0618 // Type or member is obsolete - InternalLogger.LogToTrace = logToTrace.Value; -#pragma warning restore CS0618 // Type or member is obsolete - } - - T traceListener; - if (typeof(T) == typeof(MockTraceListener)) - { - traceListener = CreateMockTraceListener() as T; - } - else - { - traceListener = CreateNLogTraceListener() as T; - } - - Trace.Listeners.Clear(); - - if (traceListener is null) - { - return null; - } - - Trace.Listeners.Add(traceListener); - - return traceListener; - } - - private const string XmlConfigurationFormat = @" - - - - - - -"; - - /// - /// Creates instance. - /// - /// instance. - private static MockTraceListener CreateMockTraceListener() - { - return new MockTraceListener(); - } - - /// - /// Creates instance. - /// - /// instance. - private static TraceListener CreateNLogTraceListener() - { -#if !NETSTANDARD1_5 - return new NLogTraceListener { Name = "Logger1", ForceLogLevel = LogLevel.Trace }; -#else - return null; -#endif - } - - private sealed class MockTraceListener : TraceListener - { - - internal readonly List Messages = new List(); - - /// - /// When overridden in a derived class, writes the specified message to the listener you create in the derived class. - /// - /// A message to write. - public override void Write(string message) - { - Messages.Add(message); - } - - /// - /// When overridden in a derived class, writes a message to the listener you create in the derived class, followed by a line terminator. - /// - /// A message to write. - public override void WriteLine(string message) - { - Messages.Add(message + Environment.NewLine); - } - - } - - } - -} diff --git a/tests/NLog.UnitTests/Config/InternalLoggingTests.cs b/tests/NLog.UnitTests/Config/InternalLoggingTests.cs index 125d2bf046..f2ef9558e9 100644 --- a/tests/NLog.UnitTests/Config/InternalLoggingTests.cs +++ b/tests/NLog.UnitTests/Config/InternalLoggingTests.cs @@ -44,19 +44,19 @@ public class InternalLoggingTests : NLogTestBase [Fact] public void InternalLoggingConfigTest1() { - InternalLoggingConfigTest(LogLevel.Trace, true, true, LogLevel.Warn, true, true, @"c:\temp\nlog\file.txt", true, true); + InternalLoggingConfigTest(LogLevel.Trace, true, true, LogLevel.Warn, true, true, @"c:\temp\nlog\file.txt", true); } [Fact] public void InternalLoggingConfigTest2() { - InternalLoggingConfigTest(LogLevel.Error, false, false, LogLevel.Info, false, false, @"c:\temp\nlog\file2.txt", false, false); + InternalLoggingConfigTest(LogLevel.Error, false, false, LogLevel.Info, false, false, @"c:\temp\nlog\file2.txt", false); } [Fact] public void InternalLoggingConfigTes3() { - InternalLoggingConfigTest(LogLevel.Info, false, false, LogLevel.Trace, false, null, @"c:\temp\nlog\file3.txt", false, true); + InternalLoggingConfigTest(LogLevel.Info, false, false, LogLevel.Trace, false, null, @"c:\temp\nlog\file3.txt", true); } [Fact] @@ -67,9 +67,6 @@ public void InternalLoggingConfigTestDefaults() InternalLogger.LogLevel = LogLevel.Error; InternalLogger.LogToConsole = true; InternalLogger.LogToConsoleError = true; -#pragma warning disable CS0618 // Type or member is obsolete - InternalLogger.LogToTrace = true; -#pragma warning restore CS0618 // Type or member is obsolete LogManager.GlobalThreshold = LogLevel.Fatal; LogManager.ThrowExceptions = true; LogManager.ThrowConfigExceptions = null; @@ -82,9 +79,6 @@ public void InternalLoggingConfigTestDefaults() Assert.Same(LogLevel.Error, InternalLogger.LogLevel); Assert.True(InternalLogger.LogToConsole); Assert.True(InternalLogger.LogToConsoleError); -#pragma warning disable CS0618 // Type or member is obsolete - Assert.True(InternalLogger.LogToTrace); -#pragma warning restore CS0618 // Type or member is obsolete Assert.Same(LogLevel.Fatal, LogManager.GlobalThreshold); Assert.True(LogManager.ThrowExceptions); Assert.Null(LogManager.ThrowConfigExceptions); @@ -139,7 +133,7 @@ public void InternalLoggingInvalidFormatString() } } - private static void InternalLoggingConfigTest(LogLevel logLevel, bool logToConsole, bool logToConsoleError, LogLevel globalThreshold, bool throwExceptions, bool? throwConfigExceptions, string file, bool logToTrace, bool autoShutdown) + private static void InternalLoggingConfigTest(LogLevel logLevel, bool logToConsole, bool logToConsoleError, LogLevel globalThreshold, bool throwExceptions, bool? throwConfigExceptions, string file, bool autoShutdown) { var logLevelString = logLevel.ToString(); var internalLogToConsoleString = logToConsole.ToString().ToLower(); @@ -147,13 +141,12 @@ private static void InternalLoggingConfigTest(LogLevel logLevel, bool logToConso var globalThresholdString = globalThreshold.ToString(); var throwExceptionsString = throwExceptions.ToString().ToLower(); var throwConfigExceptionsString = throwConfigExceptions?.ToString().ToLower() ?? string.Empty; - var logToTraceString = logToTrace.ToString().ToLower(); var autoShutdownString = autoShutdown.ToString().ToLower(); using (new InternalLoggerScope(true)) { XmlLoggingConfiguration.CreateFromXmlString($@" - + "); Assert.Same(logLevel, InternalLogger.LogLevel); @@ -161,9 +154,6 @@ private static void InternalLoggingConfigTest(LogLevel logLevel, bool logToConso Assert.Equal(file, InternalLogger.LogFile); Assert.Equal(logToConsole, InternalLogger.LogToConsole); Assert.Equal(logToConsoleError, InternalLogger.LogToConsoleError); -#pragma warning disable CS0618 // Type or member is obsolete - Assert.Equal(logToTrace, InternalLogger.LogToTrace); -#pragma warning restore CS0618 // Type or member is obsolete Assert.Same(globalThreshold, LogManager.GlobalThreshold); diff --git a/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs b/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs index 8d0d0d44ed..d0a5158e02 100644 --- a/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs +++ b/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs @@ -543,29 +543,6 @@ public void SetupInternalLoggerLogToConsoleTest() } } - [Fact] - public void SetupInternalLoggerLogToTraceTest() - { - try - { - // Arrange - InternalLogger.Reset(); - var logFactory = new LogFactory(); - - // Act - logFactory.Setup().SetupInternalLogger(b => b.SetMinimumLogLevel(LogLevel.Fatal).LogToTrace(true)); - - // Assert -#pragma warning disable CS0618 // Type or member is obsolete - Assert.True(InternalLogger.LogToTrace); -#pragma warning restore CS0618 // Type or member is obsolete - } - finally - { - InternalLogger.Reset(); - } - } - [Fact] public void SetupInternalLoggerLogToWriterTest() { @@ -587,29 +564,6 @@ public void SetupInternalLoggerLogToWriterTest() } } - [Fact] - public void SetupInternalLoggerSetupFromEnvironmentVariablesTest() - { - try - { - // Arrange - InternalLogger.Reset(); - var logFactory = new LogFactory(); - InternalLogger.IncludeTimestamp = false; - - // Act - logFactory.Setup().SetupInternalLogger(b => b.SetupFromEnvironmentVariables().SetMinimumLogLevel(LogLevel.Fatal)); - - // Assert - Assert.True(InternalLogger.IncludeTimestamp); - Assert.Equal(LogLevel.Fatal, InternalLogger.LogLevel); - } - finally - { - InternalLogger.Reset(); - } - } - [Fact] public void SetupExtensionsRegisterConditionMethodTest() { From e8d096485b872b559f764cae6178a6621498db94 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 3 Oct 2024 07:56:55 +0200 Subject: [PATCH 004/224] Remove obsolete LayoutRenderers MDC + MDLC + NDC + NDLC (#5618) --- src/NLog/Config/AssemblyExtensionTypes.cs | 10 +- src/NLog/Config/AssemblyExtensionTypes.tt | 2 +- src/NLog/LayoutRenderers/MdcLayoutRenderer.cs | 94 --------------- .../LayoutRenderers/MdlcLayoutRenderer.cs | 94 --------------- src/NLog/LayoutRenderers/NdcLayoutRenderer.cs | 113 ------------------ .../LayoutRenderers/NdlcLayoutRenderer.cs | 113 ------------------ .../NdlcTimingLayoutRenderer.cs | 96 --------------- .../ScopeContextNestedStatesLayoutRenderer.cs | 2 + .../ScopeContextPropertyLayoutRenderer.cs | 2 + .../ScopeContextTimingLayoutRenderer.cs | 8 ++ 10 files changed, 18 insertions(+), 516 deletions(-) delete mode 100644 src/NLog/LayoutRenderers/MdcLayoutRenderer.cs delete mode 100644 src/NLog/LayoutRenderers/MdlcLayoutRenderer.cs delete mode 100644 src/NLog/LayoutRenderers/NdcLayoutRenderer.cs delete mode 100644 src/NLog/LayoutRenderers/NdlcLayoutRenderer.cs delete mode 100644 src/NLog/LayoutRenderers/NdlcTimingLayoutRenderer.cs diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index daaf92ef8b..062c206f04 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -108,12 +108,7 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.LayoutRendererFactory.RegisterType("logger"); factory.LayoutRendererFactory.RegisterType("longdate"); factory.LayoutRendererFactory.RegisterType("machinename"); - factory.LayoutRendererFactory.RegisterType("mdc"); - factory.LayoutRendererFactory.RegisterType("mdlc"); factory.LayoutRendererFactory.RegisterType("message"); - factory.LayoutRendererFactory.RegisterType("ndc"); - factory.LayoutRendererFactory.RegisterType("ndlc"); - factory.LayoutRendererFactory.RegisterType("ndlctiming"); factory.LayoutRendererFactory.RegisterType("newline"); #if !NETSTANDARD1_3 factory.LayoutRendererFactory.RegisterType("nlogdir"); @@ -133,8 +128,13 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.LayoutRendererFactory.RegisterType("processtime"); factory.LayoutRendererFactory.RegisterType("scopeindent"); factory.LayoutRendererFactory.RegisterType("scopenested"); + factory.LayoutRendererFactory.RegisterType("ndc"); + factory.LayoutRendererFactory.RegisterType("ndlc"); factory.LayoutRendererFactory.RegisterType("scopeproperty"); + factory.LayoutRendererFactory.RegisterType("mdc"); + factory.LayoutRendererFactory.RegisterType("mdlc"); factory.LayoutRendererFactory.RegisterType("scopetiming"); + factory.LayoutRendererFactory.RegisterType("ndlctiming"); factory.LayoutRendererFactory.RegisterType("sequenceid"); factory.LayoutRendererFactory.RegisterType("shortdate"); #if !NETSTANDARD1_3 && !NETSTANDARD1_5 diff --git a/src/NLog/Config/AssemblyExtensionTypes.tt b/src/NLog/Config/AssemblyExtensionTypes.tt index bb77e0cafb..661502beca 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.tt +++ b/src/NLog/Config/AssemblyExtensionTypes.tt @@ -97,7 +97,7 @@ namespace NLog.Config if (netFramework.Contains(type.ToString())) { #> -#if !NETSTANDARD +#if NETFRAMEWORK <# } else if (netStandard2.Contains(type.ToString())) diff --git a/src/NLog/LayoutRenderers/MdcLayoutRenderer.cs b/src/NLog/LayoutRenderers/MdcLayoutRenderer.cs deleted file mode 100644 index cca833e0cb..0000000000 --- a/src/NLog/LayoutRenderers/MdcLayoutRenderer.cs +++ /dev/null @@ -1,94 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.LayoutRenderers -{ - using System; - using System.Text; - using NLog.Config; - using NLog.Internal; - - /// - /// Obsolete and replaced by with NLog v5. - /// Render Mapped Diagnostics Logical (MDC) from - /// - /// - /// See NLog Wiki - /// - /// Documentation on NLog Wiki - [LayoutRenderer("mdc")] - [Obsolete("Replaced by ScopeContextPropertyLayoutRenderer ${scopeproperty}. Marked obsolete on NLog 5.0")] - public class MdcLayoutRenderer : LayoutRenderer, IStringValueRenderer - { - /// - /// Gets or sets the name of the item. - /// - /// - [RequiredParameter] - [DefaultParameter] - public string Item { get; set; } - - /// - /// Format string for conversion from object to string. - /// - /// - public string Format { get; set; } - - /// - protected override void Append(StringBuilder builder, LogEventInfo logEvent) - { - var value = GetValue(); - var formatProvider = GetFormatProvider(logEvent, null); - builder.AppendFormattedValue(value, Format, formatProvider, ValueFormatter); - } - - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) => GetStringValue(logEvent); - - private string GetStringValue(LogEventInfo logEvent) - { - if (Format != MessageTemplates.ValueFormatter.FormatAsJson) - { - object value = GetValue(); - string stringValue = FormatHelper.TryFormatToString(value, Format, GetFormatProvider(logEvent, null)); - return stringValue; - } - return null; - } - - private object GetValue() - { - //don't use MappedDiagnosticsContext.Get to ensure we are not locking the Factory (indirect by LogManager.Configuration). - return MappedDiagnosticsContext.GetObject(Item); - } - } -} diff --git a/src/NLog/LayoutRenderers/MdlcLayoutRenderer.cs b/src/NLog/LayoutRenderers/MdlcLayoutRenderer.cs deleted file mode 100644 index faaecf2c7b..0000000000 --- a/src/NLog/LayoutRenderers/MdlcLayoutRenderer.cs +++ /dev/null @@ -1,94 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.LayoutRenderers -{ - using System; - using System.Text; - using NLog.Config; - using NLog.Internal; - - /// - /// Obsolete and replaced by with NLog v5. - /// Render Mapped Diagnostics Logical Context (MDLC) from - /// - /// - /// See NLog Wiki - /// - /// Documentation on NLog Wiki - [LayoutRenderer("mdlc")] - [Obsolete("Replaced by ScopeContextPropertyLayoutRenderer ${scopeproperty}. Marked obsolete on NLog 5.0")] - public class MdlcLayoutRenderer : LayoutRenderer, IStringValueRenderer - { - /// - /// Gets or sets the name of the item. - /// - /// - [RequiredParameter] - [DefaultParameter] - public string Item { get; set; } - - /// - /// Format string for conversion from object to string. - /// - /// - public string Format { get; set; } - - /// - protected override void Append(StringBuilder builder, LogEventInfo logEvent) - { - var value = GetValue(); - var formatProvider = GetFormatProvider(logEvent, null); - builder.AppendFormattedValue(value, Format, formatProvider, ValueFormatter); - } - - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) => GetStringValue(logEvent); - - private string GetStringValue(LogEventInfo logEvent) - { - if (Format != MessageTemplates.ValueFormatter.FormatAsJson) - { - object value = GetValue(); - string stringValue = FormatHelper.TryFormatToString(value, Format, GetFormatProvider(logEvent, null)); - return stringValue; - } - return null; - } - - private object GetValue() - { - //don't use MappedDiagnosticsLogicalContext.Get to ensure we are not locking the Factory (indirect by LogManager.Configuration). - return MappedDiagnosticsLogicalContext.GetObject(Item); - } - } -} diff --git a/src/NLog/LayoutRenderers/NdcLayoutRenderer.cs b/src/NLog/LayoutRenderers/NdcLayoutRenderer.cs deleted file mode 100644 index ae6e251870..0000000000 --- a/src/NLog/LayoutRenderers/NdcLayoutRenderer.cs +++ /dev/null @@ -1,113 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.LayoutRenderers -{ - using System; - using System.Text; - - /// - /// Obsolete and replaced by with NLog v5. - /// Render Nested Diagnostic Context (NDC) from - /// - /// - /// See NLog Wiki - /// - /// Documentation on NLog Wiki - [LayoutRenderer("ndc")] - [Obsolete("Replaced by ScopeContextNestedStatesLayoutRenderer ${scopenested}. Marked obsolete on NLog 5.0")] - public class NdcLayoutRenderer : LayoutRenderer - { - /// - /// Gets or sets the number of top stack frames to be rendered. - /// - /// - public int TopFrames { get; set; } = -1; - - /// - /// Gets or sets the number of bottom stack frames to be rendered. - /// - /// - public int BottomFrames { get; set; } = -1; - - /// - /// Gets or sets the separator to be used for concatenating nested diagnostics context output. - /// - /// - public string Separator { get; set; } = " "; - - /// - protected override void Append(StringBuilder builder, LogEventInfo logEvent) - { - if (TopFrames == 1) - { - // Allows fast rendering of topframes=1 - var topFrame = NestedDiagnosticsContext.PeekObject(); - if (topFrame != null) - AppendAsString(topFrame, GetFormatProvider(logEvent), builder); - return; - } - - var messages = NestedDiagnosticsContext.GetAllObjects(); - if (messages.Length == 0) - return; - - int startPos = 0; - int endPos = messages.Length; - - if (TopFrames != -1) - { - endPos = Math.Min(TopFrames, messages.Length); - } - else if (BottomFrames != -1) - { - startPos = messages.Length - Math.Min(BottomFrames, messages.Length); - } - - var formatProvider = GetFormatProvider(logEvent); - string currentSeparator = string.Empty; - for (int i = endPos - 1; i >= startPos; --i) - { - builder.Append(currentSeparator); - AppendAsString(messages[i], formatProvider, builder); - currentSeparator = Separator; - } - } - - private static void AppendAsString(object message, IFormatProvider formatProvider, StringBuilder builder) - { - string stringValue = Convert.ToString(message, formatProvider); - builder.Append(stringValue); - } - } -} diff --git a/src/NLog/LayoutRenderers/NdlcLayoutRenderer.cs b/src/NLog/LayoutRenderers/NdlcLayoutRenderer.cs deleted file mode 100644 index eb963927b6..0000000000 --- a/src/NLog/LayoutRenderers/NdlcLayoutRenderer.cs +++ /dev/null @@ -1,113 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.LayoutRenderers -{ - using System; - using System.Text; - - /// - /// Obsolete and replaced by with NLog v5. - /// Render Nested Diagnostic Context (NDLC) from - /// - /// - /// See NLog Wiki - /// - /// Documentation on NLog Wiki - [LayoutRenderer("ndlc")] - [Obsolete("Replaced by ScopeContextNestedStatesLayoutRenderer ${scopenested}. Marked obsolete on NLog 5.0")] - public class NdlcLayoutRenderer : LayoutRenderer - { - /// - /// Gets or sets the number of top stack frames to be rendered. - /// - /// - public int TopFrames { get; set; } = -1; - - /// - /// Gets or sets the number of bottom stack frames to be rendered. - /// - /// - public int BottomFrames { get; set; } = -1; - - /// - /// Gets or sets the separator to be used for concatenating nested logical context output. - /// - /// - public string Separator { get; set; } = " "; - - /// - protected override void Append(StringBuilder builder, LogEventInfo logEvent) - { - if (TopFrames == 1) - { - // Allows fast rendering of topframes=1 - var topFrame = NestedDiagnosticsLogicalContext.PeekObject(); - if (topFrame != null) - AppendAsString(topFrame, GetFormatProvider(logEvent), builder); - return; - } - - var messages = NestedDiagnosticsLogicalContext.GetAllObjects(); - if (messages.Length == 0) - return; - - int startPos = 0; - int endPos = messages.Length; - - if (TopFrames != -1) - { - endPos = Math.Min(TopFrames, messages.Length); - } - else if (BottomFrames != -1) - { - startPos = messages.Length - Math.Min(BottomFrames, messages.Length); - } - - var formatProvider = GetFormatProvider(logEvent); - string currentSeparator = string.Empty; - for (int i = endPos - 1; i >= startPos; --i) - { - builder.Append(currentSeparator); - AppendAsString(messages[i], formatProvider, builder); - currentSeparator = Separator; - } - } - - private static void AppendAsString(object message, IFormatProvider formatProvider, StringBuilder builder) - { - string stringValue = Convert.ToString(message, formatProvider); - builder.Append(stringValue); - } - } -} diff --git a/src/NLog/LayoutRenderers/NdlcTimingLayoutRenderer.cs b/src/NLog/LayoutRenderers/NdlcTimingLayoutRenderer.cs deleted file mode 100644 index 0e7e86f93b..0000000000 --- a/src/NLog/LayoutRenderers/NdlcTimingLayoutRenderer.cs +++ /dev/null @@ -1,96 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.LayoutRenderers -{ - using System; - using System.Text; - - /// - /// Obsolete and replaced by with NLog v5. - /// Render Nested Diagnostic Context (NDLC) timings from - /// - /// - /// See NLog Wiki - /// - /// Documentation on NLog Wiki - [LayoutRenderer("ndlctiming")] - [Obsolete("Replaced by ScopeContextTimingLayoutRenderer ${scopetiming}. Marked obsolete on NLog 5.0")] - public class NdlcTimingLayoutRenderer : LayoutRenderer - { - /// - /// Gets or sets whether to only include the duration of the last scope created - /// - /// - public bool CurrentScope { get; set; } - - /// - /// Gets or sets whether to just display the scope creation time, and not the duration - /// - /// - public bool ScopeBeginTime { get; set; } - - /// - /// Gets or sets the TimeSpan format. Can be any argument accepted by TimeSpan.ToString(format). - /// - /// - public string Format { get; set; } - - /// - protected override void Append(StringBuilder builder, LogEventInfo logEvent) - { - TimeSpan? scopeDuration = CurrentScope ? ScopeContext.PeekInnerNestedDuration() : ScopeContext.PeekOuterNestedDuration(); - if (scopeDuration.HasValue) - { - if (scopeDuration.Value < TimeSpan.Zero) - scopeDuration = TimeSpan.Zero; - - if (ScopeBeginTime) - { - var formatProvider = GetFormatProvider(logEvent, null); - var scopeBegin = Time.TimeSource.Current.Time.Subtract(scopeDuration.Value); - builder.Append(scopeBegin.ToString(Format, formatProvider)); - } - else - { -#if !NET35 - var formatProvider = GetFormatProvider(logEvent, null); - builder.Append(scopeDuration.Value.ToString(Format, formatProvider)); -#else - builder.Append(scopeDuration.Value.ToString()); -#endif - } - } - } - } -} diff --git a/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs index f7adb585d1..754d9fdc16 100644 --- a/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs @@ -48,6 +48,8 @@ namespace NLog.LayoutRenderers /// /// Documentation on NLog Wiki [LayoutRenderer("scopenested")] + [LayoutRenderer("ndc")] + [LayoutRenderer("ndlc")] public sealed class ScopeContextNestedStatesLayoutRenderer : LayoutRenderer { /// diff --git a/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs index b3e3ba9e08..2a63a7891e 100644 --- a/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs @@ -46,6 +46,8 @@ namespace NLog.LayoutRenderers /// /// Documentation on NLog Wiki [LayoutRenderer("scopeproperty")] + [LayoutRenderer("mdc")] + [LayoutRenderer("mdlc")] public sealed class ScopeContextPropertyLayoutRenderer : LayoutRenderer, IStringValueRenderer { /// diff --git a/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs index 4b36a1e77d..17bf73a235 100644 --- a/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs @@ -46,6 +46,7 @@ namespace NLog.LayoutRenderers /// /// Documentation on NLog Wiki [LayoutRenderer("scopetiming")] + [LayoutRenderer("ndlctiming")] public sealed class ScopeContextTimingLayoutRenderer : LayoutRenderer { /// @@ -60,6 +61,13 @@ public sealed class ScopeContextTimingLayoutRenderer : LayoutRenderer /// public bool StartTime { get; set; } + /// + /// Gets or sets whether to just display the scope creation time, and not the duration + /// + /// + [Obsolete("Replaced by StartTime. Marked obsolete on NLog 6.0")] + public bool ScopeBeginTime { get => StartTime; set => StartTime = value; } + /// /// Gets or sets the TimeSpan format. Can be any argument accepted by TimeSpan.ToString(format). /// From bbe5f41f10b87504e67a41ae46a11f61a13974bf Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 3 Oct 2024 19:49:14 +0200 Subject: [PATCH 005/224] ConfigurationItemFactory - Remove obsolete registration methods (#5621) --- src/NLog.sln | 7 - src/NLog/Config/AssemblyExtensionLoader.cs | 77 +++-- src/NLog/Config/AssemblyLoadingEventArgs.cs | 61 ---- src/NLog/Config/ConfigurationItemFactory.cs | 274 +----------------- src/NLog/Config/Factory.cs | 153 +--------- src/NLog/Config/IFactory.cs | 3 - src/NLog/Config/INamedItemFactory.cs | 80 ----- src/NLog/Config/MethodFactory.cs | 134 +-------- src/NLog/Layouts/LayoutParser.cs | 10 +- src/NLog/SetupExtensionsBuilderExtensions.cs | 4 +- .../Config/ConfigurationItemFactoryTests.cs | 23 -- tests/NLog.UnitTests/Config/ExtensionTests.cs | 93 +----- tests/NLog.UnitTests/NLog.UnitTests.csproj | 1 - .../NLogPackageLoaders.cs | 127 -------- .../PackageLoaderTestAssembly.csproj | 11 - 15 files changed, 95 insertions(+), 963 deletions(-) delete mode 100644 src/NLog/Config/AssemblyLoadingEventArgs.cs delete mode 100644 src/NLog/Config/INamedItemFactory.cs delete mode 100644 tests/PackageLoaderTestAssembly/NLogPackageLoaders.cs delete mode 100644 tests/PackageLoaderTestAssembly/PackageLoaderTestAssembly.csproj diff --git a/src/NLog.sln b/src/NLog.sln index a83f8ba219..bcf2f8d800 100644 --- a/src/NLog.sln +++ b/src/NLog.sln @@ -13,8 +13,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLogAutoLoadExtension", ".. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.WindowsEventLog", "NLog.WindowsEventLog\NLog.WindowsEventLog.csproj", "{99BCE294-CA56-41BD-AEBF-61795F5650AB}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PackageLoaderTestAssembly", "..\tests\PackageLoaderTestAssembly\PackageLoaderTestAssembly.csproj", "{1A86A7FA-1C2E-4745-87FC-FD3F583DBAD2}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestAssets", "TestAssets", "{8CCD7CF7-F30B-414A-B7FE-1B49411E4EFD}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Packages", "Packages", "{194AB4BD-C817-4C59-A86C-49D89B8C3A64}" @@ -74,10 +72,6 @@ Global {99BCE294-CA56-41BD-AEBF-61795F5650AB}.Debug|Any CPU.Build.0 = Debug|Any CPU {99BCE294-CA56-41BD-AEBF-61795F5650AB}.Release|Any CPU.ActiveCfg = Release|Any CPU {99BCE294-CA56-41BD-AEBF-61795F5650AB}.Release|Any CPU.Build.0 = Release|Any CPU - {1A86A7FA-1C2E-4745-87FC-FD3F583DBAD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1A86A7FA-1C2E-4745-87FC-FD3F583DBAD2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1A86A7FA-1C2E-4745-87FC-FD3F583DBAD2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1A86A7FA-1C2E-4745-87FC-FD3F583DBAD2}.Release|Any CPU.Build.0 = Release|Any CPU {B9714BBB-A9F6-429B-ACC1-961C16643057}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B9714BBB-A9F6-429B-ACC1-961C16643057}.Debug|Any CPU.Build.0 = Debug|Any CPU {B9714BBB-A9F6-429B-ACC1-961C16643057}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -114,7 +108,6 @@ Global {94F21E64-A259-4D79-AFCA-08974DF51701} = {8CCD7CF7-F30B-414A-B7FE-1B49411E4EFD} {B1DA63B3-1EB0-41B3-BFDA-9D7409481E27} = {8CCD7CF7-F30B-414A-B7FE-1B49411E4EFD} {99BCE294-CA56-41BD-AEBF-61795F5650AB} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} - {1A86A7FA-1C2E-4745-87FC-FD3F583DBAD2} = {8CCD7CF7-F30B-414A-B7FE-1B49411E4EFD} {B9714BBB-A9F6-429B-ACC1-961C16643057} = {8CCD7CF7-F30B-414A-B7FE-1B49411E4EFD} {BFE365AA-A01F-4E8B-B540-3F8DF0466CE6} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {1648106F-CD67-4009-878B-96B5D2369B6C} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} diff --git a/src/NLog/Config/AssemblyExtensionLoader.cs b/src/NLog/Config/AssemblyExtensionLoader.cs index 6e27a14ad5..ee557bed09 100644 --- a/src/NLog/Config/AssemblyExtensionLoader.cs +++ b/src/NLog/Config/AssemblyExtensionLoader.cs @@ -60,9 +60,48 @@ public void LoadAssemblyFromName(ConfigurationItemFactory factory, string assemb InternalLogger.Info("Loading assembly name: {0}{1}", assemblyName, string.IsNullOrEmpty(itemNamePrefix) ? "" : $" (Prefix={itemNamePrefix})"); Assembly extensionAssembly = LoadAssemblyFromName(assemblyName); - InternalLogger.LogAssemblyVersion(extensionAssembly); - factory.RegisterItemsFromAssembly(extensionAssembly, itemNamePrefix); - InternalLogger.Debug("Loading assembly name: {0} succeeded!", assemblyName); + LoadAssembly(factory, extensionAssembly, itemNamePrefix); + } + + public void LoadAssembly(ConfigurationItemFactory factory, Assembly assembly, string itemNamePrefix) + { + InternalLogger.LogAssemblyVersion(assembly); + + InternalLogger.Debug("ScanAssembly('{0}')", assembly.FullName); + var typesToScan = AssemblyHelpers.SafeGetTypes(assembly); + if (typesToScan?.Length > 0) + { + if (ReferenceEquals(assembly, typeof(LogFactory).GetAssembly())) + { + typesToScan = typesToScan.Where(t => t.IsPublic() && t.IsClass()).ToArray(); + } + + foreach (Type type in typesToScan) + { + try + { + RegisterTypeFromAssembly(factory, type, itemNamePrefix); + } + catch (Exception exception) + { + InternalLogger.Error(exception, "Failed to add type '{0}'.", type.FullName); + + if (exception.MustBeRethrown()) + { + throw; + } + } + } + } + + InternalLogger.Debug("Loading assembly name: {0} succeeded!", assembly.FullName); + } + + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2072")] + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2067")] + private static void RegisterTypeFromAssembly(ConfigurationItemFactory factory, Type type, string itemNamePrefix) + { + factory.RegisterType(type, itemNamePrefix); } private static bool SkipAlreadyLoadedAssembly(ConfigurationItemFactory factory, string assemblyName, string itemNamePrefix) @@ -197,20 +236,9 @@ private static bool IsNLogItemTypeAlreadyRegistered LoadNLogExtensionAssemblies(ConfigurationItemFactory factory, Assembly nlogAssembly, string[] extensionDlls) + private HashSet LoadNLogExtensionAssemblies(ConfigurationItemFactory factory, Assembly nlogAssembly, string[] extensionDlls) { HashSet alreadyRegistered = new HashSet(StringComparer.OrdinalIgnoreCase) { @@ -276,9 +304,12 @@ private static HashSet LoadNLogExtensionAssemblies(ConfigurationItemFact { try { - var extensionAssembly = RegisterAssemblyFromPath(factory, extensionDll); + var extensionAssembly = LoadAssemblyFromPath(extensionDll); if (extensionAssembly != null) + { + LoadAssembly(factory, extensionAssembly, string.Empty); alreadyRegistered.Add(extensionAssembly.FullName); + } } catch (Exception ex) { @@ -298,7 +329,7 @@ private static HashSet LoadNLogExtensionAssemblies(ConfigurationItemFact return alreadyRegistered; } - private static void RegisterAppDomainAssemblies(ConfigurationItemFactory factory, Assembly nlogAssembly, HashSet alreadyRegistered) + private void RegisterAppDomainAssemblies(ConfigurationItemFactory factory, Assembly nlogAssembly, HashSet alreadyRegistered) { alreadyRegistered.Add(nlogAssembly.FullName); @@ -311,7 +342,7 @@ private static void RegisterAppDomainAssemblies(ConfigurationItemFactory factory { if (assembly.FullName.StartsWith("NLog.", StringComparison.OrdinalIgnoreCase) && !alreadyRegistered.Contains(assembly.FullName)) { - factory.RegisterItemsFromAssembly(assembly); + LoadAssembly(factory, assembly, string.Empty); } if (IncludeAsHiddenAssembly(assembly.FullName)) @@ -430,8 +461,8 @@ private static Assembly LoadAssemblyFromPath(string assemblyFileName, string bas if (!string.IsNullOrEmpty(baseDirectory)) { fullFileName = System.IO.Path.Combine(baseDirectory, assemblyFileName); - InternalLogger.Debug("Loading assembly file: {0}", fullFileName); } + InternalLogger.Info("Loading assembly file: {0}", fullFileName); #if NETSTANDARD1_5 try @@ -512,5 +543,7 @@ internal interface IAssemblyExtensionLoader void LoadAssemblyFromName(ConfigurationItemFactory factory, string assemblyName, string itemNamePrefix); void LoadTypeFromName(ConfigurationItemFactory factory, string typeName, string itemNamePrefix); + + void LoadAssembly(ConfigurationItemFactory factory, Assembly assembly, string itemNamePrefix); } } diff --git a/src/NLog/Config/AssemblyLoadingEventArgs.cs b/src/NLog/Config/AssemblyLoadingEventArgs.cs deleted file mode 100644 index e181e573d4..0000000000 --- a/src/NLog/Config/AssemblyLoadingEventArgs.cs +++ /dev/null @@ -1,61 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -using System; -using System.ComponentModel; -using System.Reflection; - -namespace NLog.Config -{ - /// - /// Obsolete since dynamic assembly loading is not compatible with publish as trimmed application. - /// Event notification about trying to load assembly with NLog extensions. - /// - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - public class AssemblyLoadingEventArgs : CancelEventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// Assembly that have been loaded - public AssemblyLoadingEventArgs(Assembly assembly) - { - Assembly = assembly; - } - - /// - /// The assembly that is trying to load. - /// - public Assembly Assembly { get; } - } -} diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index e6f2f43d2f..d75c2f0a2f 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -84,14 +84,6 @@ public ItemFactory(Func> itemProperties, Func - /// Obsolete since dynamic assembly loading is not compatible with publish as trimmed application. - /// Called before the assembly with NLog extensions is being loaded. - /// - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static event EventHandler AssemblyLoading; - /// /// Initializes a new instance of the class. /// @@ -100,32 +92,16 @@ public ConfigurationItemFactory() { } - /// - /// Obsolete since dynamic assembly loading is not compatible with publish as trimmed application. - /// Initializes a new instance of the class. - /// - /// The assemblies to scan for named items. - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public ConfigurationItemFactory(params Assembly[] assemblies) - : this(LogManager.LogFactory.ServiceRepository, null) - { - foreach (var asm in assemblies) - { - RegisterItemsFromAssembly(asm); - } - } - internal ConfigurationItemFactory(ServiceRepository serviceRepository, ConfigurationItemFactory globalDefaultFactory) { _serviceRepository = Guard.ThrowIfNull(serviceRepository); - _targets = new Factory(this, globalDefaultFactory?._targets); - _filters = new Factory(this, globalDefaultFactory?._filters); + _targets = new Factory(this); + _filters = new Factory(this); _layoutRenderers = new LayoutRendererFactory(this, globalDefaultFactory?._layoutRenderers); - _layouts = new Factory(this, globalDefaultFactory?._layouts); - _conditionMethods = new MethodFactory(globalDefaultFactory?._conditionMethods); - _ambientProperties = new Factory(this, globalDefaultFactory?._ambientProperties); - _timeSources = new Factory(this, globalDefaultFactory?._timeSources); + _layouts = new Factory(this); + _conditionMethods = new MethodFactory(); + _ambientProperties = new Factory(this); + _timeSources = new Factory(this); _allFactories = new IFactory[] { _targets, @@ -189,80 +165,6 @@ internal ICollection ItemTypes } } - /// - /// Obsolete and replaced by with NLog v5.2. - /// Gets or sets the creator delegate used to instantiate configuration objects. - /// - /// - /// By overriding this property, one can enable dependency injection or interception for created objects. - /// - [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public ConfigurationItemCreator CreateInstance { get; set; } = FactoryHelper.CreateInstance; - - /// - /// Obsolete and replaced by with NLog v5.2. - /// Gets the factory. - /// - /// The target factory. - [Obsolete("Instead use LogManager.Setup().SetupExtensions(ext => ext.RegisterTarget()). Marked obsolete with NLog v5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public INamedItemFactory Targets => _targets; - - /// - /// Obsolete and replaced by with NLog v5.2. - /// Gets the factory. - /// - /// The layout factory. - [Obsolete("Instead use LogManager.Setup().SetupExtensions(ext => ext.RegisterLayout()). Marked obsolete with NLog v5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public INamedItemFactory Layouts => _layouts; - - /// - /// Obsolete and replaced by with NLog v5.2. - /// Gets the factory. - /// - /// The layout renderer factory. - [Obsolete("Instead use LogManager.Setup().SetupExtensions(ext => ext.RegisterLayoutRenderer()). Marked obsolete with NLog v5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public INamedItemFactory LayoutRenderers => _layoutRenderers; - - /// - /// Obsolete and replaced by with NLog v5.2. - /// Gets the ambient property factory. - /// - /// The ambient property factory. - [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public INamedItemFactory AmbientProperties => _ambientProperties; - - /// - /// Obsolete and replaced by with NLog v5.2. - /// Gets the factory. - /// - /// The filter factory. - [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public INamedItemFactory Filters => _filters; - - /// - /// Obsolete and replaced by with NLog v5.2. - /// Gets the time source factory. - /// - /// The time source factory. - [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public INamedItemFactory TimeSources => _timeSources; - - /// - /// Obsolete and replaced by with NLog v5.2. - /// Gets the condition method factory. - /// - /// The condition method factory. - [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public INamedItemFactory ConditionMethods => _conditionMethods; - /// /// Obsolete and replaced by with NLog v5.2. /// Gets or sets the JSON serializer to use with @@ -275,36 +177,12 @@ public IJsonConverter JsonConverter set => _serviceRepository.RegisterJsonConverter(value); } - /// - /// Obsolete and replaced by with NLog v5.2. - /// Gets or sets the string serializer to use with - /// - [Obsolete("Instead use NLog.LogManager.Setup().SetupSerialization(s => s.RegisterValueFormatter()) or ResolveService(). Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public IValueFormatter ValueFormatter - { - get => _serviceRepository.GetService(); - set => _serviceRepository.RegisterValueFormatter(value); - } - - /// - /// Obsolete and replaced by with NLog v5.2. - /// Gets or sets the parameter converter to use with or - /// - [Obsolete("Instead use LogFactory.ServiceRepository.RegisterService(). Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public IPropertyTypeConverter PropertyTypeConverter - { - get => _serviceRepository.GetService(); - set => _serviceRepository.RegisterPropertyTypeConverter(value); - } - /// /// Perform message template parsing and formatting of LogEvent messages (True = Always, False = Never, Null = Auto Detect) /// /// /// - Null (Auto Detect) : NLog-parser checks for positional parameters, and will then fallback to string.Format-rendering. - /// - True: Always performs the parsing of and rendering of using the NLog-parser (Allows custom formatting with ) + /// - True: Always performs the parsing of and rendering of using the NLog-parser (Allows custom formatting with ) /// - False: Always performs parsing and rendering using string.Format (Fastest if not using structured logging) /// public bool? ParseMessageTemplates @@ -313,144 +191,6 @@ public bool? ParseMessageTemplates set => _serviceRepository.ParseMessageTemplates(value); } - /// - /// Obsolete since dynamic assembly loading is not compatible with publish as trimmed application. - /// Registers named items from the assembly. - /// - /// The assembly. - [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public void RegisterItemsFromAssembly(Assembly assembly) - { - RegisterItemsFromAssembly(assembly, string.Empty); - } - - /// - /// Obsolete since dynamic assembly loading is not compatible with publish as trimmed application. - /// Registers named items from the assembly. - /// - /// The assembly. - /// Item name prefix. - [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public void RegisterItemsFromAssembly(Assembly assembly, string itemNamePrefix) - { - if (AssemblyLoading != null) - { - var args = new AssemblyLoadingEventArgs(assembly); - AssemblyLoading.Invoke(null, args); - if (args.Cancel) - { - InternalLogger.Info("Loading assembly '{0}' is canceled", assembly.FullName); - return; - } - } - - InternalLogger.Debug("ScanAssembly('{0}')", assembly.FullName); - var typesToScan = AssemblyHelpers.SafeGetTypes(assembly); - if (typesToScan?.Length > 0) - { - string assemblyName = string.Empty; - - if (ReferenceEquals(assembly, typeof(LogFactory).GetAssembly())) - { - typesToScan = typesToScan.Where(t => t.IsPublic() && t.IsClass()).ToArray(); - } - else - { - assemblyName = new AssemblyName(assembly.FullName).Name; - PreloadAssembly(typesToScan); - } - - lock (SyncRoot) - { - foreach (IFactory f in _allFactories) - { - f.ScanTypes(typesToScan, assemblyName, itemNamePrefix); - } - } - } - } - - /// - /// Obsolete since dynamic assembly loading is not compatible with publish as trimmed application. - /// Call Preload for NLogPackageLoader - /// - /// - /// Every package could implement a class "NLogPackageLoader" (namespace not important) with the public static method "Preload" (no arguments) - /// This method will be called just before registering all items in the assembly. - /// - /// - [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] - [UnconditionalSuppressMessage("Trimming - Ignore since obsolete", "IL2072")] - [EditorBrowsable(EditorBrowsableState.Never)] - public void PreloadAssembly(Type[] typesToScan) - { - var types = typesToScan.Where(t => t.Name.Equals("NLogPackageLoader", StringComparison.OrdinalIgnoreCase)); - - foreach (var type in types) - { - CallPreload(type); - } - } - - /// - /// Call the Preload method for . The Preload method must be static. - /// - /// - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - private void CallPreload([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type) - { - if (type is null) - { - return; - } - - InternalLogger.Debug("Found for preload'{0}'", type.FullName); - var preloadMethod = type.GetMethod("Preload"); - if (preloadMethod != null) - { - if (preloadMethod.IsStatic) - { - InternalLogger.Debug("NLogPackageLoader contains Preload method"); - - //only static, so first param null - try - { - var parameters = CreatePreloadParameters(preloadMethod, this); - - preloadMethod.Invoke(null, parameters); - InternalLogger.Debug("Preload successfully invoked for '{0}'", type.FullName); - } - catch (Exception e) - { - InternalLogger.Warn(e, "Invoking Preload for '{0}' failed", type.FullName); - } - } - else - { - InternalLogger.Debug("NLogPackageLoader contains a preload method, but isn't static"); - } - } - else - { - InternalLogger.Debug("{0} doesn't contain Preload method", type.FullName); - } - } - - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - private static object[] CreatePreloadParameters(MethodInfo preloadMethod, ConfigurationItemFactory configurationItemFactory) - { - var firstParam = preloadMethod.GetParameters().FirstOrDefault(); - object[] parameters = null; - if (firstParam?.ParameterType == typeof(ConfigurationItemFactory)) - { - parameters = new object[] { configurationItemFactory }; - } - - return parameters; - } - /// /// Clears the contents of all factories. /// diff --git a/src/NLog/Config/Factory.cs b/src/NLog/Config/Factory.cs index 9533235be9..7c4f0d8e9b 100644 --- a/src/NLog/Config/Factory.cs +++ b/src/NLog/Config/Factory.cs @@ -48,66 +48,20 @@ namespace NLog.Config /// The type of the attribute used to annotate items. internal class Factory : IFactory, IFactory -#pragma warning disable CS0618 // Type or member is obsolete - , INamedItemFactory -#pragma warning restore CS0618 // Type or member is obsolete where TBaseType : class where TAttributeType : NameBaseAttribute { - private struct ItemFactory - { - public readonly GetTypeDelegate ItemType; - public readonly Func ItemCreator; - - public ItemFactory(GetTypeDelegate type, Func itemCreator) - { - ItemType = type; - ItemCreator = itemCreator; - } - } - - private readonly Dictionary _items; + private readonly Dictionary> _items; private readonly ConfigurationItemFactory _parentFactory; - private readonly Factory _globalDefaultFactory; - internal Factory(ConfigurationItemFactory parentFactory, Factory globalDefaultFactory) + internal Factory(ConfigurationItemFactory parentFactory) { _parentFactory = parentFactory; - _globalDefaultFactory = globalDefaultFactory; - _items = new Dictionary(globalDefaultFactory is null ? 16 : 0, StringComparer.OrdinalIgnoreCase); + _items = new Dictionary>(16, StringComparer.OrdinalIgnoreCase); } private delegate Type GetTypeDelegate(); - /// - /// Scans the assembly. - /// - /// The types to scan. - /// The assembly name for the types. - /// The prefix. - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - [UnconditionalSuppressMessage("Trimming - Ignore since obsolete", "IL2072")] - [UnconditionalSuppressMessage("Trimming - Ignore since obsolete", "IL2062")] - public void ScanTypes(Type[] types, string assemblyName, string itemNamePrefix) - { - foreach (Type t in types) - { - try - { - RegisterType(t, itemNamePrefix); - } - catch (Exception exception) - { - InternalLogger.Error(exception, "Failed to add type '{0}'.", t.FullName); - - if (exception.MustBeRethrown()) - { - throw; - } - } - } - } - /// /// Registers the type. /// @@ -159,7 +113,7 @@ public void RegisterNamedType(string itemName, string typeName) lock (ConfigurationItemFactory.SyncRoot) { - _items[itemName] = new ItemFactory(typeLookup, typeCreator); + _items[itemName] = typeCreator; } } @@ -174,20 +128,6 @@ public virtual void Clear() } } - /// - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - [UnconditionalSuppressMessage("Trimming - Ignore since obsolete", "IL2067")] - void INamedItemFactory.RegisterDefinition(string itemName, Type itemDefinition) - { - if (string.IsNullOrEmpty(itemName)) - throw new ArgumentException($"Missing NLog {typeof(TBaseType).Name} type-alias", nameof(itemName)); - - if (!typeof(TBaseType).IsAssignableFrom(itemDefinition)) - throw new ArgumentException($"Not of type NLog {typeof(TBaseType).Name}", nameof(itemDefinition)); - - RegisterDefinition(itemDefinition, itemName, string.Empty); - } - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] public void RegisterDefinition(string typeAlias, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] Type itemType) { @@ -205,11 +145,10 @@ private void RegisterDefinition([DynamicallyAccessedMembers(DynamicallyAccessedM typeAlias = FactoryExtensions.NormalizeName(typeAlias); Func itemCreator = () => (TBaseType)Activator.CreateInstance(itemType); - var itemFactory = new ItemFactory(() => itemType, itemCreator); lock (ConfigurationItemFactory.SyncRoot) { _parentFactory.RegisterTypeProperties(itemType, () => itemCreator.Invoke()); - _items[itemNamePrefix + typeAlias] = itemFactory; + _items[itemNamePrefix + typeAlias] = () => itemCreator.Invoke(); } } @@ -217,11 +156,10 @@ private void RegisterDefinition([DynamicallyAccessedMembers(DynamicallyAccessedM { typeAlias = FactoryExtensions.NormalizeName(typeAlias); - var itemFactory = new ItemFactory(() => typeof(TType), () => new TType()); lock (ConfigurationItemFactory.SyncRoot) { _parentFactory.RegisterTypeProperties(() => new TType()); - _items[typeAlias] = itemFactory; + _items[typeAlias] = () => new TType(); } } @@ -229,50 +167,14 @@ private void RegisterDefinition([DynamicallyAccessedMembers(DynamicallyAccessedM { typeAlias = FactoryExtensions.NormalizeName(typeAlias); - var itemFactory = new ItemFactory(() => typeof(TType), () => itemCreator()); lock (ConfigurationItemFactory.SyncRoot) { _parentFactory.RegisterTypeProperties(() => itemCreator()); - _items[typeAlias] = itemFactory; + _items[typeAlias] = () => itemCreator(); } } - /// - [Obsolete("Use TryCreateInstance instead. Marked obsolete with NLog v5.2")] - public bool TryGetDefinition(string itemName, out Type result) - { - itemName = FactoryExtensions.NormalizeName(itemName); - - if (!TryGetItemFactory(itemName, out var itemFactory) || itemFactory.ItemType is null) - { - if (_globalDefaultFactory != null && _globalDefaultFactory.TryGetDefinition(itemName, out result)) - { - return true; - } - - result = null; - return false; - } - - try - { - result = itemFactory.ItemType.Invoke(); - return result != null; - } - catch (Exception ex) - { - if (ex.MustBeRethrown()) - { - throw; - } - - // delegate invocation failed - type is not available - result = null; - return false; - } - } - - private bool TryGetItemFactory(string typeAlias, out ItemFactory itemFactory) + private bool TryGetItemFactory(string typeAlias, out Func itemFactory) { lock (ConfigurationItemFactory.SyncRoot) { @@ -280,53 +182,20 @@ private bool TryGetItemFactory(string typeAlias, out ItemFactory itemFactory) } } - bool INamedItemFactory.TryCreateInstance(string itemName, out TBaseType result) - { - return TryCreateInstance(itemName, out result); - } - /// public virtual bool TryCreateInstance(string typeAlias, out TBaseType result) { -#pragma warning disable CS0618 // Type or member is obsolete - if (TryCreateInstanceLegacy(typeAlias, out result)) - return true; -#pragma warning restore CS0618 // Type or member is obsolete - typeAlias = FactoryExtensions.NormalizeName(typeAlias); - if (!TryGetItemFactory(typeAlias, out var itemFactory) || itemFactory.ItemCreator is null) + if (!TryGetItemFactory(typeAlias, out var itemFactory) || itemFactory is null) { result = null; return false; } - result = itemFactory.ItemCreator.Invoke(); + result = itemFactory.Invoke(); return !(result is null); } - - [Obsolete("Use TryCreateInstance instead. Marked obsolete with NLog v5.2")] - private bool TryCreateInstanceLegacy(string itemName, out TBaseType result) - { - if (!ReferenceEquals(_parentFactory.CreateInstance, FactoryHelper.CreateInstance)) - { - if (TryGetDefinition(itemName, out var itemType)) - { - result = (TBaseType)_parentFactory.CreateInstance(itemType); - return true; - } - } - - result = null; - return false; - } - - /// - [Obsolete("Use TryCreateInstance instead. Marked obsolete with NLog v5.2")] - TBaseType INamedItemFactory.CreateInstance(string itemName) - { - return FactoryExtensions.CreateInstance(this, itemName); - } } internal static class FactoryExtensions @@ -419,7 +288,7 @@ internal sealed class LayoutRendererFactory : Factory, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - void ScanTypes(Type[] types, string assemblyName, string itemNamePrefix); - void RegisterType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] Type type, string itemNamePrefix); } diff --git a/src/NLog/Config/INamedItemFactory.cs b/src/NLog/Config/INamedItemFactory.cs deleted file mode 100644 index 4be1932703..0000000000 --- a/src/NLog/Config/INamedItemFactory.cs +++ /dev/null @@ -1,80 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.Config -{ - using System; - using System.ComponentModel; - - /// - /// Obsolete since dynamic type loading is not compatible with publish as trimmed application. Replaced by . - /// Represents a factory of named items (such as targets, layouts, layout renderers, etc.). - /// - /// Base type for each item instance. - /// Item definition type (typically ). - [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public interface INamedItemFactory - where TInstanceType : class - { - /// - /// Registers new item definition. - /// - /// Name of the item. - /// Item definition. - void RegisterDefinition(string itemName, TDefinitionType itemDefinition); - - /// - /// Tries to get registered item definition. - /// - /// Name of the item. - /// Reference to a variable which will store the item definition. - /// Item definition. - bool TryGetDefinition(string itemName, out TDefinitionType result); - - /// - /// Creates item instance. - /// - /// Name of the item. - /// Newly created item instance. - TInstanceType CreateInstance(string itemName); - - /// - /// Tries to create an item instance. - /// - /// Name of the item. - /// The result. - /// True if instance was created successfully, false otherwise. - bool TryCreateInstance(string itemName, out TInstanceType result); - } -} diff --git a/src/NLog/Config/MethodFactory.cs b/src/NLog/Config/MethodFactory.cs index 92bfb66a2a..0f69df4b3b 100644 --- a/src/NLog/Config/MethodFactory.cs +++ b/src/NLog/Config/MethodFactory.cs @@ -37,20 +37,14 @@ namespace NLog.Config using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; - using NLog.Common; using NLog.Conditions; using NLog.Internal; /// /// Factory for locating methods. /// - internal sealed class MethodFactory : -#pragma warning disable CS0618 // Type or member is obsolete - INamedItemFactory, -#pragma warning restore CS0618 // Type or member is obsolete - IFactory + internal sealed class MethodFactory : IFactory { - private readonly MethodFactory _globalDefaultFactory; private readonly Dictionary _nameToMethodDetails = new Dictionary(StringComparer.OrdinalIgnoreCase); struct MethodDetails @@ -88,48 +82,6 @@ public MethodDetails( } } - /// - /// Initializes a new instance of the class. - /// - public MethodFactory(MethodFactory globalDefaultFactory) - { - _globalDefaultFactory = globalDefaultFactory; - } - - /// - /// Scans the assembly for classes marked with expected class - /// and methods marked with expected and adds them - /// to the factory. - /// - /// The types to scan. - /// The assembly name for the type. - /// The item name prefix. - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - [UnconditionalSuppressMessage("Trimming - Ignore since obsolete", "IL2072")] - [UnconditionalSuppressMessage("Trimming - Ignore since obsolete", "IL2062")] - public void ScanTypes(Type[] types, string assemblyName, string itemNamePrefix) - { - foreach (Type t in types) - { - try - { - if (t.IsClass()) - { - RegisterType(t, itemNamePrefix); - } - } - catch (Exception exception) - { - InternalLogger.Error(exception, "Failed to add type '{0}'.", t.FullName); - - if (exception.MustBeRethrown()) - { - throw; - } - } - } - } - /// /// Registers the type. /// @@ -137,23 +89,16 @@ public void ScanTypes(Type[] types, string assemblyName, string itemNamePrefix) /// The item name prefix. void IFactory.RegisterType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] Type type, string itemNamePrefix) { - RegisterType(type, itemNamePrefix); - } - - /// - /// Registers the type. - /// - /// The type to register. - /// The item name prefix. - private void RegisterType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type, string itemNamePrefix) - { - var extractedMethods = ExtractClassMethods(type); - if (extractedMethods?.Count > 0) + if (type.IsClass()) { - for (int i = 0; i < extractedMethods.Count; ++i) + var extractedMethods = ExtractClassMethods(type); + if (extractedMethods?.Count > 0) { - string methodName = string.IsNullOrEmpty(itemNamePrefix) ? extractedMethods[i].Key : itemNamePrefix + extractedMethods[i].Key; - RegisterDefinition(methodName, extractedMethods[i].Value); + for (int i = 0; i < extractedMethods.Count; ++i) + { + string methodName = string.IsNullOrEmpty(itemNamePrefix) ? extractedMethods[i].Key : itemNamePrefix + extractedMethods[i].Key; + RegisterDefinition(methodName, extractedMethods[i].Value); + } } } } @@ -196,17 +141,6 @@ public void Clear() } } - /// - /// Registers the definition of a single method. - /// - /// The method name. - /// The method info. - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - void INamedItemFactory.RegisterDefinition(string itemName, MethodInfo itemDefinition) - { - RegisterDefinition(itemName, itemDefinition); - } - internal void RegisterDefinition(string methodName, MethodInfo methodInfo) { object[] defaultMethodParameters = ResolveDefaultMethodParameters(methodInfo, out var manyParameterMinCount, out var manyParameterMaxCount, out var includeLogEvent); @@ -338,56 +272,6 @@ private static object[] ResolveMethodParameters(object[] defaultMethodParameters return methodParameters; } - /// - /// Tries to retrieve method by name. - /// - /// The method name. - /// The result. - /// A value of true if the method was found, false otherwise. - [Obsolete("Use TryCreateInstance instead. Marked obsolete with NLog v5.2")] - bool INamedItemFactory.TryCreateInstance(string itemName, out MethodInfo result) - { - return TryGetDefinition(itemName, out result); - } - - /// - /// Retrieves method by name. - /// - /// Method name. - /// MethodInfo object. - [Obsolete("Use TryCreateInstance instead. Marked obsolete with NLog v5.2")] - MethodInfo INamedItemFactory.CreateInstance(string itemName) - { - if (TryGetDefinition(itemName, out MethodInfo result)) - { - return result; - } - - throw new NLogConfigurationException($"Unknown function: '{itemName}'"); - } - - /// - /// Tries to get method definition. - /// - /// The method name. - /// The result. - /// A value of true if the method was found, false otherwise. - [Obsolete("Use TryCreateInstance instead. Marked obsolete with NLog v5.2")] - public bool TryGetDefinition(string itemName, out MethodInfo result) - { - lock (_nameToMethodDetails) - { - if (_nameToMethodDetails.TryGetValue(itemName, out var methodDetails)) - { - result = methodDetails.MethodInfo; - return result != null; - } - } - - result = null; - return _globalDefaultFactory?.TryGetDefinition(itemName, out result) ?? false; - } - public void RegisterNoParameters(string methodName, Func noParameters, MethodInfo legacyMethodInfo = null) { lock (_nameToMethodDetails) diff --git a/src/NLog/Layouts/LayoutParser.cs b/src/NLog/Layouts/LayoutParser.cs index 5935dfa58e..746d64512c 100644 --- a/src/NLog/Layouts/LayoutParser.cs +++ b/src/NLog/Layouts/LayoutParser.cs @@ -588,7 +588,15 @@ private static LayoutRenderer GetLayoutRenderer(string typeName, ConfigurationIt try { - layoutRenderer = configurationItemFactory.LayoutRendererFactory.CreateInstance(typeName); + if (throwConfigExceptions == false && !configurationItemFactory.LayoutRendererFactory.TryCreateInstance(typeName, out layoutRenderer)) + { + InternalLogger.Debug("Failed to create LayoutRenderer with unknown type-alias: '{0}'", typeName); + return new LiteralLayoutRenderer(string.Empty); // replace with empty values + } + else + { + layoutRenderer = configurationItemFactory.LayoutRendererFactory.CreateInstance(typeName); + } } catch (NLogConfigurationException ex) { diff --git a/src/NLog/SetupExtensionsBuilderExtensions.cs b/src/NLog/SetupExtensionsBuilderExtensions.cs index 7c8bda9e84..8e1bf9ada3 100644 --- a/src/NLog/SetupExtensionsBuilderExtensions.cs +++ b/src/NLog/SetupExtensionsBuilderExtensions.cs @@ -80,9 +80,7 @@ public static ISetupExtensionsBuilder AutoLoadExtensions(this ISetupExtensionsBu /// public static ISetupExtensionsBuilder RegisterAssembly(this ISetupExtensionsBuilder setupBuilder, Assembly assembly) { -#pragma warning disable CS0618 // Type or member is obsolete - ConfigurationItemFactory.Default.RegisterItemsFromAssembly(assembly); -#pragma warning restore CS0618 // Type or member is obsolete + ConfigurationItemFactory.Default.AssemblyLoader.LoadAssembly(ConfigurationItemFactory.Default, assembly, string.Empty); return setupBuilder; } diff --git a/tests/NLog.UnitTests/Config/ConfigurationItemFactoryTests.cs b/tests/NLog.UnitTests/Config/ConfigurationItemFactoryTests.cs index 1e8a335287..f342267e17 100644 --- a/tests/NLog.UnitTests/Config/ConfigurationItemFactoryTests.cs +++ b/tests/NLog.UnitTests/Config/ConfigurationItemFactoryTests.cs @@ -34,7 +34,6 @@ namespace NLog.UnitTests.Config { using System; - using System.Collections.Generic; using NLog.Config; using NLog.Targets; using Xunit; @@ -71,27 +70,5 @@ public void ConfigurationItemFactorySimpleTest() itemFactory.TargetFactory.TryCreateInstance("Debug", out var result); Assert.NotNull(result); } - - [Fact] - [Obsolete("Instead use LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] - public void ConfigurationItemFactoryDefaultTest() - { - var itemFactory = new ConfigurationItemFactory(); - Assert.IsType(itemFactory.CreateInstance(typeof(DebugTarget))); - } - - [Fact] - [Obsolete("Instead use LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] - public void ConfigurationItemFactoryUsesSuppliedDelegateToResolveObject() - { - var itemFactory = new ConfigurationItemFactory(); - itemFactory.RegisterType(typeof(DebugTarget), string.Empty); - List resolvedTypes = new List(); - itemFactory.CreateInstance = t => { resolvedTypes.Add(t); return Activator.CreateInstance(t); }; - itemFactory.TargetFactory.TryCreateInstance("Debug", out var target); - Assert.NotNull(target); - Assert.Single(resolvedTypes); - Assert.Equal(typeof(DebugTarget), resolvedTypes[0]); - } } } diff --git a/tests/NLog.UnitTests/Config/ExtensionTests.cs b/tests/NLog.UnitTests/Config/ExtensionTests.cs index bd3e84f0e9..92fe502c9d 100644 --- a/tests/NLog.UnitTests/Config/ExtensionTests.cs +++ b/tests/NLog.UnitTests/Config/ExtensionTests.cs @@ -481,91 +481,6 @@ public void ExtensionTypeWithAssemblyNameCanLoad() Assert.Equal("NLogAutloadExtension.AutoLoadTarget", autoLoadedTarget.GetType().ToString()); } - [Theory] - [InlineData(true)] - [InlineData(false)] - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - public void Extension_loading_could_be_canceled(bool cancel) - { - ConfigurationItemFactory.Default = null; - - EventHandler onAssemblyLoading = (sender, e) => - { - if (e.Assembly.FullName.Contains("NLogAutoLoadExtension")) - { - e.Cancel = cancel; - } - }; - - try - { - ConfigurationItemFactory.AssemblyLoading += onAssemblyLoading; - - using (new NoThrowNLogExceptions()) - { - var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@" - - - - - - - - -").LogFactory; - - var autoLoadedTarget = logFactory.Configuration.FindTargetByName("t"); - - if (cancel) - { - Assert.Null(autoLoadedTarget); - } - else - { - Assert.Equal("NLogAutloadExtension.AutoLoadTarget", autoLoadedTarget.GetType().ToString()); - } - } - } - finally - { - //cleanup - ConfigurationItemFactory.AssemblyLoading -= onAssemblyLoading; - } - } - - [Fact] - public void Extensions_NLogPackageLoader_should_beCalled() - { - try - { - var writer = new StringWriter(); - InternalLogger.LogWriter = writer; - InternalLogger.LogLevel = LogLevel.Debug; - - var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@" - - - - - -"); - - - var logs = writer.ToString(); - Assert.Contains("Preload successfully invoked for 'LoaderTestInternal.NLogPackageLoader'", logs); - Assert.Contains("Preload successfully invoked for 'LoaderTestPublic.NLogPackageLoader'", logs); - Assert.Contains("Preload successfully invoked for 'LoaderTestPrivateNestedStatic.SomeType+NLogPackageLoader'", logs); - Assert.Contains("Preload successfully invoked for 'LoaderTestPrivateNested.SomeType+NLogPackageLoader'", logs); - - //4 times successful - Assert.Equal(4, Regex.Matches(logs, Regex.Escape("Preload successfully invoked for '")).Count); - } - finally - { - InternalLogger.Reset(); - } - } - [Fact] public void ImplicitConversionOperatorTest() { @@ -658,19 +573,17 @@ public void NormalizeNameTest(string input, string expected) { // Arrange var assembly = LoadManuallyLoadedExtensionDll(); - var configFactory = new ConfigurationItemFactory(assembly); + var configFactory = new ConfigurationItemFactory(); + configFactory.AssemblyLoader.LoadAssembly(configFactory, assembly, string.Empty); // Act - var foundDefinition = configFactory.GetTargetFactory().TryGetDefinition(input, out var outputDefinition); var foundInstance = configFactory.TargetFactory.TryCreateInstance(input, out var outputInstance); - var instance = (foundDefinition || foundInstance || expected != null) ? configFactory.GetTargetFactory().CreateInstance(input) : null; + var instance = (foundInstance || expected != null) ? configFactory.GetTargetFactory().CreateInstance(input) : null; // Assert Assert.Equal(expected != null, foundInstance); - Assert.Equal(expected != null, foundDefinition); Assert.Equal(expected, instance?.GetType().ToString()); Assert.Equal(expected, outputInstance?.GetType().ToString()); - Assert.Equal(expected, outputDefinition?.ToString()); } [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] diff --git a/tests/NLog.UnitTests/NLog.UnitTests.csproj b/tests/NLog.UnitTests/NLog.UnitTests.csproj index e23fbaa6d4..3163e6b826 100644 --- a/tests/NLog.UnitTests/NLog.UnitTests.csproj +++ b/tests/NLog.UnitTests/NLog.UnitTests.csproj @@ -84,7 +84,6 @@ false - diff --git a/tests/PackageLoaderTestAssembly/NLogPackageLoaders.cs b/tests/PackageLoaderTestAssembly/NLogPackageLoaders.cs deleted file mode 100644 index 8e3937346e..0000000000 --- a/tests/PackageLoaderTestAssembly/NLogPackageLoaders.cs +++ /dev/null @@ -1,127 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -using System; -using System.Diagnostics; -using NLog; -using NLog.Config; - -namespace LoaderTestPublic -{ - public sealed class NLogPackageLoader - { - public static void Preload() - { - // Nothing to do - } - } -} - -namespace LoaderTestInternal -{ - /// - /// private - /// - internal sealed class NLogPackageLoader - { - public static void Preload() - { - // Nothing to do - } - } -} - -namespace LoaderTestPrivateNested -{ - internal sealed class SomeType - { - private sealed class NLogPackageLoader - { - public static void Preload(ConfigurationItemFactory fact) - { - if (fact is null) - { - throw new ArgumentNullException(nameof(fact)); - } - } - } - } -} - -namespace LoaderTestPrivateNestedStatic -{ - internal sealed class SomeType - { - private static class NLogPackageLoader - { - public static void Preload() - { - // Nothing to do - } - } - } -} - -namespace LoaderTestWrong1 -{ - public sealed class NLogPackageLoader - { - [DebuggerStepThrough] - public static void Preload() - { - throw new NLogRuntimeException("ow noos"); - } - } -} - -namespace LoaderTestWrong2 -{ - public sealed class NLogPackageLoader - { - public void Preload() - { - //im not static - } - } -} - -namespace LoaderTestWrong3 -{ - public sealed class NLogPackageLoader - { - public static void Preload(int arg1, int arg2) - { - //I have args - } - } -} diff --git a/tests/PackageLoaderTestAssembly/PackageLoaderTestAssembly.csproj b/tests/PackageLoaderTestAssembly/PackageLoaderTestAssembly.csproj deleted file mode 100644 index 6d0f15f63e..0000000000 --- a/tests/PackageLoaderTestAssembly/PackageLoaderTestAssembly.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - net462;netstandard2.0 - - - - - - - From 405544a6c90fcb70118bb2ac8c2dc98902ceed43 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 4 Oct 2024 21:20:53 +0200 Subject: [PATCH 006/224] Remove NETSTANDARD1_3 and NETSTANDARD1_5 (#5624) --- build.ps1 | 4 +- .../DatabaseObjectPropertyInfo.cs | 1 - src/NLog.Database/DatabaseTarget.cs | 46 +---- .../Internal/ReflectionHelpers.cs | 10 - src/NLog.Database/NLog.Database.csproj | 17 +- src/NLog.Database/Properties/AssemblyInfo.cs | 2 +- .../Properties/AssemblyInfo.cs | 3 +- .../Properties/AssemblyInfo.cs | 3 +- .../Properties/AssemblyInfo.cs | 1 - src/NLog/Common/AsyncHelpers.cs | 19 +- src/NLog/Common/ConversionHelpers.cs | 2 +- src/NLog/Common/InternalLogger.cs | 8 - .../ConditionRelationalExpression.cs | 4 - src/NLog/Config/AssemblyExtensionLoader.cs | 51 +---- src/NLog/Config/AssemblyExtensionTypes.cs | 40 ---- src/NLog/Config/AssemblyExtensionTypes.tt | 41 +--- src/NLog/Config/ConfigurationItemFactory.cs | 4 +- src/NLog/Config/InstallationContext.cs | 8 - src/NLog/Config/LoggingConfiguration.cs | 1 - .../Config/LoggingConfigurationFileLoader.cs | 12 +- src/NLog/Config/LoggingConfigurationParser.cs | 5 +- .../LoggingConfigurationReloadedEventArgs.cs | 3 - ...LoggingConfigurationWatchableFileLoader.cs | 4 - src/NLog/Config/MethodFactory.cs | 12 +- src/NLog/Config/PropertyTypeConverter.cs | 7 +- src/NLog/Config/ServiceRepositoryInternal.cs | 4 +- src/NLog/Config/SimpleConfigurator.cs | 2 - src/NLog/Internal/AppDomainWrapper.cs | 4 - src/NLog/Internal/AppEnvironmentWrapper.cs | 5 +- src/NLog/Internal/AssemblyHelpers.cs | 2 - src/NLog/Internal/AsyncHelpersTask.cs | 54 ----- src/NLog/Internal/EnvironmentHelper.cs | 7 - src/NLog/Internal/ExceptionHelper.cs | 2 - src/NLog/Internal/FakeAppDomain.cs | 191 ------------------ .../FileAppenders/BaseMutexFileAppender.cs | 10 - .../FileAppenders/FileAppenderCache.cs | 14 -- .../FileAppenders/IFileAppenderCache.cs | 4 - .../MutexMultiProcessFileAppender.cs | 8 - src/NLog/Internal/IAppEnvironment.cs | 3 +- .../Internal/INetworkInterfaceRetriever.cs | 3 - src/NLog/Internal/ISmtpClient.cs | 4 - src/NLog/Internal/MultiFileWatcher.cs | 4 - src/NLog/Internal/MutexDetector.cs | 6 - src/NLog/Internal/MySmtpClient.cs | 4 - src/NLog/Internal/NetStandardHelpers.cs | 120 ----------- .../Internal/NetworkInterfaceRetriever.cs | 3 - .../NetworkSenders/HttpNetworkSender.cs | 3 +- .../Internal/NetworkSenders/NetworkSender.cs | 4 - .../Internal/NetworkSenders/SslSocketProxy.cs | 4 - .../NetworkSenders/TcpNetworkSender.cs | 9 +- .../NetworkSenders/UdpNetworkSender.cs | 6 +- src/NLog/Internal/ObjectReflectionCache.cs | 12 +- src/NLog/Internal/PropertyHelper.cs | 12 +- src/NLog/Internal/ReflectionHelpers.cs | 113 +---------- src/NLog/Internal/StackTraceUsageUtils.cs | 57 +----- src/NLog/Internal/TargetWithFilterChain.cs | 5 +- src/NLog/Internal/XmlHelper.cs | 6 +- .../AssemblyVersionLayoutRenderer.cs | 4 - .../LayoutRenderers/BaseDirLayoutRenderer.cs | 9 +- .../LayoutRenderers/CallSiteLayoutRenderer.cs | 2 - .../EnvironmentUserLayoutRenderer.cs | 4 - .../ExceptionLayoutRenderer.cs | 64 +----- .../FileContentsLayoutRenderer.cs | 11 - .../LayoutRenderers/HostNameLayoutRenderer.cs | 2 - .../LayoutRenderers/IdentityLayoutRenderer.cs | 4 - .../LocalIpAddressLayoutRenderer.cs | 4 - .../Log4JXmlEventLayoutRenderer.cs | 7 +- .../LayoutRenderers/NLogDirLayoutRenderer.cs | 6 +- .../ProcessDirLayoutRenderer.cs | 4 - .../ProcessIdLayoutRenderer.cs | 4 - .../ProcessInfoLayoutRenderer.cs | 5 - .../ProcessNameLayoutRenderer.cs | 4 - .../ProcessTimeLayoutRenderer.cs | 2 - ...cialFolderApplicationDataLayoutRenderer.cs | 4 - ...lderCommonApplicationDataLayoutRenderer.cs | 4 - .../SpecialFolderLayoutRenderer.cs | 4 - ...olderLocalApplicationDataLayoutRenderer.cs | 4 - .../ThreadNameLayoutRenderer.cs | 4 - .../LayoutRenderers/TimeLayoutRenderer.cs | 2 - .../TraceActivityIdLayoutRenderer.cs | 4 - .../LowercaseLayoutRendererWrapper.cs | 17 -- .../UppercaseLayoutRendererWrapper.cs | 17 -- src/NLog/Layouts/Layout.cs | 4 - src/NLog/Layouts/LayoutParser.cs | 2 +- src/NLog/Layouts/SimpleLayout.cs | 2 +- src/NLog/Layouts/Typed/Layout.cs | 4 +- src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs | 6 +- src/NLog/LogFactory-T.cs | 4 - src/NLog/LogFactory.cs | 47 +---- src/NLog/LogManager.cs | 9 +- src/NLog/LoggerImpl.cs | 13 -- src/NLog/NLog.csproj | 43 +--- src/NLog/NLogTraceListener.cs | 4 - src/NLog/Properties/AssemblyInfo.cs | 2 +- src/NLog/SetupBuilderExtensions.cs | 3 +- .../SetupInternalLoggerBuilderExtensions.cs | 2 - src/NLog/SetupLoadConfigurationExtensions.cs | 2 - .../SetupSerializationBuilderExtensions.cs | 1 - src/NLog/Targets/AsyncTaskTarget.cs | 8 +- src/NLog/Targets/ColoredConsoleAnsiPrinter.cs | 4 - .../Targets/ColoredConsoleSystemPrinter.cs | 4 - src/NLog/Targets/ColoredConsoleTarget.cs | 4 - .../Targets/ConsoleRowHighlightingRule.cs | 4 - src/NLog/Targets/ConsoleTarget.cs | 4 - src/NLog/Targets/ConsoleTargetHelper.cs | 14 +- .../Targets/ConsoleWordHighlightingRule.cs | 4 - src/NLog/Targets/DebuggerTarget.cs | 4 - src/NLog/Targets/FileTarget.cs | 17 +- src/NLog/Targets/IColoredConsolePrinter.cs | 5 - src/NLog/Targets/MailTarget.cs | 4 - src/NLog/Targets/SmtpAuthenticationMode.cs | 4 - src/NLog/Targets/TargetWithContext.cs | 2 +- src/NLog/Targets/TraceTarget.cs | 4 - src/NLog/Targets/WebServiceTarget.cs | 8 - .../Targets/Wrappers/AsyncTargetWrapper.cs | 6 +- .../Wrappers/BufferingTargetWrapper.cs | 1 - src/NLog/Time/CachedTimeSource.cs | 2 - tests/NLog.UnitTests/Config/ExtensionTests.cs | 1 - tests/NLog.UnitTests/Config/XmlConfigTests.cs | 4 - .../Internal/ExceptionHelperTests.cs | 4 - .../Internal/SimpleStringReaderTests.cs | 2 +- .../CallSiteLineNumberTests.cs | 1 - .../LayoutRenderers/CallSiteTests.cs | 5 +- .../LayoutRenderers/SpecialFolderTests.cs | 2 - tests/NLog.UnitTests/NLogTestBase.cs | 10 - .../NLog.UnitTests/NLogTraceListenerTests.cs | 2 - .../Targets/NetworkTargetTests.cs | 20 +- .../Targets/TargetWithContextTest.cs | 1 - .../Wrappers/AsyncTargetWrapperTests.cs | 2 +- 129 files changed, 116 insertions(+), 1378 deletions(-) delete mode 100644 src/NLog/Internal/AsyncHelpersTask.cs delete mode 100644 src/NLog/Internal/FakeAppDomain.cs delete mode 100644 src/NLog/Internal/NetStandardHelpers.cs diff --git a/build.ps1 b/build.ps1 index d2ce282bdd..0edd7faf51 100644 --- a/build.ps1 +++ b/build.ps1 @@ -23,7 +23,7 @@ if (-Not (test-path $targetNugetExe)) Invoke-WebRequest $sourceNugetExe -OutFile $targetNugetExe } -msbuild /t:Restore,Pack .\src\NLog\ /p:targetFrameworks='"net46;net45;net35;netstandard1.3;netstandard1.5;netstandard2.0"' /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount +msbuild /t:Restore,Pack .\src\NLog\ /p:targetFrameworks='"net46;net45;net35;netstandard2.0"' /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount if (-Not $LastExitCode -eq 0) { exit $LastExitCode } @@ -35,7 +35,7 @@ function create-package($packageName, $targetFrameworks) { exit $LastExitCode } } -create-package 'NLog.Database' '"net35;net45;net46;netstandard1.3;netstandard1.5;netstandard2.0"' +create-package 'NLog.Database' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.OutputDebugString' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.WindowsRegistry' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.WindowsEventLog' '"netstandard2.0"' diff --git a/src/NLog.Database/DatabaseObjectPropertyInfo.cs b/src/NLog.Database/DatabaseObjectPropertyInfo.cs index be622ef1f0..0af15a4723 100644 --- a/src/NLog.Database/DatabaseObjectPropertyInfo.cs +++ b/src/NLog.Database/DatabaseObjectPropertyInfo.cs @@ -35,7 +35,6 @@ namespace NLog.Targets { using System; using System.Globalization; - using System.Reflection; using NLog.Config; using NLog.Internal; using NLog.Layouts; diff --git a/src/NLog.Database/DatabaseTarget.cs b/src/NLog.Database/DatabaseTarget.cs index cc1f792cb8..38d0bba694 100644 --- a/src/NLog.Database/DatabaseTarget.cs +++ b/src/NLog.Database/DatabaseTarget.cs @@ -41,13 +41,10 @@ namespace NLog.Targets using System.Data.Common; using System.Reflection; using System.Text; -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 using System.Transactions; -#endif using NLog.Common; using NLog.Config; - using NLog.Internal; using NLog.Layouts; #if !NETSTANDARD @@ -542,7 +539,7 @@ private void SetConnectionType() #else case "SYSTEM.DATA.SQLCLIENT": { - var assembly = typeof(IDbConnection).GetAssembly(); + var assembly = typeof(IDbConnection).Assembly; ConnectionType = assembly.GetType("System.Data.SqlClient.SqlConnection", true, true); break; } @@ -556,7 +553,7 @@ private void SetConnectionType() #if !NETSTANDARD case "OLEDB": { - var assembly = typeof(IDbConnection).GetAssembly(); + var assembly = typeof(IDbConnection).Assembly; ConnectionType = assembly.GetType("System.Data.OleDb.OleDbConnection", true, true); break; } @@ -567,7 +564,7 @@ private void SetConnectionType() #if NETSTANDARD var assembly = Assembly.Load(new AssemblyName("System.Data.Odbc")); #else - var assembly = typeof(IDbConnection).GetAssembly(); + var assembly = typeof(IDbConnection).Assembly; #endif ConnectionType = assembly.GetType("System.Data.Odbc.OdbcConnection", true, true); break; @@ -1117,42 +1114,5 @@ protected internal virtual object GetDatabaseParameterValue(LogEventInfo logEven else return value; } - -#if NETSTANDARD1_3 || NETSTANDARD1_5 - /// - /// Fake transaction - /// - /// Transactions aren't in .NET Core: https://github.com/dotnet/corefx/issues/2949 - /// - private sealed class TransactionScope : IDisposable - { - private readonly TransactionScopeOption _suppress; - - public TransactionScope(TransactionScopeOption suppress) - { - _suppress = suppress; - } - - public void Complete() - { - // Fake scope for compatibility, so nothing to do - } - - public void Dispose() - { - // Fake scope for compatibility, so nothing to do - } - } - - /// - /// Fake option - /// - private enum TransactionScopeOption - { - Required, - RequiresNew, - Suppress, - } -#endif } } diff --git a/src/NLog.Database/Internal/ReflectionHelpers.cs b/src/NLog.Database/Internal/ReflectionHelpers.cs index 7df0aecc5d..2eac56106b 100644 --- a/src/NLog.Database/Internal/ReflectionHelpers.cs +++ b/src/NLog.Database/Internal/ReflectionHelpers.cs @@ -41,16 +41,6 @@ namespace NLog.Internal /// internal static class ReflectionHelpers { - public static Assembly GetAssembly(this Type type) - { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - return type.Assembly; -#else - var typeInfo = type.GetTypeInfo(); - return typeInfo.Assembly; -#endif - } - public static Action CreatePropertySetter(this PropertyInfo propertyInfo) { var target = System.Linq.Expressions.Expression.Parameter(typeof(object), "target"); diff --git a/src/NLog.Database/NLog.Database.csproj b/src/NLog.Database/NLog.Database.csproj index 7cfacb26fe..33dd2a1f6e 100644 --- a/src/NLog.Database/NLog.Database.csproj +++ b/src/NLog.Database/NLog.Database.csproj @@ -3,7 +3,7 @@ PackageReference - net35;net46;netstandard1.3;netstandard1.5;netstandard2.0 + net35;net46;netstandard2.0 NLog Database Target NLog @@ -50,16 +50,6 @@ true - - NLog.Database for NetStandard 1.3 - 1.6.0 - - - - NLog.Database for NetStandard 1.5 - 1.6.0 - - NLog.Database for NetStandard 2.0 @@ -80,11 +70,6 @@ - - - - - diff --git a/src/NLog.Database/Properties/AssemblyInfo.cs b/src/NLog.Database/Properties/AssemblyInfo.cs index b41de73006..2d6bf6c293 100644 --- a/src/NLog.Database/Properties/AssemblyInfo.cs +++ b/src/NLog.Database/Properties/AssemblyInfo.cs @@ -42,6 +42,6 @@ [assembly: ComVisible(false)] [assembly: InternalsVisibleTo("NLog.Database.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100ef8eab4fbdeb511eeb475e1659fe53f00ec1c1340700f1aa347bf3438455d71993b28b1efbed44c8d97a989e0cb6f01bcb5e78f0b055d311546f63de0a969e04cf04450f43834db9f909e566545a67e42822036860075a1576e90e1c43d43e023a24c22a427f85592ae56cac26f13b7ec2625cbc01f9490d60f16cfbb1bc34d9")] [assembly: AllowPartiallyTrustedCallers] -#if !NET35 && !NETSTANDARD1_3 && !NETSTANDARD1_5 +#if !NET35 [assembly: SecurityRules(SecurityRuleSet.Level1)] #endif diff --git a/src/NLog.OutputDebugString/Properties/AssemblyInfo.cs b/src/NLog.OutputDebugString/Properties/AssemblyInfo.cs index 0e114863aa..3c9d9e1f4a 100644 --- a/src/NLog.OutputDebugString/Properties/AssemblyInfo.cs +++ b/src/NLog.OutputDebugString/Properties/AssemblyInfo.cs @@ -33,7 +33,6 @@ using System; using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; @@ -41,6 +40,6 @@ [assembly: CLSCompliant(true)] [assembly: ComVisible(false)] [assembly: AllowPartiallyTrustedCallers] -#if !NET35 && !NETSTANDARD1_3 && !NETSTANDARD1_5 +#if !NET35 [assembly: SecurityRules(SecurityRuleSet.Level1)] #endif diff --git a/src/NLog.WindowsEventLog/Properties/AssemblyInfo.cs b/src/NLog.WindowsEventLog/Properties/AssemblyInfo.cs index 0e114863aa..3c9d9e1f4a 100644 --- a/src/NLog.WindowsEventLog/Properties/AssemblyInfo.cs +++ b/src/NLog.WindowsEventLog/Properties/AssemblyInfo.cs @@ -33,7 +33,6 @@ using System; using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; @@ -41,6 +40,6 @@ [assembly: CLSCompliant(true)] [assembly: ComVisible(false)] [assembly: AllowPartiallyTrustedCallers] -#if !NET35 && !NETSTANDARD1_3 && !NETSTANDARD1_5 +#if !NET35 [assembly: SecurityRules(SecurityRuleSet.Level1)] #endif diff --git a/src/NLog.WindowsRegistry/Properties/AssemblyInfo.cs b/src/NLog.WindowsRegistry/Properties/AssemblyInfo.cs index 5abf4e6c0f..3c9d9e1f4a 100644 --- a/src/NLog.WindowsRegistry/Properties/AssemblyInfo.cs +++ b/src/NLog.WindowsRegistry/Properties/AssemblyInfo.cs @@ -33,7 +33,6 @@ using System; using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; diff --git a/src/NLog/Common/AsyncHelpers.cs b/src/NLog/Common/AsyncHelpers.cs index f92ab20a40..9143ce432d 100644 --- a/src/NLog/Common/AsyncHelpers.cs +++ b/src/NLog/Common/AsyncHelpers.cs @@ -47,30 +47,17 @@ public static class AsyncHelpers { internal static int GetManagedThreadId() { -#if !NETSTANDARD1_3 return Thread.CurrentThread.ManagedThreadId; -#else - return System.Environment.CurrentManagedThreadId; -#endif } - internal static void StartAsyncTask(AsyncHelpersTask asyncTask, object state) + internal static void StartAsyncTask(WaitCallback asyncDelegate, object state) { - var asyncDelegate = asyncTask.AsyncDelegate; -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 ThreadPool.QueueUserWorkItem(asyncDelegate, state); -#else - System.Threading.Tasks.Task.Factory.StartNew(asyncDelegate, state, CancellationToken.None, System.Threading.Tasks.TaskCreationOptions.None, System.Threading.Tasks.TaskScheduler.Default); -#endif } internal static void WaitForDelay(TimeSpan delay) { -#if !NETSTANDARD1_3 Thread.Sleep(delay); -#else - System.Threading.Tasks.Task.Delay(delay).ConfigureAwait(false).GetAwaiter().GetResult(); -#endif } /// @@ -232,7 +219,7 @@ public static void ForEachItemInParallel(IEnumerable values, AsyncContinua foreach (T item in items) { T itemCopy = item; - StartAsyncTask(new AsyncHelpersTask(s => + StartAsyncTask((s) => { var preventMultipleCalls = PreventMultipleCalls(continuation); try @@ -250,7 +237,7 @@ public static void ForEachItemInParallel(IEnumerable values, AsyncContinua InternalLogger.Error(ex, "ForEachItemInParallel - Unhandled Exception"); preventMultipleCalls.Invoke(ex); } - }), null); + }, null); } } diff --git a/src/NLog/Common/ConversionHelpers.cs b/src/NLog/Common/ConversionHelpers.cs index 1bd27c2fb5..9e3d0397ac 100644 --- a/src/NLog/Common/ConversionHelpers.cs +++ b/src/NLog/Common/ConversionHelpers.cs @@ -116,7 +116,7 @@ internal static bool TryParseEnum(string inputValue, bool ignoreCase, out private static bool TryParseEnum_net3(string value, bool ignoreCase, out TEnum result) where TEnum : struct { var enumType = typeof(TEnum); - if (!enumType.IsEnum()) + if (!enumType.IsEnum) throw new ArgumentException($"Type '{enumType.FullName}' is not an enum"); if (StringHelpers.IsNullOrWhiteSpace(value)) diff --git a/src/NLog/Common/InternalLogger.cs b/src/NLog/Common/InternalLogger.cs index 5a7adef2c4..a49c386f94 100644 --- a/src/NLog/Common/InternalLogger.cs +++ b/src/NLog/Common/InternalLogger.cs @@ -448,18 +448,14 @@ private static string ExpandFilePathVariables(string internalLogFile) internalLogFile = internalLogFile.Replace(baseDirToken, LogManager.LogFactory.CurrentAppEnvironment.AppDomainBaseDirectory + System.IO.Path.DirectorySeparatorChar.ToString()); if (ContainsSubStringIgnoreCase(internalLogFile, "${tempdir}", out string tempDirToken)) internalLogFile = internalLogFile.Replace(tempDirToken, LogManager.LogFactory.CurrentAppEnvironment.UserTempFilePath + System.IO.Path.DirectorySeparatorChar.ToString()); -#if !NETSTANDARD1_3 if (ContainsSubStringIgnoreCase(internalLogFile, "${processdir}", out string processDirToken)) internalLogFile = internalLogFile.Replace(processDirToken, System.IO.Path.GetDirectoryName(LogManager.LogFactory.CurrentAppEnvironment.CurrentProcessFilePath) + System.IO.Path.DirectorySeparatorChar.ToString()); -#endif -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 if (ContainsSubStringIgnoreCase(internalLogFile, "${commonApplicationDataDir}", out string commonAppDataDirToken)) internalLogFile = internalLogFile.Replace(commonAppDataDirToken, NLog.LayoutRenderers.SpecialFolderLayoutRenderer.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + System.IO.Path.DirectorySeparatorChar.ToString()); if (ContainsSubStringIgnoreCase(internalLogFile, "${userApplicationDataDir}", out string appDataDirToken)) internalLogFile = internalLogFile.Replace(appDataDirToken, NLog.LayoutRenderers.SpecialFolderLayoutRenderer.GetFolderPath(Environment.SpecialFolder.ApplicationData) + System.IO.Path.DirectorySeparatorChar.ToString()); if (ContainsSubStringIgnoreCase(internalLogFile, "${userLocalApplicationDataDir}", out string localapplicationdatadir)) internalLogFile = internalLogFile.Replace(localapplicationdatadir, NLog.LayoutRenderers.SpecialFolderLayoutRenderer.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + System.IO.Path.DirectorySeparatorChar.ToString()); -#endif if (internalLogFile.IndexOf('%') >= 0) internalLogFile = Environment.ExpandEnvironmentVariables(internalLogFile); return internalLogFile; @@ -479,18 +475,14 @@ private static bool ContainsSubStringIgnoreCase(string haystack, string needle, private static void LogToConsoleSubscription(object sender, InternalLogEventArgs eventArgs) { -#if !NETSTANDARD1_3 var logLine = CreateLogLine(eventArgs.Exception, eventArgs.Level, eventArgs.Message); NLog.Targets.ConsoleTargetHelper.WriteLineThreadSafe(Console.Out, logLine); -#endif } private static void LogToConsoleErrorSubscription(object sender, InternalLogEventArgs eventArgs) { -#if !NETSTANDARD1_3 var logLine = CreateLogLine(eventArgs.Exception, eventArgs.Level, eventArgs.Message); NLog.Targets.ConsoleTargetHelper.WriteLineThreadSafe(Console.Error, logLine); -#endif } private static void LogToFileSubscription(object sender, InternalLogEventArgs eventArgs) diff --git a/src/NLog/Conditions/ConditionRelationalExpression.cs b/src/NLog/Conditions/ConditionRelationalExpression.cs index aa2fed282d..73f0bca60f 100644 --- a/src/NLog/Conditions/ConditionRelationalExpression.cs +++ b/src/NLog/Conditions/ConditionRelationalExpression.cs @@ -99,11 +99,7 @@ protected override object EvaluateNode(LogEventInfo context) /// Result of the given relational operator. private static bool Compare(object leftValue, object rightValue, ConditionRelationalOperator relationalOperator) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - System.Collections.IComparer comparer = StringComparer.InvariantCulture; -#else System.Collections.IComparer comparer = StringComparer.Ordinal; -#endif PromoteTypes(ref leftValue, ref rightValue); switch (relationalOperator) { diff --git a/src/NLog/Config/AssemblyExtensionLoader.cs b/src/NLog/Config/AssemblyExtensionLoader.cs index ee557bed09..f8600c8451 100644 --- a/src/NLog/Config/AssemblyExtensionLoader.cs +++ b/src/NLog/Config/AssemblyExtensionLoader.cs @@ -71,9 +71,9 @@ public void LoadAssembly(ConfigurationItemFactory factory, Assembly assembly, st var typesToScan = AssemblyHelpers.SafeGetTypes(assembly); if (typesToScan?.Length > 0) { - if (ReferenceEquals(assembly, typeof(LogFactory).GetAssembly())) + if (ReferenceEquals(assembly, typeof(LogFactory).Assembly)) { - typesToScan = typesToScan.Where(t => t.IsPublic() && t.IsClass()).ToArray(); + typesToScan = typesToScan.Where(t => t.IsPublic && t.IsClass).ToArray(); } foreach (Type type in typesToScan) @@ -139,7 +139,7 @@ private static Dictionary ResolveLoadedAssemblyTypes(Configurati var loadedAssemblies = new Dictionary(); foreach (var itemType in factory.ItemTypes) { - var assembly = itemType.GetAssembly(); + var assembly = itemType.Assembly; if (assembly is null) continue; @@ -243,10 +243,9 @@ public void LoadAssemblyFromPath(ConfigurationItemFactory factory, string assemb public void ScanForAutoLoadExtensions(ConfigurationItemFactory factory) { -#if !NETSTANDARD1_3 try { - var nlogAssembly = typeof(LogFactory).GetAssembly(); + var nlogAssembly = typeof(LogFactory).Assembly; var assemblyLocation = string.Empty; var extensionDlls = ArrayHelper.Empty(); var fileLocations = GetAutoLoadingFileLocations(); @@ -287,12 +286,8 @@ public void ScanForAutoLoadExtensions(ConfigurationItemFactory factory) } } InternalLogger.Debug("Auto loading done"); -#else - // Nothing to do for Sonar Cube -#endif } -#if !NETSTANDARD1_3 private HashSet LoadNLogExtensionAssemblies(ConfigurationItemFactory factory, Assembly nlogAssembly, string[] extensionDlls) { HashSet alreadyRegistered = new HashSet(StringComparer.OrdinalIgnoreCase) @@ -333,11 +328,7 @@ private void RegisterAppDomainAssemblies(ConfigurationItemFactory factory, Assem { alreadyRegistered.Add(nlogAssembly.FullName); -#if !NETSTANDARD1_5 var allAssemblies = LogFactory.DefaultAppEnvironment.GetAppDomainRuntimeAssemblies(); -#else - var allAssemblies = new[] { nlogAssembly }; -#endif foreach (var assembly in allAssemblies) { if (assembly.FullName.StartsWith("NLog.", StringComparison.OrdinalIgnoreCase) && !alreadyRegistered.Contains(assembly.FullName)) @@ -380,7 +371,7 @@ private static bool IncludeAsHiddenAssembly(string assemblyFullName) internal static IEnumerable> GetAutoLoadingFileLocations() { - var nlogAssembly = typeof(LogFactory).GetAssembly(); + var nlogAssembly = typeof(LogFactory).Assembly; var nlogAssemblyLocation = PathHelpers.TrimDirectorySeparators(AssemblyHelpers.GetAssemblyFileLocation(nlogAssembly)); InternalLogger.Debug("Auto loading based on NLog-Assembly found location: {0}", nlogAssemblyLocation); if (!string.IsNullOrEmpty(nlogAssemblyLocation)) @@ -442,7 +433,6 @@ private static string[] GetNLogExtensionFiles(string assemblyLocation) return ArrayHelper.Empty(); } } -#endif /// /// Load from url @@ -454,34 +444,14 @@ private static string[] GetNLogExtensionFiles(string assemblyLocation) [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] private static Assembly LoadAssemblyFromPath(string assemblyFileName, string baseDirectory = null) { -#if NETSTANDARD1_3 - return null; -#else string fullFileName = assemblyFileName; if (!string.IsNullOrEmpty(baseDirectory)) { fullFileName = System.IO.Path.Combine(baseDirectory, assemblyFileName); } - InternalLogger.Info("Loading assembly file: {0}", fullFileName); -#if NETSTANDARD1_5 - try - { - var assemblyName = System.Runtime.Loader.AssemblyLoadContext.GetAssemblyName(fullFileName); - return Assembly.Load(assemblyName); - } - catch (Exception ex) - { - // this doesn't usually work - InternalLogger.Warn(ex, "Fallback to AssemblyLoadContext.Default.LoadFromAssemblyPath for file: {0}", fullFileName); - return System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(fullFileName); - } -#else - Assembly asm = Assembly.LoadFrom(fullFileName); - return asm; -#endif - -#endif + InternalLogger.Info("Loading assembly file: {0}", fullFileName); + return Assembly.LoadFrom(fullFileName); } /// @@ -489,7 +459,6 @@ private static Assembly LoadAssemblyFromPath(string assemblyFileName, string bas /// private static Assembly LoadAssemblyFromName(string assemblyName) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 try { Assembly assembly = Assembly.Load(assemblyName); @@ -509,13 +478,8 @@ private static Assembly LoadAssemblyFromName(string assemblyName) InternalLogger.Trace("Haven't found' '{0}' in current domain", assemblyName); throw; } -#else - var name = new AssemblyName(assemblyName); - return Assembly.Load(name); -#endif } -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 private static bool IsAssemblyMatch(AssemblyName expected, AssemblyName actual) { if (expected is null || actual is null) @@ -531,7 +495,6 @@ private static bool IsAssemblyMatch(AssemblyName expected, AssemblyName actual) var correctToken = expectedKeyToken is null || expectedKeyToken.SequenceEqual(actual.GetPublicKeyToken()); return correctToken; } -#endif } internal interface IAssemblyExtensionLoader diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index 062c206f04..89e4390183 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -77,9 +77,7 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.LayoutRendererFactory.RegisterType("db-null"); factory.LayoutRendererFactory.RegisterType("dir-separator"); factory.LayoutRendererFactory.RegisterType("environment"); -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 factory.LayoutRendererFactory.RegisterType("environment-user"); -#endif factory.LayoutRendererFactory.RegisterType("event-properties"); factory.LayoutRendererFactory.RegisterType("event-property"); factory.LayoutRendererFactory.RegisterType("event-context"); @@ -92,17 +90,13 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.LayoutRendererFactory.RegisterType("gdc"); factory.LayoutRendererFactory.RegisterType("guid"); factory.LayoutRendererFactory.RegisterType("hostname"); -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 factory.LayoutRendererFactory.RegisterType("identity"); -#endif factory.LayoutRendererFactory.RegisterType("install-context"); factory.LayoutRendererFactory.RegisterType("level"); factory.LayoutRendererFactory.RegisterType("loglevel"); factory.LayoutRendererFactory.RegisterType("literal"); factory.RegisterTypeProperties(() => null); -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 factory.LayoutRendererFactory.RegisterType("local-ip"); -#endif factory.LayoutRendererFactory.RegisterType("log4jxmlevent"); factory.LayoutRendererFactory.RegisterType("loggername"); factory.LayoutRendererFactory.RegisterType("logger"); @@ -110,21 +104,11 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.LayoutRendererFactory.RegisterType("machinename"); factory.LayoutRendererFactory.RegisterType("message"); factory.LayoutRendererFactory.RegisterType("newline"); -#if !NETSTANDARD1_3 factory.LayoutRendererFactory.RegisterType("nlogdir"); -#endif -#if !NETSTANDARD1_3 factory.LayoutRendererFactory.RegisterType("processdir"); -#endif -#if !NETSTANDARD1_3 factory.LayoutRendererFactory.RegisterType("processid"); -#endif -#if !NETSTANDARD1_3 factory.LayoutRendererFactory.RegisterType("processinfo"); -#endif -#if !NETSTANDARD1_3 factory.LayoutRendererFactory.RegisterType("processname"); -#endif factory.LayoutRendererFactory.RegisterType("processtime"); factory.LayoutRendererFactory.RegisterType("scopeindent"); factory.LayoutRendererFactory.RegisterType("scopenested"); @@ -137,27 +121,17 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.LayoutRendererFactory.RegisterType("ndlctiming"); factory.LayoutRendererFactory.RegisterType("sequenceid"); factory.LayoutRendererFactory.RegisterType("shortdate"); -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 factory.LayoutRendererFactory.RegisterType("userApplicationDataDir"); -#endif -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 factory.LayoutRendererFactory.RegisterType("commonApplicationDataDir"); -#endif -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 factory.LayoutRendererFactory.RegisterType("specialfolder"); -#endif -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 factory.LayoutRendererFactory.RegisterType("userLocalApplicationDataDir"); -#endif factory.LayoutRendererFactory.RegisterType("stacktrace"); factory.LayoutRendererFactory.RegisterType("tempdir"); factory.LayoutRendererFactory.RegisterType("threadid"); factory.LayoutRendererFactory.RegisterType("threadname"); factory.LayoutRendererFactory.RegisterType("ticks"); factory.LayoutRendererFactory.RegisterType("time"); -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 factory.LayoutRendererFactory.RegisterType("activityid"); -#endif factory.LayoutRendererFactory.RegisterType("var"); factory.LayoutRendererFactory.RegisterType("cached"); factory.AmbientRendererFactory.RegisterType("Cached"); @@ -216,33 +190,21 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.RegisterType(); factory.LayoutFactory.RegisterType("XmlLayout"); factory.TargetFactory.RegisterType("Chainsaw"); -#if !NETSTANDARD1_3 factory.TargetFactory.RegisterType("ColoredConsole"); -#endif -#if !NETSTANDARD1_3 factory.RegisterType(); -#endif -#if !NETSTANDARD1_3 factory.TargetFactory.RegisterType("Console"); -#endif -#if !NETSTANDARD1_3 factory.RegisterType(); -#endif -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 factory.TargetFactory.RegisterType("Debugger"); -#endif factory.TargetFactory.RegisterType("DebugSystem"); factory.TargetFactory.RegisterType("Debug"); #if NETFRAMEWORK factory.TargetFactory.RegisterType("EventLog"); #endif factory.TargetFactory.RegisterType("File"); -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 factory.TargetFactory.RegisterType("Mail"); factory.TargetFactory.RegisterType("Email"); factory.TargetFactory.RegisterType("Smtp"); factory.TargetFactory.RegisterType("SmtpClient"); -#endif factory.TargetFactory.RegisterType("Memory"); factory.RegisterType(); factory.TargetFactory.RegisterType("MethodCall"); @@ -251,10 +213,8 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.TargetFactory.RegisterType("NLogViewer"); factory.TargetFactory.RegisterType("Null"); factory.RegisterType(); -#if !NETSTANDARD1_3 factory.TargetFactory.RegisterType("Trace"); factory.TargetFactory.RegisterType("TraceSystem"); -#endif factory.TargetFactory.RegisterType("WebService"); factory.TargetFactory.RegisterType("AsyncWrapper"); factory.TargetFactory.RegisterType("AutoFlushWrapper"); diff --git a/src/NLog/Config/AssemblyExtensionTypes.tt b/src/NLog/Config/AssemblyExtensionTypes.tt index 661502beca..462dff2c1a 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.tt +++ b/src/NLog/Config/AssemblyExtensionTypes.tt @@ -61,33 +61,6 @@ namespace NLog.Config "NLog.LayoutRenderers.AppSettingLayoutRenderer", "NLog.Targets.EventLogTarget", }; - var netStandard2 = new string[] - { - "NLog.Targets.MailTarget", - "NLog.Targets.DebuggerTarget", - "NLog.LayoutRenderers.EnvironmentUserLayoutRenderer", - "NLog.LayoutRenderers.IdentityLayoutRenderer", - "NLog.LayoutRenderers.LocalIpAddressLayoutRenderer", - "NLog.LayoutRenderers.SpecialFolderApplicationDataLayoutRenderer", - "NLog.LayoutRenderers.SpecialFolderCommonApplicationDataLayoutRenderer", - "NLog.LayoutRenderers.SpecialFolderLayoutRenderer", - "NLog.LayoutRenderers.SpecialFolderLocalApplicationDataLayoutRenderer", - "NLog.LayoutRenderers.TraceActivityIdLayoutRenderer", - "NLog.LayoutRenderers.TraceActivityIdLayoutRenderer", - }; - var netStandard15 = new string[] - { - "NLog.LayoutRenderers.NLogDirLayoutRenderer", - "NLog.LayoutRenderers.ProcessDirLayoutRenderer", - "NLog.LayoutRenderers.ProcessIdLayoutRenderer", - "NLog.LayoutRenderers.ProcessInfoLayoutRenderer", - "NLog.LayoutRenderers.ProcessNameLayoutRenderer", - "NLog.Targets.ColoredConsoleTarget", - "NLog.Targets.ConsoleRowHighlightingRule", - "NLog.Targets.ConsoleWordHighlightingRule", - "NLog.Targets.ConsoleTarget", - "NLog.Targets.TraceTarget", - }; foreach(var type in types) { @@ -98,18 +71,6 @@ namespace NLog.Config { #> #if NETFRAMEWORK -<# - } - else if (netStandard2.Contains(type.ToString())) - { -#> -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 -<# - } - else if (netStandard15.Contains(type.ToString())) - { -#> -#if !NETSTANDARD1_3 <# } @@ -198,7 +159,7 @@ namespace NLog.Config } } - if (netFramework.Contains(type.ToString()) || netStandard2.Contains(type.ToString()) || netStandard15.Contains(type.ToString())) + if (netFramework.Contains(type.ToString())) { #> #endif diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index d75c2f0a2f..3af904490c 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -275,10 +275,10 @@ internal Dictionary TryGetTypeProperties(Type itemType) } } - if (itemType.IsAbstract()) + if (itemType.IsAbstract) return new Dictionary(); - if (itemType.IsGenericType() && itemType.GetGenericTypeDefinition() == typeof(Layout<>)) + if (itemType.IsGenericType && itemType.GetGenericTypeDefinition() == typeof(Layout<>)) return new Dictionary(); #pragma warning disable CS0618 // Type or member is obsolete diff --git a/src/NLog/Config/InstallationContext.cs b/src/NLog/Config/InstallationContext.cs index 0078d09be2..d4777ae3a6 100644 --- a/src/NLog/Config/InstallationContext.cs +++ b/src/NLog/Config/InstallationContext.cs @@ -38,15 +38,12 @@ namespace NLog.Config using System.ComponentModel; using System.Globalization; using System.IO; - // ReSharper disable once RedundantUsingDirective - using NLog.Internal; /// /// Provides context for install/uninstall operations. /// public sealed class InstallationContext : IDisposable { -#if !NETSTANDARD1_3 /// /// Mapping between log levels and console output colors. /// @@ -59,7 +56,6 @@ public sealed class InstallationContext : IDisposable { LogLevel.Error, ConsoleColor.Red }, { LogLevel.Fatal, ConsoleColor.DarkRed }, }; -#endif /// /// Initializes a new instance of the class. @@ -192,7 +188,6 @@ private void Log(LogLevel logLevel, string message, object[] arguments) message = string.Format(CultureInfo.InvariantCulture, message, arguments); } -#if !NETSTANDARD1_3 var oldColor = Console.ForegroundColor; Console.ForegroundColor = LogLevel2ConsoleColor[logLevel]; @@ -204,9 +199,6 @@ private void Log(LogLevel logLevel, string message, object[] arguments) { Console.ForegroundColor = oldColor; } -#else - this.LogOutput.WriteLine(message); -#endif } } } diff --git a/src/NLog/Config/LoggingConfiguration.cs b/src/NLog/Config/LoggingConfiguration.cs index 1a82fa78be..2689e4fe90 100644 --- a/src/NLog/Config/LoggingConfiguration.cs +++ b/src/NLog/Config/LoggingConfiguration.cs @@ -38,7 +38,6 @@ namespace NLog.Config using System.Collections.ObjectModel; using System.Globalization; using System.Linq; - using System.Reflection; using System.Threading; using JetBrains.Annotations; diff --git a/src/NLog/Config/LoggingConfigurationFileLoader.cs b/src/NLog/Config/LoggingConfigurationFileLoader.cs index 7dcb647d7a..6bc181fd2a 100644 --- a/src/NLog/Config/LoggingConfigurationFileLoader.cs +++ b/src/NLog/Config/LoggingConfigurationFileLoader.cs @@ -226,11 +226,8 @@ private static bool ScanForBooleanParameter(string fileContent, string parameter public IEnumerable GetDefaultCandidateConfigFilePaths(string filename = null) { string baseDirectory = PathHelpers.TrimDirectorySeparators(_appEnvironment.AppDomainBaseDirectory); -#if !NETSTANDARD1_3 string entryAssemblyLocation = PathHelpers.TrimDirectorySeparators(_appEnvironment.EntryAssemblyLocation); -#else - string entryAssemblyLocation = string.Empty; -#endif + if (filename is null) { // Scan for process specific nlog-files @@ -272,8 +269,7 @@ public IEnumerable GetDefaultCandidateConfigFilePaths(string filename = private static string LookupNLogAssemblyLocation() { -#if !NETSTANDARD1_3 - var nlogAssembly = typeof(LogFactory).GetAssembly(); + var nlogAssembly = typeof(LogFactory).Assembly; // Get path to NLog.dll.nlog only if the assembly is not in the GAC var nlogAssemblyLocation = nlogAssembly.Location; if (!string.IsNullOrEmpty(nlogAssemblyLocation)) @@ -286,7 +282,7 @@ private static string LookupNLogAssemblyLocation() #endif return nlogAssemblyLocation; } -#endif + return null; } @@ -308,7 +304,7 @@ public IEnumerable GetAppSpecificNLogLocations(string baseDirectory, str yield return Path.ChangeExtension(configurationFile.Replace(vshostSubStr, "."), ".nlog"); } } -#if NETSTANDARD && !NETSTANDARD1_3 +#if NETSTANDARD else { if (string.IsNullOrEmpty(entryAssemblyLocation)) diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index 3c6e269007..f9ac92d31a 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -331,13 +331,12 @@ private void ParseExtensionsElement(ValidatedConfigurationElement extensionsElem RegisterExtension(type, prefix); } -#if !NETSTANDARD1_3 if (!StringHelpers.IsNullOrWhiteSpace(assemblyFile)) { ParseExtensionWithAssemblyFile(baseDirectory, assemblyFile, prefix); continue; } -#endif + if (!StringHelpers.IsNullOrWhiteSpace(assemblyName)) { ParseExtensionWithAssemblyName(assemblyName?.Trim(), prefix); @@ -363,7 +362,6 @@ private void RegisterExtension(string typeName, string itemNamePrefix) } } -#if !NETSTANDARD1_3 private void ParseExtensionWithAssemblyFile(string baseDirectory, string assemblyFile, string prefix) { try @@ -381,7 +379,6 @@ private void ParseExtensionWithAssemblyFile(string baseDirectory, string assembl throw configException; } } -#endif private bool RegisterExtensionFromAssemblyName(string assemblyName, string originalTypeName) { diff --git a/src/NLog/Config/LoggingConfigurationReloadedEventArgs.cs b/src/NLog/Config/LoggingConfigurationReloadedEventArgs.cs index bb0373f542..a1e6252481 100644 --- a/src/NLog/Config/LoggingConfigurationReloadedEventArgs.cs +++ b/src/NLog/Config/LoggingConfigurationReloadedEventArgs.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.Config { using System; @@ -79,4 +77,3 @@ public LoggingConfigurationReloadedEventArgs(bool succeeded, Exception exception } } -#endif diff --git a/src/NLog/Config/LoggingConfigurationWatchableFileLoader.cs b/src/NLog/Config/LoggingConfigurationWatchableFileLoader.cs index 42a01e6a4d..fef44d6f5e 100644 --- a/src/NLog/Config/LoggingConfigurationWatchableFileLoader.cs +++ b/src/NLog/Config/LoggingConfigurationWatchableFileLoader.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.Config { using System; @@ -304,5 +302,3 @@ private void TryUnwatchConfigFile() } } } - -#endif diff --git a/src/NLog/Config/MethodFactory.cs b/src/NLog/Config/MethodFactory.cs index 0f69df4b3b..c0a8aeb74b 100644 --- a/src/NLog/Config/MethodFactory.cs +++ b/src/NLog/Config/MethodFactory.cs @@ -89,7 +89,7 @@ public MethodDetails( /// The item name prefix. void IFactory.RegisterType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] Type type, string itemNamePrefix) { - if (type.IsClass()) + if (type.IsClass) { var extractedMethods = ExtractClassMethods(type); if (extractedMethods?.Count > 0) @@ -277,7 +277,7 @@ public void RegisterNoParameters(string methodName, Func n lock (_nameToMethodDetails) { _nameToMethodDetails.TryGetValue(methodName, out var methodDetails); - legacyMethodInfo = legacyMethodInfo ?? methodDetails.MethodInfo ?? noParameters.GetDelegateInfo(); + legacyMethodInfo = legacyMethodInfo ?? methodDetails.MethodInfo ?? noParameters.Method; _nameToMethodDetails[methodName] = new MethodDetails(legacyMethodInfo, noParameters, methodDetails.OneParameter, methodDetails.TwoParameters, methodDetails.ThreeParameters, methodDetails.ManyParameters, methodDetails.ManyParameterMinCount, methodDetails.ManyParameterMaxCount, methodDetails.ManyParameterWithLogEvent); } } @@ -287,7 +287,7 @@ public void RegisterOneParameter(string methodName, Func man lock (_nameToMethodDetails) { _nameToMethodDetails.TryGetValue(methodName, out var methodDetails); - legacyMethodInfo = legacyMethodInfo ?? methodDetails.MethodInfo ?? manyParameters.GetDelegateInfo(); + legacyMethodInfo = legacyMethodInfo ?? methodDetails.MethodInfo ?? manyParameters.Method; _nameToMethodDetails[methodName] = new MethodDetails(legacyMethodInfo, methodDetails.NoParameters, methodDetails.OneParameter, methodDetails.TwoParameters, methodDetails.ThreeParameters, manyParameters, manyParameterMinCount, manyParameterMaxCount, manyParameterWithLogEvent); } } diff --git a/src/NLog/Config/PropertyTypeConverter.cs b/src/NLog/Config/PropertyTypeConverter.cs index a20b746b34..a2c0823fe0 100644 --- a/src/NLog/Config/PropertyTypeConverter.cs +++ b/src/NLog/Config/PropertyTypeConverter.cs @@ -36,7 +36,6 @@ namespace NLog.Config using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; - using System.Reflection; using NLog.Internal; /// @@ -77,7 +76,7 @@ internal static Type ConvertToType(string stringvalue, bool throwOnError) internal static bool IsComplexType(Type type) { - return !type.IsValueType() && !typeof(IConvertible).IsAssignableFrom(type) && !StringConverterLookup.ContainsKey(type) && type.GetFirstCustomAttribute() is null; + return !type.IsValueType && !typeof(IConvertible).IsAssignableFrom(type) && !StringConverterLookup.ContainsKey(type) && type.GetFirstCustomAttribute() is null; } /// @@ -123,7 +122,7 @@ private static bool TryConvertFromString(string propertyString, Type propertyTyp return true; } - if (propertyType.IsEnum()) + if (propertyType.IsEnum) { return NLog.Common.ConversionHelpers.TryParseEnum(propertyString, propertyType, out propertyValue); } @@ -147,10 +146,8 @@ private static object ChangeObjectType(object propertyValue, Type propertyType, if (propertyValue is IConvertible convertibleValue) { var typeCode = convertibleValue.GetTypeCode(); -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 if (typeCode == TypeCode.DBNull) return convertibleValue; -#endif if (typeCode == TypeCode.Empty) return null; } diff --git a/src/NLog/Config/ServiceRepositoryInternal.cs b/src/NLog/Config/ServiceRepositoryInternal.cs index 2a87e19d54..e3ecf8c333 100644 --- a/src/NLog/Config/ServiceRepositoryInternal.cs +++ b/src/NLog/Config/ServiceRepositoryInternal.cs @@ -78,7 +78,7 @@ public override void RegisterService(Type type, object instance) public override object GetService(Type serviceType) { var serviceInstance = DefaultResolveInstance(serviceType, null); - if (serviceInstance is null && serviceType.IsAbstract()) + if (serviceInstance is null && serviceType.IsAbstract) { throw new NLogDependencyResolveException("Instance of class must be registered", serviceType); } @@ -108,7 +108,7 @@ private object DefaultResolveInstance(Type itemType, HashSet seenTypes) if (objectResolver is null && compiledConstructor is null) { - if (itemType.IsAbstract()) + if (itemType.IsAbstract) { if (seenTypes is null) return null; diff --git a/src/NLog/Config/SimpleConfigurator.cs b/src/NLog/Config/SimpleConfigurator.cs index 4b54652fba..4f17ee8bc7 100644 --- a/src/NLog/Config/SimpleConfigurator.cs +++ b/src/NLog/Config/SimpleConfigurator.cs @@ -49,7 +49,6 @@ namespace NLog.Config [EditorBrowsable(EditorBrowsableState.Never)] public static class SimpleConfigurator { -#if !NETSTANDARD1_3 /// /// Obsolete and replaced by and with NLog v5.2. /// @@ -80,7 +79,6 @@ public static void ConfigureForConsoleLogging(LogLevel minLevel) config.AddRule(minLevel, LogLevel.MaxLevel, consoleTarget, "*"); LogManager.Configuration = config; } -#endif /// /// Obsolete and replaced by with NLog v5.2. diff --git a/src/NLog/Internal/AppDomainWrapper.cs b/src/NLog/Internal/AppDomainWrapper.cs index c201e99878..fcff7dbd0a 100644 --- a/src/NLog/Internal/AppDomainWrapper.cs +++ b/src/NLog/Internal/AppDomainWrapper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.Internal.Fakeables { using System; @@ -246,5 +244,3 @@ private void OnProcessExit(object sender, EventArgs eventArgs) } } } - -#endif diff --git a/src/NLog/Internal/AppEnvironmentWrapper.cs b/src/NLog/Internal/AppEnvironmentWrapper.cs index 470b9244de..b199988156 100644 --- a/src/NLog/Internal/AppEnvironmentWrapper.cs +++ b/src/NLog/Internal/AppEnvironmentWrapper.cs @@ -42,7 +42,6 @@ namespace NLog.Internal.Fakeables internal sealed class AppEnvironmentWrapper : IAppEnvironment { -#if !NETSTANDARD1_3 private const string UnknownProcessName = ""; private string _entryAssemblyLocation; @@ -61,7 +60,7 @@ internal sealed class AppEnvironmentWrapper : IAppEnvironment public string CurrentProcessBaseName => _currentProcessBaseName ?? (_currentProcessBaseName = LookupCurrentProcessNameWithFallback()); /// public int CurrentProcessId => _currentProcessId ?? (_currentProcessId = LookupCurrentProcessIdWithFallback()).Value; -#endif + #pragma warning disable CS0618 // Type or member is obsolete /// public string AppDomainBaseDirectory => AppDomain.BaseDirectory; @@ -116,7 +115,6 @@ public XmlReader LoadXmlFile(string path) return XmlReader.Create(path); } -#if !NETSTANDARD1_3 private static string LookupEntryAssemblyLocation() { var entryAssembly = System.Reflection.Assembly.GetEntryAssembly(); @@ -289,7 +287,6 @@ private static string LookupCurrentProcessNameNative() return UnknownProcessName; } -#endif #if !NETSTANDARD private static string LookupCurrentProcessFilePathNative() diff --git a/src/NLog/Internal/AssemblyHelpers.cs b/src/NLog/Internal/AssemblyHelpers.cs index accd660910..e3e8aeba30 100644 --- a/src/NLog/Internal/AssemblyHelpers.cs +++ b/src/NLog/Internal/AssemblyHelpers.cs @@ -74,7 +74,6 @@ public static Type[] SafeGetTypes(Assembly assembly) } } -#if !NETSTANDARD1_3 [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] public static string GetAssemblyFileLocation(Assembly assembly) { @@ -150,6 +149,5 @@ public static string GetAssemblyFileLocation(Assembly assembly) return string.Empty; } } -#endif } } diff --git a/src/NLog/Internal/AsyncHelpersTask.cs b/src/NLog/Internal/AsyncHelpersTask.cs deleted file mode 100644 index 2294b3221d..0000000000 --- a/src/NLog/Internal/AsyncHelpersTask.cs +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - - -namespace NLog.Internal -{ -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - using AsyncDelegate = System.Threading.WaitCallback; -#else - using AsyncDelegate = System.Action; -#endif - - /// - /// Forward declare of system delegate type for use by other classes - /// - internal struct AsyncHelpersTask - { - public readonly AsyncDelegate AsyncDelegate; - public AsyncHelpersTask(AsyncDelegate asyncDelegate) - { - AsyncDelegate = asyncDelegate; - } - } -} diff --git a/src/NLog/Internal/EnvironmentHelper.cs b/src/NLog/Internal/EnvironmentHelper.cs index 8c5563e20f..373a62ef2a 100644 --- a/src/NLog/Internal/EnvironmentHelper.cs +++ b/src/NLog/Internal/EnvironmentHelper.cs @@ -53,14 +53,7 @@ internal static string GetMachineName() { try { -#if NETSTANDARD1_3 - var machineName = EnvironmentHelper.GetSafeEnvironmentVariable("COMPUTERNAME") ?? string.Empty; - if (string.IsNullOrEmpty(machineName)) - machineName = EnvironmentHelper.GetSafeEnvironmentVariable("HOSTNAME") ?? string.Empty; - return machineName; -#else return Environment.MachineName; -#endif } catch (System.Security.SecurityException) { diff --git a/src/NLog/Internal/ExceptionHelper.cs b/src/NLog/Internal/ExceptionHelper.cs index 8dbdc5b932..e3771bf3a0 100644 --- a/src/NLog/Internal/ExceptionHelper.cs +++ b/src/NLog/Internal/ExceptionHelper.cs @@ -122,7 +122,6 @@ public static bool MustBeRethrown(this Exception exception, IInternalLoggerConte /// trueif the must be rethrown, false otherwise. public static bool MustBeRethrownImmediately(this Exception exception) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 if (exception is StackOverflowException) { return true; // StackOverflowException cannot be caught since .NetFramework 2.0 @@ -132,7 +131,6 @@ public static bool MustBeRethrownImmediately(this Exception exception) { return true; // ThreadAbortException will automatically be rethrown at end of catch-block } -#endif if (exception is OutOfMemoryException) { diff --git a/src/NLog/Internal/FakeAppDomain.cs b/src/NLog/Internal/FakeAppDomain.cs deleted file mode 100644 index 5a6883d324..0000000000 --- a/src/NLog/Internal/FakeAppDomain.cs +++ /dev/null @@ -1,191 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -#if NETSTANDARD1_3 || NETSTANDARD1_5 - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace NLog.Internal.Fakeables -{ - [Obsolete("For unit testing only. Marked obsolete on NLog 5.0")] - internal sealed class FakeAppDomain : IAppDomain - { -#if NETSTANDARD1_5 - private readonly System.Runtime.Loader.AssemblyLoadContext _defaultContext; -#endif - - /// Initializes a new instance of the class. - public FakeAppDomain() - { - BaseDirectory = AppContext.BaseDirectory; -#if NETSTANDARD1_5 - _defaultContext = System.Runtime.Loader.AssemblyLoadContext.Default; - - try - { - FriendlyName = GetFriendlyNameFromEntryAssembly() ?? GetFriendlyNameFromProcessName() ?? "UnknownAppDomain"; - } - catch - { - FriendlyName = "UnknownAppDomain"; - } -#endif - } - - #region Implementation of IAppDomain - -#if NETSTANDARD1_5 - private static string GetFriendlyNameFromEntryAssembly() - { - try - { - string assemblyName = Assembly.GetEntryAssembly()?.GetName()?.Name; - return string.IsNullOrEmpty(assemblyName) ? null : assemblyName; - } - catch - { - return null; - } - } - - private static string GetFriendlyNameFromProcessName() - { - try - { - string processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName; - return string.IsNullOrEmpty(processName) ? null : processName; - } - catch - { - return null; - } - } -#endif - - /// - /// Gets or sets the base directory that the assembly resolver uses to probe for assemblies. - /// - public string BaseDirectory { get; private set; } - - /// - /// Gets or sets the name of the configuration file for an application domain. - /// - public string ConfigurationFile { get; set; } - - /// - /// Gets or sets the list of directories under the application base directory that are probed for private assemblies. - /// - public IEnumerable PrivateBinPath { get; set; } - - /// - /// Gets or set the friendly name. - /// - public string FriendlyName { get; set; } - - /// - /// Gets an integer that uniquely identifies the application domain within the process. - /// - public int Id { get; set; } - - /// - /// Gets the assemblies that have been loaded into the execution context of this application domain. - /// - /// A list of assemblies in this application domain. - public IEnumerable GetAssemblies() - { - return Internal.ArrayHelper.Empty(); - } - - /// - /// Process exit event. - /// - public event EventHandler ProcessExit - { - add - { -#if NETSTANDARD1_5 - if (_contextUnloadingEvent == null && _defaultContext != null) - _defaultContext.Unloading += OnContextUnloading; - _contextUnloadingEvent += value; -#endif - } - remove - { -#if NETSTANDARD1_5 - _contextUnloadingEvent -= value; - if (_contextUnloadingEvent == null && _defaultContext != null) - _defaultContext.Unloading -= OnContextUnloading; -#endif - } - } - - /// - /// Domain unloaded event. - /// - public event EventHandler DomainUnload - { - add - { -#if NETSTANDARD1_5 - if (_contextUnloadingEvent == null && _defaultContext != null) - _defaultContext.Unloading += OnContextUnloading; - _contextUnloadingEvent += value; -#endif - } - remove - { -#if NETSTANDARD1_5 - _contextUnloadingEvent -= value; - if (_contextUnloadingEvent == null && _defaultContext != null) - _defaultContext.Unloading -= OnContextUnloading; -#endif - } - } - -#if NETSTANDARD1_5 - private event EventHandler _contextUnloadingEvent; - - private void OnContextUnloading(System.Runtime.Loader.AssemblyLoadContext context) - { - var handler = _contextUnloadingEvent; - if (handler != null) handler.Invoke(context, EventArgs.Empty); - } -#endif -#endregion - } -} - -#endif diff --git a/src/NLog/Internal/FileAppenders/BaseMutexFileAppender.cs b/src/NLog/Internal/FileAppenders/BaseMutexFileAppender.cs index 3172920126..8b8927b3f2 100644 --- a/src/NLog/Internal/FileAppenders/BaseMutexFileAppender.cs +++ b/src/NLog/Internal/FileAppenders/BaseMutexFileAppender.cs @@ -31,10 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 -#define SupportsMutex -#endif - namespace NLog.Internal.FileAppenders { using System; @@ -44,13 +40,11 @@ namespace NLog.Internal.FileAppenders using System.Text; using JetBrains.Annotations; -#if SupportsMutex #if !NETSTANDARD using System.Security.AccessControl; using System.Security.Principal; #endif using System.Security.Cryptography; -#endif using Common; /// @@ -74,9 +68,7 @@ protected BaseMutexFileAppender(string fileName, ICreateFileParameters createPar { if (MutexDetector.SupportsSharableMutex) { -#if SupportsMutex ArchiveMutex = CreateArchiveMutex(); -#endif } else { @@ -85,7 +77,6 @@ protected BaseMutexFileAppender(string fileName, ICreateFileParameters createPar } } -#if SupportsMutex /// /// Gets the mutually-exclusive lock for archiving files. /// @@ -190,6 +181,5 @@ private string GetMutexName(string mutexNamePrefix) int cutOffIndex = canonicalName.Length - (maxMutexNameLength - mutexName.Length); return mutexName + canonicalName.Substring(cutOffIndex); } -#endif } } diff --git a/src/NLog/Internal/FileAppenders/FileAppenderCache.cs b/src/NLog/Internal/FileAppenders/FileAppenderCache.cs index 72a561b693..fde73c164c 100644 --- a/src/NLog/Internal/FileAppenders/FileAppenderCache.cs +++ b/src/NLog/Internal/FileAppenders/FileAppenderCache.cs @@ -46,11 +46,9 @@ internal sealed class FileAppenderCache : IFileAppenderCache private readonly BaseFileAppender[] _appenders; private Timer _autoClosingTimer; -#if !NETSTANDARD1_3 private string _archiveFilePatternToWatch; private readonly MultiFileWatcher _externalFileArchivingWatcher = new MultiFileWatcher(NotifyFilters.DirectoryName | NotifyFilters.FileName); private bool _logFileWasArchived; -#endif /// /// An "empty" instance of the class with zero size and empty list of appenders. @@ -83,12 +81,9 @@ public FileAppenderCache(int size, IFileAppenderFactory appenderFactory, ICreate _autoClosingTimer = new Timer(AutoClosingTimerCallback, null, Timeout.Infinite, Timeout.Infinite); -#if !NETSTANDARD1_3 _externalFileArchivingWatcher.FileChanged += ExternalFileArchivingWatcher_OnFileChanged; -#endif } -#if !NETSTANDARD1_3 private void ExternalFileArchivingWatcher_OnFileChanged(object sender, FileSystemEventArgs e) { if (_logFileWasArchived || CheckCloseAppenders is null || _autoClosingTimer is null) @@ -163,7 +158,6 @@ public void InvalidateAppendersForArchivedFiles() CloseAppenders("Cleanup Archive"); } } -#endif private void AutoClosingTimerCallback(object state) { @@ -274,7 +268,6 @@ private BaseFileAppender CreateAppender(string fileName, int freeSpot) _appenders[0] = newAppender; appenderToWrite = newAppender; -#if !NETSTANDARD1_3 if (CheckCloseAppenders != null) { if (freeSpot == 0) @@ -292,7 +285,6 @@ private BaseFileAppender CreateAppender(string fileName, int freeSpot) } _externalFileArchivingWatcher.Watch(appenderToWrite.FileName); // Monitor the active file-appender } -#endif } catch (Exception ex) { @@ -332,14 +324,12 @@ private void CloseAllAppenders(int startIndex, string reason) /// The time which prior the appenders considered expired public void CloseExpiredAppenders(DateTime expireTimeUtc) { -#if !NETSTANDARD1_3 if (_logFileWasArchived) { _logFileWasArchived = false; CloseAppenders("Cleanup Timer"); } else -#endif { if (expireTimeUtc != DateTime.MinValue) { @@ -529,14 +519,12 @@ private void CloseAppender(BaseFileAppender appender, string reason, bool lastAp // No active appenders, deactivate background tasks _autoClosingTimer.Change(Timeout.Infinite, Timeout.Infinite); -#if !NETSTANDARD1_3 _externalFileArchivingWatcher.StopWatching(); _logFileWasArchived = false; } else { _externalFileArchivingWatcher.StopWatching(appender.FileName); -#endif } appender.Close(); @@ -546,10 +534,8 @@ public void Dispose() { CheckCloseAppenders = null; -#if !NETSTANDARD1_3 _externalFileArchivingWatcher.Dispose(); _logFileWasArchived = false; -#endif var currentTimer = _autoClosingTimer; if (currentTimer != null) diff --git a/src/NLog/Internal/FileAppenders/IFileAppenderCache.cs b/src/NLog/Internal/FileAppenders/IFileAppenderCache.cs index a929055354..8a16e134ab 100644 --- a/src/NLog/Internal/FileAppenders/IFileAppenderCache.cs +++ b/src/NLog/Internal/FileAppenders/IFileAppenderCache.cs @@ -100,8 +100,6 @@ internal interface IFileAppenderCache : IDisposable /// File Appender that matched the filePath (null if none found) BaseFileAppender InvalidateAppender(string filePath); -#if !NETSTANDARD1_3 - /// /// The archive file path pattern that is used to detect when archiving occurs. /// @@ -111,7 +109,5 @@ internal interface IFileAppenderCache : IDisposable /// Invalidates appenders for all files that were archived. /// void InvalidateAppendersForArchivedFiles(); - -#endif } } diff --git a/src/NLog/Internal/FileAppenders/MutexMultiProcessFileAppender.cs b/src/NLog/Internal/FileAppenders/MutexMultiProcessFileAppender.cs index 700a3d9c99..9fb0d0133f 100644 --- a/src/NLog/Internal/FileAppenders/MutexMultiProcessFileAppender.cs +++ b/src/NLog/Internal/FileAppenders/MutexMultiProcessFileAppender.cs @@ -31,12 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 -#define SupportsMutex -#endif - -#if SupportsMutex - namespace NLog.Internal.FileAppenders { using System; @@ -170,5 +164,3 @@ BaseFileAppender IFileAppenderFactory.Open(string fileName, ICreateFileParameter } } } - -#endif diff --git a/src/NLog/Internal/IAppEnvironment.cs b/src/NLog/Internal/IAppEnvironment.cs index 2c1fabb164..10b7d841c3 100644 --- a/src/NLog/Internal/IAppEnvironment.cs +++ b/src/NLog/Internal/IAppEnvironment.cs @@ -49,7 +49,7 @@ internal interface IAppEnvironment : IFileSystem IEnumerable GetAppDomainRuntimeAssemblies(); [Obsolete("Marked obsolete on NLog 5.0")] IAppDomain AppDomain { get; } -#if !NETSTANDARD1_3 + string CurrentProcessFilePath { get; } /// /// Gets current process name (excluding filename extension, if any). @@ -58,7 +58,6 @@ internal interface IAppEnvironment : IFileSystem int CurrentProcessId { get; } string EntryAssemblyLocation { get; } string EntryAssemblyFileName { get; } -#endif string UserTempFilePath { get; } /// diff --git a/src/NLog/Internal/INetworkInterfaceRetriever.cs b/src/NLog/Internal/INetworkInterfaceRetriever.cs index e7ec1b7ef2..3d6dc245bb 100644 --- a/src/NLog/Internal/INetworkInterfaceRetriever.cs +++ b/src/NLog/Internal/INetworkInterfaceRetriever.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.Internal { using System.Net.NetworkInformation; @@ -48,4 +46,3 @@ internal interface INetworkInterfaceRetriever NetworkInterface[] AllNetworkInterfaces { get; } } } -#endif diff --git a/src/NLog/Internal/ISmtpClient.cs b/src/NLog/Internal/ISmtpClient.cs index 34bf7ca384..395171f828 100644 --- a/src/NLog/Internal/ISmtpClient.cs +++ b/src/NLog/Internal/ISmtpClient.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.Internal { using System; @@ -86,5 +84,3 @@ internal interface ISmtpClient : IDisposable string PickupDirectoryLocation { get; set; } } } - -#endif diff --git a/src/NLog/Internal/MultiFileWatcher.cs b/src/NLog/Internal/MultiFileWatcher.cs index 8b34a2ccd9..658ae44571 100644 --- a/src/NLog/Internal/MultiFileWatcher.cs +++ b/src/NLog/Internal/MultiFileWatcher.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.Internal { using System; @@ -248,5 +246,3 @@ private void OnFileChanged(object source, FileSystemEventArgs e) } } } - -#endif diff --git a/src/NLog/Internal/MutexDetector.cs b/src/NLog/Internal/MutexDetector.cs index 39cf54b371..99644e1e0c 100644 --- a/src/NLog/Internal/MutexDetector.cs +++ b/src/NLog/Internal/MutexDetector.cs @@ -31,10 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 -#define SupportsMutex -#endif - namespace NLog.Internal { using System; @@ -59,7 +55,6 @@ private static bool ResolveSupportsSharableMutex() { try { -#if SupportsMutex #if !NETSTANDARD if (Environment.Version.Major < 4 && PlatformDetector.IsMono) return false; // MONO ver. 4 is needed for named Mutex to work @@ -67,7 +62,6 @@ private static bool ResolveSupportsSharableMutex() var mutex = BaseMutexFileAppender.ForceCreateSharableMutex("NLogMutexTester"); mutex.Close(); //"dispose" return true; -#endif } catch (Exception ex) { diff --git a/src/NLog/Internal/MySmtpClient.cs b/src/NLog/Internal/MySmtpClient.cs index 1730720ef1..ebbe4fb69b 100644 --- a/src/NLog/Internal/MySmtpClient.cs +++ b/src/NLog/Internal/MySmtpClient.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.Internal { using System.Net.Mail; @@ -62,5 +60,3 @@ public void Dispose() #endif } } - -#endif diff --git a/src/NLog/Internal/NetStandardHelpers.cs b/src/NLog/Internal/NetStandardHelpers.cs deleted file mode 100644 index aabaa8c0ca..0000000000 --- a/src/NLog/Internal/NetStandardHelpers.cs +++ /dev/null @@ -1,120 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -#if NETSTANDARD1_3 || NETSTANDARD1_5 - -namespace NLog.Internal -{ - using System; - using System.Diagnostics; - using System.IO; - using System.Reflection; - using System.Threading; - - static class NetStandardHelpers - { - public static void Close(this IDisposable disposable) - { - disposable.Dispose(); - } - -#pragma warning disable S2953 // Methods named "Dispose" should implement "IDisposable.Dispose" - public static bool Dispose(this IDisposable disposable, EventWaitHandle waitHandle) -#pragma warning restore S2953 // Methods named "Dispose" should implement "IDisposable.Dispose" - { - disposable.Dispose(); - return false; - } - - public static bool IsDefined(this Type type, Type other, bool inherit) - { - return type.GetTypeInfo().IsDefined(other, inherit); - } - - public static bool IsSubclassOf(this Type type, Type other) - { - return type.GetTypeInfo().IsSubclassOf(other); - } - - public static MethodInfo GetMethod(this Type type, string name, BindingFlags bindingAttr, object binder, Type[] types, object[] modifiers) - { - if (binder != null) - throw new ArgumentException("Not supported", nameof(binder)); - if (modifiers != null) - throw new ArgumentException("Not supported", nameof(modifiers)); - - foreach (MethodInfo method in type.GetMethods(bindingAttr)) - { - if (method.Name != name) - continue; - - var parameters = method.GetParameters(); - if (parameters is null || parameters.Length != types.Length) - continue; - - for (int i = 0; i < parameters.Length; ++i) - { - if (parameters[i].ParameterType != types[i]) - { - parameters = null; - break; - } - } - - if (parameters != null) - { - return method; - } - } - - return null; - } - - public static byte[] GetBuffer(this MemoryStream memoryStream) - { - ArraySegment bytes; - if (memoryStream.TryGetBuffer(out bytes)) - { - return bytes.Array; - } - return memoryStream.ToArray(); - } - - public static StackFrame GetFrame(this StackTrace strackTrace, int number) - { - return strackTrace.GetFrames()[number]; - } - } -} - -#endif diff --git a/src/NLog/Internal/NetworkInterfaceRetriever.cs b/src/NLog/Internal/NetworkInterfaceRetriever.cs index d31407f902..fb1d95d07a 100644 --- a/src/NLog/Internal/NetworkInterfaceRetriever.cs +++ b/src/NLog/Internal/NetworkInterfaceRetriever.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.Internal { using System.Net.NetworkInformation; @@ -48,4 +46,3 @@ internal sealed class NetworkInterfaceRetriever : INetworkInterfaceRetriever public NetworkInterface[] AllNetworkInterfaces => NetworkInterface.GetAllNetworkInterfaces(); } } -#endif diff --git a/src/NLog/Internal/NetworkSenders/HttpNetworkSender.cs b/src/NLog/Internal/NetworkSenders/HttpNetworkSender.cs index 45b8f47978..2a9446b864 100644 --- a/src/NLog/Internal/NetworkSenders/HttpNetworkSender.cs +++ b/src/NLog/Internal/NetworkSenders/HttpNetworkSender.cs @@ -65,12 +65,11 @@ protected override void BeginRequest(NetworkRequestArgs eventArgs) var webRequest = HttpRequestFactory.CreateWebRequest(_addressUri); webRequest.Method = "POST"; -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 if (SendTimeout > TimeSpan.Zero) { webRequest.Timeout = (int)SendTimeout.TotalMilliseconds; } -#endif + AsyncCallback onResponse = r => { diff --git a/src/NLog/Internal/NetworkSenders/NetworkSender.cs b/src/NLog/Internal/NetworkSenders/NetworkSender.cs index bc70194f72..0d21620c61 100644 --- a/src/NLog/Internal/NetworkSenders/NetworkSender.cs +++ b/src/NLog/Internal/NetworkSenders/NetworkSender.cs @@ -174,11 +174,7 @@ protected virtual IPAddress ResolveIpAddress(Uri uri, AddressFamily addressFamil default: { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 var addresses = Dns.GetHostEntry(uri.Host).AddressList; // Dns.GetHostEntry returns IPv6 + IPv4 addresses, but Dns.GetHostAddresses() might only return IPv6 addresses -#else - var addresses = Dns.GetHostEntryAsync(uri.Host).ConfigureAwait(false).GetAwaiter().GetResult().AddressList; -#endif if (addressFamily == AddressFamily.Unspecified && addresses.Length > 1) { Array.Sort(addresses, IPAddressComparer.Default); // Prioritize IPv4 addresses over IPv6, unless explicitly specified diff --git a/src/NLog/Internal/NetworkSenders/SslSocketProxy.cs b/src/NLog/Internal/NetworkSenders/SslSocketProxy.cs index d5783b1860..40493b1063 100644 --- a/src/NLog/Internal/NetworkSenders/SslSocketProxy.cs +++ b/src/NLog/Internal/NetworkSenders/SslSocketProxy.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.Internal.NetworkSenders { using System; @@ -200,5 +198,3 @@ public void Dispose() } } } - -#endif diff --git a/src/NLog/Internal/NetworkSenders/TcpNetworkSender.cs b/src/NLog/Internal/NetworkSenders/TcpNetworkSender.cs index 4873bd2623..70ca2ba366 100644 --- a/src/NLog/Internal/NetworkSenders/TcpNetworkSender.cs +++ b/src/NLog/Internal/NetworkSenders/TcpNetworkSender.cs @@ -46,7 +46,7 @@ internal class TcpNetworkSender : QueuedNetworkSender private static bool? EnableKeepAliveSuccessful; private ISocket _socket; private readonly EventHandler _socketOperationCompletedAsync; - private AsyncHelpersTask? _asyncBeginRequest; + System.Threading.WaitCallback _asyncBeginRequest; /// /// Initializes a new instance of the class. @@ -91,12 +91,11 @@ protected internal virtual ISocket CreateSocket(string host, AddressFamily addre socketProxy.UnderlyingSocket.SendTimeout = (int)sendTimeout.TotalMilliseconds; } -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 if (SslProtocols != System.Security.Authentication.SslProtocols.None) { return new SslSocketProxy(host, SslProtocols, socketProxy); } -#endif + return socketProxy; } @@ -236,8 +235,8 @@ protected override void BeginRequest(NetworkRequestArgs eventArgs) // Schedule async network operation to avoid blocking socket-operation (Allow adding more request) if (_asyncBeginRequest is null) - _asyncBeginRequest = new AsyncHelpersTask(BeginRequestAsync); - AsyncHelpers.StartAsyncTask(_asyncBeginRequest.Value, socketEventArgs); + _asyncBeginRequest = BeginRequestAsync; + AsyncHelpers.StartAsyncTask(_asyncBeginRequest, socketEventArgs); } private void BeginRequestAsync(object state) diff --git a/src/NLog/Internal/NetworkSenders/UdpNetworkSender.cs b/src/NLog/Internal/NetworkSenders/UdpNetworkSender.cs index 207199fed9..355cec345e 100644 --- a/src/NLog/Internal/NetworkSenders/UdpNetworkSender.cs +++ b/src/NLog/Internal/NetworkSenders/UdpNetworkSender.cs @@ -47,7 +47,7 @@ internal class UdpNetworkSender : QueuedNetworkSender private ISocket _socket; private EndPoint _endpoint; private readonly EventHandler _socketOperationCompletedAsync; - private AsyncHelpersTask? _asyncBeginRequest; + System.Threading.WaitCallback _asyncBeginRequest; /// /// Initializes a new instance of the class. @@ -123,8 +123,8 @@ protected override void BeginRequest(NetworkRequestArgs eventArgs) // Schedule async network operation to avoid blocking socket-operation (Allow adding more request) if (_asyncBeginRequest is null) - _asyncBeginRequest = new AsyncHelpersTask(BeginRequestAsync); - AsyncHelpers.StartAsyncTask(_asyncBeginRequest.Value, socketEventArgs); + _asyncBeginRequest = BeginRequestAsync; + AsyncHelpers.StartAsyncTask(_asyncBeginRequest, socketEventArgs); } private void BeginRequestAsync(object state) diff --git a/src/NLog/Internal/ObjectReflectionCache.cs b/src/NLog/Internal/ObjectReflectionCache.cs index 7eed396e57..054bb27246 100644 --- a/src/NLog/Internal/ObjectReflectionCache.cs +++ b/src/NLog/Internal/ObjectReflectionCache.cs @@ -37,7 +37,7 @@ namespace NLog.Internal using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -#if !NET35 && !NET40 && !NETSTANDARD1_3 && !NETSTANDARD1_5 +#if !NET35 && !NET40 using System.Dynamic; #endif using System.Linq; @@ -143,7 +143,7 @@ public bool TryLookupExpandoObject(object value, out ObjectPropertyList objectPr return true; } -#if !NET35 && !NET40 && !NETSTANDARD1_3 && !NETSTANDARD1_5 +#if !NET35 && !NET40 if (value is DynamicObject d) { var dictionary = DynamicObjectToDict(d); @@ -274,11 +274,7 @@ private static FastPropertyLookup[] BuildFastLookup(PropertyInfo[] properties, b var getterMethod = prop.GetGetMethod(); Type propertyType = getterMethod.ReturnType; ReflectionHelpers.LateBoundMethod valueLookup = ReflectionHelpers.CreateLateBoundMethod(getterMethod); -#if NETSTANDARD1_3 - TypeCode typeCode = propertyType == typeof(string) ? TypeCode.String : (propertyType == typeof(int) ? TypeCode.Int32 : TypeCode.Object); -#else TypeCode typeCode = Type.GetTypeCode(propertyType); // Skip cyclic-reference checks when not TypeCode.Object -#endif fastLookup[fastAccessIndex++] = new FastPropertyLookup(prop.Name, typeCode, valueLookup); } return fastLookup; @@ -548,7 +544,7 @@ public bool Equals(ObjectPropertyInfos other) } } -#if !NET35 && !NET40 && !NETSTANDARD1_3 && !NETSTANDARD1_5 +#if !NET35 && !NET40 private static Dictionary DynamicObjectToDict(DynamicObject d) { var newVal = new Dictionary(); @@ -583,7 +579,7 @@ public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, Dy private static bool IsGenericDictionaryEnumeratorType(Type interfaceType) { - if (interfaceType.IsGenericType()) + if (interfaceType.IsGenericType) { if (interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>) #if !NET35 diff --git a/src/NLog/Internal/PropertyHelper.cs b/src/NLog/Internal/PropertyHelper.cs index 9d796f391f..0da94fefb8 100644 --- a/src/NLog/Internal/PropertyHelper.cs +++ b/src/NLog/Internal/PropertyHelper.cs @@ -235,7 +235,7 @@ internal static void CheckRequiredParameters(ConfigurationItemFactory configFact { var propInfo = configProp.Value; var propertyType = propInfo.PropertyType; - if (propertyType != null && (propertyType.IsClass() || Nullable.GetUnderlyingType(propertyType) != null)) + if (propertyType != null && (propertyType.IsClass || Nullable.GetUnderlyingType(propertyType) != null)) { if (propInfo.IsDefined(_requiredParameterAttribute.GetType(), false)) { @@ -252,11 +252,7 @@ internal static void CheckRequiredParameters(ConfigurationItemFactory configFact internal static bool IsSimplePropertyType(Type type) { -#if !NETSTANDARD1_3 if (Type.GetTypeCode(type) != TypeCode.Object) -#else - if (type.IsPrimitive() || type == typeof(string)) -#endif return true; if (type == typeof(CultureInfo)) @@ -311,7 +307,7 @@ private static bool TryNLogSpecificConversion(Type propertyType, string value, C return true; } - if (propertyType.IsGenericType() && propertyType.GetGenericTypeDefinition() == typeof(Layout<>)) + if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Layout<>)) { var simpleLayout = new SimpleLayout(value, configurationItemFactory); var concreteType = typeof(Layout<>).MakeGenericType(propertyType.GetGenericArguments()); @@ -325,7 +321,7 @@ private static bool TryNLogSpecificConversion(Type propertyType, string value, C private static bool TryGetEnumValue(Type resultType, string value, out object result) { - if (!resultType.IsEnum()) + if (!resultType.IsEnum) { result = null; return false; @@ -394,7 +390,7 @@ private static object TryParseConditionValue(string stringValue, ConfigurationIt /// private static bool TryFlatListConversion(object obj, PropertyInfo propInfo, string valueRaw, ConfigurationItemFactory configurationItemFactory, out object newValue) { - if (propInfo.PropertyType.IsGenericType() && TryCreateCollectionObject(obj, propInfo, valueRaw, out var newList, out var collectionAddMethod, out var propertyType)) + if (propInfo.PropertyType.IsGenericType && TryCreateCollectionObject(obj, propInfo, valueRaw, out var newList, out var collectionAddMethod, out var propertyType)) { var values = valueRaw.SplitQuoted(',', '\'', '\\'); foreach (var value in values) diff --git a/src/NLog/Internal/ReflectionHelpers.cs b/src/NLog/Internal/ReflectionHelpers.cs index 90cebda36a..8ef4f44125 100644 --- a/src/NLog/Internal/ReflectionHelpers.cs +++ b/src/NLog/Internal/ReflectionHelpers.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using JetBrains.Annotations; - namespace NLog.Internal { using System; @@ -40,6 +38,7 @@ namespace NLog.Internal using System.Linq; using System.Linq.Expressions; using System.Reflection; + using JetBrains.Annotations; /// /// Reflection helpers. @@ -56,7 +55,7 @@ internal static class ReflectionHelpers /// public static bool IsStaticClass(this Type type) { - return type.IsClass() && type.IsAbstract() && type.IsSealed(); + return type.IsClass && type.IsAbstract && type.IsSealed; } /// @@ -163,128 +162,29 @@ private static UnaryExpression CreateParameterExpression(ParameterInfo parameter return valueCast; } - public static bool IsPublic(this Type type) - { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - return type.IsPublic; -#else - return type.GetTypeInfo().IsPublic; -#endif - } - - public static bool IsEnum(this Type type) - { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - return type.IsEnum; -#else - return type.GetTypeInfo().IsEnum; -#endif - } - - public static bool IsPrimitive(this Type type) - { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - return type.IsPrimitive; -#else - return type.GetTypeInfo().IsPrimitive; -#endif - } - - public static bool IsValueType(this Type type) - { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - return type.IsValueType; -#else - return type.GetTypeInfo().IsValueType; -#endif - } - - public static bool IsSealed(this Type type) - { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - return type.IsSealed; -#else - return type.GetTypeInfo().IsSealed; -#endif - } - - public static bool IsAbstract(this Type type) - { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - return type.IsAbstract; -#else - return type.GetTypeInfo().IsAbstract; -#endif - } - - public static bool IsClass(this Type type) - { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - return type.IsClass; -#else - return type.GetTypeInfo().IsClass; -#endif - } - - public static bool IsGenericType(this Type type) - { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - return type.IsGenericType; -#else - return type.GetTypeInfo().IsGenericType; -#endif - } - [CanBeNull] public static TAttr GetFirstCustomAttribute(this Type type) where TAttr : Attribute { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 return Attribute.GetCustomAttributes(type, typeof(TAttr)).FirstOrDefault() as TAttr; -#else - var typeInfo = type.GetTypeInfo(); - return typeInfo.GetCustomAttributes().FirstOrDefault(); -#endif } [CanBeNull] public static TAttr GetFirstCustomAttribute(this PropertyInfo info) where TAttr : Attribute { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 return Attribute.GetCustomAttributes(info, typeof(TAttr)).FirstOrDefault() as TAttr; -#else - return info.GetCustomAttributes(typeof(TAttr), false).FirstOrDefault() as TAttr; -#endif } [CanBeNull] public static TAttr GetFirstCustomAttribute(this Assembly assembly) where TAttr : Attribute { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 return Attribute.GetCustomAttributes(assembly, typeof(TAttr)).FirstOrDefault() as TAttr; -#else - return assembly.GetCustomAttributes(typeof(TAttr)).FirstOrDefault() as TAttr; -#endif } public static IEnumerable GetCustomAttributes(this Type type, bool inherit) where TAttr : Attribute { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 return (TAttr[])type.GetCustomAttributes(typeof(TAttr), inherit); -#else - return type.GetTypeInfo().GetCustomAttributes(inherit); -#endif - } - - public static Assembly GetAssembly(this Type type) - { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - return type.Assembly; -#else - var typeInfo = type.GetTypeInfo(); - return typeInfo.Assembly; -#endif } public static bool IsValidPublicProperty(this PropertyInfo p) @@ -298,15 +198,6 @@ public static object GetPropertyValue(this PropertyInfo p, object instance) return p.GetValue(instance); #else return p.GetGetMethod().Invoke(instance, null); -#endif - } - - public static MethodInfo GetDelegateInfo(this Delegate method) - { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - return method.Method; -#else - return System.Reflection.RuntimeReflectionExtensions.GetMethodInfo(method); #endif } } diff --git a/src/NLog/Internal/StackTraceUsageUtils.cs b/src/NLog/Internal/StackTraceUsageUtils.cs index bbcfededf4..f29e5d04a1 100644 --- a/src/NLog/Internal/StackTraceUsageUtils.cs +++ b/src/NLog/Internal/StackTraceUsageUtils.cs @@ -36,7 +36,6 @@ namespace NLog.Internal using System; using System.Diagnostics; using System.Reflection; - using System.Runtime.CompilerServices; using NLog.Config; /// @@ -44,9 +43,9 @@ namespace NLog.Internal /// internal static class StackTraceUsageUtils { - private static readonly Assembly nlogAssembly = typeof(StackTraceUsageUtils).GetAssembly(); - private static readonly Assembly mscorlibAssembly = typeof(string).GetAssembly(); - private static readonly Assembly systemAssembly = typeof(Debug).GetAssembly(); + private static readonly Assembly nlogAssembly = typeof(StackTraceUsageUtils).Assembly; + private static readonly Assembly mscorlibAssembly = typeof(string).Assembly; + private static readonly Assembly systemAssembly = typeof(Debug).Assembly; public static StackTraceUsage GetStackTraceUsage(bool includeFileName, int skipFrames, bool captureStackTrace) { @@ -70,11 +69,7 @@ public static StackTraceUsage GetStackTraceUsage(bool includeFileName, int skipF public static int GetFrameCount(this StackTrace strackTrace) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 return strackTrace.FrameCount; -#else - return strackTrace.GetFrames().Length; -#endif } public static string GetStackFrameMethodName(MethodBase method, bool includeMethodInfo, bool cleanAsyncMoveNext, bool cleanAnonymousDelegates) @@ -160,7 +155,7 @@ public static string GetStackFrameMethodClassName(MethodBase method, bool includ private static string GetNamespaceFromTypeAssembly(Type callerClassType) { - var classAssembly = callerClassType.GetAssembly(); + var classAssembly = callerClassType.Assembly; if (classAssembly != null && classAssembly != mscorlibAssembly && classAssembly != systemAssembly) { var assemblyFullName = classAssembly.FullName; @@ -179,47 +174,6 @@ public static MethodBase GetStackMethod(StackFrame stackFrame) return stackFrame?.GetMethod(); } - /// - /// Gets the fully qualified name of the class invoking the calling method, including the - /// namespace but not the assembly. - /// - [MethodImpl(MethodImplOptions.NoInlining)] - public static string GetClassFullName() - { - int framesToSkip = 2; - - string className = string.Empty; -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - var stackFrame = new StackFrame(framesToSkip, false); - className = GetClassFullName(stackFrame); -#else - var stackTrace = Environment.StackTrace; - var stackTraceLines = stackTrace.Replace("\r", "").SplitAndTrimTokens('\n'); - for (int i = 0; i < stackTraceLines.Length; ++i) - { - var callingClassAndMethod = stackTraceLines[i].Split(new[] { " ", "<>", "(", ")" }, StringSplitOptions.RemoveEmptyEntries)[1]; - int methodStartIndex = callingClassAndMethod.LastIndexOf(".", StringComparison.Ordinal); - if (methodStartIndex > 0) - { - // Trim method name. - var callingClass = callingClassAndMethod.Substring(0, methodStartIndex); - // Needed because of extra dot, for example if method was .ctor() - className = callingClass.TrimEnd('.'); - if (!className.StartsWith("System.Environment", StringComparison.Ordinal) && framesToSkip != 0) - { - i += framesToSkip - 1; - framesToSkip = 0; - continue; - } - if (!className.StartsWith("System.", StringComparison.Ordinal)) - break; - } - } -#endif - return className; - } - -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 /// /// Gets the fully qualified name of the class invoking the calling method, including the /// namespace but not the assembly. @@ -241,7 +195,6 @@ public static string GetClassFullName(StackFrame stackFrame) } return className; } -#endif private static string GetClassFullName(StackTrace stackTrace) { @@ -262,7 +215,7 @@ private static string GetClassFullName(StackTrace stackTrace) /// Valid assembly, or null if assembly was internal public static Assembly LookupAssemblyFromMethod(MethodBase method) { - var assembly = method?.DeclaringType?.GetAssembly() ?? method?.Module?.Assembly; + var assembly = method?.DeclaringType?.Assembly ?? method?.Module?.Assembly; // skip stack frame if the method declaring type assembly is from hidden assemblies list if (assembly == nlogAssembly) diff --git a/src/NLog/Internal/TargetWithFilterChain.cs b/src/NLog/Internal/TargetWithFilterChain.cs index 21b5e54440..600e0f6682 100644 --- a/src/NLog/Internal/TargetWithFilterChain.cs +++ b/src/NLog/Internal/TargetWithFilterChain.cs @@ -356,11 +356,8 @@ internal bool TryRememberCallSiteClassName(LogEventInfo logEvent) string internClassName = logEvent.LoggerName == className ? logEvent.LoggerName : -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 string.Intern(className); // Single string-reference for all logging-locations for the same class -#else - className; -#endif + CallSiteKey callSiteKey = new CallSiteKey(logEvent.CallerMemberName, logEvent.CallerFilePath, logEvent.CallerLineNumber); return _callSiteClassNameCache.TryAddValue(callSiteKey, internClassName); } diff --git a/src/NLog/Internal/XmlHelper.cs b/src/NLog/Internal/XmlHelper.cs index 73c47e6b9f..aa6510036a 100644 --- a/src/NLog/Internal/XmlHelper.cs +++ b/src/NLog/Internal/XmlHelper.cs @@ -45,7 +45,7 @@ internal static class XmlHelper { // found on https://stackoverflow.com/questions/397250/unicode-regex-invalid-xml-characters/961504#961504 // filters control characters but allows only properly-formed surrogate sequences -#if NET35 || NETSTANDARD1_3 || NETSTANDARD1_5 +#if NET35 private static readonly System.Text.RegularExpressions.Regex InvalidXmlChars = new System.Text.RegularExpressions.Regex( @"(? /// Cleans string of any invalid XML chars found /// diff --git a/src/NLog/LayoutRenderers/AssemblyVersionLayoutRenderer.cs b/src/NLog/LayoutRenderers/AssemblyVersionLayoutRenderer.cs index ac3d10d145..ffa885b2fe 100644 --- a/src/NLog/LayoutRenderers/AssemblyVersionLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AssemblyVersionLayoutRenderer.cs @@ -189,11 +189,7 @@ protected virtual System.Reflection.Assembly GetAssembly() { if (string.IsNullOrEmpty(Name)) { -#if !NETSTANDARD1_3 return System.Reflection.Assembly.GetEntryAssembly(); -#else - return null; -#endif } else { diff --git a/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs index 1928ef5404..e574faaa33 100644 --- a/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs @@ -54,13 +54,11 @@ public class BaseDirLayoutRenderer : LayoutRenderer { private readonly string _baseDir; -#if !NETSTANDARD1_3 /// /// cached /// private string _processDir; private readonly IAppEnvironment _appEnvironment; -#endif /// /// Use base dir of current process. Alternative one can just use ${processdir} @@ -87,9 +85,7 @@ public BaseDirLayoutRenderer() : this(LogFactory.DefaultAppEnvironment) internal BaseDirLayoutRenderer(IAppEnvironment appEnvironment) { _baseDir = appEnvironment.AppDomainBaseDirectory; -#if !NETSTANDARD1_3 _appEnvironment = appEnvironment; -#endif } /// @@ -108,7 +104,7 @@ internal BaseDirLayoutRenderer(IAppEnvironment appEnvironment) protected override void Append(StringBuilder builder, LogEventInfo logEvent) { var dir = _baseDir; -#if !NETSTANDARD1_3 + if (ProcessDir) { dir = _processDir ?? (_processDir = GetProcessDir()); @@ -117,7 +113,6 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) { dir = _processDir ?? (_processDir = GetFixedTempBaseDir(_baseDir)); } -#endif if (dir != null) { @@ -126,7 +121,6 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) } } -#if !NETSTANDARD1_3 private string GetFixedTempBaseDir(string baseDir) { try @@ -153,7 +147,6 @@ private string GetProcessDir() { return Path.GetDirectoryName(_appEnvironment.CurrentProcessFilePath); } -#endif } } diff --git a/src/NLog/LayoutRenderers/CallSiteLayoutRenderer.cs b/src/NLog/LayoutRenderers/CallSiteLayoutRenderer.cs index 621c41dc87..61e3995163 100644 --- a/src/NLog/LayoutRenderers/CallSiteLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CallSiteLayoutRenderer.cs @@ -178,7 +178,6 @@ private void AppendFileName(StringBuilder builder, string fileName, int lineNumb [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow callsite logic", "IL2026")] private void AppendExceptionCallSite(StringBuilder builder, LogEventInfo logEvent) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 var targetSite = logEvent?.Exception?.TargetSite; if (targetSite != null) { @@ -199,7 +198,6 @@ private void AppendExceptionCallSite(StringBuilder builder, LogEventInfo logEven } } else -#endif { if (ClassName || FileName) { diff --git a/src/NLog/LayoutRenderers/EnvironmentUserLayoutRenderer.cs b/src/NLog/LayoutRenderers/EnvironmentUserLayoutRenderer.cs index d11c4e27dd..114392eca5 100644 --- a/src/NLog/LayoutRenderers/EnvironmentUserLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/EnvironmentUserLayoutRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.LayoutRenderers { using System; @@ -119,5 +117,3 @@ private string GetValueSafe(Func getValue, string defaultValue) } } } - -#endif diff --git a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs index 565016f038..1f3f02778d 100644 --- a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs @@ -95,7 +95,7 @@ public class ExceptionLayoutRenderer : LayoutRenderer, IRawValue nameof(Exception.Message), nameof(Exception.Source), nameof(Exception.StackTrace), - "TargetSite",// Not available on NETSTANDARD1_3 OR NETSTANDARD1_5 + nameof(Exception.TargetSite), }, StringComparer.Ordinal); private ObjectReflectionCache ObjectReflectionCache => _objectReflectionCache ?? (_objectReflectionCache = new ObjectReflectionCache(LoggingConfiguration.GetServiceProvider())); @@ -371,11 +371,7 @@ protected virtual void AppendMessage(StringBuilder sb, Exception ex) [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow callsite logic", "IL2026")] protected virtual void AppendMethod(StringBuilder sb, Exception ex) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 sb.Append(ex.TargetSite?.ToString()); -#else - sb.Append(ParseMethodNameFromStackTrace(ex.StackTrace)); -#endif } /// @@ -543,63 +539,5 @@ private static List CompileFormat(string formatSpecifi } return formats; } - -#if NETSTANDARD1_3 || NETSTANDARD1_5 - /// - /// Find name of method on stracktrace. - /// - /// Full stracktrace - /// - protected static string ParseMethodNameFromStackTrace(string stackTrace) - { - if (string.IsNullOrEmpty(stackTrace)) - return string.Empty; - - // get the first line of the stack trace - string stackFrameLine; - - int p = stackTrace.IndexOfAny(new[] { '\r', '\n' }); - if (p >= 0) - { - stackFrameLine = stackTrace.Substring(0, p); - } - else - { - stackFrameLine = stackTrace; - } - - // stack trace is composed of lines which look like this - // - // at NLog.UnitTests.LayoutRenderers.ExceptionTests.GenericClass`3.Method2[T1,T2,T3](T1 aaa, T2 b, T3 o, Int32 i, DateTime now, Nullable`1 gfff, List`1[] something) - // - // "at " prefix can be localized so we cannot hard-code it but it's followed by a space, class name (which does not have a space in it) and opening parenthesis - int lastSpace = -1; - int startPos = 0; - int endPos = stackFrameLine.Length; - - for (int i = 0; i < stackFrameLine.Length; ++i) - { - switch (stackFrameLine[i]) - { - case ' ': - lastSpace = i; - break; - - case '(': - startPos = lastSpace + 1; - break; - - case ')': - endPos = i + 1; - - // end the loop - i = stackFrameLine.Length; - break; - } - } - - return stackTrace.Substring(startPos, endPos - startPos); - } -#endif } } diff --git a/src/NLog/LayoutRenderers/FileContentsLayoutRenderer.cs b/src/NLog/LayoutRenderers/FileContentsLayoutRenderer.cs index 857f8abd30..db0f74d4c0 100644 --- a/src/NLog/LayoutRenderers/FileContentsLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/FileContentsLayoutRenderer.cs @@ -61,11 +61,7 @@ public class FileContentsLayoutRenderer : LayoutRenderer /// public FileContentsLayoutRenderer() { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 Encoding = Encoding.Default; -#else - Encoding = Encoding.UTF8; -#endif _lastFileName = string.Empty; } @@ -104,14 +100,7 @@ private string ReadFileContents(string fileName) { try { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - using (var reader = new StreamReader(fileName, Encoding)) - { - return reader.ReadToEnd(); - } -#else return File.ReadAllText(fileName, Encoding); -#endif } catch (Exception exception) { diff --git a/src/NLog/LayoutRenderers/HostNameLayoutRenderer.cs b/src/NLog/LayoutRenderers/HostNameLayoutRenderer.cs index d7301b9cd0..681fbdd2dd 100644 --- a/src/NLog/LayoutRenderers/HostNameLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/HostNameLayoutRenderer.cs @@ -84,9 +84,7 @@ private static string GetHostName() { return TryLookupValue(() => Environment.GetEnvironmentVariable("HOSTNAME"), "HOSTNAME") ?? TryLookupValue(() => System.Net.Dns.GetHostName(), "DnsHostName") -#if !NETSTANDARD1_3 ?? TryLookupValue(() => Environment.MachineName, "MachineName") -#endif ?? TryLookupValue(() => Environment.GetEnvironmentVariable("MACHINENAME"), "MachineName"); } diff --git a/src/NLog/LayoutRenderers/IdentityLayoutRenderer.cs b/src/NLog/LayoutRenderers/IdentityLayoutRenderer.cs index f74c172829..f57888c6da 100644 --- a/src/NLog/LayoutRenderers/IdentityLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/IdentityLayoutRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.LayoutRenderers { using System.Security.Principal; @@ -112,5 +110,3 @@ private static IIdentity GetValue() } } } - -#endif diff --git a/src/NLog/LayoutRenderers/LocalIpAddressLayoutRenderer.cs b/src/NLog/LayoutRenderers/LocalIpAddressLayoutRenderer.cs index b661607403..e9119be571 100644 --- a/src/NLog/LayoutRenderers/LocalIpAddressLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LocalIpAddressLayoutRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.LayoutRenderers { using System; @@ -236,5 +234,3 @@ private static bool IsLoopbackAddressValue(IPAddress ipAddress) } } } - -#endif diff --git a/src/NLog/LayoutRenderers/Log4JXmlEventLayoutRenderer.cs b/src/NLog/LayoutRenderers/Log4JXmlEventLayoutRenderer.cs index 4ba5e6b5b3..400a50ad21 100644 --- a/src/NLog/LayoutRenderers/Log4JXmlEventLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/Log4JXmlEventLayoutRenderer.cs @@ -79,16 +79,11 @@ public Log4JXmlEventLayoutRenderer() : this(LogFactory.DefaultAppEnvironment) /// internal Log4JXmlEventLayoutRenderer(IAppEnvironment appEnvironment) { - -#if NETSTANDARD1_3 - AppInfo = "NetCore Application"; -#else AppInfo = string.Format( CultureInfo.InvariantCulture, "{0}({1})", appEnvironment.AppDomainFriendlyName, appEnvironment.CurrentProcessId); -#endif Parameters = new List(); @@ -433,7 +428,7 @@ private void AppendCallSite(LogEventInfo logEvent, XmlWriter xtw) var type = methodBase?.DeclaringType; if (type != null) { - xtw.WriteAttributeSafeString("assembly", type.GetAssembly().FullName); + xtw.WriteAttributeSafeString("assembly", type.Assembly.FullName); } xtw.WriteEndElement(); diff --git a/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs index 763f439645..1c988bd4ea 100644 --- a/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.LayoutRenderers { using System; @@ -92,7 +90,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) private static string ResolveNLogDir() { - var nlogAssembly = typeof(LogFactory).GetAssembly(); + var nlogAssembly = typeof(LogFactory).Assembly; if (!string.IsNullOrEmpty(nlogAssembly.Location)) { return Path.GetDirectoryName(nlogAssembly.Location); @@ -106,5 +104,3 @@ private static string ResolveNLogDir() } } } - -#endif diff --git a/src/NLog/LayoutRenderers/ProcessDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessDirLayoutRenderer.cs index c6a75cef91..b1ec790108 100644 --- a/src/NLog/LayoutRenderers/ProcessDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessDirLayoutRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.LayoutRenderers { using System; @@ -93,5 +91,3 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) } } } - -#endif diff --git a/src/NLog/LayoutRenderers/ProcessIdLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessIdLayoutRenderer.cs index 4edd2824eb..681fd09b0c 100644 --- a/src/NLog/LayoutRenderers/ProcessIdLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessIdLayoutRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.LayoutRenderers { using System.Text; @@ -83,5 +81,3 @@ bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) } } } - -#endif diff --git a/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs index 99777fb930..cd1c781001 100644 --- a/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs @@ -31,14 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.LayoutRenderers { using System; using System.Diagnostics; using System.Globalization; - using System.Reflection; using System.Text; using NLog.Config; using NLog.Internal; @@ -115,5 +112,3 @@ private object GetValue() } } } - -#endif diff --git a/src/NLog/LayoutRenderers/ProcessNameLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessNameLayoutRenderer.cs index da89d4790e..1b531fe253 100644 --- a/src/NLog/LayoutRenderers/ProcessNameLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessNameLayoutRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.LayoutRenderers { using System.Text; @@ -85,5 +83,3 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) } } } - -#endif diff --git a/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs index 613ef87e2b..5b3cd50b85 100644 --- a/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs @@ -86,9 +86,7 @@ internal static void WriteTimestamp(StringBuilder builder, TimeSpan ts, CultureI string ticksSeparator = "."; if (!ReferenceEquals(culture, CultureInfo.InvariantCulture)) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 timeSeparator = culture.DateTimeFormat.TimeSeparator; -#endif ticksSeparator = culture.NumberFormat.NumberDecimalSeparator; } diff --git a/src/NLog/LayoutRenderers/SpecialFolderApplicationDataLayoutRenderer.cs b/src/NLog/LayoutRenderers/SpecialFolderApplicationDataLayoutRenderer.cs index c596f4a51e..a29178c2b4 100644 --- a/src/NLog/LayoutRenderers/SpecialFolderApplicationDataLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/SpecialFolderApplicationDataLayoutRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.LayoutRenderers { using System; @@ -52,5 +50,3 @@ public SpecialFolderApplicationDataLayoutRenderer() } } } - -#endif diff --git a/src/NLog/LayoutRenderers/SpecialFolderCommonApplicationDataLayoutRenderer.cs b/src/NLog/LayoutRenderers/SpecialFolderCommonApplicationDataLayoutRenderer.cs index 28ba4143cf..32836a9117 100644 --- a/src/NLog/LayoutRenderers/SpecialFolderCommonApplicationDataLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/SpecialFolderCommonApplicationDataLayoutRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.LayoutRenderers { using System; @@ -52,5 +50,3 @@ public SpecialFolderCommonApplicationDataLayoutRenderer() } } } - -#endif diff --git a/src/NLog/LayoutRenderers/SpecialFolderLayoutRenderer.cs b/src/NLog/LayoutRenderers/SpecialFolderLayoutRenderer.cs index d1917b6d5e..777ac6d3da 100644 --- a/src/NLog/LayoutRenderers/SpecialFolderLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/SpecialFolderLayoutRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.LayoutRenderers { using System; @@ -147,5 +145,3 @@ private static string GetFolderWindowsEnvironmentVariable(Environment.SpecialFol } } } - -#endif diff --git a/src/NLog/LayoutRenderers/SpecialFolderLocalApplicationDataLayoutRenderer.cs b/src/NLog/LayoutRenderers/SpecialFolderLocalApplicationDataLayoutRenderer.cs index 42790c6be3..cfca115f6d 100644 --- a/src/NLog/LayoutRenderers/SpecialFolderLocalApplicationDataLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/SpecialFolderLocalApplicationDataLayoutRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.LayoutRenderers { using System; @@ -52,5 +50,3 @@ public SpecialFolderLocalApplicationDataLayoutRenderer() } } } - -#endif diff --git a/src/NLog/LayoutRenderers/ThreadNameLayoutRenderer.cs b/src/NLog/LayoutRenderers/ThreadNameLayoutRenderer.cs index f267ffbb14..8374a63e0e 100644 --- a/src/NLog/LayoutRenderers/ThreadNameLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ThreadNameLayoutRenderer.cs @@ -54,11 +54,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) private static string GetStringValue() { -#if !NETSTANDARD1_3 return System.Threading.Thread.CurrentThread.Name; -#else - return string.Empty; -#endif } string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) => GetStringValue(); diff --git a/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs b/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs index afc560cc8b..dde0e14bd1 100644 --- a/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs @@ -80,9 +80,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) string ticksSeparator = "."; if (!ReferenceEquals(culture, CultureInfo.InvariantCulture)) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 timeSeparator = culture.DateTimeFormat.TimeSeparator; -#endif ticksSeparator = culture.NumberFormat.NumberDecimalSeparator; } diff --git a/src/NLog/LayoutRenderers/TraceActivityIdLayoutRenderer.cs b/src/NLog/LayoutRenderers/TraceActivityIdLayoutRenderer.cs index cd7546b29a..9407e9c552 100644 --- a/src/NLog/LayoutRenderers/TraceActivityIdLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/TraceActivityIdLayoutRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.LayoutRenderers { using System; @@ -75,5 +73,3 @@ private static Guid GetValue() } } } - -#endif diff --git a/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs index 3606d646cd..ff1d4637ea 100644 --- a/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs @@ -93,26 +93,9 @@ protected override string Transform(string text) private void TransformToLowerCase(StringBuilder target, int startPos) { CultureInfo culture = Culture; - -#if NETSTANDARD1_3 || NETSTANDARD1_5 - string stringToLower = null; - if (culture != null && culture != CultureInfo.InvariantCulture) - { - stringToLower = target.ToString(startPos, target.Length - startPos); - stringToLower = culture.TextInfo.ToLower(stringToLower); - } -#endif - for (int i = startPos; i < target.Length; ++i) { -#if NETSTANDARD1_3 || NETSTANDARD1_5 - if (stringToLower != null) - target[i] = stringToLower[i]; //no char.ToLower with culture - else - target[i] = char.ToLowerInvariant(target[i]); -#else target[i] = char.ToLower(target[i], culture); -#endif } } } diff --git a/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs index 540cc20f05..c1ff52fa1c 100644 --- a/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs @@ -98,26 +98,9 @@ protected override string Transform(string text) private void TransformToUpperCase(StringBuilder target, int startPos) { CultureInfo culture = Culture; - -#if NETSTANDARD1_3 || NETSTANDARD1_5 - string stringToUpper = null; - if (culture != null && culture != CultureInfo.InvariantCulture) - { - stringToUpper = target.ToString(startPos, target.Length - startPos); - stringToUpper = culture.TextInfo.ToUpper(stringToUpper); - } -#endif - for (int i = startPos; i < target.Length; ++i) { -#if NETSTANDARD1_3 || NETSTANDARD1_5 - if (stringToUpper != null) - target[i] = stringToUpper[i]; //no char.ToUpper with culture - else - target[i] = char.ToUpperInvariant(target[i]); -#else target[i] = char.ToUpper(target[i], culture); -#endif } } } diff --git a/src/NLog/Layouts/Layout.cs b/src/NLog/Layouts/Layout.cs index 4ed95ac3f9..a393f76b65 100644 --- a/src/NLog/Layouts/Layout.cs +++ b/src/NLog/Layouts/Layout.cs @@ -147,11 +147,7 @@ public static Layout FromMethod(Func layoutMethod, LayoutR { Guard.ThrowIfNull(layoutMethod); -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 var name = $"{layoutMethod.Method?.DeclaringType?.ToString()}.{layoutMethod.Method?.Name}"; -#else - var name = $"{layoutMethod.Target?.ToString()}"; -#endif var layoutRenderer = CreateFuncLayoutRenderer((l, c) => layoutMethod(l), options, name); return new SimpleLayout(new[] { layoutRenderer }, layoutRenderer.LayoutRendererName, ConfigurationItemFactory.Default); } diff --git a/src/NLog/Layouts/LayoutParser.cs b/src/NLog/Layouts/LayoutParser.cs index 746d64512c..faf2be1acb 100644 --- a/src/NLog/Layouts/LayoutParser.cs +++ b/src/NLog/Layouts/LayoutParser.cs @@ -510,7 +510,7 @@ private static object ParseLayoutRendererPropertyValue(ConfigurationItemFactory LayoutRenderer[] renderers = CompileLayout(configurationItemFactory, stringReader, throwConfigExceptions, true, out var txt); Layout nestedLayout = new SimpleLayout(renderers, txt, configurationItemFactory); - if (propertyInfo.PropertyType.IsGenericType() && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Layout<>)) + if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Layout<>)) { var concreteType = typeof(Layout<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments()); nestedLayout = (Layout)Activator.CreateInstance(concreteType, BindingFlags.Instance | BindingFlags.Public, null, new object[] { nestedLayout }, null); diff --git a/src/NLog/Layouts/SimpleLayout.cs b/src/NLog/Layouts/SimpleLayout.cs index fb7441b695..d3a9b3701d 100644 --- a/src/NLog/Layouts/SimpleLayout.cs +++ b/src/NLog/Layouts/SimpleLayout.cs @@ -369,7 +369,7 @@ private bool IsRawValueImmutable(LogEventInfo logEvent) private static bool IsRawValueImmutable(object value) { - return value != null && (Convert.GetTypeCode(value) != TypeCode.Object || value.GetType().IsValueType()); + return value != null && (Convert.GetTypeCode(value) != TypeCode.Object || value.GetType().IsValueType); } /// diff --git a/src/NLog/Layouts/Typed/Layout.cs b/src/NLog/Layouts/Typed/Layout.cs index 8bf328846d..9f073f7b0e 100644 --- a/src/NLog/Layouts/Typed/Layout.cs +++ b/src/NLog/Layouts/Typed/Layout.cs @@ -255,7 +255,7 @@ private sealed class LayoutGenericTypeValue : LayoutTypeValue, ILayoutTypeValue< public override IPropertyTypeConverter ValueTypeConverter => _ownerLayout.ValueTypeConverter; public LayoutGenericTypeValue(Layout layout, string parseValueFormat, CultureInfo parseValueCulture, Layout ownerLayout) - :base(layout, typeof(T), parseValueFormat, parseValueCulture, null) + : base(layout, typeof(T), parseValueFormat, parseValueCulture, null) { _ownerLayout = ownerLayout; } @@ -421,7 +421,7 @@ public override int GetHashCode() /// Text to be converted. public static implicit operator Layout(T value) { - if (object.Equals(value, default(T)) && !typeof(T).IsValueType()) return null; + if (object.Equals(value, default(T)) && !typeof(T).IsValueType) return null; return new Layout(value); } diff --git a/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs b/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs index c0c55e5d5b..6ed93bf295 100644 --- a/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs +++ b/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs @@ -85,7 +85,7 @@ public Type ValueType set { _valueType = value; - if (value?.IsValueType() == true) + if (value?.IsValueType == true) _createDefaultValue = () => Activator.CreateInstance(value); else _createDefaultValue = null; @@ -125,7 +125,7 @@ private static bool UseDefaultWhenEmptyString(Type valueType, Layout defaultValu } return true; } - + /// /// Gets or sets the fallback value should be null (instead of default value of ) when result value is not available /// @@ -212,7 +212,7 @@ private ILayoutTypeValue BuildLayoutTypeValue(Layout layout) } else { - layout = string.Empty; + layout = string.Empty; } } diff --git a/src/NLog/LogFactory-T.cs b/src/NLog/LogFactory-T.cs index b86c9e9619..dbd2649b5a 100644 --- a/src/NLog/LogFactory-T.cs +++ b/src/NLog/LogFactory-T.cs @@ -63,11 +63,7 @@ public class LogFactory : LogFactory [MethodImpl(MethodImplOptions.NoInlining)] public new T GetCurrentClassLogger() { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 var className = Internal.StackTraceUsageUtils.GetClassFullName(new StackFrame(1, false)); -#else - var className = Internal.StackTraceUsageUtils.GetClassFullName(); -#endif return GetLogger(className); } } diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 6861832c2d..4526a4ccb1 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -36,12 +36,9 @@ namespace NLog using System; using System.Collections.Generic; using System.ComponentModel; - using System.Diagnostics; using System.Globalization; using System.Linq; - using System.Reflection; using System.Runtime.CompilerServices; - using System.Security; using System.Text; using System.Threading; using JetBrains.Annotations; @@ -92,7 +89,6 @@ public class LogFactory : IDisposable /// public event EventHandler ConfigurationChanged; -#if !NETSTANDARD1_3 /// /// Obsolete and replaced by with NLog v5.2. /// Occurs when logging gets reloaded. @@ -100,11 +96,9 @@ public class LogFactory : IDisposable [Obsolete("Replaced by ConfigurationChanged, but check args.ActivatedConfiguration != null. Marked obsolete on NLog 5.2")] [EditorBrowsable(EditorBrowsableState.Never)] public event EventHandler ConfigurationReloaded; -#endif private static event EventHandler LoggerShutdown; -#if !NETSTANDARD1_3 /// /// Initializes static members of the LogManager class. /// @@ -112,19 +106,12 @@ static LogFactory() { RegisterEvents(DefaultAppEnvironment); } -#endif /// /// Initializes a new instance of the class. /// public LogFactory() -#pragma warning disable CS0618 // Type or member is obsolete -#if !NETSTANDARD1_3 : this(new LoggingConfigurationWatchableFileLoader(DefaultAppEnvironment)) -#else - : this(new LoggingConfigurationFileLoader(DefaultAppEnvironment)) -#endif -#pragma warning restore CS0618 // Type or member is obsolete { _serviceRepository.TypeRegistered += ServiceRepository_TypeRegistered; RefreshMessageFormatter(); @@ -153,9 +140,7 @@ internal LogFactory(ILoggingConfigurationLoader configLoader, IAppEnvironment ap { _configLoader = configLoader; _currentAppEnvironment = appEnvironment; -#if !NETSTANDARD1_3 LoggerShutdown += OnStopLogging; -#endif } /// @@ -188,11 +173,7 @@ internal static IAppEnvironment DefaultAppEnvironment { #pragma warning disable CS0618 // Type or member is obsolete return defaultAppEnvironment ?? (defaultAppEnvironment = new AppEnvironmentWrapper(currentAppDomain ?? (currentAppDomain = -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 new AppDomainWrapper(AppDomain.CurrentDomain) -#else - new FakeAppDomain() -#endif ))); #pragma warning restore CS0618 // Type or member is obsolete } @@ -240,11 +221,9 @@ public bool AutoShutdown if (value != _autoShutdown) { _autoShutdown = value; -#if !NETSTANDARD1_3 LoggerShutdown -= OnStopLogging; if (value) LoggerShutdown += OnStopLogging; -#endif } } } @@ -412,7 +391,7 @@ internal static void LogNLogAssemblyVersion() try { - InternalLogger.LogAssemblyVersion(typeof(LogFactory).GetAssembly()); + InternalLogger.LogAssemblyVersion(typeof(LogFactory).Assembly); } catch (Exception ex) { @@ -465,11 +444,7 @@ public Logger CreateNullLogger() [MethodImpl(MethodImplOptions.NoInlining)] public Logger GetCurrentClassLogger() { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - var className = StackTraceUsageUtils.GetClassFullName(new StackFrame(1, false)); -#else - var className = StackTraceUsageUtils.GetClassFullName(); -#endif + var className = StackTraceUsageUtils.GetClassFullName(new System.Diagnostics.StackFrame(1, false)); return GetLogger(className); } @@ -485,11 +460,7 @@ public Logger GetCurrentClassLogger() [MethodImpl(MethodImplOptions.NoInlining)] public T GetCurrentClassLogger() where T : Logger, new() { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - var className = StackTraceUsageUtils.GetClassFullName(new StackFrame(1, false)); -#else - var className = StackTraceUsageUtils.GetClassFullName(); -#endif + var className = StackTraceUsageUtils.GetClassFullName(new System.Diagnostics.StackFrame(1, false)); return GetLogger(className); } @@ -508,11 +479,7 @@ public Logger GetCurrentClassLogger() [EditorBrowsable(EditorBrowsableState.Never)] public Logger GetCurrentClassLogger([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembers(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type loggerType) { -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - var className = StackTraceUsageUtils.GetClassFullName(new StackFrame(1, false)); -#else - var className = StackTraceUsageUtils.GetClassFullName(); -#endif + var className = StackTraceUsageUtils.GetClassFullName(new System.Diagnostics.StackFrame(1, false)); return GetLogger(className, loggerType ?? typeof(Logger)); } @@ -756,7 +723,6 @@ protected virtual void OnConfigurationChanged(LoggingConfigurationChangedEventAr ConfigurationChanged?.Invoke(this, e); } -#if !NETSTANDARD1_3 /// /// Obsolete and replaced by with NLog 5.2. /// @@ -774,7 +740,6 @@ internal void NotifyConfigurationReloaded(LoggingConfigurationReloadedEventArgs { OnConfigurationReloaded(eventArgs); } -#endif /// /// Change this method with NLog v6 to completely disconnect LogFactory from Targets/Layouts @@ -803,10 +768,8 @@ private void Close(TimeSpan flushTimeout) _serviceRepository.TypeRegistered -= ServiceRepository_TypeRegistered; -#if !NETSTANDARD1_3 LoggerShutdown -= OnStopLogging; ConfigurationReloaded = null; // Release event listeners -#endif if (Monitor.TryEnter(_syncRoot, 500)) { @@ -1298,9 +1261,7 @@ private void OnStopLogging(object sender, EventArgs args) // Domain-Unload has to complete in about 2 secs on Windows-platform, before being terminated. // Other platforms like Linux will fail when trying to spin up new threads at domain unload. var flushTimeout = -#if !NETSTANDARD1_3 PlatformDetector.IsWin32 ? TimeSpan.FromMilliseconds(1500) : -#endif TimeSpan.Zero; Close(flushTimeout); InternalLogger.Info("LogFactory has been closed."); diff --git a/src/NLog/LogManager.cs b/src/NLog/LogManager.cs index 0c5279d6fd..5c6820d951 100644 --- a/src/NLog/LogManager.cs +++ b/src/NLog/LogManager.cs @@ -71,7 +71,6 @@ public static event EventHandler Configura remove => factory.ConfigurationChanged -= value; } -#if !NETSTANDARD1_3 /// /// Obsolete and replaced by with NLog v5.2. /// Occurs when logging gets reloaded. @@ -83,7 +82,7 @@ public static event EventHandler Configur add => factory.ConfigurationReloaded += value; remove => factory.ConfigurationReloaded -= value; } -#endif + /// /// Gets or sets a value indicating whether NLog should throw exceptions. /// By default exceptions are not thrown under any circumstances. @@ -200,7 +199,8 @@ public static void AddHiddenAssembly(Assembly assembly) [MethodImpl(MethodImplOptions.NoInlining)] public static Logger GetCurrentClassLogger() { - return factory.GetLogger(StackTraceUsageUtils.GetClassFullName()); + var className = StackTraceUsageUtils.GetClassFullName(new System.Diagnostics.StackFrame(1, false)); + return factory.GetLogger(className); } /// @@ -219,7 +219,8 @@ public static Logger GetCurrentClassLogger() [EditorBrowsable(EditorBrowsableState.Never)] public static Logger GetCurrentClassLogger([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type loggerType) { - return factory.GetLogger(StackTraceUsageUtils.GetClassFullName(), loggerType); + var className = StackTraceUsageUtils.GetClassFullName(new System.Diagnostics.StackFrame(1, false)); + return factory.GetLogger(className, loggerType); } /// diff --git a/src/NLog/LoggerImpl.cs b/src/NLog/LoggerImpl.cs index 4117a614ee..04595e409b 100644 --- a/src/NLog/LoggerImpl.cs +++ b/src/NLog/LoggerImpl.cs @@ -31,10 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 -#define CaptureCallSiteInfo -#endif - namespace NLog { using System; @@ -57,7 +53,6 @@ internal static void Write([NotNull] Type loggerType, [NotNull] TargetWithFilter { logEvent.SetMessageFormatter(logFactory.ActiveMessageFormatter, targetsForLevel.NextInChain is null ? logFactory.SingleTargetMessageFormatter : null); -#if CaptureCallSiteInfo StackTraceUsage stu = targetsForLevel.StackTraceUsage; if (stu != StackTraceUsage.None) { @@ -76,7 +71,6 @@ internal static void Write([NotNull] Type loggerType, [NotNull] TargetWithFilter } } } -#endif AsyncContinuation exceptionHandler = SingleCallContinuation.Completed; if (logFactory.ThrowExceptions) @@ -108,18 +102,12 @@ internal static void Write([NotNull] Type loggerType, [NotNull] TargetWithFilter } } -#if CaptureCallSiteInfo private static void CaptureCallSiteInfo(LogFactory logFactory, Type loggerType, LogEventInfo logEvent, StackTraceUsage stackTraceUsage) { try { bool includeSource = (stackTraceUsage & StackTraceUsage.WithFileNameAndLineNumber) != 0; -#if NETSTANDARD1_5 - var stackTrace = (StackTrace)Activator.CreateInstance(typeof(StackTrace), new object[] { includeSource }); -#else var stackTrace = new StackTrace(StackTraceSkipMethods, includeSource); -#endif - logEvent.GetCallSiteInformationInternal().SetStackTrace(stackTrace, null, loggerType); } catch (Exception ex) @@ -135,7 +123,6 @@ private static void CaptureCallSiteInfo(LogFactory logFactory, Type loggerType, InternalLogger.Error(ex, "{0} Failed to capture CallSite. Platform might not support ${{callsite}}", logEvent.LoggerName); } } -#endif private static bool WriteToTargetWithFilterChain(Targets.Target target, FilterResult result, LogEventInfo logEvent, AsyncContinuation onException) { diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index 27457a15d6..6bd7e538fe 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -1,7 +1,7 @@ - net35;net46;netstandard1.3;netstandard1.5;netstandard2.0 + net35;net46;netstandard2.0 NLog for .NET Framework and .NET Standard NLog @@ -88,16 +88,6 @@ For all config options and platform support, check https://nlog-project.org/conf Full - - NLog for NetStandard 1.3 - 1.6.0 - - - - NLog for NetStandard 1.5 - 1.6.0 - - NLog for NetStandard 2.0 @@ -140,37 +130,6 @@ For all config options and platform support, check https://nlog-project.org/conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/NLog/NLogTraceListener.cs b/src/NLog/NLogTraceListener.cs index 3b1fc06eca..84cfed5b36 100644 --- a/src/NLog/NLogTraceListener.cs +++ b/src/NLog/NLogTraceListener.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog { using System; @@ -589,5 +587,3 @@ private void InitAttributes() } } } - -#endif diff --git a/src/NLog/Properties/AssemblyInfo.cs b/src/NLog/Properties/AssemblyInfo.cs index 3fabab2e0a..18f79e01df 100644 --- a/src/NLog/Properties/AssemblyInfo.cs +++ b/src/NLog/Properties/AssemblyInfo.cs @@ -44,7 +44,7 @@ [assembly: ComVisible(false)] [assembly: InternalsVisibleTo("NLog.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100ef8eab4fbdeb511eeb475e1659fe53f00ec1c1340700f1aa347bf3438455d71993b28b1efbed44c8d97a989e0cb6f01bcb5e78f0b055d311546f63de0a969e04cf04450f43834db9f909e566545a67e42822036860075a1576e90e1c43d43e023a24c22a427f85592ae56cac26f13b7ec2625cbc01f9490d60f16cfbb1bc34d9")] [assembly: AllowPartiallyTrustedCallers] -#if !NET35 && !NETSTANDARD1_3 && !NETSTANDARD1_5 +#if !NET35 [assembly: SecurityRules(SecurityRuleSet.Level1)] #endif // NSubstitute diff --git a/src/NLog/SetupBuilderExtensions.cs b/src/NLog/SetupBuilderExtensions.cs index 01de2e570a..cda9b10d5e 100644 --- a/src/NLog/SetupBuilderExtensions.cs +++ b/src/NLog/SetupBuilderExtensions.cs @@ -51,7 +51,8 @@ public static class SetupBuilderExtensions [MethodImpl(MethodImplOptions.NoInlining)] public static Logger GetCurrentClassLogger(this ISetupBuilder setupBuilder) { - return setupBuilder.LogFactory.GetLogger(StackTraceUsageUtils.GetClassFullName()); + var className = StackTraceUsageUtils.GetClassFullName(new System.Diagnostics.StackFrame(1, false)); + return setupBuilder.LogFactory.GetLogger(className); } /// diff --git a/src/NLog/SetupInternalLoggerBuilderExtensions.cs b/src/NLog/SetupInternalLoggerBuilderExtensions.cs index 5c757d9aea..94920d6142 100644 --- a/src/NLog/SetupInternalLoggerBuilderExtensions.cs +++ b/src/NLog/SetupInternalLoggerBuilderExtensions.cs @@ -33,8 +33,6 @@ namespace NLog { - using System; - using System.ComponentModel; using System.IO; using NLog.Common; using NLog.Config; diff --git a/src/NLog/SetupLoadConfigurationExtensions.cs b/src/NLog/SetupLoadConfigurationExtensions.cs index 179b689467..45ed67615e 100644 --- a/src/NLog/SetupLoadConfigurationExtensions.cs +++ b/src/NLog/SetupLoadConfigurationExtensions.cs @@ -437,7 +437,6 @@ public static ISetupConfigurationTargetBuilder WriteToMethodCall(this ISetupConf return configBuilder.WriteTo(methodTarget); } -#if !NETSTANDARD1_3 /// /// Write to /// @@ -537,7 +536,6 @@ public static ISetupConfigurationTargetBuilder WriteToTrace(this ISetupConfigura traceTarget.Layout = layout; return configBuilder.WriteTo(traceTarget); } -#endif /// /// Write to diff --git a/src/NLog/SetupSerializationBuilderExtensions.cs b/src/NLog/SetupSerializationBuilderExtensions.cs index 1855f44566..61d55cc921 100644 --- a/src/NLog/SetupSerializationBuilderExtensions.cs +++ b/src/NLog/SetupSerializationBuilderExtensions.cs @@ -34,7 +34,6 @@ namespace NLog { using System; - using System.Reflection; using NLog.Config; using NLog.Internal; diff --git a/src/NLog/Targets/AsyncTaskTarget.cs b/src/NLog/Targets/AsyncTaskTarget.cs index d70387a3e9..9883bdaa6e 100644 --- a/src/NLog/Targets/AsyncTaskTarget.cs +++ b/src/NLog/Targets/AsyncTaskTarget.cs @@ -86,7 +86,7 @@ public abstract class AsyncTaskTarget : TargetWithContext private readonly Timer _lazyWriterTimer; private readonly ReusableAsyncLogEventList _reusableAsyncLogEventList = new ReusableAsyncLogEventList(200); private Tuple, List> _reusableLogEvents; - private AsyncHelpersTask? _flushEventsInQueueDelegate; + private WaitCallback _flushEventsInQueueDelegate; private bool _missingServiceTypes; /// @@ -396,14 +396,14 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation) // We are holding target-SyncRoot, and might get blocked in queue, so need to schedule flush using async-task if (_flushEventsInQueueDelegate is null) { - _flushEventsInQueueDelegate = new AsyncHelpersTask(cont => + _flushEventsInQueueDelegate = (cont) => { _requestQueue.Enqueue(new AsyncLogEventInfo(null, (AsyncContinuation)cont)); lock (SyncRoot) _lazyWriterTimer.Change(0, Timeout.Infinite); // Schedule timer to empty queue, and execute asyncContinuation - }); + }; } - AsyncHelpers.StartAsyncTask(_flushEventsInQueueDelegate.Value, asyncContinuation); + AsyncHelpers.StartAsyncTask(_flushEventsInQueueDelegate, asyncContinuation); _lazyWriterTimer.Change(0, Timeout.Infinite); // Schedule timer to empty queue, so room for flush-request } } diff --git a/src/NLog/Targets/ColoredConsoleAnsiPrinter.cs b/src/NLog/Targets/ColoredConsoleAnsiPrinter.cs index 8b66bc3fab..ddf210f32b 100644 --- a/src/NLog/Targets/ColoredConsoleAnsiPrinter.cs +++ b/src/NLog/Targets/ColoredConsoleAnsiPrinter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.Targets { using System; @@ -222,5 +220,3 @@ private static string TerminalDefaultColorEscapeCode }; } } - -#endif diff --git a/src/NLog/Targets/ColoredConsoleSystemPrinter.cs b/src/NLog/Targets/ColoredConsoleSystemPrinter.cs index b42abbf87e..45ee06a081 100644 --- a/src/NLog/Targets/ColoredConsoleSystemPrinter.cs +++ b/src/NLog/Targets/ColoredConsoleSystemPrinter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.Targets { using System; @@ -112,5 +110,3 @@ public void WriteLine(TextWriter consoleWriter, string text) }; } } - -#endif diff --git a/src/NLog/Targets/ColoredConsoleTarget.cs b/src/NLog/Targets/ColoredConsoleTarget.cs index 5e686008cb..2c1d921aee 100644 --- a/src/NLog/Targets/ColoredConsoleTarget.cs +++ b/src/NLog/Targets/ColoredConsoleTarget.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.Targets { using System; @@ -633,5 +631,3 @@ private TextWriter GetOutput() } } } - -#endif diff --git a/src/NLog/Targets/ConsoleRowHighlightingRule.cs b/src/NLog/Targets/ConsoleRowHighlightingRule.cs index 45c30e4fd2..84861aaa32 100644 --- a/src/NLog/Targets/ConsoleRowHighlightingRule.cs +++ b/src/NLog/Targets/ConsoleRowHighlightingRule.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.Targets { using NLog.Conditions; @@ -105,5 +103,3 @@ public bool CheckCondition(LogEventInfo logEvent) } } } - -#endif diff --git a/src/NLog/Targets/ConsoleTarget.cs b/src/NLog/Targets/ConsoleTarget.cs index 8775ad0baf..140a9f0219 100644 --- a/src/NLog/Targets/ConsoleTarget.cs +++ b/src/NLog/Targets/ConsoleTarget.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.Targets { using System; @@ -366,5 +364,3 @@ private TextWriter GetOutput() } } } - -#endif diff --git a/src/NLog/Targets/ConsoleTargetHelper.cs b/src/NLog/Targets/ConsoleTargetHelper.cs index d565fdd7c7..0ca0727c9d 100644 --- a/src/NLog/Targets/ConsoleTargetHelper.cs +++ b/src/NLog/Targets/ConsoleTargetHelper.cs @@ -45,7 +45,7 @@ internal static class ConsoleTargetHelper public static bool IsConsoleAvailable(out string reason) { reason = string.Empty; -#if !MONO && !NETSTANDARD1_3 && !NETSTANDARD1_5 +#if !MONO try { if (!Environment.UserInteractive) @@ -76,24 +76,16 @@ public static bool IsConsoleAvailable(out string reason) public static Encoding GetConsoleOutputEncoding(Encoding currentEncoding, bool isInitialized, bool pauseLogging) { -#if !NETSTANDARD1_3 if (currentEncoding != null) return currentEncoding; else if ((isInitialized && !pauseLogging) || IsConsoleAvailable(out _)) return Console.OutputEncoding; -#if !NETSTANDARD1_5 + return Encoding.Default; -#else - return currentEncoding; -#endif -#else - return currentEncoding; -#endif } public static bool SetConsoleOutputEncoding(Encoding newEncoding, bool isInitialized, bool pauseLogging) { -#if !NETSTANDARD1_3 if (!isInitialized) { return true; // Waiting for console target to be initialized @@ -110,7 +102,7 @@ public static bool SetConsoleOutputEncoding(Encoding newEncoding, bool isInitial InternalLogger.Warn(ex, "Failed changing Console.OutputEncoding to {0}", newEncoding); } } -#endif + return false; // No console available } diff --git a/src/NLog/Targets/ConsoleWordHighlightingRule.cs b/src/NLog/Targets/ConsoleWordHighlightingRule.cs index 91c5f8293a..4a15ed2dc9 100644 --- a/src/NLog/Targets/ConsoleWordHighlightingRule.cs +++ b/src/NLog/Targets/ConsoleWordHighlightingRule.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.Targets { using System.Text.RegularExpressions; @@ -154,5 +152,3 @@ internal MatchCollection Matches(LogEventInfo logEvent, string message) } } } - -#endif diff --git a/src/NLog/Targets/DebuggerTarget.cs b/src/NLog/Targets/DebuggerTarget.cs index f452a20b49..0b8942427c 100644 --- a/src/NLog/Targets/DebuggerTarget.cs +++ b/src/NLog/Targets/DebuggerTarget.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.Targets { using System.Diagnostics; @@ -125,5 +123,3 @@ protected override void Write(LogEventInfo logEvent) } } } - -#endif diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index 4af62c96d1..6d6c043136 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -31,10 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 -#define SupportsMutex -#endif - namespace NLog.Targets { using System; @@ -703,13 +699,11 @@ public bool EnableArchiveFileCompression /// public bool ForceManaged { get; set; } = true; -#if SupportsMutex /// /// Gets or sets a value indicating whether file creation calls should be synchronized by a system global mutex. /// /// public bool ForceMutexConcurrentWrites { get; set; } -#endif /// /// Gets or sets a value indicating whether the footer should be written only when the file is archived. @@ -738,7 +732,6 @@ private void RefreshArchiveFilePatternToWatch(string fileName, LogEventInfo logE _fileAppenderCache.CheckCloseAppenders += AutoCloseAppendersAfterArchive; // Activates FileSystemWatcher } -#if !NETSTANDARD1_3 if (mustWatchArchiving) { string archiveFilePattern = GetArchiveFileNamePattern(fileName, logEvent); @@ -751,7 +744,6 @@ private void RefreshArchiveFilePatternToWatch(string fileName, LogEventInfo logE { _fileAppenderCache.ArchiveFilePatternToWatch = null; } -#endif } /// @@ -862,7 +854,6 @@ private IFileAppenderFactory GetFileAppenderFactory() } else if (ConcurrentWrites) { -#if SupportsMutex if (!ForceMutexConcurrentWrites) { #if MONO @@ -883,7 +874,6 @@ private IFileAppenderFactory GetFileAppenderFactory() return MutexMultiProcessFileAppender.TheFactory; } else -#endif // SupportsMutex { return RetryingMultiProcessFileAppender.TheFactory; } @@ -1760,10 +1750,8 @@ private bool TryArchiveFile(string fileName, LogEventInfo ev, int upcomingWriteS archivedAppender = TryCloseFileAppenderBeforeArchive(fileName, archiveFile); } -#if !NETSTANDARD1_3 // Closes all file handles if any archive operation has been detected by file-watcher _fileAppenderCache.InvalidateAppendersForArchivedFiles(); -#endif } catch (Exception exception) { @@ -1779,7 +1767,6 @@ private bool TryArchiveFile(string fileName, LogEventInfo ev, int upcomingWriteS try { -#if SupportsMutex try { if (archivedAppender is BaseMutexFileAppender mutexFileAppender && mutexFileAppender.ArchiveMutex != null) @@ -1797,17 +1784,15 @@ private bool TryArchiveFile(string fileName, LogEventInfo ev, int upcomingWriteS // the mutex has been acquired, so proceed to writing // See: https://msdn.microsoft.com/en-us/library/system.threading.abandonedmutexexception.aspx } -#endif ArchiveFileAfterCloseFileAppender(archivedAppender, archiveFile, ev, upcomingWriteSize, previousLogEventTimestamp); return true; } finally { -#if SupportsMutex if (archivedAppender is BaseMutexFileAppender mutexFileAppender) mutexFileAppender.ArchiveMutex?.ReleaseMutex(); -#endif + archivedAppender?.Dispose(); // Dispose of Archive Mutex } } diff --git a/src/NLog/Targets/IColoredConsolePrinter.cs b/src/NLog/Targets/IColoredConsolePrinter.cs index 56252124ad..d8301eb487 100644 --- a/src/NLog/Targets/IColoredConsolePrinter.cs +++ b/src/NLog/Targets/IColoredConsolePrinter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 - namespace NLog.Targets { using System; @@ -117,7 +115,4 @@ internal interface IColoredConsolePrinter /// IList DefaultConsoleRowHighlightingRules { get; } } - } - -#endif diff --git a/src/NLog/Targets/MailTarget.cs b/src/NLog/Targets/MailTarget.cs index b7ffb4fbbc..8d7e29cbb5 100644 --- a/src/NLog/Targets/MailTarget.cs +++ b/src/NLog/Targets/MailTarget.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.Targets { using System; @@ -640,5 +638,3 @@ private bool AddAddresses(MailAddressCollection mailAddressCollection, Layout la } } } - -#endif diff --git a/src/NLog/Targets/SmtpAuthenticationMode.cs b/src/NLog/Targets/SmtpAuthenticationMode.cs index 2360cac3fa..372545df99 100644 --- a/src/NLog/Targets/SmtpAuthenticationMode.cs +++ b/src/NLog/Targets/SmtpAuthenticationMode.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 - namespace NLog.Targets { /// @@ -56,5 +54,3 @@ public enum SmtpAuthenticationMode Ntlm, } } - -#endif diff --git a/src/NLog/Targets/TargetWithContext.cs b/src/NLog/Targets/TargetWithContext.cs index c9a19d8ddf..7f5c06f48c 100644 --- a/src/NLog/Targets/TargetWithContext.cs +++ b/src/NLog/Targets/TargetWithContext.cs @@ -551,7 +551,7 @@ protected virtual bool SerializeItemValue(LogEventInfo logEvent, string name, ob return true; } - if (value is string || Convert.GetTypeCode(value) != TypeCode.Object || value.GetType().IsValueType()) + if (value is string || Convert.GetTypeCode(value) != TypeCode.Object || value.GetType().IsValueType) { serializedValue = value; // Already immutable, snapshot is not needed return true; diff --git a/src/NLog/Targets/TraceTarget.cs b/src/NLog/Targets/TraceTarget.cs index 5eb0a00d5e..9a9ce2019b 100644 --- a/src/NLog/Targets/TraceTarget.cs +++ b/src/NLog/Targets/TraceTarget.cs @@ -33,8 +33,6 @@ #define TRACE -#if !NETSTANDARD1_3 - namespace NLog.Targets { using System.Diagnostics; @@ -166,5 +164,3 @@ protected override void Write(LogEventInfo logEvent) } } } - -#endif diff --git a/src/NLog/Targets/WebServiceTarget.cs b/src/NLog/Targets/WebServiceTarget.cs index 4580ecce28..682649cb50 100644 --- a/src/NLog/Targets/WebServiceTarget.cs +++ b/src/NLog/Targets/WebServiceTarget.cs @@ -100,11 +100,9 @@ public WebServiceTarget() Encoding = new UTF8Encoding(writeBOM); IncludeBOM = writeBOM; -#if NETSTANDARD1_3 || NETSTANDARD1_5 // NetCore1 throws PlatformNotSupportedException on WebRequest.GetSystemWebProxy, when using DefaultWebProxy // Net5 (or newer) will turn off Http-connection-pooling if not using DefaultWebProxy ProxyType = WebServiceProxyType.NoProxy; -#endif } /// @@ -290,13 +288,11 @@ protected override void DoInvoke(object[] parameters, AsyncLogEventInfo logEvent } } -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 var userAgent = RenderLogEvent(UserAgent, logEvent.LogEvent); if (!string.IsNullOrEmpty(userAgent)) { webRequest.UserAgent = userAgent; } -#endif } catch (Exception ex) { @@ -315,7 +311,6 @@ private HttpWebRequest CreateHttpWebRequest(Uri url) { case WebServiceProxyType.DefaultWebProxy: break; -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 case WebServiceProxyType.AutoProxy: if (_activeProxy.Value is null) { @@ -336,18 +331,15 @@ private HttpWebRequest CreateHttpWebRequest(Uri url) webRequest.Proxy = _activeProxy.Value; } break; -#endif default: webRequest.Proxy = null; break; } -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 if (PreAuthenticate || ProxyType == WebServiceProxyType.AutoProxy) { webRequest.PreAuthenticate = true; } -#endif return webRequest; } diff --git a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs index 0815ebc598..806df4eb65 100644 --- a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs @@ -253,11 +253,11 @@ public int QueueLimit protected override void FlushAsync(AsyncContinuation asyncContinuation) { if (_flushEventsInQueueDelegate is null) - _flushEventsInQueueDelegate = new AsyncHelpersTask(FlushEventsInQueue); - AsyncHelpers.StartAsyncTask(_flushEventsInQueueDelegate.Value, asyncContinuation); + _flushEventsInQueueDelegate = FlushEventsInQueue; + AsyncHelpers.StartAsyncTask(_flushEventsInQueueDelegate, asyncContinuation); } - private AsyncHelpersTask? _flushEventsInQueueDelegate; + private WaitCallback _flushEventsInQueueDelegate; /// /// Initializes the target by starting the lazy writer timer. diff --git a/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs b/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs index a6668271e3..9795de0fa3 100644 --- a/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs @@ -36,7 +36,6 @@ namespace NLog.Targets.Wrappers using System; using System.Threading; using NLog.Common; - using NLog.Internal; using NLog.Layouts; /// diff --git a/src/NLog/Time/CachedTimeSource.cs b/src/NLog/Time/CachedTimeSource.cs index c0f397fafe..04156d176e 100644 --- a/src/NLog/Time/CachedTimeSource.cs +++ b/src/NLog/Time/CachedTimeSource.cs @@ -64,9 +64,7 @@ private DateTime RetrieveFreshTime(int tickCount) { var freshTime = FreshTime; _lastTime = freshTime; // Assignment of 64 bit value is safe, also when 32bit Intel x86 that uses FPU-registers without tearing -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 System.Threading.Thread.MemoryBarrier(); // Make sure that the value written to _lastTime is visible to other threads -#endif _lastTicks = tickCount; return freshTime; } diff --git a/tests/NLog.UnitTests/Config/ExtensionTests.cs b/tests/NLog.UnitTests/Config/ExtensionTests.cs index 92fe502c9d..dfbcea52aa 100644 --- a/tests/NLog.UnitTests/Config/ExtensionTests.cs +++ b/tests/NLog.UnitTests/Config/ExtensionTests.cs @@ -37,7 +37,6 @@ namespace NLog.UnitTests.Config using System.IO; using System.Linq; using System.Reflection; - using System.Text.RegularExpressions; using MyExtensionNamespace; using NLog.Common; using NLog.Config; diff --git a/tests/NLog.UnitTests/Config/XmlConfigTests.cs b/tests/NLog.UnitTests/Config/XmlConfigTests.cs index f62b93f4bd..64d9ce090d 100644 --- a/tests/NLog.UnitTests/Config/XmlConfigTests.cs +++ b/tests/NLog.UnitTests/Config/XmlConfigTests.cs @@ -108,16 +108,13 @@ public void ParseNLogInternalLoggerPathTest() Assert.Contains(System.IO.Path.GetTempPath(), InternalLogger.LogFile); } -#if !NETSTANDARD1_3 using (new InternalLoggerScope()) { var xml = ""; var config = XmlLoggingConfiguration.CreateFromXmlString(xml); Assert.Contains(Path.GetDirectoryName(CurrentProcessPath), InternalLogger.LogFile); } -#endif -#if !NETSTANDARD1_3 && !NETSTANDARD1_5 using (new InternalLoggerScope()) { var xml = ""; @@ -138,7 +135,6 @@ public void ParseNLogInternalLoggerPathTest() var config = XmlLoggingConfiguration.CreateFromXmlString(xml); Assert.Contains(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), InternalLogger.LogFile); } -#endif using (new InternalLoggerScope()) { diff --git a/tests/NLog.UnitTests/Internal/ExceptionHelperTests.cs b/tests/NLog.UnitTests/Internal/ExceptionHelperTests.cs index 72da5a2703..4ffef6f4ed 100644 --- a/tests/NLog.UnitTests/Internal/ExceptionHelperTests.cs +++ b/tests/NLog.UnitTests/Internal/ExceptionHelperTests.cs @@ -44,10 +44,8 @@ namespace NLog.UnitTests.Internal public class ExceptionHelperTests : NLogTestBase { [Theory] -#if !NETSTANDARD1_5 [InlineData(typeof(StackOverflowException), true)] [InlineData(typeof(ThreadAbortException), true)] -#endif [InlineData(typeof(NLogConfigurationException), false)] [InlineData(typeof(Exception), false)] [InlineData(typeof(ArgumentException), false)] @@ -62,12 +60,10 @@ public void TestMustBeRethrowImmediately(Type t, bool result) } [Theory] -#if !NETSTANDARD1_5 [InlineData(typeof(StackOverflowException), true, false, false)] [InlineData(typeof(StackOverflowException), true, true, false)] [InlineData(typeof(ThreadAbortException), true, false, false)] [InlineData(typeof(ThreadAbortException), true, true, false)] -#endif [InlineData(typeof(NLogConfigurationException), true, true, true)] [InlineData(typeof(NLogConfigurationException), false, true, false)] [InlineData(typeof(NLogConfigurationException), true, true, null)] diff --git a/tests/NLog.UnitTests/Internal/SimpleStringReaderTests.cs b/tests/NLog.UnitTests/Internal/SimpleStringReaderTests.cs index 13f9858105..d8455b3db0 100644 --- a/tests/NLog.UnitTests/Internal/SimpleStringReaderTests.cs +++ b/tests/NLog.UnitTests/Internal/SimpleStringReaderTests.cs @@ -33,11 +33,11 @@ namespace NLog.UnitTests.Internal { +#if DEBUG using System; using NLog.Internal; using Xunit; -#if DEBUG public class SimpleStringReaderTests : NLogTestBase { [Theory] diff --git a/tests/NLog.UnitTests/LayoutRenderers/CallSiteLineNumberTests.cs b/tests/NLog.UnitTests/LayoutRenderers/CallSiteLineNumberTests.cs index ea29af019e..f98cc5fa6c 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/CallSiteLineNumberTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/CallSiteLineNumberTests.cs @@ -33,7 +33,6 @@ using System; using System.Threading.Tasks; -using NLog.Config; using Xunit; namespace NLog.UnitTests.LayoutRenderers diff --git a/tests/NLog.UnitTests/LayoutRenderers/CallSiteTests.cs b/tests/NLog.UnitTests/LayoutRenderers/CallSiteTests.cs index 16dda54973..5e4f30f25d 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/CallSiteTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/CallSiteTests.cs @@ -893,11 +893,10 @@ public void CallSiteShouldWorkForAsyncMethodsWithReturnValue() public async Task GetAsyncCallSite() { var logEvent = new LogEventInfo(LogLevel.Error, "logger1", "message1"); -#if !NETSTANDARD1_5 Type loggerType = typeof(Logger); var stacktrace = new System.Diagnostics.StackTrace(); logEvent.GetCallSiteInformationInternal().SetStackTrace(stacktrace, null, loggerType); -#endif + await Task.Delay(0); Layout l = "${callsite}"; var callSite = l.Render(logEvent); @@ -1142,7 +1141,6 @@ public NLogLogger Create(string name) } } -#if !NETSTANDARD1_5 /// /// If some calls got inlined, we can't find LoggerType anymore. We should fallback if loggerType can be found /// @@ -1163,7 +1161,6 @@ public void CallSiteShouldWorkEvenInlined() var callSite = l.Render(logEvent); Assert.Equal("NLog.UnitTests.LayoutRenderers.CallSiteTests.CallSiteShouldWorkEvenInlined", callSite); } -#endif #if NET35 [Fact(Skip = "NET35 not supporting async callstack")] diff --git a/tests/NLog.UnitTests/LayoutRenderers/SpecialFolderTests.cs b/tests/NLog.UnitTests/LayoutRenderers/SpecialFolderTests.cs index c0b2b3f881..718bb9f96c 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/SpecialFolderTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/SpecialFolderTests.cs @@ -33,7 +33,6 @@ namespace NLog.UnitTests.LayoutRenderers { -#if !NETSTANDARD1_5 using System; using System.IO; using Xunit; @@ -93,5 +92,4 @@ public void SpecialFolderUserLocalApplicationDataTest() AssertLayoutRendererOutput("${UserLocalApplicationDataDir}", Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)); } } -#endif } diff --git a/tests/NLog.UnitTests/NLogTestBase.cs b/tests/NLog.UnitTests/NLogTestBase.cs index 819087fa00..606724012a 100644 --- a/tests/NLog.UnitTests/NLogTestBase.cs +++ b/tests/NLog.UnitTests/NLogTestBase.cs @@ -436,16 +436,6 @@ private sealed class Logger : TextWriter public override Encoding Encoding => writer.Encoding; -#if NETSTANDARD1_5 - public override void Write(char value) - { - lock (this.writer) - { - this.writer.Write(value); - } - } -#endif - public override void Write(string value) { lock (writer) diff --git a/tests/NLog.UnitTests/NLogTraceListenerTests.cs b/tests/NLog.UnitTests/NLogTraceListenerTests.cs index 1adf5d69ba..042fb000b0 100644 --- a/tests/NLog.UnitTests/NLogTraceListenerTests.cs +++ b/tests/NLog.UnitTests/NLogTraceListenerTests.cs @@ -59,7 +59,6 @@ public void Dispose() Thread.CurrentThread.CurrentCulture = previousCultureInfo; } -#if !NETSTANDARD1_5 [Fact] public void TraceWriteTest() { @@ -318,7 +317,6 @@ public void FilterTraceTest() ts.TraceInformation("Mary had {0} lamb", "a little"); AssertDebugLastMessage("debug", "MySource1 Warn Quick brown fox Error"); } -#endif [Fact] public void GlobalAllFilterTraceTest() diff --git a/tests/NLog.UnitTests/Targets/NetworkTargetTests.cs b/tests/NLog.UnitTests/Targets/NetworkTargetTests.cs index c64a50fa37..deb53bfb53 100644 --- a/tests/NLog.UnitTests/Targets/NetworkTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/NetworkTargetTests.cs @@ -800,24 +800,24 @@ public void NetworkTargetNotConnectedTest() } }; - var loggerTask = new NLog.Internal.AsyncHelpersTask(state => + WaitCallback loggerTask = (state) => { for (int i = 0; i < toWrite; ++i) { var ev = new LogEventInfo(LogLevel.Info, "logger1", "message" + i).WithContinuation(writeFinished); target.WriteAsyncLogEvent(ev); } - }); + }; AsyncHelpers.StartAsyncTask(loggerTask, null); Assert.True(writeCompleted.WaitOne(10000), "Network Write not completed"); var shutdownCompleted = new ManualResetEvent(false); - var closeTask = new NLog.Internal.AsyncHelpersTask(state => + WaitCallback closeTask = (state) => { // no exception target.Close(); shutdownCompleted.Set(); - }); + }; AsyncHelpers.StartAsyncTask(closeTask, null); Assert.True(shutdownCompleted.WaitOne(10000), "Network Close not completed"); @@ -1097,16 +1097,16 @@ public void AsyncConnectionFailureOnCloseShouldNotDeadlock() } }; - var shutdownTask = new NLog.Internal.AsyncHelpersTask(state => + WaitCallback shutdownTask = (state) => { Thread.Sleep(10); target.Flush(ex => { }); target.Close(); shutdownCompleted.Set(); - }); + }; AsyncHelpers.StartAsyncTask(shutdownTask, null); - var loggerTask = new NLog.Internal.AsyncHelpersTask(state => + WaitCallback loggerTask = (state) => { for (int i = 0; i < total; ++i) { @@ -1115,7 +1115,7 @@ public void AsyncConnectionFailureOnCloseShouldNotDeadlock() } writeCompleted.Set(); - }); + }; AsyncHelpers.StartAsyncTask(loggerTask, null); Assert.True(writeCompleted.WaitOne(10000), "Network Write not completed"); @@ -1162,7 +1162,7 @@ public void KeepAliveTimeConfigTest(string keepAliveTimeSeconds, int expected) [InlineData("30", 30)] public void SendTimeoutConfigTest(string sendTimeoutSeconds, int expected) { - var logFactory = new LogFactory().Setup().LoadConfigurationFromXml($@" + var logFactory = new LogFactory().Setup().LoadConfigurationFromXml($@" @@ -1310,7 +1310,7 @@ protected override void BeginRequest(NetworkRequestArgs eventArgs) if (_senderFactory.AsyncMode) { - var asyncTask = new NLog.Internal.AsyncHelpersTask(state => { SendSync(eventArgs); }); + WaitCallback asyncTask = (state) => SendSync(eventArgs); AsyncHelpers.StartAsyncTask(asyncTask, null); } else diff --git a/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs b/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs index 35c30b8c81..0c74f39b11 100644 --- a/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs +++ b/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs @@ -36,7 +36,6 @@ namespace NLog.UnitTests.Targets using System; using System.Collections.Generic; using System.Linq; - using System.Threading; using NLog.Targets; using NLog.Targets.Wrappers; using Xunit; diff --git a/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs b/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs index 3eae20a3c3..d720e6fc78 100644 --- a/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs +++ b/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs @@ -178,7 +178,7 @@ private static void AsyncTargetWrapperSyncTest_WhenTimeToSleepBetweenBatchesIsEq #if DEBUG if (!IsAppVeyor()) // Skip timing test when running within OpenCover.Console.exe #endif - Assert.InRange(elapsedMilliseconds, 0, 975); + Assert.InRange(elapsedMilliseconds, 0, 975); targetWrapper.Flush(flushHandler); for (int i = 0; i < 2000 && flushCounter != 2; ++i) From 3c402908705efc1b40995c249ef56c902173e298 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 5 Oct 2024 09:56:56 +0200 Subject: [PATCH 007/224] Removed obsolete public IAppDomain from LogFactory (#5625) --- src/NLog/Internal/AppDomainWrapper.cs | 246 ------------------ src/NLog/Internal/AppEnvironmentWrapper.cs | 163 ++++++++++-- src/NLog/Internal/IAppDomain.cs | 89 ------- src/NLog/Internal/IAppEnvironment.cs | 5 +- src/NLog/LogFactory.cs | 32 +-- tests/NLog.UnitTests/ApiTests.cs | 3 - .../LayoutRenderers/BaseDirTests.cs | 27 +- tests/NLog.UnitTests/Mocks/AppDomainMock.cs | 63 ----- .../Mocks/AppEnvironmentMock.cs | 4 +- 9 files changed, 147 insertions(+), 485 deletions(-) delete mode 100644 src/NLog/Internal/AppDomainWrapper.cs delete mode 100644 src/NLog/Internal/IAppDomain.cs delete mode 100644 tests/NLog.UnitTests/Mocks/AppDomainMock.cs diff --git a/src/NLog/Internal/AppDomainWrapper.cs b/src/NLog/Internal/AppDomainWrapper.cs deleted file mode 100644 index fcff7dbd0a..0000000000 --- a/src/NLog/Internal/AppDomainWrapper.cs +++ /dev/null @@ -1,246 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.Internal.Fakeables -{ - using System; - using System.Collections.Generic; - using System.Reflection; - using NLog.Common; - - /// - /// Adapter for to - /// - [Obsolete("For unit testing only. Marked obsolete on NLog 5.0")] - internal sealed class AppDomainWrapper : IAppDomain - { - private readonly AppDomain _currentAppDomain; - - /// - /// Initializes a new instance of the class. - /// - /// The to wrap. - public AppDomainWrapper(AppDomain appDomain) - { - _currentAppDomain = appDomain; - try - { - BaseDirectory = LookupBaseDirectory(appDomain) ?? string.Empty; - } - catch (Exception ex) - { - InternalLogger.Warn(ex, "AppDomain.BaseDirectory Failed"); - BaseDirectory = string.Empty; - } - - try - { - FriendlyName = appDomain.FriendlyName; - } - catch (Exception ex) - { - InternalLogger.Warn(ex, "AppDomain.FriendlyName Failed"); - FriendlyName = GetFriendlyNameFromEntryAssembly() ?? GetFriendlyNameFromProcessName() ?? string.Empty; - } - - try - { - ConfigurationFile = LookupConfigurationFile(appDomain); - } - catch (Exception ex) - { - InternalLogger.Warn(ex, "AppDomain.SetupInformation.ConfigurationFile Failed"); - ConfigurationFile = string.Empty; - } - - try - { - PrivateBinPath = LookupPrivateBinPath(appDomain); - } - catch (Exception ex) - { - InternalLogger.Warn(ex, "AppDomain.SetupInformation.PrivateBinPath Failed"); - PrivateBinPath = ArrayHelper.Empty(); - } - - Id = appDomain.Id; - } - - private static string LookupBaseDirectory(AppDomain appDomain) - { - return appDomain.BaseDirectory; - } - - private static string LookupConfigurationFile(AppDomain appDomain) - { -#if NETFRAMEWORK - return appDomain.SetupInformation.ConfigurationFile; -#else - return string.Empty; -#endif - } - - private static string[] LookupPrivateBinPath(AppDomain appDomain) - { -#if NETFRAMEWORK - string privateBinPath = appDomain.SetupInformation.PrivateBinPath; - return string.IsNullOrEmpty(privateBinPath) - ? ArrayHelper.Empty() - : privateBinPath.SplitAndTrimTokens(';'); -#else - return ArrayHelper.Empty(); -#endif - } - - private static string GetFriendlyNameFromEntryAssembly() - { - try - { - string assemblyName = Assembly.GetEntryAssembly()?.GetName()?.Name; - return string.IsNullOrEmpty(assemblyName) ? null : assemblyName; - } - catch - { - return null; - } - } - - private static string GetFriendlyNameFromProcessName() - { - try - { - string processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName; - return string.IsNullOrEmpty(processName) ? null : processName; - } - catch - { - return null; - } - } - - /// - /// Creates an AppDomainWrapper for the current - /// - public static AppDomainWrapper CurrentDomain => new AppDomainWrapper(AppDomain.CurrentDomain); - - /// - /// Gets or sets the base directory that the assembly resolver uses to probe for assemblies. - /// - public string BaseDirectory { get; private set; } - - /// - /// Gets or sets the name of the configuration file for an application domain. - /// - public string ConfigurationFile { get; private set; } - - /// - /// Gets or sets the list of directories under the application base directory that are probed for private assemblies. - /// - public IEnumerable PrivateBinPath { get; private set; } - - /// - /// Gets or set the friendly name. - /// - public string FriendlyName { get; private set; } - - /// - /// Gets an integer that uniquely identifies the application domain within the process. - /// - public int Id { get; private set; } - - /// - /// Gets the assemblies that have been loaded into the execution context of this application domain. - /// - /// A list of assemblies in this application domain. - public IEnumerable GetAssemblies() - { - if (_currentAppDomain != null) - return _currentAppDomain.GetAssemblies(); - else - return Internal.ArrayHelper.Empty(); - } - - /// - /// Process exit event. - /// - public event EventHandler ProcessExit - { - add - { - if (processExitEvent == null && _currentAppDomain != null) - _currentAppDomain.ProcessExit += OnProcessExit; - processExitEvent += value; - } - remove - { - processExitEvent -= value; - if (processExitEvent == null && _currentAppDomain != null) - _currentAppDomain.ProcessExit -= OnProcessExit; - } - } - private event EventHandler processExitEvent; - - /// - /// Domain unloaded event. - /// - public event EventHandler DomainUnload - { - add - { - if (domainUnloadEvent == null && _currentAppDomain != null) - _currentAppDomain.DomainUnload += OnDomainUnload; - domainUnloadEvent += value; - - } - remove - { - domainUnloadEvent -= value; - if (domainUnloadEvent == null && _currentAppDomain != null) - _currentAppDomain.DomainUnload -= OnDomainUnload; - } - } - private event EventHandler domainUnloadEvent; - - private void OnDomainUnload(object sender, EventArgs e) - { - var handler = domainUnloadEvent; - handler?.Invoke(sender, e); - } - - private void OnProcessExit(object sender, EventArgs eventArgs) - { - var handler = processExitEvent; - handler?.Invoke(sender, eventArgs); - } - } -} diff --git a/src/NLog/Internal/AppEnvironmentWrapper.cs b/src/NLog/Internal/AppEnvironmentWrapper.cs index b199988156..4773596fa3 100644 --- a/src/NLog/Internal/AppEnvironmentWrapper.cs +++ b/src/NLog/Internal/AppEnvironmentWrapper.cs @@ -48,6 +48,11 @@ internal sealed class AppEnvironmentWrapper : IAppEnvironment private string _entryAssemblyFileName; private string _currentProcessFilePath; private string _currentProcessBaseName; + private string _appDomainBaseDirectory; + private string _appDomainConfigFile; + private string _appDomainFriendlyName; + private IEnumerable _appDomainPrivateBinPath; + private int? _appDomainId; private int? _currentProcessId; /// @@ -60,49 +65,36 @@ internal sealed class AppEnvironmentWrapper : IAppEnvironment public string CurrentProcessBaseName => _currentProcessBaseName ?? (_currentProcessBaseName = LookupCurrentProcessNameWithFallback()); /// public int CurrentProcessId => _currentProcessId ?? (_currentProcessId = LookupCurrentProcessIdWithFallback()).Value; - -#pragma warning disable CS0618 // Type or member is obsolete /// - public string AppDomainBaseDirectory => AppDomain.BaseDirectory; + public string AppDomainBaseDirectory => _appDomainBaseDirectory ?? (_appDomainBaseDirectory = LookupAppDomainBaseDirectory()); /// - public string AppDomainConfigurationFile => AppDomain.ConfigurationFile; + public string AppDomainConfigurationFile => _appDomainConfigFile ?? (_appDomainConfigFile = LookupAppDomainConfigFileSafe()); /// - public string AppDomainFriendlyName => AppDomain.FriendlyName; + public string AppDomainFriendlyName => _appDomainFriendlyName ?? (_appDomainFriendlyName = LookupAppDomainFriendlyName() ?? CurrentProcessBaseName); /// - public int AppDomainId => AppDomain.Id; + public int AppDomainId => _appDomainId ?? (_appDomainId = AppDomain.CurrentDomain.Id).Value; /// - public IEnumerable AppDomainPrivateBinPath => AppDomain.PrivateBinPath; + public IEnumerable AppDomainPrivateBinPath => _appDomainPrivateBinPath ?? (_appDomainPrivateBinPath = LookupAppDomainPrivateBinPathSafe()); /// - public IEnumerable GetAppDomainRuntimeAssemblies() => AppDomain.GetAssemblies(); + public IEnumerable GetAppDomainRuntimeAssemblies() => AppDomain.CurrentDomain?.GetAssemblies() ?? ArrayHelper.Empty(); /// - public event EventHandler ProcessExit + public event EventHandler ProcessExit { add { - AppDomain.ProcessExit += value; - AppDomain.DomainUnload += value; + AppDomain.CurrentDomain.ProcessExit += value; + AppDomain.CurrentDomain.DomainUnload += value; } remove { - AppDomain.DomainUnload -= value; - AppDomain.ProcessExit -= value; + AppDomain.CurrentDomain.DomainUnload -= value; + AppDomain.CurrentDomain.ProcessExit -= value; } } -#pragma warning restore CS0618 // Type or member is obsolete /// public string UserTempFilePath => Path.GetTempPath(); - [Obsolete("For unit testing only. Marked obsolete on NLog 5.0")] - public IAppDomain AppDomain { get; internal set; } - -#pragma warning disable CS0618 // Type or member is obsolete - public AppEnvironmentWrapper(IAppDomain appDomain) - { - AppDomain = appDomain; - } -#pragma warning restore CS0618 // Type or member is obsolete - /// public bool FileExists(string path) { @@ -115,6 +107,127 @@ public XmlReader LoadXmlFile(string path) return XmlReader.Create(path); } + private static string LookupAppDomainBaseDirectory() + { + try + { + return AppDomain.CurrentDomain.BaseDirectory ?? string.Empty; + } + catch (Exception ex) + { + if (ex.MustBeRethrownImmediately()) + throw; + + InternalLogger.Debug(ex, "AppDomain.BaseDirectory Failed"); + return string.Empty; + } + } + + private static string[] LookupAppDomainPrivateBinPathSafe() + { + try + { + return LookupAppDomainPrivateBinPath(); // Bonus try-catch when loading NLog for .NET Framework in .NET Core + } + catch (Exception ex) + { + if (ex.MustBeRethrownImmediately()) + throw; + + InternalLogger.Debug(ex, "AppDomain.CurrentDomain.SetupInformation.PrivateBinPath Failed"); + return ArrayHelper.Empty(); + } + } + + private static string[] LookupAppDomainPrivateBinPath() + { + try + { +#if NETFRAMEWORK + string privateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath; + return string.IsNullOrEmpty(privateBinPath) + ? ArrayHelper.Empty() + : privateBinPath.SplitAndTrimTokens(';'); +#else + return ArrayHelper.Empty(); +#endif + } + catch (Exception ex) + { + if (ex.MustBeRethrownImmediately()) + throw; + + InternalLogger.Debug(ex, "AppDomain.CurrentDomain.SetupInformation.PrivateBinPath Failed"); + return ArrayHelper.Empty(); + } + } + + private static string LookupAppDomainConfigFileSafe() + { + try + { + return LookupAppDomainConfigFile(); // Bonus try-catch when loading NLog for .NET Framework in .NET Core + } + catch (Exception ex) + { + if (ex.MustBeRethrownImmediately()) + throw; + + InternalLogger.Debug(ex, "AppDomain.SetupInformation.ConfigurationFile Failed"); + return string.Empty; + } + } + + private static string LookupAppDomainConfigFile() + { + try + { +#if NETFRAMEWORK + return AppDomain.CurrentDomain.SetupInformation.ConfigurationFile ?? string.Empty; +#else + return string.Empty; +#endif + } + catch (Exception ex) + { + if (ex.MustBeRethrownImmediately()) + throw; + + InternalLogger.Debug(ex, "AppDomain.SetupInformation.ConfigurationFile Failed"); + return string.Empty; + } + } + + private static string LookupAppDomainFriendlyName() + { + try + { + var friendlyName = AppDomain.CurrentDomain.FriendlyName; + return !string.IsNullOrEmpty(friendlyName) ? friendlyName : LookupEntryAssemblyFriendlyName(); + } + catch (Exception ex) + { + if (ex.MustBeRethrownImmediately()) + throw; + + InternalLogger.Debug(ex, "AppDomain.FriendlyName Failed"); + return LookupEntryAssemblyFriendlyName(); + } + } + + private static string LookupEntryAssemblyFriendlyName() + { + try + { + string assemblyName = System.Reflection.Assembly.GetEntryAssembly()?.GetName()?.Name; + return string.IsNullOrEmpty(assemblyName) ? null : assemblyName; + } + catch + { + return null; + } + } + private static string LookupEntryAssemblyLocation() { var entryAssembly = System.Reflection.Assembly.GetEntryAssembly(); @@ -288,7 +401,7 @@ private static string LookupCurrentProcessNameNative() return UnknownProcessName; } -#if !NETSTANDARD +#if NETFRAMEWORK private static string LookupCurrentProcessFilePathNative() { try diff --git a/src/NLog/Internal/IAppDomain.cs b/src/NLog/Internal/IAppDomain.cs deleted file mode 100644 index e3d50b9dfe..0000000000 --- a/src/NLog/Internal/IAppDomain.cs +++ /dev/null @@ -1,89 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.Internal.Fakeables -{ - using System; - using System.Collections.Generic; - using System.ComponentModel; - using System.Reflection; - - /// - /// Interface for fakeable of the current AppDomain. - /// - [Obsolete("For unit testing only. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public interface IAppDomain - { - /// - /// Gets or sets the base directory that the assembly resolver uses to probe for assemblies. - /// - string BaseDirectory { get; } - - /// - /// Gets or sets the name of the configuration file for an application domain. - /// - string ConfigurationFile { get; } - - /// - /// Gets or sets the list of directories under the application base directory that are probed for private assemblies. - /// - IEnumerable PrivateBinPath { get; } - - /// - /// Gets or set the friendly name. - /// - string FriendlyName { get; } - - /// - /// Gets an integer that uniquely identifies the application domain within the process. - /// - int Id { get; } - - /// - /// Gets the assemblies that have been loaded into the execution context of this application domain. - /// - /// A list of assemblies in this application domain. - IEnumerable GetAssemblies(); - - /// - /// Process exit event. - /// - event EventHandler ProcessExit; - - /// - /// Domain unloaded event. - /// - event EventHandler DomainUnload; - } -} diff --git a/src/NLog/Internal/IAppEnvironment.cs b/src/NLog/Internal/IAppEnvironment.cs index 10b7d841c3..ff2d09191e 100644 --- a/src/NLog/Internal/IAppEnvironment.cs +++ b/src/NLog/Internal/IAppEnvironment.cs @@ -47,9 +47,6 @@ internal interface IAppEnvironment : IFileSystem int AppDomainId { get; } IEnumerable AppDomainPrivateBinPath { get; } IEnumerable GetAppDomainRuntimeAssemblies(); - [Obsolete("Marked obsolete on NLog 5.0")] - IAppDomain AppDomain { get; } - string CurrentProcessFilePath { get; } /// /// Gets current process name (excluding filename extension, if any). @@ -63,6 +60,6 @@ internal interface IAppEnvironment : IFileSystem /// /// Process exit event. /// - event EventHandler ProcessExit; + event EventHandler ProcessExit; } } diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 4526a4ccb1..02819b492f 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -54,8 +54,6 @@ public class LogFactory : IDisposable { private static readonly TimeSpan DefaultFlushTimeout = TimeSpan.FromSeconds(15); - [Obsolete("For unit testing only. Marked obsolete on NLog 5.0")] - private static IAppDomain currentAppDomain; private static AppEnvironmentWrapper defaultAppEnvironment; /// @@ -143,39 +141,11 @@ internal LogFactory(ILoggingConfigurationLoader configLoader, IAppEnvironment ap LoggerShutdown += OnStopLogging; } - /// - /// Gets the current . - /// - [Obsolete("For unit testing only. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static IAppDomain CurrentAppDomain - { - get => currentAppDomain ?? DefaultAppEnvironment.AppDomain; - set - { - if (defaultAppEnvironment != null) - UnregisterEvents(defaultAppEnvironment); - - currentAppDomain = value; - - if (value != null && defaultAppEnvironment != null) - { - defaultAppEnvironment.AppDomain = value; - UnregisterEvents(defaultAppEnvironment); - RegisterEvents(defaultAppEnvironment); - } - } - } - internal static IAppEnvironment DefaultAppEnvironment { get { -#pragma warning disable CS0618 // Type or member is obsolete - return defaultAppEnvironment ?? (defaultAppEnvironment = new AppEnvironmentWrapper(currentAppDomain ?? (currentAppDomain = - new AppDomainWrapper(AppDomain.CurrentDomain) - ))); -#pragma warning restore CS0618 // Type or member is obsolete + return defaultAppEnvironment ?? (defaultAppEnvironment = new AppEnvironmentWrapper()); } } diff --git a/tests/NLog.UnitTests/ApiTests.cs b/tests/NLog.UnitTests/ApiTests.cs index 717fe2d86b..8b70d87484 100644 --- a/tests/NLog.UnitTests/ApiTests.cs +++ b/tests/NLog.UnitTests/ApiTests.cs @@ -138,9 +138,6 @@ public void TypesInInternalNamespaceShouldBeInternalTest() var excludes = new HashSet { typeof(NLog.Internal.Xamarin.PreserveAttribute), -#pragma warning disable CS0618 // Type or member is obsolete - typeof(NLog.Internal.Fakeables.IAppDomain), // TODO NLog 5 - handle IAppDomain -#pragma warning restore CS0618 // Type or member is obsolete }; var notInternalTypes = allTypes diff --git a/tests/NLog.UnitTests/LayoutRenderers/BaseDirTests.cs b/tests/NLog.UnitTests/LayoutRenderers/BaseDirTests.cs index 9c11383f6c..bc10cd12ab 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/BaseDirTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/BaseDirTests.cs @@ -41,7 +41,7 @@ namespace NLog.UnitTests.LayoutRenderers public class BaseDirTests : NLogTestBase { - private string baseDir = AppDomain.CurrentDomain.BaseDirectory; + private readonly string baseDir = AppDomain.CurrentDomain.BaseDirectory; [Fact] public void BaseDirTest() @@ -85,30 +85,15 @@ public void BaseDirDirFileCombineTest() } [Fact] - [Obsolete("For unit testing only. Marked obsolete on NLog 5.0")] public void InjectBaseDirAndCheckConfigPathsTest() { string fakeBaseDir = @"y:\root\"; - var old = LogFactory.CurrentAppDomain; - try - { - var currentAppDomain = new Mocks.AppDomainMock(fakeBaseDir); - LogFactory.CurrentAppDomain = currentAppDomain; - - //test 1 - AssertLayoutRendererOutput("${basedir}", fakeBaseDir); - - //test 2 - var paths = LogManager.LogFactory.GetCandidateConfigFilePaths().ToList(); - var count = paths.Count(p => p.StartsWith(fakeBaseDir)); + var appEnvironment = new Mocks.AppEnvironmentMock(null, null); + appEnvironment.AppDomainBaseDirectory = fakeBaseDir; + var baseLayoutRenderer = new NLog.LayoutRenderers.BaseDirLayoutRenderer(appEnvironment); - Assert.True(count > 0, $"At least one path should start with '{fakeBaseDir}'"); - } - finally - { - //restore - LogFactory.CurrentAppDomain = old; - } + // test1 + Assert.Equal(fakeBaseDir, baseLayoutRenderer.Render(LogEventInfo.CreateNullEvent())); } [Fact] diff --git a/tests/NLog.UnitTests/Mocks/AppDomainMock.cs b/tests/NLog.UnitTests/Mocks/AppDomainMock.cs deleted file mode 100644 index 7ce51aa937..0000000000 --- a/tests/NLog.UnitTests/Mocks/AppDomainMock.cs +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -using System; -using System.Collections.Generic; -using System.Reflection; -using NLog.Internal.Fakeables; - -namespace NLog.UnitTests.Mocks -{ - [Obsolete("For unit testing only. Marked obsolete on NLog 5.0")] - public class AppDomainMock : IAppDomain - { - public AppDomainMock(string baseDirectory) - { - BaseDirectory = baseDirectory; - } - - - public string BaseDirectory { get; set; } - public string ConfigurationFile { get; set; } - public IEnumerable PrivateBinPath { get; set; } - public string FriendlyName { get; set; } - public int Id { get; set; } - public IEnumerable GetAssemblies() - { - throw new NotImplementedException(); - } - - event EventHandler IAppDomain.ProcessExit { add { /* Nothing */ } remove { /* Nothing */ } } - event EventHandler IAppDomain.DomainUnload { add { /* Nothing */ } remove { /* Nothing */ } } - } -} diff --git a/tests/NLog.UnitTests/Mocks/AppEnvironmentMock.cs b/tests/NLog.UnitTests/Mocks/AppEnvironmentMock.cs index 5e04ccbb96..755db94610 100644 --- a/tests/NLog.UnitTests/Mocks/AppEnvironmentMock.cs +++ b/tests/NLog.UnitTests/Mocks/AppEnvironmentMock.cs @@ -73,10 +73,8 @@ public AppEnvironmentMock(Func fileExists, Func public IEnumerable GetAppDomainRuntimeAssemblies() => NLog.Internal.ArrayHelper.Empty(); - [Obsolete("For unit testing only. Marked obsolete on NLog 5.0")] - public IAppDomain AppDomain { get; set; } = new AppDomainMock(string.Empty); - public event EventHandler ProcessExit; + public event EventHandler ProcessExit; public bool FileExists(string path) { From 9ad73c7fdb8bf65b565191544e4d2620716f193a Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 5 Oct 2024 10:44:08 +0200 Subject: [PATCH 008/224] Removed obsolete ConfigurationReloaded from LogFactory (#5626) --- .../LoggingConfigurationReloadedEventArgs.cs | 79 ------------------- ...LoggingConfigurationWatchableFileLoader.cs | 11 --- src/NLog/LogFactory.cs | 27 ------- src/NLog/LogManager.cs | 12 --- tests/NLog.UnitTests/Config/ReloadTests.cs | 7 +- 5 files changed, 3 insertions(+), 133 deletions(-) delete mode 100644 src/NLog/Config/LoggingConfigurationReloadedEventArgs.cs diff --git a/src/NLog/Config/LoggingConfigurationReloadedEventArgs.cs b/src/NLog/Config/LoggingConfigurationReloadedEventArgs.cs deleted file mode 100644 index a1e6252481..0000000000 --- a/src/NLog/Config/LoggingConfigurationReloadedEventArgs.cs +++ /dev/null @@ -1,79 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.Config -{ - using System; - using System.ComponentModel; - - /// - /// Obsolete and replaced by and with NLog v5.2. - /// - [Obsolete("Replaced by ConfigurationChanged, but check args.ActivatedConfiguration != null. Marked obsolete on NLog 5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public class LoggingConfigurationReloadedEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// Whether configuration reload has succeeded. - public LoggingConfigurationReloadedEventArgs(bool succeeded) - { - Succeeded = succeeded; - } - - /// - /// Initializes a new instance of the class. - /// - /// Whether configuration reload has succeeded. - /// The exception during configuration reload. - public LoggingConfigurationReloadedEventArgs(bool succeeded, Exception exception) - { - Succeeded = succeeded; - Exception = exception; - } - - /// - /// Gets a value indicating whether configuration reload has succeeded. - /// - /// A value of true if succeeded; otherwise, false. - public bool Succeeded { get; } - - /// - /// Gets the exception which occurred during configuration reload. - /// - /// The exception. - public Exception Exception { get; } - } -} - diff --git a/src/NLog/Config/LoggingConfigurationWatchableFileLoader.cs b/src/NLog/Config/LoggingConfigurationWatchableFileLoader.cs index fef44d6f5e..d3fdc1733f 100644 --- a/src/NLog/Config/LoggingConfigurationWatchableFileLoader.cs +++ b/src/NLog/Config/LoggingConfigurationWatchableFileLoader.cs @@ -186,21 +186,13 @@ internal void ReloadConfigOnTimer(object state) } #endif InternalLogger.Warn(exception, "NLog configuration failed to reload"); -#pragma warning disable CS0618 // Type or member is obsolete - _logFactory?.NotifyConfigurationReloaded(new LoggingConfigurationReloadedEventArgs(false, exception)); -#pragma warning restore CS0618 // Type or member is obsolete return; } try { TryUnwatchConfigFile(); - _logFactory.Configuration = newConfig; // Triggers LogFactory to call Activated(...) that adds file-watch again - -#pragma warning disable CS0618 // Type or member is obsolete - _logFactory?.NotifyConfigurationReloaded(new LoggingConfigurationReloadedEventArgs(true)); -#pragma warning restore CS0618 // Type or member is obsolete } catch (Exception exception) { @@ -212,9 +204,6 @@ internal void ReloadConfigOnTimer(object state) #endif InternalLogger.Warn(exception, "NLog configuration reloaded, failed to be assigned"); _watcher.Watch(oldConfig.FileNamesToWatch); -#pragma warning disable CS0618 // Type or member is obsolete - _logFactory?.NotifyConfigurationReloaded(new LoggingConfigurationReloadedEventArgs(false, exception)); -#pragma warning restore CS0618 // Type or member is obsolete } } } diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 02819b492f..a307451c73 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -87,14 +87,6 @@ public class LogFactory : IDisposable /// public event EventHandler ConfigurationChanged; - /// - /// Obsolete and replaced by with NLog v5.2. - /// Occurs when logging gets reloaded. - /// - [Obsolete("Replaced by ConfigurationChanged, but check args.ActivatedConfiguration != null. Marked obsolete on NLog 5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public event EventHandler ConfigurationReloaded; - private static event EventHandler LoggerShutdown; /// @@ -693,24 +685,6 @@ protected virtual void OnConfigurationChanged(LoggingConfigurationChangedEventAr ConfigurationChanged?.Invoke(this, e); } - /// - /// Obsolete and replaced by with NLog 5.2. - /// - /// Raises the event when the configuration is reloaded. - /// - /// Event arguments - [Obsolete("Replaced by ConfigurationChanged, but check args.ActivatedConfiguration != null. Marked obsolete on NLog 5.2")] - protected virtual void OnConfigurationReloaded(LoggingConfigurationReloadedEventArgs e) - { - ConfigurationReloaded?.Invoke(this, e); - } - - [Obsolete("Replaced by ConfigurationChanged, but check args.ActivatedConfiguration != null. Marked obsolete on NLog 5.2")] - internal void NotifyConfigurationReloaded(LoggingConfigurationReloadedEventArgs eventArgs) - { - OnConfigurationReloaded(eventArgs); - } - /// /// Change this method with NLog v6 to completely disconnect LogFactory from Targets/Layouts /// - Remove LoggingRule-List-parameter @@ -739,7 +713,6 @@ private void Close(TimeSpan flushTimeout) _serviceRepository.TypeRegistered -= ServiceRepository_TypeRegistered; LoggerShutdown -= OnStopLogging; - ConfigurationReloaded = null; // Release event listeners if (Monitor.TryEnter(_syncRoot, 500)) { diff --git a/src/NLog/LogManager.cs b/src/NLog/LogManager.cs index 5c6820d951..0f5804031a 100644 --- a/src/NLog/LogManager.cs +++ b/src/NLog/LogManager.cs @@ -71,18 +71,6 @@ public static event EventHandler Configura remove => factory.ConfigurationChanged -= value; } - /// - /// Obsolete and replaced by with NLog v5.2. - /// Occurs when logging gets reloaded. - /// - [Obsolete("Replaced by ConfigurationChanged, but check args.ActivatedConfiguration != null. Marked obsolete on NLog 5.2")] - [EditorBrowsable(EditorBrowsableState.Never)] - public static event EventHandler ConfigurationReloaded - { - add => factory.ConfigurationReloaded += value; - remove => factory.ConfigurationReloaded -= value; - } - /// /// Gets or sets a value indicating whether NLog should throw exceptions. /// By default exceptions are not thrown under any circumstances. diff --git a/tests/NLog.UnitTests/Config/ReloadTests.cs b/tests/NLog.UnitTests/Config/ReloadTests.cs index 5cc2c66f79..b2f7ac564c 100644 --- a/tests/NLog.UnitTests/Config/ReloadTests.cs +++ b/tests/NLog.UnitTests/Config/ReloadTests.cs @@ -696,24 +696,23 @@ public void KeepVariablesOnReloadWithStaticMode() } [Fact] - [Obsolete("Replaced by ConfigurationChanged. Marked obsolete on NLog 5.2")] public void ReloadConfigOnTimer_When_No_Exception_Raises_ConfigurationReloadedEvent() { var called = false; - LoggingConfigurationReloadedEventArgs arguments = null; + LoggingConfigurationChangedEventArgs arguments = null; object calledBy = null; var configLoader = new LoggingConfigurationWatchableFileLoader(LogFactory.DefaultAppEnvironment); var logFactory = new LogFactory(configLoader); var loggingConfiguration = XmlLoggingConfigurationMock.CreateFromXml(logFactory, ""); logFactory.Configuration = loggingConfiguration; - logFactory.ConfigurationReloaded += (sender, args) => { called = true; calledBy = sender; arguments = args; }; + logFactory.ConfigurationChanged += (sender, args) => { called = true; calledBy = sender; arguments = args; }; configLoader.ReloadConfigOnTimer(loggingConfiguration); Assert.True(called); Assert.Same(calledBy, logFactory); - Assert.True(arguments.Succeeded); + Assert.NotNull(arguments.ActivatedConfiguration); } [Fact] From c8c352b15243c1150a6aa1b567417a947558e8b3 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 5 Oct 2024 12:08:59 +0200 Subject: [PATCH 009/224] Now checking define NETFRAMEWORK since the odd one (#5628) --- src/NLog.Database/DatabaseTarget.cs | 28 ++++---- .../RegistryLayoutRenderer.cs | 2 +- src/NLog/Common/InternalLogger.cs | 2 +- src/NLog/Config/Factory.cs | 6 +- .../FileAppenders/BaseFileAppender.cs | 4 +- .../FileAppenders/BaseMutexFileAppender.cs | 2 +- .../WindowsMultiProcessFileAppender.cs | 2 +- src/NLog/Internal/MutexDetector.cs | 2 +- src/NLog/Internal/NativeMethods.cs | 2 +- src/NLog/Internal/PlatformDetector.cs | 20 +++--- src/NLog/Internal/StringBuilderExt.cs | 8 +-- src/NLog/Internal/Win32FileNativeMethods.cs | 2 +- src/NLog/MessageTemplates/ValueFormatter.cs | 8 +-- src/NLog/Targets/AsyncTaskTarget.cs | 6 +- src/NLog/Targets/DefaultJsonSerializer.cs | 8 +-- .../Targets/EventLogTargetOverflowAction.cs | 2 +- src/NLog/Targets/FileTarget.cs | 2 +- .../DatabaseTargetTests.cs | 66 +++++++++---------- .../NLog.UnitTests/Config/CultureInfoTests.cs | 2 +- tests/NLog.UnitTests/Config/ExtensionTests.cs | 2 +- tests/NLog.UnitTests/Config/ReloadTests.cs | 14 ++-- .../NLog.UnitTests/ConfigFileLocatorTests.cs | 57 ++++++++-------- .../Internal/AppDomainPartialTrustTests.cs | 2 +- .../FileAppenders/FileAppenderCacheTests.cs | 2 +- .../Internal/PlatformDetectorTests.cs | 2 +- .../LayoutRenderers/AppSettingTests.cs | 2 +- .../LayoutRenderers/AssemblyVersionTests.cs | 4 +- .../CallSiteFileNameLayoutTests.cs | 36 +++++----- .../CallSiteLineNumberTests.cs | 7 +- .../LayoutRenderers/CallSiteTests.cs | 2 +- tests/NLog.UnitTests/LogManagerTests.cs | 2 +- tests/NLog.UnitTests/NLogTestBase.cs | 6 +- tests/NLog.UnitTests/ProcessRunner.cs | 2 +- .../Targets/ConcurrentFileTargetTests.cs | 2 +- .../Targets/DebugSystemTargetTests.cs | 12 ++-- .../Targets/EventLogTargetTests.cs | 2 +- .../NLog.UnitTests/Targets/FileTargetTests.cs | 6 +- .../NLog.UnitTests/Targets/MailTargetTests.cs | 8 +-- .../Targets/WebServiceTargetTests.cs | 4 +- .../RegistryTests.cs | 2 +- 40 files changed, 176 insertions(+), 174 deletions(-) diff --git a/src/NLog.Database/DatabaseTarget.cs b/src/NLog.Database/DatabaseTarget.cs index 38d0bba694..3332042c31 100644 --- a/src/NLog.Database/DatabaseTarget.cs +++ b/src/NLog.Database/DatabaseTarget.cs @@ -47,7 +47,7 @@ namespace NLog.Targets using NLog.Config; using NLog.Layouts; -#if !NETSTANDARD +#if NETFRAMEWORK using System.Configuration; using ConfigurationManager = System.Configuration.ConfigurationManager; #endif @@ -90,7 +90,7 @@ public DatabaseTarget() { DBProvider = "sqlserver"; DBHost = "."; -#if !NETSTANDARD +#if NETFRAMEWORK ConnectionStringsSettings = ConfigurationManager.ConnectionStrings; #endif CommandType = CommandType.Text; @@ -137,7 +137,7 @@ public DatabaseTarget(string name) : this() [DefaultValue("sqlserver")] public string DBProvider { get; set; } -#if !NETSTANDARD +#if NETFRAMEWORK /// /// Gets or sets the name of the connection string (as specified in <connectionStrings> configuration section. /// @@ -282,7 +282,7 @@ public Layout DBPassword /// public System.Data.IsolationLevel? IsolationLevel { get; set; } -#if !NETSTANDARD +#if NETFRAMEWORK internal DbProviderFactory ProviderFactory { get; set; } // this is so we can mock the connection string without creating sub-processes @@ -325,7 +325,7 @@ internal IDbConnection OpenConnection(string connectionString, LogEventInfo logE { IDbConnection connection; -#if !NETSTANDARD +#if NETFRAMEWORK if (ProviderFactory != null) { connection = ProviderFactory.CreateConnection(); @@ -381,7 +381,7 @@ protected override void InitializeTarget() bool foundProvider = false; string providerName = string.Empty; -#if !NETSTANDARD +#if NETFRAMEWORK if (!string.IsNullOrEmpty(ConnectionStringName)) { // read connection string and provider factory from the configuration file @@ -404,7 +404,7 @@ protected override void InitializeTarget() providerName = InitConnectionString(providerName); } -#if !NETSTANDARD +#if NETFRAMEWORK if (string.IsNullOrEmpty(providerName)) { providerName = GetProviderNameFromDbProviderFactories(providerName); @@ -455,7 +455,7 @@ private string InitConnectionString(string providerName) } catch (Exception ex) { -#if !NETSTANDARD +#if NETFRAMEWORK if (!string.IsNullOrEmpty(ConnectionStringName)) InternalLogger.Warn(ex, "{0}: DbConnectionStringBuilder failed to parse '{1}' ConnectionString", this, ConnectionStringName); else @@ -466,7 +466,7 @@ private string InitConnectionString(string providerName) return providerName; } -#if !NETSTANDARD +#if NETFRAMEWORK private bool InitProviderFactory(string providerName) { bool foundProvider; @@ -515,7 +515,7 @@ private void SetConnectionType() case "MSSQL": case "MICROSOFT": case "MSDE": -#if NETSTANDARD +#if !NETFRAMEWORK { try { @@ -550,7 +550,7 @@ private void SetConnectionType() ConnectionType = assembly.GetType("Microsoft.Data.SqlClient.SqlConnection", true, true); break; } -#if !NETSTANDARD +#if NETFRAMEWORK case "OLEDB": { var assembly = typeof(IDbConnection).Assembly; @@ -561,10 +561,10 @@ private void SetConnectionType() case "ODBC": case "SYSTEM.DATA.ODBC": { -#if NETSTANDARD - var assembly = Assembly.Load(new AssemblyName("System.Data.Odbc")); -#else +#if NETFRAMEWORK var assembly = typeof(IDbConnection).Assembly; +#else + var assembly = Assembly.Load(new AssemblyName("System.Data.Odbc")); #endif ConnectionType = assembly.GetType("System.Data.Odbc.OdbcConnection", true, true); break; diff --git a/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs b/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs index 06259d6469..3883b68392 100644 --- a/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs +++ b/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs @@ -220,7 +220,7 @@ private static ParseResult ParseKey(string key) {"HKEY_CLASSES_ROOT", RegistryHive.ClassesRoot}, {"HKEY_USERS", RegistryHive.Users}, {"HKEY_CURRENT_CONFIG", RegistryHive.CurrentConfig}, -#if !NETSTANDARD +#if NETFRAMEWORK {"HKEY_DYN_DATA", RegistryHive.DynData}, #endif {"HKEY_PERFORMANCE_DATA", RegistryHive.PerformanceData}, diff --git a/src/NLog/Common/InternalLogger.cs b/src/NLog/Common/InternalLogger.cs index a49c386f94..ccde45f244 100644 --- a/src/NLog/Common/InternalLogger.cs +++ b/src/NLog/Common/InternalLogger.cs @@ -397,7 +397,7 @@ public static void LogAssemblyVersion(Assembly assembly) var fileVersion = assembly.GetFirstCustomAttribute()?.Version; var productVersion = assembly.GetFirstCustomAttribute()?.InformationalVersion; var globalAssemblyCache = false; -#if !NETSTANDARD +#if NETFRAMEWORK if (assembly.GlobalAssemblyCache) globalAssemblyCache = true; #endif diff --git a/src/NLog/Config/Factory.cs b/src/NLog/Config/Factory.cs index 7c4f0d8e9b..c8f42636ff 100644 --- a/src/NLog/Config/Factory.cs +++ b/src/NLog/Config/Factory.cs @@ -219,10 +219,10 @@ public static TBaseType CreateInstance(this IFactory facto if (normalName != null && (normalName.StartsWith("aspnet", StringComparison.OrdinalIgnoreCase) || normalName.StartsWith("iis", StringComparison.OrdinalIgnoreCase))) { -#if NETSTANDARD - message += " - Extension NLog.Web.AspNetCore not included?"; -#else +#if NETFRAMEWORK message += " - Extension NLog.Web not included?"; +#else + message += " - Extension NLog.Web.AspNetCore not included?"; #endif } else if (normalName?.StartsWith("database", StringComparison.OrdinalIgnoreCase) == true) diff --git a/src/NLog/Internal/FileAppenders/BaseFileAppender.cs b/src/NLog/Internal/FileAppenders/BaseFileAppender.cs index ef4d4e2847..2d2195bc02 100644 --- a/src/NLog/Internal/FileAppenders/BaseFileAppender.cs +++ b/src/NLog/Internal/FileAppenders/BaseFileAppender.cs @@ -233,7 +233,7 @@ private FileStream TryCreateDirectoryFileStream(bool allowFileSharedWriting, int } } -#if !MONO && !NETSTANDARD +#if NETFRAMEWORK && !MONO private FileStream WindowsCreateFile(bool allowFileSharedWriting, int bufferSize) { int fileShare = Win32FileNativeMethods.FILE_SHARE_READ; @@ -287,7 +287,7 @@ private FileStream TryCreateFileStream(bool allowFileSharedWriting, int override { var bufferSize = overrideBufferSize > 0 ? overrideBufferSize : CreateFileParameters.BufferSize; -#if !MONO && !NETSTANDARD +#if NETFRAMEWORK && !MONO try { if (!CreateFileParameters.ForceManaged && PlatformDetector.IsWin32 && !PlatformDetector.IsMono) diff --git a/src/NLog/Internal/FileAppenders/BaseMutexFileAppender.cs b/src/NLog/Internal/FileAppenders/BaseMutexFileAppender.cs index 8b8927b3f2..186d32aeed 100644 --- a/src/NLog/Internal/FileAppenders/BaseMutexFileAppender.cs +++ b/src/NLog/Internal/FileAppenders/BaseMutexFileAppender.cs @@ -40,7 +40,7 @@ namespace NLog.Internal.FileAppenders using System.Text; using JetBrains.Annotations; -#if !NETSTANDARD +#if NETFRAMEWORK using System.Security.AccessControl; using System.Security.Principal; #endif diff --git a/src/NLog/Internal/FileAppenders/WindowsMultiProcessFileAppender.cs b/src/NLog/Internal/FileAppenders/WindowsMultiProcessFileAppender.cs index 06ed2f807b..c24171b8d8 100644 --- a/src/NLog/Internal/FileAppenders/WindowsMultiProcessFileAppender.cs +++ b/src/NLog/Internal/FileAppenders/WindowsMultiProcessFileAppender.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !MONO && NETFRAMEWORK +#if NETFRAMEWORK && !MONO namespace NLog.Internal.FileAppenders { diff --git a/src/NLog/Internal/MutexDetector.cs b/src/NLog/Internal/MutexDetector.cs index 99644e1e0c..02be04621a 100644 --- a/src/NLog/Internal/MutexDetector.cs +++ b/src/NLog/Internal/MutexDetector.cs @@ -55,7 +55,7 @@ private static bool ResolveSupportsSharableMutex() { try { -#if !NETSTANDARD +#if NETFRAMEWORK if (Environment.Version.Major < 4 && PlatformDetector.IsMono) return false; // MONO ver. 4 is needed for named Mutex to work #endif diff --git a/src/NLog/Internal/NativeMethods.cs b/src/NLog/Internal/NativeMethods.cs index a26fe374bc..f9c185653b 100644 --- a/src/NLog/Internal/NativeMethods.cs +++ b/src/NLog/Internal/NativeMethods.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD +#if NETFRAMEWORK namespace NLog.Internal { diff --git a/src/NLog/Internal/PlatformDetector.cs b/src/NLog/Internal/PlatformDetector.cs index 2a43441f1c..5888acd264 100644 --- a/src/NLog/Internal/PlatformDetector.cs +++ b/src/NLog/Internal/PlatformDetector.cs @@ -56,7 +56,7 @@ internal static class PlatformDetector /// public static bool IsUnix => CurrentOS == RuntimeOS.Linux || CurrentOS == RuntimeOS.MacOSX; -#if !NETSTANDARD +#if NETFRAMEWORK /// /// Gets a value indicating whether current runtime is Mono-based /// @@ -66,15 +66,7 @@ internal static class PlatformDetector private static RuntimeOS GetCurrentRuntimeOS() { -#if NETSTANDARD - if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) - return RuntimeOS.WindowsNT; - else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) - return RuntimeOS.MacOSX; - else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux)) - return RuntimeOS.Linux; - return RuntimeOS.Unknown; -#else +#if NETFRAMEWORK PlatformID platformID = Environment.OSVersion.Platform; if ((int)platformID == 4 || (int)platformID == 128) { @@ -92,6 +84,14 @@ private static RuntimeOS GetCurrentRuntimeOS() } return RuntimeOS.Unknown; +#else + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) + return RuntimeOS.WindowsNT; + else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) + return RuntimeOS.MacOSX; + else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux)) + return RuntimeOS.Linux; + return RuntimeOS.Unknown; #endif } } diff --git a/src/NLog/Internal/StringBuilderExt.cs b/src/NLog/Internal/StringBuilderExt.cs index bb203fd200..f6ebc405d2 100644 --- a/src/NLog/Internal/StringBuilderExt.cs +++ b/src/NLog/Internal/StringBuilderExt.cs @@ -437,7 +437,7 @@ internal static void AppendNumericInvariant(this StringBuilder sb, IConvertible case TypeCode.Single: { float floatValue = value.ToSingle(CultureInfo.InvariantCulture); -#if NETSTANDARD +#if !NETFRAMEWORK if (!float.IsNaN(floatValue) && !float.IsInfinity(floatValue) && value is IFormattable formattable) { AppendDecimalInvariant(sb, formattable, "{0:R}"); @@ -452,7 +452,7 @@ internal static void AppendNumericInvariant(this StringBuilder sb, IConvertible case TypeCode.Double: { double doubleValue = value.ToDouble(CultureInfo.InvariantCulture); -#if NETSTANDARD +#if !NETFRAMEWORK if (!double.IsNaN(doubleValue) && !double.IsInfinity(doubleValue) && value is IFormattable formattable) { AppendDecimalInvariant(sb, formattable, "{0:R}"); @@ -466,7 +466,7 @@ internal static void AppendNumericInvariant(this StringBuilder sb, IConvertible break; case TypeCode.Decimal: { -#if NETSTANDARD +#if !NETFRAMEWORK if (value is IFormattable formattable) { AppendDecimalInvariant(sb, formattable, "{0}"); @@ -484,7 +484,7 @@ internal static void AppendNumericInvariant(this StringBuilder sb, IConvertible } } -#if NETSTANDARD +#if !NETFRAMEWORK private static void AppendDecimalInvariant(StringBuilder sb, IFormattable formattable, string format) { int orgLength = sb.Length; diff --git a/src/NLog/Internal/Win32FileNativeMethods.cs b/src/NLog/Internal/Win32FileNativeMethods.cs index 045392948a..500b469ba3 100644 --- a/src/NLog/Internal/Win32FileNativeMethods.cs +++ b/src/NLog/Internal/Win32FileNativeMethods.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD +#if NETFRAMEWORK namespace NLog.Internal { diff --git a/src/NLog/MessageTemplates/ValueFormatter.cs b/src/NLog/MessageTemplates/ValueFormatter.cs index 69cf4acd76..58179896dc 100644 --- a/src/NLog/MessageTemplates/ValueFormatter.cs +++ b/src/NLog/MessageTemplates/ValueFormatter.cs @@ -221,12 +221,12 @@ private void SerializeConvertibleObject(IConvertible value, string format, IForm private static void SerializeConvertToString(object value, IFormatProvider formatProvider, StringBuilder builder) { -#if NETSTANDARD +#if !NETFRAMEWORK if (value is IFormattable) builder.AppendFormat(formatProvider, "{0}", value); // Support ISpanFormattable else #endif - builder.Append(Convert.ToString(value, formatProvider)); + builder.Append(Convert.ToString(value, formatProvider)); } private static void SerializeStringObject(string stringValue, string format, StringBuilder builder) @@ -350,12 +350,12 @@ public static void FormatToString(object value, string format, IFormatProvider f { if (value is IFormattable formattable) { -#if NETSTANDARD +#if !NETFRAMEWORK if (string.IsNullOrEmpty(format)) builder.AppendFormat(formatProvider, "{0}", formattable); // Support ISpanFormattable else #endif - builder.Append(formattable.ToString(format, formatProvider)); + builder.Append(formattable.ToString(format, formatProvider)); } else { diff --git a/src/NLog/Targets/AsyncTaskTarget.cs b/src/NLog/Targets/AsyncTaskTarget.cs index 9883bdaa6e..56bac1abfb 100644 --- a/src/NLog/Targets/AsyncTaskTarget.cs +++ b/src/NLog/Targets/AsyncTaskTarget.cs @@ -163,14 +163,14 @@ protected AsyncTaskTarget() _taskCancelledTokenReInit = TaskCancelledTokenReInit; _taskTimeoutTimer = new Timer(TaskTimeout, null, Timeout.Infinite, Timeout.Infinite); -#if NETSTANDARD2_0 +#if NETFRAMEWORK + _requestQueue = new AsyncRequestQueue(10000, AsyncTargetWrapperOverflowAction.Discard); +#else // NetStandard20 includes many optimizations for ConcurrentQueue: // - See: https://blogs.msdn.microsoft.com/dotnet/2017/06/07/performance-improvements-in-net-core/ // Net40 ConcurrencyQueue can seem to leak, because it doesn't clear properly on dequeue // - See: https://blogs.msdn.microsoft.com/pfxteam/2012/05/08/concurrentqueuet-holding-on-to-a-few-dequeued-elements/ _requestQueue = new ConcurrentRequestQueue(10000, AsyncTargetWrapperOverflowAction.Discard); -#else - _requestQueue = new AsyncRequestQueue(10000, AsyncTargetWrapperOverflowAction.Discard); #endif _lazyWriterTimer = new Timer((s) => TaskStartNext(null, false), null, Timeout.Infinite, Timeout.Infinite); diff --git a/src/NLog/Targets/DefaultJsonSerializer.cs b/src/NLog/Targets/DefaultJsonSerializer.cs index efaec1e78e..b95d77c210 100644 --- a/src/NLog/Targets/DefaultJsonSerializer.cs +++ b/src/NLog/Targets/DefaultJsonSerializer.cs @@ -260,13 +260,13 @@ private bool SerializeSimpleObjectValue(object value, StringBuilder destination, destination.Append('"'); -#if NETSTANDARD +#if NETFRAMEWORK + var str = formattable.ToString(null, CultureInfo.InvariantCulture); + AppendStringEscape(destination, str, options); +#else int startPos = destination.Length; destination.AppendFormat(CultureInfo.InvariantCulture, "{0}", formattable); // Support ISpanFormattable PerformJsonEscapeWhenNeeded(destination, startPos, options.EscapeUnicode, options.EscapeForwardSlash); -#else - var str = formattable.ToString(null, CultureInfo.InvariantCulture); - AppendStringEscape(destination, str, options); #endif destination.Append('"'); return true; diff --git a/src/NLog/Targets/EventLogTargetOverflowAction.cs b/src/NLog/Targets/EventLogTargetOverflowAction.cs index 3830e62e72..f863a73855 100644 --- a/src/NLog/Targets/EventLogTargetOverflowAction.cs +++ b/src/NLog/Targets/EventLogTargetOverflowAction.cs @@ -32,7 +32,7 @@ // -#if !NETSTANDARD || WindowsEventLogPackage +#if NETFRAMEWORK || WindowsEventLogPackage namespace NLog.Targets { diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index 6d6c043136..3878cc1c98 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -889,7 +889,7 @@ private IFileAppenderFactory GetFileAppenderFactory() private bool IsSimpleKeepFileOpen => KeepFileOpen && !ConcurrentWrites && !ReplaceFileContentsOnEachWrite; private bool EnableFileDeleteSimpleMonitor => EnableFileDelete && IsSimpleKeepFileOpen -#if !NETSTANDARD +#if NETFRAMEWORK && !PlatformDetector.IsWin32 #endif ; diff --git a/tests/NLog.Database.Tests/DatabaseTargetTests.cs b/tests/NLog.Database.Tests/DatabaseTargetTests.cs index 44b6bb8b46..b92f24bdd8 100644 --- a/tests/NLog.Database.Tests/DatabaseTargetTests.cs +++ b/tests/NLog.Database.Tests/DatabaseTargetTests.cs @@ -36,7 +36,7 @@ namespace NLog.Database.Tests using System; using System.Collections; using System.Collections.Generic; -#if !NETSTANDARD +#if NETFRAMEWORK using System.Configuration; #endif using System.Data; @@ -50,12 +50,12 @@ namespace NLog.Database.Tests #if MONO using Mono.Data.Sqlite; using System.Data.SqlClient; -#elif NETSTANDARD - using Microsoft.Data.SqlClient; - using Microsoft.Data.Sqlite; -#else +#elif NETFRAMEWORK using System.Data.SqlClient; using System.Data.SQLite; +#else + using Microsoft.Data.SqlClient; + using Microsoft.Data.Sqlite; #endif public class DatabaseTargetTests @@ -65,7 +65,7 @@ public DatabaseTargetTests() LogManager.ThrowExceptions = true; } -#if !NETSTANDARD +#if NETFRAMEWORK static DatabaseTargetTests() { var data = (DataSet)ConfigurationManager.GetSection("system.data"); @@ -1191,7 +1191,7 @@ public void DatabaseExceptionTest3() AssertLog(expectedLog); } -#if !MONO && !NETSTANDARD +#if !MONO && NETFRAMEWORK [Fact] public void ConnectionStringNameInitTest() { @@ -1339,7 +1339,7 @@ public void SqlServerShorthandNotationTest() } } -#if !NETSTANDARD +#if NETFRAMEWORK [Fact] public void OleDbShorthandNotationTest() { @@ -1440,10 +1440,10 @@ private static string GetSQLiteDbProvider() { #if MONO return "Mono.Data.Sqlite.SqliteConnection, Mono.Data.Sqlite"; -#elif NETSTANDARD - return "Microsoft.Data.Sqlite.SqliteConnection, Microsoft.Data.Sqlite"; -#else +#elif NETFRAMEWORK return "System.Data.SQLite.SQLiteConnection, System.Data.SQLite"; +#else + return "Microsoft.Data.Sqlite.SqliteConnection, Microsoft.Data.Sqlite"; #endif } @@ -1638,10 +1638,10 @@ private static LogFactory SetupSqliteConfigWithInvalidInstallCommand(string data // Use an in memory SQLite database // See https://www.sqlite.org/inmemorydb.html -#if NETSTANDARD - var connectionString = "Data Source=:memory:"; -#else +#if NETFRAMEWORK var connectionString = "Uri=file::memory:;Version=3"; +#else + var connectionString = "Data Source=:memory:"; #endif return new LogFactory().Setup().SetupExtensions(ext => ext.RegisterAssembly(typeof(DatabaseTarget).Assembly)).LoadConfigurationFromXml(String.Format(nlogXmlConfig, GetSQLiteDbProvider(), connectionString)).LogFactory; @@ -1685,10 +1685,10 @@ public void RethrowingInstallExceptions() Assert.True(context.ThrowExceptions); // Sanity check -#if MONO || NETSTANDARD - Assert.Throws(() => logFactory.Configuration.Install(context)); -#else +#if NETFRAMEWORK && !MONO Assert.Throws(() => logFactory.Configuration.Install(context)); +#else + Assert.Throws(() => logFactory.Configuration.Install(context)); #endif } finally @@ -1826,7 +1826,7 @@ LogDate date NULL } } -#if !NETSTANDARD +#if NETFRAMEWORK [Fact] public void GetProviderNameFromAppConfig() { @@ -2437,10 +2437,10 @@ private sealed class SQLiteTest public SQLiteTest(string dbName) { this.dbName = dbName; -#if NETSTANDARD - connectionString = "Data Source=" + this.dbName; -#else +#if NETFRAMEWORK connectionString = "Data Source=" + this.dbName + ";Version=3;"; +#else + connectionString = "Data Source=" + this.dbName; #endif } @@ -2508,32 +2508,32 @@ private static class SQLiteHandler { public static void CreateDatabase(string dbName) { -#if NETSTANDARD - // Using ConnectionString Mode=ReadWriteCreate -#elif MONO +#if MONO SqliteConnection.CreateFile(dbName); -#else +#elif NETFRAMEWORK SQLiteConnection.CreateFile(dbName); +#else + // Using ConnectionString Mode=ReadWriteCreate #endif } public static DbConnection GetConnection(string connectionString) { -#if NETSTANDARD - return new SqliteConnection(connectionString + ";Mode=ReadWriteCreate;"); -#elif MONO +#if MONO return new SqliteConnection(connectionString); -#else +#elif NETFRAMEWORK return new SQLiteConnection(connectionString); +#else + return new SqliteConnection(connectionString + ";Mode=ReadWriteCreate;"); #endif } public static DbCommand CreateCommand(string commandString, DbConnection connection) { -#if MONO || NETSTANDARD - return new SqliteCommand(commandString, (SqliteConnection)connection); -#else +#if NETFRAMEWORK && !MONO return new SQLiteCommand(commandString, (SQLiteConnection)connection); +#else + return new SqliteCommand(commandString, (SqliteConnection)connection); #endif } } @@ -2547,7 +2547,7 @@ static SqlServerTest() public static string GetConnectionString(bool isAppVeyor) { string connectionString = string.Empty; -#if !NETSTANDARD +#if NETFRAMEWORK connectionString = ConfigurationManager.AppSettings["SqlServerTestConnectionString"]; #endif if (String.IsNullOrWhiteSpace(connectionString)) diff --git a/tests/NLog.UnitTests/Config/CultureInfoTests.cs b/tests/NLog.UnitTests/Config/CultureInfoTests.cs index 82f354f4ac..72d4e94776 100644 --- a/tests/NLog.UnitTests/Config/CultureInfoTests.cs +++ b/tests/NLog.UnitTests/Config/CultureInfoTests.cs @@ -202,7 +202,7 @@ public void ExceptionTest() Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", false); logger.Error(ex, ""); -#if !NETSTANDARD +#if NETFRAMEWORK Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-DE", false); Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE", false); #endif diff --git a/tests/NLog.UnitTests/Config/ExtensionTests.cs b/tests/NLog.UnitTests/Config/ExtensionTests.cs index dfbcea52aa..cd2fdaa0fa 100644 --- a/tests/NLog.UnitTests/Config/ExtensionTests.cs +++ b/tests/NLog.UnitTests/Config/ExtensionTests.cs @@ -525,7 +525,7 @@ public void LoadExtensionFromAppDomain() "); // We get Exception for normal Assembly-Load only in net452. -#if !NETSTANDARD && !MONO +#if NETFRAMEWORK && !MONO var logs = writer.ToString(); Assert.Contains("Try find 'Manually-Loaded-Extension' in current domain", logs); #endif diff --git a/tests/NLog.UnitTests/Config/ReloadTests.cs b/tests/NLog.UnitTests/Config/ReloadTests.cs index b2f7ac564c..c8c7b25323 100644 --- a/tests/NLog.UnitTests/Config/ReloadTests.cs +++ b/tests/NLog.UnitTests/Config/ReloadTests.cs @@ -114,7 +114,7 @@ private static void SetLogManagerConfiguration(bool useExplicitFileLoading, stri [InlineData(false)] public void TestAutoReloadOnFileChange(bool useExplicitFileLoading) { -#if NETSTANDARD || MONO +#if !NETFRAMEWORK || MONO if (IsLinux()) { Console.WriteLine("[SKIP] ReloadTests.TestAutoReloadOnFileChange because we are running in Travis"); @@ -174,7 +174,7 @@ public void TestAutoReloadOnFileChange(bool useExplicitFileLoading) public void TestAutoReloadOnFileMove(bool useExplicitFileLoading) { -#if NETSTANDARD || MONO +#if !NETFRAMEWORK || MONO if (IsLinux()) { Console.WriteLine("[SKIP] ReloadTests.TestAutoReloadOnFileMove because we are running in Travis"); @@ -242,7 +242,7 @@ public void TestAutoReloadOnFileMove(bool useExplicitFileLoading) public void TestAutoReloadOnFileCopy(bool useExplicitFileLoading) { -#if NETSTANDARD || MONO +#if !NETFRAMEWORK || MONO if (IsLinux()) { Console.WriteLine("[SKIP] ReloadTests.TestAutoReloadOnFileCopy because we are running in Travis"); @@ -310,7 +310,7 @@ public void TestAutoReloadOnFileCopy(bool useExplicitFileLoading) public void TestIncludedConfigNoReload(bool useExplicitFileLoading) { -#if NETSTANDARD || MONO +#if !NETFRAMEWORK || MONO if (IsLinux()) { Console.WriteLine("[SKIP] ReloadTests.TestIncludedConfigNoReload because we are running in Travis"); @@ -376,7 +376,7 @@ public void TestIncludedConfigNoReload(bool useExplicitFileLoading) public void TestIncludedConfigReload(bool useExplicitFileLoading) { -#if NETSTANDARD || MONO +#if !NETFRAMEWORK || MONO if (IsLinux()) { Console.WriteLine("[SKIP] ReloadTests.TestIncludedConfigNoReload because we are running in Travis"); @@ -442,7 +442,7 @@ public void TestIncludedConfigReload(bool useExplicitFileLoading) public void TestMainConfigReload(bool useExplicitFileLoading) { -#if NETSTANDARD || MONO +#if !NETFRAMEWORK || MONO if (IsLinux()) { Console.WriteLine("[SKIP] ReloadTests.TestMainConfigReload because we are running in Travis"); @@ -513,7 +513,7 @@ public void TestMainConfigReload(bool useExplicitFileLoading) public void TestMainConfigReloadIncludedConfigNoReload(bool useExplicitFileLoading) { -#if NETSTANDARD || MONO +#if !NETFRAMEWORK || MONO if (IsLinux()) { Console.WriteLine("[SKIP] ReloadTests.TestMainConfigReload because we are running in Travis"); diff --git a/tests/NLog.UnitTests/ConfigFileLocatorTests.cs b/tests/NLog.UnitTests/ConfigFileLocatorTests.cs index 8f1c6737de..8a225f0c04 100644 --- a/tests/NLog.UnitTests/ConfigFileLocatorTests.cs +++ b/tests/NLog.UnitTests/ConfigFileLocatorTests.cs @@ -164,10 +164,10 @@ public void LoadConfigFile_NetCoreUnpublished_UseEntryDirectory() var appEnvMock = new AppEnvironmentMock(f => true, f => null) { AppDomainBaseDirectory = Path.Combine(tmpDir, "BaseDir"), -#if NETSTANDARD - AppDomainConfigurationFile = string.Empty, // NetCore style -#else +#if NETFRAMEWORK AppDomainConfigurationFile = Path.Combine(tmpDir, "EntryDir", "Entry.exe.config"), +#else + AppDomainConfigurationFile = string.Empty, // NetCore style #endif CurrentProcessFilePath = Path.Combine(tmpDir, "ProcessDir", "dotnet.exe"), // NetCore dotnet.exe EntryAssemblyLocation = Path.Combine(tmpDir, "EntryDir"), @@ -191,10 +191,11 @@ public void LoadConfigFile_NetCorePublished_UseBaseDirectory() var appEnvMock = new AppEnvironmentMock(f => true, f => null) { AppDomainBaseDirectory = Path.Combine(tmpDir, "BaseDir"), -#if NETSTANDARD - AppDomainConfigurationFile = string.Empty, // .NET 6 single-publish-style -#else +#if NETFRAMEWORK AppDomainConfigurationFile = Path.Combine(tmpDir, "BaseDir", "Entry.exe.config"), +#else + AppDomainConfigurationFile = string.Empty, // .NET 6 single-publish-style + #endif CurrentProcessFilePath = Path.Combine(tmpDir, "ProcessDir", "Entry.exe"), EntryAssemblyLocation = string.Empty, // .NET 6 single-publish-style @@ -218,10 +219,10 @@ public void LoadConfigFile_NetCorePublished_UseProcessDirectory() var appEnvMock = new AppEnvironmentMock(f => true, f => null) { AppDomainBaseDirectory = Path.Combine(tmpDir, "BaseDir"), -#if NETSTANDARD - AppDomainConfigurationFile = string.Empty, // NetCore style -#else +#if NETFRAMEWORK AppDomainConfigurationFile = Path.Combine(tmpDir, "ProcessDir", "Entry.exe.config"), +#else + AppDomainConfigurationFile = string.Empty, // NetCore style #endif CurrentProcessFilePath = Path.Combine(tmpDir, "ProcessDir", "Entry.exe"), // NetCore published exe EntryAssemblyLocation = Path.Combine(tmpDir, "ProcessDir"), @@ -245,10 +246,10 @@ public void LoadConfigFile_NetCoreSingleFilePublish_IgnoreTempDirectory() var appEnvMock = new AppEnvironmentMock(f => true, f => null) { AppDomainBaseDirectory = Path.Combine(tmpDir, "BaseDir"), -#if NETSTANDARD - AppDomainConfigurationFile = string.Empty, // NetCore style -#else +#if NETFRAMEWORK AppDomainConfigurationFile = Path.Combine(tmpDir, "TempProcessDir", "Entry.exe.config"), +#else + AppDomainConfigurationFile = string.Empty, // NetCore style #endif CurrentProcessFilePath = Path.Combine(tmpDir, "ProcessDir", "Entry.exe"), // NetCore published exe EntryAssemblyLocation = Path.Combine(tmpDir, "TempProcessDir"), @@ -262,7 +263,7 @@ public void LoadConfigFile_NetCoreSingleFilePublish_IgnoreTempDirectory() var result = fileLoader.GetDefaultCandidateConfigFilePaths().ToList(); // Assert base-directory + process-directory + nlog-assembly-directory -#if NETSTANDARD +#if !NETFRAMEWORK Assert.Equal(Path.Combine(tmpDir, "ProcessDir", "Entry.exe.nlog"), result.First(), StringComparer.OrdinalIgnoreCase); #endif AssertResult(tmpDir, "TempProcessDir", "ProcessDir", "Entry", result); @@ -276,10 +277,10 @@ public void LoadConfigFile_NetCoreSingleFilePublish_IgnoreTmpDirectory() var appEnvMock = new AppEnvironmentMock(f => true, f => null) { AppDomainBaseDirectory = Path.Combine(tmpDir, "BaseDir"), -#if NETSTANDARD - AppDomainConfigurationFile = string.Empty, // NetCore style -#else +#if NETFRAMEWORK AppDomainConfigurationFile = Path.Combine(tmpDir, "TempProcessDir", "Entry.exe.config"), +#else + AppDomainConfigurationFile = string.Empty, // NetCore style #endif CurrentProcessFilePath = Path.Combine(tmpDir, "ProcessDir", "Entry.exe"), // NetCore published exe EntryAssemblyLocation = Path.Combine(tmpDir, "TempProcessDir"), @@ -293,7 +294,7 @@ public void LoadConfigFile_NetCoreSingleFilePublish_IgnoreTmpDirectory() var result = fileLoader.GetDefaultCandidateConfigFilePaths().ToList(); // Assert base-directory + process-directory + nlog-assembly-directory -#if NETSTANDARD +#if !NETFRAMEWORK Assert.Equal(Path.Combine(tmpDir, "ProcessDir", "Entry.exe.nlog"), result.First(), StringComparer.OrdinalIgnoreCase); #endif AssertResult(tmpDir, "TempProcessDir", "ProcessDir", "Entry", result); @@ -349,7 +350,16 @@ public void CandidateConfigurationFileOnlyLoadedInitially() private static void AssertResult(string tmpDir, string appDir, string processDir, string appName, List result) { -#if NETSTANDARD +#if NETFRAMEWORK + Assert.Equal(Path.Combine(tmpDir, appDir, appName + ".exe.nlog"), result.First(), StringComparer.OrdinalIgnoreCase); + if (NLog.Internal.PlatformDetector.IsWin32) + { + if (processDir is null) + Assert.Equal(3, result.Count); // Case insensitive - No Assembly/Process-Directory + else + Assert.Equal(4, result.Count); // Case insensitive + } +#else Assert.Contains(Path.Combine(tmpDir, processDir ?? appDir, appName + ".exe.nlog"), result, StringComparer.OrdinalIgnoreCase); Assert.Contains(Path.Combine(tmpDir, appDir, "Entry.dll.nlog"), result, StringComparer.OrdinalIgnoreCase); if (NLog.Internal.PlatformDetector.IsWin32) @@ -365,22 +375,13 @@ private static void AssertResult(string tmpDir, string appDir, string processDir var priorityIndexExe = result.FindIndex(s => s.EndsWith(appName + ".exe.nlog")); var priorityIndexDll = result.FindIndex(s => s.EndsWith(appName + ".dll.nlog")); Assert.True(priorityIndexExe < priorityIndexDll, $"{appName + ".exe.nlog"}={priorityIndexExe} < {appName + ".dll.nlog"}={priorityIndexDll}"); // Always scan for exe.nlog first -#else - Assert.Equal(Path.Combine(tmpDir, appDir, appName + ".exe.nlog"), result.First(), StringComparer.OrdinalIgnoreCase); - if (NLog.Internal.PlatformDetector.IsWin32) - { - if (processDir is null) - Assert.Equal(3, result.Count); // Case insensitive - No Assembly/Process-Directory - else - Assert.Equal(4, result.Count); // Case insensitive - } #endif Assert.Contains(Path.Combine(tmpDir, "BaseDir", "NLog.config"), result, StringComparer.OrdinalIgnoreCase); Assert.Contains(Path.Combine(tmpDir, appDir, "NLog.config"), result, StringComparer.OrdinalIgnoreCase); Assert.Contains("NLog.dll.nlog", result.Last(), StringComparison.OrdinalIgnoreCase); } -#if !NETSTANDARD +#if NETFRAMEWORK private string appConfigContents = @" diff --git a/tests/NLog.UnitTests/Internal/AppDomainPartialTrustTests.cs b/tests/NLog.UnitTests/Internal/AppDomainPartialTrustTests.cs index e382002738..f8ddbfb9cf 100644 --- a/tests/NLog.UnitTests/Internal/AppDomainPartialTrustTests.cs +++ b/tests/NLog.UnitTests/Internal/AppDomainPartialTrustTests.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD && !MONO +#if NETFRAMEWORK && !MONO using System; using System.IO; diff --git a/tests/NLog.UnitTests/Internal/FileAppenders/FileAppenderCacheTests.cs b/tests/NLog.UnitTests/Internal/FileAppenders/FileAppenderCacheTests.cs index 532ad15ca1..11943b3f7b 100644 --- a/tests/NLog.UnitTests/Internal/FileAppenders/FileAppenderCacheTests.cs +++ b/tests/NLog.UnitTests/Internal/FileAppenders/FileAppenderCacheTests.cs @@ -194,7 +194,7 @@ public void FileAppenderCache_GetFileCharacteristics_Multi() FileAppenderCache_GetFileCharacteristics(appenderFactory, fileTarget); } -#if !MONO && !NETSTANDARD +#if NETFRAMEWORK && !MONO [Fact] public void FileAppenderCache_GetFileCharacteristics_Windows() { diff --git a/tests/NLog.UnitTests/Internal/PlatformDetectorTests.cs b/tests/NLog.UnitTests/Internal/PlatformDetectorTests.cs index 189cbf3e05..3c2a9a7ff0 100644 --- a/tests/NLog.UnitTests/Internal/PlatformDetectorTests.cs +++ b/tests/NLog.UnitTests/Internal/PlatformDetectorTests.cs @@ -38,7 +38,7 @@ namespace NLog.UnitTests.Internal { public class PlatformDetectorTests { -#if !NETSTANDARD +#if NETFRAMEWORK [Fact] public void IsMonoTest() { diff --git a/tests/NLog.UnitTests/LayoutRenderers/AppSettingTests.cs b/tests/NLog.UnitTests/LayoutRenderers/AppSettingTests.cs index 4ef3d2ee28..cece4c1f61 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/AppSettingTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/AppSettingTests.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD +#if NETFRAMEWORK namespace NLog.UnitTests.LayoutRenderers { diff --git a/tests/NLog.UnitTests/LayoutRenderers/AssemblyVersionTests.cs b/tests/NLog.UnitTests/LayoutRenderers/AssemblyVersionTests.cs index 94b11b499f..3f555025f8 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/AssemblyVersionTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/AssemblyVersionTests.cs @@ -44,7 +44,7 @@ public class AssemblyVersionTests : NLogTestBase { private readonly ITestOutputHelper _testOutputHelper; -#if !NETSTANDARD +#if NETFRAMEWORK private static Lazy TestAssembly = new Lazy(() => GenerateTestAssembly()); #endif @@ -109,7 +109,7 @@ public void AssemblyVersionFormatTest(string format, string expected) AssertLayoutRendererOutput($"${{assembly-version:name=NLogAutoLoadExtension:format={format}}}", expected); } -#if !NETSTANDARD +#if NETFRAMEWORK private const string AssemblyVersionTest = "1.2.3.4"; private const string AssemblyFileVersionTest = "1.1.1.2"; private const string AssemblyInformationalVersionTest = "Version 1"; diff --git a/tests/NLog.UnitTests/LayoutRenderers/CallSiteFileNameLayoutTests.cs b/tests/NLog.UnitTests/LayoutRenderers/CallSiteFileNameLayoutTests.cs index 914e8f183b..409a135731 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/CallSiteFileNameLayoutTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/CallSiteFileNameLayoutTests.cs @@ -40,10 +40,10 @@ namespace NLog.UnitTests.LayoutRenderers public class CallSiteFileNameLayoutTests : NLogTestBase { -#if !MONO - [Fact] -#else +#if MONO [Fact(Skip = "MONO is not good with callsite line numbers")] +#else + [Fact] #endif public void ShowFileNameOnlyTest() { @@ -64,10 +64,10 @@ public void ShowFileNameOnlyTest() Assert.Equal("msg", lastMessageArray[1]); } -#if !MONO - [Fact] -#else +#if MONO [Fact(Skip = "MONO is not good with callsite line numbers")] +#else + [Fact] #endif public void CallSiteFileNameNoCaptureStackTraceTest() { @@ -87,10 +87,10 @@ public void CallSiteFileNameNoCaptureStackTraceTest() logFactory.AssertDebugLastMessage("|msg"); } -#if !MONO - [Fact] -#else +#if MONO [Fact(Skip = "MONO is not good with callsite line numbers")] +#else + [Fact] #endif public void CallSiteFileNameNoCaptureStackTraceWithStackTraceTest() { @@ -115,10 +115,10 @@ public void CallSiteFileNameNoCaptureStackTraceWithStackTraceTest() Assert.Equal("msg", lastMessageArray[1]); } -#if !MONO - [Fact] -#else +#if MONO [Fact(Skip = "MONO is not good with callsite line numbers")] +#else + [Fact] #endif public void ShowFullPathTest() { @@ -141,10 +141,10 @@ public void ShowFullPathTest() Assert.Equal("msg", lastMessageArray[1]); } -#if !MONO - [Fact] -#else +#if MONO [Fact(Skip = "MONO is not good with callsite line numbers")] +#else + [Fact] #endif public void ShowFileNameOnlyAsyncTest() { @@ -164,10 +164,10 @@ public void ShowFileNameOnlyAsyncTest() Assert.Equal("msg", lastMessageArray[1]); } -#if !MONO - [Fact] -#else +#if MONO [Fact(Skip = "MONO is not good with callsite line numbers")] +#else + [Fact] #endif public void ShowFullPathAsyncTest() { diff --git a/tests/NLog.UnitTests/LayoutRenderers/CallSiteLineNumberTests.cs b/tests/NLog.UnitTests/LayoutRenderers/CallSiteLineNumberTests.cs index f98cc5fa6c..40c12d9fa1 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/CallSiteLineNumberTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/CallSiteLineNumberTests.cs @@ -70,10 +70,11 @@ public void LineNumberOnlyTest() #endif } -#if !MONO - [Fact] -#else + +#if MONO [Fact(Skip = "MONO is not good with callsite line numbers")] +#else + [Fact] #endif public void LineNumberOnlyAsyncTest() { diff --git a/tests/NLog.UnitTests/LayoutRenderers/CallSiteTests.cs b/tests/NLog.UnitTests/LayoutRenderers/CallSiteTests.cs index 5e4f30f25d..df778c9814 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/CallSiteTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/CallSiteTests.cs @@ -60,7 +60,7 @@ public static void LogDebug(this ILogger logger) public class CallSiteTests : NLogTestBase { -#if !NETSTANDARD +#if NETFRAMEWORK [Fact] public void HiddenAssemblyTest() { diff --git a/tests/NLog.UnitTests/LogManagerTests.cs b/tests/NLog.UnitTests/LogManagerTests.cs index 6669cc79d6..6f21b32bb3 100644 --- a/tests/NLog.UnitTests/LogManagerTests.cs +++ b/tests/NLog.UnitTests/LogManagerTests.cs @@ -251,7 +251,7 @@ private void OnConfigReloaded(object sender, LoggingConfigurationChangedEventArg [Fact] public void AutoReloadTest() { -#if NETSTANDARD || MONO +#if !NETFRAMEWORK || MONO if (IsLinux()) { Console.WriteLine("[SKIP] LogManagerTests.AutoReloadTest because we are running in Travis"); diff --git a/tests/NLog.UnitTests/NLogTestBase.cs b/tests/NLog.UnitTests/NLogTestBase.cs index 606724012a..9c4f40c6b9 100644 --- a/tests/NLog.UnitTests/NLogTestBase.cs +++ b/tests/NLog.UnitTests/NLogTestBase.cs @@ -46,7 +46,7 @@ namespace NLog.UnitTests using NLog.Layouts; using NLog.Targets; using Xunit; -#if !NETSTANDARD +#if NETFRAMEWORK using Ionic.Zip; #endif @@ -72,7 +72,7 @@ protected NLogTestBase() LogManager.ThrowExceptions = true; // Ensure exceptions are thrown by default during unit-testing LogManager.ThrowConfigExceptions = null; System.Diagnostics.Trace.Listeners.Clear(); -#if !NETSTANDARD +#if NETFRAMEWORK System.Diagnostics.Debug.Listeners.Clear(); #endif } @@ -161,7 +161,7 @@ public void CompressFile(string fileName, string archiveFileName) public void CompressFile(string fileName, string archiveFileName, string entryName) { -#if !NETSTANDARD +#if NETFRAMEWORK using (var zip = new Ionic.Zip.ZipFile()) { ZipEntry entry = zip.AddFile(fileName); diff --git a/tests/NLog.UnitTests/ProcessRunner.cs b/tests/NLog.UnitTests/ProcessRunner.cs index 37e0c9f532..3b0d2f03a0 100644 --- a/tests/NLog.UnitTests/ProcessRunner.cs +++ b/tests/NLog.UnitTests/ProcessRunner.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD +#if NETFRAMEWORK namespace NLog.UnitTests { diff --git a/tests/NLog.UnitTests/Targets/ConcurrentFileTargetTests.cs b/tests/NLog.UnitTests/Targets/ConcurrentFileTargetTests.cs index b13fb3ea52..3eb0eacd96 100644 --- a/tests/NLog.UnitTests/Targets/ConcurrentFileTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/ConcurrentFileTargetTests.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !NETSTANDARD +#if NETFRAMEWORK #define DISABLE_FILE_INTERNAL_LOGGING diff --git a/tests/NLog.UnitTests/Targets/DebugSystemTargetTests.cs b/tests/NLog.UnitTests/Targets/DebugSystemTargetTests.cs index 1556272e60..2534b2204f 100644 --- a/tests/NLog.UnitTests/Targets/DebugSystemTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/DebugSystemTargetTests.cs @@ -46,7 +46,7 @@ public void DebugWriteLineTest() try { // Arrange -#if !NETSTANDARD +#if NETFRAMEWORK Debug.Listeners.Clear(); Debug.Listeners.Add(new TextWriterTraceListener(sw)); #endif @@ -60,13 +60,13 @@ public void DebugWriteLineTest() // Assert Assert.Single(logFactory.Configuration.AllTargets); -#if !NETSTANDARD +#if NETFRAMEWORK Assert.Contains("Hello World", sw.ToString()); #endif } finally { -#if !NETSTANDARD +#if NETFRAMEWORK Debug.Listeners.Clear(); #endif } @@ -80,7 +80,7 @@ public void DebugWriteLineHeaderFooterTest() try { // Arrange -#if !NETSTANDARD +#if NETFRAMEWORK Debug.Listeners.Clear(); Debug.Listeners.Add(new TextWriterTraceListener(sw)); #endif @@ -102,7 +102,7 @@ public void DebugWriteLineHeaderFooterTest() // Assert Assert.Single(logFactory.Configuration.AllTargets); -#if !NETSTANDARD +#if NETFRAMEWORK Assert.Contains("Startup", sw.ToString()); Assert.Contains("Hello World", sw.ToString()); Assert.DoesNotContain("Shutdown", sw.ToString()); @@ -113,7 +113,7 @@ public void DebugWriteLineHeaderFooterTest() } finally { -#if !NETSTANDARD +#if NETFRAMEWORK Debug.Listeners.Clear(); #endif } diff --git a/tests/NLog.UnitTests/Targets/EventLogTargetTests.cs b/tests/NLog.UnitTests/Targets/EventLogTargetTests.cs index ab05c38fa6..21fa393856 100644 --- a/tests/NLog.UnitTests/Targets/EventLogTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/EventLogTargetTests.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#if !MONO && !NETSTANDARD +#if NETFRAMEWORK && !MONO namespace NLog.UnitTests.Targets { diff --git a/tests/NLog.UnitTests/Targets/FileTargetTests.cs b/tests/NLog.UnitTests/Targets/FileTargetTests.cs index a62356b990..513bd11605 100644 --- a/tests/NLog.UnitTests/Targets/FileTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/FileTargetTests.cs @@ -145,7 +145,7 @@ public void SimpleFileTest(bool concurrentWrites, bool keepFileOpen, bool forceM public void SimpleFileDeleteTest(bool concurrentWrites, bool keepFileOpen, bool forceManaged, bool forceMutexConcurrentWrites) { bool isSimpleKeepFileOpen = keepFileOpen && !concurrentWrites -#if !NETSTANDARD && !MONO +#if NETFRAMEWORK && !MONO && IsLinux() #endif ; @@ -744,7 +744,7 @@ public void DeleteFileOnStartTest_noExceptionWhenMissing() } } -#if !NETSTANDARD +#if NETFRAMEWORK public static IEnumerable ArchiveFileOnStartTests_TestParameters { get @@ -1551,7 +1551,7 @@ from forceManaged in booleanValues [MemberData(nameof(DateArchive_UsesDateFromCurrentTimeSource_TestParameters))] public void DateArchive_UsesDateFromCurrentTimeSource(DateTimeKind timeKind, bool includeDateInLogFilePath, bool concurrentWrites, bool keepFileOpen, bool includeSequenceInArchive, bool forceManaged, bool forceMutexConcurrentWrites, bool maxArhiveDays) { -#if NETSTANDARD || MONO +#if !NETFRAMEWORK || MONO if (IsLinux()) { Console.WriteLine("[SKIP] FileTargetTests.DateArchive_UsesDateFromCurrentTimeSource because SetLastWriteTime is not working on Travis"); diff --git a/tests/NLog.UnitTests/Targets/MailTargetTests.cs b/tests/NLog.UnitTests/Targets/MailTargetTests.cs index 3c02614240..6facbb32e3 100644 --- a/tests/NLog.UnitTests/Targets/MailTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/MailTargetTests.cs @@ -35,7 +35,7 @@ namespace NLog.UnitTests.Targets { using System; using System.Collections.Generic; -#if !NETSTANDARD +#if NETFRAMEWORK using System.Net.Configuration; #endif using System.IO; @@ -826,7 +826,7 @@ public void MailTarget_UseSystemNetMailSettings_True_ReadFromFromConfigFile_dont SmtpPort = 27, Body = "${level} ${logger} ${message}", UseSystemNetMailSettings = true, -#if !NETSTANDARD +#if NETFRAMEWORK SmtpSection = new SmtpSection { From = "config@foo.com" } #endif }; @@ -840,7 +840,7 @@ public void MailTarget_UseSystemNetMailSettings_True_ReadFromFromConfigFile_dont Assert.Equal("nlog@foo.com", mmt.From.ToString()); } -#if !NETSTANDARD +#if NETFRAMEWORK [Fact] public void MailTarget_UseSystemNetMailSettings_True_ReadFromFromConfigFile() { @@ -865,7 +865,7 @@ public void MailTarget_UseSystemNetMailSettings_True_ReadFromFromConfigFile() } #endif -#if !NETSTANDARD +#if NETFRAMEWORK [Fact] public void MailTarget_UseSystemNetMailSettings_False_ReadFromFromConfigFile() { diff --git a/tests/NLog.UnitTests/Targets/WebServiceTargetTests.cs b/tests/NLog.UnitTests/Targets/WebServiceTargetTests.cs index e270340ce4..85a86c799a 100644 --- a/tests/NLog.UnitTests/Targets/WebServiceTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/WebServiceTargetTests.cs @@ -44,7 +44,7 @@ namespace NLog.UnitTests.Targets using System.Threading; using System.Threading.Tasks; using System.Xml.Serialization; -#if !NETSTANDARD +#if NETFRAMEWORK using System.Web.Http; using System.Web.Http.Dependencies; using Microsoft.Owin.Hosting; @@ -214,7 +214,7 @@ private static string StreamToString(Stream s) } } -#if !NETSTANDARD +#if NETFRAMEWORK private static string getNewWsAddress() { string WsAddress = "http://localhost:9000/"; diff --git a/tests/NLog.WindowsRegistry.Tests/RegistryTests.cs b/tests/NLog.WindowsRegistry.Tests/RegistryTests.cs index e80cfcc5bb..c25108a5b5 100644 --- a/tests/NLog.WindowsRegistry.Tests/RegistryTests.cs +++ b/tests/NLog.WindowsRegistry.Tests/RegistryTests.cs @@ -37,7 +37,7 @@ namespace NLog.WindowsRegistry.Tests using Microsoft.Win32; using Xunit; -#if NETSTANDARD +#if !NETFRAMEWORK [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif public sealed class RegistryTests : IDisposable From 030ad7b98670e663c65cfda1d4f4fc88fda4ea1e Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 5 Oct 2024 18:27:04 +0200 Subject: [PATCH 010/224] Removed obsolete method LogAssemblyVersion from InternalLogger (#5632) --- src/NLog/Common/InternalLogger.cs | 28 --------- src/NLog/Config/AssemblyExtensionLoader.cs | 34 +++++++++- src/NLog/Internal/AssemblyHelpers.cs | 63 +++++++++---------- src/NLog/LogFactory.cs | 6 +- .../Internal/AppDomainPartialTrustTests.cs | 4 +- 5 files changed, 64 insertions(+), 71 deletions(-) diff --git a/src/NLog/Common/InternalLogger.cs b/src/NLog/Common/InternalLogger.cs index ccde45f244..c8503b1dde 100644 --- a/src/NLog/Common/InternalLogger.cs +++ b/src/NLog/Common/InternalLogger.cs @@ -37,7 +37,6 @@ namespace NLog.Common using System.ComponentModel; using System.Globalization; using System.IO; - using System.Reflection; using JetBrains.Annotations; using NLog.Internal; using NLog.Time; @@ -385,33 +384,6 @@ internal static bool HasActiveLoggers() return true; } - /// - /// Logs the assembly version and file version of the given Assembly. - /// - /// The assembly to log. - [Obsolete("InternalLogger should be minimal. Marked obsolete with NLog v5.3")] - public static void LogAssemblyVersion(Assembly assembly) - { - try - { - var fileVersion = assembly.GetFirstCustomAttribute()?.Version; - var productVersion = assembly.GetFirstCustomAttribute()?.InformationalVersion; - var globalAssemblyCache = false; -#if NETFRAMEWORK - if (assembly.GlobalAssemblyCache) - globalAssemblyCache = true; -#endif - Info("{0}. File version: {1}. Product version: {2}. GlobalAssemblyCache: {3}", - assembly.FullName, - fileVersion, - productVersion, - globalAssemblyCache); - } - catch (Exception ex) - { - Error(ex, "Error logging version of assembly {0}.", assembly?.FullName); - } - } private static void CreateDirectoriesIfNeeded(string filename) { try diff --git a/src/NLog/Config/AssemblyExtensionLoader.cs b/src/NLog/Config/AssemblyExtensionLoader.cs index f8600c8451..5dfeab39a4 100644 --- a/src/NLog/Config/AssemblyExtensionLoader.cs +++ b/src/NLog/Config/AssemblyExtensionLoader.cs @@ -65,10 +65,10 @@ public void LoadAssemblyFromName(ConfigurationItemFactory factory, string assemb public void LoadAssembly(ConfigurationItemFactory factory, Assembly assembly, string itemNamePrefix) { - InternalLogger.LogAssemblyVersion(assembly); + AssemblyHelpers.LogAssemblyVersion(assembly); InternalLogger.Debug("ScanAssembly('{0}')", assembly.FullName); - var typesToScan = AssemblyHelpers.SafeGetTypes(assembly); + var typesToScan = SafeGetTypes(assembly); if (typesToScan?.Length > 0) { if (ReferenceEquals(assembly, typeof(LogFactory).Assembly)) @@ -97,6 +97,36 @@ public void LoadAssembly(ConfigurationItemFactory factory, Assembly assembly, st InternalLogger.Debug("Loading assembly name: {0} succeeded!", assembly.FullName); } + /// + /// Gets all usable exported types from the given assembly. + /// + /// Assembly to scan. + /// Usable types from the given assembly. + /// Types which cannot be loaded are skipped. + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2026")] + private static Type[] SafeGetTypes(Assembly assembly) + { + try + { + return assembly.GetTypes(); + } + catch (ReflectionTypeLoadException typeLoadException) + { + var result = typeLoadException.Types?.Where(t => t != null)?.ToArray() ?? ArrayHelper.Empty(); + InternalLogger.Warn(typeLoadException, "Loaded {0} valid types from Assembly: {1}", result.Length, assembly.FullName); + foreach (var ex in typeLoadException.LoaderExceptions ?? ArrayHelper.Empty()) + { + InternalLogger.Warn(ex, "Type load exception."); + } + return result; + } + catch (Exception ex) + { + InternalLogger.Warn(ex, "Failed to load types from Assembly: {0}", assembly.FullName); + return ArrayHelper.Empty(); + } + } + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2072")] [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2067")] private static void RegisterTypeFromAssembly(ConfigurationItemFactory factory, Type type, string itemNamePrefix) diff --git a/src/NLog/Internal/AssemblyHelpers.cs b/src/NLog/Internal/AssemblyHelpers.cs index e3e8aeba30..a58fd47d32 100644 --- a/src/NLog/Internal/AssemblyHelpers.cs +++ b/src/NLog/Internal/AssemblyHelpers.cs @@ -34,7 +34,6 @@ namespace NLog.Internal { using System; using System.IO; - using System.Linq; using System.Reflection; using NLog.Common; @@ -43,38 +42,6 @@ namespace NLog.Internal /// internal static class AssemblyHelpers { - /// - /// Gets all usable exported types from the given assembly. - /// - /// Assembly to scan. - /// Usable types from the given assembly. - /// Types which cannot be loaded are skipped. - [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2026")] - [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] - public static Type[] SafeGetTypes(Assembly assembly) - { - try - { - return assembly.GetTypes(); - } - catch (ReflectionTypeLoadException typeLoadException) - { - var result = typeLoadException.Types?.Where(t => t != null)?.ToArray() ?? ArrayHelper.Empty(); - InternalLogger.Warn(typeLoadException, "Loaded {0} valid types from Assembly: {1}", result.Length, assembly.FullName); - foreach (var ex in typeLoadException.LoaderExceptions ?? ArrayHelper.Empty()) - { - InternalLogger.Warn(ex, "Type load exception."); - } - return result; - } - catch (Exception ex) - { - InternalLogger.Warn(ex, "Failed to load types from Assembly: {0}", assembly.FullName); - return ArrayHelper.Empty(); - } - } - - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] public static string GetAssemblyFileLocation(Assembly assembly) { string assemblyFullName = string.Empty; @@ -149,5 +116,35 @@ public static string GetAssemblyFileLocation(Assembly assembly) return string.Empty; } } + + /// + /// Logs the assembly version and file version of the given Assembly. + /// + /// The assembly to log. + public static void LogAssemblyVersion(Assembly assembly) + { + try + { + if (!InternalLogger.IsInfoEnabled) + return; + + var fileVersion = assembly.GetFirstCustomAttribute()?.Version; + var productVersion = assembly.GetFirstCustomAttribute()?.InformationalVersion; + var globalAssemblyCache = false; +#if NETFRAMEWORK + if (assembly.GlobalAssemblyCache) + globalAssemblyCache = true; +#endif + InternalLogger.Info("{0}. File version: {1}. Product version: {2}. GlobalAssemblyCache: {3}", + assembly.FullName, + fileVersion, + productVersion, + globalAssemblyCache); + } + catch (Exception ex) + { + InternalLogger.Error(ex, "Error logging version of assembly {0}.", assembly?.FullName); + } + } } } diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index a307451c73..6164c1b8af 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -345,15 +345,11 @@ public CultureInfo DefaultCultureInfo } internal CultureInfo _defaultCultureInfo; - [Obsolete("LogFactory should be minimal. Marked obsolete with NLog v5.3")] internal static void LogNLogAssemblyVersion() { - if (!InternalLogger.IsInfoEnabled) - return; - try { - InternalLogger.LogAssemblyVersion(typeof(LogFactory).Assembly); + AssemblyHelpers.LogAssemblyVersion(typeof(LogFactory).Assembly); } catch (Exception ex) { diff --git a/tests/NLog.UnitTests/Internal/AppDomainPartialTrustTests.cs b/tests/NLog.UnitTests/Internal/AppDomainPartialTrustTests.cs index f8ddbfb9cf..e9c730810b 100644 --- a/tests/NLog.UnitTests/Internal/AppDomainPartialTrustTests.cs +++ b/tests/NLog.UnitTests/Internal/AppDomainPartialTrustTests.cs @@ -141,7 +141,7 @@ public void PartialTrustSuccess(int times, string fileWritePath, bool autoShutdo // NOTE Using BufferingWrapper to validate that DomainUnload remembers to perform flush var configXml = $@" - + @@ -157,10 +157,8 @@ public void PartialTrustSuccess(int times, string fileWritePath, bool autoShutdo { LogManager.Configuration = XmlLoggingConfiguration.CreateFromXmlString(configXml); -#pragma warning disable CS0618 // Type or member is obsolete //this method gave issues LogFactory.LogNLogAssemblyVersion(); -#pragma warning restore CS0618 // Type or member is obsolete var logger = LogManager.GetLogger("NLog.UnitTests.Targets.FileTargetTests"); From dcfff3fd18cb344d6382681ca8bd1833bd28d02b Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 5 Oct 2024 21:24:11 +0200 Subject: [PATCH 011/224] Removed constructor reflection from ServiceRepository (#5633) --- src/NLog/Config/ServiceRepositoryInternal.cs | 145 ++---------------- .../Config/ServiceRepositoryTests.cs | 110 ------------- 2 files changed, 10 insertions(+), 245 deletions(-) diff --git a/src/NLog/Config/ServiceRepositoryInternal.cs b/src/NLog/Config/ServiceRepositoryInternal.cs index e3ecf8c333..d3c188de43 100644 --- a/src/NLog/Config/ServiceRepositoryInternal.cs +++ b/src/NLog/Config/ServiceRepositoryInternal.cs @@ -35,9 +35,6 @@ namespace NLog.Config { using System; using System.Collections.Generic; - using System.Reflection; - using JetBrains.Annotations; - using NLog.Common; using NLog.Internal; /// @@ -46,7 +43,6 @@ namespace NLog.Config internal sealed class ServiceRepositoryInternal : ServiceRepository { private readonly Dictionary> _creatorMap = new Dictionary>(); - private readonly Dictionary _lateBoundMap = new Dictionary(); private readonly object _lockObject = new object(); public event EventHandler TypeRegistered; @@ -77,151 +73,30 @@ public override void RegisterService(Type type, object instance) public override object GetService(Type serviceType) { - var serviceInstance = DefaultResolveInstance(serviceType, null); - if (serviceInstance is null && serviceType.IsAbstract) - { + object serviceInstance = TryGetService(serviceType); + if (serviceInstance is null) throw new NLogDependencyResolveException("Instance of class must be registered", serviceType); - } - return serviceInstance; - } - internal override bool TryGetService(out T serviceInstance) - { - serviceInstance = DefaultResolveInstance(typeof(T), null) as T; - return !(serviceInstance is null); + return serviceInstance; } - private object DefaultResolveInstance(Type itemType, HashSet seenTypes) + private object TryGetService(Type serviceType) { - Guard.ThrowIfNull(itemType); + Guard.ThrowIfNull(serviceType); Func objectResolver = null; - CompiledConstructor compiledConstructor = null; - lock (_lockObject) { - if (!_creatorMap.TryGetValue(itemType, out objectResolver)) - { - _lateBoundMap.TryGetValue(itemType, out compiledConstructor); - } + _creatorMap.TryGetValue(serviceType, out objectResolver); } - if (objectResolver is null && compiledConstructor is null) - { - if (itemType.IsAbstract) - { - if (seenTypes is null) - return null; - else - throw new NLogDependencyResolveException("Instance of class must be registered", itemType); - } - - // Do not hold lock while resolving types to avoid deadlock on initialization of type static members -#pragma warning disable CS0618 // Type or member is obsolete - var newCompiledConstructor = CreateCompiledConstructor(itemType); -#pragma warning restore CS0618 // Type or member is obsolete - - lock (_lockObject) - { - if (!_lateBoundMap.TryGetValue(itemType, out compiledConstructor)) - { - _lateBoundMap.Add(itemType, newCompiledConstructor); - compiledConstructor = newCompiledConstructor; - } - } - } - - // Do not hold lock while calling constructor (or resolving parameter values) to avoid deadlock - var constructorParameters = compiledConstructor?.Parameters; - if (constructorParameters is null) - { - return objectResolver?.Invoke() ?? compiledConstructor?.Ctor(null); - } - else - { - seenTypes = seenTypes ?? new HashSet(); - var parameterValues = CreateCtorParameterValues(constructorParameters, seenTypes); - return compiledConstructor?.Ctor(parameterValues); - } + return objectResolver?.Invoke(); } - [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] - [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Ignore since obsolete", "IL2070")] - private CompiledConstructor CreateCompiledConstructor(Type itemType) - { - try - { - InternalLogger.Debug("Object reflection needed to create instance of type: {0}", itemType); - - var defaultConstructor = itemType.GetConstructor(Type.EmptyTypes); - if (defaultConstructor is null) - { - InternalLogger.Trace("Resolves parameterized constructor for {0}", itemType); - var ctors = itemType.GetConstructors(); - if (ctors.Length == 0) - { - throw new NLogDependencyResolveException("No public constructor", itemType); - } - - if (ctors.Length > 1) - { - throw new NLogDependencyResolveException("Multiple public constructor are not supported if there isn't a default constructor'", itemType); - } - - var ctor = ctors[0]; - var constructorMethod = ReflectionHelpers.CreateLateBoundConstructor(ctor); - return new CompiledConstructor(constructorMethod, ctor.GetParameters()); - } - else - { - InternalLogger.Trace("Resolves default constructor for {0}", itemType); - var constructorMethod = ReflectionHelpers.CreateLateBoundConstructor(defaultConstructor); - return new CompiledConstructor(constructorMethod); - } - } - catch (MissingMethodException exception) - { - throw new NLogDependencyResolveException("Is the required permission granted?", exception, itemType); - } - finally - { - InternalLogger.Trace("Resolve {0} done", itemType.FullName); - } - } - - private object[] CreateCtorParameterValues(ParameterInfo[] parameterInfos, HashSet seenTypes) - { - var parameterValues = new object[parameterInfos.Length]; - - for (var i = 0; i < parameterInfos.Length; i++) - { - var param = parameterInfos[i]; - - var parameterType = param.ParameterType; - if (seenTypes.Contains(parameterType)) - { - throw new NLogDependencyResolveException("There is a cycle", parameterType); - } - - seenTypes.Add(parameterType); - - var paramValue = DefaultResolveInstance(parameterType, seenTypes); - parameterValues[i] = paramValue; - } - - return parameterValues; - } - - private sealed class CompiledConstructor + internal override bool TryGetService(out T serviceInstance) { - [NotNull] public ReflectionHelpers.LateBoundConstructor Ctor { get; } - [CanBeNull] public ParameterInfo[] Parameters { get; } - - public CompiledConstructor([NotNull] ReflectionHelpers.LateBoundConstructor ctor, ParameterInfo[] parameters = null) - { - Ctor = Guard.ThrowIfNull(ctor); - Parameters = parameters; - } + serviceInstance = TryGetService(typeof(T)) as T; + return !(serviceInstance is null); } } } diff --git a/tests/NLog.UnitTests/Config/ServiceRepositoryTests.cs b/tests/NLog.UnitTests/Config/ServiceRepositoryTests.cs index d0827bc2f0..2f58b2cfe3 100644 --- a/tests/NLog.UnitTests/Config/ServiceRepositoryTests.cs +++ b/tests/NLog.UnitTests/Config/ServiceRepositoryTests.cs @@ -86,52 +86,6 @@ public void SideBySideLogFactoryInternalInterfaceTest() Assert.Equal("Kenny" + "_" + name2, target2.LastMessage); } - [Fact] - public void ResolveShouldInjectDependencies() - { - // Arrange - var logFactory = new LogFactory(); - - // Act - var target = logFactory.ServiceRepository.ResolveService(); - - // Assert - Assert.NotNull(target.JsonConverter); - } - - [Fact] - public void ResolveShouldInjectNestedDependencies() - { - // Arrange - var logFactory = new LogFactory(); - - // Act - var target = logFactory.ServiceRepository.ResolveService(); - - // Assert - Assert.NotNull(target.Helper.JsonConverter); - } - - [Fact] - public void ResolveWithDirectCycleShouldThrow() - { - // Arrange - var logFactory = new LogFactory(); - - // Act & Assert - AssertCycleException(logFactory); - } - - [Fact] - public void ResolveWithIndirectCycleShouldThrow() - { - // Arrange - var logFactory = new LogFactory(); - - // Act & Assert - AssertCycleException(logFactory); - } - [Fact] public void HandleDelayedInjectDependenciesFailure() { @@ -258,70 +212,6 @@ public bool SerializeObject(object value, StringBuilder builder) } } - private class TargetWithInjection : Target - { - public TargetWithInjection([NotNull] IJsonConverter jsonConverter) - { - JsonConverter = Guard.ThrowIfNull(jsonConverter); - } - - public IJsonConverter JsonConverter { get; } - } - - private class TargetWithNestedInjection : Target - { - public ClassWithInjection Helper { get; } - - /// - public TargetWithNestedInjection([NotNull] ClassWithInjection helper) - { - Helper = Guard.ThrowIfNull(helper); - } - } - - private class TargetWithDirectCycleInjection : Target - { - /// - public TargetWithDirectCycleInjection(TargetWithDirectCycleInjection cycle1) - { - } - } - - private class TargetWithIndirectCycleInjection : Target - { - /// - public TargetWithIndirectCycleInjection(CycleHelperClass1 helper) - { - } - } - - private class CycleHelperClass1 - { - /// - public CycleHelperClass1(CycleHelperClass2 helper) - { - } - } - - private class CycleHelperClass2 - { - /// - public CycleHelperClass2(CycleHelperClass1 helper) - { - } - } - - private class ClassWithInjection - { - public IJsonConverter JsonConverter { get; } - - /// - public ClassWithInjection(IJsonConverter jsonConverter) - { - JsonConverter = jsonConverter; - } - } - private class TargetWithMissingDependency : Target { public LogEventInfo LastLogEvent { get; private set; } From 7d5854be3219ce7c953de343ebf52d7d40285e77 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 6 Oct 2024 11:00:05 +0200 Subject: [PATCH 012/224] Move WebServiceTarget to its own nuget-package (#5634) --- Test-XmlFile.ps1 | 2 +- build.ps1 | 1 + run-tests.ps1 | 4 + .../NLog.OutputDebugString.csproj | 2 +- src/NLog.Targets.WebService/N.png | Bin 0 -> 1903 bytes .../NLog.Targets.WebService.csproj | 82 +++++++++++ .../Properties/AssemblyInfo.cs | 47 +++++++ src/NLog.Targets.WebService/README.md | 25 ++++ .../WebServiceProtocol.cs | 0 .../WebServiceProxyType.cs | 0 .../WebServiceTarget.cs | 132 ++++++++++++------ src/NLog.sln | 14 ++ src/NLog/Config/AssemblyExtensionTypes.cs | 1 - .../NLog.Targets.WebService.Tests.csproj | 33 +++++ .../NLogTests.snk | Bin 0 -> 596 bytes .../WebServiceTargetTests.cs | 13 +- tests/NLog.UnitTests/NLog.UnitTests.csproj | 3 - .../NLog.WindowsRegistry.Tests.csproj | 4 - 18 files changed, 303 insertions(+), 60 deletions(-) create mode 100644 src/NLog.Targets.WebService/N.png create mode 100644 src/NLog.Targets.WebService/NLog.Targets.WebService.csproj create mode 100644 src/NLog.Targets.WebService/Properties/AssemblyInfo.cs create mode 100644 src/NLog.Targets.WebService/README.md rename src/{NLog/Targets => NLog.Targets.WebService}/WebServiceProtocol.cs (100%) rename src/{NLog/Targets => NLog.Targets.WebService}/WebServiceProxyType.cs (100%) rename src/{NLog/Targets => NLog.Targets.WebService}/WebServiceTarget.cs (87%) create mode 100644 tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj create mode 100644 tests/NLog.Targets.WebService.Tests/NLogTests.snk rename tests/{NLog.UnitTests/Targets => NLog.Targets.WebService.Tests}/WebServiceTargetTests.cs (99%) diff --git a/Test-XmlFile.ps1 b/Test-XmlFile.ps1 index ef2fb339bf..1d9412e4ab 100644 --- a/Test-XmlFile.ps1 +++ b/Test-XmlFile.ps1 @@ -54,7 +54,7 @@ function Test-XmlFile $pwd = get-location; $testFilesDir = "$pwd\examples\targets\Configuration File" $xsdFilePath = "$pwd\src\NLog\bin\Release\NLog.xsd" -$excludedTests = ("MessageBox","RichTextBox","FormControl","PerfCounter","OutputDebugString","MSMQ","Database") +$excludedTests = ("MessageBox","RichTextBox","FormControl","PerfCounter","OutputDebugString","MSMQ","Database","WebService") # Returns true if all selected tests in examples directory are valid $ret = $true Get-ChildItem -Path $testFilesDir -Directory -Exclude $excludedTests | Get-ChildItem -Recurse -File -Filter NLog.config | % { diff --git a/build.ps1 b/build.ps1 index 0edd7faf51..ab28a356f4 100644 --- a/build.ps1 +++ b/build.ps1 @@ -36,6 +36,7 @@ function create-package($packageName, $targetFrameworks) } create-package 'NLog.Database' '"net35;net45;net46;netstandard2.0"' +create-package 'NLog.Targets.WebService' '"net45;net46;netstandard2.0"' create-package 'NLog.OutputDebugString' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.WindowsRegistry' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.WindowsEventLog' '"netstandard2.0"' diff --git a/run-tests.ps1 b/run-tests.ps1 index 0fecf6f43a..bb0522b1fc 100644 --- a/run-tests.ps1 +++ b/run-tests.ps1 @@ -24,6 +24,10 @@ if ($isWindows -or $Env:WinDir) if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.WebService.Tests/ --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + dotnet test ./tests/NLog.WindowsRegistry.Tests/ --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } diff --git a/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj b/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj index 4fee6c5e45..65566d94c0 100644 --- a/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj +++ b/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj @@ -6,7 +6,7 @@ NLog.OutputDebugString NLog NLog.OutputDebugString provides support for DebugView output - NLog.PerformanceCounter v$(ProductVersion) + NLog.OutputDebugString v$(ProductVersion) $(ProductVersion) Jarek Kowalski,Kim Christensen,Julian Verdurmen $([System.DateTime]::Now.ToString(yyyy)) diff --git a/src/NLog.Targets.WebService/N.png b/src/NLog.Targets.WebService/N.png new file mode 100644 index 0000000000000000000000000000000000000000..4b8869e2e6d147f32c525bce0c5937d52e3dd1c5 GIT binary patch literal 1903 zcmV-#2ax!QP)1^@s6%GAg-00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGxhX4Q_hXIe}@nrx202p*dSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E;VI^GGzb&2H;6VK~#8N?OLIBBsUN|7xtDvn zmm4pq(?6$MhfVck%klVjI(+-%^z!uE>Gk2u>FxJlw7x~WJ$})7zCC|E9bRAO3k22C zIR;XQ7(Wbr&NBaL?m1|_Q4%(5<}F zSDm6c^(sg02601q7YrX#6wP@oYHZ@{P&2$rw?PAC^ETD2nPuf#W}p5|83+N z;H)@rU)*V@PeaiuRpIoQz92>?8t(7>c*7!Z&MC0p!X>sxktpLNA|Oh2|Kpb{3ln1& zW^+!V1)h^r92lhNl=By?dOrdrq9ExODmq2C4~t^h<&?}Nm?Y||Q$PeCYuWt> zJ*G*J7`t1Bzf%%n_T>}@38HZ&r^vuc=urq2S9dkPusGGeZ2RcEiOY1SU}9PqoT4jl zM3hW1@gD7GKorB{a-O1jcRU1hPaktj*(pQ{D~7i0*@(^|4)15=^l?E@I~R%PI7K?b zq!8ES)_Y;rXZu;UekSweJ+)K6t0`5a{-i`$n~1@X})Y08kt&J|c<< z(nwX0RB;5q*WMpFeE`fkaM-+kq9{#qs9(q_IEp($OtA1ipvN@5t6LCGDQrV*AD%OF znI?(?okQsXr?B;zT!k2((f9pb^f!3=bb=t3+%oO-an*HV)kG`DaTKJ*#qXDPOxt_q z(+69}gn+QlM`x_Y(0I5!%gIrZVM2~+en~&;GJrsUO9b$sX0Y$2&D^7 z0a+y<#qev?_}YE`wIoc-Q6r}hJH#Pv({hRqV9F0~pI9@b4Zo7JJ|gt*+r?+se*1ve zW~a1{>`TyHGMrNc&H7enB9+`H7}-85xbtubhj~t+PKR>JiU{!@1S{p2$}@u1L@U_m zl=#~RoeM^s!tPkaK_skh*4Gxa?-ZXq{q~uYXu}p7ataf-_$-JCLE<2;ZjWnd-zgFJ zc$51^wO;U2x_t(mg4J*#oVeE9X0g^9+r46bxrDXlhGv*pJr}+E{;I-@YLB|Wnh%lN{3QqAq zjCUf~b&Iq6hCTU3w~xCdV6#)S1F~F$3{-Lo5-k(Wh_g6+pW4w+*8er=_Mv&V#l`%$ zin36e_hfPVxKkQIBfMy%+V2DUp}6sw?!HqP!UOk>YQ5m4#kL_-MDgtdvbs7>IuSsx zSH#kzwfJez=$yii-{TY(CFYmv_F-aPN77!$=d^C3;*VG+EzFlD}(>pcX3!qKbudVOW7NC#>s%6@s7W~)3w~wU$pYR6H`*xh*SOU zlJk373&A)pz+-ES@w0i$;({0W8P%~8vp2wsNA-J%Lp(O3PV7wBp0SvPh;3ST@13FJ zhqi{`>~<~BgV$zJbfu|7-J@}^OTgs9q!&)1XS#NNk7K?@osA3Ne%1~MFQYhK^z@+4 zI%iyH>kvtxcG&gOLH&KyDpc106}0*JVoD8OMsZt#s*yOZ7s(5_<{}V8ZDbo!kF7oC zk*<$HEZi-8Hg6p-=s-L6PI&-{H;FS5KK5dvYz{m(`rO + + + net46;netstandard2.0 + + NLog.Targets.WebService + NLog + WebServiceTarget can call web-service for each logevent + NLog.Targets.WebService v$(ProductVersion) + $(ProductVersion) + Jarek Kowalski,Kim Christensen,Julian Verdurmen + $([System.DateTime]::Now.ToString(yyyy)) + Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ + + + WebServiceTarget Docs: + https://github.com/NLog/NLog/wiki/WebService-target + + README.md + NLog;WebService;HttpClient;Network;logging;log + N.png + https://nlog-project.org/ + BSD-3-Clause + git + https://github.com/NLog/NLog.git + + true + 5.0.0.0 + ..\NLog.snk + true + + true + true + true + true + true + copyused + + + + NLog.Targets.WebService for .NET Standard 2.0 + + + + NLog.Targets.WebService for .NET Framework 4.6 + true + + + + NLog.Targets.WebService for .NET Framework 4.5 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NLog.Targets.WebService/Properties/AssemblyInfo.cs b/src/NLog.Targets.WebService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..3e030e53e5 --- /dev/null +++ b/src/NLog.Targets.WebService/Properties/AssemblyInfo.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +[assembly: AssemblyCulture("")] +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] +[assembly: InternalsVisibleTo("NLog.Targets.WebService.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100ef8eab4fbdeb511eeb475e1659fe53f00ec1c1340700f1aa347bf3438455d71993b28b1efbed44c8d97a989e0cb6f01bcb5e78f0b055d311546f63de0a969e04cf04450f43834db9f909e566545a67e42822036860075a1576e90e1c43d43e023a24c22a427f85592ae56cac26f13b7ec2625cbc01f9490d60f16cfbb1bc34d9")] +[assembly: AllowPartiallyTrustedCallers] +#if !NET35 +[assembly: SecurityRules(SecurityRuleSet.Level1)] +#endif diff --git a/src/NLog.Targets.WebService/README.md b/src/NLog.Targets.WebService/README.md new file mode 100644 index 0000000000..8329577a2e --- /dev/null +++ b/src/NLog.Targets.WebService/README.md @@ -0,0 +1,25 @@ +# NLog WebService Target + +NLog WebService Target calls WebService method on each logevent, with support for different protocols: JsonPost, XmlPost, HttpGet, HttpPost, Soap11, Soap12 + +If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) + +See the [NLog Wiki](https://github.com/NLog/NLog/wiki/WebService-target) for available options and examples. + +## Register Extension + +NLog will only recognize type-alias `WebService` when loading from `NLog.config`-file, if having added extension to `NLog.config`-file: + +```xml + + + +``` + +Alternative register from code using [fluent configuration API](https://github.com/NLog/NLog/wiki/Fluent-Configuration-API): + +```csharp +LogManager.Setup().SetupExtensions(ext => { + ext.RegisterTarget(); +}); +``` diff --git a/src/NLog/Targets/WebServiceProtocol.cs b/src/NLog.Targets.WebService/WebServiceProtocol.cs similarity index 100% rename from src/NLog/Targets/WebServiceProtocol.cs rename to src/NLog.Targets.WebService/WebServiceProtocol.cs diff --git a/src/NLog/Targets/WebServiceProxyType.cs b/src/NLog.Targets.WebService/WebServiceProxyType.cs similarity index 100% rename from src/NLog/Targets/WebServiceProxyType.cs rename to src/NLog.Targets.WebService/WebServiceProxyType.cs diff --git a/src/NLog/Targets/WebServiceTarget.cs b/src/NLog.Targets.WebService/WebServiceTarget.cs similarity index 87% rename from src/NLog/Targets/WebServiceTarget.cs rename to src/NLog.Targets.WebService/WebServiceTarget.cs index 682649cb50..fcb208010f 100644 --- a/src/NLog/Targets/WebServiceTarget.cs +++ b/src/NLog.Targets.WebService/WebServiceTarget.cs @@ -42,7 +42,6 @@ namespace NLog.Targets using System.Xml; using NLog.Common; using NLog.Config; - using NLog.Internal; using NLog.Layouts; /// @@ -189,6 +188,7 @@ public WebServiceProxyType ProxyType /// /// A value of true if Rfc3986; otherwise, false for legacy Rfc2396. /// + [Obsolete("Replaced by WebUtility.UrlEncode. Marked obsolete with NLog 6.0")] public bool EscapeDataRfc3986 { get; set; } /// @@ -196,7 +196,7 @@ public WebServiceProxyType ProxyType /// /// A value of true if legacy encoding; otherwise, false for standard UTF8 encoding. /// - [Obsolete("Instead use default Rfc2396 or EscapeDataRfc3986. Marked obsolete with NLog v5.3")] + [Obsolete("Replaced by WebUtility.UrlEncode. Marked obsolete with NLog 6.0")] [EditorBrowsable(EditorBrowsableState.Never)] public bool EscapeDataNLogLegacy { get; set; } @@ -230,7 +230,11 @@ public WebServiceProxyType ProxyType /// public bool PreAuthenticate { get; set; } - private readonly AsyncOperationCounter _pendingManualFlushList = new AsyncOperationCounter(); + private IJsonConverter JsonConverter => _jsonConverter ?? (_jsonConverter = ResolveService()); + private IJsonConverter _jsonConverter; + + private long _pendingWriteOperations; + private Action _pendingFlushOperation; /// /// Calls the target method. Must be implemented in concrete classes. @@ -368,7 +372,7 @@ internal void DoInvoke(object[] parameters, AsyncContinuation continuation, Http postPayload = _activeProtocol.Value.PrepareRequest(webRequest, parameters); } - _pendingManualFlushList.BeginOperation(); + System.Threading.Interlocked.Increment(ref _pendingWriteOperations); try { @@ -384,7 +388,7 @@ internal void DoInvoke(object[] parameters, AsyncContinuation continuation, Http catch (Exception ex) { InternalLogger.Error(ex, "{0}: Error starting request for url={1}", this, webRequest.RequestUri); - if (ExceptionMustBeRethrown(ex)) + if (LogManager.ThrowExceptions) { throw; } @@ -455,20 +459,42 @@ private void PostPayload(AsyncContinuation continuation, HttpWebRequest webReque private void DoInvokeCompleted(AsyncContinuation continuation, Exception ex) { - _pendingManualFlushList.CompleteOperation(ex); + System.Threading.Interlocked.Decrement(ref _pendingWriteOperations); + _pendingFlushOperation?.Invoke(); continuation(ex); } /// protected override void FlushAsync(AsyncContinuation asyncContinuation) { - _pendingManualFlushList.RegisterCompletionNotification(asyncContinuation).Invoke(null); + var pendingWriteOperations = System.Threading.Interlocked.Read(ref _pendingWriteOperations); + if (pendingWriteOperations <= 0) + { + asyncContinuation?.Invoke(null); + } + else + { + var pendingFlushOperation = _pendingFlushOperation; + Action newPendingFlushOperation = null; + newPendingFlushOperation = () => + { + pendingFlushOperation?.Invoke(); + if (System.Threading.Interlocked.Decrement(ref pendingWriteOperations) == 0) + { + System.Threading.Interlocked.CompareExchange(ref _pendingFlushOperation, null, newPendingFlushOperation); + asyncContinuation.Invoke(null); + } + }; + + _pendingFlushOperation = newPendingFlushOperation; + } } /// protected override void CloseTarget() { - _pendingManualFlushList.Clear(); // Maybe consider to wait a short while if pending requests? + _pendingFlushOperation = null; // Maybe consider to wait a short while if pending requests? + _pendingWriteOperations = 0; base.CloseTarget(); } @@ -483,19 +509,10 @@ private Uri BuildWebServiceUrl(LogEventInfo logEvent, object[] parameterValues) return uri; } -#pragma warning disable CS0618 // Type or member is obsolete - bool escapeDataNLogLegacy = EscapeDataNLogLegacy; -#pragma warning restore CS0618 // Type or member is obsolete - //if the protocol is HttpGet, we need to add the parameters to the query string of the url - string queryParameters; - using (var targetBuilder = ReusableLayoutBuilder.Allocate()) - { - StringBuilder sb = targetBuilder.Result ?? new StringBuilder(); - UrlHelper.EscapeEncodingOptions encodingOptions = UrlHelper.GetUriStringEncodingFlags(escapeDataNLogLegacy, false, EscapeDataRfc3986); - BuildWebServiceQueryParameters(parameterValues, sb, encodingOptions); - queryParameters = sb.ToString(); - } + StringBuilder sb = new StringBuilder(); + BuildWebServiceQueryParameters(parameterValues, sb); + var queryParameters = sb.ToString(); var builder = new UriBuilder(uri); //append our query string to the URL following @@ -512,21 +529,43 @@ private Uri BuildWebServiceUrl(LogEventInfo logEvent, object[] parameterValues) return builder.Uri; } - private void BuildWebServiceQueryParameters(object[] parameterValues, StringBuilder sb, UrlHelper.EscapeEncodingOptions encodingOptions) + private void BuildWebServiceQueryParameters(object[] parameterValues, StringBuilder sb) { string separator = string.Empty; for (int i = 0; i < Parameters.Count; i++) { sb.Append(separator); sb.Append(Parameters[i].Name); - sb.Append('='); - string parameterValue = XmlHelper.XmlConvertToString(parameterValues[i]); - if (!string.IsNullOrEmpty(parameterValue)) + sb.Append("="); + AppendParameterAsString(parameterValues[i], sb); + separator = "&"; + } + } + + private void AppendParameterAsString(object parameterValue, StringBuilder sb) + { + var parameterObject = parameterValue as IConvertible; + if (parameterObject != null) + { + switch (parameterObject.GetTypeCode()) { - UrlHelper.EscapeDataEncode(parameterValue, sb, encodingOptions); + case TypeCode.DateTime: + sb.Append(XmlConvert.ToString(parameterObject.ToDateTime(System.Globalization.CultureInfo.InvariantCulture), XmlDateTimeSerializationMode.RoundtripKind)); + return; + case TypeCode.Object: + break; + case TypeCode.String: + break; + case TypeCode.Char: + break; + default: + JsonConverter.SerializeObject(parameterValue, sb); + return; } - separator = "&"; } + + var parameterString = Convert.ToString(parameterValue, System.Globalization.CultureInfo.InvariantCulture); + sb.Append(WebUtility.UrlEncode(parameterString)); } /// @@ -598,14 +637,8 @@ protected virtual void InitRequest(HttpWebRequest request) private sealed class HttpPostFormEncodedFormatter : HttpPostTextFormatterBase { - readonly UrlHelper.EscapeEncodingOptions _encodingOptions; - public HttpPostFormEncodedFormatter(WebServiceTarget target) : base(target) { -#pragma warning disable CS0618 // Type or member is obsolete - bool escapeDataNLogLegacy = target.EscapeDataNLogLegacy; -#pragma warning restore CS0618 // Type or member is obsolete - _encodingOptions = UrlHelper.GetUriStringEncodingFlags(escapeDataNLogLegacy, true, target.EscapeDataRfc3986); } protected override string GetContentType(WebServiceTarget target) @@ -615,7 +648,7 @@ protected override string GetContentType(WebServiceTarget target) protected override void WriteStringContent(StringBuilder builder, object[] parameterValues) { - Target.BuildWebServiceQueryParameters(parameterValues, builder, _encodingOptions); + Target.BuildWebServiceQueryParameters(parameterValues, builder); } } @@ -754,8 +787,8 @@ protected static string GetDefaultSoapAction(WebServiceTarget target) private abstract class HttpPostTextFormatterBase : HttpPostFormatterBase { - readonly ReusableBuilderCreator _reusableStringBuilder = new ReusableBuilderCreator(); - readonly ReusableBufferCreator _reusableEncodingBuffer = new ReusableBufferCreator(1024); + readonly StringBuilder _reusableStringBuilder = new StringBuilder(); + readonly char[] _reusableEncodingBuffer = new char[1024]; readonly byte[] _encodingPreamble; protected HttpPostTextFormatterBase(WebServiceTarget target) : base(target) @@ -767,16 +800,33 @@ protected override void WriteContent(MemoryStream ms, object[] parameterValues) { lock (_reusableStringBuilder) { - using (var targetBuilder = _reusableStringBuilder.Allocate()) + try { - WriteStringContent(targetBuilder.Result, parameterValues); + _reusableStringBuilder.Length = 0; + + WriteStringContent(_reusableStringBuilder, parameterValues); - using (var transformBuffer = _reusableEncodingBuffer.Allocate()) + if (_encodingPreamble.Length > 0) + ms.Write(_encodingPreamble, 0, _encodingPreamble.Length); + + int charCount; + int byteCount = Target.Encoding.GetMaxByteCount(_reusableStringBuilder.Length); + ms.SetLength(ms.Position + byteCount); + for (int i = 0; i < _reusableStringBuilder.Length; i += _reusableEncodingBuffer.Length) { - if (_encodingPreamble.Length > 0) - ms.Write(_encodingPreamble, 0, _encodingPreamble.Length); - targetBuilder.Result.CopyToStream(ms, Target.Encoding, transformBuffer.Result); + charCount = Math.Min(_reusableStringBuilder.Length - i, _reusableEncodingBuffer.Length); + _reusableStringBuilder.CopyTo(i, _reusableEncodingBuffer, 0, charCount); + byteCount = Target.Encoding.GetBytes(_reusableEncodingBuffer, 0, charCount, ms.GetBuffer(), (int)ms.Position); + ms.Position += byteCount; } + if (ms.Position != ms.Length) + { + ms.SetLength(ms.Position); + } + } + finally + { + _reusableStringBuilder.Length = 0; } } } diff --git a/src/NLog.sln b/src/NLog.sln index bcf2f8d800..bd49f14830 100644 --- a/src/NLog.sln +++ b/src/NLog.sln @@ -46,6 +46,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Database.Tests", "..\t EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestTrimPublish", "..\tests\TestTrimPublish\TestTrimPublish.csproj", "{058FBDB2-4C9C-45F6-BE9E-088AD019C77A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.WebService", "NLog.Targets.WebService\NLog.Targets.WebService.csproj", "{F5F88648-5D6C-4A18-B98E-FCEEA4D704CE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.WebService.Tests", "..\tests\NLog.Targets.WebService.Tests\NLog.Targets.WebService.Tests.csproj", "{D6FB13E7-92A7-4F4F-A09A-37EB04767A82}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -100,6 +104,14 @@ Global {058FBDB2-4C9C-45F6-BE9E-088AD019C77A}.Debug|Any CPU.Build.0 = Debug|Any CPU {058FBDB2-4C9C-45F6-BE9E-088AD019C77A}.Release|Any CPU.ActiveCfg = Release|Any CPU {058FBDB2-4C9C-45F6-BE9E-088AD019C77A}.Release|Any CPU.Build.0 = Release|Any CPU + {F5F88648-5D6C-4A18-B98E-FCEEA4D704CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5F88648-5D6C-4A18-B98E-FCEEA4D704CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5F88648-5D6C-4A18-B98E-FCEEA4D704CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5F88648-5D6C-4A18-B98E-FCEEA4D704CE}.Release|Any CPU.Build.0 = Release|Any CPU + {D6FB13E7-92A7-4F4F-A09A-37EB04767A82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6FB13E7-92A7-4F4F-A09A-37EB04767A82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6FB13E7-92A7-4F4F-A09A-37EB04767A82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6FB13E7-92A7-4F4F-A09A-37EB04767A82}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -115,6 +127,8 @@ Global {7D3A19CB-9BE0-4611-8464-6C49D4FE0106} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {35EB0745-C37C-4E42-86C5-481A7D773E2E} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {058FBDB2-4C9C-45F6-BE9E-088AD019C77A} = {8CCD7CF7-F30B-414A-B7FE-1B49411E4EFD} + {F5F88648-5D6C-4A18-B98E-FCEEA4D704CE} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {D6FB13E7-92A7-4F4F-A09A-37EB04767A82} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6B4C8E9F-1E2F-4AB3-993A-8776CFA08DC0} diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index 89e4390183..c81b56e8fc 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -215,7 +215,6 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.RegisterType(); factory.TargetFactory.RegisterType("Trace"); factory.TargetFactory.RegisterType("TraceSystem"); - factory.TargetFactory.RegisterType("WebService"); factory.TargetFactory.RegisterType("AsyncWrapper"); factory.TargetFactory.RegisterType("AutoFlushWrapper"); factory.TargetFactory.RegisterType("BufferingWrapper"); diff --git a/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj b/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj new file mode 100644 index 0000000000..6c4e86caea --- /dev/null +++ b/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj @@ -0,0 +1,33 @@ + + + + 17.0 + net462 + net462;net6.0 + false + + NLogTests.snk + false + true + true + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + diff --git a/tests/NLog.Targets.WebService.Tests/NLogTests.snk b/tests/NLog.Targets.WebService.Tests/NLogTests.snk new file mode 100644 index 0000000000000000000000000000000000000000..f168e4dbecfd20aae45a58ff7c8146fd9437ca8c GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098+j;l|->ro!-M_v|L{!{P{!ND{K0P(6c zd-FqtRo5AlvWp)3?L^4gdYGOJw(uLvUU=}ZRnrkvZ)4sHmYxL91Vs-+gH5^l3FT%~ zT4&@aA_Hh(2U-<&=?)x2)II__B*H2}e}!2p6a#Iu;d48bO^8HhTl;j8?*d<1a_R|q8E50kv?eP0U3&ZlGn@^pP7iWL$P<_mA z`9zel$Knfm4TsO>!T$Xq=dg4ToMpb)#Ve)# literal 0 HcmV?d00001 diff --git a/tests/NLog.UnitTests/Targets/WebServiceTargetTests.cs b/tests/NLog.Targets.WebService.Tests/WebServiceTargetTests.cs similarity index 99% rename from tests/NLog.UnitTests/Targets/WebServiceTargetTests.cs rename to tests/NLog.Targets.WebService.Tests/WebServiceTargetTests.cs index 85a86c799a..4bec13b109 100644 --- a/tests/NLog.UnitTests/Targets/WebServiceTargetTests.cs +++ b/tests/NLog.Targets.WebService.Tests/WebServiceTargetTests.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.UnitTests.Targets +namespace NLog.Targets.WebService { using System; using System.Collections.Concurrent; @@ -54,7 +54,7 @@ namespace NLog.UnitTests.Targets using NLog.Targets; using Xunit; - public class WebServiceTargetTests : NLogTestBase + public class WebServiceTargetTests { public WebServiceTargetTests() { @@ -166,7 +166,7 @@ private static void WebserviceTest_httppost_utf8(string bomAttr, bool includeBom var bytes = streamMock.bytes; var url = streamMock.stringed; - const string expectedUrl = "empty=&guid=336cec87129942eeabab3d8babceead7&m=Debg&date=2014-06-26+23%3a15%3a14.6348&logger=TestClient.Program&level=Debug"; + const string expectedUrl = "empty=&guid=336cec87129942eeabab3d8babceead7&m=Debg&date=2014-06-26+23%3A15%3A14.6348&logger=TestClient.Program&level=Debug"; Assert.Equal(expectedUrl, url); Assert.True(bytes.Length > 3); @@ -712,7 +712,6 @@ public IEnumerable Get(string param1 = "", string param2 = "") public void Post([FromBody] ComplexType complexType) { //this is working. - Guard.ThrowIfNull(complexType); Context.ReceivedLogsPostParam1.Add(complexType.Param1); if (Context.CountdownEvent != null) @@ -886,7 +885,7 @@ public IEnumerable GetServices(Type serviceType) } else { - return ArrayHelper.Empty(); + return Array.Empty(); } } } @@ -899,16 +898,12 @@ public class LogDocController : ApiController [HttpPost] public void Json(LogMeController.ComplexType complexType) { - Guard.ThrowIfNull(complexType); - processRequest(complexType); } [HttpPost] public void Xml(LogMeController.ComplexType complexType) { - Guard.ThrowIfNull(complexType); - processRequest(complexType); } diff --git a/tests/NLog.UnitTests/NLog.UnitTests.csproj b/tests/NLog.UnitTests/NLog.UnitTests.csproj index 3163e6b826..bdf6653cfb 100644 --- a/tests/NLog.UnitTests/NLog.UnitTests.csproj +++ b/tests/NLog.UnitTests/NLog.UnitTests.csproj @@ -51,15 +51,12 @@ - - - diff --git a/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj b/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj index 5ce368978c..b653366155 100644 --- a/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj +++ b/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj @@ -15,10 +15,6 @@ Full - - $(DefineConstants);NETSTANDARD - - From df37724f83dcf602404dcff955cd8a600ea0f2f6 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 6 Oct 2024 12:42:36 +0200 Subject: [PATCH 013/224] Move MailTarget to its own nuget-package (#5635) --- Test-XmlFile.ps1 | 2 +- .../Configuration File/Variables/NLog.config | 17 +--- run-tests.ps1 | 12 +++ src/NLog.Database/NLog.Database.csproj | 5 +- .../ISmtpClient.cs | 0 .../MailTarget.cs | 0 .../MySmtpClient.cs | 0 .../NLog.Targets.Mail.csproj | 81 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 47 ++++++++++ src/NLog.Targets.Mail/README.md | 25 ++++++ .../SmtpAuthenticationMode.cs | 0 .../NLog.Targets.WebService.csproj | 4 +- src/NLog.Targets.WebService/README.md | 2 +- .../NLog.WindowsEventLog.csproj | 4 +- .../NLog.WindowsRegistry.csproj | 3 +- src/NLog.sln | 14 +++ src/NLog/Config/AssemblyExtensionTypes.cs | 4 - src/NLog/NLog.csproj | 2 +- .../NLog.Database.Tests.csproj | 4 - .../MailTargetTests.cs | 2 +- .../NLog.Targets.Mail.Tests.csproj | 31 +++++++ tests/NLog.Targets.Mail.Tests/NLogTests.snk | Bin 0 -> 596 bytes 22 files changed, 222 insertions(+), 37 deletions(-) rename src/{NLog/Internal => NLog.Targets.Mail}/ISmtpClient.cs (100%) rename src/{NLog/Targets => NLog.Targets.Mail}/MailTarget.cs (100%) rename src/{NLog/Internal => NLog.Targets.Mail}/MySmtpClient.cs (100%) create mode 100644 src/NLog.Targets.Mail/NLog.Targets.Mail.csproj create mode 100644 src/NLog.Targets.Mail/Properties/AssemblyInfo.cs create mode 100644 src/NLog.Targets.Mail/README.md rename src/{NLog/Targets => NLog.Targets.Mail}/SmtpAuthenticationMode.cs (100%) rename tests/{NLog.UnitTests/Targets => NLog.Targets.Mail.Tests}/MailTargetTests.cs (99%) create mode 100644 tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj create mode 100644 tests/NLog.Targets.Mail.Tests/NLogTests.snk diff --git a/Test-XmlFile.ps1 b/Test-XmlFile.ps1 index 1d9412e4ab..3001cc41cf 100644 --- a/Test-XmlFile.ps1 +++ b/Test-XmlFile.ps1 @@ -54,7 +54,7 @@ function Test-XmlFile $pwd = get-location; $testFilesDir = "$pwd\examples\targets\Configuration File" $xsdFilePath = "$pwd\src\NLog\bin\Release\NLog.xsd" -$excludedTests = ("MessageBox","RichTextBox","FormControl","PerfCounter","OutputDebugString","MSMQ","Database","WebService") +$excludedTests = ("MessageBox","RichTextBox","FormControl","PerfCounter","OutputDebugString","MSMQ","Database","Mail","WebService") # Returns true if all selected tests in examples directory are valid $ret = $true Get-ChildItem -Path $testFilesDir -Directory -Exclude $excludedTests | Get-ChildItem -Recurse -File -Filter NLog.config | % { diff --git a/examples/targets/Configuration File/Variables/NLog.config b/examples/targets/Configuration File/Variables/NLog.config index b57bf7675a..0eb2f87116 100644 --- a/examples/targets/Configuration File/Variables/NLog.config +++ b/examples/targets/Configuration File/Variables/NLog.config @@ -2,13 +2,8 @@ - - - - - - + @@ -17,17 +12,11 @@ - - + - + diff --git a/run-tests.ps1 b/run-tests.ps1 index bb0522b1fc..512924dfa3 100644 --- a/run-tests.ps1 +++ b/run-tests.ps1 @@ -24,6 +24,10 @@ if ($isWindows -or $Env:WinDir) if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.Mail.Tests/ --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.WebService.Tests/ --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } @@ -59,6 +63,14 @@ else if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.Mail.Tests/ --framework net6.0 --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + + dotnet test ./tests/NLog.Targets.WebService.Tests/ --framework net6.0 --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + # Need help from MONO to run normal .NetFramework tests dotnet msbuild /t:restore ./tests/NLog.UnitTests/ /p:RestoreForce=true /p:monobuild=1 if (-Not $LastExitCode -eq 0) diff --git a/src/NLog.Database/NLog.Database.csproj b/src/NLog.Database/NLog.Database.csproj index 33dd2a1f6e..38ffa6a4eb 100644 --- a/src/NLog.Database/NLog.Database.csproj +++ b/src/NLog.Database/NLog.Database.csproj @@ -62,7 +62,7 @@ - + @@ -70,9 +70,6 @@ - - - $(Title) diff --git a/src/NLog/Internal/ISmtpClient.cs b/src/NLog.Targets.Mail/ISmtpClient.cs similarity index 100% rename from src/NLog/Internal/ISmtpClient.cs rename to src/NLog.Targets.Mail/ISmtpClient.cs diff --git a/src/NLog/Targets/MailTarget.cs b/src/NLog.Targets.Mail/MailTarget.cs similarity index 100% rename from src/NLog/Targets/MailTarget.cs rename to src/NLog.Targets.Mail/MailTarget.cs diff --git a/src/NLog/Internal/MySmtpClient.cs b/src/NLog.Targets.Mail/MySmtpClient.cs similarity index 100% rename from src/NLog/Internal/MySmtpClient.cs rename to src/NLog.Targets.Mail/MySmtpClient.cs diff --git a/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj b/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj new file mode 100644 index 0000000000..f377a522ea --- /dev/null +++ b/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj @@ -0,0 +1,81 @@ + + + + net35;net46;netstandard2.0 + + NLog.Targets.Mail + NLog + NLog.Targets.Mail for sending emails using the .NET SmtpClient + NLog.Targets.Mail v$(ProductVersion) + $(ProductVersion) + Jarek Kowalski,Kim Christensen,Julian Verdurmen + $([System.DateTime]::Now.ToString(yyyy)) + Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ + + + MailTarget Docs: + https://github.com/nlog/NLog/wiki/Mail-target + + README.md + NLog;Smtp;SmtpClient;mail;email;logging;log + N.png + https://nlog-project.org/ + BSD-3-Clause + git + https://github.com/NLog/NLog.git + + true + 5.0.0.0 + ..\NLog.snk + true + + true + true + true + true + true + copyused + + + + NLog.Targets.Mail for .NET Framework 4.6 + true + + + + NLog.Targets.Mail for .NET Framework 4.5 + true + + + + NLog.Targets.Mail for .NET Framework 3.5 + true + + + + NLog.Targets.Mail for NetStandard 2.0 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NLog.Targets.Mail/Properties/AssemblyInfo.cs b/src/NLog.Targets.Mail/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..31d35aba33 --- /dev/null +++ b/src/NLog.Targets.Mail/Properties/AssemblyInfo.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +[assembly: AssemblyCulture("")] +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] +[assembly: InternalsVisibleTo("NLog.Targets.Mail.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100ef8eab4fbdeb511eeb475e1659fe53f00ec1c1340700f1aa347bf3438455d71993b28b1efbed44c8d97a989e0cb6f01bcb5e78f0b055d311546f63de0a969e04cf04450f43834db9f909e566545a67e42822036860075a1576e90e1c43d43e023a24c22a427f85592ae56cac26f13b7ec2625cbc01f9490d60f16cfbb1bc34d9")] +[assembly: AllowPartiallyTrustedCallers] +#if !NET35 +[assembly: SecurityRules(SecurityRuleSet.Level1)] +#endif diff --git a/src/NLog.Targets.Mail/README.md b/src/NLog.Targets.Mail/README.md new file mode 100644 index 0000000000..805ce96419 --- /dev/null +++ b/src/NLog.Targets.Mail/README.md @@ -0,0 +1,25 @@ +# NLog WebService Target + +NLog Mail Target for sending email using .NET SmtpClient for each logevent. + +If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) + +See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Mail-target) for available options and examples. + +## Register Extension + +NLog will only recognize type-alias `Mail` when loading from `NLog.config`-file, if having added extension to `NLog.config`-file: + +```xml + + + +``` + +Alternative register from code using [fluent configuration API](https://github.com/NLog/NLog/wiki/Fluent-Configuration-API): + +```csharp +LogManager.Setup().SetupExtensions(ext => { + ext.RegisterTarget(); +}); +``` diff --git a/src/NLog/Targets/SmtpAuthenticationMode.cs b/src/NLog.Targets.Mail/SmtpAuthenticationMode.cs similarity index 100% rename from src/NLog/Targets/SmtpAuthenticationMode.cs rename to src/NLog.Targets.Mail/SmtpAuthenticationMode.cs diff --git a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj index 6fa6e82de7..637e38621d 100644 --- a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj +++ b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj @@ -5,7 +5,7 @@ NLog.Targets.WebService NLog - WebServiceTarget can call web-service for each logevent + WebServiceTarget for calling HTTP / SOAP / REST web-service NLog.Targets.WebService v$(ProductVersion) $(ProductVersion) Jarek Kowalski,Kim Christensen,Julian Verdurmen @@ -17,7 +17,7 @@ https://github.com/NLog/NLog/wiki/WebService-target README.md - NLog;WebService;HttpClient;Network;logging;log + NLog;WebService;Soap;Rest;HttpClient;Network;logging;log N.png https://nlog-project.org/ BSD-3-Clause diff --git a/src/NLog.Targets.WebService/README.md b/src/NLog.Targets.WebService/README.md index 8329577a2e..d7d8450f51 100644 --- a/src/NLog.Targets.WebService/README.md +++ b/src/NLog.Targets.WebService/README.md @@ -1,6 +1,6 @@ # NLog WebService Target -NLog WebService Target calls WebService method on each logevent, with support for different protocols: JsonPost, XmlPost, HttpGet, HttpPost, Soap11, Soap12 +NLog WebService Target for calling web-service for each logevent, with support for different protocols: JsonPost, XmlPost, HttpGet, HttpPost, Soap11, Soap12 If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) diff --git a/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj b/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj index 43c1d6ef9c..8e19d52644 100644 --- a/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj +++ b/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj @@ -1,8 +1,6 @@  - PackageReference - netstandard2.0 NLog.WindowsEventLog for .NET Standard @@ -41,7 +39,7 @@ NLog.WindowsEventLog for NetStandard 2.0 - $(DefineConstants);NETSTANDARD2_0;WindowsEventLogPackage + $(DefineConstants);WindowsEventLogPackage diff --git a/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj b/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj index f1091c465a..84e42040a7 100644 --- a/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj +++ b/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj @@ -54,10 +54,9 @@ NLog.WindowsRegistry for NetStandard 2.0 - $(DefineConstants);NETSTANDARD - + diff --git a/src/NLog.sln b/src/NLog.sln index bd49f14830..140d95f86a 100644 --- a/src/NLog.sln +++ b/src/NLog.sln @@ -46,6 +46,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Database.Tests", "..\t EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestTrimPublish", "..\tests\TestTrimPublish\TestTrimPublish.csproj", "{058FBDB2-4C9C-45F6-BE9E-088AD019C77A}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.Mail", "NLog.Targets.Mail\NLog.Targets.Mail.csproj", "{B47B6417-6987-4D42-9C8C-4659C8F56806}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.Mail.Tests", "..\tests\NLog.Targets.Mail.Tests\NLog.Targets.Mail.Tests.csproj", "{F26A0E10-7DED-4AD9-BB59-147E4EB70924}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.WebService", "NLog.Targets.WebService\NLog.Targets.WebService.csproj", "{F5F88648-5D6C-4A18-B98E-FCEEA4D704CE}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.WebService.Tests", "..\tests\NLog.Targets.WebService.Tests\NLog.Targets.WebService.Tests.csproj", "{D6FB13E7-92A7-4F4F-A09A-37EB04767A82}" @@ -104,6 +108,14 @@ Global {058FBDB2-4C9C-45F6-BE9E-088AD019C77A}.Debug|Any CPU.Build.0 = Debug|Any CPU {058FBDB2-4C9C-45F6-BE9E-088AD019C77A}.Release|Any CPU.ActiveCfg = Release|Any CPU {058FBDB2-4C9C-45F6-BE9E-088AD019C77A}.Release|Any CPU.Build.0 = Release|Any CPU + {B47B6417-6987-4D42-9C8C-4659C8F56806}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B47B6417-6987-4D42-9C8C-4659C8F56806}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B47B6417-6987-4D42-9C8C-4659C8F56806}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B47B6417-6987-4D42-9C8C-4659C8F56806}.Release|Any CPU.Build.0 = Release|Any CPU + {F26A0E10-7DED-4AD9-BB59-147E4EB70924}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F26A0E10-7DED-4AD9-BB59-147E4EB70924}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F26A0E10-7DED-4AD9-BB59-147E4EB70924}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F26A0E10-7DED-4AD9-BB59-147E4EB70924}.Release|Any CPU.Build.0 = Release|Any CPU {F5F88648-5D6C-4A18-B98E-FCEEA4D704CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F5F88648-5D6C-4A18-B98E-FCEEA4D704CE}.Debug|Any CPU.Build.0 = Debug|Any CPU {F5F88648-5D6C-4A18-B98E-FCEEA4D704CE}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -127,6 +139,8 @@ Global {7D3A19CB-9BE0-4611-8464-6C49D4FE0106} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {35EB0745-C37C-4E42-86C5-481A7D773E2E} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {058FBDB2-4C9C-45F6-BE9E-088AD019C77A} = {8CCD7CF7-F30B-414A-B7FE-1B49411E4EFD} + {B47B6417-6987-4D42-9C8C-4659C8F56806} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {F26A0E10-7DED-4AD9-BB59-147E4EB70924} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {F5F88648-5D6C-4A18-B98E-FCEEA4D704CE} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {D6FB13E7-92A7-4F4F-A09A-37EB04767A82} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} EndGlobalSection diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index c81b56e8fc..93818c5494 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -201,10 +201,6 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.TargetFactory.RegisterType("EventLog"); #endif factory.TargetFactory.RegisterType("File"); - factory.TargetFactory.RegisterType("Mail"); - factory.TargetFactory.RegisterType("Email"); - factory.TargetFactory.RegisterType("Smtp"); - factory.TargetFactory.RegisterType("SmtpClient"); factory.TargetFactory.RegisterType("Memory"); factory.RegisterType(); factory.TargetFactory.RegisterType("MethodCall"); diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index 6bd7e538fe..c112b77521 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -51,7 +51,7 @@ Full changelog: https://github.com/NLog/NLog/blob/master/CHANGELOG.md For all config options and platform support, check https://nlog-project.org/config/ - NLog;logging;log;structured;tracing;logfiles;database;eventlog;console;email + NLog;logging;log;structured;tracing;logfiles;database;eventlog;console N.png https://nlog-project.org/ BSD-3-Clause diff --git a/tests/NLog.Database.Tests/NLog.Database.Tests.csproj b/tests/NLog.Database.Tests/NLog.Database.Tests.csproj index 888b88ceca..6e558496b7 100644 --- a/tests/NLog.Database.Tests/NLog.Database.Tests.csproj +++ b/tests/NLog.Database.Tests/NLog.Database.Tests.csproj @@ -15,10 +15,6 @@ Full - - $(DefineConstants);NETSTANDARD - - $(DefineConstants);MONO diff --git a/tests/NLog.UnitTests/Targets/MailTargetTests.cs b/tests/NLog.Targets.Mail.Tests/MailTargetTests.cs similarity index 99% rename from tests/NLog.UnitTests/Targets/MailTargetTests.cs rename to tests/NLog.Targets.Mail.Tests/MailTargetTests.cs index 6facbb32e3..44b8173ada 100644 --- a/tests/NLog.UnitTests/Targets/MailTargetTests.cs +++ b/tests/NLog.Targets.Mail.Tests/MailTargetTests.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.UnitTests.Targets +namespace NLog.Targets.Mail { using System; using System.Collections.Generic; diff --git a/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj b/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj new file mode 100644 index 0000000000..3e736c1197 --- /dev/null +++ b/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj @@ -0,0 +1,31 @@ + + + + 17.0 + net462 + net462;net6.0 + + false + + NLogTests.snk + false + true + true + + Full + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/tests/NLog.Targets.Mail.Tests/NLogTests.snk b/tests/NLog.Targets.Mail.Tests/NLogTests.snk new file mode 100644 index 0000000000000000000000000000000000000000..f168e4dbecfd20aae45a58ff7c8146fd9437ca8c GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098+j;l|->ro!-M_v|L{!{P{!ND{K0P(6c zd-FqtRo5AlvWp)3?L^4gdYGOJw(uLvUU=}ZRnrkvZ)4sHmYxL91Vs-+gH5^l3FT%~ zT4&@aA_Hh(2U-<&=?)x2)II__B*H2}e}!2p6a#Iu;d48bO^8HhTl;j8?*d<1a_R|q8E50kv?eP0U3&ZlGn@^pP7iWL$P<_mA z`9zel$Knfm4TsO>!T$Xq=dg4ToMpb)#Ve)# literal 0 HcmV?d00001 From fa323a6eec6121b3781c53a75a1e5fde64c91a05 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 6 Oct 2024 21:38:41 +0200 Subject: [PATCH 014/224] Move NetworkTarget to its own nuget-package (#5636) --- Test-XmlFile.ps1 | 2 +- build.ps1 | 2 + run-tests.ps1 | 8 + .../ChainsawTarget.cs} | 29 +-- .../Log4JXmlEventLayout.cs | 17 +- .../Log4JXmlEventLayoutRenderer.cs | 120 +++++------- .../Log4JXmlEventParameter.cs} | 10 +- .../NLog.Targets.Network.csproj | 76 ++++++++ .../NetworkLogEventDroppedEventArgs.cs | 0 .../NetworkLogEventDroppedReason.cs | 0 .../NetworkSenders/HttpNetworkSender.cs | 0 .../NetworkSenders/INetworkSenderFactory.cs | 0 .../NetworkSenders/ISocket.cs | 0 .../NetworkSenders/IWebRequestFactory.cs | 0 .../NetworkSenders/NetworkSender.cs | 0 .../NetworkSenders/NetworkSenderFactory.cs | 0 .../NetworkSenders/QueuedNetworkSender.cs | 0 .../NetworkSenders/SocketProxy.cs | 0 .../NetworkSenders/SslSocketProxy.cs | 0 .../NetworkSenders/TcpNetworkSender.cs | 4 +- .../NetworkSenders/UdpNetworkSender.cs | 4 +- .../NetworkTarget.cs | 40 ++-- .../NetworkTargetCompressionType.cs | 0 .../NetworkTargetConnectionsOverflowAction.cs | 0 .../NetworkTargetOverflowAction.cs | 0 .../NetworkTargetQueueOverflowAction.cs | 0 src/NLog.Targets.Network/PlatformDetector.cs | 78 ++++++++ .../Properties/AssemblyInfo.cs | 49 +++++ src/NLog.Targets.Network/README.md | 25 +++ .../RuntimeOS.cs} | 53 +++--- src/NLog.Targets.Network/XmlHelper.cs | 174 ++++++++++++++++++ .../NLog.Targets.WebService.csproj | 8 +- src/NLog.sln | 19 +- src/NLog/Config/AssemblyExtensionTypes.cs | 6 - src/NLog/Internal/XmlHelper.cs | 36 +--- .../Log4JXmlTests.cs | 56 ++---- .../NLog.Targets.Network.Tests.csproj | 29 +++ .../NLog.Targets.Network.Tests/NLogTests.snk | Bin 0 -> 596 bytes .../NetworkSenders/HttpNetworkSenderTests.cs | 10 +- .../ManualDisposableMemoryStream.cs | 53 ++++++ .../NetworkSenders/TcpNetworkSenderTests.cs | 9 +- .../NetworkSenders/UdpNetworkSenderTests.cs | 9 +- .../NetworkSenders}/WebRequestFactoryMock.cs | 2 +- .../NetworkSenders}/WebRequestMock.cs | 2 +- .../NetworkTargetTests.cs | 151 ++++++++++----- .../NLog.UnitTests/Targets/FileTargetTests.cs | 2 +- 46 files changed, 775 insertions(+), 308 deletions(-) rename src/{NLog/Targets/NLogViewerTarget.cs => NLog.Targets.Network/ChainsawTarget.cs} (91%) rename src/{NLog/Layouts => NLog.Targets.Network}/Log4JXmlEventLayout.cs (93%) rename src/{NLog/LayoutRenderers => NLog.Targets.Network}/Log4JXmlEventLayoutRenderer.cs (79%) rename src/{NLog/Targets/NLogViewerParameterInfo.cs => NLog.Targets.Network/Log4JXmlEventParameter.cs} (90%) create mode 100644 src/NLog.Targets.Network/NLog.Targets.Network.csproj rename src/{NLog/Targets => NLog.Targets.Network}/NetworkLogEventDroppedEventArgs.cs (100%) rename src/{NLog/Targets => NLog.Targets.Network}/NetworkLogEventDroppedReason.cs (100%) rename src/{NLog/Internal => NLog.Targets.Network}/NetworkSenders/HttpNetworkSender.cs (100%) rename src/{NLog/Internal => NLog.Targets.Network}/NetworkSenders/INetworkSenderFactory.cs (100%) rename src/{NLog/Internal => NLog.Targets.Network}/NetworkSenders/ISocket.cs (100%) rename src/{NLog/Internal => NLog.Targets.Network}/NetworkSenders/IWebRequestFactory.cs (100%) rename src/{NLog/Internal => NLog.Targets.Network}/NetworkSenders/NetworkSender.cs (100%) rename src/{NLog/Internal => NLog.Targets.Network}/NetworkSenders/NetworkSenderFactory.cs (100%) rename src/{NLog/Internal => NLog.Targets.Network}/NetworkSenders/QueuedNetworkSender.cs (100%) rename src/{NLog/Internal => NLog.Targets.Network}/NetworkSenders/SocketProxy.cs (100%) rename src/{NLog/Internal => NLog.Targets.Network}/NetworkSenders/SslSocketProxy.cs (100%) rename src/{NLog/Internal => NLog.Targets.Network}/NetworkSenders/TcpNetworkSender.cs (98%) rename src/{NLog/Internal => NLog.Targets.Network}/NetworkSenders/UdpNetworkSender.cs (98%) rename src/{NLog/Targets => NLog.Targets.Network}/NetworkTarget.cs (95%) rename src/{NLog/Targets => NLog.Targets.Network}/NetworkTargetCompressionType.cs (100%) rename src/{NLog/Targets => NLog.Targets.Network}/NetworkTargetConnectionsOverflowAction.cs (100%) rename src/{NLog/Targets => NLog.Targets.Network}/NetworkTargetOverflowAction.cs (100%) rename src/{NLog/Targets => NLog.Targets.Network}/NetworkTargetQueueOverflowAction.cs (100%) create mode 100644 src/NLog.Targets.Network/PlatformDetector.cs create mode 100644 src/NLog.Targets.Network/Properties/AssemblyInfo.cs create mode 100644 src/NLog.Targets.Network/README.md rename src/{NLog/Targets/ChainsawTarget.cs => NLog.Targets.Network/RuntimeOS.cs} (57%) create mode 100644 src/NLog.Targets.Network/XmlHelper.cs rename tests/{NLog.UnitTests/LayoutRenderers => NLog.Targets.Network.Tests}/Log4JXmlTests.cs (86%) create mode 100644 tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj create mode 100644 tests/NLog.Targets.Network.Tests/NLogTests.snk rename tests/{NLog.UnitTests/Internal => NLog.Targets.Network.Tests}/NetworkSenders/HttpNetworkSenderTests.cs (97%) create mode 100644 tests/NLog.Targets.Network.Tests/NetworkSenders/ManualDisposableMemoryStream.cs rename tests/{NLog.UnitTests/Internal => NLog.Targets.Network.Tests}/NetworkSenders/TcpNetworkSenderTests.cs (98%) rename tests/{NLog.UnitTests/Internal => NLog.Targets.Network.Tests}/NetworkSenders/UdpNetworkSenderTests.cs (94%) rename tests/{NLog.UnitTests/Mocks => NLog.Targets.Network.Tests/NetworkSenders}/WebRequestFactoryMock.cs (98%) rename tests/{NLog.UnitTests/Mocks => NLog.Targets.Network.Tests/NetworkSenders}/WebRequestMock.cs (99%) rename tests/{NLog.UnitTests/Targets => NLog.Targets.Network.Tests}/NetworkTargetTests.cs (92%) diff --git a/Test-XmlFile.ps1 b/Test-XmlFile.ps1 index 3001cc41cf..a01674384b 100644 --- a/Test-XmlFile.ps1 +++ b/Test-XmlFile.ps1 @@ -54,7 +54,7 @@ function Test-XmlFile $pwd = get-location; $testFilesDir = "$pwd\examples\targets\Configuration File" $xsdFilePath = "$pwd\src\NLog\bin\Release\NLog.xsd" -$excludedTests = ("MessageBox","RichTextBox","FormControl","PerfCounter","OutputDebugString","MSMQ","Database","Mail","WebService") +$excludedTests = ("MessageBox","RichTextBox","FormControl","PerfCounter","OutputDebugString","MSMQ","Database","Mail","Network","Chainsaw","NLogViewer","WebService") # Returns true if all selected tests in examples directory are valid $ret = $true Get-ChildItem -Path $testFilesDir -Directory -Exclude $excludedTests | Get-ChildItem -Recurse -File -Filter NLog.config | % { diff --git a/build.ps1 b/build.ps1 index ab28a356f4..f9bec331ca 100644 --- a/build.ps1 +++ b/build.ps1 @@ -36,6 +36,8 @@ function create-package($packageName, $targetFrameworks) } create-package 'NLog.Database' '"net35;net45;net46;netstandard2.0"' +create-package 'NLog.Targets.Mail' '"net35;net45;net46;netstandard2.0"' +create-package 'NLog.Targets.Network' '"net45;net46;netstandard2.0"' create-package 'NLog.Targets.WebService' '"net45;net46;netstandard2.0"' create-package 'NLog.OutputDebugString' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.WindowsRegistry' '"net35;net45;net46;netstandard2.0"' diff --git a/run-tests.ps1 b/run-tests.ps1 index 512924dfa3..e4472cb8c0 100644 --- a/run-tests.ps1 +++ b/run-tests.ps1 @@ -28,6 +28,10 @@ if ($isWindows -or $Env:WinDir) if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.Network.Tests/ --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.WebService.Tests/ --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } @@ -67,6 +71,10 @@ else if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.Network.Tests/ --framework net6.0 --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.WebService.Tests/ --framework net6.0 --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } diff --git a/src/NLog/Targets/NLogViewerTarget.cs b/src/NLog.Targets.Network/ChainsawTarget.cs similarity index 91% rename from src/NLog/Targets/NLogViewerTarget.cs rename to src/NLog.Targets.Network/ChainsawTarget.cs index 217d72f726..9d5d393c4b 100644 --- a/src/NLog/Targets/NLogViewerTarget.cs +++ b/src/NLog.Targets.Network/ChainsawTarget.cs @@ -41,48 +41,47 @@ namespace NLog.Targets using NLog.Layouts; /// - /// Sends log messages to the remote instance of NLog Viewer. + /// Sends log messages to the remote instance of Chainsaw application from log4j. /// /// - /// See NLog Wiki + /// See NLog Wiki /// - /// Documentation on NLog Wiki + /// Documentation on NLog Wiki /// ///

/// To set up the target in the configuration file, /// use the following syntax: ///

- /// + /// ///

/// To set up the log target programmatically use code like this: ///

- /// + /// ///
+ [Target("Chainsaw")] [Target("NLogViewer")] - public class NLogViewerTarget : NetworkTarget, IIncludeContext + public class ChainsawTarget : NetworkTarget { private readonly Log4JXmlEventLayout _log4JLayout = new Log4JXmlEventLayout(); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// - public NLogViewerTarget() + public ChainsawTarget() { - IncludeNLogData = true; - IncludeEventProperties = false; // Already included when IncludeNLogData = true } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// /// Name of the target. - public NLogViewerTarget(string name) : this() + public ChainsawTarget(string name) : this() { Name = name; } @@ -91,6 +90,8 @@ public NLogViewerTarget(string name) : this() /// Gets or sets a value indicating whether to include NLog-specific extensions to log4j schema. /// /// + [Obsolete("Non standard extension to the Log4j-XML format. Marked obsolete with NLog 6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public bool IncludeNLogData { get => Renderer.IncludeNLogData; @@ -245,8 +246,8 @@ public string NdcItemSeparator /// between NLog layout and a named parameter. /// /// - [ArrayParameter(typeof(NLogViewerParameterInfo), "parameter")] - public IList Parameters => _log4JLayout.Parameters; + [ArrayParameter(typeof(Log4JXmlEventParameter), "parameter")] + public IList Parameters => _log4JLayout.Parameters; /// /// Gets the layout renderer which produces Log4j-compatible XML events. diff --git a/src/NLog/Layouts/Log4JXmlEventLayout.cs b/src/NLog.Targets.Network/Log4JXmlEventLayout.cs similarity index 93% rename from src/NLog/Layouts/Log4JXmlEventLayout.cs rename to src/NLog.Targets.Network/Log4JXmlEventLayout.cs index 57bfb6883b..7e5a27c45b 100644 --- a/src/NLog/Layouts/Log4JXmlEventLayout.cs +++ b/src/NLog.Targets.Network/Log4JXmlEventLayout.cs @@ -53,7 +53,7 @@ namespace NLog.Layouts /// Documentation on NLog Wiki [Layout("Log4JXmlEventLayout")] [ThreadAgnostic] - public class Log4JXmlEventLayout : Layout, IIncludeContext + public class Log4JXmlEventLayout : Layout { /// /// Initializes a new instance of the class. @@ -61,7 +61,7 @@ public class Log4JXmlEventLayout : Layout, IIncludeContext public Log4JXmlEventLayout() { Renderer = new Log4JXmlEventLayoutRenderer(); - Parameters = new List(); + Parameters = new List(); Renderer.Parameters = Parameters; } @@ -75,8 +75,8 @@ public Log4JXmlEventLayout() /// between NLog layout and a named parameter. /// /// - [ArrayParameter(typeof(NLogViewerParameterInfo), "parameter")] - public IList Parameters { get => Renderer.Parameters; set => Renderer.Parameters = value; } + [ArrayParameter(typeof(Log4JXmlEventParameter), "parameter")] + public IList Parameters { get => Renderer.Parameters; set => Renderer.Parameters = value; } /// /// Gets or sets the option to include all properties from the log events @@ -214,21 +214,16 @@ public bool IncludeSourceInfo set => Renderer.IncludeSourceInfo = value; } - internal override void PrecalculateBuilder(LogEventInfo logEvent, StringBuilder target) - { - PrecalculateBuilderInternal(logEvent, target, null); - } - /// protected override string GetFormattedMessage(LogEventInfo logEvent) { - return RenderAllocateBuilder(logEvent); + return Renderer.Render(logEvent); } /// protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) { - Renderer.RenderAppendBuilder(logEvent, target); + Renderer.AppendBuilder(logEvent, target); } } } diff --git a/src/NLog/LayoutRenderers/Log4JXmlEventLayoutRenderer.cs b/src/NLog.Targets.Network/Log4JXmlEventLayoutRenderer.cs similarity index 79% rename from src/NLog/LayoutRenderers/Log4JXmlEventLayoutRenderer.cs rename to src/NLog.Targets.Network/Log4JXmlEventLayoutRenderer.cs index 400a50ad21..c02b270be2 100644 --- a/src/NLog/LayoutRenderers/Log4JXmlEventLayoutRenderer.cs +++ b/src/NLog.Targets.Network/Log4JXmlEventLayoutRenderer.cs @@ -37,13 +37,11 @@ namespace NLog.LayoutRenderers using System.Collections.Generic; using System.ComponentModel; using System.Globalization; - using System.Reflection; using System.Text; using System.Xml; using NLog.Common; using NLog.Config; using NLog.Internal; - using NLog.Internal.Fakeables; using NLog.Layouts; using NLog.Targets; @@ -55,41 +53,31 @@ namespace NLog.LayoutRenderers /// /// Documentation on NLog Wiki [LayoutRenderer("log4jxmlevent")] - public class Log4JXmlEventLayoutRenderer : LayoutRenderer, IUsesStackTrace, IIncludeContext + public class Log4JXmlEventLayoutRenderer : LayoutRenderer, IUsesStackTrace { private static readonly DateTime log4jDateBase = new DateTime(1970, 1, 1); private static readonly string dummyNamespace = "http://nlog-project.org/dummynamespace/" + Guid.NewGuid(); private static readonly string dummyNamespaceRemover = " xmlns:log4j=\"" + dummyNamespace + "\""; - private static readonly string dummyNLogNamespace = "http://nlog-project.org/dummynamespace/" + Guid.NewGuid(); - private static readonly string dummyNLogNamespaceRemover = " xmlns:nlog=\"" + dummyNLogNamespace + "\""; - private readonly ScopeContextNestedStatesLayoutRenderer _scopeNestedLayoutRenderer = new ScopeContextNestedStatesLayoutRenderer(); /// /// Initializes a new instance of the class. /// - public Log4JXmlEventLayoutRenderer() : this(LogFactory.DefaultAppEnvironment) - { - } - - /// - /// Initializes a new instance of the class. - /// - internal Log4JXmlEventLayoutRenderer(IAppEnvironment appEnvironment) + public Log4JXmlEventLayoutRenderer() { AppInfo = string.Format( CultureInfo.InvariantCulture, "{0}({1})", - appEnvironment.AppDomainFriendlyName, - appEnvironment.CurrentProcessId); + AppDomain.CurrentDomain.FriendlyName, + System.Diagnostics.Process.GetCurrentProcess().Id); - Parameters = new List(); + Parameters = new List(); try { - _machineName = XmlHelper.XmlConvertToStringSafe(EnvironmentHelper.GetMachineName()); + _machineName = Environment.MachineName; if (string.IsNullOrEmpty(_machineName)) { InternalLogger.Info("MachineName is not available."); @@ -98,7 +86,7 @@ internal Log4JXmlEventLayoutRenderer(IAppEnvironment appEnvironment) catch (Exception exception) { InternalLogger.Error(exception, "Error getting machine name."); - if (exception.MustBeRethrown()) + if (LogManager.ThrowExceptions) { throw; } @@ -127,6 +115,8 @@ protected override void InitializeLayoutRenderer() /// Gets or sets a value indicating whether to include NLog-specific extensions to log4j schema. /// /// + [Obsolete("Non standard extension to the Log4j-XML format. Marked obsolete with NLog 6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public bool IncludeNLogData { get; set; } /// @@ -272,9 +262,25 @@ public string ScopeNestedSeparator private XmlWriterSettings _xmlWriterSettings; /// - StackTraceUsage IUsesStackTrace.StackTraceUsage => (IncludeCallSite || IncludeSourceInfo) ? (StackTraceUsageUtils.GetStackTraceUsage(IncludeSourceInfo, 0, true) | StackTraceUsage.WithCallSiteClassName) : StackTraceUsage.None; + StackTraceUsage IUsesStackTrace.StackTraceUsage + { + get + { + if (IncludeSourceInfo) + return StackTraceUsage.WithCallSite | StackTraceUsage.WithCallSiteClassName | StackTraceUsage.WithSource; + else if (IncludeCallSite) + return StackTraceUsage.WithCallSite | StackTraceUsage.WithCallSiteClassName; + else + return StackTraceUsage.None; + } + } + + internal IList Parameters { get; set; } - internal IList Parameters { get; set; } + internal void AppendBuilder(LogEventInfo logEvent, StringBuilder builder) + { + Append(builder, logEvent); + } /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) @@ -283,15 +289,10 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) using (XmlWriter xtw = XmlWriter.Create(sb, _xmlWriterSettings)) { xtw.WriteStartElement("log4j", "event", dummyNamespace); - bool includeNLogCallsite = (IncludeCallSite || IncludeSourceInfo) && logEvent.CallSiteInformation != null; - if (includeNLogCallsite && IncludeNLogData) - { - xtw.WriteAttributeString("xmlns", "nlog", null, dummyNLogNamespace); - } xtw.WriteAttributeSafeString("logger", LoggerName?.Render(logEvent) ?? logEvent.LoggerName); xtw.WriteAttributeString("level", logEvent.Level.Name.ToUpperInvariant()); xtw.WriteAttributeString("timestamp", Convert.ToString((long)(logEvent.TimeStamp.ToUniversalTime() - log4jDateBase).TotalMilliseconds, CultureInfo.InvariantCulture)); - xtw.WriteAttributeString("thread", AsyncHelpers.GetManagedThreadId().ToString(CultureInfo.InvariantCulture)); + xtw.WriteAttributeString("thread", System.Threading.Thread.CurrentThread.ManagedThreadId.ToString(CultureInfo.InvariantCulture)); xtw.WriteElementSafeString("log4j", "message", dummyNamespace, FormattedMessage?.Render(logEvent) ?? logEvent.FormattedMessage); if (logEvent.Exception != null) @@ -311,7 +312,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) AppendScopeContextNestedStates(xtw, logEvent); - if (includeNLogCallsite) + if (IncludeCallSite || IncludeSourceInfo) { AppendCallSite(logEvent, xtw); } @@ -327,7 +328,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) AppendParameters(logEvent, xtw); - var appInfo = XmlHelper.XmlConvertToStringSafe(AppInfo?.Render(logEvent) ?? string.Empty); + var appInfo = AppInfo?.Render(logEvent); AppendDataProperty(xtw, "log4japp", appInfo, dummyNamespace); AppendDataProperty(xtw, "log4jmachinename", _machineName, dummyNamespace); @@ -339,11 +340,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) // get rid of 'nlog' and 'log4j' namespace declarations sb.Replace(dummyNamespaceRemover, string.Empty); - if (includeNLogCallsite && IncludeNLogData) - { - sb.Replace(dummyNLogNamespaceRemover, string.Empty); - } - sb.CopyTo(builder); // StringBuilder.Replace is not good when reusing the StringBuilder + builder.Append(sb.ToString()); // StringBuilder.Replace is not good when reusing the StringBuilder } } @@ -351,22 +348,17 @@ private void AppendScopeContextProperties(XmlWriter xtw) { if (IncludeScopeProperties) { - using (var scopeEnumerator = ScopeContext.GetAllPropertiesEnumerator()) + foreach (var scopeProperty in ScopeContext.GetAllProperties()) { - while (scopeEnumerator.MoveNext()) - { - var scopeProperty = scopeEnumerator.Current; - - string propertyKey = XmlHelper.XmlConvertToStringSafe(scopeProperty.Key); - if (string.IsNullOrEmpty(propertyKey)) - continue; + string propertyKey = XmlHelper.RemoveInvalidXmlChars(scopeProperty.Key); + if (string.IsNullOrEmpty(propertyKey)) + continue; - string propertyValue = XmlHelper.XmlConvertToStringSafe(scopeProperty.Value); - if (propertyValue is null) - continue; + string propertyValue = XmlHelper.XmlConvertToStringSafe(scopeProperty.Value); + if (propertyValue is null) + continue; - AppendDataProperty(xtw, propertyKey, propertyValue, dummyNamespace); - } + AppendDataProperty(xtw, propertyKey, propertyValue, dummyNamespace); } } } @@ -391,7 +383,7 @@ private void AppendParameters(LogEventInfo logEvent, XmlWriter xtw) if (string.IsNullOrEmpty(parameterName)) continue; - var parameterValue = XmlHelper.XmlConvertToStringSafe(parameter.Layout?.Render(logEvent) ?? string.Empty); + var parameterValue = parameter.Layout?.Render(logEvent); if (!parameter.IncludeEmptyValue && string.IsNullOrEmpty(parameterValue)) continue; @@ -401,9 +393,11 @@ private void AppendParameters(LogEventInfo logEvent, XmlWriter xtw) private void AppendCallSite(LogEventInfo logEvent, XmlWriter xtw) { - MethodBase methodBase = logEvent.CallSiteInformation.GetCallerStackFrameMethod(0); - string callerClassName = logEvent.CallSiteInformation.GetCallerClassName(methodBase, true, true, true); - string callerMethodName = logEvent.CallSiteInformation.GetCallerMethodName(methodBase, true, true, true); + string callerMemberName = logEvent.CallerMemberName; + if (string.IsNullOrEmpty(callerMemberName)) + return; + + string callerClassName = logEvent.CallerClassName; xtw.WriteStartElement("log4j", "locationInfo", dummyNamespace); if (!string.IsNullOrEmpty(callerClassName)) @@ -411,31 +405,15 @@ private void AppendCallSite(LogEventInfo logEvent, XmlWriter xtw) xtw.WriteAttributeSafeString("class", callerClassName); } - xtw.WriteAttributeSafeString("method", callerMethodName); + xtw.WriteAttributeSafeString("method", callerMemberName); if (IncludeSourceInfo) { - xtw.WriteAttributeSafeString("file", logEvent.CallSiteInformation.GetCallerFilePath(0)); - xtw.WriteAttributeString("line", logEvent.CallSiteInformation.GetCallerLineNumber(0).ToString(CultureInfo.InvariantCulture)); + xtw.WriteAttributeSafeString("file", logEvent.CallerFilePath); + xtw.WriteAttributeString("line", logEvent.CallerLineNumber.ToString(CultureInfo.InvariantCulture)); } xtw.WriteEndElement(); - - if (IncludeNLogData) - { - xtw.WriteElementSafeString("nlog", "eventSequenceNumber", dummyNLogNamespace, logEvent.SequenceID.ToString(CultureInfo.InvariantCulture)); - xtw.WriteStartElement("nlog", "locationInfo", dummyNLogNamespace); - var type = methodBase?.DeclaringType; - if (type != null) - { - xtw.WriteAttributeSafeString("assembly", type.Assembly.FullName); - } - xtw.WriteEndElement(); - - xtw.WriteStartElement("nlog", "properties", dummyNLogNamespace); - AppendDataProperties("nlog", dummyNLogNamespace, xtw, logEvent); - xtw.WriteEndElement(); - } } private static void AppendDataProperties(string prefix, string propertiesNamespace, XmlWriter xtw, LogEventInfo logEvent) @@ -461,7 +439,7 @@ private static void AppendDataProperty(XmlWriter xtw, string propertyKey, string { xtw.WriteStartElement(prefix, "data", propertiesNamespace); xtw.WriteAttributeString("name", propertyKey); - xtw.WriteAttributeString("value", propertyValue); + xtw.WriteAttributeSafeString("value", propertyValue); xtw.WriteEndElement(); } } diff --git a/src/NLog/Targets/NLogViewerParameterInfo.cs b/src/NLog.Targets.Network/Log4JXmlEventParameter.cs similarity index 90% rename from src/NLog/Targets/NLogViewerParameterInfo.cs rename to src/NLog.Targets.Network/Log4JXmlEventParameter.cs index c4bc582e88..55953d8d8c 100644 --- a/src/NLog/Targets/NLogViewerParameterInfo.cs +++ b/src/NLog.Targets.Network/Log4JXmlEventParameter.cs @@ -38,15 +38,15 @@ namespace NLog.Targets using NLog.Layouts; /// - /// Represents a parameter to a NLogViewer target. + /// Represents a parameter for the /// [NLogConfigurationItem] - public class NLogViewerParameterInfo + public class Log4JXmlEventParameter { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public NLogViewerParameterInfo() + public Log4JXmlEventParameter() { } @@ -55,7 +55,7 @@ public NLogViewerParameterInfo() /// /// [RequiredParameter] - public string Name { get => _name; set => _name = XmlHelper.XmlConvertToStringSafe(value); } + public string Name { get => _name; set => _name = XmlHelper.RemoveInvalidXmlChars(value); } private string _name; /// diff --git a/src/NLog.Targets.Network/NLog.Targets.Network.csproj b/src/NLog.Targets.Network/NLog.Targets.Network.csproj new file mode 100644 index 0000000000..a80118ef9e --- /dev/null +++ b/src/NLog.Targets.Network/NLog.Targets.Network.csproj @@ -0,0 +1,76 @@ + + + + net46;netstandard2.0 + + NLog.Targets.Network + NLog + NetworkTarget can send messages using TCP / UDP + NLog.Targets.Network v$(ProductVersion) + $(ProductVersion) + Jarek Kowalski,Kim Christensen,Julian Verdurmen + $([System.DateTime]::Now.ToString(yyyy)) + Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ + + + WebServiceTarget Docs: + https://github.com/NLog/NLog/wiki/Network-target + + README.md + NLog;TCPIP;TCP;UDP;Network;logging;log + N.png + https://nlog-project.org/ + BSD-3-Clause + git + https://github.com/NLog/NLog.git + + true + 5.0.0.0 + ..\NLog.snk + true + + true + true + true + true + true + copyused + + + + NLog.Targets.Network for .NET Standard 2.0 + + + + NLog.Targets.Network for .NET Framework 4.6 + true + + + + NLog.Targets.Network for .NET Framework 4.5 + true + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NLog/Targets/NetworkLogEventDroppedEventArgs.cs b/src/NLog.Targets.Network/NetworkLogEventDroppedEventArgs.cs similarity index 100% rename from src/NLog/Targets/NetworkLogEventDroppedEventArgs.cs rename to src/NLog.Targets.Network/NetworkLogEventDroppedEventArgs.cs diff --git a/src/NLog/Targets/NetworkLogEventDroppedReason.cs b/src/NLog.Targets.Network/NetworkLogEventDroppedReason.cs similarity index 100% rename from src/NLog/Targets/NetworkLogEventDroppedReason.cs rename to src/NLog.Targets.Network/NetworkLogEventDroppedReason.cs diff --git a/src/NLog/Internal/NetworkSenders/HttpNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs similarity index 100% rename from src/NLog/Internal/NetworkSenders/HttpNetworkSender.cs rename to src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs diff --git a/src/NLog/Internal/NetworkSenders/INetworkSenderFactory.cs b/src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs similarity index 100% rename from src/NLog/Internal/NetworkSenders/INetworkSenderFactory.cs rename to src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs diff --git a/src/NLog/Internal/NetworkSenders/ISocket.cs b/src/NLog.Targets.Network/NetworkSenders/ISocket.cs similarity index 100% rename from src/NLog/Internal/NetworkSenders/ISocket.cs rename to src/NLog.Targets.Network/NetworkSenders/ISocket.cs diff --git a/src/NLog/Internal/NetworkSenders/IWebRequestFactory.cs b/src/NLog.Targets.Network/NetworkSenders/IWebRequestFactory.cs similarity index 100% rename from src/NLog/Internal/NetworkSenders/IWebRequestFactory.cs rename to src/NLog.Targets.Network/NetworkSenders/IWebRequestFactory.cs diff --git a/src/NLog/Internal/NetworkSenders/NetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/NetworkSender.cs similarity index 100% rename from src/NLog/Internal/NetworkSenders/NetworkSender.cs rename to src/NLog.Targets.Network/NetworkSenders/NetworkSender.cs diff --git a/src/NLog/Internal/NetworkSenders/NetworkSenderFactory.cs b/src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs similarity index 100% rename from src/NLog/Internal/NetworkSenders/NetworkSenderFactory.cs rename to src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs diff --git a/src/NLog/Internal/NetworkSenders/QueuedNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/QueuedNetworkSender.cs similarity index 100% rename from src/NLog/Internal/NetworkSenders/QueuedNetworkSender.cs rename to src/NLog.Targets.Network/NetworkSenders/QueuedNetworkSender.cs diff --git a/src/NLog/Internal/NetworkSenders/SocketProxy.cs b/src/NLog.Targets.Network/NetworkSenders/SocketProxy.cs similarity index 100% rename from src/NLog/Internal/NetworkSenders/SocketProxy.cs rename to src/NLog.Targets.Network/NetworkSenders/SocketProxy.cs diff --git a/src/NLog/Internal/NetworkSenders/SslSocketProxy.cs b/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs similarity index 100% rename from src/NLog/Internal/NetworkSenders/SslSocketProxy.cs rename to src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs diff --git a/src/NLog/Internal/NetworkSenders/TcpNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs similarity index 98% rename from src/NLog/Internal/NetworkSenders/TcpNetworkSender.cs rename to src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs index 70ca2ba366..03c7e53244 100644 --- a/src/NLog/Internal/NetworkSenders/TcpNetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs @@ -218,7 +218,7 @@ private void CloseSocket(AsyncContinuation continuation, Exception pendingExcept } catch (Exception exception) { - if (exception.MustBeRethrown()) + if (LogManager.ThrowExceptions) { throw; } @@ -236,7 +236,7 @@ protected override void BeginRequest(NetworkRequestArgs eventArgs) // Schedule async network operation to avoid blocking socket-operation (Allow adding more request) if (_asyncBeginRequest is null) _asyncBeginRequest = BeginRequestAsync; - AsyncHelpers.StartAsyncTask(_asyncBeginRequest, socketEventArgs); + System.Threading.ThreadPool.QueueUserWorkItem(_asyncBeginRequest, socketEventArgs); } private void BeginRequestAsync(object state) diff --git a/src/NLog/Internal/NetworkSenders/UdpNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/UdpNetworkSender.cs similarity index 98% rename from src/NLog/Internal/NetworkSenders/UdpNetworkSender.cs rename to src/NLog.Targets.Network/NetworkSenders/UdpNetworkSender.cs index 355cec345e..97c2f26559 100644 --- a/src/NLog/Internal/NetworkSenders/UdpNetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/UdpNetworkSender.cs @@ -105,7 +105,7 @@ private void CloseSocket(AsyncContinuation continuation, Exception pendingExcept } catch (Exception exception) { - if (exception.MustBeRethrown()) + if (LogManager.ThrowExceptions) { throw; } @@ -124,7 +124,7 @@ protected override void BeginRequest(NetworkRequestArgs eventArgs) // Schedule async network operation to avoid blocking socket-operation (Allow adding more request) if (_asyncBeginRequest is null) _asyncBeginRequest = BeginRequestAsync; - AsyncHelpers.StartAsyncTask(_asyncBeginRequest, socketEventArgs); + System.Threading.ThreadPool.QueueUserWorkItem(_asyncBeginRequest, socketEventArgs); } private void BeginRequestAsync(object state) diff --git a/src/NLog/Targets/NetworkTarget.cs b/src/NLog.Targets.Network/NetworkTarget.cs similarity index 95% rename from src/NLog/Targets/NetworkTarget.cs rename to src/NLog.Targets.Network/NetworkTarget.cs index 6fad19123a..c64d2c2ec9 100644 --- a/src/NLog/Targets/NetworkTarget.cs +++ b/src/NLog.Targets.Network/NetworkTarget.cs @@ -78,7 +78,8 @@ public class NetworkTarget : TargetWithLayout private readonly Dictionary> _currentSenderCache = new Dictionary>(StringComparer.Ordinal); private readonly LinkedList _openNetworkSenders = new LinkedList(); - private readonly ReusableBufferCreator _reusableEncodingBuffer = new ReusableBufferCreator(32 * 1024); + private readonly char[] _reusableEncodingBuffer = new char[32 * 1024]; + private readonly StringBuilder _reusableStringBuilder = new StringBuilder(); /// /// Initializes a new instance of the class. @@ -506,24 +507,22 @@ protected virtual byte[] GetBytesToWrite(LogEventInfo logEvent) private byte[] RenderBytesToWrite(LogEventInfo logEvent) { - using (var localBuffer = _reusableEncodingBuffer.Allocate()) + lock (_reusableEncodingBuffer) { - if (!NewLine && logEvent.TryGetCachedLayoutValue(Layout, out var text)) - { - return GetBytesFromString(localBuffer.Result, text?.ToString() ?? string.Empty); - } - else + try { - using (var localBuilder = ReusableLayoutBuilder.Allocate()) + _reusableStringBuilder.Length = 0; + Layout.Render(logEvent, _reusableStringBuilder); + if (NewLine) { - Layout.Render(logEvent, localBuilder.Result); - if (NewLine) - { - localBuilder.Result.Append(LineEnding.NewLineCharacters); - } - - return GetBytesFromStringBuilder(localBuffer.Result, localBuilder.Result); + _reusableStringBuilder.Append(LineEnding.NewLineCharacters); } + + return GetBytesFromStringBuilder(_reusableEncodingBuffer, _reusableStringBuilder); + } + finally + { + _reusableStringBuilder.Length = 0; } } } @@ -538,17 +537,6 @@ private byte[] GetBytesFromStringBuilder(char[] charBuffer, StringBuilder string return Encoding.GetBytes(stringBuilder.ToString()); } - private byte[] GetBytesFromString(char[] charBuffer, string layoutMessage) - { - InternalLogger.Trace("{0}: Sending {1}", this, layoutMessage); - if (layoutMessage.Length <= charBuffer.Length) - { - layoutMessage.CopyTo(0, charBuffer, 0, layoutMessage.Length); - return Encoding.GetBytes(charBuffer, 0, layoutMessage.Length); - } - return Encoding.GetBytes(layoutMessage); - } - private byte[] CompressBytesToWrite(byte[] payload) { if (payload.Length > CompressMinBytes) diff --git a/src/NLog/Targets/NetworkTargetCompressionType.cs b/src/NLog.Targets.Network/NetworkTargetCompressionType.cs similarity index 100% rename from src/NLog/Targets/NetworkTargetCompressionType.cs rename to src/NLog.Targets.Network/NetworkTargetCompressionType.cs diff --git a/src/NLog/Targets/NetworkTargetConnectionsOverflowAction.cs b/src/NLog.Targets.Network/NetworkTargetConnectionsOverflowAction.cs similarity index 100% rename from src/NLog/Targets/NetworkTargetConnectionsOverflowAction.cs rename to src/NLog.Targets.Network/NetworkTargetConnectionsOverflowAction.cs diff --git a/src/NLog/Targets/NetworkTargetOverflowAction.cs b/src/NLog.Targets.Network/NetworkTargetOverflowAction.cs similarity index 100% rename from src/NLog/Targets/NetworkTargetOverflowAction.cs rename to src/NLog.Targets.Network/NetworkTargetOverflowAction.cs diff --git a/src/NLog/Targets/NetworkTargetQueueOverflowAction.cs b/src/NLog.Targets.Network/NetworkTargetQueueOverflowAction.cs similarity index 100% rename from src/NLog/Targets/NetworkTargetQueueOverflowAction.cs rename to src/NLog.Targets.Network/NetworkTargetQueueOverflowAction.cs diff --git a/src/NLog.Targets.Network/PlatformDetector.cs b/src/NLog.Targets.Network/PlatformDetector.cs new file mode 100644 index 0000000000..16d430b7e1 --- /dev/null +++ b/src/NLog.Targets.Network/PlatformDetector.cs @@ -0,0 +1,78 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Internal +{ + /// + /// Detects the platform the NLog is running on. + /// + internal static class PlatformDetector + { + /// + /// Gets the current runtime OS. + /// + public static RuntimeOS CurrentOS => _currentOS ?? (_currentOS = GetCurrentRuntimeOS()).Value; + private static RuntimeOS? _currentOS; + + private static RuntimeOS GetCurrentRuntimeOS() + { +#if NETFRAMEWORK + var platformID = System.Environment.OSVersion.Platform; + if ((int)platformID == 4 || (int)platformID == 128) + { + return RuntimeOS.Linux; + } + + if (platformID == System.PlatformID.Win32Windows) + { + return RuntimeOS.Windows9x; + } + + if (platformID == System.PlatformID.Win32NT) + { + return RuntimeOS.WindowsNT; + } + + return RuntimeOS.Unknown; +#else + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) + return RuntimeOS.WindowsNT; + else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) + return RuntimeOS.MacOSX; + else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux)) + return RuntimeOS.Linux; + return RuntimeOS.Unknown; +#endif + } + } +} diff --git a/src/NLog.Targets.Network/Properties/AssemblyInfo.cs b/src/NLog.Targets.Network/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..797aaa2bce --- /dev/null +++ b/src/NLog.Targets.Network/Properties/AssemblyInfo.cs @@ -0,0 +1,49 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +[assembly: AssemblyCulture("")] +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] +[assembly: InternalsVisibleTo("NLog.Targets.Network.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100ef8eab4fbdeb511eeb475e1659fe53f00ec1c1340700f1aa347bf3438455d71993b28b1efbed44c8d97a989e0cb6f01bcb5e78f0b055d311546f63de0a969e04cf04450f43834db9f909e566545a67e42822036860075a1576e90e1c43d43e023a24c22a427f85592ae56cac26f13b7ec2625cbc01f9490d60f16cfbb1bc34d9")] +// NSubstitute +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] +[assembly: AllowPartiallyTrustedCallers] +#if !NET35 +[assembly: SecurityRules(SecurityRuleSet.Level1)] +#endif diff --git a/src/NLog.Targets.Network/README.md b/src/NLog.Targets.Network/README.md new file mode 100644 index 0000000000..0067d73adf --- /dev/null +++ b/src/NLog.Targets.Network/README.md @@ -0,0 +1,25 @@ +# NLog Network Target + +NLog Network Target for sending mesages using TCP / UDP with support for SSL / TSL. + +If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) + +See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Network-target) for available options and examples. + +## Register Extension + +NLog will only recognize type-alias `Network` when loading from `NLog.config`-file, if having added extension to `NLog.config`-file: + +```xml + + + +``` + +Alternative register from code using [fluent configuration API](https://github.com/NLog/NLog/wiki/Fluent-Configuration-API): + +```csharp +LogManager.Setup().SetupExtensions(ext => { + ext.RegisterTarget(); +}); +``` diff --git a/src/NLog/Targets/ChainsawTarget.cs b/src/NLog.Targets.Network/RuntimeOS.cs similarity index 57% rename from src/NLog/Targets/ChainsawTarget.cs rename to src/NLog.Targets.Network/RuntimeOS.cs index 3791b5820c..79da0a101d 100644 --- a/src/NLog/Targets/ChainsawTarget.cs +++ b/src/NLog.Targets.Network/RuntimeOS.cs @@ -31,45 +31,40 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.Targets +namespace NLog.Internal { /// - /// Sends log messages to the remote instance of Chainsaw application from log4j. + /// Supported operating systems. /// /// - /// See NLog Wiki + /// If you add anything here, make sure to add the appropriate detection + /// code to /// - /// Documentation on NLog Wiki - /// - ///

- /// To set up the target in the configuration file, - /// use the following syntax: - ///

- /// - ///

- /// To set up the log target programmatically use code like this: - ///

- /// - ///
- [Target("Chainsaw")] - public class ChainsawTarget : NLogViewerTarget + internal enum RuntimeOS { /// - /// Initializes a new instance of the class. + /// Unknown operating system. /// - public ChainsawTarget() - { - IncludeNLogData = false; - IncludeEventProperties = true; - } + Unknown, /// - /// Initializes a new instance of the class with a name. + /// Unix/Linux operating systems. /// - /// Name of the target. - public ChainsawTarget(string name) : this() - { - Name = name; - } + Linux, + + /// + /// Desktop versions of Windows (95,98,ME). + /// + Windows9x, + + /// + /// Windows NT, 2000, 2003 and future versions based on NT technology. + /// + WindowsNT, + + /// + /// Macintosh Mac OSX + /// + MacOSX, } } diff --git a/src/NLog.Targets.Network/XmlHelper.cs b/src/NLog.Targets.Network/XmlHelper.cs new file mode 100644 index 0000000000..f8d4da7f00 --- /dev/null +++ b/src/NLog.Targets.Network/XmlHelper.cs @@ -0,0 +1,174 @@ +using System; +using System.Globalization; +using System.Text; +using System.Xml; + +namespace NLog.Internal +{ + internal static class XmlHelper + { + /// + /// Safe version of WriteAttributeString + /// + /// + /// + /// + public static void WriteAttributeSafeString(this XmlWriter writer, string localName, string value) + { + writer.WriteAttributeString(localName, RemoveInvalidXmlChars(value)); + } + + /// + /// Safe version of WriteElementSafeString + /// + /// + /// + /// + /// + /// + public static void WriteElementSafeString(this XmlWriter writer, string prefix, string localName, string ns, string value) + { + writer.WriteElementString(prefix, localName, ns, RemoveInvalidXmlChars(value)); + } + + /// + /// Safe version of WriteCData + /// + /// + /// + public static void WriteSafeCData(this XmlWriter writer, string value) + { + writer.WriteCData(RemoveInvalidXmlChars(value)); + } + + public static string XmlConvertToStringSafe(object value) + { + try + { + var convertibleValue = value as IConvertible; + var objTypeCode = convertibleValue?.GetTypeCode() ?? (value is null ? TypeCode.Empty : TypeCode.Object); + if (objTypeCode != TypeCode.Object) + { + return XmlConvertToString(convertibleValue, objTypeCode); + } + + return XmlConvertToStringInvariant(value); + } + catch + { + return string.Empty; + } + } + + /// + /// Converts object value to invariant format (understood by JavaScript) + /// + /// Object value + /// Object TypeCode + /// Check and remove unusual unicode characters from the result string. + /// Object value converted to string + internal static string XmlConvertToString(IConvertible value, TypeCode objTypeCode, bool safeConversion = false) + { + if (objTypeCode == TypeCode.Empty || value is null) + { + return "null"; + } + + switch (objTypeCode) + { + case TypeCode.Boolean: + return XmlConvert.ToString(value.ToBoolean(CultureInfo.InvariantCulture)); // boolean as lowercase + case TypeCode.Byte: + return XmlConvert.ToString(value.ToByte(CultureInfo.InvariantCulture)); + case TypeCode.SByte: + return XmlConvert.ToString(value.ToSByte(CultureInfo.InvariantCulture)); + case TypeCode.Int16: + return XmlConvert.ToString(value.ToInt16(CultureInfo.InvariantCulture)); + case TypeCode.Int32: + return XmlConvert.ToString(value.ToInt32(CultureInfo.InvariantCulture)); + case TypeCode.Int64: + return XmlConvert.ToString(value.ToInt64(CultureInfo.InvariantCulture)); + case TypeCode.UInt16: + return XmlConvert.ToString(value.ToUInt16(CultureInfo.InvariantCulture)); + case TypeCode.UInt32: + return XmlConvert.ToString(value.ToUInt32(CultureInfo.InvariantCulture)); + case TypeCode.UInt64: + return XmlConvert.ToString(value.ToUInt64(CultureInfo.InvariantCulture)); + case TypeCode.Single: + return XmlConvert.ToString(value.ToSingle(CultureInfo.InvariantCulture)); + case TypeCode.Double: + return XmlConvert.ToString(value.ToDouble(CultureInfo.InvariantCulture)); + case TypeCode.Decimal: + return XmlConvert.ToString(value.ToDecimal(CultureInfo.InvariantCulture)); + case TypeCode.DateTime: + return XmlConvert.ToString(value.ToDateTime(CultureInfo.InvariantCulture), XmlDateTimeSerializationMode.Utc); + case TypeCode.Char: + return RemoveInvalidXmlChars(value.ToString(CultureInfo.InvariantCulture)); + case TypeCode.String: + return RemoveInvalidXmlChars(value.ToString(CultureInfo.InvariantCulture)); + default: + return XmlConvertToStringInvariant(value); + } + } + + private static string XmlConvertToStringInvariant(object value) + { + try + { + string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + return RemoveInvalidXmlChars(valueString); + } + catch + { + return string.Empty; + } + } + + + /// + /// removes any unusual unicode characters that can't be encoded into XML + /// + public static string RemoveInvalidXmlChars(string text) + { + if (string.IsNullOrEmpty(text)) + return string.Empty; + + int length = text.Length; + for (int i = 0; i < length; ++i) + { + char ch = text[i]; + if (!XmlConvert.IsXmlChar(ch)) + { + if (i + 1 < text.Length && XmlConvert.IsXmlSurrogatePair(text[i + 1], ch)) + { + ++i; + } + else + { + return CreateValidXmlString(text); // rare expensive case + } + } + } + return text; + } + + /// + /// Cleans string of any invalid XML chars found + /// + /// unclean string + /// string with only valid XML chars + private static string CreateValidXmlString(string text) + { + var sb = new StringBuilder(text.Length); + for (int i = 0; i < text.Length; ++i) + { + char ch = text[i]; + if (XmlConvert.IsXmlChar(ch)) + { + sb.Append(ch); + } + } + return sb.ToString(); + } + } +} diff --git a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj index 637e38621d..d5d31deae6 100644 --- a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj +++ b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj @@ -51,13 +51,7 @@ true - - - - - - - + diff --git a/src/NLog.sln b/src/NLog.sln index 140d95f86a..e72392b315 100644 --- a/src/NLog.sln +++ b/src/NLog.sln @@ -50,9 +50,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.Mail", "NLog.T EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.Mail.Tests", "..\tests\NLog.Targets.Mail.Tests\NLog.Targets.Mail.Tests.csproj", "{F26A0E10-7DED-4AD9-BB59-147E4EB70924}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.WebService", "NLog.Targets.WebService\NLog.Targets.WebService.csproj", "{F5F88648-5D6C-4A18-B98E-FCEEA4D704CE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.WebService", "NLog.Targets.WebService\NLog.Targets.WebService.csproj", "{F5F88648-5D6C-4A18-B98E-FCEEA4D704CE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.WebService.Tests", "..\tests\NLog.Targets.WebService.Tests\NLog.Targets.WebService.Tests.csproj", "{D6FB13E7-92A7-4F4F-A09A-37EB04767A82}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.WebService.Tests", "..\tests\NLog.Targets.WebService.Tests\NLog.Targets.WebService.Tests.csproj", "{D6FB13E7-92A7-4F4F-A09A-37EB04767A82}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.Network", "NLog.Targets.Network\NLog.Targets.Network.csproj", "{B048DD1F-4A24-453B-9C3D-F08280AE3FF3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.Network.Tests", "..\tests\NLog.Targets.Network.Tests\NLog.Targets.Network.Tests.csproj", "{38828090-4953-4EF5-929C-8063FD4BCCC9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -124,11 +128,20 @@ Global {D6FB13E7-92A7-4F4F-A09A-37EB04767A82}.Debug|Any CPU.Build.0 = Debug|Any CPU {D6FB13E7-92A7-4F4F-A09A-37EB04767A82}.Release|Any CPU.ActiveCfg = Release|Any CPU {D6FB13E7-92A7-4F4F-A09A-37EB04767A82}.Release|Any CPU.Build.0 = Release|Any CPU + {B048DD1F-4A24-453B-9C3D-F08280AE3FF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B048DD1F-4A24-453B-9C3D-F08280AE3FF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B048DD1F-4A24-453B-9C3D-F08280AE3FF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B048DD1F-4A24-453B-9C3D-F08280AE3FF3}.Release|Any CPU.Build.0 = Release|Any CPU + {38828090-4953-4EF5-929C-8063FD4BCCC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {38828090-4953-4EF5-929C-8063FD4BCCC9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {38828090-4953-4EF5-929C-8063FD4BCCC9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {38828090-4953-4EF5-929C-8063FD4BCCC9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {A0BFF0DB-ED9A-4639-AE86-8E709A1EFC66} = {8CCD7CF7-F30B-414A-B7FE-1B49411E4EFD} {94F21E64-A259-4D79-AFCA-08974DF51701} = {8CCD7CF7-F30B-414A-B7FE-1B49411E4EFD} {B1DA63B3-1EB0-41B3-BFDA-9D7409481E27} = {8CCD7CF7-F30B-414A-B7FE-1B49411E4EFD} {99BCE294-CA56-41BD-AEBF-61795F5650AB} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} @@ -143,6 +156,8 @@ Global {F26A0E10-7DED-4AD9-BB59-147E4EB70924} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {F5F88648-5D6C-4A18-B98E-FCEEA4D704CE} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {D6FB13E7-92A7-4F4F-A09A-37EB04767A82} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {B048DD1F-4A24-453B-9C3D-F08280AE3FF3} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {38828090-4953-4EF5-929C-8063FD4BCCC9} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6B4C8E9F-1E2F-4AB3-993A-8776CFA08DC0} diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index 93818c5494..c434bc2490 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -97,7 +97,6 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.LayoutRendererFactory.RegisterType("literal"); factory.RegisterTypeProperties(() => null); factory.LayoutRendererFactory.RegisterType("local-ip"); - factory.LayoutRendererFactory.RegisterType("log4jxmlevent"); factory.LayoutRendererFactory.RegisterType("loggername"); factory.LayoutRendererFactory.RegisterType("logger"); factory.LayoutRendererFactory.RegisterType("longdate"); @@ -184,12 +183,10 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.RegisterType(); factory.LayoutFactory.RegisterType("JsonLayout"); factory.LayoutFactory.RegisterType("LayoutWithHeaderAndFooter"); - factory.LayoutFactory.RegisterType("Log4JXmlEventLayout"); factory.LayoutFactory.RegisterType("SimpleLayout"); factory.RegisterType(); factory.RegisterType(); factory.LayoutFactory.RegisterType("XmlLayout"); - factory.TargetFactory.RegisterType("Chainsaw"); factory.TargetFactory.RegisterType("ColoredConsole"); factory.RegisterType(); factory.TargetFactory.RegisterType("Console"); @@ -204,9 +201,6 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.TargetFactory.RegisterType("Memory"); factory.RegisterType(); factory.TargetFactory.RegisterType("MethodCall"); - factory.TargetFactory.RegisterType("Network"); - factory.RegisterType(); - factory.TargetFactory.RegisterType("NLogViewer"); factory.TargetFactory.RegisterType("Null"); factory.RegisterType(); factory.TargetFactory.RegisterType("Trace"); diff --git a/src/NLog/Internal/XmlHelper.cs b/src/NLog/Internal/XmlHelper.cs index aa6510036a..7e9701ce2a 100644 --- a/src/NLog/Internal/XmlHelper.cs +++ b/src/NLog/Internal/XmlHelper.cs @@ -411,7 +411,7 @@ internal static string XmlConvertToString(IConvertible value, TypeCode objTypeCo case TypeCode.DateTime: return XmlConvert.ToString(value.ToDateTime(CultureInfo.InvariantCulture), XmlDateTimeSerializationMode.Utc); case TypeCode.Char: - return XmlConvert.ToString(value.ToChar(CultureInfo.InvariantCulture)); + return safeConversion ? RemoveInvalidXmlChars(value.ToString(CultureInfo.InvariantCulture)) : value.ToString(CultureInfo.InvariantCulture); case TypeCode.String: return safeConversion ? RemoveInvalidXmlChars(value.ToString(CultureInfo.InvariantCulture)) : value.ToString(CultureInfo.InvariantCulture); default: @@ -419,40 +419,6 @@ internal static string XmlConvertToString(IConvertible value, TypeCode objTypeCo } } - /// - /// Safe version of WriteAttributeString - /// - /// - /// - /// - public static void WriteAttributeSafeString(this XmlWriter writer, string localName, string value) - { - writer.WriteAttributeString(localName, RemoveInvalidXmlChars(value)); - } - - /// - /// Safe version of WriteElementSafeString - /// - /// - /// - /// - /// - /// - public static void WriteElementSafeString(this XmlWriter writer, string prefix, string localName, string ns, string value) - { - writer.WriteElementString(prefix, localName, ns, RemoveInvalidXmlChars(value)); - } - - /// - /// Safe version of WriteCData - /// - /// - /// - public static void WriteSafeCData(this XmlWriter writer, string value) - { - writer.WriteCData(RemoveInvalidXmlChars(value)); - } - private static string EnsureDecimalPlace(string text) { if (text.IndexOf('.') != -1 || text.IndexOfAny(DecimalScientificExponent) != -1) diff --git a/tests/NLog.UnitTests/LayoutRenderers/Log4JXmlTests.cs b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs similarity index 86% rename from tests/NLog.UnitTests/LayoutRenderers/Log4JXmlTests.cs rename to tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs index c8aec1fa7e..19ac765a0c 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/Log4JXmlTests.cs +++ b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.UnitTests.LayoutRenderers +namespace NLog.Targets.Network { using System; using System.Collections.Generic; @@ -39,16 +39,24 @@ namespace NLog.UnitTests.LayoutRenderers using System.Reflection; using System.Xml; using NLog.Internal; + using NLog.LayoutRenderers; using NLog.Layouts; using NLog.Targets; using Xunit; - public class Log4JXmlTests : NLogTestBase + public class Log4JXmlTests { + public Log4JXmlTests() + { + LogManager.ThrowExceptions = true; + } + [Fact] public void Log4JXmlTest() { - var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@" + var logFactory = new LogFactory().Setup() + .SetupExtensions(ext => ext.RegisterLayoutRenderer()) + .LoadConfigurationFromXml(@" @@ -72,7 +80,7 @@ public void Log4JXmlTest() var logEventInfo = LogEventInfo.Create(LogLevel.Debug, "A", new Exception("Hello Exception", new Exception("Goodbye Exception")), null, "some message \u0014"); logEventInfo.Properties["nlogPropertyKey"] = "nlogPropertyValue"; logger.Log(logEventInfo); - string result = GetDebugLastMessage("debug", logFactory); + string result = logFactory.Configuration.FindTargetByName("debug").LastMessage; Assert.DoesNotContain("dummy", result); string wrappedResult = "" + result + ""; @@ -89,12 +97,9 @@ public void Log4JXmlTest() "log4j.message", "log4j.NDC", "log4j.locationInfo", - "nlog.locationInfo", "log4j.properties", - "nlog.properties", "log4j.throwable", "log4j.data", - "nlog.data", }; using (XmlReader reader = XmlReader.Create(stringReader)) @@ -127,7 +132,7 @@ public void Log4JXmlTest() var now = DateTime.UtcNow; Assert.True(now.Ticks - time.Ticks < TimeSpan.FromSeconds(3).Ticks); - Assert.Equal(CurrentManagedThreadId.ToString(), reader.GetAttribute("thread")); + Assert.Equal(Environment.CurrentManagedThreadId.ToString(), reader.GetAttribute("thread")); break; case "message": @@ -142,7 +147,7 @@ public void Log4JXmlTest() case "locationInfo": Assert.Equal(MethodBase.GetCurrentMethod().DeclaringType.FullName, reader.GetAttribute("class")); - Assert.Equal(MethodBase.GetCurrentMethod().ToString(), reader.GetAttribute("method")); + Assert.Equal(MethodBase.GetCurrentMethod().Name, reader.GetAttribute("method")); break; case "properties": @@ -160,7 +165,7 @@ public void Log4JXmlTest() switch (name) { case "log4japp": - Assert.Equal(AppDomain.CurrentDomain.FriendlyName + "(" + CurrentProcessId + ")", value); + Assert.Equal(AppDomain.CurrentDomain.FriendlyName + "(" + System.Diagnostics.Process.GetCurrentProcess().Id + ")", value); break; case "log4jmachinename": @@ -192,33 +197,6 @@ public void Log4JXmlTest() default: throw new NotSupportedException("Unknown element: " + key); } - continue; - } - - if (reader.NodeType == XmlNodeType.Element && reader.Prefix == "nlog") - { - switch (key) - { - case "eventSequenceNumber": - break; - - case "locationInfo": - Assert.Equal(GetType().Assembly.FullName, reader.GetAttribute("assembly")); - break; - - case "properties": - break; - - case "data": - var name = reader.GetAttribute("name"); - var value = reader.GetAttribute("value"); - Assert.Equal("nlogPropertyKey", name); - Assert.Equal("nlogPropertyValue", value); - break; - - default: - throw new NotSupportedException("Unknown element: " + key); - } } } } @@ -236,7 +214,7 @@ public void Log4JXmlEventLayoutParameterTest() { Parameters = { - new NLogViewerParameterInfo + new Log4JXmlEventParameter { Name = "mt", Layout = "${message:raw=true}", @@ -253,7 +231,7 @@ public void Log4JXmlEventLayoutParameterTest() Parameters = new[] { "world" }, }; - var threadid = CurrentManagedThreadId; + var threadid = Environment.CurrentManagedThreadId; var machinename = Environment.MachineName; Assert.Equal($"hello, <world>", log4jLayout.Render(logEventInfo)); } diff --git a/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj b/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj new file mode 100644 index 0000000000..47f2c1a26c --- /dev/null +++ b/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj @@ -0,0 +1,29 @@ + + + + 17.0 + net462 + net462;net6.0 + false + + NLogTests.snk + false + true + true + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/tests/NLog.Targets.Network.Tests/NLogTests.snk b/tests/NLog.Targets.Network.Tests/NLogTests.snk new file mode 100644 index 0000000000000000000000000000000000000000..f168e4dbecfd20aae45a58ff7c8146fd9437ca8c GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098+j;l|->ro!-M_v|L{!{P{!ND{K0P(6c zd-FqtRo5AlvWp)3?L^4gdYGOJw(uLvUU=}ZRnrkvZ)4sHmYxL91Vs-+gH5^l3FT%~ zT4&@aA_Hh(2U-<&=?)x2)II__B*H2}e}!2p6a#Iu;d48bO^8HhTl;j8?*d<1a_R|q8E50kv?eP0U3&ZlGn@^pP7iWL$P<_mA z`9zel$Knfm4TsO>!T$Xq=dg4ToMpb)#Ve)# literal 0 HcmV?d00001 diff --git a/tests/NLog.UnitTests/Internal/NetworkSenders/HttpNetworkSenderTests.cs b/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs similarity index 97% rename from tests/NLog.UnitTests/Internal/NetworkSenders/HttpNetworkSenderTests.cs rename to tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs index c63e13687e..35d16e5dbd 100644 --- a/tests/NLog.UnitTests/Internal/NetworkSenders/HttpNetworkSenderTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs @@ -36,14 +36,18 @@ using NLog.Config; using NLog.Internal.NetworkSenders; using NLog.Targets; -using NLog.UnitTests.Mocks; using NSubstitute; using Xunit; -namespace NLog.UnitTests.Internal.NetworkSenders +namespace NLog.Targets.Network { - public class HttpNetworkSenderTests : NLogTestBase + public class HttpNetworkSenderTests { + public HttpNetworkSenderTests() + { + LogManager.ThrowExceptions = true; + } + /// /// Test via /// diff --git a/tests/NLog.Targets.Network.Tests/NetworkSenders/ManualDisposableMemoryStream.cs b/tests/NLog.Targets.Network.Tests/NetworkSenders/ManualDisposableMemoryStream.cs new file mode 100644 index 0000000000..371db62ca9 --- /dev/null +++ b/tests/NLog.Targets.Network.Tests/NetworkSenders/ManualDisposableMemoryStream.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +using System.IO; + +namespace NLog.Targets.Network +{ + /// + /// Memorystream that doesn't dispose by default for asserting the content + /// + public sealed class ManualDisposableMemoryStream : MemoryStream + { + public bool Disposed { get; set; } + protected override void Dispose(bool disposing) + { + Disposed = true; + } + public void RealDispose() + { + base.Dispose(true); + } + } +} diff --git a/tests/NLog.UnitTests/Internal/NetworkSenders/TcpNetworkSenderTests.cs b/tests/NLog.Targets.Network.Tests/NetworkSenders/TcpNetworkSenderTests.cs similarity index 98% rename from tests/NLog.UnitTests/Internal/NetworkSenders/TcpNetworkSenderTests.cs rename to tests/NLog.Targets.Network.Tests/NetworkSenders/TcpNetworkSenderTests.cs index a322d6df2f..d098078213 100644 --- a/tests/NLog.UnitTests/Internal/NetworkSenders/TcpNetworkSenderTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkSenders/TcpNetworkSenderTests.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.UnitTests.Internal.NetworkSenders +namespace NLog.Targets.Network { using System; using System.Collections.Generic; @@ -43,8 +43,13 @@ namespace NLog.UnitTests.Internal.NetworkSenders using NLog.Internal.NetworkSenders; using Xunit; - public class TcpNetworkSenderTests : NLogTestBase + public class TcpNetworkSenderTests { + public TcpNetworkSenderTests() + { + LogManager.ThrowExceptions = true; + } + [Fact] public void TcpHappyPathTest() { diff --git a/tests/NLog.UnitTests/Internal/NetworkSenders/UdpNetworkSenderTests.cs b/tests/NLog.Targets.Network.Tests/NetworkSenders/UdpNetworkSenderTests.cs similarity index 94% rename from tests/NLog.UnitTests/Internal/NetworkSenders/UdpNetworkSenderTests.cs rename to tests/NLog.Targets.Network.Tests/NetworkSenders/UdpNetworkSenderTests.cs index 89e9da3f6c..0ee65bf38b 100644 --- a/tests/NLog.UnitTests/Internal/NetworkSenders/UdpNetworkSenderTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkSenders/UdpNetworkSenderTests.cs @@ -31,16 +31,19 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.UnitTests.Internal.NetworkSenders +namespace NLog.Targets.Network { using System.Net.Sockets; - using NLog.Internal.NetworkSenders; - using Xunit; public class UdpNetworkSenderTests { + public UdpNetworkSenderTests() + { + LogManager.ThrowExceptions = true; + } + [Theory] [InlineData("udp://127.0.0.1:8080", false)] [InlineData("udp://255.255.255.255:8080", true)] diff --git a/tests/NLog.UnitTests/Mocks/WebRequestFactoryMock.cs b/tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestFactoryMock.cs similarity index 98% rename from tests/NLog.UnitTests/Mocks/WebRequestFactoryMock.cs rename to tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestFactoryMock.cs index 9888152d07..0238ccbbd3 100644 --- a/tests/NLog.UnitTests/Mocks/WebRequestFactoryMock.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestFactoryMock.cs @@ -35,7 +35,7 @@ using System.Net; using NLog.Internal.NetworkSenders; -namespace NLog.UnitTests.Mocks +namespace NLog.Targets.Network { [Obsolete("WebRequest is obsolete. Use HttpClient instead.")] class WebRequestFactoryMock : IWebRequestFactory diff --git a/tests/NLog.UnitTests/Mocks/WebRequestMock.cs b/tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestMock.cs similarity index 99% rename from tests/NLog.UnitTests/Mocks/WebRequestMock.cs rename to tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestMock.cs index fa714a0fe0..66bd5eccbd 100644 --- a/tests/NLog.UnitTests/Mocks/WebRequestMock.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestMock.cs @@ -36,7 +36,7 @@ using System.Net; using NSubstitute; -namespace NLog.UnitTests.Mocks +namespace NLog.Targets.Network { [Obsolete("WebRequest is obsolete. Use HttpClient instead.")] public sealed class WebRequestMock : WebRequest, IDisposable diff --git a/tests/NLog.UnitTests/Targets/NetworkTargetTests.cs b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs similarity index 92% rename from tests/NLog.UnitTests/Targets/NetworkTargetTests.cs rename to tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs index deb53bfb53..e0dc7715c7 100644 --- a/tests/NLog.UnitTests/Targets/NetworkTargetTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.UnitTests.Targets +namespace NLog.Targets.Network { using System; using System.Collections.Generic; @@ -48,8 +48,13 @@ namespace NLog.UnitTests.Targets using NLog.Targets; using Xunit; - public class NetworkTargetTests : NLogTestBase + public class NetworkTargetTests { + public NetworkTargetTests() + { + LogManager.ThrowExceptions = true; + } + [Fact] public void HappyPathDefaultsTest() { @@ -77,7 +82,8 @@ private static void HappyPathTest(LineEndingMode lineEnding, params string[] mes target.Layout = "${message}"; target.LineEnding = lineEnding; target.KeepConnection = true; - target.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; var exceptions = new List(); var mre = new ManualResetEvent(false); @@ -107,7 +113,7 @@ private static void HappyPathTest(LineEndingMode lineEnding, params string[] mes Assert.Single(senderFactory.Senders); var sender = senderFactory.Senders[0]; - target.Close(); + logFactory.Shutdown(); // Get the length of all the messages and their line endings var eol = lineEnding?.NewLineCharacters ?? string.Empty; @@ -151,7 +157,8 @@ public void NetworkTargetMultipleConnectionsTest() target.SenderFactory = senderFactory; target.Layout = "${message}"; target.KeepConnection = true; - target.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; var exceptions = new List(); var mre = new ManualResetEvent(false); @@ -187,7 +194,7 @@ public void NetworkTargetMultipleConnectionsTest() target.Flush(flushContinuation); Assert.True(mre.WaitOne(10000), "Network Flush not completed"); - target.Close(); + logFactory.Shutdown(); var actual = senderFactory.Log.ToString(); Assert.Contains("1: connect tcp://logger1.company.lan/", actual); @@ -213,7 +220,8 @@ public void NothingToFlushTest() target.SenderFactory = senderFactory; target.Layout = "${message}"; target.KeepConnection = true; - target.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; var mre = new ManualResetEvent(false); @@ -224,7 +232,7 @@ public void NothingToFlushTest() target.Flush(flushContinuation); Assert.True(mre.WaitOne(10000), "Network Flush not completed"); - target.Close(); + logFactory.Shutdown(); string expectedLog = @""; Assert.Equal(expectedLog, senderFactory.Log.ToString()); @@ -240,7 +248,8 @@ public void NetworkTargetMultipleConnectionsWithCacheOverflowTest() target.Layout = "${message}"; target.KeepConnection = true; target.ConnectionCacheSize = 2; - target.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; var exceptions = new List(); var mre = new ManualResetEvent(false); @@ -271,7 +280,7 @@ public void NetworkTargetMultipleConnectionsWithCacheOverflowTest() Assert.True(ex == null, $"Network Write Exception: {ex?.ToString()}"); } - target.Close(); + logFactory.Shutdown(); string result = senderFactory.Log.ToString(); Assert.Contains("1: connect tcp://logger1.company.lan/", result); @@ -299,7 +308,8 @@ public void NetworkTargetMultipleConnectionsWithoutKeepAliveTest() target.SenderFactory = senderFactory; target.Layout = "${message}"; target.KeepConnection = false; - target.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; var exceptions = new List(); var mre = new ManualResetEvent(false); @@ -329,7 +339,7 @@ public void NetworkTargetMultipleConnectionsWithoutKeepAliveTest() Assert.True(ex == null, $"Network Write Exception: {ex?.ToString()}"); } - target.Close(); + logFactory.Shutdown(); string result = senderFactory.Log.ToString(); Assert.Contains("1: connect tcp://logger1.company.lan/", result); @@ -363,7 +373,8 @@ public void NetworkTargetMultipleConnectionsWithMessageDiscardTest() target.KeepConnection = true; target.MaxMessageSize = 10; target.OnOverflow = NetworkTargetOverflowAction.Discard; - target.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; int droppedLogs = 0; target.LogEventDropped += (sender, args) => droppedLogs++; @@ -393,7 +404,7 @@ public void NetworkTargetMultipleConnectionsWithMessageDiscardTest() Assert.True(ex == null, $"Network Write Exception: {ex?.ToString()}"); } - target.Close(); + logFactory.Shutdown(); string result = senderFactory.Log.ToString(); Assert.Contains("1: connect tcp://logger1.company.lan/", result); @@ -419,7 +430,8 @@ public void NetworkTargetMultipleConnectionsWithMessageErrorTest() target.KeepConnection = true; target.MaxMessageSize = 10; target.OnOverflow = NetworkTargetOverflowAction.Error; - target.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; int droppedLogs = 0; target.LogEventDropped += (sender, args) => droppedLogs++; @@ -449,7 +461,7 @@ public void NetworkTargetMultipleConnectionsWithMessageErrorTest() Assert.Equal("NetworkTarget: Discarded LogEvent because MessageSize=15 is above MaxMessageSize=10", exceptions[1].Message); Assert.Null(exceptions[2]); - target.Close(); + logFactory.Shutdown(); string result = senderFactory.Log.ToString(); Assert.Contains("1: connect tcp://logger1.company.lan/", result); @@ -492,7 +504,7 @@ public void NetworkTargetSendFailureTests() } }; - target.Initialize(null); + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; var exceptions = new List(); var mre = new ManualResetEvent(false); @@ -522,7 +534,7 @@ public void NetworkTargetSendFailureTests() Assert.Null(exceptions[3]); Assert.Null(exceptions[4]); - target.Close(); + logFactory.Shutdown(); var result = senderFactory.Log.ToString(); Assert.Contains("1: connect tcp://logger1.company.lan/", result); @@ -595,7 +607,8 @@ public void NetworkTargetTcpTest() } }, null); - target.Initialize(new LoggingConfiguration()); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; int pendingWrites = 100; var writeCompleted = new ManualResetEvent(false); @@ -624,7 +637,7 @@ public void NetworkTargetTcpTest() } Assert.True(writeCompleted.WaitOne(10000), "Network Writes did not complete"); - target.Close(); + logFactory.Shutdown(); foreach (var ex in exceptions) { Assert.True(ex == null, $"Network Write Exception: {ex?.ToString()}"); @@ -639,13 +652,35 @@ public void NetworkTargetTcpTest() [Fact] public void NetworkTargetUdpSplitEnabledTest() { - RetryingIntegrationTest(3, () => NetworkTargetUdpTest(true)); + for (int i = 1; i <= 3; ++i) + { + try + { + NetworkTargetUdpTest(true); + } + catch + { + if (i == 3) + throw; + } + } } [Fact] public void NetworkTargetUdpSplitDisabledTest() { - RetryingIntegrationTest(3, () => NetworkTargetUdpTest(false)); + for (int i = 1; i <= 3; ++i) + { + try + { + NetworkTargetUdpTest(false); + } + catch + { + if (i == 3) + throw; + } + } } private static int getNewNetworkPort() @@ -727,7 +762,7 @@ private static void NetworkTargetUdpTest(bool splitMessage) remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); listener.BeginReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, ref remoteEndPoint, receivedDatagram, null); - target.Initialize(new LoggingConfiguration()); + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; var writeCompleted = new ManualResetEvent(false); var exceptions = new List(); @@ -754,7 +789,7 @@ private static void NetworkTargetUdpTest(bool splitMessage) } Assert.True(writeCompleted.WaitOne(10000), "Network Write not completed"); - target.Close(); + logFactory.Shutdown(); foreach (var ex in exceptions) { Assert.True(ex == null, $"Network Write Exception: {ex?.ToString()}"); @@ -779,7 +814,7 @@ public void NetworkTargetNotConnectedTest() KeepConnection = true, }; - target.Initialize(new LoggingConfiguration()); + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; int toWrite = 10; int pendingWrites = toWrite; @@ -808,19 +843,20 @@ public void NetworkTargetNotConnectedTest() target.WriteAsyncLogEvent(ev); } }; - AsyncHelpers.StartAsyncTask(loggerTask, null); + System.Threading.ThreadPool.QueueUserWorkItem(loggerTask, null); Assert.True(writeCompleted.WaitOne(10000), "Network Write not completed"); var shutdownCompleted = new ManualResetEvent(false); WaitCallback closeTask = (state) => { // no exception - target.Close(); shutdownCompleted.Set(); }; - AsyncHelpers.StartAsyncTask(closeTask, null); + System.Threading.ThreadPool.QueueUserWorkItem(closeTask, null); Assert.True(shutdownCompleted.WaitOne(10000), "Network Close not completed"); + logFactory.Shutdown(); + Assert.Equal(toWrite, exceptions.Count); foreach (var ex in exceptions) { @@ -838,7 +874,8 @@ public void NetworkTargetQueueDiscardTest() target.SenderFactory = senderFactory; target.Layout = "${message}"; target.KeepConnection = true; - target.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; int discardEventCalls = 0; target.LogEventDropped += (sender, args) => discardEventCalls++; @@ -876,6 +913,9 @@ public void NetworkTargetQueueDiscardTest() target.WriteAsyncLogEvent(new LogEventInfo(LogLevel.Info, "logger", $"msg{i}").WithContinuation(asyncContinuation)); } Assert.True(mre.WaitOne(10000), "Network Write not completed"); + + logFactory.Shutdown(); + Assert.True(exceptions.Count >= 6, $"Network write not completed: {exceptions.Count}"); Assert.True(senderFactory.BeginRequestCounter <= 3, $"Network write not discarded: {senderFactory.BeginRequestCounter}"); @@ -898,7 +938,8 @@ public void NetworkTargetQueueGrowTest() target.SenderFactory = senderFactory; target.Layout = "${message}"; target.KeepConnection = true; - target.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; int pendingWrites = 1; var exceptions = new List(); @@ -933,6 +974,9 @@ public void NetworkTargetQueueGrowTest() } Assert.True(exceptions.Count < 4, $"Network write not growing: {exceptions.Count}"); Assert.True(mre.WaitOne(10000), "Network Write not completed"); + + logFactory.Shutdown(); + Assert.Equal(6, exceptions.Count); Assert.Equal(6, senderFactory.BeginRequestCounter); @@ -953,7 +997,8 @@ public void NetworkTargetQueueBlockTest() target.SenderFactory = senderFactory; target.Layout = "${message}"; target.KeepConnection = true; - target.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; int pendingWrites = 1; var exceptions = new List(); @@ -988,6 +1033,7 @@ public void NetworkTargetQueueBlockTest() } Assert.True(exceptions.Count >= 4, $"Network write not blocking: {exceptions.Count}"); Assert.True(mre.WaitOne(10000), "Network Write not completed"); + logFactory.Shutdown(); Assert.Equal(6, exceptions.Count); Assert.Equal(6, senderFactory.BeginRequestCounter); @@ -1012,7 +1058,8 @@ public void NetworkTargetSendFailureWithoutKeepAliveTests() target.Layout = "${message}"; target.KeepConnection = false; target.OnOverflow = NetworkTargetOverflowAction.Discard; - target.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; var exceptions = new List(); var mre = new ManualResetEvent(false); @@ -1042,7 +1089,7 @@ public void NetworkTargetSendFailureWithoutKeepAliveTests() Assert.Null(exceptions[3]); Assert.Null(exceptions[4]); - target.Close(); + logFactory.Shutdown(); var result = senderFactory.Log.ToString(); Assert.Contains("1: connect tcp://logger1.company.lan/", result); @@ -1079,7 +1126,8 @@ public void AsyncConnectionFailureOnCloseShouldNotDeadlock() target.SenderFactory = senderFactory; target.Layout = "${message}"; target.KeepConnection = true; - target.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; var exceptions = new List(); var writeCompleted = new ManualResetEvent(false); @@ -1101,10 +1149,10 @@ public void AsyncConnectionFailureOnCloseShouldNotDeadlock() { Thread.Sleep(10); target.Flush(ex => { }); - target.Close(); + logFactory.Shutdown(); shutdownCompleted.Set(); }; - AsyncHelpers.StartAsyncTask(shutdownTask, null); + System.Threading.ThreadPool.QueueUserWorkItem(shutdownTask, null); WaitCallback loggerTask = (state) => { @@ -1116,7 +1164,7 @@ public void AsyncConnectionFailureOnCloseShouldNotDeadlock() writeCompleted.Set(); }; - AsyncHelpers.StartAsyncTask(loggerTask, null); + System.Threading.ThreadPool.QueueUserWorkItem(loggerTask, null); Assert.True(writeCompleted.WaitOne(10000), "Network Write not completed"); Assert.True(shutdownCompleted.WaitOne(10000), "Network Shutdown not completed"); @@ -1129,14 +1177,17 @@ public void AsyncConnectionFailureOnCloseShouldNotDeadlock() [InlineData("tls,tls11", SslProtocols.Tls11 | SslProtocols.Tls)] public void SslProtocolsConfigTest(string sslOptions, SslProtocols expected) { - var config = XmlLoggingConfiguration.CreateFromXmlString($@" + var logFactory = new LogFactory().Setup().SetupExtensions(ext => ext.RegisterTarget()) + .LoadConfigurationFromXml($@" - "); + ").LogFactory; - var target = config.FindTargetByName("target1"); + var target = logFactory.Configuration.FindTargetByName("target1"); Assert.Equal(expected, target.SslProtocols); + + logFactory.Shutdown(); } [Theory] @@ -1144,7 +1195,8 @@ public void SslProtocolsConfigTest(string sslOptions, SslProtocols expected) [InlineData("30", 30)] public void KeepAliveTimeConfigTest(string keepAliveTimeSeconds, int expected) { - var logFactory = new LogFactory().Setup().LoadConfigurationFromXml($@" + var logFactory = new LogFactory().Setup().SetupExtensions(ext => ext.RegisterTarget()) + .LoadConfigurationFromXml($@" @@ -1162,7 +1214,8 @@ public void KeepAliveTimeConfigTest(string keepAliveTimeSeconds, int expected) [InlineData("30", 30)] public void SendTimeoutConfigTest(string sendTimeoutSeconds, int expected) { - var logFactory = new LogFactory().Setup().LoadConfigurationFromXml($@" + var logFactory = new LogFactory().Setup().SetupExtensions(ext => ext.RegisterTarget()) + .LoadConfigurationFromXml($@" @@ -1180,7 +1233,8 @@ public void Bug3990StackOverflowWhenUsingNLogViewerTarget() { // this would fail because of stack overflow in the // constructor of NLogViewerTarget - var config = XmlLoggingConfiguration.CreateFromXmlString(@" + var logFactory = new LogFactory().Setup().SetupExtensions(ext => ext.RegisterTarget("NLogViewer")) + .LoadConfigurationFromXml(@" @@ -1188,9 +1242,9 @@ public void Bug3990StackOverflowWhenUsingNLogViewerTarget() -"); +").LogFactory; - var target = config.LoggingRules[0].Targets[0] as NLogViewerTarget; + var target = logFactory.Configuration.LoggingRules[0].Targets[0] as ChainsawTarget; Assert.NotNull(target); } @@ -1206,7 +1260,8 @@ public void GzipCompressionTest() target.Compress = NetworkTargetCompressionType.GZip; target.CompressMinBytes = 15; target.LineEnding = LineEndingMode.CRLF; - target.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; var exceptions = new List(); var mre = new ManualResetEvent(false); @@ -1237,6 +1292,8 @@ public void GzipCompressionTest() var bigMessage = target.Encoding.GetString(outstream.GetBuffer(), 0, (int)outstream.Length); Assert.Equal("superbigmessage\r\n", bigMessage); } + + logFactory.Shutdown(); } internal sealed class MyQueudSenderFactory : INetworkSenderFactory @@ -1311,7 +1368,7 @@ protected override void BeginRequest(NetworkRequestArgs eventArgs) if (_senderFactory.AsyncMode) { WaitCallback asyncTask = (state) => SendSync(eventArgs); - AsyncHelpers.StartAsyncTask(asyncTask, null); + System.Threading.ThreadPool.QueueUserWorkItem(asyncTask, null); } else { diff --git a/tests/NLog.UnitTests/Targets/FileTargetTests.cs b/tests/NLog.UnitTests/Targets/FileTargetTests.cs index 513bd11605..50b16c2ada 100644 --- a/tests/NLog.UnitTests/Targets/FileTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/FileTargetTests.cs @@ -3618,7 +3618,7 @@ public void LoggingShouldNotTriggerTypeResolveEventTest() - + From 08358123f0cdce52c3ba79c7083b24c5cddd404b Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 6 Oct 2024 22:29:17 +0200 Subject: [PATCH 015/224] Updated dependabot.yml after removal of NETSTANDARD1 (#5637) --- .github/dependabot.yml | 47 +----------------------------------------- src/NLog.sln | 3 +-- 2 files changed, 2 insertions(+), 48 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 81e66e2bc0..6f6e4ecf9b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,49 +6,4 @@ updates: schedule: interval: daily open-pull-requests-limit: 10 - ignore: - - dependency-name: Microsoft.Extensions.PlatformAbstractions - versions: - - ">= 1.1.a, < 1.2" - - dependency-name: System.ComponentModel.Primitives - versions: - - ">= 4.3.a, < 4.4" - - dependency-name: System.ComponentModel.TypeConverter - versions: - - ">= 4.3.a, < 4.4" - - dependency-name: System.Data.Common - versions: - - ">= 4.3.a, < 4.4" - - dependency-name: System.Diagnostics.Process - versions: - - ">= 4.3.a, < 4.4" - - dependency-name: System.Diagnostics.StackTrace - versions: - - ">= 4.3.a, < 4.4" - - dependency-name: System.Diagnostics.TraceSource - versions: - - ">= 4.3.a, < 4.4" - - dependency-name: System.IO.FileSystem.Watcher - versions: - - ">= 4.3.a, < 4.4" - - dependency-name: System.Net.NameResolution - versions: - - ">= 4.3.a, < 4.4" - - dependency-name: System.Net.Requests - versions: - - ">= 4.3.a, < 4.4" - - dependency-name: System.Reflection.TypeExtensions - versions: - - ">= 4.3.a, < 4.7" - - dependency-name: System.Runtime.Loader - versions: - - ">= 4.3.a, < 4.4" - - dependency-name: System.Threading.Thread - versions: - - ">= 4.3.a, < 4.4" - - dependency-name: System.Xml.XmlDocument - versions: - - ">= 4.3.a, < 4.4" - - dependency-name: System.Xml.XmlSerializer - versions: - - ">= 4.3.a, < 4.4" \ No newline at end of file + \ No newline at end of file diff --git a/src/NLog.sln b/src/NLog.sln index e72392b315..a36450bcda 100644 --- a/src/NLog.sln +++ b/src/NLog.sln @@ -56,7 +56,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.WebService.Tes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.Network", "NLog.Targets.Network\NLog.Targets.Network.csproj", "{B048DD1F-4A24-453B-9C3D-F08280AE3FF3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.Network.Tests", "..\tests\NLog.Targets.Network.Tests\NLog.Targets.Network.Tests.csproj", "{38828090-4953-4EF5-929C-8063FD4BCCC9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.Network.Tests", "..\tests\NLog.Targets.Network.Tests\NLog.Targets.Network.Tests.csproj", "{38828090-4953-4EF5-929C-8063FD4BCCC9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -141,7 +141,6 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {A0BFF0DB-ED9A-4639-AE86-8E709A1EFC66} = {8CCD7CF7-F30B-414A-B7FE-1B49411E4EFD} {94F21E64-A259-4D79-AFCA-08974DF51701} = {8CCD7CF7-F30B-414A-B7FE-1B49411E4EFD} {B1DA63B3-1EB0-41B3-BFDA-9D7409481E27} = {8CCD7CF7-F30B-414A-B7FE-1B49411E4EFD} {99BCE294-CA56-41BD-AEBF-61795F5650AB} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} From cc2f92eda88e74fe0aedad93dbbdb250fb1a72ec Mon Sep 17 00:00:00 2001 From: Tobias Berger Date: Thu, 10 Oct 2024 17:13:58 +0200 Subject: [PATCH 016/224] Fix XML docs for ICreateFileParameters options (#5641) --- src/NLog/Internal/FileAppenders/ICreateFileParameters.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NLog/Internal/FileAppenders/ICreateFileParameters.cs b/src/NLog/Internal/FileAppenders/ICreateFileParameters.cs index 78fe71b254..d96a6825f8 100644 --- a/src/NLog/Internal/FileAppenders/ICreateFileParameters.cs +++ b/src/NLog/Internal/FileAppenders/ICreateFileParameters.cs @@ -41,13 +41,13 @@ namespace NLog.Internal.FileAppenders internal interface ICreateFileParameters { /// - /// Gets or sets the delay in milliseconds to wait before attempting to write to the file again. + /// Gets or sets the number of times the write is attempted on the file before NLog + /// discards the log message. /// int FileOpenRetryCount { get; } /// - /// Gets or sets the number of times the write is appended on the file before NLog - /// discards the log message. + /// Gets or sets the delay in milliseconds to wait before attempting to write to the file again. /// int FileOpenRetryDelay { get; } From f4839e5cdb41203ccf13547f9f759be0b7762bca Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 12 Oct 2024 12:06:26 +0200 Subject: [PATCH 017/224] Fixing Failed to convert the binary code coverage reports to XML in SonarQube (#5639) --- .../{ => Internal}/PlatformDetector.cs | 43 +++++++----------- .../{RuntimeOS.cs => Internal/PlatformOS.cs} | 17 ++----- .../{XmlHelper.cs => Internal/XmlHelpers.cs} | 45 ++++++++++++++++--- .../Log4JXmlEventLayoutRenderer.cs | 10 ++--- .../Log4JXmlEventParameter.cs | 4 +- .../NLog.Targets.Network.csproj | 4 +- .../NetworkSenders/IWebRequestFactory.cs | 6 +-- .../NetworkSenders/TcpNetworkSender.cs | 5 ++- src/NLog.Targets.Network/NetworkTarget.cs | 3 +- src/NLog.Targets.Network/README.md | 2 +- .../NLog.Targets.WebService.csproj | 2 +- src/NLog/Internal/PlatformDetector.cs | 24 +++------- .../NLog.Database.Tests.csproj | 2 +- .../NLog.Targets.Mail.Tests.csproj | 2 +- .../Log4JXmlTests.cs | 2 +- .../NLog.Targets.Network.Tests.csproj | 5 ++- .../NetworkSenders/HttpNetworkSenderTests.cs | 15 +++---- .../ManualDisposableMemoryStream.cs | 4 +- .../NetworkSenders/WebRequestFactoryMock.cs | 8 ++-- .../NetworkSenders/WebRequestMock.cs | 10 ++--- .../NetworkTargetTests.cs | 3 +- .../NLog.Targets.WebService.Tests.csproj | 6 ++- .../NLog.WindowsRegistry.Tests.csproj | 2 +- 23 files changed, 116 insertions(+), 108 deletions(-) rename src/NLog.Targets.Network/{ => Internal}/PlatformDetector.cs (66%) rename src/NLog.Targets.Network/{RuntimeOS.cs => Internal/PlatformOS.cs} (82%) rename src/NLog.Targets.Network/{XmlHelper.cs => Internal/XmlHelpers.cs} (78%) diff --git a/src/NLog.Targets.Network/PlatformDetector.cs b/src/NLog.Targets.Network/Internal/PlatformDetector.cs similarity index 66% rename from src/NLog.Targets.Network/PlatformDetector.cs rename to src/NLog.Targets.Network/Internal/PlatformDetector.cs index 16d430b7e1..f46b8a6590 100644 --- a/src/NLog.Targets.Network/PlatformDetector.cs +++ b/src/NLog.Targets.Network/Internal/PlatformDetector.cs @@ -31,8 +31,10 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.Internal +namespace NLog.Targets.Internal { + using System; + /// /// Detects the platform the NLog is running on. /// @@ -41,38 +43,27 @@ internal static class PlatformDetector /// /// Gets the current runtime OS. /// - public static RuntimeOS CurrentOS => _currentOS ?? (_currentOS = GetCurrentRuntimeOS()).Value; - private static RuntimeOS? _currentOS; + public static PlatformOS CurrentOS => _currentOS ?? (_currentOS = GetCurrentPlatformOS()).Value; + private static PlatformOS? _currentOS; + - private static RuntimeOS GetCurrentRuntimeOS() + private static PlatformOS GetCurrentPlatformOS() { #if NETFRAMEWORK - var platformID = System.Environment.OSVersion.Platform; + PlatformID platformID = Environment.OSVersion.Platform; + if (platformID == PlatformID.Win32NT || platformID == PlatformID.Win32Windows) + return PlatformOS.Windows; if ((int)platformID == 4 || (int)platformID == 128) - { - return RuntimeOS.Linux; - } - - if (platformID == System.PlatformID.Win32Windows) - { - return RuntimeOS.Windows9x; - } - - if (platformID == System.PlatformID.Win32NT) - { - return RuntimeOS.WindowsNT; - } - - return RuntimeOS.Unknown; + return PlatformOS.Linux; #else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) - return RuntimeOS.WindowsNT; - else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) - return RuntimeOS.MacOSX; - else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux)) - return RuntimeOS.Linux; - return RuntimeOS.Unknown; + return PlatformOS.Windows; + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) + return PlatformOS.MacOSX; + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux)) + return PlatformOS.Linux; #endif + return PlatformOS.Unknown; } } } diff --git a/src/NLog.Targets.Network/RuntimeOS.cs b/src/NLog.Targets.Network/Internal/PlatformOS.cs similarity index 82% rename from src/NLog.Targets.Network/RuntimeOS.cs rename to src/NLog.Targets.Network/Internal/PlatformOS.cs index 79da0a101d..1bfa073180 100644 --- a/src/NLog.Targets.Network/RuntimeOS.cs +++ b/src/NLog.Targets.Network/Internal/PlatformOS.cs @@ -31,16 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.Internal +namespace NLog.Targets.Internal { /// /// Supported operating systems. /// - /// - /// If you add anything here, make sure to add the appropriate detection - /// code to - /// - internal enum RuntimeOS + internal enum PlatformOS { /// /// Unknown operating system. @@ -53,14 +49,9 @@ internal enum RuntimeOS Linux, /// - /// Desktop versions of Windows (95,98,ME). + /// Windows operating systems. /// - Windows9x, - - /// - /// Windows NT, 2000, 2003 and future versions based on NT technology. - /// - WindowsNT, + Windows, /// /// Macintosh Mac OSX diff --git a/src/NLog.Targets.Network/XmlHelper.cs b/src/NLog.Targets.Network/Internal/XmlHelpers.cs similarity index 78% rename from src/NLog.Targets.Network/XmlHelper.cs rename to src/NLog.Targets.Network/Internal/XmlHelpers.cs index f8d4da7f00..3457e4f1cc 100644 --- a/src/NLog.Targets.Network/XmlHelper.cs +++ b/src/NLog.Targets.Network/Internal/XmlHelpers.cs @@ -1,11 +1,44 @@ -using System; -using System.Globalization; -using System.Text; -using System.Xml; +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// -namespace NLog.Internal +namespace NLog.Targets.Internal { - internal static class XmlHelper + using System; + using System.Globalization; + using System.Text; + using System.Xml; + + internal static class XmlHelpers { /// /// Safe version of WriteAttributeString diff --git a/src/NLog.Targets.Network/Log4JXmlEventLayoutRenderer.cs b/src/NLog.Targets.Network/Log4JXmlEventLayoutRenderer.cs index c02b270be2..31fd78a854 100644 --- a/src/NLog.Targets.Network/Log4JXmlEventLayoutRenderer.cs +++ b/src/NLog.Targets.Network/Log4JXmlEventLayoutRenderer.cs @@ -41,9 +41,9 @@ namespace NLog.LayoutRenderers using System.Xml; using NLog.Common; using NLog.Config; - using NLog.Internal; using NLog.Layouts; using NLog.Targets; + using NLog.Targets.Internal; /// /// XML event description compatible with log4j, Chainsaw and NLogViewer. @@ -350,11 +350,11 @@ private void AppendScopeContextProperties(XmlWriter xtw) { foreach (var scopeProperty in ScopeContext.GetAllProperties()) { - string propertyKey = XmlHelper.RemoveInvalidXmlChars(scopeProperty.Key); + string propertyKey = XmlHelpers.RemoveInvalidXmlChars(scopeProperty.Key); if (string.IsNullOrEmpty(propertyKey)) continue; - string propertyValue = XmlHelper.XmlConvertToStringSafe(scopeProperty.Value); + string propertyValue = XmlHelpers.XmlConvertToStringSafe(scopeProperty.Value); if (propertyValue is null) continue; @@ -422,11 +422,11 @@ private static void AppendDataProperties(string prefix, string propertiesNamespa { foreach (var contextProperty in logEvent.Properties) { - string propertyKey = XmlHelper.XmlConvertToStringSafe(contextProperty.Key); + string propertyKey = XmlHelpers.XmlConvertToStringSafe(contextProperty.Key); if (string.IsNullOrEmpty(propertyKey)) continue; - string propertyValue = XmlHelper.XmlConvertToStringSafe(contextProperty.Value); + string propertyValue = XmlHelpers.XmlConvertToStringSafe(contextProperty.Value); if (propertyValue is null) continue; diff --git a/src/NLog.Targets.Network/Log4JXmlEventParameter.cs b/src/NLog.Targets.Network/Log4JXmlEventParameter.cs index 55953d8d8c..7c902e47da 100644 --- a/src/NLog.Targets.Network/Log4JXmlEventParameter.cs +++ b/src/NLog.Targets.Network/Log4JXmlEventParameter.cs @@ -34,8 +34,8 @@ namespace NLog.Targets { using NLog.Config; - using NLog.Internal; using NLog.Layouts; + using NLog.Targets.Internal; /// /// Represents a parameter for the @@ -55,7 +55,7 @@ public Log4JXmlEventParameter() /// /// [RequiredParameter] - public string Name { get => _name; set => _name = XmlHelper.RemoveInvalidXmlChars(value); } + public string Name { get => _name; set => _name = XmlHelpers.RemoveInvalidXmlChars(value); } private string _name; /// diff --git a/src/NLog.Targets.Network/NLog.Targets.Network.csproj b/src/NLog.Targets.Network/NLog.Targets.Network.csproj index a80118ef9e..652c0b977e 100644 --- a/src/NLog.Targets.Network/NLog.Targets.Network.csproj +++ b/src/NLog.Targets.Network/NLog.Targets.Network.csproj @@ -5,7 +5,7 @@ NLog.Targets.Network NLog - NetworkTarget can send messages using TCP / UDP + NetworkTarget for sending messages using TCP / UDP sockets with support for SSL / TSL NLog.Targets.Network v$(ProductVersion) $(ProductVersion) Jarek Kowalski,Kim Christensen,Julian Verdurmen @@ -13,7 +13,7 @@ Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ - WebServiceTarget Docs: + NetworkTarget Docs: https://github.com/NLog/NLog/wiki/Network-target README.md diff --git a/src/NLog.Targets.Network/NetworkSenders/IWebRequestFactory.cs b/src/NLog.Targets.Network/NetworkSenders/IWebRequestFactory.cs index f6b8154ccb..69731b3a5f 100644 --- a/src/NLog.Targets.Network/NetworkSenders/IWebRequestFactory.cs +++ b/src/NLog.Targets.Network/NetworkSenders/IWebRequestFactory.cs @@ -31,11 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Net; - namespace NLog.Internal.NetworkSenders { + using System; + using System.Net; + internal interface IWebRequestFactory { WebRequest CreateWebRequest(Uri address); diff --git a/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs index 03c7e53244..ba1fb83e6c 100644 --- a/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs @@ -37,6 +37,7 @@ namespace NLog.Internal.NetworkSenders using System.IO; using System.Net.Sockets; using NLog.Common; + using NLog.Targets.Internal; /// /// Sends messages over a TCP network connection. @@ -110,7 +111,7 @@ private static bool TryEnableKeepAlive(Socket underlyingSocket, int keepAliveTim SocketOptionName TcpKeepAliveTime = (SocketOptionName)0x3; SocketOptionName TcpKeepAliveInterval = (SocketOptionName)0x11; - if (PlatformDetector.CurrentOS == RuntimeOS.Linux) + if (PlatformDetector.CurrentOS == PlatformOS.Linux) { // https://github.com/torvalds/linux/blob/v4.16/include/net/tcp.h // #define TCP_KEEPIDLE 4 /* Start keepalives after this period */ @@ -118,7 +119,7 @@ private static bool TryEnableKeepAlive(Socket underlyingSocket, int keepAliveTim TcpKeepAliveTime = (SocketOptionName)0x4; TcpKeepAliveInterval = (SocketOptionName)0x5; } - else if (PlatformDetector.CurrentOS == RuntimeOS.MacOSX) + else if (PlatformDetector.CurrentOS == PlatformOS.MacOSX) { // https://opensource.apple.com/source/xnu/xnu-4570.41.2/bsd/netinet/tcp.h.auto.html // #define TCP_KEEPALIVE 0x10 /* idle time used when SO_KEEPALIVE is enabled */ diff --git a/src/NLog.Targets.Network/NetworkTarget.cs b/src/NLog.Targets.Network/NetworkTarget.cs index c64d2c2ec9..6387ae656c 100644 --- a/src/NLog.Targets.Network/NetworkTarget.cs +++ b/src/NLog.Targets.Network/NetworkTarget.cs @@ -38,9 +38,8 @@ namespace NLog.Targets using System.Text; using System.Threading; using NLog.Common; - using NLog.Internal; - using NLog.Internal.NetworkSenders; using NLog.Layouts; + using NLog.Internal.NetworkSenders; /// /// Sends log messages over the network. diff --git a/src/NLog.Targets.Network/README.md b/src/NLog.Targets.Network/README.md index 0067d73adf..9408943585 100644 --- a/src/NLog.Targets.Network/README.md +++ b/src/NLog.Targets.Network/README.md @@ -1,6 +1,6 @@ # NLog Network Target -NLog Network Target for sending mesages using TCP / UDP with support for SSL / TSL. +NLog Network Target for sending mesages using TCP / UDP sockets with support for SSL / TSL. If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) diff --git a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj index d5d31deae6..bcc81bf0ef 100644 --- a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj +++ b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj @@ -5,7 +5,7 @@ NLog.Targets.WebService NLog - WebServiceTarget for calling HTTP / SOAP / REST web-service + WebServiceTarget for calling HTTP / SOAP / REST web-service methods NLog.Targets.WebService v$(ProductVersion) $(ProductVersion) Jarek Kowalski,Kim Christensen,Julian Verdurmen diff --git a/src/NLog/Internal/PlatformDetector.cs b/src/NLog/Internal/PlatformDetector.cs index 5888acd264..f8b11c2ea6 100644 --- a/src/NLog/Internal/PlatformDetector.cs +++ b/src/NLog/Internal/PlatformDetector.cs @@ -68,31 +68,21 @@ private static RuntimeOS GetCurrentRuntimeOS() { #if NETFRAMEWORK PlatformID platformID = Environment.OSVersion.Platform; - if ((int)platformID == 4 || (int)platformID == 128) - { - return RuntimeOS.Linux; - } - - if (platformID == PlatformID.Win32Windows) - { - return RuntimeOS.Windows9x; - } - if (platformID == PlatformID.Win32NT) - { return RuntimeOS.WindowsNT; - } - - return RuntimeOS.Unknown; + if (platformID == PlatformID.Win32Windows) + return RuntimeOS.Windows9x; + if ((int)platformID == 4 || (int)platformID == 128) + return RuntimeOS.Linux; #else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) return RuntimeOS.WindowsNT; - else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) return RuntimeOS.MacOSX; - else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux)) + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux)) return RuntimeOS.Linux; - return RuntimeOS.Unknown; #endif + return RuntimeOS.Unknown; } } } diff --git a/tests/NLog.Database.Tests/NLog.Database.Tests.csproj b/tests/NLog.Database.Tests/NLog.Database.Tests.csproj index 6e558496b7..e4ffe29be2 100644 --- a/tests/NLog.Database.Tests/NLog.Database.Tests.csproj +++ b/tests/NLog.Database.Tests/NLog.Database.Tests.csproj @@ -25,7 +25,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj b/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj index 3e736c1197..f5213ce32f 100644 --- a/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj +++ b/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj @@ -17,7 +17,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs index 19ac765a0c..56ecf6b4cb 100644 --- a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs +++ b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs @@ -38,10 +38,10 @@ namespace NLog.Targets.Network using System.IO; using System.Reflection; using System.Xml; - using NLog.Internal; using NLog.LayoutRenderers; using NLog.Layouts; using NLog.Targets; + using NLog.Targets.Internal; using Xunit; public class Log4JXmlTests diff --git a/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj b/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj index 47f2c1a26c..fbb57dda5d 100644 --- a/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj +++ b/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj @@ -4,21 +4,24 @@ 17.0 net462 net462;net6.0 + false NLogTests.snk false true true + + Full + all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs b/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs index 35d16e5dbd..55b390804c 100644 --- a/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs @@ -31,16 +31,15 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Security.Authentication; -using NLog.Config; -using NLog.Internal.NetworkSenders; -using NLog.Targets; -using NSubstitute; -using Xunit; - namespace NLog.Targets.Network { + using System; + using System.Security.Authentication; + using NLog.Config; + using NLog.Internal.NetworkSenders; + using NSubstitute; + using Xunit; + public class HttpNetworkSenderTests { public HttpNetworkSenderTests() diff --git a/tests/NLog.Targets.Network.Tests/NetworkSenders/ManualDisposableMemoryStream.cs b/tests/NLog.Targets.Network.Tests/NetworkSenders/ManualDisposableMemoryStream.cs index 371db62ca9..2129f3f815 100644 --- a/tests/NLog.Targets.Network.Tests/NetworkSenders/ManualDisposableMemoryStream.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkSenders/ManualDisposableMemoryStream.cs @@ -31,10 +31,10 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System.IO; - namespace NLog.Targets.Network { + using System.IO; + /// /// Memorystream that doesn't dispose by default for asserting the content /// diff --git a/tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestFactoryMock.cs b/tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestFactoryMock.cs index 0238ccbbd3..9f01b6a08c 100644 --- a/tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestFactoryMock.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestFactoryMock.cs @@ -31,12 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Net; -using NLog.Internal.NetworkSenders; - namespace NLog.Targets.Network { + using System; + using System.Net; + using NLog.Internal.NetworkSenders; + [Obsolete("WebRequest is obsolete. Use HttpClient instead.")] class WebRequestFactoryMock : IWebRequestFactory { diff --git a/tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestMock.cs b/tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestMock.cs index 66bd5eccbd..5d89419463 100644 --- a/tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestMock.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkSenders/WebRequestMock.cs @@ -31,13 +31,13 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.IO; -using System.Net; -using NSubstitute; - namespace NLog.Targets.Network { + using System; + using System.IO; + using System.Net; + using NSubstitute; + [Obsolete("WebRequest is obsolete. Use HttpClient instead.")] public sealed class WebRequestMock : WebRequest, IDisposable { diff --git a/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs index e0dc7715c7..7e929e3d05 100644 --- a/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs @@ -43,9 +43,8 @@ namespace NLog.Targets.Network using System.Text; using System.Threading; using NLog.Common; - using NLog.Config; - using NLog.Internal.NetworkSenders; using NLog.Targets; + using NLog.Internal.NetworkSenders; using Xunit; public class NetworkTargetTests diff --git a/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj b/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj index 6c4e86caea..48c91630ea 100644 --- a/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj +++ b/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj @@ -4,21 +4,24 @@ 17.0 net462 net462;net6.0 + false NLogTests.snk false true true + + Full + all runtime; build; native; contentfiles; analyzers; buildtransitive - @@ -29,5 +32,4 @@ - diff --git a/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj b/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj index b653366155..977a22aa79 100644 --- a/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj +++ b/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj @@ -17,7 +17,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 96c45c765ca6aa0640e193eae502677f3b5154c5 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 12 Oct 2024 13:31:23 +0200 Subject: [PATCH 018/224] Log4JXmlTests more stable by extension registration upfront (#5643) --- src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs | 1 + tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs b/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs index 9795de0fa3..a6668271e3 100644 --- a/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs @@ -36,6 +36,7 @@ namespace NLog.Targets.Wrappers using System; using System.Threading; using NLog.Common; + using NLog.Internal; using NLog.Layouts; /// diff --git a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs index 56ecf6b4cb..3fcbb96d90 100644 --- a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs +++ b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs @@ -49,13 +49,17 @@ public class Log4JXmlTests public Log4JXmlTests() { LogManager.ThrowExceptions = true; + LogManager.Setup().SetupExtensions(ext => + { + ext.RegisterLayoutRenderer(); + ext.RegisterLayout(); + }); } [Fact] public void Log4JXmlTest() { var logFactory = new LogFactory().Setup() - .SetupExtensions(ext => ext.RegisterLayoutRenderer()) .LoadConfigurationFromXml(@" From af573157fdbd500409353d6f0a5f3261074cdc81 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 12 Oct 2024 14:26:33 +0200 Subject: [PATCH 019/224] MultiplePatternLoggerNameMatcher - Replaced RegEx with basic compare (#5644) --- src/NLog/Config/LoggerNameMatcher.cs | 82 +++++++++++++++---- tests/NLog.UnitTests/Config/ConfigApiTests.cs | 13 ++- 2 files changed, 77 insertions(+), 18 deletions(-) diff --git a/src/NLog/Config/LoggerNameMatcher.cs b/src/NLog/Config/LoggerNameMatcher.cs index ee7bebb8f0..be9d0f1cba 100644 --- a/src/NLog/Config/LoggerNameMatcher.cs +++ b/src/NLog/Config/LoggerNameMatcher.cs @@ -34,7 +34,7 @@ namespace NLog.Config { using System; - using System.Text.RegularExpressions; + using NLog.Internal; /// /// Encapsulates and the logic to match the actual logger name @@ -233,25 +233,77 @@ public override bool NameMatches(string loggerName) private sealed class MultiplePatternLoggerNameMatcher : LoggerNameMatcher { protected override string MatchMode => "MultiplePattern"; - private readonly Regex _regex; - private static string ConvertToRegex(string wildcardsPattern) - { - return - '^' + - Regex.Escape(wildcardsPattern) - .Replace("\\*", ".*") - .Replace("\\?", ".") - + '$'; - } + private readonly string _wildCardPattern; + public MultiplePatternLoggerNameMatcher(string pattern) - : base(pattern, ConvertToRegex(pattern)) + : base(pattern, pattern) { - _regex = new Regex(_matchingArgument, RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture); + _wildCardPattern = Guard.ThrowIfNull(pattern); } + public override bool NameMatches(string loggerName) { - if (loggerName is null) return false; - return _regex.IsMatch(loggerName); + return MatchingName(0, _wildCardPattern.Length, loggerName, 0, loggerName?.Length ?? 0); + } + + bool MatchingName(int wildcardStart, int wildcardEnd, string loggerName, int loggerNameStart, int loggerNameEnd) + { + for (int i = wildcardStart; i < wildcardEnd; ++i) + { + char matchToken = _wildCardPattern[i]; + if (matchToken == '*') + break; + + if (loggerNameStart >= loggerNameEnd) + return false; + + char inputToken = loggerName[loggerNameStart]; + if (matchToken != '?' && matchToken != inputToken) + return false; + + ++loggerNameStart; + ++wildcardStart; + } + + for (int i = wildcardEnd - 1; i >= wildcardStart; --i) + { + char matchToken = _wildCardPattern[i]; + if (matchToken == '*') + break; + + if (loggerNameStart >= loggerNameEnd) + return false; + + char inputToken = loggerName[loggerNameEnd - 1]; + if (matchToken != '?' && matchToken != inputToken) + return false; + + --loggerNameEnd; + --wildcardEnd; + } + + var nextMatch = '*'; + for (int i = wildcardStart; i < wildcardEnd; ++i) + { + nextMatch = _wildCardPattern[i]; + if (nextMatch != '*') + break; + ++wildcardStart; + } + if (nextMatch == '*') + return true; + + for (int i = loggerNameStart; i < loggerNameEnd; ++i) + { + char inputToken = loggerName[i]; + if (nextMatch == '?' || inputToken == nextMatch) + { + // Found match, now scan the remaining sub-string + return MatchingName(wildcardStart, wildcardEnd, loggerName, i, loggerNameEnd); + } + } + + return false; } } } diff --git a/tests/NLog.UnitTests/Config/ConfigApiTests.cs b/tests/NLog.UnitTests/Config/ConfigApiTests.cs index 21fad9420c..8e5aee7360 100644 --- a/tests/NLog.UnitTests/Config/ConfigApiTests.cs +++ b/tests/NLog.UnitTests/Config/ConfigApiTests.cs @@ -432,21 +432,21 @@ public void LoggerNameMatcher_Contains() public void LoggerNameMatcher_MultiplePattern_StarInternal() { var matcher = LoggerNameMatcher.Create("a*bc"); - Assert.Equal("logNamePattern: (^a.*bc$:MultiplePattern)", matcher.ToString()); + Assert.Equal("logNamePattern: (a*bc:MultiplePattern)", matcher.ToString()); } [Fact] public void LoggerNameMatcher_MultiplePattern_QuestionMark() { var matcher = LoggerNameMatcher.Create("a?bc"); - Assert.Equal("logNamePattern: (^a.bc$:MultiplePattern)", matcher.ToString()); + Assert.Equal("logNamePattern: (a?bc:MultiplePattern)", matcher.ToString()); } [Fact] public void LoggerNameMatcher_MultiplePattern_EscapedChars() { var matcher = LoggerNameMatcher.Create("a?b.c.foo.bar"); - Assert.Equal("logNamePattern: (^a.b\\.c\\.foo\\.bar$:MultiplePattern)", matcher.ToString()); + Assert.Equal("logNamePattern: (a?b.c.foo.bar:MultiplePattern)", matcher.ToString()); } [Theory] @@ -527,6 +527,13 @@ public void LoggerNameMatcher_Matches_MultiplePattern(string name, bool result) [InlineData("MultiplePattern", "server[*].connection[??].*", "server[].connection[2].reader", false)] [InlineData("MultiplePattern", "server[*].connection[??].*", "server[].connection[25].reader", true)] [InlineData("MultiplePattern", "server[*].connection[??].*", "server[].connection[254].reader", false)] + [InlineData("MultiplePattern", "*???*", "abcd", true)] + [InlineData("MultiplePattern", "*???*", "abc", true)] + [InlineData("MultiplePattern", "*???*", "ab", false)] + [InlineData("MultiplePattern", "?*???*", "abcd", true)] + [InlineData("MultiplePattern", "?*???*", "abc", false)] + [InlineData("MultiplePattern", "*???*?", "abcd", true)] + [InlineData("MultiplePattern", "*???*?", "abc", false)] public void LoggerNameMatcher_Matches(string matcherType, string pattern, string name, bool result) { var matcher = LoggerNameMatcher.Create(pattern); From 0ed89e8ffe2ce941d8f096c6f00acf5bc419b329 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 12 Oct 2024 15:49:45 +0200 Subject: [PATCH 020/224] Improve test-coverage of source-code (#5645) --- .../Internal/PlatformDetector.cs | 12 ++++++------ .../NetworkSenders/HttpNetworkSender.cs | 4 ++-- .../NetworkSenders/QueuedNetworkSender.cs | 2 +- src/NLog/Internal/PlatformDetector.cs | 16 ++++++++-------- src/NLog/Internal/ReflectionHelpers.cs | 19 ------------------- 5 files changed, 17 insertions(+), 36 deletions(-) diff --git a/src/NLog.Targets.Network/Internal/PlatformDetector.cs b/src/NLog.Targets.Network/Internal/PlatformDetector.cs index f46b8a6590..20b7de4c53 100644 --- a/src/NLog.Targets.Network/Internal/PlatformDetector.cs +++ b/src/NLog.Targets.Network/Internal/PlatformDetector.cs @@ -51,17 +51,17 @@ private static PlatformOS GetCurrentPlatformOS() { #if NETFRAMEWORK PlatformID platformID = Environment.OSVersion.Platform; - if (platformID == PlatformID.Win32NT || platformID == PlatformID.Win32Windows) - return PlatformOS.Windows; if ((int)platformID == 4 || (int)platformID == 128) return PlatformOS.Linux; -#else - if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) + if (platformID == PlatformID.Win32NT || platformID == PlatformID.Win32Windows) return PlatformOS.Windows; - if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) - return PlatformOS.MacOSX; +#else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux)) return PlatformOS.Linux; + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) + return PlatformOS.MacOSX; + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) + return PlatformOS.Windows; #endif return PlatformOS.Unknown; } diff --git a/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs index 2a9446b864..ea1d616a8a 100644 --- a/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs @@ -86,7 +86,7 @@ protected override void BeginRequest(NetworkRequestArgs eventArgs) catch (Exception ex) { #if DEBUG - if (ex.MustBeRethrownImmediately()) + if (LogManager.ThrowExceptions) { throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) } @@ -111,7 +111,7 @@ protected override void BeginRequest(NetworkRequestArgs eventArgs) catch (Exception ex) { #if DEBUG - if (ex.MustBeRethrownImmediately()) + if (LogManager.ThrowExceptions) { throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) } diff --git a/src/NLog.Targets.Network/NetworkSenders/QueuedNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/QueuedNetworkSender.cs index 3f797fa8bb..c6f9c1866d 100644 --- a/src/NLog.Targets.Network/NetworkSenders/QueuedNetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/QueuedNetworkSender.cs @@ -213,7 +213,7 @@ protected void BeginInitialize() catch (Exception ex) { #if DEBUG - if (ex.MustBeRethrownImmediately()) + if (LogManager.ThrowExceptions) { throw; // Throwing exceptions here will crash the entire application } diff --git a/src/NLog/Internal/PlatformDetector.cs b/src/NLog/Internal/PlatformDetector.cs index f8b11c2ea6..61c5a85d35 100644 --- a/src/NLog/Internal/PlatformDetector.cs +++ b/src/NLog/Internal/PlatformDetector.cs @@ -68,19 +68,19 @@ private static RuntimeOS GetCurrentRuntimeOS() { #if NETFRAMEWORK PlatformID platformID = Environment.OSVersion.Platform; - if (platformID == PlatformID.Win32NT) - return RuntimeOS.WindowsNT; - if (platformID == PlatformID.Win32Windows) - return RuntimeOS.Windows9x; if ((int)platformID == 4 || (int)platformID == 128) return RuntimeOS.Linux; -#else - if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) + if (platformID == PlatformID.Win32Windows) + return RuntimeOS.Windows9x; + if (platformID == PlatformID.Win32NT) return RuntimeOS.WindowsNT; - if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) - return RuntimeOS.MacOSX; +#else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux)) return RuntimeOS.Linux; + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) + return RuntimeOS.MacOSX; + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) + return RuntimeOS.WindowsNT; #endif return RuntimeOS.Unknown; } diff --git a/src/NLog/Internal/ReflectionHelpers.cs b/src/NLog/Internal/ReflectionHelpers.cs index 8ef4f44125..420fbf70b1 100644 --- a/src/NLog/Internal/ReflectionHelpers.cs +++ b/src/NLog/Internal/ReflectionHelpers.cs @@ -107,25 +107,6 @@ public static LateBoundMethod CreateLateBoundMethod(MethodInfo methodInfo) } } - /// - /// Creates an optimized delegate for calling the constructors using Expression-Trees - /// - /// Constructor to optimize - /// Optimized delegate for invoking the constructor - public static LateBoundConstructor CreateLateBoundConstructor(ConstructorInfo constructor) - { - var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); - - // build parameter list - var parameterExpressions = BuildParameterList(constructor, parametersParameter); - - var ctorCall = Expression.New(constructor, parameterExpressions); - - var lambda = Expression.Lambda(ctorCall, parametersParameter); - - return lambda.Compile(); - } - private static IEnumerable BuildParameterList(MethodBase methodInfo, ParameterExpression parametersParameter) { var parameterExpressions = new List(); From 0cfd0a03ab09826b4abf2fbbfe4023d575d65622 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 13 Oct 2024 15:25:42 +0200 Subject: [PATCH 021/224] ColoredConsoleTarget without direct RegEx support for word highlight (#5646) --- build.ps1 | 1 + run-tests.ps1 | 8 + .../Conditions/RegexConditionMethods.cs | 43 ++++ .../Internal/RegexHelper.cs | 10 +- .../RegexReplaceLayoutRendererWrapper.cs | 175 ++++++++++++++++ src/NLog.RegEx/NLog.RegEx.csproj | 80 ++++++++ src/NLog.RegEx/README.md | 25 +++ .../ConsoleWordHighlightingRuleRegex.cs | 95 +++++++++ .../WebServiceTarget.cs | 4 +- src/NLog.sln | 14 ++ src/NLog/Conditions/ConditionMethods.cs | 30 --- src/NLog/Config/AssemblyExtensionTypes.cs | 2 - src/NLog/Config/AssemblyExtensionTypes.tt | 2 - src/NLog/Internal/StringHelpers.cs | 42 ++-- .../Wrappers/ReplaceLayoutRendererWrapper.cs | 118 +---------- src/NLog/Targets/ColoredConsoleTarget.cs | 18 +- .../Targets/ConsoleWordHighlightingRule.cs | 91 ++++----- .../ColoredConsoleTargetTests.cs | 190 ++++++++++++++++++ .../ConditionEvaluatorTests.cs | 85 ++++++++ .../NLog.RegEx.Tests/NLog.RegEx.Tests.csproj | 26 +++ tests/NLog.RegEx.Tests/RegexReplaceTests.cs | 181 +++++++++++++++++ .../Conditions/ConditionEvaluatorTests.cs | 24 +-- .../LayoutRenderers/Wrappers/ReplaceTests.cs | 131 +----------- .../Layouts/SimpleLayoutParserTests.cs | 6 +- .../Targets/ColoredConsoleTargetTests.cs | 69 +------ 25 files changed, 1034 insertions(+), 436 deletions(-) create mode 100644 src/NLog.RegEx/Conditions/RegexConditionMethods.cs rename src/{NLog => NLog.RegEx}/Internal/RegexHelper.cs (92%) create mode 100644 src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs create mode 100644 src/NLog.RegEx/NLog.RegEx.csproj create mode 100644 src/NLog.RegEx/README.md create mode 100644 src/NLog.RegEx/Targets/ConsoleWordHighlightingRuleRegex.cs create mode 100644 tests/NLog.RegEx.Tests/ColoredConsoleTargetTests.cs create mode 100644 tests/NLog.RegEx.Tests/ConditionEvaluatorTests.cs create mode 100644 tests/NLog.RegEx.Tests/NLog.RegEx.Tests.csproj create mode 100644 tests/NLog.RegEx.Tests/RegexReplaceTests.cs diff --git a/build.ps1 b/build.ps1 index f9bec331ca..64b4438095 100644 --- a/build.ps1 +++ b/build.ps1 @@ -40,6 +40,7 @@ create-package 'NLog.Targets.Mail' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.Network' '"net45;net46;netstandard2.0"' create-package 'NLog.Targets.WebService' '"net45;net46;netstandard2.0"' create-package 'NLog.OutputDebugString' '"net35;net45;net46;netstandard2.0"' +create-package 'NLog.RegEx' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.WindowsRegistry' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.WindowsEventLog' '"netstandard2.0"' diff --git a/run-tests.ps1 b/run-tests.ps1 index e4472cb8c0..6bd9ef0d21 100644 --- a/run-tests.ps1 +++ b/run-tests.ps1 @@ -24,6 +24,10 @@ if ($isWindows -or $Env:WinDir) if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.RegEx.Tests/ --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.Mail.Tests/ --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } @@ -67,6 +71,10 @@ else if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.RegEx.Tests/ --framework net6.0 --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.Mail.Tests/ --framework net6.0 --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } diff --git a/src/NLog.RegEx/Conditions/RegexConditionMethods.cs b/src/NLog.RegEx/Conditions/RegexConditionMethods.cs new file mode 100644 index 0000000000..e212c96e7e --- /dev/null +++ b/src/NLog.RegEx/Conditions/RegexConditionMethods.cs @@ -0,0 +1,43 @@ +using System; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace NLog.Conditions +{ + /// + /// A bunch of utility methods (mostly predicates) which can be used in + /// condition expressions. Partially inspired by XPath 1.0. + /// + [ConditionMethods] + public static class RegexConditionMethods + { + /// + /// Indicates whether the specified regular expression finds a match in the specified input string. + /// + /// The string to search for a match. + /// The regular expression pattern to match. + /// A string consisting of the desired options for the test. The possible values are those of the separated by commas. + /// true if the regular expression finds a match; otherwise, false. + [ConditionMethod("regex-matches")] + public static bool RegexMatches(string input, string pattern, [Optional, DefaultParameterValue("")] string options) + { + RegexOptions regexOpts = ParseRegexOptions(options) | RegexOptions.ExplicitCapture; + return Regex.IsMatch(input, pattern, regexOpts); + } + + /// + /// + /// + /// + /// + private static RegexOptions ParseRegexOptions(string options) + { + if (string.IsNullOrEmpty(options)) + { + return RegexOptions.None; + } + + return (RegexOptions)Enum.Parse(typeof(RegexOptions), options, true); + } + } +} diff --git a/src/NLog/Internal/RegexHelper.cs b/src/NLog.RegEx/Internal/RegexHelper.cs similarity index 92% rename from src/NLog/Internal/RegexHelper.cs rename to src/NLog.RegEx/Internal/RegexHelper.cs index cccce4b50f..a7fae2a717 100644 --- a/src/NLog/Internal/RegexHelper.cs +++ b/src/NLog.RegEx/Internal/RegexHelper.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.Internal +namespace NLog.RegEx.Internal { using System; using System.Text.RegularExpressions; @@ -43,7 +43,6 @@ internal sealed class RegexHelper private string _regexPattern; private bool _wholeWords; private bool _ignoreCase; - private bool _simpleSearchText; public string SearchText { @@ -121,7 +120,6 @@ public Regex Regex private void ResetRegex() { - _simpleSearchText = !WholeWords && !CompileRegex && !string.IsNullOrEmpty(SearchText); if (!string.IsNullOrEmpty(SearchText)) { _regexPattern = Regex.Escape(SearchText); @@ -151,11 +149,7 @@ private RegexOptions GetRegexOptions() public string Replace(string input, string replacement) { - if (_simpleSearchText) - { - return IgnoreCase ? StringHelpers.Replace(input, SearchText, replacement, StringComparison.CurrentCultureIgnoreCase) : input.Replace(SearchText, replacement); - } - else if (CompileRegex) + if (CompileRegex) { return Regex?.Replace(input, replacement) ?? input; } diff --git a/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs b/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs new file mode 100644 index 0000000000..dacef07b04 --- /dev/null +++ b/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs @@ -0,0 +1,175 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.LayoutRenderers.Wrappers +{ + using System; + using System.Linq; + using System.Text; + using System.Text.RegularExpressions; + using NLog.Common; + using NLog.Config; + using NLog.RegEx.Internal; + + /// + /// Replaces a string in the output of another layout with another string. + /// + /// + /// ${replace:searchFor=\\n+:replaceWith=-:regex=true:inner=${message}} + /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki + [LayoutRenderer("regex-replace")] + [AppDomainFixedOutput] + [ThreadAgnostic] + public sealed class RegexReplaceLayoutRendererWrapper : WrapperLayoutRendererBase + { + private RegexHelper _regexHelper; + private MatchEvaluator _groupMatchEvaluator; + + /// + /// Gets or sets the text to search for. + /// + /// The text search for. + /// + [RequiredParameter] + public string SearchFor { get; set; } + + /// + /// Gets or sets a value indicating whether regular expressions should be used. + /// + /// A value of true if regular expressions should be used otherwise, false. + /// + public bool Regex { get; set; } + + /// + /// Gets or sets the replacement string. + /// + /// The replacement string. + /// + public string ReplaceWith { get; set; } = string.Empty; + + /// + /// Gets or sets the group name to replace when using regular expressions. + /// Leave null or empty to replace without using group name. + /// + /// The group name. + /// + public string ReplaceGroupName { get; set; } + + /// + /// Gets or sets a value indicating whether to ignore case. + /// + /// A value of true if case should be ignored when searching; otherwise, false. + /// + public bool IgnoreCase { get; set; } + + /// + /// Gets or sets a value indicating whether to search for whole words. + /// + /// A value of true if whole words should be searched for; otherwise, false. + /// + public bool WholeWords { get; set; } + + /// + /// Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. + /// + /// + public bool CompileRegex { get; set; } + + /// + protected override void InitializeLayoutRenderer() + { + base.InitializeLayoutRenderer(); + + _regexHelper = new RegexHelper() + { + IgnoreCase = IgnoreCase, + WholeWords = WholeWords, + CompileRegex = CompileRegex, + }; + if (Regex) + _regexHelper.RegexPattern = SearchFor; + else + _regexHelper.SearchText = SearchFor; + + if (!string.IsNullOrEmpty(ReplaceGroupName) && _regexHelper.Regex?.GetGroupNames()?.Contains(ReplaceGroupName) == false) + { + InternalLogger.Warn("RegEx-Replace-LayoutRenderer assigned unknown ReplaceGroupName: {0}", ReplaceGroupName); + } + } + + /// + protected override string Transform(string text) + { + if (string.IsNullOrEmpty(ReplaceGroupName)) + { + return _regexHelper.Replace(text, ReplaceWith); + } + else + { + if (_groupMatchEvaluator is null) + _groupMatchEvaluator = m => ReplaceNamedGroup(ReplaceGroupName, ReplaceWith, m); + return _regexHelper.Regex?.Replace(text, _groupMatchEvaluator) ?? text; + } + } + + /// + /// A match evaluator for Regular Expression based replacing + /// + /// Group name in the regex. + /// Replace value. + /// Match from regex. + /// Groups replaced with . + private static string ReplaceNamedGroup(string groupName, string replacement, Match match) + { + var sb = new StringBuilder(match.Value); + var matchLength = match.Length; + + var captures = match.Groups[groupName].Captures.OfType().OrderByDescending(c => c.Index); + foreach (var capt in captures) + { + matchLength += replacement.Length - capt.Length; + + sb.Remove(capt.Index - match.Index, capt.Length); + sb.Insert(capt.Index - match.Index, replacement); + } + + var end = matchLength; + sb.Remove(end, sb.Length - end); + return sb.ToString(); + } + } +} diff --git a/src/NLog.RegEx/NLog.RegEx.csproj b/src/NLog.RegEx/NLog.RegEx.csproj new file mode 100644 index 0000000000..c107c0f951 --- /dev/null +++ b/src/NLog.RegEx/NLog.RegEx.csproj @@ -0,0 +1,80 @@ + + + + net35;net46;netstandard2.0 + + NLog.RegEx + NLog + NLog.RegEx included ${regex-replace} and condition method regex-matches + NLog.RegEx v$(ProductVersion) + $(ProductVersion) + Jarek Kowalski,Kim Christensen,Julian Verdurmen + $([System.DateTime]::Now.ToString(yyyy)) + Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ + + + RegEx-Replace Docs: + https://github.com/NLog/NLog/wiki/Replace-Layout-Renderer + + README.md + NLog;RegEx:RegularExpressions;;logging;log + N.png + https://nlog-project.org/ + BSD-3-Clause + git + https://github.com/NLog/NLog.git + + true + 5.0.0.0 + ..\NLog.snk + true + + true + true + true + true + true + copyused + + + + NLog.RegEx for .NET Framework 4.6 + true + + + + NLog.RegEx for .NET Framework 4.5 + true + + + + NLog.RegEx for .NET Framework 3.5 + true + + + + NLog.RegEx for NetStandard 2.0 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NLog.RegEx/README.md b/src/NLog.RegEx/README.md new file mode 100644 index 0000000000..5d852455e5 --- /dev/null +++ b/src/NLog.RegEx/README.md @@ -0,0 +1,25 @@ +# NLog RegEx Replace LayoutRenderer + +NLog RegEx Replace LayoutRenderer for complex search and replace operations + +If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) + +See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Replace-Layout-Renderer) for available options and examples. + +## Register Extension + +NLog will only recognize type-alias `regex-replace` when loading from `NLog.config`-file, if having added extension to `NLog.config`-file: + +```xml + + + +``` + +Alternative register from code using [fluent configuration API](https://github.com/NLog/NLog/wiki/Fluent-Configuration-API): + +```csharp +LogManager.Setup().SetupExtensions(ext => { + ext.RegisterLayoutRenderer(); +}); +``` diff --git a/src/NLog.RegEx/Targets/ConsoleWordHighlightingRuleRegex.cs b/src/NLog.RegEx/Targets/ConsoleWordHighlightingRuleRegex.cs new file mode 100644 index 0000000000..544fc4a9be --- /dev/null +++ b/src/NLog.RegEx/Targets/ConsoleWordHighlightingRuleRegex.cs @@ -0,0 +1,95 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets +{ + using System.Collections.Generic; + using NLog.Config; + using NLog.RegEx.Internal; + + /// + /// Highlighting rule for Win32 colorful console. + /// + [NLogConfigurationItem] + public class ConsoleWordHighlightingRuleRegex : ConsoleWordHighlightingRule + { + private readonly RegexHelper _regexHelper = new RegexHelper(); + + /// + /// Gets or sets the regular expression to be matched. You must specify either text or regex. + /// + /// + public string Regex + { + get => _regexHelper.RegexPattern; + set => _regexHelper.RegexPattern = value; + } + + /// + /// Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. + /// + /// + public bool CompileRegex + { + get => _regexHelper.CompileRegex; + set => _regexHelper.CompileRegex = value; + } + + private string _searchText; + + /// + protected override IEnumerable> GetWordsForHighlighting(string haystack) + { + if (!ReferenceEquals(_searchText, Text)) + { + if (!string.IsNullOrEmpty(Text)) + _regexHelper.SearchText = Text; + _regexHelper.WholeWords = WholeWords; + _regexHelper.IgnoreCase = IgnoreCase; + _searchText = Text; + } + + var matches = _regexHelper.Matches(haystack); + if (matches is null || matches.Count == 0) + return null; + + return YieldWordsForHighlighting(matches); + } + + private static IEnumerable> YieldWordsForHighlighting(System.Text.RegularExpressions.MatchCollection matches) + { + foreach (System.Text.RegularExpressions.Match match in matches) + yield return new KeyValuePair(match.Index, match.Length); + } + } +} diff --git a/src/NLog.Targets.WebService/WebServiceTarget.cs b/src/NLog.Targets.WebService/WebServiceTarget.cs index fcb208010f..3d0a4897b0 100644 --- a/src/NLog.Targets.WebService/WebServiceTarget.cs +++ b/src/NLog.Targets.WebService/WebServiceTarget.cs @@ -414,7 +414,7 @@ private void WaitForReponse(AsyncContinuation continuation, HttpWebRequest webRe catch (Exception ex) { #if DEBUG - if (ex.MustBeRethrownImmediately()) + if (LogManager.ThrowExceptions) { throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) } @@ -445,7 +445,7 @@ private void PostPayload(AsyncContinuation continuation, HttpWebRequest webReque catch (Exception ex) { #if DEBUG - if (ex.MustBeRethrownImmediately()) + if (LogManager.ThrowExceptions) { throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) } diff --git a/src/NLog.sln b/src/NLog.sln index a36450bcda..d3a54532b4 100644 --- a/src/NLog.sln +++ b/src/NLog.sln @@ -58,6 +58,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.Network", "NLo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.Network.Tests", "..\tests\NLog.Targets.Network.Tests\NLog.Targets.Network.Tests.csproj", "{38828090-4953-4EF5-929C-8063FD4BCCC9}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.RegEx", "NLog.RegEx\NLog.RegEx.csproj", "{A87BFA04-DBFD-44F4-8223-A02AC0E85C7F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.RegEx.Tests", "..\tests\NLog.RegEx.Tests\NLog.RegEx.Tests.csproj", "{7C77DA2A-DF9A-4F4C-91BF-A593B31D7619}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -136,6 +140,14 @@ Global {38828090-4953-4EF5-929C-8063FD4BCCC9}.Debug|Any CPU.Build.0 = Debug|Any CPU {38828090-4953-4EF5-929C-8063FD4BCCC9}.Release|Any CPU.ActiveCfg = Release|Any CPU {38828090-4953-4EF5-929C-8063FD4BCCC9}.Release|Any CPU.Build.0 = Release|Any CPU + {A87BFA04-DBFD-44F4-8223-A02AC0E85C7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A87BFA04-DBFD-44F4-8223-A02AC0E85C7F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A87BFA04-DBFD-44F4-8223-A02AC0E85C7F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A87BFA04-DBFD-44F4-8223-A02AC0E85C7F}.Release|Any CPU.Build.0 = Release|Any CPU + {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -157,6 +169,8 @@ Global {D6FB13E7-92A7-4F4F-A09A-37EB04767A82} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {B048DD1F-4A24-453B-9C3D-F08280AE3FF3} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {38828090-4953-4EF5-929C-8063FD4BCCC9} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {A87BFA04-DBFD-44F4-8223-A02AC0E85C7F} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6B4C8E9F-1E2F-4AB3-993A-8776CFA08DC0} diff --git a/src/NLog/Conditions/ConditionMethods.cs b/src/NLog/Conditions/ConditionMethods.cs index fe9c12c310..044a956c30 100644 --- a/src/NLog/Conditions/ConditionMethods.cs +++ b/src/NLog/Conditions/ConditionMethods.cs @@ -35,7 +35,6 @@ namespace NLog.Conditions { using System; using System.Runtime.InteropServices; - using System.Text.RegularExpressions; /// /// A bunch of utility methods (mostly predicates) which can be used in @@ -118,34 +117,5 @@ public static int Length(string text) { return text?.Length ?? 0; } - - /// - /// Indicates whether the specified regular expression finds a match in the specified input string. - /// - /// The string to search for a match. - /// The regular expression pattern to match. - /// A string consisting of the desired options for the test. The possible values are those of the separated by commas. - /// true if the regular expression finds a match; otherwise, false. - [ConditionMethod("regex-matches")] - public static bool RegexMatches(string input, string pattern, [Optional, DefaultParameterValue("")] string options) - { - RegexOptions regexOpts = ParseRegexOptions(options) | RegexOptions.ExplicitCapture; - return Regex.IsMatch(input, pattern, regexOpts); - } - - /// - /// - /// - /// - /// - private static RegexOptions ParseRegexOptions(string options) - { - if (string.IsNullOrEmpty(options)) - { - return RegexOptions.None; - } - - return (RegexOptions)Enum.Parse(typeof(RegexOptions), options, true); - } } } diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index c434bc2490..058fb86beb 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -233,8 +233,6 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.ConditionMethodFactory.RegisterThreeParameters("starts-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); factory.ConditionMethodFactory.RegisterTwoParameters("ends-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString())); factory.ConditionMethodFactory.RegisterThreeParameters("ends-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); - factory.ConditionMethodFactory.RegisterTwoParameters("regex-matches", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.RegexMatches(arg1?.ToString(), arg2?.ToString())); - factory.ConditionMethodFactory.RegisterThreeParameters("regex-matches", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.RegexMatches(arg1?.ToString(), arg2?.ToString(), arg3?.ToString() ?? string.Empty)); #pragma warning restore CS0618 // Type or member is obsolete } diff --git a/src/NLog/Config/AssemblyExtensionTypes.tt b/src/NLog/Config/AssemblyExtensionTypes.tt index 462dff2c1a..92212e2def 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.tt +++ b/src/NLog/Config/AssemblyExtensionTypes.tt @@ -177,8 +177,6 @@ namespace NLog.Config factory.ConditionMethodFactory.RegisterThreeParameters("starts-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); factory.ConditionMethodFactory.RegisterTwoParameters("ends-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString())); factory.ConditionMethodFactory.RegisterThreeParameters("ends-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); - factory.ConditionMethodFactory.RegisterTwoParameters("regex-matches", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.RegexMatches(arg1?.ToString(), arg2?.ToString())); - factory.ConditionMethodFactory.RegisterThreeParameters("regex-matches", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.RegexMatches(arg1?.ToString(), arg2?.ToString(), arg3?.ToString() ?? string.Empty)); #pragma warning restore CS0618 // Type or member is obsolete } diff --git a/src/NLog/Internal/StringHelpers.cs b/src/NLog/Internal/StringHelpers.cs index 7d2bcca24d..8a2a0788e1 100644 --- a/src/NLog/Internal/StringHelpers.cs +++ b/src/NLog/Internal/StringHelpers.cs @@ -84,12 +84,8 @@ internal static string[] SplitAndTrimTokens(this string value, char delimiter) /// /// Replace string with /// - /// - /// - /// - /// /// The same reference of nothing has been replaced. - public static string Replace([NotNull] string str, [NotNull] string oldValue, string newValue, StringComparison comparison) + public static string Replace([NotNull] string str, [NotNull] string oldValue, string newValue, StringComparison comparison, bool wholeWords = false) { Guard.ThrowIfNull(str); @@ -107,22 +103,27 @@ public static string Replace([NotNull] string str, [NotNull] string oldValue, st int index = str.IndexOf(oldValue, comparison); while (index != -1) { - sb = sb ?? new StringBuilder(str.Length); - - if (previousIndex >= str.Length) + if (!wholeWords ||IsWholeWord(str, oldValue, index)) { - // for cases that 2 chars is one symbol - break; + sb = sb ?? new StringBuilder(str.Length); + if (previousIndex >= str.Length) + { + // for cases that 2 chars is one symbol + break; + } + sb.Append(str, previousIndex, index - previousIndex); + sb.Append(newValue); + index += oldValue.Length; + previousIndex = index; + if (index >= str.Length) + { + // for cases that 2 chars is one symbol + break; + } } - sb.Append(str, previousIndex, index - previousIndex); - sb.Append(newValue); - index += oldValue.Length; - - previousIndex = index; - if (index >= str.Length) + else { - // for cases that 2 chars is one symbol - break; + index += oldValue.Length; } index = str.IndexOf(oldValue, index, comparison); @@ -142,6 +143,11 @@ public static string Replace([NotNull] string str, [NotNull] string oldValue, st return sb.ToString(); } + public static bool IsWholeWord(string str, string token, int index) + { + return (index + token.Length == str.Length || !char.IsLetterOrDigit(str[index + token.Length]) && (index == 0 || !char.IsLetterOrDigit(str[index - 1]))); + } + /// Concatenates all the elements of a string array, using the specified separator between each element. /// The string to use as a separator. is included in the returned string only if has more than one element. /// An collection that contains the elements to concatenate. diff --git a/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs index 7d1fb2ad27..1c3acdf82e 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs @@ -34,10 +34,6 @@ namespace NLog.LayoutRenderers.Wrappers { using System; - using System.Linq; - using System.Text; - using System.Text.RegularExpressions; - using NLog.Common; using NLog.Config; using NLog.Internal; @@ -45,7 +41,7 @@ namespace NLog.LayoutRenderers.Wrappers /// Replaces a string in the output of another layout with another string. /// /// - /// ${replace:searchFor=\\n+:replaceWith=-:regex=true:inner=${message}} + /// ${replace:searchFor=foo:replaceWith=bar:inner=${message}} /// /// /// See NLog Wiki @@ -56,9 +52,6 @@ namespace NLog.LayoutRenderers.Wrappers [ThreadAgnostic] public sealed class ReplaceLayoutRendererWrapper : WrapperLayoutRendererBase { - private RegexHelper _regexHelper; - private MatchEvaluator _groupMatchEvaluator; - /// /// Gets or sets the text to search for. /// @@ -67,13 +60,6 @@ public sealed class ReplaceLayoutRendererWrapper : WrapperLayoutRendererBase [RequiredParameter] public string SearchFor { get; set; } - /// - /// Gets or sets a value indicating whether regular expressions should be used. - /// - /// A value of true if regular expressions should be used otherwise, false. - /// - public bool Regex { get; set; } - /// /// Gets or sets the replacement string. /// @@ -81,14 +67,6 @@ public sealed class ReplaceLayoutRendererWrapper : WrapperLayoutRendererBase /// public string ReplaceWith { get; set; } = string.Empty; - /// - /// Gets or sets the group name to replace when using regular expressions. - /// Leave null or empty to replace without using group name. - /// - /// The group name. - /// - public string ReplaceGroupName { get; set; } - /// /// Gets or sets a value indicating whether to ignore case. /// @@ -97,108 +75,24 @@ public sealed class ReplaceLayoutRendererWrapper : WrapperLayoutRendererBase public bool IgnoreCase { get; set; } /// - /// Gets or sets a value indicating whether to search for whole words. + /// Gets or sets a value indicating whether to search for whole words /// /// A value of true if whole words should be searched for; otherwise, false. /// public bool WholeWords { get; set; } - /// - /// Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. - /// - /// - public bool CompileRegex { get; set; } - - /// - protected override void InitializeLayoutRenderer() - { - base.InitializeLayoutRenderer(); - - _regexHelper = new RegexHelper() - { - IgnoreCase = IgnoreCase, - WholeWords = WholeWords, - CompileRegex = CompileRegex, - }; - if (Regex) - _regexHelper.RegexPattern = SearchFor; - else - _regexHelper.SearchText = SearchFor; - - if (!string.IsNullOrEmpty(ReplaceGroupName) && _regexHelper.Regex?.GetGroupNames()?.Contains(ReplaceGroupName) == false) - { - InternalLogger.Warn("Replace-LayoutRenderer assigned unknown ReplaceGroupName: {0}", ReplaceGroupName); - } - } - /// protected override string Transform(string text) { - if (string.IsNullOrEmpty(ReplaceGroupName)) + if (IgnoreCase || WholeWords) { - return _regexHelper.Replace(text, ReplaceWith); + var stringComparer = IgnoreCase ? StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture; + return StringHelpers.Replace(text, SearchFor, ReplaceWith, stringComparer, WholeWords); } else { - if (_groupMatchEvaluator is null) - _groupMatchEvaluator = m => ReplaceNamedGroup(ReplaceGroupName, ReplaceWith, m); - return _regexHelper.Regex?.Replace(text, _groupMatchEvaluator) ?? text; + return text.Replace(SearchFor, ReplaceWith); } } - - /// - /// This class was created instead of simply using a lambda expression so that the "ThreadAgnosticAttributeTest" will pass - /// - [ThreadAgnostic] - [Obsolete("This class should not be used. Marked obsolete on NLog 4.7")] - public class Replacer - { - private readonly string _replaceGroupName; - private readonly string _replaceWith; - - internal Replacer(string text, string replaceGroupName, string replaceWith) - { - _replaceGroupName = replaceGroupName; - _replaceWith = replaceWith; - } - - internal string EvaluateMatch(Match match) - { - return ReplaceNamedGroup(_replaceGroupName, _replaceWith, match); - } - } - - /// - /// A match evaluator for Regular Expression based replacing - /// - /// Input string. - /// Group name in the regex. - /// Replace value. - /// Match from regex. - /// Groups replaced with . - [Obsolete("This method should not be used. Marked obsolete on NLog 4.7")] - public static string ReplaceNamedGroup(string input, string groupName, string replacement, Match match) - { - return ReplaceNamedGroup(groupName, replacement, match); - } - - internal static string ReplaceNamedGroup(string groupName, string replacement, Match match) - { - var sb = new StringBuilder(match.Value); - var matchLength = match.Length; - - var captures = match.Groups[groupName].Captures.OfType().OrderByDescending(c => c.Index); - foreach (var capt in captures) - { - matchLength += replacement.Length - capt.Length; - - sb.Remove(capt.Index - match.Index, capt.Length); - sb.Insert(capt.Index - match.Index, replacement); - } - - var end = matchLength; - sb.Remove(end, sb.Length - end); - return sb.ToString(); - } } } diff --git a/src/NLog/Targets/ColoredConsoleTarget.cs b/src/NLog/Targets/ColoredConsoleTarget.cs index 2c1d921aee..2607b22441 100644 --- a/src/NLog/Targets/ColoredConsoleTarget.cs +++ b/src/NLog/Targets/ColoredConsoleTarget.cs @@ -463,27 +463,33 @@ private string GenerateColorEscapeSequences(LogEventInfo logEvent, string messag for (int i = 0; i < WordHighlightingRules.Count; ++i) { var hl = WordHighlightingRules[i]; - var matches = hl.Matches(logEvent, message); - if (matches is null || matches.Count == 0) + if (hl.Condition != null && false.Equals(hl.Condition.Evaluate(logEvent))) + continue; + + var matches = hl.GetWordsForHighlighting(message); + if (matches is null) continue; if (sb != null) sb.Length = 0; int previousIndex = 0; - foreach (System.Text.RegularExpressions.Match match in matches) + foreach (var match in matches) { sb = sb ?? new StringBuilder(message.Length + 5); - sb.Append(message, previousIndex, match.Index - previousIndex); + sb.Append(message, previousIndex, match.Key - previousIndex); sb.Append('\a'); sb.Append((char)((int)hl.ForegroundColor + 'A')); sb.Append((char)((int)hl.BackgroundColor + 'A')); - sb.Append(match.Value); + for (int j = 0; j < match.Value; ++j) + { + sb.Append(message[j + match.Key]); + } sb.Append('\a'); sb.Append('X'); - previousIndex = match.Index + match.Length; + previousIndex = match.Key + match.Value; } if (sb?.Length > 0) diff --git a/src/NLog/Targets/ConsoleWordHighlightingRule.cs b/src/NLog/Targets/ConsoleWordHighlightingRule.cs index 4a15ed2dc9..7d04e2f5c0 100644 --- a/src/NLog/Targets/ConsoleWordHighlightingRule.cs +++ b/src/NLog/Targets/ConsoleWordHighlightingRule.cs @@ -33,7 +33,7 @@ namespace NLog.Targets { - using System.Text.RegularExpressions; + using System.Collections.Generic; using NLog.Conditions; using NLog.Config; using NLog.Internal; @@ -44,8 +44,6 @@ namespace NLog.Targets [NLogConfigurationItem] public class ConsoleWordHighlightingRule { - private readonly RegexHelper _regexHelper = new RegexHelper(); - /// /// Initializes a new instance of the class. /// @@ -63,66 +61,35 @@ public ConsoleWordHighlightingRule() /// Color of the background. public ConsoleWordHighlightingRule(string text, ConsoleOutputColor foregroundColor, ConsoleOutputColor backgroundColor) { - _regexHelper.SearchText = text; + Text = text; ForegroundColor = foregroundColor; BackgroundColor = backgroundColor; } - /// - /// Gets or sets the regular expression to be matched. You must specify either text or regex. - /// - /// - public string Regex - { - get => _regexHelper.RegexPattern; - set => _regexHelper.RegexPattern = value; - } - /// /// Gets or sets the condition that must be met before scanning the row for highlight of words /// /// public ConditionExpression Condition { get; set; } - /// - /// Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. - /// - /// - public bool CompileRegex - { - get => _regexHelper.CompileRegex; - set => _regexHelper.CompileRegex = value; - } - /// /// Gets or sets the text to be matched. You must specify either text or regex. /// /// - public string Text - { - get => _regexHelper.SearchText; - set => _regexHelper.SearchText = value; - } + public string Text { get => _text; set => _text = string.IsNullOrEmpty(value) ? string.Empty : value; } + private string _text = string.Empty; /// /// Gets or sets a value indicating whether to match whole words only. /// /// - public bool WholeWords - { - get => _regexHelper.WholeWords; - set => _regexHelper.WholeWords = value; - } + public bool WholeWords { get; set; } /// /// Gets or sets a value indicating whether to ignore case when comparing texts. /// /// - public bool IgnoreCase - { - get => _regexHelper.IgnoreCase; - set => _regexHelper.IgnoreCase = value; - } + public bool IgnoreCase { get; set; } /// /// Gets or sets the foreground color. @@ -137,18 +104,52 @@ public bool IgnoreCase public ConsoleOutputColor BackgroundColor { get; set; } /// - /// Gets the compiled regular expression that matches either Text or Regex property. Only used when is true. + /// Scans the for words that should be highlighted. /// - public Regex CompiledRegex => _regexHelper.Regex; + /// List of words with start-position (Key) and word-length (Value) + internal protected virtual IEnumerable> GetWordsForHighlighting(string haystack) + { + if (ReferenceEquals(_text, string.Empty)) + return null; + + int firstIndex = FindNextWordForHighlighting(haystack, null); + if (firstIndex < 0) + return null; + + int nextIndex = FindNextWordForHighlighting(haystack, firstIndex); + if (nextIndex < 0) + return new[] { new KeyValuePair(firstIndex, Text.Length) }; + + return YieldWordsForHighlighting(haystack, firstIndex, nextIndex); + } - internal MatchCollection Matches(LogEventInfo logEvent, string message) + private IEnumerable> YieldWordsForHighlighting(string haystack, int firstIndex, int nextIndex) { - if (Condition != null && false.Equals(Condition.Evaluate(logEvent))) + yield return new KeyValuePair(firstIndex, _text.Length); + + yield return new KeyValuePair(nextIndex, _text.Length); + + int index = nextIndex; + while (index >= 0) { - return null; + index = FindNextWordForHighlighting(haystack, index); + if (index >= 0) + yield return new KeyValuePair(index, _text.Length); } + } - return _regexHelper.Matches(message); + private int FindNextWordForHighlighting(string haystack, int? prevIndex) + { + int index = prevIndex.HasValue ? prevIndex.Value + _text.Length : 0; + while (index >= 0) + { + index = IgnoreCase ? haystack.IndexOf(_text, index, System.StringComparison.CurrentCultureIgnoreCase) : haystack.IndexOf(_text, index); + if (index < 0 || (!WholeWords || StringHelpers.IsWholeWord(haystack, _text, index))) + return index; + + index += _text.Length; + } + return index; } } } diff --git a/tests/NLog.RegEx.Tests/ColoredConsoleTargetTests.cs b/tests/NLog.RegEx.Tests/ColoredConsoleTargetTests.cs new file mode 100644 index 0000000000..e8e6d1a40c --- /dev/null +++ b/tests/NLog.RegEx.Tests/ColoredConsoleTargetTests.cs @@ -0,0 +1,190 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.RegEx.Tests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using NLog.Targets; + using Xunit; + + public class ColoredConsoleTargetTests + { + public ColoredConsoleTargetTests() + { + LogManager.ThrowExceptions = true; + LogManager.Setup().SetupExtensions(ext => ext.RegisterAssembly(typeof(Conditions.RegexConditionMethods).Assembly)); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void WordHighlightingTextIgnoreCase(bool compileRegex) + { + var target = new ColoredConsoleTarget { Layout = "${logger} ${message}" }; + target.WordHighlightingRules.Add( + new ConsoleWordHighlightingRuleRegex + { + ForegroundColor = ConsoleOutputColor.Red, + Text = "at", + IgnoreCase = true, + CompileRegex = compileRegex + }); + + AssertOutput(target, "The Cat Sat At The Bar.", + new string[] { "The C", "at", " S", "at", " ", "At", " The Bar." }); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void WordHighlightingTextWholeWords(bool compileRegex) + { + var target = new ColoredConsoleTarget { Layout = "${logger} ${message}" }; + target.WordHighlightingRules.Add( + new ConsoleWordHighlightingRuleRegex + { + ForegroundColor = ConsoleOutputColor.Red, + Text = "at", + WholeWords = true, + CompileRegex = compileRegex + }); + + AssertOutput(target, "The cat sat at the bar.", + new string[] { "The cat sat ", "at", " the bar." }); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void WordHighlightingRegex(bool compileRegex) + { + var target = new ColoredConsoleTarget { Layout = "${logger} ${message}" }; + target.WordHighlightingRules.Add( + new ConsoleWordHighlightingRuleRegex + { + ForegroundColor = ConsoleOutputColor.Red, + Regex = "\\wat", + CompileRegex = compileRegex + }); + + AssertOutput(target, "The cat sat at the bar.", + new string[] { "The ", "cat", " ", "sat", " at the bar." }); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void DonRemoveIfRegexIsEmpty(bool compileRegex) + { + var target = new ColoredConsoleTarget { Layout = "${logger} ${message}" }; + target.WordHighlightingRules.Add( + new ConsoleWordHighlightingRuleRegex + { + ForegroundColor = ConsoleOutputColor.Red, + Text = null, + IgnoreCase = true, + CompileRegex = compileRegex + }); + + AssertOutput(target, "The Cat Sat At The Bar.", + new string[] { "The Cat Sat At The Bar." }); + } + + private static void AssertOutput(Target target, string message, string[] expectedParts, string loggerName = "Logger ") + { + var consoleOutWriter = new PartsWriter(); + TextWriter oldConsoleOutWriter = Console.Out; + Console.SetOut(consoleOutWriter); + + try + { + var exceptions = new List(); + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; + target.WriteAsyncLogEvent(new LogEventInfo(LogLevel.Info, loggerName.Trim(), message).WithContinuation(exceptions.Add)); + logFactory.Shutdown(); + + Assert.Single(exceptions); + Assert.True(exceptions.TrueForAll(e => e is null)); + } + finally + { + Console.SetOut(oldConsoleOutWriter); + } + + var expected = Enumerable.Repeat(loggerName + expectedParts[0], 1).Concat(expectedParts.Skip(1)); + Assert.Equal(expected, consoleOutWriter.Values); + Assert.True(consoleOutWriter.SingleWriteLine); + } + + private sealed class PartsWriter : StringWriter + { + public PartsWriter() + { + Values = new List(); + } + + public List Values { get; private set; } + public bool SingleWriteLine { get; private set; } + + public override void Write(string value) + { + Values.Add(value); + } + + public override void WriteLine(string value) + { + if (SingleWriteLine) + { + Values.Clear(); + throw new InvalidOperationException("Single WriteLine only"); + } + SingleWriteLine = true; + if (!string.IsNullOrEmpty(value)) + Values.Add(value); + } + + public override void WriteLine() + { + if (SingleWriteLine) + { + Values.Clear(); + throw new InvalidOperationException("Single WriteLine only"); + } + SingleWriteLine = true; + } + } + } +} diff --git a/tests/NLog.RegEx.Tests/ConditionEvaluatorTests.cs b/tests/NLog.RegEx.Tests/ConditionEvaluatorTests.cs new file mode 100644 index 0000000000..8c156355d5 --- /dev/null +++ b/tests/NLog.RegEx.Tests/ConditionEvaluatorTests.cs @@ -0,0 +1,85 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.RegEx.Tests +{ + using NLog.Conditions; + using Xunit; + + public class ConditionEvaluatorTests + { + public ConditionEvaluatorTests() + { + LogManager.ThrowExceptions = true; + LogManager.Setup().SetupExtensions(ext => ext.RegisterAssembly(typeof(Conditions.RegexConditionMethods).Assembly)); + } + + [Fact] + public void ConditionMethodsTest() + { + AssertEvaluationResult(true, "regex-matches('foo', '^foo$')"); + AssertEvaluationResult(false, "regex-matches('foo', '^bar$')"); + + //Check that calling with empty string is equivalent with not passing the parameter + AssertEvaluationResult(true, "regex-matches('foo', '^foo$', '')"); + AssertEvaluationResult(false, "regex-matches('foo', '^bar$', '')"); + + //Check that options are parsed correctly + AssertEvaluationResult(true, "regex-matches('Foo', '^foo$', 'ignorecase')"); + AssertEvaluationResult(false, "regex-matches('Foo', '^foo$')"); + AssertEvaluationResult(true, "regex-matches('foo\nbar', '^Foo$', 'ignorecase,multiline')"); + AssertEvaluationResult(false, "regex-matches('foo\nbar', '^Foo$')"); + Assert.Throws(() => AssertEvaluationResult(true, "regex-matches('foo\nbar', '^Foo$', 'ignorecase,nonexistent')")); + } + + private static void AssertEvaluationResult(object expectedResult, string conditionText) + { + ConditionExpression condition = ConditionParser.ParseExpression(conditionText); + LogEventInfo context = CreateWellKnownContext(); + object actualResult = condition.Evaluate(context); + Assert.Equal(expectedResult, actualResult); + } + + private static LogEventInfo CreateWellKnownContext() + { + var context = new LogEventInfo + { + Level = LogLevel.Warn, + Message = "some message", + LoggerName = "MyCompany.Product.Class" + }; + + return context; + } + } +} diff --git a/tests/NLog.RegEx.Tests/NLog.RegEx.Tests.csproj b/tests/NLog.RegEx.Tests/NLog.RegEx.Tests.csproj new file mode 100644 index 0000000000..b61a92c4f1 --- /dev/null +++ b/tests/NLog.RegEx.Tests/NLog.RegEx.Tests.csproj @@ -0,0 +1,26 @@ + + + + 17.0 + net462 + net462;net6.0 + + false + + Full + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/tests/NLog.RegEx.Tests/RegexReplaceTests.cs b/tests/NLog.RegEx.Tests/RegexReplaceTests.cs new file mode 100644 index 0000000000..4b75acafb6 --- /dev/null +++ b/tests/NLog.RegEx.Tests/RegexReplaceTests.cs @@ -0,0 +1,181 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.RegEx.Tests +{ + using System; + using System.Collections.Generic; + using NLog; + using NLog.Config; + using NLog.LayoutRenderers.Wrappers; + using NLog.Layouts; + using NLog.Targets; + using Xunit; + + public class RegexReplaceTests + { + public RegexReplaceTests() + { + LogManager.ThrowExceptions = true; + LogManager.Setup().SetupExtensions(ext => ext.RegisterAssembly(typeof(Conditions.RegexConditionMethods).Assembly)); + } + + [Fact] + public void ReplaceTestWithoutRegEx() + { + // Arrange + SimpleLayout layout = @"${regex-replace:inner=${message}:searchFor=foo:replaceWith=BAR}"; + + // Act + var result = layout.Render(new LogEventInfo(LogLevel.Info, "Test", " foo bar bar foo bar FOO")); + + // Assert + Assert.Equal(" BAR bar bar BAR bar FOO", result); + } + + [Fact] + public void ReplaceTestIgnoreCaseWithoutRegEx() + { + // Arrange + SimpleLayout layout = @"${regex-replace:inner=${message}:searchFor=foo:replaceWith=BAR:ignorecase=true}"; + + // Act + var result = layout.Render(new LogEventInfo(LogLevel.Info, "Test", " foo bar bar foo bar FOO")); + + // Assert + Assert.Equal(" BAR bar bar BAR bar BAR", result); + } + + [Fact] + public void ReplaceTestWholeWordsWithoutRegEx() + { + // Arrange + SimpleLayout layout = @"${regex-replace:inner=${message}:searchFor=foo:replaceWith=BAR:ignorecase=true:WholeWords=true}"; + + // Act + var result = layout.Render(new LogEventInfo(LogLevel.Info, "Test", " foo bar bar foobar bar FOO")); + + // Assert + Assert.Equal(" BAR bar bar foobar bar BAR", result); + } + + [Fact] + public void ReplaceTestWithSimpleRegExFromConfig() + { + var configuration = XmlLoggingConfiguration.CreateFromXmlString(@" + + + + + + + +"); + + var d1 = configuration.FindTargetByName("d1") as DebugTarget; + Assert.NotNull(d1); + var layout = d1.Layout as SimpleLayout; + Assert.NotNull(layout); + + var result = layout.Render(new LogEventInfo(LogLevel.Info, "Test", "\r\nfoo\rbar\nbar\tbar bar \n bar")); + Assert.Equal(" foo bar bar bar bar bar", result); + } + + [Fact] + public void ReplaceTestWithSimpleRegExFromConfig2() + { + var configuration = XmlLoggingConfiguration.CreateFromXmlString(@" + + + + + + + + + +"); + + var d1 = configuration.FindTargetByName("d1") as DebugTarget; + Assert.NotNull(d1); + var layout = d1.Layout as SimpleLayout; + Assert.NotNull(layout); + + var result = layout.Render(new LogEventInfo(LogLevel.Info, "Test", "\r\nfoo\rbar\nbar\tbar bar \n bar")); + Assert.Equal(" foo bar bar bar bar bar", result); + } + + [Fact] + public void ReplaceTestWithComplexRegEx() + { + var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@" + + + + + + + + + + + + +").LogFactory; + + var d1 = logFactory.Configuration.FindTargetByName("d1"); + Assert.NotNull(d1); + var layout = d1.Layout as SimpleLayout; + Assert.NotNull(layout); + + var testCases = new List> + { + Tuple.Create("1234", "1234"), + Tuple.Create("1234-5678-1234-5678", "XXXX-XXXX-XXXX-5678"), + Tuple.Create("1234 5678 1234 5678", "XXXX XXXX XXXX 5678"), + Tuple.Create("1234567812345678", "XXXXXXXXXXXX5678"), + Tuple.Create("ABCD-1234-5678-1234-5678", "ABCD-XXXX-XXXX-XXXX-5678"), + Tuple.Create("1234-5678-1234-5678-ABCD", "XXXX-XXXX-XXXX-5678-ABCD"), + Tuple.Create("ABCD-1234-5678-1234-5678-ABCD", "ABCD-XXXX-XXXX-XXXX-5678-ABCD"), + }; + + foreach (var testCase in testCases) + { + var result = layout.Render(new LogEventInfo(LogLevel.Info, "Test", testCase.Item1)); + Assert.Equal(testCase.Item2, result); + } + } + } +} diff --git a/tests/NLog.UnitTests/Conditions/ConditionEvaluatorTests.cs b/tests/NLog.UnitTests/Conditions/ConditionEvaluatorTests.cs index de74ed98a9..5528e9d59d 100644 --- a/tests/NLog.UnitTests/Conditions/ConditionEvaluatorTests.cs +++ b/tests/NLog.UnitTests/Conditions/ConditionEvaluatorTests.cs @@ -35,8 +35,6 @@ namespace NLog.UnitTests.Conditions { using System; using System.Globalization; - using System.IO; - using System.Runtime.Serialization.Formatters.Binary; using NLog.Conditions; using NLog.Config; using Xunit; @@ -76,20 +74,6 @@ public void ConditionMethodsTest() AssertEvaluationResult(false, "contains('foobar','oobe')"); AssertEvaluationResult(false, "contains('','foo')"); AssertEvaluationResult(true, "contains('foo','')"); - - AssertEvaluationResult(true, "regex-matches('foo', '^foo$')"); - AssertEvaluationResult(false, "regex-matches('foo', '^bar$')"); - - //Check that calling with empty string is equivalent with not passing the parameter - AssertEvaluationResult(true, "regex-matches('foo', '^foo$', '')"); - AssertEvaluationResult(false, "regex-matches('foo', '^bar$', '')"); - - //Check that options are parsed correctly - AssertEvaluationResult(true, "regex-matches('Foo', '^foo$', 'ignorecase')"); - AssertEvaluationResult(false, "regex-matches('Foo', '^foo$')"); - AssertEvaluationResult(true, "regex-matches('foo\nbar', '^Foo$', 'ignorecase,multiline')"); - AssertEvaluationResult(false, "regex-matches('foo\nbar', '^Foo$')"); - Assert.Throws(() => AssertEvaluationResult(true, "regex-matches('foo\nbar', '^Foo$', 'ignorecase,nonexistent')")); } [Fact] @@ -335,8 +319,8 @@ public void ExceptionTest4() { var inner = new InvalidOperationException("f"); var ex1 = new ConditionEvaluationException("msg", inner); - BinaryFormatter bf = new BinaryFormatter(); - MemoryStream ms = new MemoryStream(); + var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); + var ms = new System.IO.MemoryStream(); bf.Serialize(ms, ex1); ms.Position = 0; Exception ex2 = (Exception)bf.Deserialize(ms); @@ -375,8 +359,8 @@ public void ExceptionTest14() { var inner = new InvalidOperationException("f"); var ex1 = new ConditionParseException("msg", inner); - BinaryFormatter bf = new BinaryFormatter(); - MemoryStream ms = new MemoryStream(); + var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); + var ms = new System.IO.MemoryStream(); bf.Serialize(ms, ex1); ms.Position = 0; Exception ex2 = (Exception)bf.Deserialize(ms); diff --git a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/ReplaceTests.cs b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/ReplaceTests.cs index 32b5bc1ece..d41f05179a 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/ReplaceTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/ReplaceTests.cs @@ -33,13 +33,8 @@ namespace NLog.UnitTests.LayoutRenderers.Wrappers { - using System; - using System.Collections.Generic; using NLog; - using NLog.Config; - using NLog.LayoutRenderers.Wrappers; using NLog.Layouts; - using NLog.Targets; using Xunit; public class ReplaceTests : NLogTestBase @@ -71,134 +66,16 @@ public void ReplaceTestIgnoreCaseWithoutRegEx() } [Fact] - public void ReplaceTestWithSimpleRegEx() + public void ReplaceTestWholeWordsWithoutRegEx() { // Arrange - SimpleLayout layout = @"${replace:inner=${message}:searchFor=\\r\\n|\\s:replaceWith= :regex=true}"; + SimpleLayout layout = @"${replace:inner=${message}:searchFor=foo:replaceWith=BAR:ignorecase=true:WholeWords=true}"; // Act - var result = layout.Render(new LogEventInfo(LogLevel.Info, "Test", "\r\nfoo\rbar\nbar\tbar bar \n bar")); + var result = layout.Render(new LogEventInfo(LogLevel.Info, "Test", "foo bar bar foobar barfoo bar FOO")); // Assert - Assert.Equal(" foo bar bar bar bar bar", result); - } - - [Fact] - public void ReplaceTestWithSimpleRegExFromConfig() - { - var configuration = XmlLoggingConfiguration.CreateFromXmlString(@" - - - - - - - -"); - - var d1 = configuration.FindTargetByName("d1") as DebugTarget; - Assert.NotNull(d1); - var layout = d1.Layout as SimpleLayout; - Assert.NotNull(layout); - - var result = layout.Render(new LogEventInfo(LogLevel.Info, "Test", "\r\nfoo\rbar\nbar\tbar bar \n bar")); - Assert.Equal(" foo bar bar bar bar bar", result); - } - - [Fact] - public void ReplaceTestWithSimpleRegExFromConfig2() - { - var configuration = XmlLoggingConfiguration.CreateFromXmlString(@" - - - - - - - - - -"); - - var d1 = configuration.FindTargetByName("d1") as DebugTarget; - Assert.NotNull(d1); - var layout = d1.Layout as SimpleLayout; - Assert.NotNull(layout); - - var result = layout.Render(new LogEventInfo(LogLevel.Info, "Test", "\r\nfoo\rbar\nbar\tbar bar \n bar")); - Assert.Equal(" foo bar bar bar bar bar", result); - } - - [Fact] - public void ReplaceTestWithComplexRegEx() - { - var configuration = XmlLoggingConfiguration.CreateFromXmlString(@" - - - - - - - - - - - - -"); - - var d1 = configuration.FindTargetByName("d1") as DebugTarget; - Assert.NotNull(d1); - var layout = d1.Layout as SimpleLayout; - Assert.NotNull(layout); - - var testCases = new List> - { - Tuple.Create("1234", "1234"), - Tuple.Create("1234-5678-1234-5678", "XXXX-XXXX-XXXX-5678"), - }; - - foreach (var testCase in testCases) - { - var result = layout.Render(new LogEventInfo(LogLevel.Info, "Test", testCase.Item1)); - Assert.Equal(testCase.Item2, result); - } - } - - [Fact] - public void ReplaceNamedGroupTests() - { - var pattern = @"(?\d)[ -]*){8,16}(?=(\d[ -]*){3}(\d)(?![ -]*\d))"; - var groupName = @"digits"; - - var regex = new System.Text.RegularExpressions.Regex( - pattern, - System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.IgnoreCase); - - var testCases = new List> - { - Tuple.Create("1234", "1234", "X"), - Tuple.Create("1234-5678-1234-5678", "XXXX-XXXX-XXXX-5678", "X"), - Tuple.Create("1234 5678 1234 5678", "XXXX XXXX XXXX 5678", "X"), - Tuple.Create("1234567812345678", "XXXXXXXXXXXX5678", "X"), - Tuple.Create("ABCD-1234-5678-1234-5678", "ABCD-XXXX-XXXX-XXXX-5678", "X"), - Tuple.Create("1234-5678-1234-5678-ABCD", "XXXX-XXXX-XXXX-5678-ABCD", "X"), - Tuple.Create("ABCD-1234-5678-1234-5678-ABCD", "ABCD-XXXX-XXXX-XXXX-5678-ABCD", "X"), - Tuple.Create("ABCD-1234-5678-1234-5678-ABCD", "ABCD-XXXXXXXX-XXXXXXXX-XXXXXXXX-5678-ABCD", "XX"), - }; - - foreach (var testCase in testCases) - { - var input = testCase.Item1; - var replacement = testCase.Item3; - - var result = regex.Replace( - input, m => ReplaceLayoutRendererWrapper.ReplaceNamedGroup(groupName, replacement, m)); - - Assert.Equal(testCase.Item2, result); - } + Assert.Equal("BAR bar bar foobar barfoo bar BAR", result); } } } diff --git a/tests/NLog.UnitTests/Layouts/SimpleLayoutParserTests.cs b/tests/NLog.UnitTests/Layouts/SimpleLayoutParserTests.cs index 5ccdbb2e56..80a8ae419a 100644 --- a/tests/NLog.UnitTests/Layouts/SimpleLayoutParserTests.cs +++ b/tests/NLog.UnitTests/Layouts/SimpleLayoutParserTests.cs @@ -314,7 +314,7 @@ public void LayoutParserEscapeCodesForRegExTestV1() value=""(?<!\\d[ -]*)(?\u003a(?<digits>\\d)[ -]*)\u007b8,16\u007d(?=(\\d[ -]*)\u007b3\u007d(\\d)(?![ -]\\d))"" /> - + @@ -336,7 +336,6 @@ public void LayoutParserEscapeCodesForRegExTestV1() var l1 = layout.Renderers[0] as ReplaceLayoutRendererWrapper; Assert.NotNull(l1); - Assert.True(l1.Regex); Assert.True(l1.IgnoreCase); Assert.Equal(@"::", l1.ReplaceWith); Assert.Equal(@"(?\d)[ -]*){8,16}(?=(\d[ -]*){3}(\d)(?![ -]\d))", l1.SearchFor); @@ -353,7 +352,7 @@ public void LayoutParserEscapeCodesForRegExTestV2() value=""(?<!\\d[ -]*)(?\:(?<digits>\\d)[ -]*)\{8,16\}(?=(\\d[ -]*)\{3\}(\\d)(?![ -]\\d))"" /> - + @@ -375,7 +374,6 @@ public void LayoutParserEscapeCodesForRegExTestV2() var l1 = layout.Renderers[0] as ReplaceLayoutRendererWrapper; Assert.NotNull(l1); - Assert.True(l1.Regex); Assert.True(l1.IgnoreCase); Assert.Equal(@"::", l1.ReplaceWith); Assert.Equal(@"(?\d)[ -]*){8,16}(?=(\d[ -]*){3}(\d)(?![ -]\d))", l1.SearchFor); diff --git a/tests/NLog.UnitTests/Targets/ColoredConsoleTargetTests.cs b/tests/NLog.UnitTests/Targets/ColoredConsoleTargetTests.cs index 4cc138fe9d..3c657683bc 100644 --- a/tests/NLog.UnitTests/Targets/ColoredConsoleTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/ColoredConsoleTargetTests.cs @@ -47,13 +47,11 @@ public void RowHighLightNewLineTest() { var target = new ColoredConsoleTarget { Layout = "${logger} ${message}" }; AssertOutput(target, "Before\a\nAfter\a", - new string[] { "Before", "After" }); + new string[] { "Before", "After" }); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void WordHighlightingTextTest(bool compileRegex) + [Fact] + public void WordHighlightingTextTest() { var target = new ColoredConsoleTarget { Layout = "${logger} ${message}" }; target.WordHighlightingRules.Add( @@ -61,8 +59,6 @@ public void WordHighlightingTextTest(bool compileRegex) { ForegroundColor = ConsoleOutputColor.Red, Text = "at", - CompileRegex = compileRegex - }); AssertOutput(target, "The Cat Sat At The Bar\t\n.\a", @@ -90,10 +86,8 @@ public void WordHighlightingTextCondition() new string[] { "The Cat Sat At The Bar" }, loggerName: "DefaultLogger"); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void WordHighlightingTextIgnoreCase(bool compileRegex) + [Fact] + public void WordHighlightingTextIgnoreCase() { var target = new ColoredConsoleTarget { Layout = "${logger} ${message}" }; target.WordHighlightingRules.Add( @@ -102,17 +96,14 @@ public void WordHighlightingTextIgnoreCase(bool compileRegex) ForegroundColor = ConsoleOutputColor.Red, Text = "at", IgnoreCase = true, - CompileRegex = compileRegex }); AssertOutput(target, "The Cat Sat At The Bar.", new string[] { "The C", "at", " S", "at", " ", "At", " The Bar." }); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void WordHighlightingTextWholeWords(bool compileRegex) + [Fact] + public void WordHighlightingTextWholeWords() { var target = new ColoredConsoleTarget { Layout = "${logger} ${message}" }; target.WordHighlightingRules.Add( @@ -121,31 +112,12 @@ public void WordHighlightingTextWholeWords(bool compileRegex) ForegroundColor = ConsoleOutputColor.Red, Text = "at", WholeWords = true, - CompileRegex = compileRegex }); AssertOutput(target, "The cat sat at the bar.", new string[] { "The cat sat ", "at", " the bar." }); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void WordHighlightingRegex(bool compileRegex) - { - var target = new ColoredConsoleTarget { Layout = "${logger} ${message}" }; - target.WordHighlightingRules.Add( - new ConsoleWordHighlightingRule - { - ForegroundColor = ConsoleOutputColor.Red, - Regex = "\\wat", - CompileRegex = compileRegex - }); - - AssertOutput(target, "The cat sat at the bar.", - new string[] { "The ", "cat", " ", "sat", " at the bar." }); - } - [Fact] public void ColoredConsoleAnsi_OverlappingWordHighlight_VerificationTest() { @@ -270,29 +242,8 @@ public void ColoredConsoleAnsi_RowColorWithWordHighlight_VerificationTest() string.Empty); } - /// - /// With or without CompileRegex, CompileRegex is never null, even if not used when CompileRegex=false. (needed for backwards-compatibility) - /// - /// - [Theory] - [InlineData(true)] - [InlineData(false)] - public void CompiledRegexPropertyNotNull(bool compileRegex) - { - var rule = new ConsoleWordHighlightingRule - { - ForegroundColor = ConsoleOutputColor.Red, - Regex = "\\wat", - CompileRegex = compileRegex - }; - - Assert.NotNull(rule.CompiledRegex); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void DonRemoveIfRegexIsEmpty(bool compileRegex) + [Fact] + public void DonRemoveIfRegexIsEmpty() { var target = new ColoredConsoleTarget { Layout = "${logger} ${message}" }; target.WordHighlightingRules.Add( @@ -301,7 +252,6 @@ public void DonRemoveIfRegexIsEmpty(bool compileRegex) ForegroundColor = ConsoleOutputColor.Red, Text = null, IgnoreCase = true, - CompileRegex = compileRegex }); AssertOutput(target, "The Cat Sat At The Bar.", @@ -314,7 +264,6 @@ public void ColortedConsoleAutoFlushOnWrite() var target = new ColoredConsoleTarget { Layout = "${logger} ${message}", AutoFlush = true }; AssertOutput(target, "The Cat Sat At The Bar.", new string[] { "The Cat Sat At The Bar." }); - } #if !MONO From fa4a7d9ad90bd206ad61db9b4735513c68c5dda3 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 13 Oct 2024 16:25:49 +0200 Subject: [PATCH 022/224] Regex-Replace - Removed RegEx-option since implicit by type name (#5647) --- .../Wrappers/RegexReplaceLayoutRendererWrapper.cs | 14 ++------------ tests/NLog.RegEx.Tests/RegexReplaceTests.cs | 6 +++--- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs b/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs index dacef07b04..f2e5bfe04d 100644 --- a/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs +++ b/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs @@ -45,7 +45,7 @@ namespace NLog.LayoutRenderers.Wrappers /// Replaces a string in the output of another layout with another string. /// /// - /// ${replace:searchFor=\\n+:replaceWith=-:regex=true:inner=${message}} + /// ${replace:searchFor=\\n+:replaceWith=-:inner=${message}} /// /// /// See NLog Wiki @@ -67,13 +67,6 @@ public sealed class RegexReplaceLayoutRendererWrapper : WrapperLayoutRendererBas [RequiredParameter] public string SearchFor { get; set; } - /// - /// Gets or sets a value indicating whether regular expressions should be used. - /// - /// A value of true if regular expressions should be used otherwise, false. - /// - public bool Regex { get; set; } - /// /// Gets or sets the replacement string. /// @@ -120,10 +113,7 @@ protected override void InitializeLayoutRenderer() WholeWords = WholeWords, CompileRegex = CompileRegex, }; - if (Regex) - _regexHelper.RegexPattern = SearchFor; - else - _regexHelper.SearchText = SearchFor; + _regexHelper.RegexPattern = SearchFor; if (!string.IsNullOrEmpty(ReplaceGroupName) && _regexHelper.Regex?.GetGroupNames()?.Contains(ReplaceGroupName) == false) { diff --git a/tests/NLog.RegEx.Tests/RegexReplaceTests.cs b/tests/NLog.RegEx.Tests/RegexReplaceTests.cs index 4b75acafb6..c46667cee0 100644 --- a/tests/NLog.RegEx.Tests/RegexReplaceTests.cs +++ b/tests/NLog.RegEx.Tests/RegexReplaceTests.cs @@ -95,7 +95,7 @@ public void ReplaceTestWithSimpleRegExFromConfig() var configuration = XmlLoggingConfiguration.CreateFromXmlString(@" - + @@ -117,7 +117,7 @@ public void ReplaceTestWithSimpleRegExFromConfig2() var configuration = XmlLoggingConfiguration.CreateFromXmlString(@" - + @@ -144,7 +144,7 @@ public void ReplaceTestWithComplexRegEx() value=""(?<!\\d[ -]*)(?\:(?<digits>\\d)[ -]*)\{8,16\}(?=(\\d[ -]*)\{3\}(\\d)(?![ -]\\d))"" /> - + From e260830c8193562c44ac8eaa8e3a8e9238eae2b3 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 19 Oct 2024 12:49:18 +0200 Subject: [PATCH 023/224] Moved NLog.AutoReloadConfig into own nuget-package (#5649) --- .coderabbit.yaml | 16 + build.ps1 | 1 + run-tests.ps1 | 8 + src/NLog.AutoReloadConfig/MultiFileWatcher.cs | 237 +++++++ .../NLog.AutoReloadConfig.csproj | 80 +++ src/NLog.AutoReloadConfig/README.md | 15 + .../SetupBuilderAutoReloadExtensions.cs | 267 ++++++++ src/NLog.Targets.Mail/README.md | 2 +- src/NLog.sln | 15 + src/NLog/Config/AssemblyExtensionLoader.cs | 2 +- src/NLog/Config/ConfigSectionHandler.cs | 25 + src/NLog/Config/IInitializeSucceeded.cs | 43 -- .../Config/ILoggingConfigurationLoader.cs | 7 - src/NLog/Config/LoggingConfiguration.cs | 2 +- .../Config/LoggingConfigurationFileLoader.cs | 33 +- src/NLog/Config/LoggingConfigurationParser.cs | 3 + ...LoggingConfigurationWatchableFileLoader.cs | 293 --------- src/NLog/Config/XmlLoggingConfiguration.cs | 127 +--- src/NLog/Internal/MultiFileWatcher.cs | 24 +- src/NLog/LogFactory.cs | 4 +- src/NLog/SetupBuilderExtensions.cs | 33 +- .../AutoReloadTests.cs | 589 ++++++++++++++++++ .../NLog.AutoReloadConfig.Tests.csproj | 26 + tests/NLog.UnitTests/Config/ReloadTests.cs | 498 +++++++-------- tests/NLog.UnitTests/Config/XmlConfigTests.cs | 2 - tests/NLog.UnitTests/LogFactoryTests.cs | 61 -- tests/NLog.UnitTests/LogManagerTests.cs | 107 ---- 27 files changed, 1559 insertions(+), 961 deletions(-) create mode 100644 .coderabbit.yaml create mode 100644 src/NLog.AutoReloadConfig/MultiFileWatcher.cs create mode 100644 src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj create mode 100644 src/NLog.AutoReloadConfig/README.md create mode 100644 src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs delete mode 100644 src/NLog/Config/IInitializeSucceeded.cs delete mode 100644 src/NLog/Config/LoggingConfigurationWatchableFileLoader.cs create mode 100644 tests/NLog.AutoReloadConfig.Tests/AutoReloadTests.cs create mode 100644 tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000000..87282a5c34 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,16 @@ +# yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json +language: en-US +early_access: false +enable_free_tier: true +reviews: + profile: chill + request_changes_workflow: false + high_level_summary: false # Disable the high level summary because this feature modifies the original PR description. Ideally this feature would add a new comment to the PR instead of modifying the original PR description. + poem: false # Disable the poem feature because it adds noise and doesn't improve the review process. + review_status: false + auto_review: + enabled: true + auto_incremental_review: true + drafts: false +chat: + auto_reply: true diff --git a/build.ps1 b/build.ps1 index 64b4438095..f9b3787026 100644 --- a/build.ps1 +++ b/build.ps1 @@ -35,6 +35,7 @@ function create-package($packageName, $targetFrameworks) { exit $LastExitCode } } +create-package 'NLog.AutoReloadConfig' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Database' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.Mail' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.Network' '"net45;net46;netstandard2.0"' diff --git a/run-tests.ps1 b/run-tests.ps1 index 6bd9ef0d21..521b084db5 100644 --- a/run-tests.ps1 +++ b/run-tests.ps1 @@ -24,6 +24,10 @@ if ($isWindows -or $Env:WinDir) if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.AutoReloadConfig.Tests/ --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + dotnet test ./tests/NLog.RegEx.Tests/ --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } @@ -71,6 +75,10 @@ else if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.AutoReloadConfig.Tests/ --framework net6.0 --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + dotnet test ./tests/NLog.RegEx.Tests/ --framework net6.0 --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } diff --git a/src/NLog.AutoReloadConfig/MultiFileWatcher.cs b/src/NLog.AutoReloadConfig/MultiFileWatcher.cs new file mode 100644 index 0000000000..a9c573814d --- /dev/null +++ b/src/NLog.AutoReloadConfig/MultiFileWatcher.cs @@ -0,0 +1,237 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Internal +{ + using System; + using System.Collections.Generic; + using System.IO; + using NLog.Common; + + /// + /// Watches multiple files at the same time and raises an event whenever + /// a single change is detected in any of those files. + /// + internal sealed class MultiFileWatcher : IDisposable + { + private readonly Dictionary _watcherMap = new Dictionary(); + + /// + /// The types of changes to watch for. + /// + public NotifyFilters NotifyFilters { get; set; } + + /// + /// Occurs when a change is detected in one of the monitored files. + /// + public event FileSystemEventHandler FileChanged; + + public MultiFileWatcher() : + this(NotifyFilters.LastWrite | NotifyFilters.CreationTime | NotifyFilters.Size | NotifyFilters.Security | NotifyFilters.Attributes) + { } + + public MultiFileWatcher(NotifyFilters notifyFilters) + { + NotifyFilters = notifyFilters; + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + FileChanged = null; // Release event listeners + StopWatching(); + } + + /// + /// Stops watching all files. + /// + public void StopWatching() + { + lock (_watcherMap) + { + foreach (var watcher in _watcherMap) + { + StopWatching(watcher.Value); + } + _watcherMap.Clear(); + } + } + + /// + /// Watches the specified files for changes. + /// + /// The file names. + public void Watch(IEnumerable fileNames) + { + if (fileNames is null) + { + return; + } + + foreach (string s in fileNames) + { + Watch(s); + } + } + + public void Watch(string fileName) + { + try + { + var directory = Path.GetDirectoryName(fileName); + var fileFilter = Path.GetFileName(fileName); + directory = Path.GetFullPath(directory); + if (!Directory.Exists(directory)) + { + InternalLogger.Warn("Cannot watch file-filter '{0}' when non-existing directory: {1}", fileFilter, directory); + return; + } + + if (TryAddWatch(fileName, directory, fileFilter)) + { + InternalLogger.Debug("Start watching file-filter '{0}' in directory: {1}", fileFilter, directory); + } + } + catch (Exception ex) + { + InternalLogger.Debug(ex, "Failed to start FileSystemWatcher with file-path: {0}", fileName); + if (LogManager.ThrowExceptions) + throw; + } + } + + private bool TryAddWatch(string fileName, string directory, string fileFilter) + { + lock (_watcherMap) + { + if (_watcherMap.ContainsKey(fileName)) + return false; + + FileSystemWatcher watcher = null; + + try + { + watcher = new FileSystemWatcher + { + Path = directory, + Filter = fileFilter, + NotifyFilter = NotifyFilters + }; + + watcher.Created += OnFileChanged; + watcher.Changed += OnFileChanged; + watcher.Deleted += OnFileChanged; + watcher.Renamed += OnFileChanged; + watcher.Error += OnWatcherError; + watcher.EnableRaisingEvents = true; + + _watcherMap.Add(fileName, watcher); + return true; + } + catch (Exception ex) + { + InternalLogger.Error(ex, "Failed to start FileSystemWatcher with file-filter '{0}' in directory: {1}", fileFilter, directory); + if (ex is System.Security.SecurityException || ex is UnauthorizedAccessException || ex is NotSupportedException || ex is NotImplementedException || ex is PlatformNotSupportedException) + return false; + + if (LogManager.ThrowExceptions) + throw; + + if (watcher != null) + { + StopWatching(watcher); + } + + return false; + } + } + } + + private void StopWatching(FileSystemWatcher watcher) + { + string fileFilter = string.Empty; + string fileDirectory = string.Empty; + + try + { + fileFilter = watcher.Filter; + fileDirectory = watcher.Path; + + InternalLogger.Debug("Stop watching file-filter '{0}' in directory: {1}", fileFilter, fileDirectory); + watcher.EnableRaisingEvents = false; + watcher.Created -= OnFileChanged; + watcher.Changed -= OnFileChanged; + watcher.Deleted -= OnFileChanged; + watcher.Renamed -= OnFileChanged; + watcher.Error -= OnWatcherError; + watcher.Dispose(); + } + catch (Exception ex) + { + InternalLogger.Error(ex, "Failed to stop FileSystemWatcher with file-filter '{0}' in directory: {1}", fileFilter, fileDirectory); + if (LogManager.ThrowExceptions) + throw; + } + } + + private static void OnWatcherError(object source, ErrorEventArgs e) + { + var watcher = source as FileSystemWatcher; + var fileFilter = watcher?.Filter ?? string.Empty; + var fileDirectory = watcher?.Path ?? string.Empty; + InternalLogger.Warn(e.GetException(), "Error from FileSystemWatcher with file-filter '{0}' in directory: {1}", fileFilter, fileDirectory); + } + + private void OnFileChanged(object source, FileSystemEventArgs e) + { + var changed = FileChanged; + if (changed != null) + { + try + { + changed(source, e); + } + catch (Exception ex) + { +#if DEBUG + if (LogManager.ThrowExceptions) + throw; // Throwing exceptions here might crash the entire application (.NET 2.0 behavior) +#endif + InternalLogger.Error(ex, "Error handling event from FileSystemWatcher with file-filter: '{0}' in directory: {1}", e.Name, e.FullPath); + } + } + } + } +} diff --git a/src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj b/src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj new file mode 100644 index 0000000000..afb7fe325f --- /dev/null +++ b/src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj @@ -0,0 +1,80 @@ + + + + net35;net46;netstandard2.0 + + NLog.AutoReloadConfig + NLog + NLog.AutoReloadConfig enables AutoReload support for NLog.config XML-files + NLog.AutoReloadConfig v$(ProductVersion) + $(ProductVersion) + Jarek Kowalski,Kim Christensen,Julian Verdurmen + $([System.DateTime]::Now.ToString(yyyy)) + Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ + + + AutoReload Docs: + https://github.com/NLog/NLog/wiki/Configuration-file#automatic-reconfiguration + + README.md + NLog;AutoReload;logging;log + N.png + https://nlog-project.org/ + BSD-3-Clause + git + https://github.com/NLog/NLog.git + + true + 5.0.0.0 + ..\NLog.snk + true + + true + true + true + true + true + copyused + + + + NLog.AutoReloadConfig for .NET Framework 4.6 + true + + + + NLog.AutoReloadConfig for .NET Framework 4.5 + true + + + + NLog.AutoReloadConfig for .NET Framework 3.5 + true + + + + NLog.AutoReloadConfig for NetStandard 2.0 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NLog.AutoReloadConfig/README.md b/src/NLog.AutoReloadConfig/README.md new file mode 100644 index 0000000000..25c9a4be1f --- /dev/null +++ b/src/NLog.AutoReloadConfig/README.md @@ -0,0 +1,15 @@ +# NLog AutoReload Config + +NLog AutoReload Config Monitor for activating AutoReload support for `NLog.config` XML-files. + +If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) + +## Register Extension + +AutoReload Config Monitor must be enabled from code using [fluent configuration API](https://github.com/NLog/NLog/wiki/Fluent-Configuration-API): + +```csharp +LogManager.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(); +``` + +Notice if using `appsettings.json` for NLog configuration, then this extension is not required for supporting `AutoReload`. \ No newline at end of file diff --git a/src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs b/src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs new file mode 100644 index 0000000000..d390a74687 --- /dev/null +++ b/src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs @@ -0,0 +1,267 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// +// + +namespace NLog +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading; + using NLog.Common; + using NLog.Config; + using NLog.Internal; + + /// + /// Setup()-extension to activate AutoReload support by monitoring + /// + public static class SetupBuilderAutoReloadExtensions + { + private static readonly Dictionary _watchers = new Dictionary(); + + /// + /// Activates AutoReload support by monitoring + /// + /// + /// Hooks into and setup file-monitoring for NLog.config file-changes. + /// + public static ISetupBuilder SetupMonitorForAutoReload(this ISetupBuilder setupBuilder) + { + AutoReloadConfigFileWatcher fileWatcher = null; + lock (_watchers) + { + _watchers.TryGetValue(setupBuilder.LogFactory, out fileWatcher); + } + + if (fileWatcher is null || fileWatcher.IsDisposed) + { + InternalLogger.Info("AutoReload Config File Monitor starting"); + fileWatcher = new AutoReloadConfigFileWatcher(setupBuilder.LogFactory); + lock (_watchers) + { + _watchers[setupBuilder.LogFactory] = fileWatcher; + } + } + + setupBuilder.LoadConfiguration(cfg => + { + var fileNamesToWatch = cfg.Configuration.FileNamesToWatch?.ToList(); + if (fileNamesToWatch?.Count > 0 || cfg.Configuration is XmlLoggingConfiguration) + { + fileWatcher.RefreshFileWatcher(fileNamesToWatch ?? System.Linq.Enumerable.Empty()); + } + else if (cfg.Configuration.LoggingRules.Count == 0 && cfg.Configuration.AllTargets.Count == 0) + { + cfg.Configuration = null; + } + }); + return setupBuilder; + } + + private sealed class AutoReloadConfigFileWatcher : IDisposable + { + private readonly LogFactory _logFactory; + private readonly MultiFileWatcher _fileWatcher = new MultiFileWatcher(); + private readonly object _lockObject = new object(); + private Timer _reloadTimer; + private bool _isDisposing; + + internal bool IsDisposed => _isDisposing; + + public AutoReloadConfigFileWatcher(LogFactory logFactory) + { + _logFactory = logFactory; + _logFactory.ConfigurationChanged += LogFactory_ConfigurationChanged; + _fileWatcher.FileChanged += FileWatcher_FileChanged; + } + + private void LogFactory_ConfigurationChanged(object sender, LoggingConfigurationChangedEventArgs e) + { + try + { + if (_isDisposing) + return; + + if (e.ActivatedConfiguration is null) + { + InternalLogger.Info("AutoReload Config File Monitor stopping, since no active configuration"); + Dispose(); + } + else + { + InternalLogger.Debug("AutoReload Config File Monitor refreshing after configuration changed"); + var fileNamesToWatch = e.ActivatedConfiguration?.FileNamesToWatch ?? Enumerable.Empty(); + _fileWatcher.Watch(fileNamesToWatch); + } + } + catch (Exception ex) + { + InternalLogger.Error(ex, "AutoReload Config File Monitor failed to refresh after configuration changed."); + + if (LogManager.ThrowExceptions || _logFactory.ThrowExceptions) + { + throw; + } + } + } + + private void FileWatcher_FileChanged(object sender, System.IO.FileSystemEventArgs e) + { + lock (_lockObject) + { + if (_isDisposing) + return; + + var reloadTimer = _reloadTimer; + if (reloadTimer is null) + { + var currentConfig = _logFactory.Configuration; + if (currentConfig is null) + return; + + _reloadTimer = new Timer((s) => ReloadTimer(s), currentConfig, 1000, Timeout.Infinite); + } + else + { + reloadTimer.Change(1000, Timeout.Infinite); + } + } + } + + private void ReloadTimer(object state) + { + if (_isDisposing) + { + return; //timer was disposed already. + } + + LoggingConfiguration oldConfig = (LoggingConfiguration)state; + + InternalLogger.Info("AutoReload Config File Monitor reloading configuration..."); + + lock (_lockObject) + { + if (_isDisposing) + { + return; //timer was disposed already. + } + + var currentTimer = _reloadTimer; + if (currentTimer != null) + { + _reloadTimer = null; + currentTimer.Dispose(); + } + } + + LoggingConfiguration newConfig = null; + + try + { + var currentConfig = _logFactory.Configuration; + if (!ReferenceEquals(currentConfig, oldConfig)) + { + InternalLogger.Debug("AutoReload Config File Monitor skipping reload, since existing NLog config has changed."); + return; + } + + newConfig = oldConfig.Reload(); + if (newConfig is null || ReferenceEquals(newConfig, oldConfig)) + { + InternalLogger.Debug("AutoReload Config File Monitor skipping reload, since new configuration has not changed."); + return; + } + + currentConfig = _logFactory.Configuration; + if (!ReferenceEquals(currentConfig, oldConfig)) + { + InternalLogger.Debug("AutoReload Config File Monitor skipping reload, since existing NLog config has changed."); + return; + } + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "AutoReload Config File Monitor failed to reload NLog LoggingConfiguration."); + return; + } + + try + { + TryUnwatchConfigFile(); + _logFactory.Configuration = newConfig; // Will trigger LogFactory_ConfigurationChanged + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "AutoReload Config File Monitor failed to activate new NLog LoggingConfiguration."); + _fileWatcher.Watch(oldConfig.FileNamesToWatch); + } + } + + public void RefreshFileWatcher(IEnumerable fileNamesToWatch) + { + _fileWatcher.Watch(fileNamesToWatch); + } + + public void Dispose() + { + _isDisposing = true; + _logFactory.ConfigurationChanged -= LogFactory_ConfigurationChanged; + _fileWatcher.FileChanged -= FileWatcher_FileChanged; + lock (_lockObject) + { + var reloadTimer = _reloadTimer; + _reloadTimer = null; + reloadTimer?.Dispose(); + } + _fileWatcher.Dispose(); + } + + private void TryUnwatchConfigFile() + { + try + { + _fileWatcher?.StopWatching(); + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "AutoReload Config File Monitor failed to stop file watcher."); + + if (LogManager.ThrowExceptions || _logFactory.ThrowExceptions) + { + throw; + } + } + } + } + } +} diff --git a/src/NLog.Targets.Mail/README.md b/src/NLog.Targets.Mail/README.md index 805ce96419..0f8613b8e0 100644 --- a/src/NLog.Targets.Mail/README.md +++ b/src/NLog.Targets.Mail/README.md @@ -1,4 +1,4 @@ -# NLog WebService Target +# NLog Mail Target NLog Mail Target for sending email using .NET SmtpClient for each logevent. diff --git a/src/NLog.sln b/src/NLog.sln index d3a54532b4..8d5144d3cb 100644 --- a/src/NLog.sln +++ b/src/NLog.sln @@ -21,6 +21,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManuallyLoadedExtension", " EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0C18FFD9-FD83-4F04-B4B5-91745D2D125E}" ProjectSection(SolutionItems) = preProject + ..\.coderabbit.yaml = ..\.coderabbit.yaml ..\.editorconfig = ..\.editorconfig ..\.gitattributes = ..\.gitattributes ..\.gitignore = ..\.gitignore @@ -62,6 +63,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.RegEx", "NLog.RegEx\NL EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.RegEx.Tests", "..\tests\NLog.RegEx.Tests\NLog.RegEx.Tests.csproj", "{7C77DA2A-DF9A-4F4C-91BF-A593B31D7619}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.AutoReloadConfig", "NLog.AutoReloadConfig\NLog.AutoReloadConfig.csproj", "{FB2DFD9F-5EB1-4BBF-99F3-38C2E997206E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.AutoReloadConfig.Tests", "..\tests\NLog.AutoReloadConfig.Tests\NLog.AutoReloadConfig.Tests.csproj", "{47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -148,6 +153,14 @@ Global {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619}.Release|Any CPU.Build.0 = Release|Any CPU + {FB2DFD9F-5EB1-4BBF-99F3-38C2E997206E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB2DFD9F-5EB1-4BBF-99F3-38C2E997206E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB2DFD9F-5EB1-4BBF-99F3-38C2E997206E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB2DFD9F-5EB1-4BBF-99F3-38C2E997206E}.Release|Any CPU.Build.0 = Release|Any CPU + {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -171,6 +184,8 @@ Global {38828090-4953-4EF5-929C-8063FD4BCCC9} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {A87BFA04-DBFD-44F4-8223-A02AC0E85C7F} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {FB2DFD9F-5EB1-4BBF-99F3-38C2E997206E} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6B4C8E9F-1E2F-4AB3-993A-8776CFA08DC0} diff --git a/src/NLog/Config/AssemblyExtensionLoader.cs b/src/NLog/Config/AssemblyExtensionLoader.cs index 5dfeab39a4..0627c3bf90 100644 --- a/src/NLog/Config/AssemblyExtensionLoader.cs +++ b/src/NLog/Config/AssemblyExtensionLoader.cs @@ -112,7 +112,7 @@ private static Type[] SafeGetTypes(Assembly assembly) } catch (ReflectionTypeLoadException typeLoadException) { - var result = typeLoadException.Types?.Where(t => t != null)?.ToArray() ?? ArrayHelper.Empty(); + var result = typeLoadException.Types?.Where(t => t != null).ToArray() ?? ArrayHelper.Empty(); InternalLogger.Warn(typeLoadException, "Loaded {0} valid types from Assembly: {1}", result.Length, assembly.FullName); foreach (var ex in typeLoadException.LoaderExceptions ?? ArrayHelper.Empty()) { diff --git a/src/NLog/Config/ConfigSectionHandler.cs b/src/NLog/Config/ConfigSectionHandler.cs index e09dd2c0c1..bff2f1e631 100644 --- a/src/NLog/Config/ConfigSectionHandler.cs +++ b/src/NLog/Config/ConfigSectionHandler.cs @@ -53,6 +53,31 @@ public sealed class ConfigSectionHandler : ConfigurationSection { private XmlLoggingConfiguration _config; + /// + /// Gets the default object by parsing + /// the application configuration file (app.exe.config). + /// + public static LoggingConfiguration AppConfig + { + get + { + try + { + object o = System.Configuration.ConfigurationManager.GetSection("nlog"); + return o as LoggingConfiguration; + } + catch (Exception exception) + { + // Load can fail due to an invalid XML file (app.config) etc. + InternalLogger.Error(exception, "Failed loading XML configuration from NLog ConfigSection in application configuration file (app.config / web.config)"); + if (exception.MustBeRethrown()) + throw; + } + + return null; + } + } + /// /// Overriding base implementation to just store /// of the relevant app.config section. diff --git a/src/NLog/Config/IInitializeSucceeded.cs b/src/NLog/Config/IInitializeSucceeded.cs deleted file mode 100644 index b22a1d21fd..0000000000 --- a/src/NLog/Config/IInitializeSucceeded.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.Config -{ - internal interface IInitializeSucceeded - { - /// - /// Did the Initialize Succeeded? true= success, false= error, null = initialize not started yet. - /// - bool? InitializeSucceeded { get; } - } -} diff --git a/src/NLog/Config/ILoggingConfigurationLoader.cs b/src/NLog/Config/ILoggingConfigurationLoader.cs index 0d8f82cdaa..f7c097a605 100644 --- a/src/NLog/Config/ILoggingConfigurationLoader.cs +++ b/src/NLog/Config/ILoggingConfigurationLoader.cs @@ -49,13 +49,6 @@ internal interface ILoggingConfigurationLoader : IDisposable /// NLog configuration (or null if none found) LoggingConfiguration Load(LogFactory logFactory, string filename = null); - /// - /// Notifies when LoggingConfiguration has been successfully applied - /// - /// LogFactory that owns the NLog configuration - /// NLog Config - void Activated(LogFactory logFactory, LoggingConfiguration config); - /// /// Get file paths (including filename) for the possible NLog config files. /// diff --git a/src/NLog/Config/LoggingConfiguration.cs b/src/NLog/Config/LoggingConfiguration.cs index 2689e4fe90..3d2d131640 100644 --- a/src/NLog/Config/LoggingConfiguration.cs +++ b/src/NLog/Config/LoggingConfiguration.cs @@ -490,7 +490,7 @@ public bool RemoveRuleByName(string ruleName) } /// - /// Called by LogManager when one of the log configuration files changes. + /// Loads the NLog LoggingConfiguration from its original source (Ex. read from original config-file after it was updated) /// /// /// A new instance of that represents the updated configuration. diff --git a/src/NLog/Config/LoggingConfigurationFileLoader.cs b/src/NLog/Config/LoggingConfigurationFileLoader.cs index 6bc181fd2a..ea2b137124 100644 --- a/src/NLog/Config/LoggingConfigurationFileLoader.cs +++ b/src/NLog/Config/LoggingConfigurationFileLoader.cs @@ -55,8 +55,17 @@ public LoggingConfigurationFileLoader(IAppEnvironment appEnvironment) _appEnvironment = appEnvironment; } - public virtual LoggingConfiguration Load(LogFactory logFactory, string filename = null) + public LoggingConfiguration Load(LogFactory logFactory, string filename = null) { +#if NETFRAMEWORK + if (string.IsNullOrEmpty(filename)) + { + var appConfig = ConfigSectionHandler.AppConfig; + if (appConfig != null) + return appConfig; + } +#endif + if (string.IsNullOrEmpty(filename) || FilePathLayout.DetectFilePathKind(filename) == FilePathKind.Relative) { return TryLoadFromFilePaths(logFactory, filename); @@ -69,11 +78,6 @@ public virtual LoggingConfiguration Load(LogFactory logFactory, string filename return null; } - public virtual void Activated(LogFactory logFactory, LoggingConfiguration config) - { - // Nothing to do - } - private LoggingConfiguration TryLoadFromFilePaths(LogFactory logFactory, string filename) { #pragma warning disable CS0618 // Type or member is obsolete @@ -139,22 +143,7 @@ private LoggingConfiguration LoadXmlLoggingConfiguration(XmlReader xmlReader, st { try { - var newConfig = new XmlLoggingConfiguration(xmlReader, configFile, logFactory); - if (newConfig.InitializeSucceeded != true) - { - if (ThrowXmlConfigExceptions(configFile, xmlReader, logFactory, out var autoReload)) - { - using (var xmlReaderRetry = _appEnvironment.LoadXmlFile(configFile)) - { - newConfig = new XmlLoggingConfiguration(xmlReaderRetry, configFile, logFactory); // Scan again after having updated LogFactory, to throw correct exception - } - } - else if (autoReload && !newConfig.AutoReload) - { - return CreateEmptyDefaultConfig(configFile, logFactory, autoReload); - } - } - return newConfig; + return new XmlLoggingConfiguration(xmlReader, configFile, logFactory); } catch (Exception ex) { diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index f9ac92d31a..e7b4220444 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -51,6 +51,9 @@ namespace NLog.Config /// /// Loads NLog configuration from /// + /// + /// Make sure to update official NLog.xsd schema, when adding new config-options outside targets/layouts + /// public abstract class LoggingConfigurationParser : LoggingConfiguration { private readonly ServiceRepository _serviceRepository; diff --git a/src/NLog/Config/LoggingConfigurationWatchableFileLoader.cs b/src/NLog/Config/LoggingConfigurationWatchableFileLoader.cs deleted file mode 100644 index d3fdc1733f..0000000000 --- a/src/NLog/Config/LoggingConfigurationWatchableFileLoader.cs +++ /dev/null @@ -1,293 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.Config -{ - using System; - using System.Linq; - using System.Threading; - using NLog.Common; - using NLog.Internal; - using NLog.Internal.Fakeables; - - /// - /// Enables FileWatcher for the currently loaded NLog Configuration File, - /// and supports automatic reload on file modification. - /// - internal sealed class LoggingConfigurationWatchableFileLoader : LoggingConfigurationFileLoader - { - private const int ReconfigAfterFileChangedTimeout = 1000; - private readonly object _lockObject = new object(); - private Timer _reloadTimer; - private MultiFileWatcher _watcher; - private bool _isDisposing; - private LogFactory _logFactory; - - public LoggingConfigurationWatchableFileLoader(IAppEnvironment appEnvironment) - : base(appEnvironment) - { - } - - public override LoggingConfiguration Load(LogFactory logFactory, string filename = null) - { -#if NETFRAMEWORK - if (string.IsNullOrEmpty(filename)) - { - var config = TryLoadFromAppConfig(); - if (config != null) - return config; - } -#endif - - return base.Load(logFactory, filename); - } - - public override void Activated(LogFactory logFactory, LoggingConfiguration config) - { - _logFactory = logFactory; - - TryUnwatchConfigFile(); - - if (config != null) - { - TryWachtingConfigFile(config); - } - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - _isDisposing = true; - - if (_watcher != null) - { - // Disable startup of new reload-timers - _watcher.FileChanged -= ConfigFileChanged; - _watcher.StopWatching(); - } - - var currentTimer = _reloadTimer; - if (currentTimer != null) - { - _reloadTimer = null; - currentTimer.WaitForDispose(TimeSpan.Zero); - } - - // Dispose file-watcher after having dispose timer to avoid race - _watcher?.Dispose(); - } - - base.Dispose(disposing); - } - -#if NETFRAMEWORK - private LoggingConfiguration TryLoadFromAppConfig() - { - try - { - // Try to load default configuration. - return XmlLoggingConfiguration.AppConfig; - } - catch (Exception ex) - { - //loading could fail due to an invalid XML file (app.config) etc. - if (ex.MustBeRethrown()) - { - throw; - } - return null; - } - } -#endif - - internal void ReloadConfigOnTimer(object state) - { - if (_reloadTimer is null && _isDisposing) - { - return; //timer was disposed already. - } - - LoggingConfiguration oldConfig = (LoggingConfiguration)state; - - InternalLogger.Info("Reloading configuration..."); - lock (_lockObject) - { - if (_isDisposing) - { - return; //timer was disposed already. - } - - var currentTimer = _reloadTimer; - if (currentTimer != null) - { - _reloadTimer = null; - currentTimer.WaitForDispose(TimeSpan.Zero); - } - } - - lock (_logFactory._syncRoot) - { - LoggingConfiguration newConfig; - try - { - if (!ReferenceEquals(_logFactory._config, oldConfig)) - { - InternalLogger.Warn("NLog Config changed in between. Not reloading."); - return; - } - - newConfig = oldConfig.Reload(); - if (ReferenceEquals(newConfig, oldConfig)) - return; - - if (newConfig is IInitializeSucceeded config2 && config2.InitializeSucceeded != true) - { - InternalLogger.Warn("NLog Config Reload() failed. Invalid XML?"); - return; - } - } - catch (Exception exception) - { -#if DEBUG - if (exception.MustBeRethrownImmediately()) - { - throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) - } -#endif - InternalLogger.Warn(exception, "NLog configuration failed to reload"); - return; - } - - try - { - TryUnwatchConfigFile(); - _logFactory.Configuration = newConfig; // Triggers LogFactory to call Activated(...) that adds file-watch again - } - catch (Exception exception) - { -#if DEBUG - if (exception.MustBeRethrownImmediately()) - { - throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) - } -#endif - InternalLogger.Warn(exception, "NLog configuration reloaded, failed to be assigned"); - _watcher.Watch(oldConfig.FileNamesToWatch); - } - } - } - - private void ConfigFileChanged(object sender, EventArgs args) - { - InternalLogger.Info("Configuration file change detected! Reloading in {0}ms...", ReconfigAfterFileChangedTimeout); - - // In the rare cases we may get multiple notifications here, - // but we need to reload config only once. - // - // The trick is to schedule the reload in one second after - // the last change notification comes in. - lock (_lockObject) - { - if (_isDisposing) - { - return; - } - - if (_reloadTimer is null) - { - var configuration = _logFactory._config; - if (configuration != null) - { - _reloadTimer = new Timer( - ReloadConfigOnTimer, - configuration, - ReconfigAfterFileChangedTimeout, - Timeout.Infinite); - } - } - else - { - _reloadTimer.Change( - ReconfigAfterFileChangedTimeout, - Timeout.Infinite); - } - } - } - - private void TryWachtingConfigFile(LoggingConfiguration config) - { - try - { - var fileNamesToWatch = config.FileNamesToWatch?.ToList(); - if (fileNamesToWatch?.Count > 0) - { - if (_watcher is null) - { - _watcher = new MultiFileWatcher(); - _watcher.FileChanged += ConfigFileChanged; - } - - _watcher.Watch(fileNamesToWatch); - } - } - catch (Exception exception) - { - if (exception.MustBeRethrown()) - { - throw; - } - - //ToArray needed for .Net 3.5 - InternalLogger.Warn(exception, "Cannot start file watching: {0}", string.Join(",", _logFactory._config.FileNamesToWatch.ToArray())); - } - } - - private void TryUnwatchConfigFile() - { - try - { - _watcher?.StopWatching(); - } - catch (Exception exception) - { - InternalLogger.Error(exception, "Cannot stop file watching."); - - if (exception.MustBeRethrown()) - { - throw; - } - } - } - } -} diff --git a/src/NLog/Config/XmlLoggingConfiguration.cs b/src/NLog/Config/XmlLoggingConfiguration.cs index e9226fabb4..accdf83849 100644 --- a/src/NLog/Config/XmlLoggingConfiguration.cs +++ b/src/NLog/Config/XmlLoggingConfiguration.cs @@ -45,16 +45,12 @@ namespace NLog.Config using NLog.Layouts; /// - /// A class for configuring NLog through an XML configuration file - /// (App.config style or App.nlog style). - /// - /// Parsing of the XML file is also implemented in this class. + /// Loads NLog LoggingConfiguration from xml-file (like app.config) using /// - /// - /// - This class is thread-safe..ToList() is used for that purpose. - /// - Update TemplateXSD.xml for changes outside targets + /// + /// Make sure to update official NLog.xsd schema, when adding new config-options outside targets/layouts /// - public class XmlLoggingConfiguration : LoggingConfigurationParser, IInitializeSucceeded + public class XmlLoggingConfiguration : LoggingConfigurationParser { private readonly Dictionary _fileMustAutoReloadLookup = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -86,38 +82,6 @@ public XmlLoggingConfiguration([NotNull] string fileName, LogFactory logFactory) LoadFromXmlFile(fileName); } - /// - /// Obsolete and replaced by with NLog v4.7. - /// - /// Initializes a new instance of the class. - /// - /// Configuration file to be read. - /// Ignore any errors during configuration. - [Obsolete("Constructor with parameter ignoreErrors has limited effect. Instead use LogManager.ThrowConfigExceptions. Marked obsolete in NLog 4.7")] - [EditorBrowsable(EditorBrowsableState.Never)] - public XmlLoggingConfiguration([NotNull] string fileName, bool ignoreErrors) - : this(fileName, ignoreErrors, LogManager.LogFactory) - { } - - /// - /// Obsolete and replaced by with NLog v4.7. - /// - /// Initializes a new instance of the class. - /// - /// Configuration file to be read. - /// Ignore any errors during configuration. - /// The to which to apply any applicable configuration values. - [Obsolete("Constructor with parameter ignoreErrors has limited effect. Instead use LogManager.ThrowConfigExceptions. Marked obsolete in NLog 4.7")] - [EditorBrowsable(EditorBrowsableState.Never)] - public XmlLoggingConfiguration([NotNull] string fileName, bool ignoreErrors, LogFactory logFactory) - : base(logFactory) - { - using (XmlReader reader = CreateFileReader(fileName)) - { - Initialize(reader, fileName, ignoreErrors); - } - } - /// /// Initializes a new instance of the class. /// @@ -143,38 +107,7 @@ public XmlLoggingConfiguration([NotNull] XmlReader reader, [CanBeNull] string fi public XmlLoggingConfiguration([NotNull] XmlReader reader, [CanBeNull] string fileName, LogFactory logFactory) : base(logFactory) { - Initialize(reader, fileName); - } - - /// - /// Obsolete and replaced by with NLog v4.7. - /// - /// Initializes a new instance of the class. - /// - /// containing the configuration section. - /// Name of the file that contains the element (to be used as a base for including other files). null is allowed. - /// Ignore any errors during configuration. - [Obsolete("Constructor with parameter ignoreErrors has limited effect. Instead use LogManager.ThrowConfigExceptions. Marked obsolete in NLog 4.7")] - [EditorBrowsable(EditorBrowsableState.Never)] - public XmlLoggingConfiguration([NotNull] XmlReader reader, [CanBeNull] string fileName, bool ignoreErrors) - : this(reader, fileName, ignoreErrors, LogManager.LogFactory) - { } - - /// - /// Obsolete and replaced by with NLog v4.7. - /// - /// Initializes a new instance of the class. - /// - /// containing the configuration section. - /// Name of the file that contains the element (to be used as a base for including other files). null is allowed. - /// Ignore any errors during configuration. - /// The to which to apply any applicable configuration values. - [Obsolete("Constructor with parameter ignoreErrors has limited effect. Instead use LogManager.ThrowConfigExceptions. Marked obsolete in NLog 4.7")] - [EditorBrowsable(EditorBrowsableState.Never)] - public XmlLoggingConfiguration([NotNull] XmlReader reader, [CanBeNull] string fileName, bool ignoreErrors, LogFactory logFactory) - : base(logFactory) - { - Initialize(reader, fileName, ignoreErrors); + ParseFromXmlReader(reader, fileName); } /// @@ -208,28 +141,8 @@ public static XmlLoggingConfiguration CreateFromXmlString(string xml, LogFactory return new XmlLoggingConfiguration(xml, string.Empty, logFactory); } -#if NETFRAMEWORK /// - /// Gets the default object by parsing - /// the application configuration file (app.exe.config). - /// - public static LoggingConfiguration AppConfig - { - get - { - object o = System.Configuration.ConfigurationManager.GetSection("nlog"); - return o as LoggingConfiguration; - } - } -#endif - - /// - /// Did the Succeeded? true= success, false= error, null = initialize not started yet. - /// - public bool? InitializeSucceeded { get; private set; } - - /// - /// Gets or sets a value indicating whether all of the configuration files + /// Gets or sets a value indicating whether any of the configuration files /// should be watched for changes and reloaded automatically when changed. /// public bool AutoReload @@ -239,7 +152,7 @@ public bool AutoReload if (_fileMustAutoReloadLookup.Count == 0) return false; else - return _fileMustAutoReloadLookup.Values.All(mustAutoReload => mustAutoReload); + return _fileMustAutoReloadLookup.Values.Any(mustAutoReload => mustAutoReload); } set { @@ -258,12 +171,15 @@ public override IEnumerable FileNamesToWatch { get { - return _fileMustAutoReloadLookup.Where(entry => entry.Value).Select(entry => entry.Key); + if (_fileMustAutoReloadLookup.Count == 0) + return ArrayHelper.Empty(); + else + return _fileMustAutoReloadLookup.Where(entry => entry.Value).Select(entry => entry.Key); } } /// - /// Re-reads the original configuration file and returns the new object. + /// Loads the NLog LoggingConfiguration from its original configuration file and returns the new object. /// /// The newly loaded instance. /// Must assign the returned object to LogManager.Configuration to activate it @@ -322,7 +238,7 @@ private void LoadFromXmlFile(string fileName) { using (XmlReader reader = CreateFileReader(fileName)) { - Initialize(reader, fileName); + ParseFromXmlReader(reader, fileName); } } @@ -332,7 +248,7 @@ internal void LoadFromXmlContent(string xmlContent, string fileName) { using (XmlReader reader = XmlReader.Create(stringReader)) { - Initialize(reader, fileName); + ParseFromXmlReader(reader, fileName); } } } @@ -357,12 +273,10 @@ private XmlReader CreateFileReader(string fileName) /// /// containing the configuration section. /// Name of the file that contains the element (to be used as a base for including other files). null is allowed. - /// Ignore any errors during configuration. - private void Initialize([NotNull] XmlReader reader, [CanBeNull] string fileName, bool ignoreErrors = false) + private void ParseFromXmlReader([NotNull] XmlReader reader, [CanBeNull] string fileName) { try { - InitializeSucceeded = null; _originalFileName = string.IsNullOrEmpty(fileName) ? fileName : GetFileLookupKey(fileName); reader.MoveToContent(); var content = new XmlLoggingConfigurationElement(reader); @@ -375,21 +289,12 @@ private void Initialize([NotNull] XmlReader reader, [CanBeNull] string fileName, { ParseTopLevel(content, null, autoReloadDefault: false); } - InitializeSucceeded = true; } catch (Exception exception) { - InitializeSucceeded = false; - - if (exception.MustBeRethrownImmediately()) - { - throw; - } - var configurationException = new NLogConfigurationException($"Exception when loading configuration {fileName}", exception); InternalLogger.Error(exception, configurationException.Message); - if (!ignoreErrors && (LogFactory.ThrowConfigExceptions ?? LogFactory.ThrowExceptions || configurationException.MustBeRethrown())) - throw configurationException; + throw configurationException; } } diff --git a/src/NLog/Internal/MultiFileWatcher.cs b/src/NLog/Internal/MultiFileWatcher.cs index 658ae44571..6b1463bb66 100644 --- a/src/NLog/Internal/MultiFileWatcher.cs +++ b/src/NLog/Internal/MultiFileWatcher.cs @@ -105,23 +105,6 @@ public void StopWatching(string fileName) } } - /// - /// Watches the specified files for changes. - /// - /// The file names. - public void Watch(IEnumerable fileNames) - { - if (fileNames is null) - { - return; - } - - foreach (string s in fileNames) - { - Watch(s); - } - } - public void Watch(string fileName) { try @@ -140,9 +123,11 @@ public void Watch(string fileName) InternalLogger.Debug("Start watching file-filter '{0}' in directory: {1}", fileFilter, directory); } } - catch (System.Security.SecurityException ex) + catch (Exception ex) { InternalLogger.Debug(ex, "Failed to start FileSystemWatcher with file-path: {0}", fileName); + if (LogManager.ThrowExceptions) + throw; } } @@ -177,6 +162,9 @@ private bool TryAddWatch(string fileName, string directory, string fileFilter) catch (Exception ex) { InternalLogger.Error(ex, "Failed to start FileSystemWatcher with file-filter '{0}' in directory: {1}", fileFilter, directory); + if (ex is System.Security.SecurityException || ex is UnauthorizedAccessException || ex is NotSupportedException || ex is NotImplementedException || ex is PlatformNotSupportedException) + return false; + if (ex.MustBeRethrown()) throw; diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 6164c1b8af..72cfb20547 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -101,7 +101,7 @@ static LogFactory() /// Initializes a new instance of the class. /// public LogFactory() - : this(new LoggingConfigurationWatchableFileLoader(DefaultAppEnvironment)) + : this(new LoggingConfigurationFileLoader(DefaultAppEnvironment)) { _serviceRepository.TypeRegistered += ServiceRepository_TypeRegistered; RefreshMessageFormatter(); @@ -236,7 +236,6 @@ public LoggingConfiguration Configuration { _config = value; _configLoaded = false; - _configLoader.Activated(this, value); } else { @@ -260,7 +259,6 @@ private void ActivateLoggingConfiguration(LoggingConfiguration config) _config = config; _configLoaded = true; _config.OnConfigurationAssigned(this); - _configLoader.Activated(this, _config); _config.Dump(); ReconfigExistingLoggers(); InternalLogger.Info("Configuration initialized."); diff --git a/src/NLog/SetupBuilderExtensions.cs b/src/NLog/SetupBuilderExtensions.cs index cda9b10d5e..e288e52b49 100644 --- a/src/NLog/SetupBuilderExtensions.cs +++ b/src/NLog/SetupBuilderExtensions.cs @@ -37,6 +37,7 @@ namespace NLog using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; + using NLog.Common; using NLog.Config; using NLog.Internal; @@ -234,12 +235,36 @@ public static ISetupBuilder LoadConfigurationFromAssemblyResource(this ISetupBui /// Logevents produced during the configuration-reload can become lost, as targets are unavailable while closing and initializing. public static ISetupBuilder ReloadConfiguration(this ISetupBuilder setupBuilder) { - var newConfig = setupBuilder.LogFactory._config?.Reload(); - if (newConfig is null || (newConfig as IInitializeSucceeded)?.InitializeSucceeded == false) + try + { + InternalLogger.Debug("Reloading NLog LoggingConfiguration"); + var newConfig = setupBuilder.LogFactory._config?.Reload(); + if (newConfig is null) + return setupBuilder; + + setupBuilder.LogFactory.Configuration = newConfig; return setupBuilder; + } + catch (NLogConfigurationException ex) + { + InternalLogger.Error(ex, "Failed to reload NLog LoggingConfiguration"); + if (ex.MustBeRethrown() || (setupBuilder.LogFactory.ThrowConfigExceptions ?? setupBuilder.LogFactory.ThrowExceptions)) + { + throw; + } - setupBuilder.LogFactory.Configuration = newConfig; - return setupBuilder; + return setupBuilder; + } + catch (Exception ex) + { + InternalLogger.Error(ex, "Failed to reload NLog LoggingConfiguration"); + if (ex.MustBeRethrownImmediately()) + { + throw; + } + + return setupBuilder; + } } } } diff --git a/tests/NLog.AutoReloadConfig.Tests/AutoReloadTests.cs b/tests/NLog.AutoReloadConfig.Tests/AutoReloadTests.cs new file mode 100644 index 0000000000..ee4299e4af --- /dev/null +++ b/tests/NLog.AutoReloadConfig.Tests/AutoReloadTests.cs @@ -0,0 +1,589 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + + +namespace NLog.AutoReloadConfig.Tests +{ + using System; + using System.IO; + using System.Threading; + using NLog.Config; + using NLog.Targets; + using Xunit; + + public class AutoReloadTests + { + public AutoReloadTests() + { + LogManager.ThrowExceptions = true; + } + + [Fact] + public void TestNoAutoReload() + { + string config1 = @" + + + "; + string config2 = @" + + + "; + + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var logFactory = new LogFactory(); + + try + { + Directory.CreateDirectory(tempDir); + + string configFilePath = Path.Combine(tempDir, nameof(TestNoAutoReload) + ".nlog"); + WriteConfigFile(configFilePath, config1); + + var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(configFilePath).GetCurrentClassLogger(); + + Assert.False(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + + logger.Debug("aaa"); + AssertDebugLastMessage("aaa", logFactory); + + ChangeAndReloadConfigFile(logFactory, configFilePath, config2, assertDidReload: false); + + logger.Debug("bbb"); + // Assert that config1 is still loaded. + AssertDebugLastMessage("bbb", logFactory); + } + finally + { + logFactory.Shutdown(); + + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void TestAutoReloadOnFileChange() + { + string config1 = @" + + + "; + string config2 = @" + + + "; + string badConfig = @" + + "; + + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var logFactory = new LogFactory(); + + try + { + Directory.CreateDirectory(tempDir); + + string configFilePath = Path.Combine(tempDir, nameof(TestAutoReloadOnFileChange) + ".nlog"); + WriteConfigFile(configFilePath, config1); + + var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(configFilePath).GetCurrentClassLogger(); + + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + + logger.Debug("aaa"); + AssertDebugLastMessage("aaa", logFactory); + + ChangeAndReloadConfigFile(logFactory, configFilePath, badConfig, assertDidReload: false); + + logger.Debug("bbb"); + // Assert that config1 is still loaded. + AssertDebugLastMessage("bbb", logFactory); + + ChangeAndReloadConfigFile(logFactory, configFilePath, config2); + + logger.Debug("ccc"); + // Assert that config2 is loaded. + AssertDebugLastMessage("[ccc]", logFactory); + } + finally + { + logFactory.Shutdown(); + + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void TestAutoReloadOnFileMove() + { +#if !NETFRAMEWORK || MONO + if (IsLinux()) + { + Console.WriteLine("[SKIP] AutoReloadTests.TestAutoReloadOnFileMove because we are running in Travis"); + return; + } +#endif + + string config1 = @" + + + "; + string config2 = @" + + + "; + + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var logFactory = new LogFactory(); + + try + { + Directory.CreateDirectory(tempDir); + + string configFilePath = Path.Combine(tempDir, "reload.nlog"); + WriteConfigFile(configFilePath, config1); + string otherFilePath = Path.Combine(tempDir, "other.nlog"); + + var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(configFilePath).GetCurrentClassLogger(); + + logger.Debug("aaa"); + AssertDebugLastMessage("aaa", logFactory); + + using (var reloadWaiter = new ConfigurationReloadWaiter(logFactory)) + { + File.Move(configFilePath, otherFilePath); + reloadWaiter.WaitForReload(); + } + + logger.Debug("bbb"); + // Assert that config1 is still loaded. + AssertDebugLastMessage("bbb", logFactory); + + WriteConfigFile(otherFilePath, config2); + using (var reloadWaiter = new ConfigurationReloadWaiter(logFactory)) + { + File.Move(otherFilePath, configFilePath); + + reloadWaiter.WaitForReload(); + Assert.True(reloadWaiter.DidReload); + } + + logger.Debug("ccc"); + // Assert that config2 is loaded. + AssertDebugLastMessage("[ccc]", logFactory); + } + finally + { + logFactory.Shutdown(); + + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + protected static bool IsLinux() + { + var val = Environment.GetEnvironmentVariable("WINDIR"); + return string.IsNullOrEmpty(val); + } + + [Fact] + public void TestAutoReloadOnFileCopy() + { + string config1 = @" + + + "; + string config2 = @" + + + "; + + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var logFactory = new LogFactory(); + + try + { + Directory.CreateDirectory(tempPath); + + string configFilePath = Path.Combine(tempPath, "reload.nlog"); + WriteConfigFile(configFilePath, config1); + string otherFilePath = Path.Combine(tempPath, "other.nlog"); + + var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(configFilePath).GetCurrentClassLogger(); + + logger.Debug("aaa"); + AssertDebugLastMessage("aaa", logFactory); + + using (var reloadWaiter = new ConfigurationReloadWaiter(logFactory)) + { + File.Delete(configFilePath); + reloadWaiter.WaitForReload(); + } + + logger.Debug("bbb"); + // Assert that config1 is still loaded. + AssertDebugLastMessage("bbb", logFactory); + + WriteConfigFile(otherFilePath, config2); + using (var reloadWaiter = new ConfigurationReloadWaiter(logFactory)) + { + File.Copy(otherFilePath, configFilePath); + File.Delete(otherFilePath); + + reloadWaiter.WaitForReload(); + Assert.True(reloadWaiter.DidReload); + } + + logger.Debug("ccc"); + // Assert that config2 is loaded. + AssertDebugLastMessage("[ccc]", logFactory); + } + finally + { + logFactory.Shutdown(); + + if (Directory.Exists(tempPath)) + Directory.Delete(tempPath, true); + } + } + + [Fact] + public void TestIncludedConfigNoReload() + { + string mainConfig1 = @" + + + "; + string mainConfig2 = @" + + + "; + string includedConfig1 = @" + + "; + string includedConfig2 = @" + + "; + + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var logFactory = new LogFactory(); + + try + { + Directory.CreateDirectory(tempDir); + + string mainConfigFilePath = Path.Combine(tempDir, "main.nlog"); + WriteConfigFile(mainConfigFilePath, mainConfig1); + + string includedConfigFilePath = Path.Combine(tempDir, "included.nlog"); + WriteConfigFile(includedConfigFilePath, includedConfig1); + + var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(mainConfigFilePath).GetCurrentClassLogger(); + + logger.Debug("aaa"); + AssertDebugLastMessage("aaa", logFactory); + + ChangeAndReloadConfigFile(logFactory, mainConfigFilePath, mainConfig2, assertDidReload: false); + + logger.Debug("bbb"); + // Assert that mainConfig1 is still loaded. + AssertDebugLastMessage("bbb", logFactory); + + WriteConfigFile(mainConfigFilePath, mainConfig1); + ChangeAndReloadConfigFile(logFactory, includedConfigFilePath, includedConfig2, assertDidReload: false); + + logger.Debug("ccc"); + // Assert that includedConfig1 is still loaded. + AssertDebugLastMessage("ccc", logFactory); + } + finally + { + logFactory.Shutdown(); + + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void TestIncludedConfigReload() + { + string mainConfig1 = @" + + + "; + string mainConfig2 = @" + + + "; + string includedConfig1 = @" + + "; + string includedConfig2 = @" + + "; + + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var logFactory = new LogFactory(); + + try + { + Directory.CreateDirectory(tempDir); + + string mainConfigFilePath = Path.Combine(tempDir, "main.nlog"); + WriteConfigFile(mainConfigFilePath, mainConfig1); + + string includedConfigFilePath = Path.Combine(tempDir, "included.nlog"); + WriteConfigFile(includedConfigFilePath, includedConfig1); + + var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(mainConfigFilePath).GetCurrentClassLogger(); + + logger.Debug("aaa"); + AssertDebugLastMessage("aaa", logFactory); + + ChangeAndReloadConfigFile(logFactory, mainConfigFilePath, mainConfig2, assertDidReload: false); + + logger.Debug("bbb"); + // Assert that mainConfig1 is still loaded. + AssertDebugLastMessage("bbb", logFactory); + + WriteConfigFile(mainConfigFilePath, mainConfig1); + ChangeAndReloadConfigFile(logFactory, includedConfigFilePath, includedConfig2); + + logger.Debug("ccc"); + // Assert that includedConfig2 is loaded. + AssertDebugLastMessage("[ccc]", logFactory); + } + finally + { + logFactory.Shutdown(); + + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void TestMainConfigReload() + { + string mainConfig1 = @" + + + "; + string mainConfig2 = @" + + + "; + string included1Config = @" + + "; + string included2Config1 = @" + + "; + string included2Config2 = @" + + "; + + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var logFactory = new LogFactory(); + + try + { + Directory.CreateDirectory(tempDir); + + string mainConfigFilePath = Path.Combine(tempDir, "main.nlog"); + WriteConfigFile(mainConfigFilePath, mainConfig1); + + string included1ConfigFilePath = Path.Combine(tempDir, "included.nlog"); + WriteConfigFile(included1ConfigFilePath, included1Config); + + string included2ConfigFilePath = Path.Combine(tempDir, "included2.nlog"); + WriteConfigFile(included2ConfigFilePath, included2Config1); + + var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(mainConfigFilePath).GetCurrentClassLogger(); + + logger.Debug("aaa"); + AssertDebugLastMessage("aaa", logFactory); + + ChangeAndReloadConfigFile(logFactory, mainConfigFilePath, mainConfig2); + + logger.Debug("bbb"); + // Assert that mainConfig2 is loaded (which refers to included2.nlog). + AssertDebugLastMessage("[bbb]", logFactory); + + ChangeAndReloadConfigFile(logFactory, included2ConfigFilePath, included2Config2); + + logger.Debug("ccc"); + // Assert that included2Config2 is loaded. + AssertDebugLastMessage("(ccc)", logFactory); + } + finally + { + logFactory.Shutdown(); + + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void TestMainConfigReloadIncludedConfigNoReload() + { + string mainConfig1 = @" + + + "; + string mainConfig2 = @" + + + "; + string included1Config = @" + + "; + string included2Config1 = @" + + "; + string included2Config2 = @" + + "; + + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + + var logFactory = new LogFactory(); + + try + { + Directory.CreateDirectory(tempDir); + + string mainConfigFilePath = Path.Combine(tempDir, "main.nlog"); + WriteConfigFile(mainConfigFilePath, mainConfig1); + + string included1ConfigFilePath = Path.Combine(tempDir, "included.nlog"); + WriteConfigFile(included1ConfigFilePath, included1Config); + + string included2ConfigFilePath = Path.Combine(tempDir, "included2.nlog"); + WriteConfigFile(included2ConfigFilePath, included2Config1); + + var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(mainConfigFilePath).GetCurrentClassLogger(); + + logger.Debug("aaa"); + AssertDebugLastMessage("aaa", logFactory); + + ChangeAndReloadConfigFile(logFactory, mainConfigFilePath, mainConfig2); + + logger.Debug("bbb"); + // Assert that mainConfig2 is loaded (which refers to included2.nlog). + AssertDebugLastMessage("[bbb]", logFactory); + + ChangeAndReloadConfigFile(logFactory, included2ConfigFilePath, included2Config2, assertDidReload: false); + + logger.Debug("ccc"); + // Assert that included2Config1 is still loaded. + AssertDebugLastMessage("[ccc]", logFactory); + } + finally + { + logFactory.Shutdown(); + + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + private static void WriteConfigFile(string configFilePath, string config) + { + using (StreamWriter writer = File.CreateText(configFilePath)) + writer.Write(config); + } + + private static void ChangeAndReloadConfigFile(LogFactory logFactory, string configFilePath, string config, bool assertDidReload = true) + { + using (var reloadWaiter = new ConfigurationReloadWaiter(logFactory)) + { + WriteConfigFile(configFilePath, config); + reloadWaiter.WaitForReload(); + + if (assertDidReload) + Assert.True(reloadWaiter.DidReload, $"Config '{configFilePath}' did not reload."); + } + } + + protected static void AssertDebugLastMessage(string msg, LogFactory logFactory) + { + var debugTarget = logFactory.Configuration.FindTargetByName("debug"); + Assert.Equal(msg, debugTarget.LastMessage); + } + + private sealed class ConfigurationReloadWaiter : IDisposable + { + private readonly ManualResetEvent _counterEvent = new ManualResetEvent(false); + private readonly LogFactory _logFactory; + + public ConfigurationReloadWaiter(LogFactory logFactory) + { + _logFactory = logFactory; + _logFactory.ConfigurationChanged += SignalCounterEvent(_counterEvent); + } + + public bool DidReload => _counterEvent.WaitOne(0); + + public void Dispose() + { + _logFactory.ConfigurationChanged -= SignalCounterEvent(_counterEvent); + } + + public void WaitForReload() + { + _counterEvent.WaitOne(3000); // Handle Timer-delay of 1 sec + } + + private static EventHandler SignalCounterEvent(ManualResetEvent counterEvent) + { + return (sender, e) => + { + counterEvent.Set(); + }; + } + } + } +} diff --git a/tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj b/tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj new file mode 100644 index 0000000000..07399530c0 --- /dev/null +++ b/tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj @@ -0,0 +1,26 @@ + + + + 17.0 + net462 + net462;net6.0 + + false + + Full + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/tests/NLog.UnitTests/Config/ReloadTests.cs b/tests/NLog.UnitTests/Config/ReloadTests.cs index c8c7b25323..19499dd76c 100644 --- a/tests/NLog.UnitTests/Config/ReloadTests.cs +++ b/tests/NLog.UnitTests/Config/ReloadTests.cs @@ -31,14 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // - -#if !MONO - namespace NLog.UnitTests.Config { using System; using System.IO; - using System.Threading; + using System.Linq; using NLog.Config; using Xunit; @@ -52,60 +49,15 @@ public ReloadTests() } } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void TestNoAutoReload(bool useExplicitFileLoading) - { - string config1 = @" - - - "; - string config2 = @" - - - "; - - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempDir); - - string configFilePath = Path.Combine(tempDir, "noreload.nlog"); - WriteConfigFile(configFilePath, config1); - - try - { - SetLogManagerConfiguration(useExplicitFileLoading, configFilePath); - - Assert.False(((XmlLoggingConfiguration)LogManager.Configuration).AutoReload); - - var logger = LogManager.GetLogger("A"); - logger.Debug("aaa"); - AssertDebugLastMessage("debug", "aaa"); - - ChangeAndReloadConfigFile(configFilePath, config2, assertDidReload: false); - - logger.Debug("bbb"); - // Assert that config1 is still loaded. - AssertDebugLastMessage("debug", "bbb"); - } - finally - { - if (Directory.Exists(tempDir)) - Directory.Delete(tempDir, true); - } - } - - private static void SetLogManagerConfiguration(bool useExplicitFileLoading, string configFilePath) + private static void SetLogManagerConfiguration(LogFactory logFactory, bool useExplicitFileLoading, string configFilePath) { if (useExplicitFileLoading) { - LogManager.Configuration = new XmlLoggingConfiguration(configFilePath); + logFactory.Configuration = new XmlLoggingConfiguration(configFilePath); } else { -#pragma warning disable CS0618 // Type or member is obsolete - LogManager.LogFactory.SetCandidateConfigFilePaths(new string[] { configFilePath }); -#pragma warning restore CS0618 // Type or member is obsolete + logFactory.Setup().LoadConfigurationFromFile(new string[] { configFilePath }); } } @@ -114,13 +66,6 @@ private static void SetLogManagerConfiguration(bool useExplicitFileLoading, stri [InlineData(false)] public void TestAutoReloadOnFileChange(bool useExplicitFileLoading) { -#if !NETFRAMEWORK || MONO - if (IsLinux()) - { - Console.WriteLine("[SKIP] ReloadTests.TestAutoReloadOnFileChange because we are running in Travis"); - return; - } -#endif string config1 = @" @@ -134,35 +79,47 @@ public void TestAutoReloadOnFileChange(bool useExplicitFileLoading) "; string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempDir); - string configFilePath = Path.Combine(tempDir, "reload.nlog"); - WriteConfigFile(configFilePath, config1); + var logFactory = new LogFactory(); try { - SetLogManagerConfiguration(useExplicitFileLoading, configFilePath); + Directory.CreateDirectory(tempDir); - Assert.True(((XmlLoggingConfiguration)LogManager.Configuration).AutoReload); + string configFilePath = Path.Combine(tempDir, "reload.nlog"); + WriteConfigFile(configFilePath, config1); - var logger = LogManager.GetLogger("A"); + SetLogManagerConfiguration(logFactory, useExplicitFileLoading, configFilePath); + + var logger = logFactory.GetCurrentClassLogger(); logger.Debug("aaa"); - AssertDebugLastMessage("debug", "aaa"); + AssertDebugLastMessage("debug", "aaa", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Single(logFactory.Configuration.FileNamesToWatch); + Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); - ChangeAndReloadConfigFile(configFilePath, badConfig, assertDidReload: false); + WriteConfigFileAndReload(logFactory, configFilePath, badConfig); logger.Debug("bbb"); // Assert that config1 is still loaded. - AssertDebugLastMessage("debug", "bbb"); + AssertDebugLastMessage("debug", "bbb", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Single(logFactory.Configuration.FileNamesToWatch); + Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); - ChangeAndReloadConfigFile(configFilePath, config2); + WriteConfigFileAndReload(logFactory, configFilePath, config2); logger.Debug("ccc"); // Assert that config2 is loaded. - AssertDebugLastMessage("debug", "[ccc]"); + AssertDebugLastMessage("debug", "[ccc]", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Single(logFactory.Configuration.FileNamesToWatch); + Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); } finally { + logFactory.Shutdown(); + if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); } @@ -174,14 +131,6 @@ public void TestAutoReloadOnFileChange(bool useExplicitFileLoading) public void TestAutoReloadOnFileMove(bool useExplicitFileLoading) { -#if !NETFRAMEWORK || MONO - if (IsLinux()) - { - Console.WriteLine("[SKIP] ReloadTests.TestAutoReloadOnFileMove because we are running in Travis"); - return; - } -#endif - string config1 = @" @@ -192,45 +141,50 @@ public void TestAutoReloadOnFileMove(bool useExplicitFileLoading) "; string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempDir); - string configFilePath = Path.Combine(tempDir, "reload.nlog"); - WriteConfigFile(configFilePath, config1); - string otherFilePath = Path.Combine(tempDir, "other.nlog"); + var logFactory = new LogFactory(); try { - SetLogManagerConfiguration(useExplicitFileLoading, configFilePath); + Directory.CreateDirectory(tempDir); - var logger = LogManager.GetLogger("A"); - logger.Debug("aaa"); - AssertDebugLastMessage("debug", "aaa"); + string configFilePath = Path.Combine(tempDir, "reload.nlog"); + WriteConfigFile(configFilePath, config1); + string otherFilePath = Path.Combine(tempDir, "other.nlog"); - using (var reloadWaiter = new ConfigurationReloadWaiter()) - { - File.Move(configFilePath, otherFilePath); - reloadWaiter.WaitForReload(); - } + SetLogManagerConfiguration(logFactory, useExplicitFileLoading, configFilePath); + + var logger = logFactory.GetCurrentClassLogger(); + logger.Debug("aaa"); + AssertDebugLastMessage("debug", "aaa", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Single(logFactory.Configuration.FileNamesToWatch); + Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + File.Move(configFilePath, otherFilePath); + logFactory.Setup().ReloadConfiguration(); logger.Debug("bbb"); // Assert that config1 is still loaded. - AssertDebugLastMessage("debug", "bbb"); + AssertDebugLastMessage("debug", "bbb", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Single(logFactory.Configuration.FileNamesToWatch); + Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); WriteConfigFile(otherFilePath, config2); - using (var reloadWaiter = new ConfigurationReloadWaiter()) - { - File.Move(otherFilePath, configFilePath); - - reloadWaiter.WaitForReload(); - Assert.True(reloadWaiter.DidReload); - } + File.Move(otherFilePath, configFilePath); + logFactory.Setup().ReloadConfiguration(); logger.Debug("ccc"); // Assert that config2 is loaded. - AssertDebugLastMessage("debug", "[ccc]"); + AssertDebugLastMessage("debug", "[ccc]", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Single(logFactory.Configuration.FileNamesToWatch); + Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); } finally { + logFactory.Shutdown(); + if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); } @@ -239,16 +193,8 @@ public void TestAutoReloadOnFileMove(bool useExplicitFileLoading) [Theory] [InlineData(true)] [InlineData(false)] - public void TestAutoReloadOnFileCopy(bool useExplicitFileLoading) { -#if !NETFRAMEWORK || MONO - if (IsLinux()) - { - Console.WriteLine("[SKIP] ReloadTests.TestAutoReloadOnFileCopy because we are running in Travis"); - return; - } -#endif string config1 = @" @@ -259,46 +205,52 @@ public void TestAutoReloadOnFileCopy(bool useExplicitFileLoading) "; string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempPath); - string configFilePath = Path.Combine(tempPath, "reload.nlog"); - WriteConfigFile(configFilePath, config1); - string otherFilePath = Path.Combine(tempPath, "other.nlog"); + var logFactory = new LogFactory(); try { - SetLogManagerConfiguration(useExplicitFileLoading, configFilePath); + Directory.CreateDirectory(tempPath); - var logger = LogManager.GetLogger("A"); + string configFilePath = Path.Combine(tempPath, "reload.nlog"); + WriteConfigFile(configFilePath, config1); + string otherFilePath = Path.Combine(tempPath, "other.nlog"); + + SetLogManagerConfiguration(logFactory, useExplicitFileLoading, configFilePath); + + var logger = logFactory.GetCurrentClassLogger(); logger.Debug("aaa"); - AssertDebugLastMessage("debug", "aaa"); + AssertDebugLastMessage("debug", "aaa", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Single(logFactory.Configuration.FileNamesToWatch); + Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); - using (var reloadWaiter = new ConfigurationReloadWaiter()) - { - File.Delete(configFilePath); - reloadWaiter.WaitForReload(); - } + File.Delete(configFilePath); + logFactory.Setup().ReloadConfiguration(); logger.Debug("bbb"); // Assert that config1 is still loaded. - AssertDebugLastMessage("debug", "bbb"); + AssertDebugLastMessage("debug", "bbb", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Single(logFactory.Configuration.FileNamesToWatch); + Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); WriteConfigFile(otherFilePath, config2); - using (var reloadWaiter = new ConfigurationReloadWaiter()) - { - File.Copy(otherFilePath, configFilePath); - File.Delete(otherFilePath); - - reloadWaiter.WaitForReload(); - Assert.True(reloadWaiter.DidReload); - } + File.Copy(otherFilePath, configFilePath); + File.Delete(otherFilePath); + logFactory.Setup().ReloadConfiguration(); logger.Debug("ccc"); // Assert that config2 is loaded. - AssertDebugLastMessage("debug", "[ccc]"); + AssertDebugLastMessage("debug", "[ccc]", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Single(logFactory.Configuration.FileNamesToWatch); + Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); } finally { + logFactory.Shutdown(); + if (Directory.Exists(tempPath)) Directory.Delete(tempPath, true); } @@ -307,17 +259,8 @@ public void TestAutoReloadOnFileCopy(bool useExplicitFileLoading) [Theory] [InlineData(true)] [InlineData(false)] - public void TestIncludedConfigNoReload(bool useExplicitFileLoading) { -#if !NETFRAMEWORK || MONO - if (IsLinux()) - { - Console.WriteLine("[SKIP] ReloadTests.TestIncludedConfigNoReload because we are running in Travis"); - return; - } -#endif - string mainConfig1 = @" @@ -334,37 +277,50 @@ public void TestIncludedConfigNoReload(bool useExplicitFileLoading) "; string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempDir); - - string mainConfigFilePath = Path.Combine(tempDir, "main.nlog"); - WriteConfigFile(mainConfigFilePath, mainConfig1); - string includedConfigFilePath = Path.Combine(tempDir, "included.nlog"); - WriteConfigFile(includedConfigFilePath, includedConfig1); + var logFactory = new LogFactory(); try { - SetLogManagerConfiguration(useExplicitFileLoading, mainConfigFilePath); + Directory.CreateDirectory(tempDir); + + string mainConfigFilePath = Path.Combine(tempDir, "main.nlog"); + WriteConfigFile(mainConfigFilePath, mainConfig1); + + string includedConfigFilePath = Path.Combine(tempDir, "included.nlog"); + WriteConfigFile(includedConfigFilePath, includedConfig1); - var logger = LogManager.GetLogger("A"); + SetLogManagerConfiguration(logFactory, useExplicitFileLoading, mainConfigFilePath); + + var logger = logFactory.GetCurrentClassLogger(); logger.Debug("aaa"); - AssertDebugLastMessage("debug", "aaa"); + AssertDebugLastMessage("debug", "aaa", logFactory); + Assert.False(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Empty(logFactory.Configuration.FileNamesToWatch); - ChangeAndReloadConfigFile(mainConfigFilePath, mainConfig2, assertDidReload: false); + WriteConfigFileAndReload(logFactory, mainConfigFilePath, mainConfig2); logger.Debug("bbb"); - // Assert that mainConfig1 is still loaded. - AssertDebugLastMessage("debug", "bbb"); + // Assert that mainConfig2 has been loaded. + AssertDebugLastMessage("debug", "", logFactory); + Assert.False(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Empty(logFactory.Configuration.FileNamesToWatch); + logger.Info("bbb"); + AssertDebugLastMessage("debug", "bbb", logFactory); WriteConfigFile(mainConfigFilePath, mainConfig1); - ChangeAndReloadConfigFile(includedConfigFilePath, includedConfig2, assertDidReload: false); + WriteConfigFileAndReload(logFactory, includedConfigFilePath, includedConfig2); logger.Debug("ccc"); - // Assert that includedConfig1 is still loaded. - AssertDebugLastMessage("debug", "ccc"); + // Assert that includedConfig2 has been loaded. + AssertDebugLastMessage("debug", "[ccc]", logFactory); + Assert.False(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Empty(logFactory.Configuration.FileNamesToWatch); } finally { + logFactory.Shutdown(); + if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); } @@ -373,25 +329,12 @@ public void TestIncludedConfigNoReload(bool useExplicitFileLoading) [Theory] [InlineData(true)] [InlineData(false)] - public void TestIncludedConfigReload(bool useExplicitFileLoading) { -#if !NETFRAMEWORK || MONO - if (IsLinux()) - { - Console.WriteLine("[SKIP] ReloadTests.TestIncludedConfigNoReload because we are running in Travis"); - return; - } -#endif - string mainConfig1 = @" "; - string mainConfig2 = @" - - - "; string includedConfig1 = @" "; @@ -400,37 +343,41 @@ public void TestIncludedConfigReload(bool useExplicitFileLoading) "; string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempDir); - - string mainConfigFilePath = Path.Combine(tempDir, "main.nlog"); - WriteConfigFile(mainConfigFilePath, mainConfig1); - string includedConfigFilePath = Path.Combine(tempDir, "included.nlog"); - WriteConfigFile(includedConfigFilePath, includedConfig1); + var logFactory = new LogFactory(); try { - SetLogManagerConfiguration(useExplicitFileLoading, mainConfigFilePath); + Directory.CreateDirectory(tempDir); - var logger = LogManager.GetLogger("A"); - logger.Debug("aaa"); - AssertDebugLastMessage("debug", "aaa"); + string mainConfigFilePath = Path.Combine(tempDir, "main.nlog"); + WriteConfigFile(mainConfigFilePath, mainConfig1); - ChangeAndReloadConfigFile(mainConfigFilePath, mainConfig2, assertDidReload: false); + string includedConfigFilePath = Path.Combine(tempDir, "included.nlog"); + WriteConfigFile(includedConfigFilePath, includedConfig1); - logger.Debug("bbb"); - // Assert that mainConfig1 is still loaded. - AssertDebugLastMessage("debug", "bbb"); + SetLogManagerConfiguration(logFactory, useExplicitFileLoading, mainConfigFilePath); - WriteConfigFile(mainConfigFilePath, mainConfig1); - ChangeAndReloadConfigFile(includedConfigFilePath, includedConfig2); + var logger = logFactory.GetCurrentClassLogger(); + logger.Debug("aaa"); + AssertDebugLastMessage("debug", "aaa", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Single(logFactory.Configuration.FileNamesToWatch); + Assert.Equal(includedConfigFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + + WriteConfigFileAndReload(logFactory, includedConfigFilePath, includedConfig2); logger.Debug("ccc"); // Assert that includedConfig2 is loaded. - AssertDebugLastMessage("debug", "[ccc]"); + AssertDebugLastMessage("debug", "[ccc]", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Single(logFactory.Configuration.FileNamesToWatch); + Assert.Equal(includedConfigFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); } finally { + logFactory.Shutdown(); + if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); } @@ -439,17 +386,8 @@ public void TestIncludedConfigReload(bool useExplicitFileLoading) [Theory] [InlineData(true)] [InlineData(false)] - public void TestMainConfigReload(bool useExplicitFileLoading) { -#if !NETFRAMEWORK || MONO - if (IsLinux()) - { - Console.WriteLine("[SKIP] ReloadTests.TestMainConfigReload because we are running in Travis"); - return; - } -#endif - string mainConfig1 = @" @@ -469,39 +407,56 @@ public void TestMainConfigReload(bool useExplicitFileLoading) "; string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempDir); - - string mainConfigFilePath = Path.Combine(tempDir, "main.nlog"); - WriteConfigFile(mainConfigFilePath, mainConfig1); - - string included1ConfigFilePath = Path.Combine(tempDir, "included.nlog"); - WriteConfigFile(included1ConfigFilePath, included1Config); - string included2ConfigFilePath = Path.Combine(tempDir, "included2.nlog"); - WriteConfigFile(included2ConfigFilePath, included2Config1); + var logFactory = new LogFactory(); try { - SetLogManagerConfiguration(useExplicitFileLoading, mainConfigFilePath); + Directory.CreateDirectory(tempDir); + + string mainConfigFilePath = Path.Combine(tempDir, "main.nlog"); + WriteConfigFile(mainConfigFilePath, mainConfig1); + + string included1ConfigFilePath = Path.Combine(tempDir, "included.nlog"); + WriteConfigFile(included1ConfigFilePath, included1Config); + + string included2ConfigFilePath = Path.Combine(tempDir, "included2.nlog"); + WriteConfigFile(included2ConfigFilePath, included2Config1); - var logger = LogManager.GetLogger("A"); + SetLogManagerConfiguration(logFactory, useExplicitFileLoading, mainConfigFilePath); + + var logger = logFactory.GetCurrentClassLogger(); logger.Debug("aaa"); - AssertDebugLastMessage("debug", "aaa"); + AssertDebugLastMessage("debug", "aaa", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.NotEmpty(logFactory.Configuration.FileNamesToWatch); + Assert.Contains(mainConfigFilePath, logFactory.Configuration.FileNamesToWatch); + Assert.Contains(included1ConfigFilePath, logFactory.Configuration.FileNamesToWatch); - ChangeAndReloadConfigFile(mainConfigFilePath, mainConfig2); + WriteConfigFileAndReload(logFactory, mainConfigFilePath, mainConfig2); logger.Debug("bbb"); // Assert that mainConfig2 is loaded (which refers to included2.nlog). - AssertDebugLastMessage("debug", "[bbb]"); + AssertDebugLastMessage("debug", "[bbb]", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.NotEmpty(logFactory.Configuration.FileNamesToWatch); + Assert.Contains(mainConfigFilePath, logFactory.Configuration.FileNamesToWatch); + Assert.Contains(included2ConfigFilePath, logFactory.Configuration.FileNamesToWatch); - ChangeAndReloadConfigFile(included2ConfigFilePath, included2Config2); + WriteConfigFileAndReload(logFactory, included2ConfigFilePath, included2Config2); logger.Debug("ccc"); // Assert that included2Config2 is loaded. - AssertDebugLastMessage("debug", "(ccc)"); + AssertDebugLastMessage("debug", "(ccc)", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.NotEmpty(logFactory.Configuration.FileNamesToWatch); + Assert.Contains(mainConfigFilePath, logFactory.Configuration.FileNamesToWatch); + Assert.Contains(included2ConfigFilePath, logFactory.Configuration.FileNamesToWatch); } finally { + logFactory.Shutdown(); + if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); } @@ -510,17 +465,8 @@ public void TestMainConfigReload(bool useExplicitFileLoading) [Theory] [InlineData(true)] [InlineData(false)] - public void TestMainConfigReloadIncludedConfigNoReload(bool useExplicitFileLoading) { -#if !NETFRAMEWORK || MONO - if (IsLinux()) - { - Console.WriteLine("[SKIP] ReloadTests.TestMainConfigReload because we are running in Travis"); - return; - } -#endif - string mainConfig1 = @" @@ -540,39 +486,53 @@ public void TestMainConfigReloadIncludedConfigNoReload(bool useExplicitFileLoadi "; string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(tempDir); - - string mainConfigFilePath = Path.Combine(tempDir, "main.nlog"); - WriteConfigFile(mainConfigFilePath, mainConfig1); - - string included1ConfigFilePath = Path.Combine(tempDir, "included.nlog"); - WriteConfigFile(included1ConfigFilePath, included1Config); - string included2ConfigFilePath = Path.Combine(tempDir, "included2.nlog"); - WriteConfigFile(included2ConfigFilePath, included2Config1); + var logFactory = new LogFactory(); try { - SetLogManagerConfiguration(useExplicitFileLoading, mainConfigFilePath); + Directory.CreateDirectory(tempDir); + + string mainConfigFilePath = Path.Combine(tempDir, "main.nlog"); + WriteConfigFile(mainConfigFilePath, mainConfig1); + + string included1ConfigFilePath = Path.Combine(tempDir, "included.nlog"); + WriteConfigFile(included1ConfigFilePath, included1Config); - var logger = LogManager.GetLogger("A"); + string included2ConfigFilePath = Path.Combine(tempDir, "included2.nlog"); + WriteConfigFile(included2ConfigFilePath, included2Config1); + + SetLogManagerConfiguration(logFactory, useExplicitFileLoading, mainConfigFilePath); + + var logger = logFactory.GetCurrentClassLogger(); logger.Debug("aaa"); - AssertDebugLastMessage("debug", "aaa"); + AssertDebugLastMessage("debug", "aaa", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.NotEmpty(logFactory.Configuration.FileNamesToWatch); + Assert.Contains(mainConfigFilePath, logFactory.Configuration.FileNamesToWatch); + Assert.Contains(included1ConfigFilePath, logFactory.Configuration.FileNamesToWatch); - ChangeAndReloadConfigFile(mainConfigFilePath, mainConfig2); + WriteConfigFileAndReload(logFactory, mainConfigFilePath, mainConfig2); logger.Debug("bbb"); // Assert that mainConfig2 is loaded (which refers to included2.nlog). - AssertDebugLastMessage("debug", "[bbb]"); + AssertDebugLastMessage("debug", "[bbb]", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Single(logFactory.Configuration.FileNamesToWatch); + Assert.Equal(mainConfigFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); - ChangeAndReloadConfigFile(included2ConfigFilePath, included2Config2, assertDidReload: false); + WriteConfigFileAndReload(logFactory, included2ConfigFilePath, included2Config2); logger.Debug("ccc"); - // Assert that included2Config1 is still loaded. - AssertDebugLastMessage("debug", "[ccc]"); + // Assert that included2Config2 has been loaded. + AssertDebugLastMessage("debug", "(ccc)", logFactory); + Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); + Assert.Single(logFactory.Configuration.FileNamesToWatch); + Assert.Equal(mainConfigFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); } finally { + logFactory.Shutdown(); if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); @@ -580,7 +540,6 @@ public void TestMainConfigReloadIncludedConfigNoReload(bool useExplicitFileLoadi } [Fact] - [Obsolete("Replaced by ConfigurationChanged. Marked obsolete on NLog 5.2")] public void TestKeepVariablesOnReload() { string config = @" @@ -588,13 +547,12 @@ public void TestKeepVariablesOnReload() "; - var configLoader = new LoggingConfigurationWatchableFileLoader(LogFactory.DefaultAppEnvironment); - var logFactory = new LogFactory(configLoader); + var logFactory = new LogFactory(); var configuration = XmlLoggingConfigurationMock.CreateFromXml(logFactory, config); logFactory.Configuration = configuration; logFactory.Configuration.Variables["var1"] = "new_value"; logFactory.Configuration.Variables["var3"] = "new_value3"; - configLoader.ReloadConfigOnTimer(configuration); + var nullEvent = LogEventInfo.CreateNullEvent(); Assert.Equal("new_value", logFactory.Configuration.Variables["var1"].Render(nullEvent)); Assert.Equal("keep_value", logFactory.Configuration.Variables["var2"].Render(nullEvent)); @@ -641,7 +599,6 @@ public void TestKeepVariablesOnReloadAllowUpdate() } [Fact] - [Obsolete("Replaced by ConfigurationChanged. Marked obsolete on NLog 5.2")] public void TestResetVariablesOnReload() { string config = @" @@ -649,15 +606,14 @@ public void TestResetVariablesOnReload() "; - var configLoader = new LoggingConfigurationWatchableFileLoader(LogFactory.DefaultAppEnvironment); - var logFactory = new LogFactory(configLoader); + var logFactory = new LogFactory(); var configuration = XmlLoggingConfigurationMock.CreateFromXml(logFactory, config); logFactory.Configuration = configuration; logFactory.Configuration.Variables["var1"] = "new_value"; logFactory.Configuration.Variables["var3"] = "new_value3"; - configLoader.ReloadConfigOnTimer(configuration); + LogEventInfo nullEvent = LogEventInfo.CreateNullEvent(); - Assert.Equal("", logFactory.Configuration.Variables["var1"].Render(nullEvent)); + Assert.Equal("new_value", logFactory.Configuration.Variables["var1"].Render(nullEvent)); Assert.Equal("keep_value", logFactory.Configuration.Variables["var2"].Render(nullEvent)); logFactory.Configuration = configuration.Reload(); @@ -702,13 +658,12 @@ public void ReloadConfigOnTimer_When_No_Exception_Raises_ConfigurationReloadedEv LoggingConfigurationChangedEventArgs arguments = null; object calledBy = null; - var configLoader = new LoggingConfigurationWatchableFileLoader(LogFactory.DefaultAppEnvironment); - var logFactory = new LogFactory(configLoader); + var logFactory = new LogFactory(); var loggingConfiguration = XmlLoggingConfigurationMock.CreateFromXml(logFactory, ""); logFactory.Configuration = loggingConfiguration; logFactory.ConfigurationChanged += (sender, args) => { called = true; calledBy = sender; arguments = args; }; - configLoader.ReloadConfigOnTimer(loggingConfiguration); + logFactory.Setup().ReloadConfiguration(); Assert.True(called); Assert.Same(calledBy, logFactory); @@ -716,7 +671,6 @@ public void ReloadConfigOnTimer_When_No_Exception_Raises_ConfigurationReloadedEv } [Fact] - [Obsolete("Replaced by LogFactory.Setup().LoadConfigurationFromFile(). Marked obsolete on NLog 5.2")] public void TestReloadingInvalidConfiguration() { var validXmlConfig = @" @@ -738,7 +692,7 @@ public void TestReloadingInvalidConfiguration() { var nlogConfigFile = Path.Combine(tempDir, "NLog.config"); LogFactory logFactory = new LogFactory(); - logFactory.SetCandidateConfigFilePaths(new[] { nlogConfigFile }); + logFactory.Setup().LoadConfigurationFromFile(new[] { nlogConfigFile }); var config = logFactory.Configuration; Assert.Null(config); @@ -763,7 +717,6 @@ public void TestReloadingInvalidConfiguration() } [Fact] - [Obsolete("Replaced by LogFactory.Setup().LoadConfigurationFromFile(). Marked obsolete on NLog 5.2")] public void TestThrowExceptionWhenInvalidXml() { var invalidXmlConfig = @" @@ -779,8 +732,7 @@ public void TestThrowExceptionWhenInvalidXml() var nlogConfigFile = Path.Combine(tempDir, "NLog.config"); WriteConfigFile(nlogConfigFile, invalidXmlConfig); LogFactory logFactory = new LogFactory(); - logFactory.SetCandidateConfigFilePaths(new[] { nlogConfigFile }); - Assert.Throws(() => logFactory.GetLogger("Hello")); + Assert.Throws(() => logFactory.Setup().LoadConfigurationFromFile(new[] { nlogConfigFile }).GetCurrentClassLogger()); } } finally @@ -798,45 +750,18 @@ private static void WriteConfigFile(string configFilePath, string config) writer.Write(config); } - private static void ChangeAndReloadConfigFile(string configFilePath, string config, bool assertDidReload = true) + private static void WriteConfigFileAndReload(LogFactory logFactory, string configFilePath, string config) { - using (var reloadWaiter = new ConfigurationReloadWaiter()) - { - WriteConfigFile(configFilePath, config); - reloadWaiter.WaitForReload(); - - if (assertDidReload) - Assert.True(reloadWaiter.DidReload, $"Config '{configFilePath}' did not reload."); - } - } - - private sealed class ConfigurationReloadWaiter : IDisposable - { - private ManualResetEvent counterEvent = new ManualResetEvent(false); - - public ConfigurationReloadWaiter() - { - LogManager.ConfigurationChanged += SignalCounterEvent(counterEvent); - } - - public bool DidReload => counterEvent.WaitOne(0); - - public void Dispose() - { - LogManager.ConfigurationChanged -= SignalCounterEvent(counterEvent); - } + using (StreamWriter writer = File.CreateText(configFilePath)) + writer.Write(config); - public void WaitForReload() + try { - counterEvent.WaitOne(3000); + logFactory.Setup().ReloadConfiguration(); } - - private static EventHandler SignalCounterEvent(ManualResetEvent counterEvent) + catch { - return (sender, e) => - { - counterEvent.Set(); - }; + // Swallow issues from loading bad config } } } @@ -870,4 +795,3 @@ public static XmlLoggingConfigurationMock CreateFromXml(LogFactory logFactory, s } } } -#endif diff --git a/tests/NLog.UnitTests/Config/XmlConfigTests.cs b/tests/NLog.UnitTests/Config/XmlConfigTests.cs index 64d9ce090d..bf281be813 100644 --- a/tests/NLog.UnitTests/Config/XmlConfigTests.cs +++ b/tests/NLog.UnitTests/Config/XmlConfigTests.cs @@ -52,7 +52,6 @@ public void ParseNLogOptionsDefaultTest() var config = XmlLoggingConfiguration.CreateFromXmlString(xml); Assert.False(config.AutoReload); - Assert.True(config.InitializeSucceeded); Assert.Equal("", InternalLogger.LogFile); Assert.False(InternalLogger.LogToConsole); Assert.False(InternalLogger.LogToConsoleError); @@ -73,7 +72,6 @@ public void ParseNLogOptionsTest() var config = XmlLoggingConfiguration.CreateFromXmlString(xml); Assert.False(config.AutoReload); - Assert.True(config.InitializeSucceeded); Assert.Equal("", InternalLogger.LogFile); Assert.True(InternalLogger.LogToConsole); Assert.True(InternalLogger.LogToConsoleError); diff --git a/tests/NLog.UnitTests/LogFactoryTests.cs b/tests/NLog.UnitTests/LogFactoryTests.cs index c732c22bd9..2b20f687c0 100644 --- a/tests/NLog.UnitTests/LogFactoryTests.cs +++ b/tests/NLog.UnitTests/LogFactoryTests.cs @@ -219,67 +219,6 @@ public void SecondaryLogFactoryDoesNotTakePrimaryLogFactoryLock() } } - [Fact] - public void ReloadConfigOnTimer_DoesNotThrowConfigException_IfConfigChangedInBetween() - { - EventHandler testChanged = null; - - try - { - LogManager.Configuration = null; - - var loggingConfiguration = new LoggingConfiguration(); - LogManager.Configuration = loggingConfiguration; - - var configLoader = new LoggingConfigurationWatchableFileLoader(LogFactory.DefaultAppEnvironment); - var logFactory = new LogFactory(configLoader); - logFactory.Configuration = loggingConfiguration; - - var differentConfiguration = new LoggingConfiguration(); - - // Verify that the random configuration change is ignored (Only the final reset is reacted upon) - bool called = false; - LoggingConfiguration oldConfiguration = null, newConfiguration = null; - testChanged = (s, e) => { called = true; oldConfiguration = e.DeactivatedConfiguration; newConfiguration = e.ActivatedConfiguration; }; - LogManager.LogFactory.ConfigurationChanged += testChanged; - - var exRecorded = Record.Exception(() => configLoader.ReloadConfigOnTimer(differentConfiguration)); - Assert.Null(exRecorded); - - // Final reset clears the configuration, so it is changed to null - LogManager.Configuration = null; - Assert.True(called); - Assert.Equal(loggingConfiguration, oldConfiguration); - Assert.Null(newConfiguration); - } - finally - { - if (testChanged != null) - LogManager.LogFactory.ConfigurationChanged -= testChanged; - } - } - - private class ReloadNullConfiguration : LoggingConfiguration - { - public override LoggingConfiguration Reload() - { - return null; - } - } - - [Fact] - public void ReloadConfigOnTimer_DoesNotThrowConfigException_IfConfigReloadReturnsNull() - { - var loggingConfiguration = new ReloadNullConfiguration(); - LogManager.Configuration = loggingConfiguration; - var configLoader = new LoggingConfigurationWatchableFileLoader(LogFactory.DefaultAppEnvironment); - var logFactory = new LogFactory(configLoader); - logFactory.Configuration = loggingConfiguration; - - var exRecorded = Record.Exception(() => configLoader.ReloadConfigOnTimer(loggingConfiguration)); - Assert.Null(exRecorded); - } - /// /// We should be forward compatible so that we can add easily attributes in the future. /// diff --git a/tests/NLog.UnitTests/LogManagerTests.cs b/tests/NLog.UnitTests/LogManagerTests.cs index 6f21b32bb3..73b0cd3ca4 100644 --- a/tests/NLog.UnitTests/LogManagerTests.cs +++ b/tests/NLog.UnitTests/LogManagerTests.cs @@ -232,113 +232,6 @@ public void DisableLoggingTest_WithoutUsingStatement() LogManager.Configuration = null; } - private int _reloadCounter; - - private void WaitForConfigReload(int counter) - { - while (_reloadCounter < counter) - { - System.Threading.Thread.Sleep(100); - } - } - - private void OnConfigReloaded(object sender, LoggingConfigurationChangedEventArgs e) - { - ++_reloadCounter; - Console.WriteLine("OnConfigReloaded triggered: {0}", _reloadCounter); - } - - [Fact] - public void AutoReloadTest() - { -#if !NETFRAMEWORK || MONO - if (IsLinux()) - { - Console.WriteLine("[SKIP] LogManagerTests.AutoReloadTest because we are running in Travis"); - return; - } -#endif - - using (new InternalLoggerScope()) - { - string fileName = Path.GetTempFileName(); - try - { - _reloadCounter = 0; - LogManager.ConfigurationChanged += OnConfigReloaded; - using (StreamWriter fs = File.CreateText(fileName)) - { - fs.Write(@" - - - - - "); - } - LogManager.Configuration = new XmlLoggingConfiguration(fileName); - AssertDebugCounter("debug", 0); - var logger = LogManager.GetLogger("A"); - logger.Debug("aaa"); - AssertDebugLastMessage("debug", "aaa"); - - InternalLogger.Info("Rewriting test file..."); - - // now write the file again - using (StreamWriter fs = File.CreateText(fileName)) - { - fs.Write(@" - - - - - "); - } - - InternalLogger.Info("Rewritten."); - WaitForConfigReload(2); - - logger.Debug("aaa"); - AssertDebugLastMessage("debug", "xxx aaa"); - - // write the file again, this time make an error - using (StreamWriter fs = File.CreateText(fileName)) - { - fs.Write(@" - - - - - "); - } - - WaitForConfigReload(3); - logger.Debug("bbb"); - AssertDebugLastMessage("debug", "xxx bbb"); - - // write the corrected file again - using (StreamWriter fs = File.CreateText(fileName)) - { - fs.Write(@" - - - - - "); - } - WaitForConfigReload(4); - logger.Debug("ccc"); - AssertDebugLastMessage("debug", "zzz ccc"); - - } - finally - { - LogManager.ConfigurationChanged -= OnConfigReloaded; - if (File.Exists(fileName)) - File.Delete(fileName); - } - } - } - [Fact] public void GivenCurrentClass_WhenGetCurrentClassLogger_ThenLoggerShouldBeCurrentClass() { From fbf791e8f4210d8820a02696d47f8a86385c4d14 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 21 Oct 2024 22:59:12 +0200 Subject: [PATCH 024/224] ObjectGraphScanner - Handle property-getter that throws when AOT (#5652) --- .bettercodehub.yml | 11 ------ .coderabbit.yaml | 16 --------- src/NLog/Internal/ObjectGraphScanner.cs | 22 ++++++++++-- .../DatabaseTargetTests.cs | 35 ++++++++++++++++--- .../NetworkTargetTests.cs | 6 ++++ .../Wrappers/AsyncTargetWrapperTests.cs | 10 ++++-- 6 files changed, 64 insertions(+), 36 deletions(-) delete mode 100644 .bettercodehub.yml delete mode 100644 .coderabbit.yaml diff --git a/.bettercodehub.yml b/.bettercodehub.yml deleted file mode 100644 index f3e187636e..0000000000 --- a/.bettercodehub.yml +++ /dev/null @@ -1,11 +0,0 @@ -exclude: -- /examples/.* -- /src/VSIntegration/.* -- /src/Docs/.* -- /src/NLogAutoLoadExtension/.* -- /src/InstallNLogConfig/.* -- /tests/SampleExtensions/.* -- /tools/.* -component_depth: 3 -languages: -- csharp diff --git a/.coderabbit.yaml b/.coderabbit.yaml deleted file mode 100644 index 87282a5c34..0000000000 --- a/.coderabbit.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json -language: en-US -early_access: false -enable_free_tier: true -reviews: - profile: chill - request_changes_workflow: false - high_level_summary: false # Disable the high level summary because this feature modifies the original PR description. Ideally this feature would add a new comment to the PR instead of modifying the original PR description. - poem: false # Disable the poem feature because it adds noise and doesn't improve the review process. - review_status: false - auto_review: - enabled: true - auto_incremental_review: true - drafts: false -chat: - auto_reply: true diff --git a/src/NLog/Internal/ObjectGraphScanner.cs b/src/NLog/Internal/ObjectGraphScanner.cs index 4ad16a4ff9..531e3f7559 100644 --- a/src/NLog/Internal/ObjectGraphScanner.cs +++ b/src/NLog/Internal/ObjectGraphScanner.cs @@ -109,11 +109,11 @@ private static void ScanProperties(ConfigurationItemFactory configFactory, bo if (string.IsNullOrEmpty(configProp.Key)) continue; // Ignore default values - if (!PropertyHelper.IsConfigurationItemType(configFactory, configProp.Value.PropertyType)) + var propInfo = configProp.Value; + if (!PropertyHelper.IsConfigurationItemType(configFactory, propInfo.PropertyType)) continue; - var propInfo = configProp.Value; - var propValue = propInfo.GetValue(targetObject, null); + object propValue = ScanPropertyValue(targetObject, type, propInfo); if (propValue is null) continue; @@ -122,6 +122,22 @@ private static void ScanProperties(ConfigurationItemFactory configFactory, bo } } + private static object ScanPropertyValue(object targetObject, Type type, PropertyInfo propInfo) + { + try + { + return propInfo.GetValue(targetObject, null); + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "Failed scanning property: {0}.{1}", type, propInfo.Name); + if (exception.MustBeRethrownImmediately()) + throw; + + return null; + } + } + private static void ScanPropertyForObject(ConfigurationItemFactory configFactory, bool aggressiveSearch, object propValue, PropertyInfo prop, List result, int level, HashSet visitedObjects) where T : class { if (InternalLogger.IsTraceEnabled) diff --git a/tests/NLog.Database.Tests/DatabaseTargetTests.cs b/tests/NLog.Database.Tests/DatabaseTargetTests.cs index b92f24bdd8..ab433ec5f3 100644 --- a/tests/NLog.Database.Tests/DatabaseTargetTests.cs +++ b/tests/NLog.Database.Tests/DatabaseTargetTests.cs @@ -1707,11 +1707,24 @@ public void SqlServer_NoTargetInstallException() } bool isAppVeyor = IsAppVeyor(); - SqlServerTest.TryDropDatabase(isAppVeyor); try { - SqlServerTest.CreateDatabase(isAppVeyor); + for (int i = 1; i <= 3; ++i) + { + try + { + SqlServerTest.TryDropDatabase(isAppVeyor); + SqlServerTest.CreateDatabase(isAppVeyor); + break; + } + catch + { + if (i >= 3) + throw; + System.Threading.Thread.Sleep(i * 5000); + } + } var connectionString = SqlServerTest.GetConnectionString(isAppVeyor); @@ -1759,11 +1772,25 @@ public void SqlServer_InstallAndLogMessage() } bool isAppVeyor = IsAppVeyor(); - SqlServerTest.TryDropDatabase(isAppVeyor); try { - SqlServerTest.CreateDatabase(isAppVeyor); + for (int i = 1; i <= 3; ++i) + { + try + { + SqlServerTest.TryDropDatabase(isAppVeyor); + SqlServerTest.CreateDatabase(isAppVeyor); + break; + } + catch + { + if (i >= 3) + throw; + + System.Threading.Thread.Sleep(i * 5000); + } + } var connectionString = SqlServerTest.GetConnectionString(IsAppVeyor()); diff --git a/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs index 7e929e3d05..1264cb62aa 100644 --- a/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs @@ -656,11 +656,14 @@ public void NetworkTargetUdpSplitEnabledTest() try { NetworkTargetUdpTest(true); + break; } catch { if (i == 3) throw; + + System.Threading.Thread.Sleep(1000 * i); } } } @@ -673,11 +676,14 @@ public void NetworkTargetUdpSplitDisabledTest() try { NetworkTargetUdpTest(false); + break; } catch { if (i == 3) throw; + + System.Threading.Thread.Sleep(1000 * i); } } } diff --git a/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs b/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs index d720e6fc78..951a9d3435 100644 --- a/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs +++ b/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs @@ -73,13 +73,19 @@ public void AsyncTargetWrapperInitTest2() [Fact] public void AsyncTargetWrapperSyncTest_WithLock_WhenTimeToSleepBetweenBatchesIsEqualToZero() { - AsyncTargetWrapperSyncTest_WhenTimeToSleepBetweenBatchesIsEqualToZero(true); + RetryingIntegrationTest(3, () => + { + AsyncTargetWrapperSyncTest_WhenTimeToSleepBetweenBatchesIsEqualToZero(true); + }); } [Fact] public void AsyncTargetWrapperSyncTest_NoLock_WhenTimeToSleepBetweenBatchesIsEqualToZero() { - AsyncTargetWrapperSyncTest_WhenTimeToSleepBetweenBatchesIsEqualToZero(false); + RetryingIntegrationTest(3, () => + { + AsyncTargetWrapperSyncTest_WhenTimeToSleepBetweenBatchesIsEqualToZero(false); + }); } /// From aa2a4cad0b30e9454acbe1b261b94755b16736a6 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 21 Oct 2024 23:00:22 +0200 Subject: [PATCH 025/224] Fix Sonar issues in example code (#5654) --- examples/ExtendingLoggers/README.html | 8 ++++---- .../ASPNetBufferingWrapper/NormalPage.aspx | 2 +- .../ASPNetBufferingWrapper/PageWithWarnings.aspx | 2 +- examples/targets/Configuration API/ASPNetTrace/test.aspx | 6 +++++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/ExtendingLoggers/README.html b/examples/ExtendingLoggers/README.html index 2cb9e10d5a..8eaf306f23 100644 --- a/examples/ExtendingLoggers/README.html +++ b/examples/ExtendingLoggers/README.html @@ -1,10 +1,10 @@ - + + NLog Examples diff --git a/examples/targets/Configuration API/ASPNetBufferingWrapper/NormalPage.aspx b/examples/targets/Configuration API/ASPNetBufferingWrapper/NormalPage.aspx index d012e129a2..7a4032b2c9 100644 --- a/examples/targets/Configuration API/ASPNetBufferingWrapper/NormalPage.aspx +++ b/examples/targets/Configuration API/ASPNetBufferingWrapper/NormalPage.aspx @@ -2,7 +2,7 @@ - + Untitled Page diff --git a/examples/targets/Configuration API/ASPNetBufferingWrapper/PageWithWarnings.aspx b/examples/targets/Configuration API/ASPNetBufferingWrapper/PageWithWarnings.aspx index f38c27442f..a3f02210bd 100644 --- a/examples/targets/Configuration API/ASPNetBufferingWrapper/PageWithWarnings.aspx +++ b/examples/targets/Configuration API/ASPNetBufferingWrapper/PageWithWarnings.aspx @@ -2,7 +2,7 @@ - + Untitled Page diff --git a/examples/targets/Configuration API/ASPNetTrace/test.aspx b/examples/targets/Configuration API/ASPNetTrace/test.aspx index a58ee3c3ea..1238ea441b 100644 --- a/examples/targets/Configuration API/ASPNetTrace/test.aspx +++ b/examples/targets/Configuration API/ASPNetTrace/test.aspx @@ -1,4 +1,7 @@ <%@ Page language="c#" AutoEventWireup="true" %> + + + - + + ASP.NET Trace Test

From 0d8aa5e1a4525826170ea5ebd80a35a7a110b247 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 23 Oct 2024 07:32:55 +0200 Subject: [PATCH 026/224] LoggingConfigurationParser - Handle property-getter that throws when AOT (#5655) --- src/NLog/Config/LoggingConfigurationParser.cs | 56 ++++++++++++++----- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index e7b4220444..b6a4d40d5e 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -1154,8 +1154,31 @@ private void SetPropertyValuesFromElement(T targetObject, ValidatedConfigurat return; } - object propertyValue = propInfo.GetValue(targetObject, null); - ConfigureFromAttributesAndElements(propertyValue, childElement); + if (TryGetPropertyValue(targetObject, propInfo, out var propertyValue)) + { + ConfigureFromAttributesAndElements(propertyValue, childElement); + } + } + + private bool TryGetPropertyValue(T targetObject, PropertyInfo propInfo, out object propertyValue) where T : class + { + try + { + propertyValue = propInfo.GetValue(targetObject, null); + return true; + } + catch (Exception ex) + { + if (ex.MustBeRethrownImmediately()) + throw; + + var configException = new NLogConfigurationException($"Failed getting property {propInfo.Name} for type: {typeof(T).Name}", ex); + if (MustThrowConfigException(configException)) + throw configException; + + propertyValue = null; + return false; + } } private bool AddArrayItemFromElement(object o, PropertyInfo propInfo, ValidatedConfigurationElement element) @@ -1163,23 +1186,26 @@ private bool AddArrayItemFromElement(object o, PropertyInfo propInfo, ValidatedC Type elementType = PropertyHelper.GetArrayItemType(propInfo); if (elementType != null) { - IList propertyValue = (IList)propInfo.GetValue(o, null); - - if (string.Equals(propInfo.Name, element.Name, StringComparison.OrdinalIgnoreCase)) + if (TryGetPropertyValue(o, propInfo, out var propertyValue)) { - bool foundChild = false; - foreach (var child in element.ValidChildren) + IList listValue = (IList)propertyValue; + + if (string.Equals(propInfo.Name, element.Name, StringComparison.OrdinalIgnoreCase)) { - foundChild = true; - propertyValue.Add(ParseArrayItemFromElement(elementType, child)); + bool foundChild = false; + foreach (var child in element.ValidChildren) + { + foundChild = true; + listValue.Add(ParseArrayItemFromElement(elementType, child)); + } + if (foundChild) + return true; } - if (foundChild) - return true; - } - object arrayItem = ParseArrayItemFromElement(elementType, element); - propertyValue.Add(arrayItem); - return true; + object arrayItem = ParseArrayItemFromElement(elementType, element); + listValue.Add(arrayItem); + return true; + } } return false; From 359752c394bdf35ee653852f3803b581f4cc18ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 24 Nov 2024 13:02:20 +0100 Subject: [PATCH 027/224] Bump Microsoft.NET.Test.Sdk from 17.11.1 to 17.12.0 in /tests/NLog.UnitTests (#5665) Bump Microsoft.NET.Test.Sdk in /tests/NLog.UnitTests Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.11.1 to 17.12.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v17.11.1...v17.12.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/NLog.UnitTests/NLog.UnitTests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NLog.UnitTests/NLog.UnitTests.csproj b/tests/NLog.UnitTests/NLog.UnitTests.csproj index bdf6653cfb..919ffc568b 100644 --- a/tests/NLog.UnitTests/NLog.UnitTests.csproj +++ b/tests/NLog.UnitTests/NLog.UnitTests.csproj @@ -43,7 +43,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 90dcce09ed0a2b82036b452e3d66d03956074d4a Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 13 Jan 2025 20:26:30 +0100 Subject: [PATCH 028/224] DatabaseTargetTests - Skip checking if NLogTest database already exists (#5670) --- .../DatabaseTargetTests.cs | 85 +++++++------------ 1 file changed, 33 insertions(+), 52 deletions(-) diff --git a/tests/NLog.Database.Tests/DatabaseTargetTests.cs b/tests/NLog.Database.Tests/DatabaseTargetTests.cs index ab433ec5f3..4aa7d5a206 100644 --- a/tests/NLog.Database.Tests/DatabaseTargetTests.cs +++ b/tests/NLog.Database.Tests/DatabaseTargetTests.cs @@ -1710,24 +1710,10 @@ public void SqlServer_NoTargetInstallException() try { - for (int i = 1; i <= 3; ++i) - { - try - { - SqlServerTest.TryDropDatabase(isAppVeyor); - SqlServerTest.CreateDatabase(isAppVeyor); - break; - } - catch - { - if (i >= 3) - throw; - System.Threading.Thread.Sleep(i * 5000); - } - } - var connectionString = SqlServerTest.GetConnectionString(isAppVeyor); + CreateDatabaseWithRetry(isAppVeyor); + DatabaseTarget testTarget = new DatabaseTarget("TestDbTarget"); testTarget.ConnectionString = connectionString; @@ -1775,24 +1761,9 @@ public void SqlServer_InstallAndLogMessage() try { - for (int i = 1; i <= 3; ++i) - { - try - { - SqlServerTest.TryDropDatabase(isAppVeyor); - SqlServerTest.CreateDatabase(isAppVeyor); - break; - } - catch - { - if (i >= 3) - throw; - - System.Threading.Thread.Sleep(i * 5000); - } - } + var connectionString = SqlServerTest.GetConnectionString(isAppVeyor); - var connectionString = SqlServerTest.GetConnectionString(IsAppVeyor()); + CreateDatabaseWithRetry(isAppVeyor); var logFactory = new LogFactory().Setup().SetupExtensions(ext => ext.RegisterAssembly(typeof(DatabaseTarget).Assembly)).LoadConfigurationFromXml(@" 1) + { + SqlServerTest.TryDropDatabase(isAppVeyor); + System.Threading.Thread.Sleep(1000); + } + SqlServerTest.CreateDatabase(isAppVeyor); + break; + } + catch + { + if (i >= 3) + throw; + System.Threading.Thread.Sleep(i * 5000); + } + } + } + #if NETFRAMEWORK [Fact] public void GetProviderNameFromAppConfig() @@ -2605,25 +2599,16 @@ public static void CreateDatabase(bool isAppVeyor) IssueCommand(IsAppVeyor(), "CREATE DATABASE NLogTest", connectionString); } - public static bool NLogTestDatabaseExists(bool isAppVeyor) - { - var connectionString = GetMasterConnectionString(isAppVeyor); - var dbId = IssueScalarQuery(IsAppVeyor(), "select db_id('NLogTest')", connectionString); - return dbId != null && dbId != DBNull.Value; - } - private static string GetMasterConnectionString(bool isAppVeyor) { return isAppVeyor ? AppVeyorConnectionStringMaster : LocalConnectionStringMaster; } - public static void IssueCommand(bool isAppVeyor, string commandString, string connectionString = null) + public static void IssueCommand(bool isAppVeyor, string commandString, string connectionString) { using (var connection = new SqlConnection(connectionString ?? GetConnectionString(isAppVeyor))) { connection.Open(); - if (connectionString is null) - connection.ChangeDatabase("NLogTest"); using (var command = new SqlCommand(commandString, connection)) { command.ExecuteNonQuery(); @@ -2653,15 +2638,11 @@ public static bool TryDropDatabase(bool isAppVeyor) { try { - if (NLogTestDatabaseExists(isAppVeyor)) - { - var connectionString = GetMasterConnectionString(isAppVeyor); - IssueCommand(isAppVeyor, - "ALTER DATABASE [NLogTest] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE NLogTest;", - connectionString); - return true; - } - return false; + var connectionString = GetMasterConnectionString(isAppVeyor); + IssueCommand(isAppVeyor, + "ALTER DATABASE [NLogTest] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE NLogTest;", + connectionString); + return true; } catch (Exception) { From 22cc8a89fd281e736a7f2e0d73413fc7f8fafe33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 21:16:19 +0100 Subject: [PATCH 029/224] Bump xunit from 2.9.2 to 2.9.3 in /tests/NLog.UnitTests (#5669) Bumps [xunit](https://github.com/xunit/xunit) from 2.9.2 to 2.9.3. - [Commits](https://github.com/xunit/xunit/compare/v2-2.9.2...v2-2.9.3) --- updated-dependencies: - dependency-name: xunit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/NLog.UnitTests/NLog.UnitTests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NLog.UnitTests/NLog.UnitTests.csproj b/tests/NLog.UnitTests/NLog.UnitTests.csproj index 919ffc568b..5e87e33bb3 100644 --- a/tests/NLog.UnitTests/NLog.UnitTests.csproj +++ b/tests/NLog.UnitTests/NLog.UnitTests.csproj @@ -38,7 +38,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 2e3cd6fc361b986368b1d09a68b708a5d4ab93ae Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 13 Jan 2025 22:18:32 +0100 Subject: [PATCH 030/224] CounterLayoutRenderer - Lock on readonly field to fix Sonar Code Smell (#5672) --- .../LayoutRenderers/CounterLayoutRenderer.cs | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs b/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs index c8ada25bd9..28d3be6bf9 100644 --- a/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs @@ -51,7 +51,7 @@ namespace NLog.LayoutRenderers [ThreadAgnostic] public class CounterLayoutRenderer : LayoutRenderer, IRawValue { - private static Dictionary sequences = new Dictionary(StringComparer.Ordinal); + private static readonly Dictionary Sequences = new Dictionary(StringComparer.Ordinal); ///

/// Gets or sets the initial value of the counter. @@ -83,34 +83,25 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) private long GetNextValue(LogEventInfo logEvent) { - long v; - if (Sequence is null) { - v = Value; + long currentValue = Value; Value += Increment; - } - else - { - v = GetNextSequenceValue(Sequence.Render(logEvent), Value, Increment); + return currentValue; } - return v; - } - - private static long GetNextSequenceValue(string sequenceName, long defaultValue, int increment) - { - lock (sequences) + var sequenceName = Sequence.Render(logEvent); + lock (Sequences) { - if (!sequences.TryGetValue(sequenceName, out var val)) + if (!Sequences.TryGetValue(sequenceName, out var nextValue)) { - val = defaultValue; + nextValue = Value; } - var retVal = val; - val += increment; - sequences[sequenceName] = val; - return retVal; + var currentValue = nextValue; + nextValue += Increment; + Sequences[sequenceName] = nextValue; + return currentValue; } } From 5f1ce0955a21befdcd5a91efba2cfb71087aa491 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 26 Jan 2025 17:11:06 +0100 Subject: [PATCH 031/224] Duplicate ConcurrentFileTarget.cs --- .../ConcurrentFileTarget.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{NLog/Targets/FileTarget.cs => NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs} (100%) diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs similarity index 100% rename from src/NLog/Targets/FileTarget.cs rename to src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs From 558351e7d6a1adbb0c2d25a33a219b7476ff2930 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 26 Jan 2025 17:11:54 +0100 Subject: [PATCH 032/224] Restore FileTarget.cs --- src/NLog/Targets/FileTarget.cs | 2541 ++++++++++++++++++++++++++++++++ 1 file changed, 2541 insertions(+) create mode 100644 src/NLog/Targets/FileTarget.cs diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs new file mode 100644 index 0000000000..3878cc1c98 --- /dev/null +++ b/src/NLog/Targets/FileTarget.cs @@ -0,0 +1,2541 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets +{ + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.Globalization; + using System.IO; + using System.Text; + using System.Threading; + using NLog.Common; + using NLog.Config; + using NLog.Internal; + using NLog.Internal.FileAppenders; + using NLog.Layouts; + using NLog.Targets.FileArchiveModes; + using NLog.Time; + + /// + /// Writes log messages to one or more files. + /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki + [Target("File")] + public class FileTarget : TargetWithLayoutHeaderAndFooter, ICreateFileParameters + { + /// + /// Default clean up period of the initialized files. When a file exceeds the clean up period is removed from the list. + /// + /// Clean up period is defined in days. + private const int InitializedFilesCleanupPeriod = 2; + + /// + /// This value disables file archiving based on the size. + /// + private const long ArchiveAboveSizeDisabled = -1L; + + /// + /// Holds the initialized files each given time by the instance. Against each file, the last write time is stored. + /// + /// Last write time is store in local time (no UTC). + private readonly Dictionary _initializedFiles = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// List of the associated file appenders with the instance. + /// + private IFileAppenderCache _fileAppenderCache; + + IFileArchiveMode GetFileArchiveHelper(string archiveFilePattern) + { + return _fileArchiveHelper ?? (_fileArchiveHelper = FileArchiveModeFactory.CreateArchiveStyle(archiveFilePattern, ArchiveNumbering, GetArchiveDateFormatString(ArchiveDateFormat), ArchiveFileName != null, MaxArchiveFiles > 0 || MaxArchiveDays > 0)); + } + private IFileArchiveMode _fileArchiveHelper; + + private Timer _autoClosingTimer; + + /// + /// The number of initialized files at any one time. + /// + private int _initializedFilesCounter; + + /// + /// The maximum number of archive files that should be kept. + /// + private int _maxArchiveFiles; + + /// + /// The maximum days of archive files that should be kept. + /// + private int _maxArchiveDays; + + /// + /// The filename as target + /// + private FilePathLayout _fullFileName; + + /// + /// The archive file name as target + /// + private FilePathLayout _fullArchiveFileName; + + private FileArchivePeriod _archiveEvery; + private long _archiveAboveSize; + + private bool _enableArchiveFileCompression; + + /// + /// The date of the previous log event. + /// + private DateTime? _previousLogEventTimestamp; + + /// + /// The file name of the previous log event. + /// + private string _previousLogFileName; + + private bool _concurrentWrites; + private bool _cleanupFileName; + private FilePathKind _fileNameKind = FilePathKind.Unknown; + private FilePathKind _archiveFileKind; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} + /// + public FileTarget() : this(FileAppenderCache.Empty) + { + } + + internal FileTarget(IFileAppenderCache fileAppenderCache) + { + ArchiveNumbering = ArchiveNumberingMode.Sequence; + _maxArchiveFiles = 0; + _maxArchiveDays = 0; + + ArchiveEvery = FileArchivePeriod.None; + ArchiveAboveSize = ArchiveAboveSizeDisabled; + _cleanupFileName = true; + + _fileAppenderCache = fileAppenderCache; + } + +#if !NET35 && !NET40 + static FileTarget() + { + FileCompressor = new ZipArchiveFileCompressor(); + } +#endif + + /// + /// Initializes a new instance of the class. + /// + /// + /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} + /// + /// Name of the target. + public FileTarget(string name) : this() + { + Name = name; + } + + /// + /// Gets or sets the name of the file to write to. + /// + /// + /// This FileName string is a layout which may include instances of layout renderers. + /// This lets you use a single target to write to multiple files. + /// + /// + /// The following value makes NLog write logging events to files based on the log level in the directory where + /// the application runs. + /// ${basedir}/${level}.log + /// All Debug messages will go to Debug.log, all Info messages will go to Info.log and so on. + /// You can combine as many of the layout renderers as you want to produce an arbitrary log file name. + /// + /// + [RequiredParameter] + public Layout FileName + { + get + { + return _fullFileName?.GetLayout(); + } + set + { + _fullFileName = CreateFileNameLayout(value); + ResetFileAppenders("FileName Changed"); + } + } + + private FilePathLayout CreateFileNameLayout(Layout value) + { + if (value is null) + return null; + + return new FilePathLayout(value, CleanupFileName, FileNameKind); + } + + /// + /// Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. + /// If set to false, nothing gets written when the filename is wrong. + /// + /// + public bool CleanupFileName + { + get => _cleanupFileName; + set + { + if (_cleanupFileName != value) + { + _cleanupFileName = value; + _fullFileName = CreateFileNameLayout(FileName); + _fullArchiveFileName = CreateFileNameLayout(ArchiveFileName); + ResetFileAppenders("CleanupFileName Changed"); + } + } + } + + /// + /// Is the an absolute or relative path? + /// + /// + public FilePathKind FileNameKind + { + get => _fileNameKind; + set + { + if (_fileNameKind != value) + { + _fileNameKind = value; + _fullFileName = CreateFileNameLayout(FileName); + _fullArchiveFileName = CreateFileNameLayout(ArchiveFileName); + ResetFileAppenders("FileNameKind Changed"); + } + } + } + + /// + /// Gets or sets a value indicating whether to create directories if they do not exist. + /// + /// + /// Setting this to false may improve performance a bit, but you'll receive an error + /// when attempting to write to a directory that's not present. + /// + /// + public bool CreateDirs { get; set; } = true; + + /// + /// Gets or sets a value indicating whether to delete old log file on startup. + /// + /// + /// This option works only when the "FileName" parameter denotes a single file. + /// + /// + public bool DeleteOldFileOnStartup { get; set; } + + /// + /// Gets or sets a value indicating whether to replace file contents on each write instead of appending log message at the end. + /// + /// + public bool ReplaceFileContentsOnEachWrite { get; set; } + + /// + /// Gets or sets a value indicating whether to keep log file open instead of opening and closing it on each logging event. + /// + /// + /// KeepFileOpen = true gives the best performance, and ensure the file-lock is not lost to other applications.
+ /// KeepFileOpen = false gives the best compability, but slow performance and lead to file-locking issues with other applications. + ///
+ /// + public bool KeepFileOpen + { + get => _keepFileOpen; + set + { + if (_keepFileOpen != value) + { + _keepFileOpen = value; + ResetFileAppenders("KeepFileOpen Changed"); + } + } + } + private bool _keepFileOpen = true; + + /// + /// Gets or sets a value indicating whether to enable log file(s) to be deleted. + /// + /// + public bool EnableFileDelete { get; set; } = true; + + /// + /// Gets or sets the file attributes (Windows only). + /// + /// + public Win32FileAttributes FileAttributes + { + get => _fileAttributes; + set + { + if (value != Win32FileAttributes.Normal && PlatformDetector.IsWin32) + { + ForceManaged = false; + } + _fileAttributes = value; + } + } + Win32FileAttributes _fileAttributes = Win32FileAttributes.Normal; + + bool ICreateFileParameters.IsArchivingEnabled => IsArchivingEnabled; + + int ICreateFileParameters.FileOpenRetryCount => ConcurrentWrites ? ConcurrentWriteAttempts : (KeepFileOpen ? 0 : (_concurrentWriteAttempts ?? 2)); + + int ICreateFileParameters.FileOpenRetryDelay => ConcurrentWriteAttemptDelay; + + /// + /// Gets or sets the line ending mode. + /// + /// + public LineEndingMode LineEnding { get; set; } = LineEndingMode.Default; + + /// + /// Gets or sets a value indicating whether to automatically flush the file buffers after each log message. + /// + /// + public bool AutoFlush { get; set; } = true; + + /// + /// Gets or sets the number of files to be kept open. Setting this to a higher value may improve performance + /// in a situation where a single File target is writing to many files + /// (such as splitting by level or by logger). + /// + /// + /// The files are managed on a LRU (least recently used) basis, which flushes + /// the files that have not been used for the longest period of time should the + /// cache become full. As a rule of thumb, you shouldn't set this parameter to + /// a very high value. A number like 10-15 shouldn't be exceeded, because you'd + /// be keeping a large number of files open which consumes system resources. + /// + /// + public int OpenFileCacheSize { get; set; } = 5; + + /// + /// Gets or sets the maximum number of seconds that files are kept open. Zero or negative means disabled. + /// + /// + public int OpenFileCacheTimeout { get; set; } + + /// + /// Gets or sets the maximum number of seconds before open files are flushed. Zero or negative means disabled. + /// + /// + public int OpenFileFlushTimeout { get; set; } + + /// + /// Gets or sets the log file buffer size in bytes. + /// + /// + public int BufferSize { get; set; } = 32768; + + /// + /// Gets or sets the file encoding. + /// + /// + public Encoding Encoding + { + get => _encoding; + set + { + _encoding = value; + if (!_writeBom.HasValue && InitialValueBom(value)) + _writeBom = true; + } + } + private Encoding _encoding = Encoding.UTF8; + + /// + /// Gets or sets whether or not this target should just discard all data that its asked to write. + /// Mostly used for when testing NLog Stack except final write + /// + /// + public bool DiscardAll { get; set; } + + /// + /// Gets or sets a value indicating whether concurrent writes to the log file by multiple processes on the same host. + /// + /// + /// This makes multi-process logging possible. NLog uses a special technique + /// that lets it keep the files open for writing. + /// + /// + public bool ConcurrentWrites + { + get => _concurrentWrites; + set + { + if (_concurrentWrites != value) + { + _concurrentWrites = value; + ResetFileAppenders("ConcurrentWrites Changed"); + } + } + } + + /// + /// Obsolete and replaced by = false with NLog v5.3. + /// Gets or sets a value indicating whether concurrent writes to the log file by multiple processes on different network hosts. + /// + /// + /// This effectively prevents files from being kept open. + /// + /// + [Obsolete("Instead use KeepFileOpen = false. Marked obsolete with NLog v5.3")] + [EditorBrowsable(EditorBrowsableState.Never)] + public bool NetworkWrites { get => !KeepFileOpen; set => KeepFileOpen = !value; } + + /// + /// Gets or sets a value indicating whether to write BOM (byte order mark) in created files. + /// + /// Defaults to true for UTF-16 and UTF-32 + /// + /// + public bool WriteBom + { + get => _writeBom ?? false; + set => _writeBom = value; + } + private bool? _writeBom; + + /// + /// Gets or sets the number of times the write is appended on the file before NLog + /// discards the log message. + /// + /// + public int ConcurrentWriteAttempts { get => _concurrentWriteAttempts ?? 10; set => _concurrentWriteAttempts = value; } + private int? _concurrentWriteAttempts; + + /// + /// Gets or sets the delay in milliseconds to wait before attempting to write to the file again. + /// + /// + /// The actual delay is a random value between 0 and the value specified + /// in this parameter. On each failed attempt the delay base is doubled + /// up to times. + /// + /// + /// Assuming that ConcurrentWriteAttemptDelay is 10 the time to wait will be:

+ /// a random value between 0 and 10 milliseconds - 1st attempt
+ /// a random value between 0 and 20 milliseconds - 2nd attempt
+ /// a random value between 0 and 40 milliseconds - 3rd attempt
+ /// a random value between 0 and 80 milliseconds - 4th attempt
+ /// ...

+ /// and so on. + /// + /// + public int ConcurrentWriteAttemptDelay { get; set; } = 1; + + ///

+ /// Gets or sets a value indicating whether to archive old log file on startup. + /// + /// + /// This option works only when the "FileName" parameter denotes a single file. + /// After archiving the old file, the current log file will be empty. + /// + /// + public bool ArchiveOldFileOnStartup + { + get => _archiveOldFileOnStartup ?? false; + set => _archiveOldFileOnStartup = value; + } + private bool? _archiveOldFileOnStartup; + + /// + /// Gets or sets whether to write the Header on initial creation of file appender, even if the file is not empty. + /// Default value is , which means only write header when initial file is empty (Ex. ensures valid CSV files) + /// + /// + /// Alternative use to ensure each application session gets individual log-file. + /// + public bool WriteHeaderWhenInitialFileNotEmpty { get; set; } + + /// + /// Gets or sets a value of the file size threshold to archive old log file on startup. + /// + /// + /// This option won't work if is set to false + /// Default value is 0 which means that the file is archived as soon as archival on + /// startup is enabled. + /// + /// + public long ArchiveOldFileOnStartupAboveSize { get; set; } + + /// + /// Gets or sets a value specifying the date format to use when archiving files. + /// + /// + /// This option works only when the "ArchiveNumbering" parameter is set either to Date or DateAndSequence. + /// + /// + public string ArchiveDateFormat + { + get => _archiveDateFormat; + set + { + if (_archiveDateFormat != value) + { + _archiveDateFormat = value; + ResetFileAppenders("ArchiveDateFormat Changed"); // Reset archive file-monitoring + } + } + } + private string _archiveDateFormat = string.Empty; + + /// + /// Gets or sets the size in bytes above which log files will be automatically archived. + /// + /// + /// Notice when combined with then it will attempt to append to any existing + /// archive file if grown above size multiple times. New archive file will be created when using + /// + /// + public long ArchiveAboveSize + { + get => _archiveAboveSize; + set + { + var newValue = value > 0 ? value : ArchiveAboveSizeDisabled; + if ((_archiveAboveSize > 0) != (newValue > 0)) + { + _archiveAboveSize = newValue; + ResetFileAppenders("ArchiveAboveSize Changed"); // Reset archive file-monitoring + } + else + { + _archiveAboveSize = newValue; + } + } + } + + /// + /// Gets or sets a value indicating whether to automatically archive log files every time the specified time passes. + /// + /// + /// Files are moved to the archive as part of the write operation if the current period of time changes. For example + /// if the current hour changes from 10 to 11, the first write that will occur + /// on or after 11:00 will trigger the archiving. + /// + /// + public FileArchivePeriod ArchiveEvery + { + get => _archiveEvery; + set + { + if (_archiveEvery != value) + { + _archiveEvery = value; + ResetFileAppenders("ArchiveEvery Changed"); // Reset archive file-monitoring + } + } + } + + /// + /// Is the an absolute or relative path? + /// + /// + public FilePathKind ArchiveFileKind + { + get => _archiveFileKind; + set + { + if (_archiveFileKind != value) + { + _archiveFileKind = value; + _fullArchiveFileName = CreateFileNameLayout(ArchiveFileName); + ResetFileAppenders("ArchiveFileKind Changed"); // Reset archive file-monitoring + } + } + } + + /// + /// Gets or sets the name of the file to be used for an archive. + /// + /// + /// It may contain a special placeholder {#####} + /// that will be replaced with a sequence of numbers depending on + /// the archiving strategy. The number of hash characters used determines + /// the number of numerical digits to be used for numbering files. + /// + /// + public Layout ArchiveFileName + { + get + { + return _fullArchiveFileName?.GetLayout(); + } + set + { + _fullArchiveFileName = CreateFileNameLayout(value); + ResetFileAppenders("ArchiveFileName Changed"); // Reset archive file-monitoring + } + } + + /// + /// Gets or sets the maximum number of archive files that should be kept. + /// + /// + public int MaxArchiveFiles + { + get => _maxArchiveFiles; + set + { + if (_maxArchiveFiles != value) + { + _maxArchiveFiles = value; + ResetFileAppenders("MaxArchiveFiles Changed"); // Enforce archive cleanup + } + } + } + + /// + /// Gets or sets the maximum days of archive files that should be kept. + /// + /// + public int MaxArchiveDays + { + get => _maxArchiveDays; + set + { + if (_maxArchiveDays != value) + { + _maxArchiveDays = value; + ResetFileAppenders("MaxArchiveDays Changed"); // Enforce archive cleanup + } + } + } + + /// + /// Gets or sets the way file archives are numbered. + /// + /// + public ArchiveNumberingMode ArchiveNumbering + { + get => _archiveNumbering; + set + { + if (_archiveNumbering != value) + { + _archiveNumbering = value; + ResetFileAppenders("ArchiveNumbering Changed"); // Reset archive file-monitoring + } + } + } + private ArchiveNumberingMode _archiveNumbering; + + /// + /// Used to compress log files during archiving. + /// This may be used to provide your own implementation of a zip file compressor, + /// on platforms other than .Net4.5. + /// Defaults to ZipArchiveFileCompressor on .Net4.5 and to null otherwise. + /// + /// + public static IFileCompressor FileCompressor { get; set; } + + /// + /// Gets or sets a value indicating whether to compress archive files into the zip archive format. + /// + /// + public bool EnableArchiveFileCompression + { + get => _enableArchiveFileCompression && FileCompressor != null; + set + { + if (_enableArchiveFileCompression != value) + { + _enableArchiveFileCompression = value; + ResetFileAppenders("EnableArchiveFileCompression Changed"); // Reset archive file-monitoring + } + } + } + + /// + /// Gets or set a value indicating whether a managed file stream is forced, instead of using the native implementation. + /// + /// + public bool ForceManaged { get; set; } = true; + + /// + /// Gets or sets a value indicating whether file creation calls should be synchronized by a system global mutex. + /// + /// + public bool ForceMutexConcurrentWrites { get; set; } + + /// + /// Gets or sets a value indicating whether the footer should be written only when the file is archived. + /// + /// + public bool WriteFooterOnArchivingOnly { get; set; } + + /// + /// Gets the characters that are appended after each line. + /// + protected internal string NewLineChars => LineEnding.NewLineCharacters; + + /// + /// Refresh the ArchiveFilePatternToWatch option of the . + /// The log file must be watched for archiving when multiple processes are writing to the same + /// open file. + /// + private void RefreshArchiveFilePatternToWatch(string fileName, LogEventInfo logEvent) + { + _fileAppenderCache.CheckCloseAppenders -= AutoCloseAppendersAfterArchive; + + bool mustWatchArchiving = IsArchivingEnabled && KeepFileOpen && ConcurrentWrites; + bool mustWatchActiveFile = EnableFileDelete && ((KeepFileOpen && ConcurrentWrites) || (IsSimpleKeepFileOpen && !EnableFileDeleteSimpleMonitor)); + if (mustWatchArchiving || mustWatchActiveFile) + { + _fileAppenderCache.CheckCloseAppenders += AutoCloseAppendersAfterArchive; // Activates FileSystemWatcher + } + + if (mustWatchArchiving) + { + string archiveFilePattern = GetArchiveFileNamePattern(fileName, logEvent); + var fileArchiveStyle = !string.IsNullOrEmpty(archiveFilePattern) ? GetFileArchiveHelper(archiveFilePattern) : null; + string fileNameMask = fileArchiveStyle != null ? _fileArchiveHelper.GenerateFileNameMask(archiveFilePattern) : string.Empty; + string directoryMask = !string.IsNullOrEmpty(fileNameMask) ? Path.Combine(Path.GetDirectoryName(archiveFilePattern), fileNameMask) : string.Empty; + _fileAppenderCache.ArchiveFilePatternToWatch = directoryMask; + } + else + { + _fileAppenderCache.ArchiveFilePatternToWatch = null; + } + } + + /// + /// Removes records of initialized files that have not been + /// accessed in the last two days. + /// + /// + /// Files are marked 'initialized' for the purpose of writing footers when the logging finishes. + /// + public void CleanupInitializedFiles() + { + try + { + CleanupInitializedFiles(TimeSource.Current.Time.AddDays(-InitializedFilesCleanupPeriod)); + } + catch (Exception exception) + { + if (exception.MustBeRethrownImmediately()) + throw; + + InternalLogger.Error(exception, "{0}: Exception in CleanupInitializedFiles", this); + } + } + + /// + /// Removes records of initialized files that have not been + /// accessed after the specified date. + /// + /// The cleanup threshold. + /// + /// Files are marked 'initialized' for the purpose of writing footers when the logging finishes. + /// + public void CleanupInitializedFiles(DateTime cleanupThreshold) + { + InternalLogger.Trace("{0}: CleanupInitializedFiles with cleanupThreshold {1}", this, cleanupThreshold); + + List filesToFinalize = null; + + // Select the files require to be finalized. + foreach (var file in _initializedFiles) + { + if (file.Value < cleanupThreshold) + { + if (filesToFinalize is null) + { + filesToFinalize = new List(); + } + filesToFinalize.Add(file.Key); + } + } + + // Finalize the files. + if (filesToFinalize != null) + { + foreach (string fileName in filesToFinalize) + { + FinalizeFile(fileName); + } + } + + InternalLogger.Trace("{0}: CleanupInitializedFiles Completed and finalized {0} files", this, filesToFinalize?.Count ?? 0); + } + + /// + /// Flushes all pending file operations. + /// + /// The asynchronous continuation. + /// + /// The timeout parameter is ignored, because file APIs don't provide + /// the needed functionality. + /// + protected override void FlushAsync(AsyncContinuation asyncContinuation) + { + try + { + InternalLogger.Trace("{0}: FlushAsync", this); + _fileAppenderCache.FlushAppenders(); + asyncContinuation(null); + InternalLogger.Trace("{0}: FlushAsync Done", this); + } + catch (Exception exception) + { + if (ExceptionMustBeRethrown(exception)) + { + throw; + } + + asyncContinuation(exception); + } + } + + /// + /// Returns the suitable appender factory ( ) to be used to generate the file + /// appenders associated with the instance. + /// + /// The type of the file appender factory returned depends on the values of various properties. + /// + /// suitable for this instance. + private IFileAppenderFactory GetFileAppenderFactory() + { + if (DiscardAll) + { + return NullAppender.TheFactory; + } + else if (!KeepFileOpen) + { + return RetryingMultiProcessFileAppender.TheFactory; + } + else if (ConcurrentWrites) + { + if (!ForceMutexConcurrentWrites) + { +#if MONO + if (PlatformDetector.IsUnix) + { + return UnixMultiProcessFileAppender.TheFactory; + } +#elif NETFRAMEWORK + if (PlatformDetector.IsWin32 && !PlatformDetector.IsMono) + { + return WindowsMultiProcessFileAppender.TheFactory; + } +#endif + } + + if (MutexDetector.SupportsSharableMutex) + { + return MutexMultiProcessFileAppender.TheFactory; + } + else + { + return RetryingMultiProcessFileAppender.TheFactory; + } + } + else if (IsArchivingEnabled) + return CountingSingleProcessFileAppender.TheFactory; + else + return SingleProcessFileAppender.TheFactory; + } + + private bool IsArchivingEnabled => ArchiveAboveSize != ArchiveAboveSizeDisabled || ArchiveEvery != FileArchivePeriod.None; + + private bool IsSimpleKeepFileOpen => KeepFileOpen && !ConcurrentWrites && !ReplaceFileContentsOnEachWrite; + + private bool EnableFileDeleteSimpleMonitor => EnableFileDelete && IsSimpleKeepFileOpen +#if NETFRAMEWORK + && !PlatformDetector.IsWin32 +#endif + ; + + bool ICreateFileParameters.EnableFileDeleteSimpleMonitor => EnableFileDeleteSimpleMonitor; + + /// + /// Initializes file logging by creating data structures that + /// enable efficient multi-file logging. + /// + protected override void InitializeTarget() + { + base.InitializeTarget(); + + var appenderFactory = GetFileAppenderFactory(); + if (InternalLogger.IsTraceEnabled) + { + InternalLogger.Trace("{0}: Using appenderFactory: {1}", this, appenderFactory.GetType()); + } + + _fileAppenderCache = new FileAppenderCache(OpenFileCacheSize, appenderFactory, this); + + if ((OpenFileCacheSize > 0 || EnableFileDelete) && (OpenFileCacheTimeout > 0 || OpenFileFlushTimeout > 0)) + { + int openFileAutoTimeoutSecs = (OpenFileCacheTimeout > 0 && OpenFileFlushTimeout > 0) ? Math.Min(OpenFileCacheTimeout, OpenFileFlushTimeout) : Math.Max(OpenFileCacheTimeout, OpenFileFlushTimeout); + InternalLogger.Trace("{0}: Start autoClosingTimer", this); + _autoClosingTimer = new Timer( + (state) => AutoClosingTimerCallback(this, EventArgs.Empty), + null, + openFileAutoTimeoutSecs * 1000, + openFileAutoTimeoutSecs * 1000); + } + } + + /// + /// Closes the file(s) opened for writing. + /// + protected override void CloseTarget() + { + base.CloseTarget(); + + foreach (string fileName in new List(_initializedFiles.Keys)) + { + FinalizeFile(fileName); + } + + _fileArchiveHelper = null; + + var currentTimer = _autoClosingTimer; + if (currentTimer != null) + { + InternalLogger.Trace("{0}: Stop autoClosingTimer", this); + _autoClosingTimer = null; + currentTimer.WaitForDispose(TimeSpan.Zero); + } + + _fileAppenderCache.CloseAppenders("Dispose"); + _fileAppenderCache.Dispose(); + } + + private void ResetFileAppenders(string reason) + { + _fileArchiveHelper = null; + if (IsInitialized) + { + _fileAppenderCache.CloseAppenders(reason); + _initializedFiles.Clear(); + } + } + + private readonly ReusableStreamCreator _reusableFileWriteStream = new ReusableStreamCreator(); + private readonly ReusableStreamCreator _reusableBatchFileWriteStream = new ReusableStreamCreator(true); + private readonly ReusableBufferCreator _reusableEncodingBuffer = new ReusableBufferCreator(1024); + + /// + /// Writes the specified logging event to a file specified in the FileName + /// parameter. + /// + /// The logging event. + protected override void Write(LogEventInfo logEvent) + { + var logFileName = GetFullFileName(logEvent); + if (string.IsNullOrEmpty(logFileName)) + { + throw new ArgumentException("The path is not of a legal form."); + } + + using (var targetStream = _reusableBatchFileWriteStream.Allocate()) + { + using (var targetBuilder = ReusableLayoutBuilder.Allocate()) + using (var targetBuffer = _reusableEncodingBuffer.Allocate()) + { + RenderFormattedMessageToStream(logEvent, targetBuilder.Result, targetBuffer.Result, targetStream.Result); + } + + ProcessLogEvent(logEvent, logFileName, new ArraySegment(targetStream.Result.GetBuffer(), 0, (int)targetStream.Result.Length)); + } + } + + /// + /// Get full filename (=absolute) and cleaned if needed. + /// + /// + /// + internal string GetFullFileName(LogEventInfo logEvent) + { + if (_fullFileName is null) + return null; + + if (_fullFileName.IsFixedFilePath) + return _fullFileName.Render(logEvent); + + using (var targetBuilder = ReusableLayoutBuilder.Allocate()) + { + return _fullFileName.RenderWithBuilder(logEvent, targetBuilder.Result); + } + } + + SortHelpers.KeySelector _getFullFileNameDelegate; + + /// + /// Writes the specified array of logging events to a file specified in the FileName + /// parameter. + /// + /// An array of objects. + /// + /// This function makes use of the fact that the events are batched by sorting + /// the requests by filename. This optimizes the number of open/close calls + /// and can help improve performance. + /// + protected override void Write(IList logEvents) + { + if (_getFullFileNameDelegate is null) + _getFullFileNameDelegate = c => GetFullFileName(c.LogEvent); + + var buckets = logEvents.BucketSort(_getFullFileNameDelegate); + + using (var reusableStream = _reusableBatchFileWriteStream.Allocate()) + { + var ms = reusableStream.Result ?? new MemoryStream(); + + foreach (var bucket in buckets) + { + int bucketCount = bucket.Value.Count; + if (bucketCount <= 0) + continue; + + string fileName = bucket.Key; + if (string.IsNullOrEmpty(fileName)) + { + InternalLogger.Warn("{0}: FileName Layout returned empty string. The path is not of a legal form.", this); + var emptyPathException = new ArgumentException("The path is not of a legal form."); + for (int i = 0; i < bucketCount; ++i) + { + bucket.Value[i].Continuation(emptyPathException); + } + continue; + } + + int currentIndex = 0; + while (currentIndex < bucketCount) + { + ms.Position = 0; + ms.SetLength(0); + + var written = WriteToMemoryStream(bucket.Value, currentIndex, ms); + AppendMemoryStreamToFile(fileName, bucket.Value[currentIndex].LogEvent, ms, out var lastException); + for (int i = 0; i < written; ++i) + { + bucket.Value[currentIndex++].Continuation(lastException); + } + } + } + } + } + + private int WriteToMemoryStream(IList logEvents, int startIndex, MemoryStream ms) + { + long maxBufferSize = BufferSize * 100; // Max Buffer Default = 30 KiloByte * 100 = 3 MegaByte + + using (var targetStream = _reusableFileWriteStream.Allocate()) + using (var targetBuilder = ReusableLayoutBuilder.Allocate()) + using (var targetBuffer = _reusableEncodingBuffer.Allocate()) + { + var formatBuilder = targetBuilder.Result; + var transformBuffer = targetBuffer.Result; + var encodingStream = targetStream.Result; + + for (int i = startIndex; i < logEvents.Count; ++i) + { + // For some CPU's then it is faster to write to a small MemoryStream, and then copy to the larger one + encodingStream.Position = 0; + encodingStream.SetLength(0); + formatBuilder.ClearBuilder(); + + AsyncLogEventInfo ev = logEvents[i]; + RenderFormattedMessageToStream(ev.LogEvent, formatBuilder, transformBuffer, encodingStream); + ms.Write(encodingStream.GetBuffer(), 0, (int)encodingStream.Length); + if (ms.Length > maxBufferSize && !ReplaceFileContentsOnEachWrite) + return i - startIndex + 1; // Max Chunk Size Limit to avoid out-of-memory issues + } + } + + return logEvents.Count - startIndex; + } + + private void ProcessLogEvent(LogEventInfo logEvent, string fileName, ArraySegment bytesToWrite) + { + DateTime previousLogEventTimestamp = InitializeFile(fileName, logEvent); + bool initializedNewFile = previousLogEventTimestamp == DateTime.MinValue; + if (initializedNewFile && fileName == _previousLogFileName && _previousLogEventTimestamp.HasValue) + previousLogEventTimestamp = _previousLogEventTimestamp.Value; + + bool archiveOccurred = TryArchiveFile(fileName, logEvent, bytesToWrite.Count, previousLogEventTimestamp, initializedNewFile); + if (archiveOccurred) + initializedNewFile = InitializeFile(fileName, logEvent) == DateTime.MinValue || initializedNewFile; + + if (ReplaceFileContentsOnEachWrite) + { + ReplaceFileContent(fileName, bytesToWrite, true); + } + else + { + WriteToFile(fileName, bytesToWrite, initializedNewFile); + } + + _previousLogFileName = fileName; + _previousLogEventTimestamp = logEvent.TimeStamp; + } + + /// + /// Obsolete and replaced by with NLog v5. + /// Formats the log event for write. + /// + /// The log event to be formatted. + /// A string representation of the log event. + [Obsolete("No longer used and replaced by RenderFormattedMessage. Marked obsolete on NLog 5.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual string GetFormattedMessage(LogEventInfo logEvent) + { + return Layout.Render(logEvent); + } + + /// + /// Obsolete and replaced by with NLog v5. + /// Gets the bytes to be written to the file. + /// + /// Log event. + /// Array of bytes that are ready to be written. + [Obsolete("No longer used and replaced by RenderFormattedMessage. Marked obsolete on NLog 5.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual byte[] GetBytesToWrite(LogEventInfo logEvent) + { + string text = GetFormattedMessage(logEvent); + int textBytesCount = Encoding.GetByteCount(text); + int newLineBytesCount = Encoding.GetByteCount(NewLineChars); + byte[] bytes = new byte[textBytesCount + newLineBytesCount]; + Encoding.GetBytes(text, 0, text.Length, bytes, 0); + Encoding.GetBytes(NewLineChars, 0, NewLineChars.Length, bytes, textBytesCount); + return TransformBytes(bytes); + } + + /// + /// Obsolete and replaced by with NLog v5. + /// Modifies the specified byte array before it gets sent to a file. + /// + /// The byte array. + /// The modified byte array. The function can do the modification in-place. + [Obsolete("No longer used and replaced by TransformStream. Marked obsolete on NLog 5.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + protected virtual byte[] TransformBytes(byte[] value) + { + return value; + } + + /// + /// Gets the bytes to be written to the file. + /// + /// The log event to be formatted. + /// to help format log event. + /// Optional temporary char-array to help format log event. + /// Destination for the encoded result. + protected virtual void RenderFormattedMessageToStream(LogEventInfo logEvent, StringBuilder formatBuilder, char[] transformBuffer, MemoryStream streamTarget) + { + RenderFormattedMessage(logEvent, formatBuilder); + formatBuilder.Append(NewLineChars); + TransformBuilderToStream(logEvent, formatBuilder, transformBuffer, streamTarget); + } + + /// + /// Formats the log event for write. + /// + /// The log event to be formatted. + /// for the result. + protected virtual void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) + { + Layout.Render(logEvent, target); + } + + private void TransformBuilderToStream(LogEventInfo logEvent, StringBuilder builder, char[] transformBuffer, MemoryStream workStream) + { + builder.CopyToStream(workStream, Encoding, transformBuffer); + TransformStream(logEvent, workStream); + } + + /// + /// Modifies the specified byte array before it gets sent to a file. + /// + /// The LogEvent being written + /// The byte array. + protected virtual void TransformStream(LogEventInfo logEvent, MemoryStream stream) + { + } + + private void AppendMemoryStreamToFile(string currentFileName, LogEventInfo firstLogEvent, MemoryStream ms, out Exception lastException) + { + try + { + ArraySegment bytes = new ArraySegment(ms.GetBuffer(), 0, (int)ms.Length); + ProcessLogEvent(firstLogEvent, currentFileName, bytes); + lastException = null; + } + catch (Exception exception) + { + if (ExceptionMustBeRethrown(exception)) + { + throw; + } + + lastException = exception; + } + } + + /// + /// Archives fileName to archiveFileName. + /// + /// File name to be archived. + /// Name of the archive file. + private void ArchiveFile(string fileName, string archiveFileName) + { + string archiveFolderPath = Path.GetDirectoryName(archiveFileName); + if (archiveFolderPath != null && !Directory.Exists(archiveFolderPath)) + Directory.CreateDirectory(archiveFolderPath); + + if (string.Equals(fileName, archiveFileName, StringComparison.OrdinalIgnoreCase)) + { + InternalLogger.Info("{0}: Archiving {1} skipped as ArchiveFileName equals FileName", this, fileName); + } + else if (EnableArchiveFileCompression) + { + InternalLogger.Info("{0}: Archiving {1} to compressed {2}", this, fileName, archiveFileName); + if (File.Exists(archiveFileName)) + { + InternalLogger.Warn("{0}: Failed archiving because compressed file already exists: {1}", this, archiveFileName); + } + else + { + ArchiveFileCompress(fileName, archiveFileName); + } + } + else + { + InternalLogger.Info("{0}: Archiving {1} to {2}", this, fileName, archiveFileName); + if (File.Exists(archiveFileName)) + { + ArchiveFileAppendExisting(fileName, archiveFileName); + } + else + { + ArchiveFileMove(fileName, archiveFileName); + } + } + } + + private void ArchiveFileCompress(string fileName, string archiveFileName) + { + int fileCompressRetryCount = ConcurrentWrites ? ConcurrentWriteAttempts : 2; + for (int i = 1; i <= fileCompressRetryCount; ++i) + { + try + { + if (FileCompressor is IArchiveFileCompressor archiveFileCompressor) + { + string entryName = (ArchiveNumbering != ArchiveNumberingMode.Rolling) ? (Path.GetFileNameWithoutExtension(archiveFileName) + Path.GetExtension(fileName)) : Path.GetFileName(fileName); + archiveFileCompressor.CompressFile(fileName, archiveFileName, entryName); + } + else + { + FileCompressor.CompressFile(fileName, archiveFileName); + } + + break; // Success + } + catch (DirectoryNotFoundException) + { + throw; // Skip retry when directory does not exist + } + catch (FileNotFoundException) + { + throw; // Skip retry when file does not exist + } + catch (IOException ex) + { + if (i == fileCompressRetryCount) + throw; + + if (File.Exists(archiveFileName)) + throw; + + int sleepTimeMs = i * 50; + InternalLogger.Warn("{0}: Archiving Attempt #{1} to compress {2} to {3} failed - {4} {5}. Sleeping for {6}ms", this, i, fileName, archiveFileName, ex.GetType(), ex.Message, sleepTimeMs); + AsyncHelpers.WaitForDelay(TimeSpan.FromMilliseconds(sleepTimeMs)); + } + } + + DeleteAndWaitForFileDelete(fileName); + } + + private void ArchiveFileAppendExisting(string fileName, string archiveFileName) + { + //todo handle double footer + InternalLogger.Info("{0}: Already exists, append to {1}", this, archiveFileName); + + //copy to archive file. + var fileShare = FileShare.ReadWrite; + if (EnableFileDelete) + { + fileShare |= FileShare.Delete; + } + + using (FileStream fileStream = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, fileShare)) + using (FileStream archiveFileStream = File.Open(archiveFileName, FileMode.Append)) + { + fileStream.CopyAndSkipBom(archiveFileStream, Encoding); + //clear old content + fileStream.SetLength(0); + + if (EnableFileDelete && !DeleteOldArchiveFile(fileName)) + { + // Attempt to delete file to reset File-Creation-Time (Delete under file-lock) + fileShare &= ~FileShare.Delete; // Retry after having released file-lock + } + + fileStream.Close(); // This flushes the content, too. +#if !NET35 + archiveFileStream.Flush(true); +#else + archiveFileStream.Flush(); +#endif + } + + if ((fileShare & FileShare.Delete) == FileShare.None) + { + DeleteOldArchiveFile(fileName); // Attempt to delete file to reset File-Creation-Time + } + } + + private void ArchiveFileMove(string fileName, string archiveFileName) + { + try + { + InternalLogger.Debug("{0}: Move file from '{1}' to '{2}'", this, fileName, archiveFileName); + File.Move(fileName, archiveFileName); + } + catch (IOException ex) + { + if (IsSimpleKeepFileOpen) + throw; // No need to retry, when only single process access + + if (!EnableFileDelete && KeepFileOpen) + throw; // No need to retry when file delete has been disabled + + if (ConcurrentWrites && !MutexDetector.SupportsSharableMutex) + throw; // No need to retry when not having a real archive mutex to protect us + + // It is possible to move a file while other processes has open file-handles. + // Unless the other process is actively writing, then the file move might fail. + // We are already holding the archive-mutex, so lets retry if things are stable + InternalLogger.Warn(ex, "{0}: Archiving failed. Checking for retry move of {1} to {2}.", this, fileName, archiveFileName); + if (!File.Exists(fileName) || File.Exists(archiveFileName)) + throw; + + AsyncHelpers.WaitForDelay(TimeSpan.FromMilliseconds(50)); + + if (!File.Exists(fileName) || File.Exists(archiveFileName)) + throw; + + InternalLogger.Debug("{0}: Archiving retrying move of {1} to {2}.", this, fileName, archiveFileName); + File.Move(fileName, archiveFileName); + } + } + + private bool DeleteOldArchiveFile(string fileName) + { + try + { + InternalLogger.Info("{0}: Deleting old archive file: '{1}'.", this, fileName); + CloseInvalidFileHandle(fileName); + File.Delete(fileName); + return true; + } + catch (DirectoryNotFoundException exception) + { + //never rethrow this, as this isn't an exceptional case. + InternalLogger.Debug(exception, "{0}: Failed to delete old log file '{1}' as directory is missing.", this, fileName); + return false; + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "{0}: Failed to delete old archive file: '{1}'.", this, fileName); + if (ExceptionMustBeRethrown(exception)) + { + throw; + } + + return false; + } + } + + private void DeleteAndWaitForFileDelete(string fileName) + { + try + { + InternalLogger.Trace("{0}: Waiting for file delete of '{1}' for 12 sec", this, fileName); + var originalFileCreationTime = (new FileInfo(fileName)).CreationTime; + if (DeleteOldArchiveFile(fileName) && File.Exists(fileName)) + { + FileInfo currentFileInfo; + for (int i = 0; i < 120; ++i) + { + AsyncHelpers.WaitForDelay(TimeSpan.FromMilliseconds(100)); + currentFileInfo = new FileInfo(fileName); + if (!currentFileInfo.Exists || currentFileInfo.CreationTime != originalFileCreationTime) + return; + } + + InternalLogger.Warn("{0}: Timeout while deleting old archive file: '{1}'.", this, fileName); + } + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "{0}: Failed to delete old archive file: '{1}'.", this, fileName); + if (ExceptionMustBeRethrown(exception)) + { + throw; + } + } + } + + /// + /// Gets the correct formatting to be used based on the value of for converting values which will be inserting into file + /// names during archiving. + /// + /// This value will be computed only when a empty value or is passed into + /// + /// Date format to used irrespectively of value. + /// Formatting for dates. + private string GetArchiveDateFormatString(string defaultFormat) + { + // If archiveDateFormat is not set in the config file, use a default + // date format string based on the archive period. + if (!string.IsNullOrEmpty(defaultFormat)) + return defaultFormat; + + switch (ArchiveEvery) + { + case FileArchivePeriod.Year: + return "yyyy"; + case FileArchivePeriod.Month: + return "yyyyMM"; + case FileArchivePeriod.Hour: + return "yyyyMMddHH"; + case FileArchivePeriod.Minute: + return "yyyyMMddHHmm"; + default: + return "yyyyMMdd"; // Also for Weekdays + } + } + + private DateTime? GetArchiveDate(string fileName, LogEventInfo logEvent, DateTime previousLogEventTimestamp) + { + // Using File LastModified to handle FileArchivePeriod.Month (where file creation time is one month ago) + var fileLastModifiedUtc = _fileAppenderCache.GetFileLastWriteTimeUtc(fileName); + + InternalLogger.Trace("{0}: Calculating archive date. File-LastModifiedUtc: {1}; Previous LogEvent-TimeStamp: {2}", this, fileLastModifiedUtc, previousLogEventTimestamp); + if (!fileLastModifiedUtc.HasValue) + { + if (previousLogEventTimestamp == DateTime.MinValue) + { + InternalLogger.Info("{0}: Unable to acquire useful timestamp to archive file: {1}", this, fileName); + return null; + } + return previousLogEventTimestamp; + } + + var lastWriteTimeSource = Time.TimeSource.Current.FromSystemTime(fileLastModifiedUtc.Value); + if (previousLogEventTimestamp != DateTime.MinValue) + { + if (previousLogEventTimestamp > lastWriteTimeSource) + { + InternalLogger.Trace("{0}: Using previous LogEvent-TimeStamp {1}, because more recent than File-LastModified {2}", this, previousLogEventTimestamp, lastWriteTimeSource); + return previousLogEventTimestamp; + } + + if (PreviousLogOverlappedPeriod(logEvent, previousLogEventTimestamp, lastWriteTimeSource)) + { + InternalLogger.Trace("{0}: Using previous LogEvent-TimeStamp {1}, because archive period is overlapping with File-LastModified {2}", this, previousLogEventTimestamp, lastWriteTimeSource); + return previousLogEventTimestamp; + } + + if (!AutoFlush && IsSimpleKeepFileOpen && previousLogEventTimestamp < lastWriteTimeSource) + { + InternalLogger.Trace("{0}: Using previous LogEvent-TimeStamp {1}, because AutoFlush=false affects File-LastModified {2}", this, previousLogEventTimestamp, lastWriteTimeSource); + return previousLogEventTimestamp; + } + } + + InternalLogger.Trace("{0}: Using last write time: {1}", this, lastWriteTimeSource); + return lastWriteTimeSource; + } + + private bool PreviousLogOverlappedPeriod(LogEventInfo logEvent, DateTime previousLogEventTimestamp, DateTime lastFileWrite) + { + string formatString = GetArchiveDateFormatString(string.Empty); + string lastWriteTimeString = lastFileWrite.ToString(formatString, CultureInfo.InvariantCulture); + string logEventTimeString = logEvent.TimeStamp.ToString(formatString, CultureInfo.InvariantCulture); + + if (lastWriteTimeString != logEventTimeString) + return false; + + DateTime? periodAfterPreviousLogEventTime = CalculateNextArchiveEventTime(previousLogEventTimestamp); + if (!periodAfterPreviousLogEventTime.HasValue) + return false; + + string periodAfterPreviousLogEventTimeString = periodAfterPreviousLogEventTime.Value.ToString(formatString, CultureInfo.InvariantCulture); + return lastWriteTimeString == periodAfterPreviousLogEventTimeString; + } + + DateTime? CalculateNextArchiveEventTime(DateTime timestamp) + { + switch (ArchiveEvery) + { + case FileArchivePeriod.Year: + return timestamp.AddYears(1); + case FileArchivePeriod.Month: + return timestamp.AddMonths(1); + case FileArchivePeriod.Day: + return timestamp.AddDays(1); + case FileArchivePeriod.Hour: + return timestamp.AddHours(1); + case FileArchivePeriod.Minute: + return timestamp.AddMinutes(1); + case FileArchivePeriod.Sunday: + return CalculateNextWeekday(timestamp, DayOfWeek.Sunday); + case FileArchivePeriod.Monday: + return CalculateNextWeekday(timestamp, DayOfWeek.Monday); + case FileArchivePeriod.Tuesday: + return CalculateNextWeekday(timestamp, DayOfWeek.Tuesday); + case FileArchivePeriod.Wednesday: + return CalculateNextWeekday(timestamp, DayOfWeek.Wednesday); + case FileArchivePeriod.Thursday: + return CalculateNextWeekday(timestamp, DayOfWeek.Thursday); + case FileArchivePeriod.Friday: + return CalculateNextWeekday(timestamp, DayOfWeek.Friday); + case FileArchivePeriod.Saturday: + return CalculateNextWeekday(timestamp, DayOfWeek.Saturday); + default: + return null; + } + } + + /// + /// Calculate the DateTime of the requested day of the week. + /// + /// The DateTime of the previous log event. + /// The next occurring day of the week to return a DateTime for. + /// The DateTime of the next occurring dayOfWeek. + /// For example: if previousLogEventTimestamp is Thursday 2017-03-02 and dayOfWeek is Sunday, this will return + /// Sunday 2017-03-05. If dayOfWeek is Thursday, this will return *next* Thursday 2017-03-09. + public static DateTime CalculateNextWeekday(DateTime previousLogEventTimestamp, DayOfWeek dayOfWeek) + { + // Shamelessly taken from https://stackoverflow.com/a/7611480/1354930 + int start = (int)previousLogEventTimestamp.DayOfWeek; + int target = (int)dayOfWeek; + if (target <= start) + target += 7; + return previousLogEventTimestamp.AddDays(target - start); + } + + /// + /// Invokes the archiving process after determining when and which type of archiving is required. + /// + /// File name to be checked and archived. + /// Log event that the instance is currently processing. + /// The DateTime of the previous log event for this file. + /// File has just been opened. + private void DoAutoArchive(string fileName, LogEventInfo eventInfo, DateTime previousLogEventTimestamp, bool initializedNewFile) + { + InternalLogger.Debug("{0}: Do archive file: '{1}'", this, fileName); + var fileInfo = new FileInfo(fileName); + if (!fileInfo.Exists) + { + CloseInvalidFileHandle(fileName); // Close possible stale file handles + return; + } + + string archiveFilePattern = GetArchiveFileNamePattern(fileName, eventInfo); + if (string.IsNullOrEmpty(archiveFilePattern)) + { + InternalLogger.Warn("{0}: Skip auto archive because archiveFilePattern is blank", this); + return; + } + + DateTime? archiveDate = GetArchiveDate(fileName, eventInfo, previousLogEventTimestamp); + + var archiveFileName = GenerateArchiveFileNameAfterCleanup(fileName, fileInfo, archiveFilePattern, archiveDate, initializedNewFile); + if (!string.IsNullOrEmpty(archiveFileName)) + { + ArchiveFile(fileInfo.FullName, archiveFileName); + } + } + + private string GenerateArchiveFileNameAfterCleanup(string fileName, FileInfo fileInfo, string archiveFilePattern, DateTime? archiveDate, bool initializedNewFile) + { + InternalLogger.Trace("{0}: Archive pattern '{1}'", this, archiveFilePattern); + + var fileArchiveStyle = GetFileArchiveHelper(archiveFilePattern); + var existingArchiveFiles = fileArchiveStyle.GetExistingArchiveFiles(archiveFilePattern); + + if (MaxArchiveFiles == 1) + { + InternalLogger.Trace("{0}: MaxArchiveFiles = 1", this); + // Perform archive cleanup before generating the next filename, + // as next archive-filename can be affected by existing files. + for (int i = existingArchiveFiles.Count - 1; i >= 0; i--) + { + var oldArchiveFile = existingArchiveFiles[i]; + if (!string.Equals(oldArchiveFile.FileName, fileInfo.FullName, StringComparison.OrdinalIgnoreCase)) + { + DeleteOldArchiveFile(oldArchiveFile.FileName); + existingArchiveFiles.RemoveAt(i); + } + } + + if (initializedNewFile && string.Equals(Path.GetDirectoryName(archiveFilePattern), fileInfo.DirectoryName, StringComparison.OrdinalIgnoreCase)) + { + DeleteOldArchiveFile(fileName); + return null; + } + } + + var archiveFileName = archiveDate.HasValue ? fileArchiveStyle.GenerateArchiveFileName(archiveFilePattern, archiveDate.Value, existingArchiveFiles) : null; + if (archiveFileName is null) + return null; + + if (!initializedNewFile) + { + FinalizeFile(fileName, isArchiving: true); + } + + if (existingArchiveFiles.Count > 0) + { + CleanupOldArchiveFiles(fileInfo, archiveFilePattern, existingArchiveFiles, archiveFileName); + } + + return archiveFileName.FileName; + } + + private void CleanupOldArchiveFiles(FileInfo currentFile, string archiveFilePattern, List existingArchiveFiles, DateAndSequenceArchive newArchiveFile = null) + { + var fileArchiveStyle = GetFileArchiveHelper(archiveFilePattern); + + if (fileArchiveStyle.IsArchiveCleanupEnabled) + { + if (currentFile != null) + ExcludeActiveFileFromOldArchiveFiles(currentFile, existingArchiveFiles); + + if (newArchiveFile != null) + existingArchiveFiles.Add(newArchiveFile); + + var cleanupArchiveFiles = fileArchiveStyle.CheckArchiveCleanup(archiveFilePattern, existingArchiveFiles, MaxArchiveFiles, MaxArchiveDays); + foreach (var oldArchiveFile in cleanupArchiveFiles) + { + DeleteOldArchiveFile(oldArchiveFile.FileName); + } + } + } + + private static void ExcludeActiveFileFromOldArchiveFiles(FileInfo currentFile, List existingArchiveFiles) + { + if (existingArchiveFiles.Count > 0) + { + var archiveDirectory = Path.GetDirectoryName(existingArchiveFiles[0].FileName); + if (string.Equals(archiveDirectory, currentFile.DirectoryName, StringComparison.OrdinalIgnoreCase)) + { + // Extra handling when archive-directory is the same as logging-directory + for (int i = 0; i < existingArchiveFiles.Count; ++i) + { + if (string.Equals(existingArchiveFiles[i].FileName, currentFile.FullName, StringComparison.OrdinalIgnoreCase)) + { + existingArchiveFiles.RemoveAt(i); + break; + } + } + } + } + } + + /// + /// Gets the pattern that archive files will match + /// + /// Filename of the log file + /// Log event that the instance is currently processing. + /// A string with a pattern that will match the archive filenames + private string GetArchiveFileNamePattern(string fileName, LogEventInfo eventInfo) + { + if (_fullArchiveFileName is null) + { + if (EnableArchiveFileCompression) + return Path.ChangeExtension(fileName, ".zip"); + else + return fileName; + } + else + { + //The archive file name is given. There are two possibilities + //(1) User supplied the Filename with pattern + //(2) User supplied the normal filename + string archiveFileName = _fullArchiveFileName.Render(eventInfo); + return archiveFileName; + } + } + + /// + /// Archives the file if it should be archived. + /// + /// The file name to check for. + /// Log event that the instance is currently processing. + /// The size in bytes of the next chunk of data to be written in the file. + /// The DateTime of the previous log event for this file. + /// File has just been opened. + /// True when archive operation of the file was completed (by this target or a concurrent target) + private bool TryArchiveFile(string fileName, LogEventInfo ev, int upcomingWriteSize, DateTime previousLogEventTimestamp, bool initializedNewFile) + { + if (!IsArchivingEnabled) + return false; + + string archiveFile = string.Empty; + + BaseFileAppender archivedAppender = null; + + try + { + archiveFile = GetArchiveFileName(fileName, ev, upcomingWriteSize, previousLogEventTimestamp, initializedNewFile); + if (!string.IsNullOrEmpty(archiveFile)) + { + archivedAppender = TryCloseFileAppenderBeforeArchive(fileName, archiveFile); + } + + // Closes all file handles if any archive operation has been detected by file-watcher + _fileAppenderCache.InvalidateAppendersForArchivedFiles(); + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "{0}: Failed to check archive for file '{1}'.", this, fileName); + if (ExceptionMustBeRethrown(exception)) + { + throw; + } + } + + if (string.IsNullOrEmpty(archiveFile)) + return false; + + try + { + try + { + if (archivedAppender is BaseMutexFileAppender mutexFileAppender && mutexFileAppender.ArchiveMutex != null) + { + mutexFileAppender.ArchiveMutex.WaitOne(); + } + else if (!IsSimpleKeepFileOpen) + { + InternalLogger.Debug("{0}: Archive mutex not available for file '{1}'", this, archiveFile); + } + } + catch (AbandonedMutexException) + { + // ignore the exception, another process was killed without properly releasing the mutex + // the mutex has been acquired, so proceed to writing + // See: https://msdn.microsoft.com/en-us/library/system.threading.abandonedmutexexception.aspx + } + + ArchiveFileAfterCloseFileAppender(archivedAppender, archiveFile, ev, upcomingWriteSize, previousLogEventTimestamp); + return true; + } + finally + { + if (archivedAppender is BaseMutexFileAppender mutexFileAppender) + mutexFileAppender.ArchiveMutex?.ReleaseMutex(); + + archivedAppender?.Dispose(); // Dispose of Archive Mutex + } + } + + /// + /// Closes any active file-appenders that matches the input filenames. + /// File-appender is requested to invalidate/close its filehandle, but keeping its archive-mutex alive + /// + private BaseFileAppender TryCloseFileAppenderBeforeArchive(string fileName, string archiveFile) + { + InternalLogger.Trace("{0}: Archive attempt for file '{1}'", this, archiveFile); + BaseFileAppender archivedAppender = _fileAppenderCache.InvalidateAppender(fileName); + if (fileName != archiveFile) + { + var fileAppender = _fileAppenderCache.InvalidateAppender(archiveFile); + archivedAppender = archivedAppender ?? fileAppender; + } + + if (!string.IsNullOrEmpty(_previousLogFileName) && _previousLogFileName != archiveFile && _previousLogFileName != fileName) + { + var fileAppender = _fileAppenderCache.InvalidateAppender(_previousLogFileName); + archivedAppender = archivedAppender ?? fileAppender; + } + + return archivedAppender; + } + + private void ArchiveFileAfterCloseFileAppender(BaseFileAppender archivedAppender, string archiveFile, LogEventInfo ev, int upcomingWriteSize, DateTime previousLogEventTimestamp) + { + try + { + DateTime fallbackFileCreationTimeSource = previousLogEventTimestamp; + + if (archivedAppender != null && IsSimpleKeepFileOpen) + { + var fileCreationTimeUtc = archivedAppender.GetFileCreationTimeUtc(); + if (fileCreationTimeUtc > DateTime.MinValue) + { + var fileCreationTimeSource = Time.TimeSource.Current.FromSystemTime(fileCreationTimeUtc.Value); + if (fileCreationTimeSource < fallbackFileCreationTimeSource || fallbackFileCreationTimeSource == DateTime.MinValue) + { + fallbackFileCreationTimeSource = fileCreationTimeSource; + } + } + } + + // Check again if archive is needed. We could have been raced by another process + var validatedArchiveFile = GetArchiveFileName(archiveFile, ev, upcomingWriteSize, fallbackFileCreationTimeSource, false); + if (string.IsNullOrEmpty(validatedArchiveFile)) + { + InternalLogger.Debug("{0}: Skip archiving '{1}' because no longer necessary", this, archiveFile); + _initializedFiles.Remove(archiveFile); + } + else + { + if (archiveFile != validatedArchiveFile) + { + _initializedFiles.Remove(archiveFile); + archiveFile = validatedArchiveFile; + } + _initializedFiles.Remove(archiveFile); + + DoAutoArchive(archiveFile, ev, previousLogEventTimestamp, false); + } + + if (_previousLogFileName == archiveFile) + { + _previousLogFileName = null; + _previousLogEventTimestamp = null; + } + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "{0}: Failed to archive file '{1}'.", this, archiveFile); + if (ExceptionMustBeRethrown(exception)) + { + throw; + } + } + } + + /// + /// Indicates if the automatic archiving process should be executed. + /// + /// File name to be written. + /// Log event that the instance is currently processing. + /// The size in bytes of the next chunk of data to be written in the file. + /// The DateTime of the previous log event for this file. + /// File has just been opened. + /// Filename to archive. If null, then nothing to archive. + private string GetArchiveFileName(string fileName, LogEventInfo ev, int upcomingWriteSize, DateTime previousLogEventTimestamp, bool initializedNewFile) + { + fileName = fileName ?? _previousLogFileName; + if (!string.IsNullOrEmpty(fileName)) + { + return GetArchiveFileNameBasedOnFileSize(fileName, upcomingWriteSize, initializedNewFile) ?? + GetArchiveFileNameBasedOnTime(fileName, ev, previousLogEventTimestamp, initializedNewFile); + } + + return null; + } + + /// + /// Returns the correct filename to archive + /// + private string GetPotentialFileForArchiving(string fileName) + { + if (!string.IsNullOrEmpty(fileName)) + { + return fileName; + } + + if (!string.IsNullOrEmpty(_previousLogFileName)) + { + return _previousLogFileName; + } + + return fileName; + } + + /// + /// Gets the file name for archiving, or null if archiving should not occur based on file size. + /// + /// File name to be written. + /// The size in bytes of the next chunk of data to be written in the file. + /// File has just been opened. + /// Filename to archive. If null, then nothing to archive. + private string GetArchiveFileNameBasedOnFileSize(string fileName, int upcomingWriteSize, bool initializedNewFile) + { + if (ArchiveAboveSize == ArchiveAboveSizeDisabled) + { + return null; + } + + var archiveFileName = GetPotentialFileForArchiving(fileName); + if (string.IsNullOrEmpty(archiveFileName)) + { + return null; + } + + //this is an expensive call + var fileLength = _fileAppenderCache.GetFileLength(archiveFileName); + if (!fileLength.HasValue) + { + archiveFileName = TryFallbackToPreviousLogFileName(archiveFileName, initializedNewFile); + if (!string.IsNullOrEmpty(archiveFileName)) + { + upcomingWriteSize = 0; + return GetArchiveFileNameBasedOnFileSize(archiveFileName, upcomingWriteSize, false); + } + else + { + return null; + } + } + + if (archiveFileName != fileName) + { + upcomingWriteSize = 0; // Not going to write to this file + } + + var shouldArchive = (fileLength.Value + upcomingWriteSize) > ArchiveAboveSize; + if (shouldArchive) + { + InternalLogger.Debug("{0}: Start archiving '{1}' because FileSize={2} + {3} is larger than ArchiveAboveSize={4}", this, archiveFileName, fileLength.Value, upcomingWriteSize, ArchiveAboveSize); + return archiveFileName; // Will re-check if archive is still necessary after flush/close file + } + + return null; + } + + /// + /// Check if archive operation should check previous filename, because FileAppenderCache tells us current filename no longer exists + /// + private string TryFallbackToPreviousLogFileName(string archiveFileName, bool initializedNewFile) + { + if (!initializedNewFile && _initializedFiles.Remove(archiveFileName)) + { + // Register current filename needs re-initialization + InternalLogger.Debug("{0}: Invalidate appender as archive file no longer exists: '{1}'", this, archiveFileName); + _fileAppenderCache.InvalidateAppender(archiveFileName)?.Dispose(); + } + + if (!string.IsNullOrEmpty(_previousLogFileName) && !string.Equals(archiveFileName, _previousLogFileName, StringComparison.OrdinalIgnoreCase)) + { + return _previousLogFileName; + } + + return string.Empty; + } + + /// + /// Returns the file name for archiving, or null if archiving should not occur based on date/time. + /// + /// File name to be written. + /// Log event that the instance is currently processing. + /// The DateTime of the previous log event for this file. + /// File has just been opened. + /// Filename to archive. If null, then nothing to archive. + private string GetArchiveFileNameBasedOnTime(string fileName, LogEventInfo logEvent, DateTime previousLogEventTimestamp, bool initializedNewFile) + { + if (ArchiveEvery == FileArchivePeriod.None) + { + return null; + } + + var archiveFileName = GetPotentialFileForArchiving(fileName); + if (string.IsNullOrEmpty(archiveFileName)) + { + return null; + } + + DateTime? creationTimeSource = TryGetArchiveFileCreationTimeSource(archiveFileName, previousLogEventTimestamp); + if (!creationTimeSource.HasValue) + { + archiveFileName = TryFallbackToPreviousLogFileName(archiveFileName, initializedNewFile); + if (!string.IsNullOrEmpty(archiveFileName)) + { + return GetArchiveFileNameBasedOnTime(archiveFileName, logEvent, previousLogEventTimestamp, false); + } + else + { + return null; + } + } + + DateTime fileCreateTime = TruncateArchiveTime(creationTimeSource.Value, ArchiveEvery); + DateTime logEventTime = TruncateArchiveTime(logEvent.TimeStamp, ArchiveEvery); + if (fileCreateTime != logEventTime) + { + string formatString = GetArchiveDateFormatString(string.Empty); + var validLogEventTime = EnsureValidLogEventTimeStamp(logEvent.TimeStamp, creationTimeSource.Value); + string fileCreated = creationTimeSource.Value.ToString(formatString, CultureInfo.InvariantCulture); + string logEventRecorded = validLogEventTime.ToString(formatString, CultureInfo.InvariantCulture); + var shouldArchive = fileCreated != logEventRecorded; + if (shouldArchive) + { + InternalLogger.Debug("{0}: Start archiving '{1}' because FileCreatedTime='{2}' is older than now '{3}' using ArchiveEvery='{4}'", this, archiveFileName, fileCreated, logEventRecorded, formatString); + return archiveFileName; // Will re-check if archive is still necessary after flush/close file + } + } + + return null; + } + + private DateTime? TryGetArchiveFileCreationTimeSource(string fileName, DateTime previousLogEventTimestamp) + { + // Linux FileSystems doesn't always have file-birth-time, so NLog tries to provide a little help + DateTime? fallbackTimeSourceLinux = (previousLogEventTimestamp != DateTime.MinValue && IsSimpleKeepFileOpen) ? previousLogEventTimestamp : (DateTime?)null; + var creationTimeSource = _fileAppenderCache.GetFileCreationTimeSource(fileName, fallbackTimeSourceLinux); + if (!creationTimeSource.HasValue) + return null; + + if (previousLogEventTimestamp > DateTime.MinValue && previousLogEventTimestamp < creationTimeSource) + { + if (TruncateArchiveTime(previousLogEventTimestamp, FileArchivePeriod.Minute) < TruncateArchiveTime(creationTimeSource.Value, FileArchivePeriod.Minute) && PlatformDetector.IsUnix) + { + if (IsSimpleKeepFileOpen) + { + InternalLogger.Debug("{0}: Adjusted file creation time from {1} to {2}. Linux FileSystem probably don't support file birthtime.", this, creationTimeSource, previousLogEventTimestamp); + creationTimeSource = previousLogEventTimestamp; + } + else + { + InternalLogger.Debug("{0}: File creation time {1} newer than previous file write time {2}. Linux FileSystem probably don't support file birthtime, unless multiple applications are writing to the same file. Configure FileTarget.KeepFileOpen=true AND FileTarget.ConcurrentWrites=false, so NLog can fix this.", this, creationTimeSource, previousLogEventTimestamp); + } + } + } + + return creationTimeSource; + } + + /// + /// Truncates the input-time, so comparison of low resolution times (like dates) are not affected by ticks + /// + /// High resolution Time + /// Time Resolution Level + /// Truncated Low Resolution Time + private static DateTime TruncateArchiveTime(DateTime input, FileArchivePeriod resolution) + { + switch (resolution) + { + case FileArchivePeriod.Year: + return new DateTime(input.Year, 1, 1, 0, 0, 0, 0, input.Kind); + case FileArchivePeriod.Month: + return new DateTime(input.Year, input.Month, 1, 0, 0, 0, input.Kind); + case FileArchivePeriod.Day: + return input.Date; + case FileArchivePeriod.Hour: + return input.AddTicks(-(input.Ticks % TimeSpan.TicksPerHour)); + case FileArchivePeriod.Minute: + return input.AddTicks(-(input.Ticks % TimeSpan.TicksPerMinute)); + case FileArchivePeriod.Sunday: + return CalculateNextWeekday(input.Date, DayOfWeek.Sunday); + case FileArchivePeriod.Monday: + return CalculateNextWeekday(input.Date, DayOfWeek.Monday); + case FileArchivePeriod.Tuesday: + return CalculateNextWeekday(input.Date, DayOfWeek.Tuesday); + case FileArchivePeriod.Wednesday: + return CalculateNextWeekday(input.Date, DayOfWeek.Wednesday); + case FileArchivePeriod.Thursday: + return CalculateNextWeekday(input.Date, DayOfWeek.Thursday); + case FileArchivePeriod.Friday: + return CalculateNextWeekday(input.Date, DayOfWeek.Friday); + case FileArchivePeriod.Saturday: + return CalculateNextWeekday(input.Date, DayOfWeek.Saturday); + default: + return input; // Unknown time-resolution-truncate, leave unchanged + } + } + + private void AutoCloseAppendersAfterArchive(object sender, EventArgs state) + { + bool lockTaken = Monitor.TryEnter(SyncRoot, TimeSpan.FromSeconds(2)); + if (!lockTaken) + return; // Archive events triggered by FileWatcher are important, but not life critical + + try + { + if (!IsInitialized) + { + return; + } + + InternalLogger.Trace("{0}: Auto Close FileAppenders after archive", this); + _fileAppenderCache.CloseExpiredAppenders(DateTime.MinValue); + } + catch (Exception exception) + { +#if DEBUG + if (exception.MustBeRethrownImmediately()) + { + throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) + } +#endif + + InternalLogger.Warn(exception, "{0}: Exception in AutoCloseAppendersAfterArchive", this); + } + finally + { + Monitor.Exit(SyncRoot); + } + } + + private void AutoClosingTimerCallback(object sender, EventArgs state) + { + bool lockTaken = Monitor.TryEnter(SyncRoot, TimeSpan.FromSeconds(0.5)); + if (!lockTaken) + return; // Timer will trigger again, no need for timers to queue up + + try + { + if (!IsInitialized) + { + return; + } + + if (OpenFileCacheTimeout > 0) + { + DateTime expireTimeUtc = DateTime.UtcNow.AddSeconds(-OpenFileCacheTimeout); + InternalLogger.Trace("{0}: Auto Close FileAppenders", this); + _fileAppenderCache.CloseExpiredAppenders(expireTimeUtc); + } + + if (OpenFileFlushTimeout > 0 && !AutoFlush) + { + ConditionalFlushOpenFileAppenders(); + } + } + catch (Exception exception) + { +#if DEBUG + if (exception.MustBeRethrownImmediately()) + { + throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) + } +#endif + + InternalLogger.Warn(exception, "{0}: Exception in AutoClosingTimerCallback", this); + } + finally + { + Monitor.Exit(SyncRoot); + } + } + + private void ConditionalFlushOpenFileAppenders() + { + DateTime flushTime = Time.TimeSource.Current.Time.AddSeconds(-Math.Max(OpenFileFlushTimeout, 5) * 2); + + bool flushAppenders = false; + foreach (var file in _initializedFiles) + { + if (file.Value > flushTime) + { + flushAppenders = true; + break; + } + } + + if (flushAppenders) + { + // Only request flush of file-handles, when something has been written + InternalLogger.Trace("{0}: Auto Flush FileAppenders", this); + _fileAppenderCache.FlushAppenders(); + } + } + + /// + /// Evaluates which parts of a file should be written (header, content, footer) based on various properties of + /// instance and writes them. + /// + /// File name to be written. + /// Raw sequence of to be written into the content part of the file. + /// File has just been opened. + private void WriteToFile(string fileName, ArraySegment bytes, bool initializedNewFile) + { + BaseFileAppender appender = _fileAppenderCache.AllocateAppender(fileName); + try + { + if (initializedNewFile) + { + WriteHeaderAndBom(appender); + } + + appender.Write(bytes.Array, bytes.Offset, bytes.Count); + + if (AutoFlush) + { + appender.Flush(); + } + } + catch (Exception ex) + { + InternalLogger.Error(ex, "{0}: Failed write to file '{1}'.", this, fileName); + _fileAppenderCache.InvalidateAppender(fileName)?.Dispose(); + throw; + } + } + + /// + /// Initialize a file to be used by the instance. Based on the number of initialized + /// files and the values of various instance properties clean up and/or archiving processes can be invoked. + /// + /// File name to be written. + /// Log event that the instance is currently processing. + /// The DateTime of the previous log event for this file (DateTime.MinValue if just initialized). + private DateTime InitializeFile(string fileName, LogEventInfo logEvent) + { + if (_initializedFiles.Count != 0 && logEvent.TimeStamp == _previousLogEventTimestamp && _previousLogFileName == fileName) + { + return logEvent.TimeStamp; + } + + var now = logEvent.TimeStamp; + if (!_initializedFiles.TryGetValue(fileName, out var lastTime)) + { + PrepareForNewFile(fileName, logEvent); + + _initializedFilesCounter++; + if (_initializedFilesCounter > OpenFileCacheSize) + { + // Attempt to write footer, before closing file appender + _initializedFilesCounter = 0; + CleanupInitializedFiles(); + _initializedFilesCounter = Math.Min(_initializedFiles.Count, OpenFileCacheSize / 2); + } + + _initializedFiles[fileName] = now; + return DateTime.MinValue; + } + else if (lastTime != now) + { + now = EnsureValidLogEventTimeStamp(now, lastTime); + _initializedFiles[fileName] = now; + } + + return lastTime; + } + + private static DateTime EnsureValidLogEventTimeStamp(DateTime logEventTimeStamp, DateTime previousTimeStamp) + { + // Truncating using DateTime.Date is "expensive", so first check if it look like it is from the past + if (logEventTimeStamp < previousTimeStamp && logEventTimeStamp.Date < previousTimeStamp.Date) + { + // Received LogEvent from the past when comparing to the previous timestamp + var currentTime = TimeSource.Current.Time; + if (logEventTimeStamp.Date < currentTime.AddMinutes(-1).Date) + { + // It is not because the machine-time has changed. Probably a LogEvent from the past + if (currentTime.Date < previousTimeStamp.Date) + return currentTime; // Previous timestamp is from the future. We choose machine-time + else + return previousTimeStamp; + } + } + + return logEventTimeStamp; + } + + private void CloseInvalidFileHandle(string fileName) + { + try + { + _fileAppenderCache.InvalidateAppender(fileName)?.Dispose(); + } + finally + { + _initializedFiles.Remove(fileName); // Skip finalize non-existing file + } + } + + /// + /// Writes the file footer and finalizes the file in instance internal structures. + /// + /// File name to close. + /// Indicates if the file is being finalized for archiving. + private void FinalizeFile(string fileName, bool isArchiving = false) + { + try + { + InternalLogger.Trace("{0}: FinalizeFile '{1}, isArchiving: {2}'", this, fileName, isArchiving); + if ((isArchiving) || (!WriteFooterOnArchivingOnly)) + WriteFooter(fileName); + } + finally + { + CloseInvalidFileHandle(fileName); + } + } + + /// + /// Writes the footer information to a file. + /// + /// The file path to write to. + private void WriteFooter(string fileName) + { + ArraySegment footerBytes = GetLayoutBytes(Footer); + if (footerBytes.Count > 0 && File.Exists(fileName)) + { + WriteToFile(fileName, footerBytes, false); + } + } + + /// + /// Decision logic whether to archive logfile on startup. + /// and properties. + /// + /// File name to be written. + /// Decision whether to archive or not. + internal bool ShouldArchiveOldFileOnStartup(string fileName) + { + if (_archiveOldFileOnStartup == false) + { + // explicitly disabled and not the default + return false; + } + + var aboveSizeSet = ArchiveOldFileOnStartupAboveSize > 0; + if (aboveSizeSet) + { + // Check whether size threshold exceeded + var length = _fileAppenderCache.GetFileLength(fileName); + return length.HasValue && length.Value > ArchiveOldFileOnStartupAboveSize; + } + + // No size threshold specified, use archiveOldFileOnStartup flag + return _archiveOldFileOnStartup == true; + } + + /// + /// Invokes the archiving and clean up of older archive file based on the values of + /// and + /// properties respectively. + /// + /// File name to be written. + /// Log event that the instance is currently processing. + private void PrepareForNewFile(string fileName, LogEventInfo logEvent) + { + InternalLogger.Debug("{0}: Preparing for new file: '{1}'", this, fileName); + RefreshArchiveFilePatternToWatch(fileName, logEvent); + + try + { + if (ShouldArchiveOldFileOnStartup(fileName)) + { + DoAutoArchive(fileName, logEvent, DateTime.MinValue, true); + } + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "{0}: Unable to archive old log file '{1}'.", this, fileName); + if (ExceptionMustBeRethrown(exception)) + { + throw; + } + } + + if (DeleteOldFileOnStartup) + { + DeleteOldArchiveFile(fileName); + } + + try + { + string archiveFilePattern = GetArchiveFileNamePattern(fileName, logEvent); + if (!string.IsNullOrEmpty(archiveFilePattern)) + { + var fileArchiveStyle = GetFileArchiveHelper(archiveFilePattern); + if (fileArchiveStyle.AttemptCleanupOnInitializeFile(archiveFilePattern, MaxArchiveFiles, MaxArchiveDays)) + { + var existingArchiveFiles = fileArchiveStyle.GetExistingArchiveFiles(archiveFilePattern); + if (existingArchiveFiles.Count > 0) + { + CleanupOldArchiveFiles(new FileInfo(fileName), archiveFilePattern, existingArchiveFiles); + } + } + } + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "FileTarget(Name={0}): Failed to cleanup old archive files when starting on new file: '{1}'", Name, fileName); + + if (ExceptionMustBeRethrown(exception)) + { + throw; + } + } + } + + /// + /// Creates the file specified in and writes the file content in each entirety i.e. + /// Header, Content and Footer. + /// + /// The name of the file to be written. + /// Sequence of to be written in the content section of the file. + /// First attempt to write? + /// This method is used when the content of the log file is re-written on every write. + private void ReplaceFileContent(string fileName, ArraySegment bytes, bool firstAttempt) + { + try + { + using (FileStream fs = File.Create(fileName)) + { + ArraySegment headerBytes = GetLayoutBytes(Header); + if (headerBytes.Count > 0) + { + fs.Write(headerBytes.Array, headerBytes.Offset, headerBytes.Count); + } + + fs.Write(bytes.Array, bytes.Offset, bytes.Count); + + ArraySegment footerBytes = GetLayoutBytes(Footer); + if (footerBytes.Count > 0) + { + fs.Write(footerBytes.Array, footerBytes.Offset, footerBytes.Count); + } + } + } + catch (DirectoryNotFoundException) + { + if (!CreateDirs || !firstAttempt) + { + throw; + } + Directory.CreateDirectory(Path.GetDirectoryName(fileName)); + //retry. + ReplaceFileContent(fileName, bytes, false); + } + } + + private static bool InitialValueBom(Encoding encoding) + { + // Initial of true for UTF 16 and UTF 32 + const int utf16 = 1200; + const int utf16Be = 1201; + const int utf32 = 12000; + const int urf32Be = 12001; + var codePage = encoding?.CodePage ?? 0; + return codePage == utf16 + || codePage == utf16Be + || codePage == utf32 + || codePage == urf32Be; + } + + /// + /// Writes the header information and byte order mark to a file. + /// + /// File appender associated with the file. + private void WriteHeaderAndBom(BaseFileAppender appender) + { + //performance: cheap check before checking file info + if (Header is null && !WriteBom) return; + + var length = appender.GetFileLength(); + // File is empty or file info cannot be obtained + var isNewOrEmptyFile = length is null || length == 0; + + if (isNewOrEmptyFile && WriteBom) + { + InternalLogger.Trace("{0}: Write byte order mark from encoding={1}", this, Encoding); + var preamble = Encoding.GetPreamble(); + if (preamble.Length > 0) + appender.Write(preamble, 0, preamble.Length); + } + + if (Header != null && (isNewOrEmptyFile || WriteHeaderWhenInitialFileNotEmpty)) + { + InternalLogger.Trace("{0}: Write header", this); + ArraySegment headerBytes = GetLayoutBytes(Header); + if (headerBytes.Count > 0) + { + appender.Write(headerBytes.Array, headerBytes.Offset, headerBytes.Count); + } + } + } + + /// + /// The sequence of to be written in a file after applying any formatting and any + /// transformations required from the . + /// + /// The layout used to render output message. + /// Sequence of to be written. + /// Usually it is used to render the header and hooter of the files. + private ArraySegment GetLayoutBytes(Layout layout) + { + if (layout is null) + { + return default(ArraySegment); + } + + using (var targetBuilder = ReusableLayoutBuilder.Allocate()) + using (var targetBuffer = _reusableEncodingBuffer.Allocate()) + { + var nullEvent = LogEventInfo.CreateNullEvent(); + layout.Render(nullEvent, targetBuilder.Result); + targetBuilder.Result.Append(NewLineChars); + using (MemoryStream ms = new MemoryStream(targetBuilder.Result.Length)) + { + TransformBuilderToStream(nullEvent, targetBuilder.Result, targetBuffer.Result, ms); + return new ArraySegment(ms.ToArray()); + } + } + } + } +} From 5c71f664d1782db686ce86a022e2e79aee8fd814 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 26 Jan 2025 17:17:52 +0100 Subject: [PATCH 033/224] Duplicate ConcurrentFileTargetTests --- .../ConcurrentFileTargetTests.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{NLog.UnitTests/Targets/FileTargetTests.cs => NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs} (100%) diff --git a/tests/NLog.UnitTests/Targets/FileTargetTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs similarity index 100% rename from tests/NLog.UnitTests/Targets/FileTargetTests.cs rename to tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs From f27fbb7ec3d5df2f83f3c5b52353688577ac53ed Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 26 Jan 2025 17:18:26 +0100 Subject: [PATCH 034/224] Restore FileTargetTests --- .../NLog.UnitTests/Targets/FileTargetTests.cs | 4521 +++++++++++++++++ 1 file changed, 4521 insertions(+) create mode 100644 tests/NLog.UnitTests/Targets/FileTargetTests.cs diff --git a/tests/NLog.UnitTests/Targets/FileTargetTests.cs b/tests/NLog.UnitTests/Targets/FileTargetTests.cs new file mode 100644 index 0000000000..50b16c2ada --- /dev/null +++ b/tests/NLog.UnitTests/Targets/FileTargetTests.cs @@ -0,0 +1,4521 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.UnitTests.Targets +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using NLog.Config; + using NLog.Layouts; + using NLog.Targets; + using NLog.Targets.Wrappers; + using NLog.Time; + using NSubstitute; + using Xunit; + + public class FileTargetTests : NLogTestBase + { + private readonly Logger logger = LogManager.GetLogger("NLog.UnitTests.Targets.FileTargetTests"); + + public static IEnumerable SimpleFileTest_TestParameters + { + get + { + var booleanValues = new[] { true, false }; + return + from concurrentWrites in booleanValues + from keepFileOpen in booleanValues + from forceMutexConcurrentWrites in booleanValues + where UniqueBaseAppender(concurrentWrites, keepFileOpen, forceMutexConcurrentWrites) + from forceManaged in booleanValues + select new object[] { concurrentWrites, keepFileOpen, forceManaged, forceMutexConcurrentWrites }; + } + } + + private static bool UniqueBaseAppender(bool concurrentWrites, bool keepFileOpen, bool forceMutexConcurrentWrites) + { + if (!keepFileOpen && !concurrentWrites && !forceMutexConcurrentWrites) + return true; + if (!keepFileOpen && concurrentWrites && !forceMutexConcurrentWrites) + return true; + if (keepFileOpen && !forceMutexConcurrentWrites) + return true; + return false; + } + + [Fact] + public void SetupBuilder_WriteToFile() + { + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + LogFactory logFactory = null; + + try + { + logFactory = new LogFactory().Setup().LoadConfiguration(c => + { + c.ForLogger().WriteToFile(Path.Combine(tempDir, "${logger}.txt"), "${message}", Encoding.UTF8, LineEndingMode.LF); + }).LogFactory; + + logFactory.GetLogger("SetupBuilder").Info("Hello"); + + AssertFileContents(Path.Combine(tempDir, "SetupBuilder.txt"), "Hello\n", Encoding.UTF8); + } + finally + { + logFactory?.Shutdown(); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Theory] + [MemberData(nameof(SimpleFileTest_TestParameters))] + public void SimpleFileTest(bool concurrentWrites, bool keepFileOpen, bool forceManaged, bool forceMutexConcurrentWrites) + { + var logFile = Path.GetTempFileName(); + try + { + var fileTarget = new FileTarget + { + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}", + OpenFileCacheTimeout = 0, + ConcurrentWrites = concurrentWrites, + KeepFileOpen = keepFileOpen, + ForceManaged = forceManaged, + ForceMutexConcurrentWrites = forceMutexConcurrentWrites, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + + LogManager.Configuration = null; // Flush + + AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + } + } + + [Theory] + [MemberData(nameof(SimpleFileTest_TestParameters))] + public void SimpleFileDeleteTest(bool concurrentWrites, bool keepFileOpen, bool forceManaged, bool forceMutexConcurrentWrites) + { + bool isSimpleKeepFileOpen = keepFileOpen && !concurrentWrites +#if NETFRAMEWORK && !MONO + && IsLinux() +#endif + ; + +#if MONO + if (IsLinux() && concurrentWrites && keepFileOpen) + { + Console.WriteLine("[SKIP] FileTargetTests.SimpleFileDeleteTest Not supported on MONO on Travis, because of FileSystemWatcher not working"); + return; + } +#endif + foreach (var archiveSameFolder in new[] { true, false }) + { + RetryingIntegrationTest(3, () => + { + var logPath = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString(), "Archive"); + var logFile = Path.GetFullPath(Path.Combine(logPath, "..", "nlogA.txt")); + //var arhiveFile = archiveSameFolder ? Path.GetFullPath(Path.Combine(logPath, "..", "nlogB.txt")) : Path.GetFullPath(Path.Combine(logPath, "nlogB.txt")); + var arhiveFile = Path.GetFullPath(Path.Combine(logPath, archiveSameFolder ? ".." : ".", "nlogB.txt")); + + try + { + var fileTarget = new FileTarget + { + FileName = SimpleLayout.Escape(logFile), + ArchiveFileName = SimpleLayout.Escape(arhiveFile), + ArchiveEvery = FileArchivePeriod.Year, + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}", + OpenFileCacheTimeout = 0, + EnableFileDelete = true, + ConcurrentWrites = concurrentWrites, + KeepFileOpen = keepFileOpen, + ForceManaged = forceManaged, + ForceMutexConcurrentWrites = forceMutexConcurrentWrites, + ArchiveAboveSize = archiveSameFolder ? 1000000 : 0, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + + LogManager.Flush(); + + Directory.CreateDirectory(Path.GetDirectoryName(arhiveFile)); + File.Move(logFile, arhiveFile); + + if (isSimpleKeepFileOpen) + Thread.Sleep(1500); // Ensure EnableFileDeleteSimpleMonitor will trigger + else if (keepFileOpen) + Thread.Sleep(150); // Allow AutoClose-Timer-Thread to react (FileWatcher schedules timer after 50 msec) + + logger.Info("bbb"); + + LogManager.Configuration = null; + + AssertFileContents(logFile, "Info bbb\n", Encoding.UTF8); + } + finally + { + if (File.Exists(arhiveFile)) + { + File.Delete(arhiveFile); + } + + if (File.Exists(logFile)) + { + File.Delete(logFile); + } + + if (Directory.Exists(Path.GetDirectoryName(arhiveFile))) + { + Directory.Delete(Path.GetDirectoryName(arhiveFile)); + } + + if (Directory.Exists(Path.GetDirectoryName(logFile))) + { + Directory.Delete(Path.GetDirectoryName(logFile)); + } + } + }); + } + } + + /// + /// There was a bug when creating the file in the root. + /// + /// Please note that this test can fail because the unit test doesn't have write access in the root. + /// + [Fact] + public void SimpleFileTestInRoot() + { + if (NLog.Internal.PlatformDetector.IsWin32) + { + var dirPath = "C:\\"; + var directoryInfo = new DirectoryInfo(dirPath); + + if (directoryInfo.Exists) + { + return; + } + + var logFile = dirPath + "nlog-test.log"; + SimpleFileWriteLogTest(logFile); + } + } + + + [Fact] + public void SimpleFileWithSpecialCharsTest() + { + var logFile = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid() + "!@#$%^&()_-=+ .log"); + SimpleFileWriteLogTest(logFile); + } + + private void SimpleFileWriteLogTest(string logFile) + { + try + { + var fileTarget = new FileTarget + { + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}", + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + + LogManager.Configuration = null; // Flush + + AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + } + } + + [Fact] + public void SimpleFileTestWriteBom() + { + var logFile = Path.GetTempFileName(); + try + { + var fileTarget = new FileTarget + { + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Encoding = Encoding.UTF8, + WriteBom = true, + Layout = "${level} ${message}", + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + + LogManager.Configuration = null; // Flush + + AssertFileContents(logFile, "Debug aaa\n", Encoding.UTF8, true); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + } + } + +#if !MONO + /// + /// If a drive doesn't existing, before repeatably creating a dir was tried. This test was taking +60 seconds + /// + [Theory] + [MemberData(nameof(SimpleFileTest_TestParameters))] + public void NonExistingDriveShouldNotDelayMuch(bool concurrentWrites, bool keepFileOpen, bool forceManaged, bool forceMutexConcurrentWrites) + { + var nonExistingDrive = GetFirstNonExistingDriveWindows(); + + var logFile = nonExistingDrive + "://dont-extist/no-timeout.log"; + + DateTime start = DateTime.UtcNow; + + try + { + using (new NoThrowNLogExceptions()) + { + var fileTarget = new FileTarget + { + FileName = logFile, + Layout = "${level} ${message}", + ConcurrentWrites = concurrentWrites, + KeepFileOpen = keepFileOpen, + ForceManaged = forceManaged, + ForceMutexConcurrentWrites = forceMutexConcurrentWrites, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + for (int i = 0; i < 300; i++) + { + logger.Debug("aaa"); + } + + LogManager.Configuration = null; // Flush + + Assert.True(DateTime.UtcNow - start < TimeSpan.FromSeconds(5)); + } + } + finally + { + //should not be necessary + if (File.Exists(logFile)) + File.Delete(logFile); + } + } + + /// + /// Get first drive letter of non-existing drive + /// + /// + private static char GetFirstNonExistingDriveWindows() + { + var existingDrives = new HashSet(Environment.GetLogicalDrives().Select(d => d[0].ToString()), + StringComparer.OrdinalIgnoreCase); + var nonExistingDrive = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToList().First(driveLetter => !existingDrives.Contains(driveLetter.ToString())); + return nonExistingDrive; + } + +#endif + + [Fact] + public void RollingArchiveEveryMonth() + { + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var defaultTimeSource = TimeSource.Current; + + try + { + var timeSource = new TimeSourceTests.ShiftedTimeSource(DateTimeKind.Local); + if (timeSource.Time.Minute == 59) + { + // Avoid double-archive due to overflow of the hour. + timeSource.AddToLocalTime(TimeSpan.FromMinutes(1)); + timeSource.AddToSystemTime(TimeSpan.FromMinutes(1)); + } + TimeSource.Current = timeSource; + + var fileTarget = new FileTarget + { + FileName = Path.Combine(tempDir, "${date:format=dd}_AppName.log"), + LineEnding = LineEndingMode.LF, + Layout = "${message}", + ArchiveNumbering = ArchiveNumberingMode.Rolling, + ArchiveEvery = FileArchivePeriod.Month, + MaxArchiveFiles = 1, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + for (int i = 0; i < 12; ++i) + { + for (int j = 0; j < 31; ++j) + { + logger.Debug("aaa"); + timeSource.AddToLocalTime(TimeSpan.FromDays(1)); + timeSource.AddToSystemTime(TimeSpan.FromDays(1)); + } + } + + var files = Directory.GetFiles(tempDir); + // Cleanup doesn't work, as all file names has the same timestamp + if (files.Length < 28 || files.Length > 31) + Assert.Equal(30, files.Length); + + foreach (var file in files) + { + Assert.Equal(14, Path.GetFileName(file).Length); + } + + fileTarget.Close(); + } + finally + { + TimeSource.Current = defaultTimeSource; + + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + +#if !MONO + [Theory] +#else + [Theory(Skip="Not supported on MONO on Travis, because of File birthtime not working")] +#endif + [InlineData(false, false, ArchiveNumberingMode.DateAndSequence)] + [InlineData(false, true, ArchiveNumberingMode.DateAndSequence)] + [InlineData(false, false, ArchiveNumberingMode.Sequence)] + [InlineData(false, true, ArchiveNumberingMode.Sequence)] + [InlineData(true, false, ArchiveNumberingMode.DateAndSequence)] + [InlineData(true, true, ArchiveNumberingMode.DateAndSequence)] + [InlineData(true, false, ArchiveNumberingMode.Sequence)] + [InlineData(true, true, ArchiveNumberingMode.Sequence)] + public void DatedArchiveEveryMonth(bool archiveSubFolder, bool maxArchiveDays, ArchiveNumberingMode archiveNumberingMode) + { + if (IsLinux()) + { + Console.WriteLine("[SKIP] FileTargetTests.DatedArchiveEveryMonth because SetCreationTime is not working on Travis"); + return; + } + + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "AppName.log"); + var archiveDir = archiveSubFolder ? Path.Combine(tempDir, "Archive") : tempDir; + + var defaultTimeSource = TimeSource.Current; + + try + { + var timeSource = new TimeSourceTests.ShiftedTimeSource(DateTimeKind.Local); + if (timeSource.Time.Minute == 59) + { + // Avoid double-archive due to overflow of the hour. + timeSource.AddToLocalTime(TimeSpan.FromMinutes(1)); + timeSource.AddToSystemTime(TimeSpan.FromMinutes(1)); + } + TimeSource.Current = timeSource; + + // Generate 4 files with the following contents + // 000 - 6 Months Old (Deleted) + // 111 - 4 Months Old + // 222 - 2 Months Old + // 333 - Current + List createdFiles = new List(); + List currentFiles = new List(); + for (int i = 0; i < 4; ++i) + { + if (i != 0) + { + // Make the files 2 months older, and lets try again + for (int x = 0; x < createdFiles.Count; ++x) + { + var existingFile = createdFiles[x]; + var monthsOld = x == 0 ? 2 : ((i - x) * 2 + 2); + File.SetCreationTime(existingFile, DateTime.Now.AddDays(-(32 * monthsOld))); + } + + timeSource.AddToLocalTime(TimeSpan.FromDays(32 * 3)); + timeSource.AddToSystemTime(TimeSpan.FromDays(32 * 3)); + } + + var fileTarget = new FileTarget + { + FileName = logFile, + LineEnding = LineEndingMode.LF, + Encoding = Encoding.ASCII, + Layout = "${message}", + KeepFileOpen = i % 2 != 0, + ArchiveFileName = archiveSubFolder ? Path.Combine(archiveDir, "AppName.{#}.log") : (Layout)null, + ArchiveNumbering = archiveNumberingMode, + ArchiveEvery = FileArchivePeriod.Month, + ArchiveDateFormat = "yyyyMMdd", + MaxArchiveFiles = maxArchiveDays ? 0 : 2, + MaxArchiveDays = maxArchiveDays ? 5 * 30 : 0 + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + logger.Debug($"{i.ToString()}{i.ToString()}{i.ToString()}"); + LogManager.Configuration = null; // Flush + + currentFiles = Directory.GetFiles(tempDir).ToList(); + if (archiveSubFolder && Directory.Exists(archiveDir)) + currentFiles.AddRange(Directory.GetFiles(archiveDir)); + + string newFile = string.Empty; + foreach (var fileName in currentFiles) + { + if (!createdFiles.Contains(fileName)) + { + Assert.Empty(newFile); + newFile = fileName; + + if (archiveNumberingMode == ArchiveNumberingMode.DateAndSequence && createdFiles.Count > 1) + { + // Verify it used the last-modified-time (And not file-creation-time) + string dateName = string.Empty; + dateName = Path.GetFileName(fileName); + dateName = dateName.Replace("AppName.", ""); + dateName = dateName.Replace(".0.log", ""); + dateName = dateName.Replace("log", ""); + Assert.NotEmpty(dateName); + Assert.Equal(timeSource.Time.Month, DateTime.ParseExact(dateName, "yyyyMMdd", null).Month); + } + } + } + + Assert.False(string.IsNullOrEmpty(newFile), $"Missing new file. OldFileCount={createdFiles.Count}, NewFileCount={currentFiles.Count}"); + createdFiles.Add(newFile); + } + + Assert.Equal(3, currentFiles.Count); + AssertFileContents(logFile, "333\n", Encoding.ASCII); + } + finally + { + TimeSource.Current = defaultTimeSource; + + if (Directory.Exists(archiveDir)) + Directory.Delete(archiveDir, true); + + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void CsvHeaderTest() + { + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "log.log"); + if (Path.DirectorySeparatorChar == '\\') + logFile = logFile.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + + try + { + for (var i = 0; i < 2; i++) + { + var layout = new CsvLayout + { + Delimiter = CsvColumnDelimiterMode.Semicolon, + WithHeader = true, + Columns = + { + new CsvColumn("name", "${logger}"), + new CsvColumn("level", "${level}"), + new CsvColumn("message", "${message}"), + } + }; + + var fileTarget = new FileTarget + { + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = layout, + OpenFileCacheTimeout = 0, + ReplaceFileContentsOnEachWrite = false, + ArchiveAboveSize = 120, // Only 2 LogEvents per file + MaxArchiveFiles = 1, + }; + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + if (i == 0) + { + for (int j = 0; j < 3; j++) + logger.Debug("aaa"); + + LogManager.Configuration = null; // Flush + + // See that the 3rd LogEvent was placed in its own file + AssertFileContents(logFile, "name;level;message\nNLog.UnitTests.Targets.FileTargetTests;Debug;aaa\n", Encoding.UTF8); + } + else + { + logger.Debug("aaa"); + } + } + + // See that opening closing + AssertFileContents(logFile, "name;level;message\nNLog.UnitTests.Targets.FileTargetTests;Debug;aaa\nNLog.UnitTests.Targets.FileTargetTests;Debug;aaa\n", Encoding.UTF8); + + Assert.NotEqual(3, Directory.GetFiles(tempDir).Length); // See that archive cleanup worked + + LogManager.Configuration = null; // Close + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void DeleteFileOnStartTest() + { + var logFile = Path.GetTempFileName(); + try + { + var fileTarget = new FileTarget + { + DeleteOldFileOnStartup = false, + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}" + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + + LogManager.Configuration = null; + + AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); + + // configure again, without + // DeleteOldFileOnStartup + + fileTarget = new FileTarget + { + DeleteOldFileOnStartup = false, + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}" + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + + LogManager.Configuration = null; + AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\nDebug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); + + // configure again, this time with + // DeleteOldFileOnStartup + + fileTarget = new FileTarget + { + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}", + DeleteOldFileOnStartup = true + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + + LogManager.Configuration = null; // Flush + + AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + } + } + + /// + /// todo not needed to execute twice. + /// + [Fact] + public void DeleteFileOnStartTest_noExceptionWhenMissing() + { + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "log.log"); + + try + { + LogManager.Setup().LoadConfigurationFromXml($@" + + + + + + + +"); + + Assert.False(File.Exists(logFile)); + + var logger = LogManager.GetCurrentClassLogger(); + logger.Trace("running test"); + + Assert.NotNull(LogManager.Configuration); + + LogManager.Configuration = null; // Flush + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + +#if NETFRAMEWORK + public static IEnumerable ArchiveFileOnStartTests_TestParameters + { + get + { + var booleanValues = new[] { true, false }; + return + from enableCompression in booleanValues + from customFileCompressor in booleanValues + select new object[] { enableCompression, customFileCompressor }; + } + } +#else + public static IEnumerable ArchiveFileOnStartTests_TestParameters + { + get + { + var booleanValues = new[] { true, false }; + return + from enableCompression in booleanValues + select new object[] { enableCompression, false }; + } + } +#endif + + [Theory] + [MemberData(nameof(ArchiveFileOnStartTests_TestParameters))] + public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompressor) + { + var logFile = Path.GetTempFileName() + ".txt"; + var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "Archive"); + var archiveExtension = enableCompression ? "zip" : "txt"; + IFileCompressor fileCompressor = null; + try + { + if (customFileCompressor) + { + fileCompressor = FileTarget.FileCompressor; + FileTarget.FileCompressor = new CustomFileCompressor(); + } + + // Configure first time with ArchiveOldFileOnStartup = false. + var fileTarget = new FileTarget + { + ArchiveOldFileOnStartup = false, + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}" + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + + LogManager.Configuration = null; + + AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); + + // Configure second time with ArchiveOldFileOnStartup = false again. + // Expected behavior: Extra content to be appended to the file. + fileTarget = new FileTarget + { + ArchiveOldFileOnStartup = false, + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}" + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + + LogManager.Configuration = null; // Flush + AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\nDebug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); + + + // Configure third time with ArchiveOldFileOnStartup = true again. + // Expected behavior: Extra content will be stored in a new file; the + // old content should be moved into a new location. + + var archiveTempName = Path.Combine(tempArchiveFolder, "archive." + archiveExtension); + + FileTarget ft; + fileTarget = ft = new FileTarget + { + EnableArchiveFileCompression = enableCompression, + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}", + ArchiveOldFileOnStartup = true, + ArchiveFileName = archiveTempName, + ArchiveNumbering = ArchiveNumberingMode.Sequence, + MaxArchiveFiles = 1 + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + logger.Debug("ddd"); + logger.Info("eee"); + logger.Warn("fff"); + + LogManager.Configuration = null; // Flush + + AssertFileContents(logFile, "Debug ddd\nInfo eee\nWarn fff\n", Encoding.UTF8); + Assert.True(File.Exists(archiveTempName)); + + var assertFileContents = ft.EnableArchiveFileCompression ? + new Action(AssertZipFileContents) : + AssertFileContents; + +#if !NET35 + string expectedEntryName = Path.GetFileNameWithoutExtension(archiveTempName) + ".txt"; +#else + string expectedEntryName = Path.GetFileName(logFile); +#endif + assertFileContents(archiveTempName, expectedEntryName, "Debug aaa\nInfo bbb\nWarn ccc\nDebug aaa\nInfo bbb\nWarn ccc\n", + Encoding.UTF8); + } + finally + { + if (customFileCompressor) + FileTarget.FileCompressor = fileCompressor; + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempArchiveFolder)) + Directory.Delete(tempArchiveFolder, true); + } + } + + [Fact] + public void ArchiveOldFileOnStartupAboveSize() + { + var logFile = Path.GetTempFileName(); + var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "Archive"); + var archiveTempName = Path.Combine(tempArchiveFolder, "archive_size_threshold.txt"); + FileTarget CreateTestTarget(long threshold) + { + return new FileTarget + { + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}", + ArchiveOldFileOnStartupAboveSize = threshold, + ArchiveFileName = archiveTempName, + ArchiveNumbering = ArchiveNumberingMode.Sequence, + MaxArchiveFiles = 1 + }; + } + try + { + // No archive on startup (ignoring threshold) + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(CreateTestTarget(1000))); + logger.Info("aaa"); + LogManager.Shutdown(); + AssertFileContents(logFile, "Info aaa\n", Encoding.UTF8); + Assert.False(File.Exists(archiveTempName)); + + // Archive on startup with small threshold -> Must be archived + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(CreateTestTarget(3))); + logger.Info("ccc"); + LogManager.Flush(); + AssertFileContents(logFile, "Info ccc\n", Encoding.UTF8); + Assert.True(File.Exists(archiveTempName)); + AssertFileContents(archiveTempName, "Info aaa\n", Encoding.UTF8); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempArchiveFolder)) + Directory.Delete(tempArchiveFolder, true); + } + } + + [Fact] + public void ArchiveOldFileOnStartupAboveSizeWhenFileLocked() + { + var logFile = Path.GetTempFileName(); + var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "Archive"); + var archiveTempName = Path.Combine(tempArchiveFolder, "archive_size_threshold.zip"); + + FileTarget CreateTestTarget(long threshold) + { + return new FileTarget + { + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}", + ArchiveOldFileOnStartupAboveSize = threshold, + ArchiveFileName = archiveTempName, + ArchiveNumbering = ArchiveNumberingMode.Sequence, + EnableArchiveFileCompression = true, + MaxArchiveFiles = 1 + }; + } + + try + { + // No archive on startup (ignoring threshold) + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(CreateTestTarget(1000))); + logger.Info("aaa"); + LogManager.Shutdown(); + AssertFileContents(logFile, "Info aaa\n", Encoding.UTF8); + Assert.False(File.Exists(archiveTempName)); + + NLog.LogManager.ThrowExceptions = false; + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(CreateTestTarget(3))); + + using (var fileStream = new FileStream(logFile, FileMode.Open, FileAccess.Write, FileShare.None)) + { + // Archive on startup with small threshold -> Must be archived + logger.Info("ccc"); + LogManager.Flush(); + fileStream.Close(); + AssertFileContents(logFile, "Info aaa\n", Encoding.UTF8); + Assert.False(File.Exists(archiveTempName)); + } + } + finally + { + NLog.LogManager.ThrowExceptions = true; + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempArchiveFolder)) + Directory.Delete(tempArchiveFolder, true); + } + } + + [Fact] + public void RetryFileOpenWhenFileLocked() + { + var logFile = Path.GetTempFileName(); + + var fileTarget = new FileTarget("file") + { + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}", + KeepFileOpen = false, + ConcurrentWriteAttempts = 100, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + try + { + var fileStream = new FileStream(logFile, FileMode.Open, FileAccess.Write, FileShare.None); + var task = Task.Run(() => logger.Info("aaa")); + Assert.False(task.Wait(TimeSpan.FromMilliseconds(50))); + fileStream.Dispose(); + Assert.True(task.Wait(TimeSpan.FromSeconds(60))); + + AssertFileContents(logFile, "Info aaa\n", Encoding.UTF8); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + } + } + + public static IEnumerable ReplaceFileContentsOnEachWriteTest_TestParameters + { + get + { + bool[] boolValues = new[] { false, true }; + return + from useHeader in boolValues + from useFooter in boolValues + select new object[] { useHeader, useFooter }; + } + } + + [Theory] + [MemberData(nameof(ReplaceFileContentsOnEachWriteTest_TestParameters))] + public void ReplaceFileContentsOnEachWriteTest(bool useHeader, bool useFooter) + { + const string header = "Headerline", footer = "Footerline"; + + var logFile = Path.GetTempFileName(); + try + { + var fileTarget = new FileTarget + { + DeleteOldFileOnStartup = false, + FileName = SimpleLayout.Escape(logFile), + ReplaceFileContentsOnEachWrite = true, + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}" + }; + if (useHeader) + fileTarget.Header = header; + if (useFooter) + fileTarget.Footer = footer; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + string headerPart = useHeader ? header + LineEndingMode.LF.NewLineCharacters : string.Empty; + string footerPart = useFooter ? footer + LineEndingMode.LF.NewLineCharacters : string.Empty; + + logger.Debug("aaa"); + LogManager.Flush(); + AssertFileContents(logFile, headerPart + "Debug aaa\n" + footerPart, Encoding.UTF8); + + logger.Info("bbb"); + LogManager.Flush(); + AssertFileContents(logFile, headerPart + "Info bbb\n" + footerPart, Encoding.UTF8); + + logger.Warn("ccc"); + LogManager.Flush(); + AssertFileContents(logFile, headerPart + "Warn ccc\n" + footerPart, Encoding.UTF8); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + } + } + + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ReplaceFileContentsOnEachWrite_CreateDirs(bool createDirs) + { + var tempDir = Path.Combine(Path.GetTempPath(), "dir_" + Guid.NewGuid().ToString()); + var logfile = Path.Combine(tempDir, "log.log"); + + try + { + using (new NoThrowNLogExceptions()) + { + var target = new FileTarget + { + FileName = logfile, + ReplaceFileContentsOnEachWrite = true, + CreateDirs = createDirs + }; + var config = new LoggingConfiguration(); + + config.AddTarget("logfile", target); + + config.AddRuleForAllLevels(target); + + LogManager.Configuration = config; + + var logger = LogManager.GetLogger("A"); + logger.Info("a"); + + Assert.Equal(createDirs, Directory.Exists(tempDir)); + } + } + finally + { + if (File.Exists(logfile)) + File.Delete(logfile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void CreateDirsTest() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + var fileTarget = new FileTarget + { + FileName = logFile, + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}" + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + + LogManager.Configuration = null; // Flush + AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); + } + finally + { + LogManager.Configuration = null; + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Theory] + [InlineData(true, 0)] + [InlineData(false, 0)] + [InlineData(false, 1)] + public void AutoFlushTest(bool autoFlush, int autoFlushTimeout) + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + var fileTarget = new FileTarget + { + FileName = logFile, + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}", + KeepFileOpen = true, + ConcurrentWrites = false, + AutoFlush = autoFlush, + OpenFileFlushTimeout = autoFlushTimeout, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + + if (autoFlush) + { + AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); + } + else + { + AssertFileContents(logFile, string.Empty, Encoding.UTF8); + if (autoFlushTimeout > 0) + { + Thread.Sleep(TimeSpan.FromSeconds(autoFlushTimeout * 1.5)); + AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); + } + } + + LogManager.Configuration = null; // Flush + AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); + } + finally + { + LogManager.Configuration = null; + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void SequentialArchiveTest() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + string archiveFolder = Path.Combine(tempDir, "archive"); + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveAboveSize = 100, + LineEnding = LineEndingMode.LF, + Layout = "${message}", + MaxArchiveFiles = 3, + ArchiveNumbering = ArchiveNumberingMode.Sequence + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + // we emit 5 * 25 *(3 x aaa + \n) bytes + // so that we should get a full file + 3 archives + Generate100BytesLog('a'); + Generate100BytesLog('b'); + Generate100BytesLog('c'); + Generate100BytesLog('d'); + Generate100BytesLog('e'); + + LogManager.Configuration = null; // Flush + + var times = 25; + AssertFileContents(logFile, + StringRepeat(times, "eee\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(archiveFolder, "0001.txt"), + StringRepeat(times, "bbb\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(archiveFolder, "0002.txt"), + StringRepeat(times, "ccc\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(archiveFolder, "0003.txt"), + StringRepeat(times, "ddd\n"), + Encoding.UTF8); + + //0000 should not exists because of MaxArchiveFiles=3 + Assert.False(File.Exists(Path.Combine(archiveFolder, "0000.txt"))); + Assert.False(File.Exists(Path.Combine(archiveFolder, "0004.txt"))); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void SequentialArchiveTest_MaxArchiveFiles_0() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + string archiveFolder = Path.Combine(tempDir, "archive"); + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveAboveSize = 100, + LineEnding = LineEndingMode.LF, + ArchiveNumbering = ArchiveNumberingMode.Sequence, + Layout = "${message}", + MaxArchiveFiles = 0 + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + // we emit 5 * 25 *(3 x aaa + \n) bytes + // so that we should get a full file + 4 archives + Generate100BytesLog('a'); + Generate100BytesLog('b'); + Generate100BytesLog('c'); + Generate100BytesLog('d'); + Generate100BytesLog('e'); + + LogManager.Configuration = null; // Flush + + AssertFileContents(logFile, + StringRepeat(25, "eee\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(archiveFolder, "0000.txt"), + StringRepeat(25, "aaa\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(archiveFolder, "0001.txt"), + StringRepeat(25, "bbb\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(archiveFolder, "0002.txt"), + StringRepeat(25, "ccc\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(archiveFolder, "0003.txt"), + StringRepeat(25, "ddd\n"), + Encoding.UTF8); + + Assert.False(File.Exists(Path.Combine(archiveFolder, "0004.txt"))); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void ArchiveAboveSizeWithArchiveNumberingModeDate_maxfiles_o() + { + var tempDir = Path.Combine(Path.GetTempPath(), "ArchiveEveryCombinedWithArchiveAboveSize_" + Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + string archiveFolder = Path.Combine(tempDir, "archive"); + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveAboveSize = 100, + LineEnding = LineEndingMode.LF, + Layout = "${message}", + ArchiveNumbering = ArchiveNumberingMode.Date + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + //e.g. 20150804 + var archiveFileName = DateTime.Now.ToString("yyyyMMdd"); + + // we emit 5 * 25 *(3 x aaa + \n) bytes + // so that we should get a full file + 3 archives + var times = 25; + for (var i = 0; i < times; ++i) + { + logger.Debug("aaa"); + } + + for (var i = 0; i < times; ++i) + { + logger.Debug("bbb"); + } + for (var i = 0; i < times; ++i) + { + logger.Debug("ccc"); + } + for (var i = 0; i < times; ++i) + { + logger.Debug("ddd"); + } + for (var i = 0; i < times; ++i) + { + logger.Debug("eee"); + } + + LogManager.Configuration = null; // Flush + + //we expect only eee and all other in the archive + AssertFileContents(logFile, + StringRepeat(times, "eee\n"), + Encoding.UTF8); + + //DUNNO what to expected! + //try (which fails) + AssertFileContents( + Path.Combine(archiveFolder, $"{archiveFileName}.txt"), + StringRepeat(times, "aaa\n") + StringRepeat(times, "bbb\n") + StringRepeat(times, "ccc\n") + StringRepeat(times, "ddd\n"), + Encoding.UTF8); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void DeleteArchiveFilesByDate() + { + const int maxArchiveFiles = 3; + + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + string archiveFolder = Path.Combine(tempDir, "archive"); + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), + ArchiveAboveSize = 50, + LineEnding = LineEndingMode.LF, + ArchiveNumbering = ArchiveNumberingMode.Date, + ArchiveDateFormat = "yyyyMMddHHmmssfff", //make sure the milliseconds are set in the filename + Layout = "${message}", + MaxArchiveFiles = maxArchiveFiles + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + //writing 19 times 10 bytes (9 char + linefeed) will result in 3 archive files and 1 current file + for (var i = 0; i < 19; ++i) + { + logger.Debug("123456789"); + //build in a small sleep to make sure the current time is reflected in the filename + //do this every 5 entries + if (i % 5 == 0) + Thread.Sleep(50); + } + + //Setting the Configuration to [null] will result in a 'Dump' of the current log entries + LogManager.Configuration = null; // Flush + + var files = Directory.GetFiles(archiveFolder).OrderBy(s => s); + //the amount of archived files may not exceed the set 'MaxArchiveFiles' + Assert.Equal(maxArchiveFiles, files.Count()); + + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + //writing just one line of 11 bytes will trigger the cleanup of old archived files + //as stated by the MaxArchiveFiles property, but will only delete the oldest file + logger.Debug("1234567890"); + + LogManager.Configuration = null; // Flush + + var files2 = Directory.GetFiles(archiveFolder).OrderBy(s => s); + Assert.Equal(maxArchiveFiles, files2.Count()); + + //the oldest file should be deleted + Assert.DoesNotContain(files.ElementAt(0), files2); + //two files should still be there + Assert.Equal(files.ElementAt(1), files2.ElementAt(0)); + Assert.Equal(files.ElementAt(2), files2.ElementAt(1)); + //one new archive file should be created + Assert.DoesNotContain(files2.ElementAt(2), files); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void DeleteArchiveFilesByDateWithDateName() + { + const int maxArchiveFiles = 3; + LogManager.ThrowExceptions = true; + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + try + { + var logFile = Path.Combine(tempDir, "${date:format=yyyyMMddHHmmssfff}.txt"); + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(tempDir, "{#}.txt"), + ArchiveEvery = FileArchivePeriod.Year, + LineEnding = LineEndingMode.LF, + ArchiveNumbering = ArchiveNumberingMode.Date, + ArchiveDateFormat = "yyyyMMddHHmmssfff", //make sure the milliseconds are set in the filename + Layout = "${message}", + MaxArchiveFiles = maxArchiveFiles + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + for (var i = 0; i < 4; ++i) + { + logger.Debug("123456789"); + //build in a sleep to make sure the current time is reflected in the filename + Thread.Sleep(50); + } + //Setting the Configuration to [null] will result in a 'Dump' of the current log entries + LogManager.Configuration = null; // Flush + + var files = Directory.GetFiles(tempDir).OrderBy(s => s); + //we expect 3 archive files, plus one current file + Assert.Equal(maxArchiveFiles + 1, files.Count()); + + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + //writing 50ms later will trigger the cleanup of old archived files + //as stated by the MaxArchiveFiles property, but will only delete the oldest file + Thread.Sleep(50); + logger.Debug("123456789"); + LogManager.Configuration = null; // Flush + + var files2 = Directory.GetFiles(tempDir).OrderBy(s => s); + Assert.Equal(maxArchiveFiles + 1, files2.Count()); + + //the oldest file should be deleted + Assert.DoesNotContain(files.ElementAt(0), files2); + //two files should still be there + Assert.Equal(files.ElementAt(1), files2.ElementAt(0)); + Assert.Equal(files.ElementAt(2), files2.ElementAt(1)); + Assert.Equal(files.ElementAt(3), files2.ElementAt(2)); + //one new file should be created + Assert.DoesNotContain(files2.ElementAt(3), files); + } + finally + { + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + public static IEnumerable DateArchive_UsesDateFromCurrentTimeSource_TestParameters + { + get + { + var maxArchiveDays = false; + var booleanValues = new[] { true, false }; + var timeKindValues = new[] { DateTimeKind.Utc, DateTimeKind.Local }; + return + from timeKind in timeKindValues + from includeDateInLogFilePath in booleanValues + from concurrentWrites in booleanValues + from keepFileOpen in booleanValues + from forceMutexConcurrentWrites in booleanValues + where UniqueBaseAppender(concurrentWrites, keepFileOpen, forceMutexConcurrentWrites) + from includeSequenceInArchive in booleanValues + from forceManaged in booleanValues + select new object[] { timeKind, includeDateInLogFilePath, concurrentWrites, keepFileOpen, includeSequenceInArchive, forceManaged, forceMutexConcurrentWrites, maxArchiveDays }; + } + } + + [Theory] + [MemberData(nameof(DateArchive_UsesDateFromCurrentTimeSource_TestParameters))] + public void DateArchive_UsesDateFromCurrentTimeSource(DateTimeKind timeKind, bool includeDateInLogFilePath, bool concurrentWrites, bool keepFileOpen, bool includeSequenceInArchive, bool forceManaged, bool forceMutexConcurrentWrites, bool maxArhiveDays) + { +#if !NETFRAMEWORK || MONO + if (IsLinux()) + { + Console.WriteLine("[SKIP] FileTargetTests.DateArchive_UsesDateFromCurrentTimeSource because SetLastWriteTime is not working on Travis"); + return; + } +#endif + + const string archiveDateFormat = "yyyyMMdd"; + const int maxArchiveFiles = 3; + + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, includeDateInLogFilePath ? "file_${shortdate}.txt" : "file.txt"); + var defaultTimeSource = TimeSource.Current; + try + { + var timeSource = new TimeSourceTests.ShiftedTimeSource(timeKind); + + TimeSource.Current = timeSource; + + string archiveFolder = Path.Combine(tempDir, "archive"); + string archiveFileNameTemplate = Path.Combine(archiveFolder, "{#}.txt"); + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = archiveFileNameTemplate, + LineEnding = LineEndingMode.LF, + ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, + ArchiveEvery = FileArchivePeriod.Day, + ArchiveDateFormat = archiveDateFormat, + Layout = "${date:format=O}|${message}", + MaxArchiveFiles = maxArhiveDays ? 0 : maxArchiveFiles, + MaxArchiveDays = maxArhiveDays ? maxArchiveFiles : 0, + ConcurrentWrites = concurrentWrites, + KeepFileOpen = keepFileOpen, + ForceManaged = forceManaged, + ForceMutexConcurrentWrites = forceMutexConcurrentWrites, + Header = "header", + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("123456789"); + DateTime previousWriteTime = timeSource.Time; + + const int daysToTestLogging = 3; + const int intervalsPerDay = 24; + var loggingInterval = TimeSpan.FromHours(1); + for (var i = 0; i < daysToTestLogging * intervalsPerDay; ++i) + { + timeSource.AddToLocalTime(loggingInterval); + + if (timeSource.Time.Date != previousWriteTime.Date) + { + string currentLogFile = includeDateInLogFilePath + ? logFile.Replace("${shortdate}", timeSource.Time.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)) + : logFile; + // Simulate that previous file write began in previous day and ended on current day. + try + { + File.SetLastWriteTime(currentLogFile, timeSource.SystemTime); + } + catch { } + } + + var eventInfo = new LogEventInfo(LogLevel.Debug, logger.Name, "123456789"); + logger.Log(eventInfo); + LogManager.Flush(); + + var dayIsChanged = eventInfo.TimeStamp.Date != previousWriteTime.Date; + // ensure new archive is created only when the day part of time is changed + var archiveFileName = archiveFileNameTemplate.Replace("{#}", previousWriteTime.ToString(archiveDateFormat) + (includeSequenceInArchive ? ".0" : string.Empty)); + var archiveExists = File.Exists(archiveFileName); + if (dayIsChanged) + Assert.True(archiveExists, + $"new archive should be created when the day part of {timeKind} time is changed"); + else + Assert.False(archiveExists, + $"new archive should not be create when day part of {timeKind} time is unchanged"); + + previousWriteTime = eventInfo.TimeStamp.Date; + if (dayIsChanged) + timeSource.AddToSystemTime(TimeSpan.FromDays(1)); + } + //Setting the Configuration to [null] will result in a 'Dump' of the current log entries + LogManager.Configuration = null; // Flush + + var files = Directory.GetFiles(archiveFolder); + //the amount of archived files may not exceed the set 'MaxArchiveFiles' + Assert.Equal(maxArchiveFiles, files.Length); + + foreach (var file in files) + AssertFileContentsStartsWith(file, "header", Encoding.UTF8); + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + //writing one line on a new day will trigger the cleanup of old archived files + //as stated by the MaxArchiveFiles property, but will only delete the oldest file + timeSource.AddToLocalTime(TimeSpan.FromDays(1)); + logger.Debug("1234567890"); + + LogManager.Configuration = null; // Flush + + var files2 = Directory.GetFiles(archiveFolder); + Assert.Equal(maxArchiveFiles, files2.Length); + + //the oldest file should be deleted + Assert.DoesNotContain(files[0], files2); + //two files should still be there + Assert.Equal(files[1], files2[0]); + Assert.Equal(files[2], files2[1]); + //one new archive file should be created + Assert.DoesNotContain(files2[2], files); + } + finally + { + TimeSource.Current = defaultTimeSource; // restore default time source + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Theory] + [InlineData(DateTimeKind.Utc, false, false)] + [InlineData(DateTimeKind.Local, false, false)] + [InlineData(DateTimeKind.Utc, true, false)] + [InlineData(DateTimeKind.Local, true, false)] + [InlineData(DateTimeKind.Utc, false, true)] + [InlineData(DateTimeKind.Local, false, true)] + [InlineData(DateTimeKind.Utc, true, true)] + [InlineData(DateTimeKind.Local, true, true)] + public void DateArchive_UsesDateFromCurrentTimeSource_MaxArchiveDays(DateTimeKind timeKind, bool includeDateInLogFilePath, bool includeSequenceInArchive) + { + const bool MaxArchiveDays = true; + DateArchive_UsesDateFromCurrentTimeSource(timeKind, includeDateInLogFilePath, false, false, includeSequenceInArchive, false, false, MaxArchiveDays); + } + + public static IEnumerable DateArchive_ArchiveOnceOnly_TestParameters + { + get + { + var booleanValues = new[] { true, false }; + return + from concurrentWrites in booleanValues + from keepFileOpen in booleanValues + from forceMutexConcurrentWrites in booleanValues + where UniqueBaseAppender(concurrentWrites, keepFileOpen, forceMutexConcurrentWrites) + from includeDateInLogFilePath in booleanValues + from includeSequenceInArchive in booleanValues + from forceManaged in booleanValues + select new object[] { concurrentWrites, keepFileOpen, includeDateInLogFilePath, includeSequenceInArchive, forceManaged, forceMutexConcurrentWrites }; + } + } + + [Theory] + [MemberData(nameof(DateArchive_ArchiveOnceOnly_TestParameters))] + public void DateArchive_ArchiveOnceOnly(bool concurrentWrites, bool keepFileOpen, bool dateInLogFilePath, bool includeSequenceInArchive, bool forceManaged, bool forceMutexConcurrentWrites) + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, dateInLogFilePath ? "file_${shortdate}.txt" : "file.txt"); + + var defaultTimeSource = TimeSource.Current; + + try + { + var timeSource = new TimeSourceTests.ShiftedTimeSource(DateTimeKind.Local); + if (timeSource.Time.Minute == 59) + { + // Avoid double-archive due to overflow of the hour. + timeSource.AddToLocalTime(TimeSpan.FromMinutes(1)); + timeSource.AddToSystemTime(TimeSpan.FromMinutes(1)); + } + TimeSource.Current = timeSource; + + string archiveFolder = Path.Combine(tempDir, "archive"); + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), + LineEnding = LineEndingMode.LF, + ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, + ArchiveEvery = FileArchivePeriod.Day, + ArchiveDateFormat = "yyyyMMdd", + Layout = "${message}", + ConcurrentWrites = concurrentWrites, + KeepFileOpen = keepFileOpen, + ForceManaged = forceManaged, + ForceMutexConcurrentWrites = forceMutexConcurrentWrites, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("123456789"); + LogManager.Configuration = null; + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + timeSource.AddToLocalTime(TimeSpan.FromDays(1)); + if (!dateInLogFilePath) + File.SetCreationTimeUtc(logFile, timeSource.Time.ToUniversalTime()); + timeSource.AddToLocalTime(TimeSpan.FromDays(1)); + + // This should archive the log before logging. + logger.Debug("123456789"); + + LogManager.Configuration = null; + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + timeSource.AddToSystemTime(TimeSpan.FromDays(2)); // Archive only once + + // This must not archive. + logger.Debug("123456789"); + + LogManager.Configuration = null; // Flush + + Assert.Single(Directory.GetFiles(archiveFolder)); + var prevLogFile = Directory.GetFiles(archiveFolder)[0]; + AssertFileContents(prevLogFile, StringRepeat(1, "123456789\n"), Encoding.UTF8); + + var currentLogFile = Directory.GetFiles(tempDir)[0]; + AssertFileContents(currentLogFile, StringRepeat(2, "123456789\n"), Encoding.UTF8); + } + finally + { + TimeSource.Current = defaultTimeSource; // restore default time source + + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + public static IEnumerable DateArchive_SkipPeriod_TestParameters + { + get + { + var timeKindValues = new[] { DateTimeKind.Utc, DateTimeKind.Local }; + var archivePeriodValues = new[] { FileArchivePeriod.Day, FileArchivePeriod.Hour }; + var booleanValues = new[] { true, false }; + return + from timeKind in timeKindValues + from archivePeriod in archivePeriodValues + from includeDateInLogFilePath in booleanValues + from includeSequenceInArchive in booleanValues + select new object[] { timeKind, archivePeriod, includeDateInLogFilePath, includeSequenceInArchive }; + } + } + + [Theory] + [MemberData(nameof(DateArchive_SkipPeriod_TestParameters))] + public void DateArchive_SkipPeriod(DateTimeKind timeKind, FileArchivePeriod archivePeriod, bool includeDateInLogFilePath, bool includeSequenceInArchive) + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, includeDateInLogFilePath ? "file_${date:format=yyyyMMddHHmm}.txt" : "file.txt"); + var defaultTimeSource = TimeSource.Current; + try + { + // Avoid inconsistency in file's last-write-time due to overflow of the minute during test run. + while (DateTime.Now.Second > 55) + Thread.Sleep(1000); + + var timeSource = new TimeSourceTests.ShiftedTimeSource(timeKind); + if (timeSource.Time.Minute == 59) + { + // Avoid double-archive due to overflow of the hour. + timeSource.AddToLocalTime(TimeSpan.FromMinutes(1)); + timeSource.AddToSystemTime(TimeSpan.FromMinutes(1)); + } + TimeSource.Current = timeSource; + + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(tempDir, "archive", "{#}.txt"), + LineEnding = LineEndingMode.LF, + ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, + ArchiveEvery = archivePeriod, + ArchiveDateFormat = "yyyyMMddHHmm", + Layout = "${date:format=O}|${message}", + }; + string archiveDateFormat = fileTarget.ArchiveDateFormat; + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("1234567890"); + timeSource.AddToLocalTime(TimeSpan.FromMinutes(1)); + logger.Debug("1234567890"); + // The archive file name must be based on the last time the file was written. + string archiveFileName = + $"{timeSource.Time.ToString(archiveDateFormat) + (includeSequenceInArchive ? ".0" : string.Empty)}.txt"; + // Effectively update the file's last-write-time. + timeSource.AddToSystemTime(TimeSpan.FromMinutes(1)); + + timeSource.AddToLocalTime(TimeSpan.FromDays(2)); + logger.Debug("1234567890"); + + LogManager.Configuration = null; // Flush + + string archivePath = Path.Combine(tempDir, "archive"); + var archiveFiles = Directory.GetFiles(archivePath); + Assert.Single(archiveFiles); + Assert.Equal(archiveFileName, Path.GetFileName(archiveFiles[0])); + } + finally + { + TimeSource.Current = defaultTimeSource; // restore default time source + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + public static IEnumerable DateArchive_AllLoggersTransferToCurrentLogFile_TestParameters + { + get + { + var booleanValues = new[] { true, false }; + return + from concurrentWrites in booleanValues + from keepFileOpen in booleanValues + from forceMutexConcurrentWrites in booleanValues + where UniqueBaseAppender(concurrentWrites, keepFileOpen, forceMutexConcurrentWrites) + from includeDateInLogFilePath in booleanValues + from includeSequenceInArchive in booleanValues + from enableArchiveCompression in booleanValues + from forceManaged in booleanValues + select new object[] { concurrentWrites, keepFileOpen, includeDateInLogFilePath, includeSequenceInArchive, enableArchiveCompression, forceManaged, forceMutexConcurrentWrites }; + } + } + + [Theory] + [MemberData(nameof(DateArchive_AllLoggersTransferToCurrentLogFile_TestParameters))] + public void DateArchive_AllLoggersTransferToCurrentLogFile(bool concurrentWrites, bool keepFileOpen, bool includeDateInLogFilePath, bool includeSequenceInArchive, bool enableArchiveCompression, bool forceManaged, bool forceMutexConcurrentWrites) + { + if (keepFileOpen && !concurrentWrites) + return; // This combination do not support two local FileTargets to the same file + +#if NET35 || NET40 + if (enableArchiveCompression) + return; // No need to test with compression +#endif + + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logfile = Path.Combine(tempDir, includeDateInLogFilePath ? "file_${shortdate}.txt" : "file.txt"); + var defaultTimeSource = TimeSource.Current; + +#if NET35 || NET40 + IFileCompressor fileCompressor = null; +#endif + + try + { + var timeSource = new TimeSourceTests.ShiftedTimeSource(DateTimeKind.Local); + if (timeSource.Time.Minute == 59) + { + // Avoid double-archive due to overflow of the hour. + timeSource.AddToLocalTime(TimeSpan.FromMinutes(1)); + timeSource.AddToSystemTime(TimeSpan.FromMinutes(1)); + } + TimeSource.Current = timeSource; + + var config = new LoggingConfiguration(); + +#if NET35 || NET40 + if (enableArchiveCompression) + { + fileCompressor = FileTarget.FileCompressor; + FileTarget.FileCompressor = new CustomFileCompressor(); + } +#endif + + string archiveFolder = Path.Combine(tempDir, "archive"); + var fileTarget1 = new FileTarget + { + FileName = logfile, + ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), + LineEnding = LineEndingMode.LF, + ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, + ArchiveEvery = FileArchivePeriod.Day, + ArchiveDateFormat = "yyyyMMdd", + EnableArchiveFileCompression = enableArchiveCompression, + Layout = "${message}", + ConcurrentWrites = concurrentWrites, + KeepFileOpen = keepFileOpen, + ForceManaged = forceManaged, + ForceMutexConcurrentWrites = forceMutexConcurrentWrites, + }; + var logger1Rule = new LoggingRule("logger1", LogLevel.Debug, fileTarget1); + config.LoggingRules.Add(logger1Rule); + + var fileTarget2 = new FileTarget + { + FileName = logfile, + ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), + LineEnding = LineEndingMode.LF, + ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, + ArchiveEvery = FileArchivePeriod.Day, + ArchiveDateFormat = "yyyyMMdd", + EnableArchiveFileCompression = enableArchiveCompression, + Layout = "${message}", + ConcurrentWrites = concurrentWrites, + KeepFileOpen = keepFileOpen, + ForceManaged = forceManaged, + ForceMutexConcurrentWrites = forceMutexConcurrentWrites, + }; + var logger2Rule = new LoggingRule("logger2", LogLevel.Debug, fileTarget2); + config.LoggingRules.Add(logger2Rule); + + LogManager.Configuration = config; + + var logger1 = LogManager.GetLogger("logger1"); + var logger2 = LogManager.GetLogger("logger2"); + + logger1.Debug("123456789"); + logger2.Debug("123456789"); + LogManager.Flush(); + + timeSource.AddToLocalTime(TimeSpan.FromDays(1)); + + // This should archive the log before logging. + logger1.Debug("123456789"); + + timeSource.AddToSystemTime(TimeSpan.FromDays(1)); // Archive only once + + Thread.Sleep(10); + logger2.Debug("123456789"); + + LogManager.Configuration = null; // Flush + + var files = Directory.GetFiles(archiveFolder); + Assert.Single(files); + if (!enableArchiveCompression) + { + string prevLogFile = Directory.GetFiles(archiveFolder)[0]; + AssertFileContents(prevLogFile, StringRepeat(2, "123456789\n"), Encoding.UTF8); + } + string currentLogFile = Directory.GetFiles(tempDir)[0]; + AssertFileContents(currentLogFile, StringRepeat(2, "123456789\n"), Encoding.UTF8); + } + finally + { + TimeSource.Current = defaultTimeSource; // restore default time source + +#if NET35 || NET40 + if (enableArchiveCompression) + { + FileTarget.FileCompressor = fileCompressor; + } +#endif + + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void DeleteArchiveFilesByDate_MaxArchiveFiles_0() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + string archiveFolder = Path.Combine(tempDir, "archive"); + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), + ArchiveAboveSize = 50, + LineEnding = LineEndingMode.LF, + ArchiveNumbering = ArchiveNumberingMode.Date, + ArchiveDateFormat = "yyyyMMddHHmmssfff", //make sure the milliseconds are set in the filename + Layout = "${message}", + MaxArchiveFiles = 0 + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + //writing 19 times 10 bytes (9 char + linefeed) will result in 3 archive files and 1 current file + for (var i = 0; i < 19; ++i) + { + logger.Debug("123456789"); + //build in a small sleep to make sure the current time is reflected in the filename + //do this every 5 entries + if (i % 5 == 0) + { + Thread.Sleep(50); + } + } + + //Setting the Configuration to [null] will result in a 'Dump' of the current log entries + LogManager.Configuration = null; // Flush + + var fileCount = Directory.EnumerateFiles(archiveFolder).Count(); + + Assert.Equal(3, fileCount); + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + //create 1 new file for archive + logger.Debug("1234567890"); + LogManager.Configuration = null; + + var fileCount2 = Directory.EnumerateFiles(archiveFolder).Count(); + //there should be 1 more file + Assert.Equal(4, fileCount2); + } + finally + { + if (File.Exists(logFile)) + { + File.Delete(logFile); + } + + if (Directory.Exists(tempDir)) + { + Directory.Delete(tempDir, true); + } + } + } + + [Fact] + public void DeleteArchiveFilesByDate_AlteredMaxArchive() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + string archiveFolder = Path.Combine(tempDir, "archive"); + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), + ArchiveAboveSize = 50, + LineEnding = LineEndingMode.LF, + ArchiveNumbering = ArchiveNumberingMode.Date, + ArchiveDateFormat = "yyyyMMddHHmmssfff", //make sure the milliseconds are set in the filename + Layout = "${message}", + MaxArchiveFiles = 5 + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + //writing 29 times 10 bytes (9 char + linefeed) will result in 3 archive files and 1 current file + for (var i = 0; i < 29; ++i) + { + logger.Debug("123456789"); + //build in a small sleep to make sure the current time is reflected in the filename + //do this every 5 entries + if (i % 5 == 0) + Thread.Sleep(50); + } + //Setting the Configuration to [null] will result in a 'Dump' of the current log entries + LogManager.Configuration = null; + + var files = Directory.GetFiles(archiveFolder).OrderBy(s => s); + //the amount of archived files may not exceed the set 'MaxArchiveFiles' + Assert.Equal(fileTarget.MaxArchiveFiles, files.Count()); + + //alter the MaxArchivedFiles + fileTarget.MaxArchiveFiles = 2; + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + //writing just one line of 11 bytes will trigger the cleanup of old archived files + //as stated by the MaxArchiveFiles property, but will only delete the oldest files + logger.Debug("1234567890"); + LogManager.Configuration = null; // Flush + + var files2 = Directory.GetFiles(archiveFolder).OrderBy(s => s); + Assert.Equal(fileTarget.MaxArchiveFiles, files2.Count()); + + //the oldest files should be deleted + Assert.DoesNotContain(files.ElementAt(0), files2); + Assert.DoesNotContain(files.ElementAt(1), files2); + Assert.DoesNotContain(files.ElementAt(2), files2); + Assert.DoesNotContain(files.ElementAt(3), files2); + //one files should still be there + Assert.Equal(files.ElementAt(4), files2.ElementAt(0)); + //one new archive file should be created + Assert.DoesNotContain(files2.ElementAt(1), files); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void RepeatingHeaderTest() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + const string header = "Headerline"; + + string archiveFolder = Path.Combine(tempDir, "archive"); + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveAboveSize = 51, + LineEnding = LineEndingMode.LF, + ArchiveNumbering = ArchiveNumberingMode.Sequence, + Layout = "${message}", + Header = header, + MaxArchiveFiles = 2, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + // Writing 16 times 10 bytes = 160 bytes = 3 files + for (var i = 0; i < 16; ++i) + { + logger.Debug("123456789"); + } + + LogManager.Configuration = null; // Flush + + AssertFileContentsStartsWith(logFile, header, Encoding.UTF8); + + AssertFileContentsStartsWith(Path.Combine(archiveFolder, "0002.txt"), header, Encoding.UTF8); + + AssertFileContentsStartsWith(Path.Combine(archiveFolder, "0001.txt"), header, Encoding.UTF8); + + Assert.False(File.Exists(Path.Combine(archiveFolder, "0000.txt"))); // MaxArchiveFiles = 2 (Removes the first file) + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void RepeatingFooterTest(bool writeFooterOnArchivingOnly) + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + const string footer = "Footerline"; + + string archiveFolder = Path.Combine(tempDir, "archive"); + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveAboveSize = 51, + LineEnding = LineEndingMode.LF, + ArchiveNumbering = ArchiveNumberingMode.Sequence, + Layout = "${message}", + Footer = footer, + MaxArchiveFiles = 2, + WriteFooterOnArchivingOnly = writeFooterOnArchivingOnly + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + // Writing 16 times 10 bytes = 160 bytes = 3 files + for (var i = 0; i < 16; ++i) + { + logger.Debug("123456789"); + } + + LogManager.Configuration = null; // Flush + + string expectedEnding = footer + fileTarget.LineEnding.NewLineCharacters; + if (writeFooterOnArchivingOnly) + Assert.False(File.ReadAllText(logFile).EndsWith(expectedEnding), "Footer was unexpectedly written to log file."); + else + AssertFileContentsEndsWith(logFile, expectedEnding, Encoding.UTF8); + AssertFileContentsEndsWith(Path.Combine(archiveFolder, "0002.txt"), expectedEnding, Encoding.UTF8); + AssertFileContentsEndsWith(Path.Combine(archiveFolder, "0001.txt"), expectedEnding, Encoding.UTF8); + Assert.False(File.Exists(Path.Combine(archiveFolder, "0000.txt"))); // MaxArchiveFiles = 2 (Removes the first file) + } + finally + { + LogManager.Configuration = null; + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty) + { + var logFile = Path.GetTempFileName() + ".txt"; + try + { + const string header = "Headerline"; + + // Configure first time + var fileTarget = new FileTarget + { + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = "${message}", + Header = header, + WriteBom = true, + WriteHeaderWhenInitialFileNotEmpty = true + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + + LogManager.Configuration = null; + + string headerPart = header + LineEndingMode.LF.NewLineCharacters; + string logPart = "aaa\nbbb\nccc\n"; + AssertFileContents(logFile, headerPart + logPart, Encoding.UTF8, addBom: true); + + // Configure second time + fileTarget = new FileTarget + { + FileName = SimpleLayout.Escape(logFile), + LineEnding = LineEndingMode.LF, + Layout = "${message}", + Header = header, + WriteBom = true, + WriteHeaderWhenInitialFileNotEmpty = writeHeaderWhenInitialFileNotEmpty + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + + LogManager.Configuration = null; // Flush + + if (writeHeaderWhenInitialFileNotEmpty) + AssertFileContents(logFile, headerPart + logPart + headerPart + logPart, Encoding.UTF8, addBom: true); + else + AssertFileContents(logFile, headerPart + logPart + logPart, Encoding.UTF8, addBom: true); + } + finally + { + LogManager.Configuration = null; + if (File.Exists(logFile)) + File.Delete(logFile); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void RollingArchiveTest(bool specifyArchiveFileName) + { + RollingArchiveTests(enableCompression: false, specifyArchiveFileName: specifyArchiveFileName); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void RollingArchiveCompressionTest(bool specifyArchiveFileName) + { + RollingArchiveTests(enableCompression: true, specifyArchiveFileName: specifyArchiveFileName); + } + + private void RollingArchiveTests(bool enableCompression, bool specifyArchiveFileName) + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + var archiveExtension = enableCompression ? "zip" : "txt"; + +#if NET35 || NET40 + IFileCompressor fileCompressor = null; +#endif + + try + { + var fileTarget = new FileTarget + { + EnableArchiveFileCompression = enableCompression, + FileName = logFile, + ArchiveAboveSize = 100, + LineEnding = LineEndingMode.LF, + ArchiveNumbering = ArchiveNumberingMode.Rolling, + Layout = "${message}", + MaxArchiveFiles = 3 + }; + +#if NET35 || NET40 + if (enableCompression) + { + fileCompressor = FileTarget.FileCompressor; + FileTarget.FileCompressor = new CustomFileCompressor(); + } +#endif + + if (specifyArchiveFileName) + fileTarget.ArchiveFileName = Path.Combine(tempDir, "archive", "{####}." + archiveExtension); + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + // we emit 5 * 25 * (3 x aaa + \n) bytes + // so that we should get a full file + 3 archives + Generate100BytesLog('a'); + Generate100BytesLog('b'); + Generate100BytesLog('c'); + Generate100BytesLog('d'); + Generate100BytesLog('e'); + + LogManager.Configuration = null; // Flush + + var assertFileContents = + enableCompression ? new Action(AssertZipFileContents) : AssertFileContents; + + var times = 25; + AssertFileContents(logFile, + StringRepeat(times, "eee\n"), + Encoding.UTF8); + + string archiveFileNameFormat = specifyArchiveFileName + ? Path.Combine("archive", "000{0}." + archiveExtension) + : "file.{0}." + archiveExtension; + + assertFileContents( + Path.Combine(tempDir, string.Format(archiveFileNameFormat, 0)), + "file.txt", + StringRepeat(times, "ddd\n"), + Encoding.UTF8); + + assertFileContents( + Path.Combine(tempDir, string.Format(archiveFileNameFormat, 1)), + "file.txt", + StringRepeat(times, "ccc\n"), + Encoding.UTF8); + + assertFileContents( + Path.Combine(tempDir, string.Format(archiveFileNameFormat, 2)), + "file.txt", + StringRepeat(times, "bbb\n"), + Encoding.UTF8); + + Assert.False(File.Exists(Path.Combine(tempDir, string.Format(archiveFileNameFormat, 3)))); + } + finally + { +#if NET35 || NET40 + if (enableCompression) + { + FileTarget.FileCompressor = fileCompressor; + } +#endif + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [InlineData("/")] + [InlineData("\\")] + [Theory] + public void RollingArchiveTest_MaxArchiveFiles_0(string slash) + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(tempDir, "archive" + slash + "{####}.txt"), + ArchiveAboveSize = 100, + LineEnding = LineEndingMode.LF, + ArchiveNumbering = ArchiveNumberingMode.Rolling, + Layout = "${message}", + MaxArchiveFiles = 0 + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + // we emit 5 * 25 * (3 x aaa + \n) bytes + // so that we should get a full file + 4 archives + Generate100BytesLog('a'); + Generate100BytesLog('b'); + Generate100BytesLog('c'); + Generate100BytesLog('d'); + Generate100BytesLog('e'); + + LogManager.Configuration = null; // Flush + + var times = 25; + AssertFileContents(logFile, + StringRepeat(times, "eee\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(tempDir, "archive" + slash + "0000.txt"), + StringRepeat(times, "ddd\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(tempDir, "archive" + slash + "0001.txt"), + StringRepeat(times, "ccc\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(tempDir, "archive" + slash + "0002.txt"), + StringRepeat(times, "bbb\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(tempDir, "archive" + slash + "0003.txt"), + StringRepeat(times, "aaa\n"), + Encoding.UTF8); + } + finally + { + + if (File.Exists(logFile)) + { + File.Delete(logFile); + } + + if (Directory.Exists(tempDir)) + { + Directory.Delete(tempDir, true); + } + } + } + + [Fact] + public void MultiFileWrite() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + try + { + var fileTarget = new FileTarget + { + FileName = Path.Combine(tempDir, "${level}.txt"), + LineEnding = LineEndingMode.LF, + Layout = "${message}" + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger(LogLevel.Debug).WriteTo(fileTarget)); + + var times = 25; + for (var i = 0; i < times; ++i) + { + logger.Trace("@@@"); + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + logger.Error("ddd"); + logger.Fatal("eee"); + } + + LogManager.Configuration = null; // Flush + + Assert.False(File.Exists(Path.Combine(tempDir, "Trace.txt"))); + + AssertFileContents(Path.Combine(tempDir, "Debug.txt"), + StringRepeat(times, "aaa\n"), Encoding.UTF8); + + AssertFileContents(Path.Combine(tempDir, "Info.txt"), + StringRepeat(times, "bbb\n"), Encoding.UTF8); + + AssertFileContents(Path.Combine(tempDir, "Warn.txt"), + StringRepeat(times, "ccc\n"), Encoding.UTF8); + + AssertFileContents(Path.Combine(tempDir, "Error.txt"), + StringRepeat(times, "ddd\n"), Encoding.UTF8); + + AssertFileContents(Path.Combine(tempDir, "Fatal.txt"), + StringRepeat(times, "eee\n"), Encoding.UTF8); + } + finally + { + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void BufferedMultiFileWrite() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + try + { + var fileTarget = new FileTarget + { + FileName = Path.Combine(tempDir, "${level}.txt"), + LineEnding = LineEndingMode.LF, + Layout = "${message}" + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger(LogLevel.Debug).WriteTo(new BufferingTargetWrapper(fileTarget, 10))); + + var times = 25; + for (var i = 0; i < times; ++i) + { + logger.Trace("@@@"); + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + logger.Error("ddd"); + logger.Fatal("eee"); + } + + LogManager.Configuration = null; // Flush + + Assert.False(File.Exists(Path.Combine(tempDir, "Trace.txt"))); + + AssertFileContents(Path.Combine(tempDir, "Debug.txt"), + StringRepeat(times, "aaa\n"), Encoding.UTF8); + + AssertFileContents(Path.Combine(tempDir, "Info.txt"), + StringRepeat(times, "bbb\n"), Encoding.UTF8); + + AssertFileContents(Path.Combine(tempDir, "Warn.txt"), + StringRepeat(times, "ccc\n"), Encoding.UTF8); + + AssertFileContents(Path.Combine(tempDir, "Error.txt"), + StringRepeat(times, "ddd\n"), Encoding.UTF8); + + AssertFileContents(Path.Combine(tempDir, "Fatal.txt"), + StringRepeat(times, "eee\n"), Encoding.UTF8); + } + finally + { + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void AsyncMultiFileWrite() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + try + { + var fileTarget = new FileTarget + { + FileName = Path.Combine(tempDir, "${level}.txt"), + LineEnding = LineEndingMode.LF, + Layout = "${message} ${threadid}" + }; + + // this also checks that thread-volatile layouts + // such as ${threadid} are properly cached and not recalculated + // in logging threads. + var threadID = CurrentManagedThreadId.ToString(); + + LogManager.Setup().LoadConfiguration(c => c.ForLogger(LogLevel.Debug).WriteTo(fileTarget).WithAsync()); + + var times = 25; + for (var i = 0; i < times; ++i) + { + logger.Trace("@@@"); + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + logger.Error("ddd"); + logger.Fatal("eee"); + } + + LogManager.Configuration = null; // Flush + + Assert.False(File.Exists(Path.Combine(tempDir, "Trace.txt"))); + + AssertFileContents(Path.Combine(tempDir, "Debug.txt"), + StringRepeat(times, "aaa " + threadID + "\n"), Encoding.UTF8); + + AssertFileContents(Path.Combine(tempDir, "Info.txt"), + StringRepeat(times, "bbb " + threadID + "\n"), Encoding.UTF8); + + AssertFileContents(Path.Combine(tempDir, "Warn.txt"), + StringRepeat(times, "ccc " + threadID + "\n"), Encoding.UTF8); + + AssertFileContents(Path.Combine(tempDir, "Error.txt"), + StringRepeat(times, "ddd " + threadID + "\n"), Encoding.UTF8); + + AssertFileContents(Path.Combine(tempDir, "Fatal.txt"), + StringRepeat(times, "eee " + threadID + "\n"), Encoding.UTF8); + } + finally + { + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void DisposingFileTarget_WhenNotIntialized_ShouldNotThrow() + { + var exceptionThrown = false; + var fileTarget = new FileTarget(); + + try + { + fileTarget.Dispose(); + } + catch + { + exceptionThrown = true; + } + + Assert.False(exceptionThrown); + } + + [Fact] + public void FileTarget_ArchiveNumbering_DateAndSequence() + { + FileTarget_ArchiveNumbering_DateAndSequenceTests(enableCompression: false, fileTxt: "file.txt", archiveFileName: Path.Combine("archive", "{#}.txt")); + } + + [Fact] + public void FileTarget_ArchiveNumbering_DateAndSequence_archive_same_as_log_name() + { + FileTarget_ArchiveNumbering_DateAndSequenceTests(enableCompression: false, fileTxt: "file-${date:format=yyyy-MM-dd}.txt", archiveFileName: "file-{#}.txt"); + } + + [Fact] + public void FileTarget_ArchiveNumbering_DateAndSequence_WithCompression() + { + FileTarget_ArchiveNumbering_DateAndSequenceTests(enableCompression: true, fileTxt: "file.txt", archiveFileName: Path.Combine("archive", "{#}.zip")); + } + + private void FileTarget_ArchiveNumbering_DateAndSequenceTests(bool enableCompression, string fileTxt, string archiveFileName) + { + const string archiveDateFormat = "yyyy-MM-dd"; + const int archiveAboveSize = 100; + + var tempDir = ArchiveFileNameHelper.GenerateTempPath(); + Layout logFile = Path.Combine(tempDir, fileTxt); + var logFileName = logFile.Render(LogEventInfo.CreateNullEvent()); + +#if NET35 || NET40 + IFileCompressor fileCompressor = null; +#endif + + try + { + var fileTarget = new FileTarget + { + EnableArchiveFileCompression = enableCompression, + FileName = logFile, + ArchiveFileName = Path.Combine(tempDir, archiveFileName), + ArchiveDateFormat = archiveDateFormat, + ArchiveAboveSize = archiveAboveSize, + LineEnding = LineEndingMode.LF, + Layout = "${message}", + MaxArchiveFiles = 3, + ArchiveNumbering = ArchiveNumberingMode.DateAndSequence, + ArchiveEvery = FileArchivePeriod.Day + }; + + +#if NET35 || NET40 + if (enableCompression) + { + fileCompressor = FileTarget.FileCompressor; + FileTarget.FileCompressor = new CustomFileCompressor(); + } +#endif + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + // we emit 5 * 25 *(3 x aaa + \n) bytes + // so that we should get a full file + 3 archives + Generate100BytesLog('a'); + Generate100BytesLog('b'); + Generate100BytesLog('c'); + Generate100BytesLog('d'); + Generate100BytesLog('e'); + + string renderedArchiveFileName = archiveFileName.Replace("{#}", DateTime.Now.ToString(archiveDateFormat)); + + LogManager.Configuration = null; + + var assertFileContents = enableCompression ? new Action(AssertZipFileContents) : AssertFileContents; + + var extension = Path.GetExtension(renderedArchiveFileName); + var fileNameWithoutExt = renderedArchiveFileName.Substring(0, renderedArchiveFileName.Length - extension.Length); + ArchiveFileNameHelper helper = new ArchiveFileNameHelper(tempDir, fileNameWithoutExt, extension); + + var times = 25; + AssertFileContents(logFileName, + StringRepeat(times, "eee\n"), + Encoding.UTF8); + +#if !NET35 + string expectedEntry1Name = Path.GetFileNameWithoutExtension(helper.GetFullPath(1)) + ".txt"; + string expectedEntry2Name = Path.GetFileNameWithoutExtension(helper.GetFullPath(2)) + ".txt"; + string expectedEntry3Name = Path.GetFileNameWithoutExtension(helper.GetFullPath(3)) + ".txt"; +#else + string expectedEntry1Name = fileTxt; + string expectedEntry2Name = fileTxt; + string expectedEntry3Name = fileTxt; +#endif + assertFileContents(helper.GetFullPath(1), expectedEntry1Name, StringRepeat(times, "bbb\n"), Encoding.UTF8); + assertFileContents(helper.GetFullPath(2), expectedEntry2Name, StringRepeat(times, "ccc\n"), Encoding.UTF8); + assertFileContents(helper.GetFullPath(3), expectedEntry3Name, StringRepeat(times, "ddd\n"), Encoding.UTF8); + + Assert.False(helper.Exists(0), "First archive should have been deleted due to max archive count."); + Assert.False(helper.Exists(4), "Fifth archive must not have been created yet."); + } + finally + { +#if NET35 || NET40 + if (enableCompression) + { + FileTarget.FileCompressor = fileCompressor; + } +#endif + + if (File.Exists(logFileName)) + File.Delete(logFileName); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Theory] + [InlineData("archive/test.log.{####}", "archive/test.log.0000", ArchiveNumberingMode.Sequence)] + [InlineData("archive\\test.log.{####}", "archive\\test.log.0000", ArchiveNumberingMode.Sequence)] + [InlineData("file-${date:format=yyyyMMdd}.txt", "file-${date:format=yyyyMMdd}.txt", ArchiveNumberingMode.Sequence)] + [InlineData("file-{#}.txt", "file-${date:format=yyyyMMdd}.txt", ArchiveNumberingMode.Date)] + public void FileTargetArchiveFileNameTest(string archiveFileName, string expectedArchiveFileName, ArchiveNumberingMode archiveNumbering) + { + var subPath = Guid.NewGuid().ToString(); + var tempDir = Path.Combine(Path.GetTempPath(), subPath); + var logFile = Path.Combine(tempDir, "file-${date:format=yyyyMMdd}.txt"); + try + { + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(tempDir, "..", subPath, archiveFileName), + ArchiveNumbering = archiveNumbering, + ArchiveAboveSize = 1000, + MaxArchiveFiles = 1000, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + for (var i = 0; i < 25; ++i) + { + logger.Debug("a"); + } + + LogManager.Configuration = null; + + logFile = new SimpleLayout(logFile).Render(LogEventInfo.CreateNullEvent()); + expectedArchiveFileName = new SimpleLayout(expectedArchiveFileName).Render(LogEventInfo.CreateNullEvent()); + + Assert.True(File.Exists(logFile)); + Assert.True(File.Exists(Path.Combine(tempDir, expectedArchiveFileName))); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void FileTarget_InvalidFileNameCorrection() + { + var tempFile = Path.GetTempFileName(); + var invalidLogFileName = tempFile + Path.GetInvalidFileNameChars()[0]; + var expectedCorrectedTempFile = tempFile + "_"; + + try + { + var fileTarget = new FileTarget + { + FileName = SimpleLayout.Escape(invalidLogFileName), + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}", + OpenFileCacheTimeout = 0 + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Fatal("aaa"); + + LogManager.Configuration = null; // Flush + + AssertFileContents(expectedCorrectedTempFile, "Fatal aaa\n", Encoding.UTF8); + } + finally + { + if (File.Exists(expectedCorrectedTempFile)) + File.Delete(expectedCorrectedTempFile); + if (File.Exists(invalidLogFileName)) + File.Delete(invalidLogFileName); + } + } + + [Fact] + public void FileTarget_LogAndArchiveFilesWithSameName_ShouldArchive() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "Application.log"); + var tempDirectory = new DirectoryInfo(tempDir); + try + { + + var archiveFile = Path.Combine(tempDir, "Application{#}.log"); + var archiveFileMask = "Application*.log"; + + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = archiveFile, + ArchiveAboveSize = 1, //Force immediate archival + ArchiveNumbering = ArchiveNumberingMode.DateAndSequence, + MaxArchiveFiles = 5 + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + //Creates 5 archive files. + for (int i = 0; i <= 5; i++) + { + logger.Debug("a"); + } + + LogManager.Configuration = null; // Flush + + Assert.True(File.Exists(logFile)); + + //Five archive files, plus the log file itself. + Assert.True(tempDirectory.GetFiles(archiveFileMask).Length == 5 + 1); + } + finally + { + if (tempDirectory.Exists) + { + tempDirectory.Delete(true); + } + } + } + + [Fact] + public void FileTarget_Handle_Other_Files_That_Match_Archive_Format() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "Application.log"); + var tempDirectory = new DirectoryInfo(tempDir); + + try + { + string archiveFileLayout = Path.Combine(Path.GetDirectoryName(logFile), Path.GetFileNameWithoutExtension(logFile) + "{#}" + Path.GetExtension(logFile)); + + var fileTarget = new FileTarget + { + FileName = logFile, + Layout = "${message}", + EnableFileDelete = false, + Encoding = Encoding.UTF8, + ArchiveFileName = archiveFileLayout, + ArchiveEvery = FileArchivePeriod.Day, + ArchiveNumbering = ArchiveNumberingMode.Date, + ArchiveDateFormat = "___________yyyyMMddHHmm", + MaxArchiveFiles = 10 // Get past the optimization to avoid deleting old files. + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + string existingFile = archiveFileLayout.Replace("{#}", "notadate"); + Directory.CreateDirectory(Path.GetDirectoryName(logFile)); + File.Create(existingFile).Close(); + + logger.Debug("test"); + + LogManager.Configuration = null; // Flush + + AssertFileContents(logFile, "test" + LineEndingMode.Default.NewLineCharacters, Encoding.UTF8); + Assert.True(File.Exists(existingFile)); + } + finally + { + if (tempDirectory.Exists) + { + tempDirectory.Delete(true); + } + } + } + + [Fact] + public void SingleArchiveFileRollsCorrectly() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(tempDir, "archive", "file.txt2"), + ArchiveAboveSize = 100, + LineEnding = LineEndingMode.LF, + Layout = "${message}", + MaxArchiveFiles = 1, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + // we emit 2 * 25 *(aaa + \n) bytes + // so that we should get a full file + 1 archives + var times = 25; + for (var i = 0; i < times; ++i) + { + logger.Debug("aaa"); + } + for (var i = 0; i < times; ++i) + { + logger.Debug("bbb"); + } + + LogManager.Flush(); + + AssertFileContents(logFile, + StringRepeat(times, "bbb\n"), + Encoding.UTF8); + AssertFileContents( + Path.Combine(tempDir, "archive", "file.txt2"), + StringRepeat(times, "aaa\n"), + Encoding.UTF8); + + for (var i = 0; i < times; ++i) + { + logger.Debug("ccc"); + } + + LogManager.Configuration = null; // Flush + + AssertFileContents(logFile, + StringRepeat(times, "ccc\n"), + Encoding.UTF8); + AssertFileContents( + Path.Combine(tempDir, "archive", "file.txt2"), + StringRepeat(times, "bbb\n"), + Encoding.UTF8); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void ArchiveFileRollsCorrectly() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(tempDir, "archive", "file.txt2"), + ArchiveAboveSize = 100, + LineEnding = LineEndingMode.LF, + Layout = "${message}", + MaxArchiveFiles = 2, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + // we emit 3 * 25 *(aaa + \n) bytes + // so that we should get a full file + 2 archives + var times = 25; + for (var i = 0; i < times; ++i) + { + logger.Debug("aaa"); + } + for (var i = 0; i < times; ++i) + { + logger.Debug("bbb"); + } + for (var i = 0; i < times; ++i) + { + logger.Debug("ccc"); + } + + LogManager.Flush(); + + AssertFileContents(logFile, + StringRepeat(times, "ccc\n"), + Encoding.UTF8); + AssertFileContents( + Path.Combine(tempDir, "archive", "file.1.txt2"), + StringRepeat(times, "bbb\n"), + Encoding.UTF8); + AssertFileContents( + Path.Combine(tempDir, "archive", "file.txt2"), + StringRepeat(times, "aaa\n"), + Encoding.UTF8); + + for (var i = 0; i < times; ++i) + { + logger.Debug("ddd"); + } + + LogManager.Configuration = null; // Flush + + AssertFileContents(logFile, + StringRepeat(times, "ddd\n"), + Encoding.UTF8); + AssertFileContents( + Path.Combine(tempDir, "archive", "file.2.txt2"), + StringRepeat(times, "ccc\n"), + Encoding.UTF8); + AssertFileContents( + Path.Combine(tempDir, "archive", "file.1.txt2"), + StringRepeat(times, "bbb\n"), + Encoding.UTF8); + Assert.False(File.Exists(Path.Combine(tempDir, "archive", "file.txt2"))); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void ArchiveFileRollsCorrectly_ExistingArchives() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + Directory.CreateDirectory(Path.Combine(tempDir, "archive")); + File.Create(Path.Combine(tempDir, "archive", "file.10.txt2")).Dispose(); + File.Create(Path.Combine(tempDir, "archive", "file.9.txt2")).Dispose(); + + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(tempDir, "archive", "file.txt2"), + ArchiveAboveSize = 100, + LineEnding = LineEndingMode.LF, + Layout = "${message}", + MaxArchiveFiles = 2, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + // we emit 2 * 25 *(aaa + \n) bytes + // so that we should get a full file + 1 archive + var times = 25; + for (var i = 0; i < times; ++i) + { + logger.Debug("aaa"); + } + for (var i = 0; i < times; ++i) + { + logger.Debug("bbb"); + } + + LogManager.Configuration = null; // Flush + + AssertFileContents(logFile, + StringRepeat(times, "bbb\n"), + Encoding.UTF8); + AssertFileContents( + Path.Combine(tempDir, "archive", "file.11.txt2"), + StringRepeat(times, "aaa\n"), + Encoding.UTF8); + Assert.True(File.Exists(Path.Combine(tempDir, "archive", "file.10.txt2"))); + Assert.False(File.Exists(Path.Combine(tempDir, "archive", "file.9.txt2"))); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + /// + /// Remove archived files in correct order + /// + [Fact] + public void FileTarget_ArchiveNumbering_remove_correct_order() + { + const int maxArchiveFiles = 10; + + var tempDir = ArchiveFileNameHelper.GenerateTempPath(); + var logFile = Path.Combine(tempDir, "file.txt"); + var archiveExtension = "txt"; + try + { + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(tempDir, "archive", "{#}." + archiveExtension), + ArchiveDateFormat = "yyyy-MM-dd", + ArchiveAboveSize = 100, + LineEnding = LineEndingMode.LF, + Layout = "${message}", + MaxArchiveFiles = maxArchiveFiles, + ArchiveNumbering = ArchiveNumberingMode.DateAndSequence, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + ArchiveFileNameHelper helper = new ArchiveFileNameHelper(Path.Combine(tempDir, "archive"), DateTime.Now.ToString(fileTarget.ArchiveDateFormat), archiveExtension); + + Generate100BytesLog('a'); + + for (int i = 0; i < maxArchiveFiles; i++) + { + Generate100BytesLog('a'); + Assert.True(helper.Exists(i), $"file {i} is missing"); + } + + for (int i = maxArchiveFiles; i < 21; i++) + { + Generate100BytesLog('b'); + var numberToBeRemoved = i - maxArchiveFiles; // number 11, we need to remove 1 etc + Assert.False(helper.Exists(numberToBeRemoved), + $"archive file {numberToBeRemoved} has not been removed! We are created file {i}"); + } + + LogManager.Configuration = null; + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + /// + /// Allow multiple archives within the same directory + /// + [Fact] + public void FileTarget_ArchiveNumbering_remove_correct_wildcard() + { + const int maxArchiveFiles = 5; + + var tempDir = ArchiveFileNameHelper.GenerateTempPath(); + var logFile = Path.Combine(tempDir, "{0}{1}.txt"); + + var defaultTimeSource = TimeSource.Current; + try + { + var timeSource = new TimeSourceTests.ShiftedTimeSource(DateTimeKind.Local); + if (timeSource.Time.Minute == 59) + { + // Avoid double-archive due to overflow of the hour. + timeSource.AddToLocalTime(TimeSpan.FromMinutes(1)); + timeSource.AddToSystemTime(TimeSpan.FromMinutes(1)); + } + TimeSource.Current = timeSource; + + var fileTarget = new FileTarget + { + FileName = string.Format(logFile, "${logger}", "${shortdate}"), + ArchiveAboveSize = 100, + LineEnding = LineEndingMode.LF, + Layout = "${message}", + MaxArchiveFiles = maxArchiveFiles, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + var logger1 = LogManager.GetLogger("log"); + var logger2 = LogManager.GetLogger("log-other"); + + timeSource.AddToLocalTime(TimeSpan.Zero - TimeSpan.FromDays(1)); + + Generate100BytesLog((char)('0'), logger1); + Generate100BytesLog((char)('0'), logger2); + + for (int i = 0; i <= maxArchiveFiles - 3; i++) + { + Generate100BytesLog((char)('1' + i), logger1); + Generate100BytesLog((char)('1' + i), logger2); + var logFile1 = string.Format(logFile, logger1.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd")); + var logFile2 = string.Format(logFile, logger2.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd")); + Assert.True(File.Exists(logFile1), + $"{logFile1} is missing"); + Assert.True(File.Exists(logFile2), + $"{logFile2} is missing"); + logFile1 = string.Format(logFile, logger1.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + i.ToString()); + logFile2 = string.Format(logFile, logger2.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + i.ToString()); + Assert.True(File.Exists(logFile1), + $"{logFile1} is missing"); + Assert.True(File.Exists(logFile2), + $"{logFile2} is missing"); + } + + TimeSource.Current = defaultTimeSource; // restore default time source + Generate100BytesLog((char)('a'), logger1); + Generate100BytesLog((char)('a'), logger2); + for (int i = 0; i < maxArchiveFiles; i++) + { + Generate100BytesLog((char)('b' + i), logger1); + Generate100BytesLog((char)('b' + i), logger2); + var logFile1 = string.Format(logFile, logger1.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + i.ToString()); + var logFile2 = string.Format(logFile, logger2.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + i.ToString()); + Assert.True(File.Exists(logFile1), + $"{logFile1} is missing"); + Assert.True(File.Exists(logFile2), + $"{logFile2} is missing"); + } + + for (int i = maxArchiveFiles; i < 10; i++) + { + Generate100BytesLog((char)('b' + i), logger1); + Generate100BytesLog((char)('b' + i), logger2); + var numberToBeRemoved = i - maxArchiveFiles; + + var logFile1 = string.Format(logFile, logger1.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + numberToBeRemoved.ToString()); + var logFile2 = string.Format(logFile, logger2.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + numberToBeRemoved.ToString()); + + Assert.False(File.Exists(logFile1), + $"archive FirstFile {numberToBeRemoved} has not been removed! We are created file {i}"); + Assert.False(File.Exists(logFile2), + $"archive SecondFile {numberToBeRemoved} has not been removed! We are created file {i}"); + + logFile1 = string.Format(logFile, logger1.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + i.ToString()); + logFile2 = string.Format(logFile, logger2.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + i.ToString()); + + Assert.True(File.Exists(logFile1), + $"{logFile1} is missing"); + Assert.True(File.Exists(logFile2), + $"{logFile2} is missing"); + } + + // Verify that archieve-cleanup after startup handles same folder archive correctly + fileTarget.ArchiveAboveSize = 200; + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + logger1.Info("Bye"); + logger2.Info("Bye"); + Assert.Equal(12, Directory.GetFiles(tempDir).Length); + + LogManager.Configuration = null; + } + finally + { + TimeSource.Current = defaultTimeSource; // restore default time source + + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + /// + /// See that dynamic sequence archive supports same-folder archiving. + /// + [Fact] + public void FileTarget_SameDirectory_MaxArchiveFiles_One() + { + const int maxArchiveFiles = 1; + + var tempDir = ArchiveFileNameHelper.GenerateTempPath(); + var logFile1 = Path.Combine(tempDir, "Log{0}.txt"); + try + { + var fileTarget = new FileTarget + { + FileName = string.Format(logFile1, ""), + ArchiveAboveSize = 100, + LineEnding = LineEndingMode.LF, + Layout = "${message}", + MaxArchiveFiles = maxArchiveFiles, + Encoding = Encoding.ASCII, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + Generate100BytesLog('a'); + Generate100BytesLog('b'); + Generate100BytesLog('c'); + + var times = 25; + AssertFileContents(string.Format(logFile1, ".0"), + StringRepeat(times, "bbb\n"), + Encoding.ASCII); + + AssertFileContents(string.Format(logFile1, ""), + StringRepeat(times, "ccc\n"), + Encoding.ASCII); + + LogManager.Configuration = null; + } + finally + { + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + private void Generate100BytesLog(char c, Logger logger = null) + { + logger = logger ?? this.logger; + for (var i = 0; i < 25; ++i) + { + //3 chars with newlines = 4 bytes + logger.Debug(new string(c, 3)); + } + } + + /// + /// Archive file helepr + /// + /// TODO rewrite older test + private sealed class ArchiveFileNameHelper + { + public string FolderName { get; private set; } + + public string FileName { get; private set; } + /// + /// Ext without dot + /// + public string Ext { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public ArchiveFileNameHelper(string folderName, string fileName, string ext) + { + Ext = ext.TrimStart('.'); + FileName = fileName; + FolderName = folderName; + } + + public bool Exists(int number) + { + return File.Exists(GetFullPath(number)); + } + + public string GetFullPath(int number) + { + return Path.Combine($"{FolderName}/{FileName}.{number}.{Ext}"); + } + + public static string GenerateTempPath() + { + return Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + } + } + + [Theory] + [InlineData("##", 0, "00")] + [InlineData("###", 1, "001")] + [InlineData("#", 20, "20")] + public void FileTarget_WithDateAndSequenceArchiveNumbering_ShouldPadSequenceNumberInArchiveFileName( + string placeHolderSharps, int sequenceNumber, string expectedSequenceInArchiveFileName) + { + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + const string archiveDateFormat = "yyyy-MM-dd"; + string archiveFileName = Path.Combine(tempDir, $"{{{placeHolderSharps}}}.log"); + string expectedArchiveFullName = + $"{tempDir}/{DateTime.Now.ToString(archiveDateFormat)}.{expectedSequenceInArchiveFileName}.log"; + + GenerateArchives(count: sequenceNumber + 1, archiveDateFormat: archiveDateFormat, + archiveFileName: archiveFileName, archiveNumbering: ArchiveNumberingMode.DateAndSequence); + bool resultArchiveWithExpectedNameExists = File.Exists(expectedArchiveFullName); + + Assert.True(resultArchiveWithExpectedNameExists); + } + + [Theory] + [InlineData("yyyy-MM-dd HHmm")] + [InlineData("y")] + [InlineData("D")] + public void FileTarget_WithDateAndSequenceArchiveNumbering_ShouldRespectArchiveDateFormat( + string archiveDateFormat) + { + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string archiveFileName = Path.Combine(tempDir, "{#}.log"); + string expectedDateInArchiveFileName = DateTime.Now.ToString(archiveDateFormat); + string expectedArchiveFullName = $"{tempDir}/{expectedDateInArchiveFileName}.1.log"; + + // We generate 2 archives so that the algorithm that seeks old archives is also tested. + GenerateArchives(count: 2, archiveDateFormat: archiveDateFormat, archiveFileName: archiveFileName, + archiveNumbering: ArchiveNumberingMode.DateAndSequence); + bool resultArchiveWithExpectedNameExists = File.Exists(expectedArchiveFullName); + + Assert.True(resultArchiveWithExpectedNameExists); + } + + private void GenerateArchives(int count, string archiveDateFormat, string archiveFileName, + ArchiveNumberingMode archiveNumbering) + { + string logFileName = Path.GetTempFileName(); + const int logFileMaxSize = 1; + var fileTarget = new FileTarget + { + FileName = logFileName, + ArchiveFileName = archiveFileName, + ArchiveDateFormat = archiveDateFormat, + ArchiveNumbering = archiveNumbering, + ArchiveAboveSize = logFileMaxSize + }; + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + for (int currentSequenceNumber = 0; currentSequenceNumber < count; currentSequenceNumber++) + logger.Debug("Test {0}", currentSequenceNumber); + + LogManager.Flush(); + } + + [Fact] + public void Dont_throw_Exception_when_archiving_is_enabled() + { + try + { + LogManager.Setup().LoadConfigurationFromXml(@" + + + + + + + + + + +"); + + LogManager.GetLogger("Test").Info("very important message"); + + Assert.NotNull(LogManager.Configuration); + } + finally + { + LogManager.Configuration = null; + } + } + + [Fact] + public void Dont_throw_Exception_when_archiving_is_enabled_with_async() + { + try + { + LogManager.Setup().LoadConfigurationFromXml(@" + + + + + + + + + + +"); + + LogManager.GetLogger("Test").Info("very important message"); + + Assert.NotNull(LogManager.Configuration); + } + finally + { + LogManager.Configuration = null; + } + } + + [Fact] + public void DatedArchiveForFileTargetWithMultipleFiles() + { + var defaultTimeSource = TimeSource.Current; + + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()) + Path.DirectorySeparatorChar; + + try + { + var timeSource = new TimeSourceTests.ShiftedTimeSource(DateTimeKind.Local); + if (timeSource.Time.Minute == 59) + { + // Avoid double-archive due to overflow of the hour. + timeSource.AddToLocalTime(TimeSpan.FromMinutes(1)); + timeSource.AddToSystemTime(TimeSpan.FromMinutes(1)); + } + TimeSource.Current = timeSource; + + GlobalDiagnosticsContext.Set("basedir", tempDir); + + LogManager.Setup().LoadConfigurationFromXml(@" + + + + + + + + + +"); + + var fileLogger = LogManager.GetLogger(nameof(DatedArchiveForFileTargetWithMultipleFiles)); + LogEventInfo logEvent = LogEventInfo.Create(LogLevel.Info, fileLogger.Name, "Very Important Message"); + logEvent.Properties["serialNo"] = "M91803ED2172"; + logger.Log(logEvent); + + LogEventInfo logEvent2 = LogEventInfo.Create(LogLevel.Info, fileLogger.Name, "Very Important Message"); + logEvent2.Properties["serialNo"] = "M91803ED2137"; + logger.Log(logEvent2); + + var currentDate = timeSource.Time.Date; + timeSource.AddToLocalTime(TimeSpan.FromDays(5)); + + LogEventInfo logEvent3 = LogEventInfo.Create(LogLevel.Info, fileLogger.Name, "Very Important Message"); + logEvent3.Properties["serialNo"] = logEvent.Properties["serialNo"]; + logger.Log(logEvent3); + + LogEventInfo logEvent4 = LogEventInfo.Create(LogLevel.Info, fileLogger.Name, "Very Important Message"); + logEvent4.Properties["serialNo"] = logEvent2.Properties["serialNo"]; + logger.Log(logEvent4); + + var currentFiles = new DirectoryInfo(tempDir).GetFiles(); + Assert.Equal(4, currentFiles.Length); + Assert.Contains(logEvent.Properties["serialNo"] + ".txt", currentFiles.Select(f => f.Name)); + Assert.Contains(logEvent.Properties["serialNo"] + "." + currentDate.ToString("yyyy-MM-dd") + ".txt", currentFiles.Select(f => f.Name)); + Assert.Contains(logEvent2.Properties["serialNo"] + ".txt", currentFiles.Select(f => f.Name)); + Assert.Contains(logEvent2.Properties["serialNo"] + "." + currentDate.ToString("yyyy-MM-dd") + ".txt", currentFiles.Select(f => f.Name)); + } + finally + { + TimeSource.Current = defaultTimeSource; + + LogManager.Configuration = null; + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void LoggingShouldNotTriggerTypeResolveEventTest() + { + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "log.log"); + + System.ResolveEventHandler noResolveTest = (s, args) => { Assert.True(false); return null; }; + + try + { + LogManager.Setup().LoadConfigurationFromXml(@" + + + + + + + + + + + + + + "); + + LogManager.GetLogger("Test").Info("very important message"); + + Assert.NotNull(LogManager.Configuration); + + AppDomain.CurrentDomain.TypeResolve += noResolveTest; + AppDomain.CurrentDomain.AssemblyResolve += noResolveTest; + + LogManager.GetLogger("Test").Info("very important message"); + } + finally + { + AppDomain.CurrentDomain.TypeResolve -= noResolveTest; + AppDomain.CurrentDomain.AssemblyResolve -= noResolveTest; + LogManager.Configuration = null; + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Theory] + [InlineData("yyyyMMdd-HHmm")] + [InlineData("yyyyMMdd")] + [InlineData("yyyy-MM-dd")] + public void MaxArchiveFilesWithDateFormatTest(string archiveDateFormat) + { + TestMaxArchiveFilesWithDate(2, 2, archiveDateFormat, true); + TestMaxArchiveFilesWithDate(2, 2, archiveDateFormat, false); + } + + /// + /// + /// + /// max count of archived files + /// expected count of archived files + /// date format + /// change file creation/last write date + private static void TestMaxArchiveFilesWithDate(int maxArchiveFilesConfig, int expectedArchiveFiles, string dateFormat, bool changeCreationAndWriteTime) + { + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string archivePath = Path.Combine(tempDir, "archive"); + + var archiveDir = new DirectoryInfo(archivePath); + try + { + archiveDir.Create(); + //set-up, create files. + + //same dateformat as in config + string fileExt = ".log"; + DateTime now = DateTime.Now; + int i = 0; + foreach (string filePath in ArchiveFileNamesGenerator(archivePath, dateFormat, fileExt).Take(30)) + { + File.WriteAllLines(filePath, new[] { "test archive ", "=====", filePath }); + var time = now.AddDays(i); + if (changeCreationAndWriteTime) + { + File.SetCreationTime(filePath, time); + File.SetLastWriteTime(filePath, time); + } + i--; + } + + //create config with archiving + var configuration = XmlLoggingConfiguration.CreateFromXmlString(@" + + + + + + + + "); + + LogManager.Configuration = configuration; + var logger = LogManager.GetCurrentClassLogger(); + logger.Info("test"); + + var currentFilesCount = archiveDir.GetFiles().Length; + Assert.Equal(expectedArchiveFiles, currentFilesCount); + + LogManager.Configuration = null; // Flush + } + finally + { + //cleanup + archiveDir.Delete(true); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + /// unit test for issue #1681. + ///- When clearing out archive files exceeding maxArchiveFiles, NLog should + /// not delete all files in the directory.Only files matching the target's + /// archiveFileName pattern. + ///- Create test for 2 applications sharing the same archive directory. + ///- Create test for 2 applications sharing same archive directory and 1 + /// application containing multiple targets to the same archive directory. + /// *\* Expected outcome of this should be verified** + [Theory] + [InlineData(true)] + [InlineData(false)] + public void HandleArchiveFilesMultipleContextMultipleTargetTest(bool changeCreationAndWriteTime) + { + HandleArchiveFilesMultipleContextMultipleTargetsTest(2, 2, "yyyyMMdd-HHmm", changeCreationAndWriteTime); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void HandleArchiveFilesMultipleContextSingleTargetTest_ascii(bool changeCreationAndWriteTime) + { + HandleArchiveFilesMultipleContextSingleTargetsTest(2, 2, "yyyyMMdd-HHmm", changeCreationAndWriteTime); + } + + /// + /// Test the case when multiple applications are archiving to the same directory and using multiple targets. + /// Only the archives for this application instance should be deleted per the target archive rules. + /// + /// # to use for maxArchiveFiles in NLog configuration. + /// Expected number of archive files after archiving has occured. + /// string to be used for formatting log file names + /// + private static void HandleArchiveFilesMultipleContextMultipleTargetsTest( + int maxArchiveFilesConfig, int expectedArchiveFiles, string dateFormat, bool changeCreationAndWriteTime) + { + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string archivePath = Path.Combine(tempDir, "archive"); + var archiveDir = new DirectoryInfo(archivePath); + try + { + archiveDir.Create(); + //set-up, create files. + var numberFilesCreatedPerTargetArchive = 30; + + // use same config vars for mock files, as for nlog config + var fileExt = ".log"; + var app1TraceNm = "App1_Trace"; + var app1DebugNm = "App1_Debug"; + var app2Nm = "App2"; + + var now = DateTime.Now; + var i = 0; + // create mock app1_trace archives (matches app1 config for trace target) + foreach (string filePath in ArchiveFileNamesGenerator(archivePath, dateFormat, app1TraceNm + fileExt).Take(numberFilesCreatedPerTargetArchive)) + { + File.WriteAllLines(filePath, new[] { "test archive ", "=====", filePath }); + var time = now.AddDays(i); + if (changeCreationAndWriteTime) + { + File.SetCreationTime(filePath, time); + File.SetLastWriteTime(filePath, time); + } + i--; + } + i = 0; + // create mock app1_debug archives (matches app1 config for debug target) + foreach (string filePath in ArchiveFileNamesGenerator(archivePath, dateFormat, app1DebugNm + fileExt).Take(numberFilesCreatedPerTargetArchive)) + { + File.WriteAllLines(filePath, new[] { "test archive ", "=====", filePath }); + var time = now.AddDays(i); + if (changeCreationAndWriteTime) + { + File.SetCreationTime(filePath, time); + File.SetLastWriteTime(filePath, time); + } + i--; + } + i = 0; + // create mock app2 archives (matches app2 config for target) + foreach (string filePath in ArchiveFileNamesGenerator(archivePath, dateFormat, app2Nm + fileExt).Take(numberFilesCreatedPerTargetArchive)) + { + File.WriteAllLines(filePath, new[] { "test archive ", "=====", filePath }); + var time = now.AddDays(i); + if (changeCreationAndWriteTime) + { + File.SetCreationTime(filePath, time); + File.SetLastWriteTime(filePath, time); + } + i--; + } + + // Create same app1 Debug file as config defines. Will force archiving to happen on startup + File.WriteAllLines(tempDir + "\\" + app1DebugNm + fileExt, new[] { "Write first app debug target. Startup will archive this file" }, Encoding.ASCII); + + var app1Config = XmlLoggingConfiguration.CreateFromXmlString(@" + + + + + + + + + "); + + var app2Config = XmlLoggingConfiguration.CreateFromXmlString(@" + + + ; + + + + "); + + LogManager.Configuration = app1Config; + + var logger = LogManager.GetCurrentClassLogger(); + // Trigger archive to happen on startup + logger.Trace("Test 1 - Write to the log file that already exists; trigger archive to happen because archiveOldFileOnStartup='true'"); + + // TODO: perhaps extra App1 Debug and Trace files should both be deleted? (then app1TraceTargetFileCnt would be expected to = expectedArchiveFiles too) + // I think it depends on how NLog works with logging to both of those files in the call to logger.Debug() above + + // verify file counts. EXPECTED OUTCOME: + // app1 trace target: removed all extra + // app1 debug target: has all extra files + // app2: has all extra files + var app1TraceTargetFileCnt = archiveDir.GetFiles("*" + app1TraceNm + "*").Length; + var app1DebugTargetFileCnt = archiveDir.GetFiles("*" + app1DebugNm + "*").Length; + var app2FileTargetCnt = archiveDir.GetFiles("*" + app2Nm + "*").Length; + + Assert.Equal(numberFilesCreatedPerTargetArchive, app1DebugTargetFileCnt); + Assert.Equal(numberFilesCreatedPerTargetArchive, app2FileTargetCnt); + Assert.Equal(expectedArchiveFiles, app1TraceTargetFileCnt); + } + finally + { + //cleanup + LogManager.Configuration = null; + archiveDir.Delete(true); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + /// + /// Test the case when multiple applications are archiving to the same directory. + /// Only the archives for this application instance should be deleted per the target archive rules. + /// + /// # to use for maxArchiveFiles in NLog configuration. + /// Expected number of archive files after archiving has occured. + /// string to be used for formatting log file names + /// + private static void HandleArchiveFilesMultipleContextSingleTargetsTest( + int maxArchiveFilesConfig, int expectedArchiveFiles, string dateFormat, bool changeCreationAndWriteTime) + { + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string archivePath = Path.Combine(tempDir, "archive"); + var archiveDir = new DirectoryInfo(archivePath); + try + { + archiveDir.Create(); + var numberFilesCreatedPerTargetArchive = 30; + + // use same config vars for mock files, as for nlog config + var fileExt = ".log"; + var app1Nm = "App1"; + var app2Nm = "App2"; + + var now = DateTime.Now; + var i = 0; + // create mock app1 archives (matches app1 config for target) + foreach (string filePath in ArchiveFileNamesGenerator(archivePath, dateFormat, app1Nm + fileExt).Take(numberFilesCreatedPerTargetArchive)) + { + File.WriteAllLines(filePath, new[] { "test archive ", "=====", filePath }); + var time = now.AddDays(i); + if (changeCreationAndWriteTime) + { + File.SetCreationTime(filePath, time); + File.SetLastWriteTime(filePath, time); + } + i--; + } + i = 0; + // create mock app2 archives (matches app2 config for target) + foreach (string filePath in ArchiveFileNamesGenerator(archivePath, dateFormat, app2Nm + fileExt).Take(numberFilesCreatedPerTargetArchive)) + { + File.WriteAllLines(filePath, new[] { "test archive ", "=====", filePath }); + var time = now.AddDays(i); + if (changeCreationAndWriteTime) + { + File.SetCreationTime(filePath, time); + File.SetLastWriteTime(filePath, time); + } + i--; + } + + // Create same app1 file as config defines. Will force archiving to happen on startup + File.WriteAllLines(Path.Combine(tempDir, app1Nm + fileExt), new[] { "Write first app debug target. Startup will archive this file" }, Encoding.ASCII); + + var app1Config = XmlLoggingConfiguration.CreateFromXmlString(@" + + + ; + + + + "); + + var app2Config = XmlLoggingConfiguration.CreateFromXmlString(@" + + + ; + + + + "); + + LogManager.Configuration = app1Config; + + var logger = LogManager.GetCurrentClassLogger(); + // Trigger archive to happen on startup + logger.Debug("Test 1 - Write to the log file that already exists; trigger archive to happen because archiveOldFileOnStartup='true'"); + + // verify file counts. EXPECTED OUTCOME: + // app1: Removed extra archives + // app2: Has all extra archives + var app1TargetFileCnt = archiveDir.GetFiles("*" + app1Nm + "*").Length; + var app2FileTargetCnt = archiveDir.GetFiles("*" + app2Nm + "*").Length; + + Assert.Equal(numberFilesCreatedPerTargetArchive, app2FileTargetCnt); + Assert.Equal(expectedArchiveFiles, app1TargetFileCnt); + } + finally + { + //cleanup + LogManager.Configuration = null; + archiveDir.Delete(true); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + /// + /// Generate unlimited archivefiles names. Don't use toList on this ;) + /// + /// + /// + /// fileext with . + /// + private static IEnumerable ArchiveFileNamesGenerator(string path, string dateFormat, string fileExt) + { + //yyyyMMdd-HHmm + int dateOffset = 1; + var now = DateTime.Now; + while (true) + { + dateOffset--; + yield return Path.Combine(path, now.AddDays(dateOffset).ToString(dateFormat) + fileExt); + } + } + + [Fact] + public void RelativeFileNaming_ShouldSuccess() + { + var relativeFileName = @"Logs\myapp.log"; + var fullFilePath = Path.GetFullPath(relativeFileName); + try + { + var fileTarget = new FileTarget + { + FileName = fullFilePath, + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}", + OpenFileCacheTimeout = 0 + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + LogManager.Configuration = null; + AssertFileContents(fullFilePath, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); + } + finally + { + if (File.Exists(fullFilePath)) + File.Delete(fullFilePath); + } + } + + [Fact] + public void RelativeFileNaming_DirectoryNavigation_ShouldSuccess() + { + var relativeFileName = @"..\..\Logs\myapp.log"; + var fullFilePath = Path.GetFullPath(relativeFileName); + try + { + var fileTarget = new FileTarget + { + FileName = fullFilePath, + LineEnding = LineEndingMode.LF, + Layout = "${level} ${message}", + OpenFileCacheTimeout = 0 + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaa"); + logger.Info("bbb"); + logger.Warn("ccc"); + LogManager.Configuration = null; + AssertFileContents(fullFilePath, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); + } + finally + { + if (File.Exists(fullFilePath)) + File.Delete(fullFilePath); + } + } + + [Fact] + public void RelativeSequentialArchiveTest_MaxArchiveFiles_0() + { + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logfile = Path.Combine(tempDir, "file.txt"); + try + { + string archiveFolder = Path.Combine(tempDir, "archive"); + var fileTarget = new FileTarget + { + FileName = logfile, + ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveAboveSize = 100, + ArchiveOldFileOnStartup = true, // Verify ArchiveOldFileOnStartup works together with ArchiveAboveSize + LineEnding = LineEndingMode.LF, + ArchiveNumbering = ArchiveNumberingMode.Sequence, + Layout = "${message}", + MaxArchiveFiles = 0 + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + logfile = Path.GetFullPath(logfile); + // we emit 5 * 25 *(3 x aaa + \n) bytes + // so that we should get a full file + 4 archives + Generate100BytesLog('a'); + Generate100BytesLog('b'); + Generate100BytesLog('c'); + Generate100BytesLog('d'); + Generate100BytesLog('e'); + + LogManager.Configuration = null; + + var times = 25; + AssertFileContents(logfile, + StringRepeat(times, "eee\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(archiveFolder, "0000.txt"), + StringRepeat(times, "aaa\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(archiveFolder, "0001.txt"), + StringRepeat(times, "bbb\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(archiveFolder, "0002.txt"), + StringRepeat(times, "ccc\n"), + Encoding.UTF8); + + AssertFileContents( + Path.Combine(archiveFolder, "0003.txt"), + StringRepeat(times, "ddd\n"), + Encoding.UTF8); + + Assert.False(File.Exists(Path.Combine(archiveFolder, "0004.txt"))); + } + finally + { + if (File.Exists(logfile)) + File.Delete(logfile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void TestFilenameCleanup() + { + var invalidChars = Path.GetInvalidFileNameChars(); + var invalidFileName = Path.DirectorySeparatorChar.ToString(); + var expectedFileName = ""; + for (int i = 0; i < invalidChars.Length; i++) + { + var invalidChar = invalidChars[i]; + if (invalidChar == Path.DirectorySeparatorChar || invalidChar == Path.AltDirectorySeparatorChar) + { + //ignore, won't used in cleanup (but for find filename in path) + continue; + } + + invalidFileName += i + invalidChar.ToString(); + //underscore is used for clean + expectedFileName += i + "_"; + } + //under mono this the invalid chars is sometimes only 1 char (so min width 2) + Assert.True(invalidFileName.Length >= 2); + //CleanupFileName is default true; + var fileTarget = new FileTarget(); + fileTarget.FileName = invalidFileName; + + var filePathLayout = new NLog.Internal.FilePathLayout(invalidFileName, true, FilePathKind.Absolute); + + + var path = filePathLayout.Render(LogEventInfo.CreateNullEvent()); + Assert.Equal(expectedFileName, path); + } + + [Theory] + [InlineData(DayOfWeek.Sunday, "2017-03-02 15:27:34.651", "2017-03-05 15:27:34.651")] // On a Thursday, finding next Sunday + [InlineData(DayOfWeek.Thursday, "2017-03-02 15:27:34.651", "2017-03-09 15:27:34.651")] // On a Thursday, finding next Thursday + [InlineData(DayOfWeek.Monday, "2017-03-02 00:00:00.000", "2017-03-06 00:00:00.000")] // On Thursday at Midnight, finding next Monday + public void TestCalculateNextWeekday(DayOfWeek day, string todayString, string expectedString) + { + DateTime today = DateTime.Parse(todayString); + DateTime expected = DateTime.Parse(expectedString); + DateTime actual = FileTarget.CalculateNextWeekday(today, day); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData("UTF-16", true)] + [InlineData("UTF-16BE", true)] + [InlineData("UTF-32", true)] + [InlineData("UTF-32BE", true)] +#if !NET6_0_OR_GREATER + [InlineData("UTF-7", false)] +#endif + [InlineData("UTF-8", false)] + [InlineData("ASCII", false)] + public void TestInitialBomValue(string encodingName, bool expected) + { + var fileTarget = new FileTarget(); + + // Act + fileTarget.Encoding = Encoding.GetEncoding(encodingName); + + // Assert + Assert.Equal(expected, fileTarget.WriteBom); + } + + [Fact] + public void BatchErrorHandlingTest() + { + using (new NoThrowNLogExceptions()) + { + var fileTarget = new FileTarget { FileName = "${logger}", Layout = "${message}", ArchiveAboveSize = 10, DiscardAll = true }; + fileTarget.Initialize(null); + + // make sure that when file names get sorted, the asynchronous continuations are sorted with them as well + var exceptions = new List(); + var events = new[] + { + new LogEventInfo(LogLevel.Info, "file99.txt", "msg1").WithContinuation(exceptions.Add), + new LogEventInfo(LogLevel.Info, "", "msg2").WithContinuation(exceptions.Add), + new LogEventInfo(LogLevel.Info, "", "msg3").WithContinuation(exceptions.Add), + new LogEventInfo(LogLevel.Info, "", "msg4").WithContinuation(exceptions.Add), + new LogEventInfo(LogLevel.Info, "file99.txt", "msg5").WithContinuation(exceptions.Add), + }; + + fileTarget.WriteAsyncLogEvents(events); + LogManager.Flush(); + + Assert.Equal(5, exceptions.Count); + Assert.Null(exceptions[0]); + Assert.Null(exceptions[1]); // Will be written together + Assert.NotNull(exceptions[2]); + Assert.NotNull(exceptions[3]); + Assert.NotNull(exceptions[4]); + } + } + + [Fact] + public void BatchBufferOverflowTest() + { + string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logfile = Path.Combine(tempDir, "file.txt"); + try + { + // Arrange + var fileTarget = new FileTarget + { + FileName = logfile, + BufferSize = 5, + LineEnding = LineEndingMode.LF, + Layout = "${message}", + Encoding = Encoding.UTF8, + }; + fileTarget.Initialize(null); + + var result = new List(); + var events = new List(); + var times = 200; + for (int i = 1; i <= times; ++i) + { + int counter = i; + events.Add(new LogEventInfo(LogLevel.Info, "logger", counter.ToString()).WithContinuation(ex => result.Add(ex is null ? counter : -1))); + } + + // Act + fileTarget.WriteAsyncLogEvents(events); + fileTarget.Close(); + + // Assert + Assert.Equal(Enumerable.Range(1, times).ToList(), result); + AssertFileContents(logfile, string.Join("\n", result.ToArray()) + "\n", Encoding.UTF8); + } + finally + { + if (File.Exists(logfile)) + File.Delete(logfile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void HandleArchiveFileAlreadyExistsTest_noBom() + { + //NO bom + var utf8nobom = new UTF8Encoding(false); + + HandleArchiveFileAlreadyExistsTest(utf8nobom, false); + } + + [Fact] + public void HandleArchiveFileAlreadyExistsTest_withBom() + { + // bom + var utf8nobom = new UTF8Encoding(true); + + HandleArchiveFileAlreadyExistsTest(utf8nobom, true); + } + + [Fact] + public void HandleArchiveFileAlreadyExistsTest_ascii() + { + //NO bom + var encoding = Encoding.ASCII; + + HandleArchiveFileAlreadyExistsTest(encoding, false); + } + + private static void HandleArchiveFileAlreadyExistsTest(Encoding encoding, bool hasBom) + { + var tempDir = Path.Combine(Path.GetTempPath(), "HandleArchiveFileAlreadyExistsTest-" + Guid.NewGuid()); + string logFile = Path.Combine(tempDir, "log.txt"); + try + { + // set log file access times the same way as when this issue comes up. + Directory.CreateDirectory(tempDir); + + File.WriteAllText(logFile, "some content" + Environment.NewLine, encoding); + var oldTime = DateTime.Now.AddDays(-2); + File.SetCreationTime(logFile, oldTime); + File.SetLastWriteTime(logFile, oldTime); + File.SetLastAccessTime(logFile, oldTime); + + //write to archive directly + var archiveDateFormat = "yyyy-MM-dd"; + var archiveFileNamePattern = Path.Combine(tempDir, "log-{#}.txt"); + var archiveFileName = archiveFileNamePattern.Replace("{#}", oldTime.ToString(archiveDateFormat)); + File.WriteAllText(archiveFileName, "message already in archive" + Environment.NewLine, encoding); + + LogManager.ThrowExceptions = true; + + // configure nlog + var fileTarget = new FileTarget("file") + { + FileName = logFile, + ArchiveEvery = FileArchivePeriod.Day, + ArchiveFileName = archiveFileNamePattern, + ArchiveNumbering = ArchiveNumberingMode.Date, + ArchiveDateFormat = archiveDateFormat, + Encoding = encoding, + Layout = "${message}", + WriteBom = hasBom, + }; + + var config = new LoggingConfiguration(); + config.AddTarget(fileTarget); + config.AddRuleForAllLevels(fileTarget); + + LogManager.Configuration = config; + + var logger = LogManager.GetLogger("HandleArchiveFileAlreadyExistsTest"); + // write, this should append. + logger.Info("log to force archiving"); + logger.Info("log to same file"); + + LogManager.Configuration = null; // Flush + + AssertFileContents(archiveFileName, "message already in archive" + Environment.NewLine + "some content" + Environment.NewLine, encoding, hasBom); + AssertFileContents(logFile, "log to force archiving" + Environment.NewLine + "log to same file" + Environment.NewLine, encoding, hasBom); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void DontCrashWhenDateAndSequenceDoesntMatchFiles() + { + var tempDir = Path.Combine(Path.GetTempPath(), "DontCrashWhenDateAndSequenceDoesntMatchFiles-" + Guid.NewGuid()); + string logFile = Path.Combine(tempDir, "log.txt"); + try + { + // set log file access times the same way as when this issue comes up. + Directory.CreateDirectory(tempDir); + + File.WriteAllText(logFile, "some content" + Environment.NewLine); + var oldTime = DateTime.Now.AddDays(-2); + File.SetCreationTime(logFile, oldTime); + File.SetLastWriteTime(logFile, oldTime); + File.SetLastAccessTime(logFile, oldTime); + + //write to archive directly + var archiveDateFormat = "yyyyMMdd"; + var archiveFileNamePattern = Path.Combine(tempDir, "log-{#}.txt"); + var archiveFileName = archiveFileNamePattern.Replace("{#}", oldTime.ToString(archiveDateFormat)); + File.WriteAllText(archiveFileName, "some archive content"); + + LogManager.ThrowExceptions = true; + + // configure nlog + var fileTarget = new FileTarget("file") + { + FileName = logFile, + ArchiveEvery = FileArchivePeriod.Day, + ArchiveFileName = "log-{#}.txt", + ArchiveNumbering = ArchiveNumberingMode.DateAndSequence, + ArchiveAboveSize = 50000, + MaxArchiveFiles = 7 + }; + + var config = new LoggingConfiguration(); + config.AddRuleForAllLevels(fileTarget); + LogManager.Configuration = config; + + // write + var logger = LogManager.GetLogger("DontCrashWhenDateAndSequenceDoesntMatchFiles"); + logger.Info("Log message"); + + LogManager.Configuration = null; + + Assert.True(File.Exists(logFile)); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Theory] + [InlineData(true, 100, true)] // archive, as size of file is 101 + [InlineData(true, 101, false)] //equals is not above + [InlineData(true, 102, false)] // don;t archive, we didn't reach the aboveSize + [InlineData(false, 100, false)] + [InlineData(null, 0, false)] + [InlineData(null, 99, true)] + [InlineData(null, 100, true)] + [InlineData(null, 101, false)] + public void ShouldArchiveOldFileOnStartupTest(bool? archiveOldFileOnStartup, long archiveOldFileOnStartupAboveSize, bool expected) + { + // Arrange + var fileAppenderCacheMock = Substitute.For(); + + var filePath = "x:/somewhere/file.txt"; + fileAppenderCacheMock.GetFileLength(filePath).Returns(101); + + var target = new FileTarget(fileAppenderCacheMock) + { + ArchiveOldFileOnStartupAboveSize = archiveOldFileOnStartupAboveSize + }; + if (archiveOldFileOnStartup.HasValue) + { + target.ArchiveOldFileOnStartup = archiveOldFileOnStartup.Value; + } + + // Act + var result = target.ShouldArchiveOldFileOnStartup(filePath); + + // Assert + Assert.Equal(expected, result); + } + + [Fact] + public void ShouldNotArchiveWhenMeetingOldLogEventTimestamps() + { + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); + try + { + string archiveFolder = Path.Combine(tempDir, "archive"); + var fileTarget = new FileTarget + { + FileName = logFile, + ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveEvery = FileArchivePeriod.Day, + LineEnding = LineEndingMode.LF, + ArchiveNumbering = ArchiveNumberingMode.Sequence, + Layout = "${message}", + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + var logger = LogManager.GetLogger(nameof(ShouldNotArchiveWhenMeetingOldLogEventTimestamps)); + logger.Info("123"); + logger.Info("456"); + logger.Log(new LogEventInfo(LogLevel.Info, null, "789") { TimeStamp = NLog.Time.TimeSource.Current.Time.AddDays(-2) }); + logger.Log(new LogEventInfo(LogLevel.Info, null, "123") { TimeStamp = NLog.Time.TimeSource.Current.Time.AddDays(-2) }); + logger.Info("456"); + logger.Log(new LogEventInfo(LogLevel.Info, null, "123") { TimeStamp = NLog.Time.TimeSource.Current.Time.AddDays(1) }); + LogManager.Configuration = null; // Flush + + AssertFileContents(logFile, + "123\n", + Encoding.UTF8); + + AssertFileContents( + Path.Combine(archiveFolder, "0000.txt"), + "123\n456\n789\n123\n456\n", + Encoding.UTF8); + + Assert.False(File.Exists(Path.Combine(archiveFolder, "0001.txt"))); + } + finally + { + if (File.Exists(logFile)) + File.Delete(logFile); + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + } +} From 968447337863cd5fe86e407f9c6463409434c519 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 29 Jan 2025 22:03:56 +0100 Subject: [PATCH 035/224] FileTarget without ConcurrentWrites and Mutex and FileSystemWatcher (#5673) --- build.ps1 | 1 + .../File/Archive1/NLog.config | 2 - .../File/Archive2/NLog.config | 2 - .../File/Archive3/NLog.config | 2 - .../File/Archive4/NLog.config | 2 - .../ArchiveNumberingMode.cs | 0 .../ConcurrentFileTarget.cs | 184 +- .../FileAppenders/BaseFileAppender.cs | 6 +- .../FileAppenders/BaseMutexFileAppender.cs | 3 +- .../CountingSingleProcessFileAppender.cs | 0 .../FileAppenders/FileAppenderCache.cs | 2 +- .../FileAppenders/ICreateFileParameters.cs | 2 +- .../FileAppenders/IFileAppenderCache.cs | 0 .../FileAppenders/IFileAppenderFactory.cs | 0 .../MutexMultiProcessFileAppender.cs | 0 .../FileAppenders/NullAppender.cs | 0 .../RetryingMultiProcessFileAppender.cs | 0 .../SingleProcessFileAppender.cs | 0 .../UnixMultiProcessFileAppender.cs | 0 .../WindowsMultiProcessFileAppender.cs | 0 .../FileArchiveEveryPeriod.cs | 108 + .../FileArchiveModes/FileArchiveModeBase.cs | 0 .../FileArchiveModes/FileArchiveModeDate.cs | 4 +- .../FileArchiveModeDateAndSequence.cs | 4 +- .../FileArchiveModeDynamicSequence.cs | 8 +- .../FileArchiveModeDynamicTemplate.cs | 0 .../FileArchiveModeFactory.cs | 0 .../FileArchiveModeRolling.cs | 2 +- .../FileArchiveModeSequence.cs | 4 +- .../FileArchiveModes}/IFileArchiveMode.cs | 0 .../FilePathKind.cs | 0 .../IArchiveFileCompressor.cs | 4 +- .../IFileCompressor.cs | 4 +- .../Internal}/DateAndSequenceArchive.cs | 5 +- .../ExcludeFromCodeCoverageAttribute.cs | 51 + .../Internal/FileInfoExt.cs | 26 +- .../Internal/FilePathLayout.cs | 22 +- .../Internal/MultiFileWatcher.cs | 6 +- .../Internal/MutexDetector.cs | 4 - .../Internal/PlatformDetector.cs | 89 + .../Internal/PlatformSystem.cs | 70 + .../Internal/ReusableBufferCreator.cs | 46 + .../Internal/ReusableBuilderCreator.cs | 61 + .../Internal/ReusableObjectCreator.cs | 92 + .../Internal/ReusableStreamCreator.cs | 79 + .../Internal/SortHelpers.cs | 389 +++ .../Internal/StreamHelpers.cs | 48 +- .../Internal/StringHelpers.cs | 73 + .../Internal/Win32FileNativeMethods.cs | 0 .../Internal}/ZipArchiveFileCompressor.cs | 4 +- .../NLog.Targets.ConcurrentFile.csproj | 93 + .../Properties/AssemblyInfo.cs | 49 + src/NLog.Targets.ConcurrentFile/README.md | 25 + .../Win32FileAttributes.cs | 0 src/NLog.sln | 16 + .../Config/LoggingConfigurationFileLoader.cs | 3 +- src/NLog/Internal/FileInfoHelper.cs | 34 +- src/NLog/Internal/StringHelpers.cs | 2 +- src/NLog/NLog.csproj | 2 - src/NLog/SetupLoadConfigurationExtensions.cs | 4 +- .../FileAppenders/DiscardAllFileAppender.cs | 75 + .../ExclusiveFileLockingAppender.cs | 205 ++ .../Targets/FileAppenders/IFileAppender.cs | 62 + .../MinimalFileLockingAppender.cs | 144 + .../BaseFileArchiveHandler.cs | 378 +++ .../DisabledFileArchiveHandler.cs | 47 + .../IFileArchiveHandler.cs | 53 + .../LegacyArchiveFileNameHandler.cs | 272 ++ .../RollingArchiveFileHandler.cs | 143 + .../ZeroFileArchiveHandler.cs | 94 + src/NLog/Targets/FileArchivePeriod.cs | 26 +- src/NLog/Targets/FileTarget.cs | 2437 ++++------------- .../TargetWithLayoutHeaderAndFooter.cs | 4 +- .../ConcurrentFileTargetTests.cs | 651 +++-- .../ConcurrentWritesMultiProcessTests.cs} | 62 +- .../FileAppenderCacheTests.cs | 69 +- .../FilePathLayoutTests.cs | 0 .../NLog.Targets.ConcurrentFile.Tests.csproj | 41 + .../NLogTests.snk | Bin 0 -> 596 bytes .../ProcessRunner.cs | 0 .../Properties/AssemblyInfo.cs | 11 +- .../Config/TargetConfigurationTests.cs | 1 - tests/NLog.UnitTests/Config/VariableTests.cs | 6 +- .../Internal/FileInfoHelperTests.cs | 79 + tests/NLog.UnitTests/NLogTestBase.cs | 113 - .../NLog.UnitTests/Targets/FileTargetTests.cs | 1154 ++++---- 86 files changed, 4711 insertions(+), 3053 deletions(-) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile}/ArchiveNumberingMode.cs (100%) rename src/{NLog/Internal => NLog.Targets.ConcurrentFile}/FileAppenders/BaseFileAppender.cs (98%) rename src/{NLog/Internal => NLog.Targets.ConcurrentFile}/FileAppenders/BaseMutexFileAppender.cs (99%) rename src/{NLog/Internal => NLog.Targets.ConcurrentFile}/FileAppenders/CountingSingleProcessFileAppender.cs (100%) rename src/{NLog/Internal => NLog.Targets.ConcurrentFile}/FileAppenders/FileAppenderCache.cs (99%) rename src/{NLog/Internal => NLog.Targets.ConcurrentFile}/FileAppenders/ICreateFileParameters.cs (99%) rename src/{NLog/Internal => NLog.Targets.ConcurrentFile}/FileAppenders/IFileAppenderCache.cs (100%) rename src/{NLog/Internal => NLog.Targets.ConcurrentFile}/FileAppenders/IFileAppenderFactory.cs (100%) rename src/{NLog/Internal => NLog.Targets.ConcurrentFile}/FileAppenders/MutexMultiProcessFileAppender.cs (100%) rename src/{NLog/Internal => NLog.Targets.ConcurrentFile}/FileAppenders/NullAppender.cs (100%) rename src/{NLog/Internal => NLog.Targets.ConcurrentFile}/FileAppenders/RetryingMultiProcessFileAppender.cs (100%) rename src/{NLog/Internal => NLog.Targets.ConcurrentFile}/FileAppenders/SingleProcessFileAppender.cs (100%) rename src/{NLog/Internal => NLog.Targets.ConcurrentFile}/FileAppenders/UnixMultiProcessFileAppender.cs (100%) rename src/{NLog/Internal => NLog.Targets.ConcurrentFile}/FileAppenders/WindowsMultiProcessFileAppender.cs (100%) create mode 100644 src/NLog.Targets.ConcurrentFile/FileArchiveEveryPeriod.cs rename src/{NLog/Targets => NLog.Targets.ConcurrentFile}/FileArchiveModes/FileArchiveModeBase.cs (100%) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile}/FileArchiveModes/FileArchiveModeDate.cs (94%) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile}/FileArchiveModes/FileArchiveModeDateAndSequence.cs (96%) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile}/FileArchiveModes/FileArchiveModeDynamicSequence.cs (97%) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile}/FileArchiveModes/FileArchiveModeDynamicTemplate.cs (100%) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile}/FileArchiveModes/FileArchiveModeFactory.cs (100%) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile}/FileArchiveModes/FileArchiveModeRolling.cs (98%) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile}/FileArchiveModes/FileArchiveModeSequence.cs (94%) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile/FileArchiveModes}/IFileArchiveMode.cs (100%) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile}/FilePathKind.cs (100%) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile}/IArchiveFileCompressor.cs (91%) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile}/IFileCompressor.cs (91%) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile/Internal}/DateAndSequenceArchive.cs (94%) create mode 100644 src/NLog.Targets.ConcurrentFile/Internal/ExcludeFromCodeCoverageAttribute.cs rename src/{NLog => NLog.Targets.ConcurrentFile}/Internal/FileInfoExt.cs (67%) rename src/{NLog => NLog.Targets.ConcurrentFile}/Internal/FilePathLayout.cs (92%) rename src/{NLog => NLog.Targets.ConcurrentFile}/Internal/MultiFileWatcher.cs (98%) rename src/{NLog => NLog.Targets.ConcurrentFile}/Internal/MutexDetector.cs (93%) create mode 100644 src/NLog.Targets.ConcurrentFile/Internal/PlatformDetector.cs create mode 100644 src/NLog.Targets.ConcurrentFile/Internal/PlatformSystem.cs create mode 100644 src/NLog.Targets.ConcurrentFile/Internal/ReusableBufferCreator.cs create mode 100644 src/NLog.Targets.ConcurrentFile/Internal/ReusableBuilderCreator.cs create mode 100644 src/NLog.Targets.ConcurrentFile/Internal/ReusableObjectCreator.cs create mode 100644 src/NLog.Targets.ConcurrentFile/Internal/ReusableStreamCreator.cs create mode 100644 src/NLog.Targets.ConcurrentFile/Internal/SortHelpers.cs rename src/{NLog => NLog.Targets.ConcurrentFile}/Internal/StreamHelpers.cs (70%) create mode 100644 src/NLog.Targets.ConcurrentFile/Internal/StringHelpers.cs rename src/{NLog => NLog.Targets.ConcurrentFile}/Internal/Win32FileNativeMethods.cs (100%) rename src/{NLog/Targets => NLog.Targets.ConcurrentFile/Internal}/ZipArchiveFileCompressor.cs (93%) create mode 100644 src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj create mode 100644 src/NLog.Targets.ConcurrentFile/Properties/AssemblyInfo.cs create mode 100644 src/NLog.Targets.ConcurrentFile/README.md rename src/{NLog/Targets => NLog.Targets.ConcurrentFile}/Win32FileAttributes.cs (100%) create mode 100644 src/NLog/Targets/FileAppenders/DiscardAllFileAppender.cs create mode 100644 src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs create mode 100644 src/NLog/Targets/FileAppenders/IFileAppender.cs create mode 100644 src/NLog/Targets/FileAppenders/MinimalFileLockingAppender.cs create mode 100644 src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs create mode 100644 src/NLog/Targets/FileArchiveHandlers/DisabledFileArchiveHandler.cs create mode 100644 src/NLog/Targets/FileArchiveHandlers/IFileArchiveHandler.cs create mode 100644 src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs create mode 100644 src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs create mode 100644 src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs rename tests/{NLog.UnitTests/Targets/ConcurrentFileTargetTests.cs => NLog.Targets.ConcurrentFile.Tests/ConcurrentWritesMultiProcessTests.cs} (89%) rename tests/{NLog.UnitTests/Internal/FileAppenders => NLog.Targets.ConcurrentFile.Tests}/FileAppenderCacheTests.cs (81%) rename tests/{NLog.UnitTests/Internal => NLog.Targets.ConcurrentFile.Tests}/FilePathLayoutTests.cs (100%) create mode 100644 tests/NLog.Targets.ConcurrentFile.Tests/NLog.Targets.ConcurrentFile.Tests.csproj create mode 100644 tests/NLog.Targets.ConcurrentFile.Tests/NLogTests.snk rename tests/{NLog.UnitTests => NLog.Targets.ConcurrentFile.Tests}/ProcessRunner.cs (100%) rename src/NLog/Internal/EncodingHelpers.cs => tests/NLog.Targets.ConcurrentFile.Tests/Properties/AssemblyInfo.cs (87%) create mode 100644 tests/NLog.UnitTests/Internal/FileInfoHelperTests.cs diff --git a/build.ps1 b/build.ps1 index f9b3787026..0fd5a45d97 100644 --- a/build.ps1 +++ b/build.ps1 @@ -37,6 +37,7 @@ function create-package($packageName, $targetFrameworks) create-package 'NLog.AutoReloadConfig' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Database' '"net35;net45;net46;netstandard2.0"' +create-package 'NLog.Targets.ConcurrentFile' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.Mail' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.Network' '"net45;net46;netstandard2.0"' create-package 'NLog.Targets.WebService' '"net45;net46;netstandard2.0"' diff --git a/examples/targets/Configuration File/File/Archive1/NLog.config b/examples/targets/Configuration File/File/Archive1/NLog.config index f1988b5bb7..a7aed6451e 100644 --- a/examples/targets/Configuration File/File/Archive1/NLog.config +++ b/examples/targets/Configuration File/File/Archive1/NLog.config @@ -8,8 +8,6 @@ fileName="${basedir}/logs/logfile.txt" archiveFileName="${basedir}/archives/log.{#####}.txt" archiveAboveSize="10240" - archiveNumbering="Sequence" - concurrentWrites="true" keepFileOpen="false" encoding="iso-8859-2" /> diff --git a/examples/targets/Configuration File/File/Archive2/NLog.config b/examples/targets/Configuration File/File/Archive2/NLog.config index 60aafb784f..cf4118716e 100644 --- a/examples/targets/Configuration File/File/Archive2/NLog.config +++ b/examples/targets/Configuration File/File/Archive2/NLog.config @@ -8,8 +8,6 @@ fileName="${basedir}/logs/logfile.txt" archiveFileName="${basedir}/archives/log.{#####}.txt" archiveEvery="Minute" - archiveNumbering="Rolling" - concurrentWrites="true" keepFileOpen="false" encoding="iso-8859-2" /> diff --git a/examples/targets/Configuration File/File/Archive3/NLog.config b/examples/targets/Configuration File/File/Archive3/NLog.config index edd9961e67..8518e2757e 100644 --- a/examples/targets/Configuration File/File/Archive3/NLog.config +++ b/examples/targets/Configuration File/File/Archive3/NLog.config @@ -9,9 +9,7 @@ archiveFileName="${basedir}/archives/log.{#####}.txt" archiveEvery="Minute" archiveAboveSize="10000" - archiveNumbering="Rolling" maxArchiveFiles="3" - concurrentWrites="true" keepFileOpen="false" encoding="iso-8859-2" /> diff --git a/examples/targets/Configuration File/File/Archive4/NLog.config b/examples/targets/Configuration File/File/Archive4/NLog.config index d23b3e83fb..74a8663d2e 100644 --- a/examples/targets/Configuration File/File/Archive4/NLog.config +++ b/examples/targets/Configuration File/File/Archive4/NLog.config @@ -9,9 +9,7 @@ archiveFileName="${basedir}/archives/${level}/log.{#####}.txt" archiveEvery="Minute" archiveAboveSize="10000" - archiveNumbering="Rolling" maxArchiveFiles="3" - concurrentWrites="true" keepFileOpen="false" encoding="iso-8859-2" /> diff --git a/src/NLog/Targets/ArchiveNumberingMode.cs b/src/NLog.Targets.ConcurrentFile/ArchiveNumberingMode.cs similarity index 100% rename from src/NLog/Targets/ArchiveNumberingMode.cs rename to src/NLog.Targets.ConcurrentFile/ArchiveNumberingMode.cs diff --git a/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs b/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs index 3878cc1c98..04bcc18a55 100644 --- a/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs +++ b/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs @@ -56,7 +56,7 @@ namespace NLog.Targets /// /// Documentation on NLog Wiki [Target("File")] - public class FileTarget : TargetWithLayoutHeaderAndFooter, ICreateFileParameters + public class ConcurrentFileTarget : TargetWithLayoutHeaderAndFooter, ICreateFileParameters { /// /// Default clean up period of the initialized files. When a file exceeds the clean up period is removed from the list. @@ -70,13 +70,13 @@ public class FileTarget : TargetWithLayoutHeaderAndFooter, ICreateFileParameters private const long ArchiveAboveSizeDisabled = -1L; /// - /// Holds the initialized files each given time by the instance. Against each file, the last write time is stored. + /// Holds the initialized files each given time by the instance. Against each file, the last write time is stored. /// /// Last write time is store in local time (no UTC). private readonly Dictionary _initializedFiles = new Dictionary(StringComparer.OrdinalIgnoreCase); /// - /// List of the associated file appenders with the instance. + /// List of the associated file appenders with the instance. /// private IFileAppenderCache _fileAppenderCache; @@ -113,7 +113,7 @@ IFileArchiveMode GetFileArchiveHelper(string archiveFilePattern) /// private FilePathLayout _fullArchiveFileName; - private FileArchivePeriod _archiveEvery; + private FileArchiveEveryPeriod _archiveEvery; private long _archiveAboveSize; private bool _enableArchiveFileCompression; @@ -134,22 +134,22 @@ IFileArchiveMode GetFileArchiveHelper(string archiveFilePattern) private FilePathKind _archiveFileKind; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// - public FileTarget() : this(FileAppenderCache.Empty) + public ConcurrentFileTarget() : this(FileAppenderCache.Empty) { } - internal FileTarget(IFileAppenderCache fileAppenderCache) + internal ConcurrentFileTarget(IFileAppenderCache fileAppenderCache) { ArchiveNumbering = ArchiveNumberingMode.Sequence; _maxArchiveFiles = 0; _maxArchiveDays = 0; - ArchiveEvery = FileArchivePeriod.None; + ArchiveEvery = FileArchiveEveryPeriod.None; ArchiveAboveSize = ArchiveAboveSizeDisabled; _cleanupFileName = true; @@ -157,20 +157,20 @@ internal FileTarget(IFileAppenderCache fileAppenderCache) } #if !NET35 && !NET40 - static FileTarget() + static ConcurrentFileTarget() { FileCompressor = new ZipArchiveFileCompressor(); } #endif /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// /// Name of the target. - public FileTarget(string name) : this() + public ConcurrentFileTarget(string name) : this() { Name = name; } @@ -561,7 +561,7 @@ public long ArchiveAboveSize /// on or after 11:00 will trigger the archiving. /// /// - public FileArchivePeriod ArchiveEvery + public FileArchiveEveryPeriod ArchiveEvery { get => _archiveEvery; set @@ -761,9 +761,6 @@ public void CleanupInitializedFiles() } catch (Exception exception) { - if (exception.MustBeRethrownImmediately()) - throw; - InternalLogger.Error(exception, "{0}: Exception in CleanupInitializedFiles", this); } } @@ -826,7 +823,7 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation) } catch (Exception exception) { - if (ExceptionMustBeRethrown(exception)) + if (LogManager.ThrowExceptions) { throw; } @@ -837,9 +834,9 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation) /// /// Returns the suitable appender factory ( ) to be used to generate the file - /// appenders associated with the instance. + /// appenders associated with the instance. /// - /// The type of the file appender factory returned depends on the values of various properties. + /// The type of the file appender factory returned depends on the values of various properties. /// /// suitable for this instance. private IFileAppenderFactory GetFileAppenderFactory() @@ -884,7 +881,7 @@ private IFileAppenderFactory GetFileAppenderFactory() return SingleProcessFileAppender.TheFactory; } - private bool IsArchivingEnabled => ArchiveAboveSize != ArchiveAboveSizeDisabled || ArchiveEvery != FileArchivePeriod.None; + private bool IsArchivingEnabled => ArchiveAboveSize != ArchiveAboveSizeDisabled || ArchiveEvery != FileArchiveEveryPeriod.None; private bool IsSimpleKeepFileOpen => KeepFileOpen && !ConcurrentWrites && !ReplaceFileContentsOnEachWrite; @@ -943,7 +940,8 @@ protected override void CloseTarget() { InternalLogger.Trace("{0}: Stop autoClosingTimer", this); _autoClosingTimer = null; - currentTimer.WaitForDispose(TimeSpan.Zero); + currentTimer.Change(Timeout.Infinite, Timeout.Infinite); + currentTimer.Dispose(); } _fileAppenderCache.CloseAppenders("Dispose"); @@ -960,6 +958,7 @@ private void ResetFileAppenders(string reason) } } + private readonly ReusableBuilderCreator _reusableLayoutBuilder = new ReusableBuilderCreator(); private readonly ReusableStreamCreator _reusableFileWriteStream = new ReusableStreamCreator(); private readonly ReusableStreamCreator _reusableBatchFileWriteStream = new ReusableStreamCreator(true); private readonly ReusableBufferCreator _reusableEncodingBuffer = new ReusableBufferCreator(1024); @@ -979,7 +978,7 @@ protected override void Write(LogEventInfo logEvent) using (var targetStream = _reusableBatchFileWriteStream.Allocate()) { - using (var targetBuilder = ReusableLayoutBuilder.Allocate()) + using (var targetBuilder = _reusableLayoutBuilder.Allocate()) using (var targetBuffer = _reusableEncodingBuffer.Allocate()) { RenderFormattedMessageToStream(logEvent, targetBuilder.Result, targetBuffer.Result, targetStream.Result); @@ -1002,7 +1001,7 @@ internal string GetFullFileName(LogEventInfo logEvent) if (_fullFileName.IsFixedFilePath) return _fullFileName.Render(logEvent); - using (var targetBuilder = ReusableLayoutBuilder.Allocate()) + using (var targetBuilder = _reusableLayoutBuilder.Allocate()) { return _fullFileName.RenderWithBuilder(logEvent, targetBuilder.Result); } @@ -1071,7 +1070,7 @@ private int WriteToMemoryStream(IList logEvents, int startInd long maxBufferSize = BufferSize * 100; // Max Buffer Default = 30 KiloByte * 100 = 3 MegaByte using (var targetStream = _reusableFileWriteStream.Allocate()) - using (var targetBuilder = ReusableLayoutBuilder.Allocate()) + using (var targetBuilder = _reusableLayoutBuilder.Allocate()) using (var targetBuffer = _reusableEncodingBuffer.Allocate()) { var formatBuilder = targetBuilder.Result; @@ -1083,7 +1082,7 @@ private int WriteToMemoryStream(IList logEvents, int startInd // For some CPU's then it is faster to write to a small MemoryStream, and then copy to the larger one encodingStream.Position = 0; encodingStream.SetLength(0); - formatBuilder.ClearBuilder(); + formatBuilder.Length = 0; AsyncLogEventInfo ev = logEvents[i]; RenderFormattedMessageToStream(ev.LogEvent, formatBuilder, transformBuffer, encodingStream); @@ -1214,10 +1213,8 @@ private void AppendMemoryStreamToFile(string currentFileName, LogEventInfo first } catch (Exception exception) { - if (ExceptionMustBeRethrown(exception)) - { + if (LogManager.ThrowExceptions) throw; - } lastException = exception; } @@ -1301,7 +1298,7 @@ private void ArchiveFileCompress(string fileName, string archiveFileName) int sleepTimeMs = i * 50; InternalLogger.Warn("{0}: Archiving Attempt #{1} to compress {2} to {3} failed - {4} {5}. Sleeping for {6}ms", this, i, fileName, archiveFileName, ex.GetType(), ex.Message, sleepTimeMs); - AsyncHelpers.WaitForDelay(TimeSpan.FromMilliseconds(sleepTimeMs)); + Thread.Sleep(TimeSpan.FromMilliseconds(sleepTimeMs)); } } @@ -1372,7 +1369,7 @@ private void ArchiveFileMove(string fileName, string archiveFileName) if (!File.Exists(fileName) || File.Exists(archiveFileName)) throw; - AsyncHelpers.WaitForDelay(TimeSpan.FromMilliseconds(50)); + Thread.Sleep(TimeSpan.FromMilliseconds(50)); if (!File.Exists(fileName) || File.Exists(archiveFileName)) throw; @@ -1400,7 +1397,7 @@ private bool DeleteOldArchiveFile(string fileName) catch (Exception exception) { InternalLogger.Warn(exception, "{0}: Failed to delete old archive file: '{1}'.", this, fileName); - if (ExceptionMustBeRethrown(exception)) + if (LogManager.ThrowExceptions) { throw; } @@ -1420,7 +1417,7 @@ private void DeleteAndWaitForFileDelete(string fileName) FileInfo currentFileInfo; for (int i = 0; i < 120; ++i) { - AsyncHelpers.WaitForDelay(TimeSpan.FromMilliseconds(100)); + Thread.Sleep(TimeSpan.FromMilliseconds(100)); currentFileInfo = new FileInfo(fileName); if (!currentFileInfo.Exists || currentFileInfo.CreationTime != originalFileCreationTime) return; @@ -1432,10 +1429,8 @@ private void DeleteAndWaitForFileDelete(string fileName) catch (Exception exception) { InternalLogger.Warn(exception, "{0}: Failed to delete old archive file: '{1}'.", this, fileName); - if (ExceptionMustBeRethrown(exception)) - { + if (LogManager.ThrowExceptions) throw; - } } } @@ -1457,13 +1452,13 @@ private string GetArchiveDateFormatString(string defaultFormat) switch (ArchiveEvery) { - case FileArchivePeriod.Year: + case FileArchiveEveryPeriod.Year: return "yyyy"; - case FileArchivePeriod.Month: + case FileArchiveEveryPeriod.Month: return "yyyyMM"; - case FileArchivePeriod.Hour: + case FileArchiveEveryPeriod.Hour: return "yyyyMMddHH"; - case FileArchivePeriod.Minute: + case FileArchiveEveryPeriod.Minute: return "yyyyMMddHHmm"; default: return "yyyyMMdd"; // Also for Weekdays @@ -1533,29 +1528,29 @@ private bool PreviousLogOverlappedPeriod(LogEventInfo logEvent, DateTime previou { switch (ArchiveEvery) { - case FileArchivePeriod.Year: + case FileArchiveEveryPeriod.Year: return timestamp.AddYears(1); - case FileArchivePeriod.Month: + case FileArchiveEveryPeriod.Month: return timestamp.AddMonths(1); - case FileArchivePeriod.Day: + case FileArchiveEveryPeriod.Day: return timestamp.AddDays(1); - case FileArchivePeriod.Hour: + case FileArchiveEveryPeriod.Hour: return timestamp.AddHours(1); - case FileArchivePeriod.Minute: + case FileArchiveEveryPeriod.Minute: return timestamp.AddMinutes(1); - case FileArchivePeriod.Sunday: + case FileArchiveEveryPeriod.Sunday: return CalculateNextWeekday(timestamp, DayOfWeek.Sunday); - case FileArchivePeriod.Monday: + case FileArchiveEveryPeriod.Monday: return CalculateNextWeekday(timestamp, DayOfWeek.Monday); - case FileArchivePeriod.Tuesday: + case FileArchiveEveryPeriod.Tuesday: return CalculateNextWeekday(timestamp, DayOfWeek.Tuesday); - case FileArchivePeriod.Wednesday: + case FileArchiveEveryPeriod.Wednesday: return CalculateNextWeekday(timestamp, DayOfWeek.Wednesday); - case FileArchivePeriod.Thursday: + case FileArchiveEveryPeriod.Thursday: return CalculateNextWeekday(timestamp, DayOfWeek.Thursday); - case FileArchivePeriod.Friday: + case FileArchiveEveryPeriod.Friday: return CalculateNextWeekday(timestamp, DayOfWeek.Friday); - case FileArchivePeriod.Saturday: + case FileArchiveEveryPeriod.Saturday: return CalculateNextWeekday(timestamp, DayOfWeek.Saturday); default: return null; @@ -1584,7 +1579,7 @@ public static DateTime CalculateNextWeekday(DateTime previousLogEventTimestamp, /// Invokes the archiving process after determining when and which type of archiving is required. ///
/// File name to be checked and archived. - /// Log event that the instance is currently processing. + /// Log event that the instance is currently processing. /// The DateTime of the previous log event for this file. /// File has just been opened. private void DoAutoArchive(string fileName, LogEventInfo eventInfo, DateTime previousLogEventTimestamp, bool initializedNewFile) @@ -1703,7 +1698,7 @@ private static void ExcludeActiveFileFromOldArchiveFiles(FileInfo currentFile, L /// Gets the pattern that archive files will match ///
/// Filename of the log file - /// Log event that the instance is currently processing. + /// Log event that the instance is currently processing. /// A string with a pattern that will match the archive filenames private string GetArchiveFileNamePattern(string fileName, LogEventInfo eventInfo) { @@ -1728,7 +1723,7 @@ private string GetArchiveFileNamePattern(string fileName, LogEventInfo eventInfo /// Archives the file if it should be archived. /// /// The file name to check for. - /// Log event that the instance is currently processing. + /// Log event that the instance is currently processing. /// The size in bytes of the next chunk of data to be written in the file. /// The DateTime of the previous log event for this file. /// File has just been opened. @@ -1756,10 +1751,8 @@ private bool TryArchiveFile(string fileName, LogEventInfo ev, int upcomingWriteS catch (Exception exception) { InternalLogger.Warn(exception, "{0}: Failed to check archive for file '{1}'.", this, fileName); - if (ExceptionMustBeRethrown(exception)) - { + if (LogManager.ThrowExceptions) throw; - } } if (string.IsNullOrEmpty(archiveFile)) @@ -1867,10 +1860,8 @@ private void ArchiveFileAfterCloseFileAppender(BaseFileAppender archivedAppender catch (Exception exception) { InternalLogger.Warn(exception, "{0}: Failed to archive file '{1}'.", this, archiveFile); - if (ExceptionMustBeRethrown(exception)) - { + if (LogManager.ThrowExceptions) throw; - } } } @@ -1878,7 +1869,7 @@ private void ArchiveFileAfterCloseFileAppender(BaseFileAppender archivedAppender /// Indicates if the automatic archiving process should be executed. /// /// File name to be written. - /// Log event that the instance is currently processing. + /// Log event that the instance is currently processing. /// The size in bytes of the next chunk of data to be written in the file. /// The DateTime of the previous log event for this file. /// File has just been opened. @@ -1988,13 +1979,13 @@ private string TryFallbackToPreviousLogFileName(string archiveFileName, bool ini /// Returns the file name for archiving, or null if archiving should not occur based on date/time. /// /// File name to be written. - /// Log event that the instance is currently processing. + /// Log event that the instance is currently processing. /// The DateTime of the previous log event for this file. /// File has just been opened. /// Filename to archive. If null, then nothing to archive. private string GetArchiveFileNameBasedOnTime(string fileName, LogEventInfo logEvent, DateTime previousLogEventTimestamp, bool initializedNewFile) { - if (ArchiveEvery == FileArchivePeriod.None) + if (ArchiveEvery == FileArchiveEveryPeriod.None) { return null; } @@ -2048,7 +2039,7 @@ private string GetArchiveFileNameBasedOnTime(string fileName, LogEventInfo logEv if (previousLogEventTimestamp > DateTime.MinValue && previousLogEventTimestamp < creationTimeSource) { - if (TruncateArchiveTime(previousLogEventTimestamp, FileArchivePeriod.Minute) < TruncateArchiveTime(creationTimeSource.Value, FileArchivePeriod.Minute) && PlatformDetector.IsUnix) + if (TruncateArchiveTime(previousLogEventTimestamp, FileArchiveEveryPeriod.Minute) < TruncateArchiveTime(creationTimeSource.Value, FileArchiveEveryPeriod.Minute) && PlatformDetector.IsUnix) { if (IsSimpleKeepFileOpen) { @@ -2057,7 +2048,7 @@ private string GetArchiveFileNameBasedOnTime(string fileName, LogEventInfo logEv } else { - InternalLogger.Debug("{0}: File creation time {1} newer than previous file write time {2}. Linux FileSystem probably don't support file birthtime, unless multiple applications are writing to the same file. Configure FileTarget.KeepFileOpen=true AND FileTarget.ConcurrentWrites=false, so NLog can fix this.", this, creationTimeSource, previousLogEventTimestamp); + InternalLogger.Debug("{0}: File creation time {1} newer than previous file write time {2}. Linux FileSystem probably don't support file birthtime, unless multiple applications are writing to the same file. Configure ConcurrentFileTarget.KeepFileOpen=true AND ConcurrentFileTarget.ConcurrentWrites=false, so NLog can fix this.", this, creationTimeSource, previousLogEventTimestamp); } } } @@ -2071,33 +2062,33 @@ private string GetArchiveFileNameBasedOnTime(string fileName, LogEventInfo logEv /// High resolution Time /// Time Resolution Level /// Truncated Low Resolution Time - private static DateTime TruncateArchiveTime(DateTime input, FileArchivePeriod resolution) + private static DateTime TruncateArchiveTime(DateTime input, FileArchiveEveryPeriod resolution) { switch (resolution) { - case FileArchivePeriod.Year: + case FileArchiveEveryPeriod.Year: return new DateTime(input.Year, 1, 1, 0, 0, 0, 0, input.Kind); - case FileArchivePeriod.Month: + case FileArchiveEveryPeriod.Month: return new DateTime(input.Year, input.Month, 1, 0, 0, 0, input.Kind); - case FileArchivePeriod.Day: + case FileArchiveEveryPeriod.Day: return input.Date; - case FileArchivePeriod.Hour: + case FileArchiveEveryPeriod.Hour: return input.AddTicks(-(input.Ticks % TimeSpan.TicksPerHour)); - case FileArchivePeriod.Minute: + case FileArchiveEveryPeriod.Minute: return input.AddTicks(-(input.Ticks % TimeSpan.TicksPerMinute)); - case FileArchivePeriod.Sunday: + case FileArchiveEveryPeriod.Sunday: return CalculateNextWeekday(input.Date, DayOfWeek.Sunday); - case FileArchivePeriod.Monday: + case FileArchiveEveryPeriod.Monday: return CalculateNextWeekday(input.Date, DayOfWeek.Monday); - case FileArchivePeriod.Tuesday: + case FileArchiveEveryPeriod.Tuesday: return CalculateNextWeekday(input.Date, DayOfWeek.Tuesday); - case FileArchivePeriod.Wednesday: + case FileArchiveEveryPeriod.Wednesday: return CalculateNextWeekday(input.Date, DayOfWeek.Wednesday); - case FileArchivePeriod.Thursday: + case FileArchiveEveryPeriod.Thursday: return CalculateNextWeekday(input.Date, DayOfWeek.Thursday); - case FileArchivePeriod.Friday: + case FileArchiveEveryPeriod.Friday: return CalculateNextWeekday(input.Date, DayOfWeek.Friday); - case FileArchivePeriod.Saturday: + case FileArchiveEveryPeriod.Saturday: return CalculateNextWeekday(input.Date, DayOfWeek.Saturday); default: return input; // Unknown time-resolution-truncate, leave unchanged @@ -2122,13 +2113,6 @@ private void AutoCloseAppendersAfterArchive(object sender, EventArgs state) } catch (Exception exception) { -#if DEBUG - if (exception.MustBeRethrownImmediately()) - { - throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) - } -#endif - InternalLogger.Warn(exception, "{0}: Exception in AutoCloseAppendersAfterArchive", this); } finally @@ -2164,13 +2148,6 @@ private void AutoClosingTimerCallback(object sender, EventArgs state) } catch (Exception exception) { -#if DEBUG - if (exception.MustBeRethrownImmediately()) - { - throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) - } -#endif - InternalLogger.Warn(exception, "{0}: Exception in AutoClosingTimerCallback", this); } finally @@ -2203,7 +2180,7 @@ private void ConditionalFlushOpenFileAppenders() /// /// Evaluates which parts of a file should be written (header, content, footer) based on various properties of - /// instance and writes them. + /// instance and writes them. /// /// File name to be written. /// Raw sequence of to be written into the content part of the file. @@ -2234,11 +2211,11 @@ private void WriteToFile(string fileName, ArraySegment bytes, bool initial } /// - /// Initialize a file to be used by the instance. Based on the number of initialized + /// Initialize a file to be used by the instance. Based on the number of initialized /// files and the values of various instance properties clean up and/or archiving processes can be invoked. /// /// File name to be written. - /// Log event that the instance is currently processing. + /// Log event that the instance is currently processing. /// The DateTime of the previous log event for this file (DateTime.MinValue if just initialized). private DateTime InitializeFile(string fileName, LogEventInfo logEvent) { @@ -2306,7 +2283,7 @@ private void CloseInvalidFileHandle(string fileName) } /// - /// Writes the file footer and finalizes the file in instance internal structures. + /// Writes the file footer and finalizes the file in instance internal structures. /// /// File name to close. /// Indicates if the file is being finalized for archiving. @@ -2365,11 +2342,11 @@ internal bool ShouldArchiveOldFileOnStartup(string fileName) /// /// Invokes the archiving and clean up of older archive file based on the values of - /// and - /// properties respectively. + /// and + /// properties respectively. /// /// File name to be written. - /// Log event that the instance is currently processing. + /// Log event that the instance is currently processing. private void PrepareForNewFile(string fileName, LogEventInfo logEvent) { InternalLogger.Debug("{0}: Preparing for new file: '{1}'", this, fileName); @@ -2385,10 +2362,8 @@ private void PrepareForNewFile(string fileName, LogEventInfo logEvent) catch (Exception exception) { InternalLogger.Warn(exception, "{0}: Unable to archive old log file '{1}'.", this, fileName); - if (ExceptionMustBeRethrown(exception)) - { + if (LogManager.ThrowExceptions) throw; - } } if (DeleteOldFileOnStartup) @@ -2415,11 +2390,8 @@ private void PrepareForNewFile(string fileName, LogEventInfo logEvent) catch (Exception exception) { InternalLogger.Warn(exception, "FileTarget(Name={0}): Failed to cleanup old archive files when starting on new file: '{1}'", Name, fileName); - - if (ExceptionMustBeRethrown(exception)) - { + if (LogManager.ThrowExceptions) throw; - } } } @@ -2524,7 +2496,7 @@ private ArraySegment GetLayoutBytes(Layout layout) return default(ArraySegment); } - using (var targetBuilder = ReusableLayoutBuilder.Allocate()) + using (var targetBuilder = _reusableLayoutBuilder.Allocate()) using (var targetBuffer = _reusableEncodingBuffer.Allocate()) { var nullEvent = LogEventInfo.CreateNullEvent(); diff --git a/src/NLog/Internal/FileAppenders/BaseFileAppender.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/BaseFileAppender.cs similarity index 98% rename from src/NLog/Internal/FileAppenders/BaseFileAppender.cs rename to src/NLog.Targets.ConcurrentFile/FileAppenders/BaseFileAppender.cs index 2d2195bc02..fad3144044 100644 --- a/src/NLog/Internal/FileAppenders/BaseFileAppender.cs +++ b/src/NLog.Targets.ConcurrentFile/FileAppenders/BaseFileAppender.cs @@ -192,7 +192,7 @@ protected FileStream CreateFileStream(bool allowFileSharedWriting, int overrideB int actualDelay = _random.Next(currentDelay); InternalLogger.Warn("{0}: Attempt #{1} to open {2} failed - {3} {4}. Sleeping for {5}ms", CreateFileParameters, i, FileName, ex.GetType(), ex.Message, actualDelay); currentDelay *= 2; - AsyncHelpers.WaitForDelay(TimeSpan.FromMilliseconds(actualDelay)); + System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(actualDelay)); } } @@ -243,7 +243,7 @@ private FileStream WindowsCreateFile(bool allowFileSharedWriting, int bufferSize fileShare |= Win32FileNativeMethods.FILE_SHARE_WRITE; } - if (CreateFileParameters.EnableFileDelete && PlatformDetector.CurrentOS != RuntimeOS.Windows9x) + if (CreateFileParameters.EnableFileDelete && PlatformDetector.CurrentOS != PlatformSystem.Windows9x) { fileShare |= Win32FileNativeMethods.FILE_SHARE_DELETE; } @@ -363,7 +363,7 @@ protected static void CloseFileSafe(ref FileStream fileStream, string fileName) { // Swallow exception as the file-stream now is in final state (broken instead of closed) InternalLogger.Warn(ex, "FileTarget: Failed to close file '{0}'", fileName); - AsyncHelpers.WaitForDelay(TimeSpan.FromMilliseconds(1)); // Artificial delay to avoid hammering a bad file location + System.Threading.Thread.Sleep(TimeSpan.FromMilliseconds(1)); // Artificial delay to avoid hammering a bad file location } finally { diff --git a/src/NLog/Internal/FileAppenders/BaseMutexFileAppender.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/BaseMutexFileAppender.cs similarity index 99% rename from src/NLog/Internal/FileAppenders/BaseMutexFileAppender.cs rename to src/NLog.Targets.ConcurrentFile/FileAppenders/BaseMutexFileAppender.cs index 186d32aeed..3e035d4b18 100644 --- a/src/NLog/Internal/FileAppenders/BaseMutexFileAppender.cs +++ b/src/NLog.Targets.ConcurrentFile/FileAppenders/BaseMutexFileAppender.cs @@ -81,7 +81,6 @@ protected BaseMutexFileAppender(string fileName, ICreateFileParameters createPar /// Gets the mutually-exclusive lock for archiving files. /// /// The mutex for archiving. - [CanBeNull] public Mutex ArchiveMutex { get; private set; } private Mutex CreateArchiveMutex() @@ -99,7 +98,7 @@ private Mutex CreateArchiveMutex() } InternalLogger.Error(ex, "{0}: Failed to create global archive mutex: {1}", CreateFileParameters, FileName); - if (ex.MustBeRethrown()) + if (LogManager.ThrowExceptions) throw; return new Mutex(); } diff --git a/src/NLog/Internal/FileAppenders/CountingSingleProcessFileAppender.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/CountingSingleProcessFileAppender.cs similarity index 100% rename from src/NLog/Internal/FileAppenders/CountingSingleProcessFileAppender.cs rename to src/NLog.Targets.ConcurrentFile/FileAppenders/CountingSingleProcessFileAppender.cs diff --git a/src/NLog/Internal/FileAppenders/FileAppenderCache.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/FileAppenderCache.cs similarity index 99% rename from src/NLog/Internal/FileAppenders/FileAppenderCache.cs rename to src/NLog.Targets.ConcurrentFile/FileAppenders/FileAppenderCache.cs index fde73c164c..2bc2ae087b 100644 --- a/src/NLog/Internal/FileAppenders/FileAppenderCache.cs +++ b/src/NLog.Targets.ConcurrentFile/FileAppenders/FileAppenderCache.cs @@ -541,7 +541,7 @@ public void Dispose() if (currentTimer != null) { _autoClosingTimer = null; - currentTimer.WaitForDispose(TimeSpan.Zero); + currentTimer.Dispose(); } } } diff --git a/src/NLog/Internal/FileAppenders/ICreateFileParameters.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/ICreateFileParameters.cs similarity index 99% rename from src/NLog/Internal/FileAppenders/ICreateFileParameters.cs rename to src/NLog.Targets.ConcurrentFile/FileAppenders/ICreateFileParameters.cs index d96a6825f8..42b0a3591b 100644 --- a/src/NLog/Internal/FileAppenders/ICreateFileParameters.cs +++ b/src/NLog.Targets.ConcurrentFile/FileAppenders/ICreateFileParameters.cs @@ -33,7 +33,7 @@ namespace NLog.Internal.FileAppenders { - using Targets; + using NLog.Targets; /// /// Interface that provides parameters for create file function. diff --git a/src/NLog/Internal/FileAppenders/IFileAppenderCache.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/IFileAppenderCache.cs similarity index 100% rename from src/NLog/Internal/FileAppenders/IFileAppenderCache.cs rename to src/NLog.Targets.ConcurrentFile/FileAppenders/IFileAppenderCache.cs diff --git a/src/NLog/Internal/FileAppenders/IFileAppenderFactory.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/IFileAppenderFactory.cs similarity index 100% rename from src/NLog/Internal/FileAppenders/IFileAppenderFactory.cs rename to src/NLog.Targets.ConcurrentFile/FileAppenders/IFileAppenderFactory.cs diff --git a/src/NLog/Internal/FileAppenders/MutexMultiProcessFileAppender.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/MutexMultiProcessFileAppender.cs similarity index 100% rename from src/NLog/Internal/FileAppenders/MutexMultiProcessFileAppender.cs rename to src/NLog.Targets.ConcurrentFile/FileAppenders/MutexMultiProcessFileAppender.cs diff --git a/src/NLog/Internal/FileAppenders/NullAppender.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/NullAppender.cs similarity index 100% rename from src/NLog/Internal/FileAppenders/NullAppender.cs rename to src/NLog.Targets.ConcurrentFile/FileAppenders/NullAppender.cs diff --git a/src/NLog/Internal/FileAppenders/RetryingMultiProcessFileAppender.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/RetryingMultiProcessFileAppender.cs similarity index 100% rename from src/NLog/Internal/FileAppenders/RetryingMultiProcessFileAppender.cs rename to src/NLog.Targets.ConcurrentFile/FileAppenders/RetryingMultiProcessFileAppender.cs diff --git a/src/NLog/Internal/FileAppenders/SingleProcessFileAppender.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/SingleProcessFileAppender.cs similarity index 100% rename from src/NLog/Internal/FileAppenders/SingleProcessFileAppender.cs rename to src/NLog.Targets.ConcurrentFile/FileAppenders/SingleProcessFileAppender.cs diff --git a/src/NLog/Internal/FileAppenders/UnixMultiProcessFileAppender.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/UnixMultiProcessFileAppender.cs similarity index 100% rename from src/NLog/Internal/FileAppenders/UnixMultiProcessFileAppender.cs rename to src/NLog.Targets.ConcurrentFile/FileAppenders/UnixMultiProcessFileAppender.cs diff --git a/src/NLog/Internal/FileAppenders/WindowsMultiProcessFileAppender.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/WindowsMultiProcessFileAppender.cs similarity index 100% rename from src/NLog/Internal/FileAppenders/WindowsMultiProcessFileAppender.cs rename to src/NLog.Targets.ConcurrentFile/FileAppenders/WindowsMultiProcessFileAppender.cs diff --git a/src/NLog.Targets.ConcurrentFile/FileArchiveEveryPeriod.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveEveryPeriod.cs new file mode 100644 index 0000000000..3af9835963 --- /dev/null +++ b/src/NLog.Targets.ConcurrentFile/FileArchiveEveryPeriod.cs @@ -0,0 +1,108 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets +{ + /// + /// Modes of archiving files based on time. + /// + public enum FileArchiveEveryPeriod + { + /// + /// Don't archive based on time. + /// + None, + + /// + /// Archive every new year. + /// + Year, + + /// + /// Archive every new month. + /// + Month, + + /// + /// Archive every new day. + /// + Day, + + /// + /// Archive every new hour. + /// + Hour, + + /// + /// Archive every new minute. + /// + Minute, + + #region Weekdays + /// + /// Archive every Sunday. + /// + Sunday, + + /// + /// Archive every Monday. + /// + Monday, + + /// + /// Archive every Tuesday. + /// + Tuesday, + + /// + /// Archive every Wednesday. + /// + Wednesday, + + /// + /// Archive every Thursday. + /// + Thursday, + + /// + /// Archive every Friday. + /// + Friday, + + /// + /// Archive every Saturday. + /// + Saturday + #endregion + } +} diff --git a/src/NLog/Targets/FileArchiveModes/FileArchiveModeBase.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeBase.cs similarity index 100% rename from src/NLog/Targets/FileArchiveModes/FileArchiveModeBase.cs rename to src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeBase.cs diff --git a/src/NLog/Targets/FileArchiveModes/FileArchiveModeDate.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDate.cs similarity index 94% rename from src/NLog/Targets/FileArchiveModes/FileArchiveModeDate.cs rename to src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDate.cs index fb253b335d..7b51203185 100644 --- a/src/NLog/Targets/FileArchiveModes/FileArchiveModeDate.cs +++ b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDate.cs @@ -42,8 +42,8 @@ namespace NLog.Targets.FileArchiveModes /// Archives the log-files using a date style numbering. Archives will be stamped with the /// prior period (Year, Month, Day, Hour, Minute) datetime. /// - /// When the number of archive files exceed the obsolete archives are deleted. - /// When the age of archive files exceed the obsolete archives are deleted. + /// When the number of archive files exceed the obsolete archives are deleted. + /// When the age of archive files exceed the obsolete archives are deleted. /// internal sealed class FileArchiveModeDate : FileArchiveModeBase { diff --git a/src/NLog/Targets/FileArchiveModes/FileArchiveModeDateAndSequence.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDateAndSequence.cs similarity index 96% rename from src/NLog/Targets/FileArchiveModes/FileArchiveModeDateAndSequence.cs rename to src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDateAndSequence.cs index 5761b02139..c103d975ff 100644 --- a/src/NLog/Targets/FileArchiveModes/FileArchiveModeDateAndSequence.cs +++ b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDateAndSequence.cs @@ -44,8 +44,8 @@ namespace NLog.Targets.FileArchiveModes /// with the prior period (Year, Month, Day) datetime. The most recent archive has the highest number (in /// combination with the date). /// - /// When the number of archive files exceed the obsolete archives are deleted. - /// When the age of archive files exceed the obsolete archives are deleted. + /// When the number of archive files exceed the obsolete archives are deleted. + /// When the age of archive files exceed the obsolete archives are deleted. /// internal sealed class FileArchiveModeDateAndSequence : FileArchiveModeBase { diff --git a/src/NLog/Targets/FileArchiveModes/FileArchiveModeDynamicSequence.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDynamicSequence.cs similarity index 97% rename from src/NLog/Targets/FileArchiveModes/FileArchiveModeDynamicSequence.cs rename to src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDynamicSequence.cs index eef1f72c2a..d0b14ad4a5 100644 --- a/src/NLog/Targets/FileArchiveModes/FileArchiveModeDynamicSequence.cs +++ b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDynamicSequence.cs @@ -50,8 +50,8 @@ namespace NLog.Targets.FileArchiveModes /// /// The most recent archive has the highest number. /// - /// When the number of archive files exceed the obsolete archives are deleted. - /// When the age of archive files exceed the obsolete archives are deleted. + /// When the number of archive files exceed the obsolete archives are deleted. + /// When the age of archive files exceed the obsolete archives are deleted. /// internal sealed class FileArchiveModeDynamicSequence : FileArchiveModeBase { @@ -70,7 +70,7 @@ public FileArchiveModeDynamicSequence(ArchiveNumberingMode archiveNumbering, str private static bool RemoveNonLetters(string fileName, int startPosition, StringBuilder sb, out int digitsRemoved) { digitsRemoved = 0; - sb.ClearBuilder(); + sb.Length = 0; for (int i = 0; i < startPosition; i++) { @@ -143,7 +143,7 @@ protected override FileNameTemplate GenerateFileNameTemplate(string archiveFileP RemoveNonLetters(currentFileName, optimalStartPosition, sb, out digitsRemoved); if (digitsRemoved <= 1) { - sb.ClearBuilder(); + sb.Length = 0; sb.Append(currentFileName); } diff --git a/src/NLog/Targets/FileArchiveModes/FileArchiveModeDynamicTemplate.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDynamicTemplate.cs similarity index 100% rename from src/NLog/Targets/FileArchiveModes/FileArchiveModeDynamicTemplate.cs rename to src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDynamicTemplate.cs diff --git a/src/NLog/Targets/FileArchiveModes/FileArchiveModeFactory.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeFactory.cs similarity index 100% rename from src/NLog/Targets/FileArchiveModes/FileArchiveModeFactory.cs rename to src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeFactory.cs diff --git a/src/NLog/Targets/FileArchiveModes/FileArchiveModeRolling.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeRolling.cs similarity index 98% rename from src/NLog/Targets/FileArchiveModes/FileArchiveModeRolling.cs rename to src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeRolling.cs index d6b02214ee..c6a868123d 100644 --- a/src/NLog/Targets/FileArchiveModes/FileArchiveModeRolling.cs +++ b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeRolling.cs @@ -41,7 +41,7 @@ namespace NLog.Targets.FileArchiveModes /// Archives the log-files using a rolling style numbering (the most recent is always #0 then /// #1, ..., #N. /// - /// When the number of archive files exceed the obsolete archives + /// When the number of archive files exceed the obsolete archives /// are deleted. /// internal sealed class FileArchiveModeRolling : IFileArchiveMode diff --git a/src/NLog/Targets/FileArchiveModes/FileArchiveModeSequence.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeSequence.cs similarity index 94% rename from src/NLog/Targets/FileArchiveModes/FileArchiveModeSequence.cs rename to src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeSequence.cs index 20b537cc2e..08712ed0d9 100644 --- a/src/NLog/Targets/FileArchiveModes/FileArchiveModeSequence.cs +++ b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeSequence.cs @@ -42,8 +42,8 @@ namespace NLog.Targets.FileArchiveModes /// /// Archives the log-files using a sequence style numbering. The most recent archive has the highest number. /// - /// When the number of archive files exceed the obsolete archives are deleted. - /// When the age of archive files exceed the obsolete archives are deleted. + /// When the number of archive files exceed the obsolete archives are deleted. + /// When the age of archive files exceed the obsolete archives are deleted. /// internal sealed class FileArchiveModeSequence : FileArchiveModeBase { diff --git a/src/NLog/Targets/IFileArchiveMode.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/IFileArchiveMode.cs similarity index 100% rename from src/NLog/Targets/IFileArchiveMode.cs rename to src/NLog.Targets.ConcurrentFile/FileArchiveModes/IFileArchiveMode.cs diff --git a/src/NLog/Targets/FilePathKind.cs b/src/NLog.Targets.ConcurrentFile/FilePathKind.cs similarity index 100% rename from src/NLog/Targets/FilePathKind.cs rename to src/NLog.Targets.ConcurrentFile/FilePathKind.cs diff --git a/src/NLog/Targets/IArchiveFileCompressor.cs b/src/NLog.Targets.ConcurrentFile/IArchiveFileCompressor.cs similarity index 91% rename from src/NLog/Targets/IArchiveFileCompressor.cs rename to src/NLog.Targets.ConcurrentFile/IArchiveFileCompressor.cs index 3d64e5f1c4..714ea265d9 100644 --- a/src/NLog/Targets/IArchiveFileCompressor.cs +++ b/src/NLog.Targets.ConcurrentFile/IArchiveFileCompressor.cs @@ -34,8 +34,8 @@ namespace NLog.Targets { /// - /// may be configured to compress archived files in a custom way - /// by setting before logging your first event. + /// may be configured to compress archived files in a custom way + /// by setting before logging your first event. /// internal interface IArchiveFileCompressor : IFileCompressor { diff --git a/src/NLog/Targets/IFileCompressor.cs b/src/NLog.Targets.ConcurrentFile/IFileCompressor.cs similarity index 91% rename from src/NLog/Targets/IFileCompressor.cs rename to src/NLog.Targets.ConcurrentFile/IFileCompressor.cs index d5e7dc73de..6b9f74da4e 100644 --- a/src/NLog/Targets/IFileCompressor.cs +++ b/src/NLog.Targets.ConcurrentFile/IFileCompressor.cs @@ -34,8 +34,8 @@ namespace NLog.Targets { /// - /// may be configured to compress archived files in a custom way - /// by setting before logging your first event. + /// may be configured to compress archived files in a custom way + /// by setting before logging your first event. /// public interface IFileCompressor { diff --git a/src/NLog/Targets/DateAndSequenceArchive.cs b/src/NLog.Targets.ConcurrentFile/Internal/DateAndSequenceArchive.cs similarity index 94% rename from src/NLog/Targets/DateAndSequenceArchive.cs rename to src/NLog.Targets.ConcurrentFile/Internal/DateAndSequenceArchive.cs index 9276372210..94e07b591e 100644 --- a/src/NLog/Targets/DateAndSequenceArchive.cs +++ b/src/NLog.Targets.ConcurrentFile/Internal/DateAndSequenceArchive.cs @@ -34,7 +34,6 @@ namespace NLog.Targets { using System; - using NLog.Internal; /// /// A descriptor for an archive created with the DateAndSequence numbering mode. @@ -75,8 +74,8 @@ public bool HasSameFormattedDate(DateTime date) /// public DateAndSequenceArchive(string fileName, DateTime date, string dateFormat, int sequence) { - FileName = Guard.ThrowIfNull(fileName); - _dateFormat = Guard.ThrowIfNull(dateFormat); + FileName = fileName ?? throw new ArgumentNullException(nameof(fileName)); + _dateFormat = dateFormat ?? throw new ArgumentNullException(nameof(dateFormat)); Date = date; Sequence = sequence; } diff --git a/src/NLog.Targets.ConcurrentFile/Internal/ExcludeFromCodeCoverageAttribute.cs b/src/NLog.Targets.ConcurrentFile/Internal/ExcludeFromCodeCoverageAttribute.cs new file mode 100644 index 0000000000..171c60257b --- /dev/null +++ b/src/NLog.Targets.ConcurrentFile/Internal/ExcludeFromCodeCoverageAttribute.cs @@ -0,0 +1,51 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#if NET35 + +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)] + internal sealed class ExcludeFromCodeCoverageAttribute : Attribute + { + // + // Summary: + // Initializes a new instance of the System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute + // class. + public ExcludeFromCodeCoverageAttribute() + { + } + } +} + +#endif diff --git a/src/NLog/Internal/FileInfoExt.cs b/src/NLog.Targets.ConcurrentFile/Internal/FileInfoExt.cs similarity index 67% rename from src/NLog/Internal/FileInfoExt.cs rename to src/NLog.Targets.ConcurrentFile/Internal/FileInfoExt.cs index c7cd0cebfa..a9cc2c3253 100644 --- a/src/NLog/Internal/FileInfoExt.cs +++ b/src/NLog.Targets.ConcurrentFile/Internal/FileInfoExt.cs @@ -31,11 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.IO; - namespace NLog.Internal { + using System; + using System.IO; + internal static class FileInfoExt { public static DateTime GetLastWriteTimeUtc(this FileInfo fileInfo) @@ -49,15 +49,31 @@ public static DateTime GetCreationTimeUtc(this FileInfo fileInfo) public static DateTime LookupValidFileCreationTimeUtc(this FileInfo fileInfo) { - return FileInfoHelper.LookupValidFileCreationTimeUtc(fileInfo, (f) => f.GetCreationTimeUtc(), (f) => f.GetLastWriteTimeUtc()).Value; + return LookupValidFileCreationTimeUtc(fileInfo, (f) => f.GetCreationTimeUtc(), (f) => f.GetLastWriteTimeUtc()).Value; } public static DateTime LookupValidFileCreationTimeUtc(this FileInfo fileInfo, DateTime? fallbackTime) { if (fallbackTime > DateTime.MinValue) - return FileInfoHelper.LookupValidFileCreationTimeUtc(fileInfo, (f) => f.GetCreationTimeUtc(), (f) => fallbackTime.Value, (f) => f.GetLastWriteTimeUtc()).Value; + return LookupValidFileCreationTimeUtc(fileInfo, (f) => f.GetCreationTimeUtc(), (f) => fallbackTime.Value, (f) => f.GetLastWriteTimeUtc()).Value; else return LookupValidFileCreationTimeUtc(fileInfo); } + + internal static DateTime? LookupValidFileCreationTimeUtc(T fileInfo, Func primary, Func fallback, Func finalFallback = null) + { + DateTime? fileCreationTime = primary(fileInfo); + + if (fileCreationTime.HasValue && fileCreationTime.Value.Year < 1980 && !PlatformDetector.IsWin32) + { + // Non-Windows-FileSystems doesn't always provide correct CreationTime/BirthTime + fileCreationTime = fallback(fileInfo); + if (finalFallback != null && (!fileCreationTime.HasValue || fileCreationTime.Value.Year < 1980)) + { + fileCreationTime = finalFallback(fileInfo); + } + } + return fileCreationTime; + } } } diff --git a/src/NLog/Internal/FilePathLayout.cs b/src/NLog.Targets.ConcurrentFile/Internal/FilePathLayout.cs similarity index 92% rename from src/NLog/Internal/FilePathLayout.cs rename to src/NLog.Targets.ConcurrentFile/Internal/FilePathLayout.cs index f33992a41e..e388123bff 100644 --- a/src/NLog/Internal/FilePathLayout.cs +++ b/src/NLog.Targets.ConcurrentFile/Internal/FilePathLayout.cs @@ -43,7 +43,7 @@ namespace NLog.Internal /// /// A layout that represents a filePath. /// - internal sealed class FilePathLayout : IRenderable + internal sealed class FilePathLayout { /// /// Cached directory separator char array to avoid memory allocation on each method call. @@ -94,7 +94,7 @@ public FilePathLayout(Layout layout, bool cleanupInvalidChars, FilePathKind file if (_filePathKind == FilePathKind.Relative) { - _baseDir = LogFactory.DefaultAppEnvironment.AppDomainBaseDirectory; + _baseDir = AppDomain.CurrentDomain.BaseDirectory; InternalLogger.Debug("FileTarget FilePathLayout with FilePathKind.Relative using AppDomain.BaseDirectory: {0}", _baseDir); } @@ -118,14 +118,11 @@ public FilePathLayout(Layout layout, bool cleanupInvalidChars, FilePathKind file if (layout is SimpleLayout simpleLayout && simpleLayout.OriginalText?.IndexOfAny(new char[] { '\a', '\b', '\f', '\n', '\r', '\t', '\v', '\"' }) >= 0) { - if (_cleanedFixedResult is null) - InternalLogger.Warn("FileTarget FilePathLayout contains unexpected escape characters (Maybe change to forward-slash): {0}", simpleLayout.OriginalText); - else - InternalLogger.Warn("FileTarget FilePathLayout contains unexpected escape characters (Maybe change to forward-slash): {0}", _cleanedFixedResult); + InternalLogger.Warn("FileTarget FilePathLayout contains unexpected escape characters (Maybe change to forward-slash): {0}", _cleanedFixedResult ?? simpleLayout.OriginalText); } else if (_cleanedFixedResult != null) { - if (DetectFilePathKind(_cleanedFixedResult) != FilePathKind.Absolute && _cleanedFixedResult.IndexOfAny(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }) < 0) + if (DetectFilePathKind(_cleanedFixedResult) != FilePathKind.Absolute && _cleanedFixedResult.IndexOfAny(DirectorySeparatorChars) < 0) { InternalLogger.Warn("FileTarget FilePathLayout not recognized as absolute path (Maybe change to forward-slash): {0}", _cleanedFixedResult); } @@ -151,15 +148,6 @@ private string GetRenderedFileName(LogEventInfo logEvent, System.Text.StringBuil } else { - if (!_layout.ThreadAgnostic || _layout.ThreadAgnosticImmutable) - { - object cachedResult; - if (logEvent.TryGetCachedLayoutValue(_layout, out cachedResult)) - { - return cachedResult?.ToString() ?? string.Empty; - } - } - _layout.Render(logEvent, reusableBuilder); if (_cachedPrevRawFileName != null && reusableBuilder.EqualTo(_cachedPrevRawFileName)) @@ -249,7 +237,7 @@ internal static FilePathKind DetectFilePathKind(string path, bool isFixedText = { if (!string.IsNullOrEmpty(path)) { - path = path.TrimStart(ArrayHelper.Empty()); + path = path.TrimStart(); int length = path.Length; if (length >= 1) diff --git a/src/NLog/Internal/MultiFileWatcher.cs b/src/NLog.Targets.ConcurrentFile/Internal/MultiFileWatcher.cs similarity index 98% rename from src/NLog/Internal/MultiFileWatcher.cs rename to src/NLog.Targets.ConcurrentFile/Internal/MultiFileWatcher.cs index 6b1463bb66..4f0a53ae89 100644 --- a/src/NLog/Internal/MultiFileWatcher.cs +++ b/src/NLog.Targets.ConcurrentFile/Internal/MultiFileWatcher.cs @@ -165,7 +165,7 @@ private bool TryAddWatch(string fileName, string directory, string fileFilter) if (ex is System.Security.SecurityException || ex is UnauthorizedAccessException || ex is NotSupportedException || ex is NotImplementedException || ex is PlatformNotSupportedException) return false; - if (ex.MustBeRethrown()) + if (LogManager.ThrowExceptions) throw; if (watcher != null) @@ -200,7 +200,7 @@ private void StopWatching(FileSystemWatcher watcher) catch (Exception ex) { InternalLogger.Error(ex, "Failed to stop FileSystemWatcher with file-filter '{0}' in directory: {1}", fileFilter, fileDirectory); - if (ex.MustBeRethrown()) + if (LogManager.ThrowExceptions) throw; } } @@ -225,7 +225,7 @@ private void OnFileChanged(object source, FileSystemEventArgs e) catch (Exception ex) { #if DEBUG - if (ex.MustBeRethrownImmediately()) + if (LogManager.ThrowExceptions) throw; // Throwing exceptions here might crash the entire application (.NET 2.0 behavior) #endif InternalLogger.Error(ex, "Error handling event from FileSystemWatcher with file-filter: '{0}' in directory: {1}", e.Name, e.FullPath); diff --git a/src/NLog/Internal/MutexDetector.cs b/src/NLog.Targets.ConcurrentFile/Internal/MutexDetector.cs similarity index 93% rename from src/NLog/Internal/MutexDetector.cs rename to src/NLog.Targets.ConcurrentFile/Internal/MutexDetector.cs index 02be04621a..c0bcbf7a88 100644 --- a/src/NLog/Internal/MutexDetector.cs +++ b/src/NLog.Targets.ConcurrentFile/Internal/MutexDetector.cs @@ -55,10 +55,6 @@ private static bool ResolveSupportsSharableMutex() { try { -#if NETFRAMEWORK - if (Environment.Version.Major < 4 && PlatformDetector.IsMono) - return false; // MONO ver. 4 is needed for named Mutex to work -#endif var mutex = BaseMutexFileAppender.ForceCreateSharableMutex("NLogMutexTester"); mutex.Close(); //"dispose" return true; diff --git a/src/NLog.Targets.ConcurrentFile/Internal/PlatformDetector.cs b/src/NLog.Targets.ConcurrentFile/Internal/PlatformDetector.cs new file mode 100644 index 0000000000..035e77369e --- /dev/null +++ b/src/NLog.Targets.ConcurrentFile/Internal/PlatformDetector.cs @@ -0,0 +1,89 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Internal +{ + using System; + + /// + /// Detects the platform the NLog is running on. + /// + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + internal static class PlatformDetector + { + /// + /// Gets the current runtime OS. + /// + public static PlatformSystem CurrentOS => _currentOS ?? (_currentOS = GetCurrentRuntimeOS()).Value; + private static PlatformSystem? _currentOS; + + /// + /// Gets a value indicating whether current OS is Win32-based (desktop or mobile). + /// + public static bool IsWin32 => CurrentOS == PlatformSystem.WindowsNT || CurrentOS == PlatformSystem.Windows9x; + + /// + /// Gets a value indicating whether current OS is Unix-based. + /// + public static bool IsUnix => CurrentOS == PlatformSystem.Linux || CurrentOS == PlatformSystem.MacOSX; + +#if NETFRAMEWORK + /// + /// Gets a value indicating whether current runtime is Mono-based + /// + public static bool IsMono => _isMono ?? (_isMono = Type.GetType("Mono.Runtime") != null).Value; + private static bool? _isMono; +#endif + + private static PlatformSystem GetCurrentRuntimeOS() + { +#if NETFRAMEWORK + PlatformID platformID = Environment.OSVersion.Platform; + if ((int)platformID == 4 || (int)platformID == 128) + return PlatformSystem.Linux; + if (platformID == PlatformID.Win32Windows) + return PlatformSystem.Windows9x; + if (platformID == PlatformID.Win32NT) + return PlatformSystem.WindowsNT; +#else + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Linux)) + return PlatformSystem.Linux; + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.OSX)) + return PlatformSystem.MacOSX; + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) + return PlatformSystem.WindowsNT; +#endif + return PlatformSystem.Unknown; + } + } +} diff --git a/src/NLog.Targets.ConcurrentFile/Internal/PlatformSystem.cs b/src/NLog.Targets.ConcurrentFile/Internal/PlatformSystem.cs new file mode 100644 index 0000000000..145461aab9 --- /dev/null +++ b/src/NLog.Targets.ConcurrentFile/Internal/PlatformSystem.cs @@ -0,0 +1,70 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Internal +{ + /// + /// Supported operating systems. + /// + /// + /// If you add anything here, make sure to add the appropriate detection + /// code to + /// + internal enum PlatformSystem + { + /// + /// Unknown operating system. + /// + Unknown, + + /// + /// Unix/Linux operating systems. + /// + Linux, + + /// + /// Desktop versions of Windows (95,98,ME). + /// + Windows9x, + + /// + /// Windows NT, 2000, 2003 and future versions based on NT technology. + /// + WindowsNT, + + /// + /// Macintosh Mac OSX + /// + MacOSX, + } +} diff --git a/src/NLog.Targets.ConcurrentFile/Internal/ReusableBufferCreator.cs b/src/NLog.Targets.ConcurrentFile/Internal/ReusableBufferCreator.cs new file mode 100644 index 0000000000..85a851eac8 --- /dev/null +++ b/src/NLog.Targets.ConcurrentFile/Internal/ReusableBufferCreator.cs @@ -0,0 +1,46 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Internal +{ + /// + /// Controls a single allocated char[]-buffer for reuse (only one active user) + /// + internal sealed class ReusableBufferCreator : ReusableObjectCreator + { + public ReusableBufferCreator(int initialCapacity) + : base(() => new char[initialCapacity], (b) => { }) + { + } + } +} diff --git a/src/NLog.Targets.ConcurrentFile/Internal/ReusableBuilderCreator.cs b/src/NLog.Targets.ConcurrentFile/Internal/ReusableBuilderCreator.cs new file mode 100644 index 0000000000..132c04a5f5 --- /dev/null +++ b/src/NLog.Targets.ConcurrentFile/Internal/ReusableBuilderCreator.cs @@ -0,0 +1,61 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Internal +{ + using System.Text; + + /// + /// Controls a single allocated StringBuilder for reuse (only one active user) + /// + internal sealed class ReusableBuilderCreator : ReusableObjectCreator + { + private const int MaxBuilderCapacity = 40960; // Avoid Large-Object-Heap (LOH) + + public ReusableBuilderCreator() + : base(() => new StringBuilder(512), (sb) => ResetCapacity(sb)) + { + } + + private static void ResetCapacity(StringBuilder stringBuilder) + { + if (stringBuilder.Length > MaxBuilderCapacity && stringBuilder.Capacity > MaxBuilderCapacity * 10) + { + stringBuilder.Remove(0, stringBuilder.Length - 1); // Attempt soft clear that skips re-allocation + stringBuilder.Capacity = MaxBuilderCapacity; + } + + stringBuilder.Length = 0; + } + } +} diff --git a/src/NLog.Targets.ConcurrentFile/Internal/ReusableObjectCreator.cs b/src/NLog.Targets.ConcurrentFile/Internal/ReusableObjectCreator.cs new file mode 100644 index 0000000000..4e02c4fe61 --- /dev/null +++ b/src/NLog.Targets.ConcurrentFile/Internal/ReusableObjectCreator.cs @@ -0,0 +1,92 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Internal +{ + using System; + + /// + /// Controls a single allocated object for reuse (only one active user) + /// + internal class ReusableObjectCreator where T : class + { + protected T _reusableObject; + private readonly Action _clearObject; + private readonly Func _createObject; + + protected ReusableObjectCreator(Func createObject, Action clearObject) + { + _reusableObject = createObject(); + _clearObject = clearObject; + _createObject = createObject; + } + + /// + /// Creates handle to the reusable char[]-buffer for active usage + /// + /// Handle to the reusable item, that can release it again + public LockOject Allocate() + { + var reusableObject = _reusableObject ?? _createObject(); + System.Diagnostics.Debug.Assert(_reusableObject != null); + _reusableObject = null; + return new LockOject(this, reusableObject); + } + + private void Deallocate(T reusableObject) + { + _clearObject(reusableObject); + _reusableObject = reusableObject; + } + + public struct LockOject : IDisposable + { + /// + /// Access the acquired reusable object + /// + public readonly T Result; + private readonly ReusableObjectCreator _owner; + + public LockOject(ReusableObjectCreator owner, T reusableObject) + { + Result = reusableObject; + _owner = owner; + } + + public void Dispose() + { + _owner?.Deallocate(Result); + } + } + } +} diff --git a/src/NLog.Targets.ConcurrentFile/Internal/ReusableStreamCreator.cs b/src/NLog.Targets.ConcurrentFile/Internal/ReusableStreamCreator.cs new file mode 100644 index 0000000000..d538f373bd --- /dev/null +++ b/src/NLog.Targets.ConcurrentFile/Internal/ReusableStreamCreator.cs @@ -0,0 +1,79 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Internal +{ + using System; + using System.IO; + + /// + /// Controls a single allocated MemoryStream for reuse (only one active user) + /// + internal sealed class ReusableStreamCreator : ReusableObjectCreator, IDisposable + { + public ReusableStreamCreator() + : base(() => new MemoryStream(4096), (ms) => ResetCapacity(ms)) + { + } + + public ReusableStreamCreator(bool batchStream) + : base(() => new MemoryStream(4096), (ms) => ResetBatchCapacity(ms)) + { + } + + private static void ResetCapacity(MemoryStream memoryStream) + { + memoryStream.Position = 0; + memoryStream.SetLength(0); + if (memoryStream.Capacity > 1000000) + { + memoryStream.Capacity = 81920; // Avoid Large-Object-Heap (LOH) + } + } + + private static void ResetBatchCapacity(MemoryStream memoryStream) + { + memoryStream.Position = 0; + memoryStream.SetLength(0); + if (memoryStream.Capacity > 10000000) + { + memoryStream.Capacity = 81920; // Avoid Large-Object-Heap (LOH) + } + } + + void IDisposable.Dispose() + { + _reusableObject.Dispose(); + } + } +} diff --git a/src/NLog.Targets.ConcurrentFile/Internal/SortHelpers.cs b/src/NLog.Targets.ConcurrentFile/Internal/SortHelpers.cs new file mode 100644 index 0000000000..7c7d7a6c5c --- /dev/null +++ b/src/NLog.Targets.ConcurrentFile/Internal/SortHelpers.cs @@ -0,0 +1,389 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Internal +{ + using System; + using System.Collections; + using System.Collections.Generic; + using NLog.Common; + + /// + /// Provides helpers to sort log events and associated continuations. + /// + internal static class SortHelpers + { + /// + /// Key selector delegate. + /// + /// The type of the value. + /// The type of the key. + /// Value to extract key information from. + /// Key selected from log event. + internal delegate TKey KeySelector(TValue value); + + /// + /// Performs bucket sort (group by) on an array of items and returns a dictionary for easy traversal of the result set. + /// + /// The type of the value. + /// The type of the key. + /// The inputs. + /// The key selector function. + /// + /// Dictionary where keys are unique input keys, and values are lists of . + /// + public static ReadOnlySingleBucketDictionary> BucketSort(this IList inputs, KeySelector keySelector) + { + return BucketSort(inputs, keySelector, EqualityComparer.Default); + } + + /// + /// Performs bucket sort (group by) on an array of items and returns a dictionary for easy traversal of the result set. + /// + /// The type of the value. + /// The type of the key. + /// The inputs. + /// The key selector function. + /// The key comparer function. + /// + /// Dictionary where keys are unique input keys, and values are lists of . + /// + public static ReadOnlySingleBucketDictionary> BucketSort(this IList inputs, KeySelector keySelector, IEqualityComparer keyComparer) + { + Dictionary> buckets = null; + TKey singleBucketKey = default(TKey); + for (int i = 0; i < inputs.Count; i++) + { + TKey keyValue = keySelector(inputs[i]); + if (i == 0) + { + singleBucketKey = keyValue; + } + else if (buckets is null) + { + if (!keyComparer.Equals(singleBucketKey, keyValue)) + { + // Multiple buckets needed, allocate full dictionary + buckets = CreateBucketDictionaryWithValue(inputs, keyComparer, i, singleBucketKey, keyValue); + } + } + else + { + if (!buckets.TryGetValue(keyValue, out var eventsInBucket)) + { + eventsInBucket = new List(); + buckets.Add(keyValue, eventsInBucket); + } + eventsInBucket.Add(inputs[i]); + } + } + + if (buckets is null) + return new ReadOnlySingleBucketDictionary>(new KeyValuePair>(singleBucketKey, inputs), keyComparer); + else + return new ReadOnlySingleBucketDictionary>(buckets, keyComparer); + } + + private static Dictionary> CreateBucketDictionaryWithValue(IList inputs, IEqualityComparer keyComparer, int currentIndex, TKey firstBucketKey, TKey nextBucketKey) + { + var buckets = new Dictionary>(keyComparer); + var firstBucket = new List(currentIndex); + for (int i = 0; i < currentIndex; i++) + { + firstBucket.Add(inputs[i]); + } + buckets[firstBucketKey] = firstBucket; + + var nextBucket = new List { inputs[currentIndex] }; + buckets[nextBucketKey] = nextBucket; + return buckets; + } + + /// + /// Single-Bucket optimized readonly dictionary. Uses normal internally Dictionary if multiple buckets are needed. + /// + /// Avoids allocating a new dictionary, when all items are using the same bucket + /// + /// The type of the key. + /// The type of the value. + public struct ReadOnlySingleBucketDictionary : IDictionary + { + KeyValuePair? _singleBucket; // Not readonly to avoid struct-copy, and to avoid VerificationException when medium-trust AppDomain + readonly Dictionary _multiBucket; + readonly IEqualityComparer _comparer; + public IEqualityComparer Comparer => _comparer; + + public ReadOnlySingleBucketDictionary(KeyValuePair singleBucket) + : this(singleBucket, EqualityComparer.Default) + { + } + + public ReadOnlySingleBucketDictionary(Dictionary multiBucket) + : this(multiBucket, EqualityComparer.Default) + { + } + + public ReadOnlySingleBucketDictionary(KeyValuePair singleBucket, IEqualityComparer comparer) + { + _comparer = comparer; + _multiBucket = null; + _singleBucket = singleBucket; + } + + public ReadOnlySingleBucketDictionary(Dictionary multiBucket, IEqualityComparer comparer) + { + _comparer = comparer; + _multiBucket = multiBucket; + _singleBucket = default(KeyValuePair); + } + + /// + public int Count { get { if (_multiBucket != null) return _multiBucket.Count; else if (_singleBucket.HasValue) return 1; else return 0; } } + + /// + public ICollection Keys + { + get + { + if (_multiBucket != null) + return _multiBucket.Keys; + else if (_singleBucket.HasValue) + return new[] { _singleBucket.Value.Key }; + else + return (ICollection)System.Linq.Enumerable.Empty(); + } + } + + /// + public ICollection Values + { + get + { + if (_multiBucket != null) + return _multiBucket.Values; + else if (_singleBucket.HasValue) + return new TValue[] { _singleBucket.Value.Value }; + else + return (ICollection)System.Linq.Enumerable.Empty(); + } + } + + /// + public bool IsReadOnly => true; + + /// + /// Allows direct lookup of existing keys. If trying to access non-existing key exception is thrown. + /// Consider to use instead for better safety. + /// + /// Key value for lookup + /// Mapped value found + public TValue this[TKey key] + { + get + { + if (_multiBucket != null) + return _multiBucket[key]; + else if (_singleBucket.HasValue && _comparer.Equals(_singleBucket.Value.Key, key)) + return _singleBucket.Value.Value; + else + throw new KeyNotFoundException(); + } + set => throw new NotSupportedException("Readonly"); + } + + /// + /// Non-Allocating struct-enumerator + /// + public struct Enumerator : IEnumerator> + { + bool _singleBucketFirstRead; + KeyValuePair _singleBucket; // Not readonly to avoid struct-copy, and to avoid VerificationException when medium-trust AppDomain + readonly IEnumerator> _multiBuckets; + + internal Enumerator(Dictionary multiBucket) + { + _singleBucketFirstRead = false; + _singleBucket = default(KeyValuePair); + _multiBuckets = multiBucket.GetEnumerator(); + } + + internal Enumerator(KeyValuePair singleBucket) + { + _singleBucketFirstRead = false; + _singleBucket = singleBucket; + _multiBuckets = null; + } + + public KeyValuePair Current + { + get + { + if (_multiBuckets != null) + return new KeyValuePair(_multiBuckets.Current.Key, _multiBuckets.Current.Value); + else + return new KeyValuePair(_singleBucket.Key, _singleBucket.Value); + } + } + + object IEnumerator.Current => Current; + + public void Dispose() + { + if (_multiBuckets != null) + _multiBuckets.Dispose(); + } + + public bool MoveNext() + { + if (_multiBuckets != null) + return _multiBuckets.MoveNext(); + else if (_singleBucketFirstRead) + return false; + else + return _singleBucketFirstRead = true; + + } + + public void Reset() + { + if (_multiBuckets != null) + _multiBuckets.Reset(); + else + _singleBucketFirstRead = false; + } + } + + public Enumerator GetEnumerator() + { + if (_multiBucket != null) + return new Enumerator(_multiBucket); + else if (_singleBucket.HasValue) + return new Enumerator(_singleBucket.Value); + else + return new Enumerator(new Dictionary()); + } + + /// + IEnumerator> IEnumerable>.GetEnumerator() + { + return GetEnumerator(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + public bool ContainsKey(TKey key) + { + if (_multiBucket != null) + return _multiBucket.ContainsKey(key); + else if (_singleBucket.HasValue) + return _comparer.Equals(_singleBucket.Value.Key, key); + else + return false; + } + + /// Will always throw, as dictionary is readonly + public void Add(TKey key, TValue value) + { + throw new NotSupportedException(); // Readonly + } + + /// Will always throw, as dictionary is readonly + public bool Remove(TKey key) + { + throw new NotSupportedException(); // Readonly + } + + /// + public bool TryGetValue(TKey key, out TValue value) + { + if (_multiBucket != null) + { + return _multiBucket.TryGetValue(key, out value); + } + else if (_singleBucket.HasValue && _comparer.Equals(_singleBucket.Value.Key, key)) + { + value = _singleBucket.Value.Value; + return true; + } + else + { + value = default(TValue); + return false; + } + } + + /// Will always throw, as dictionary is readonly + public void Add(KeyValuePair item) + { + throw new NotSupportedException(); // Readonly + } + + /// Will always throw, as dictionary is readonly + public void Clear() + { + throw new NotSupportedException(); // Readonly + } + + /// + public bool Contains(KeyValuePair item) + { + if (_multiBucket != null) + return ((IDictionary)_multiBucket).Contains(item); + else if (_singleBucket.HasValue) + return _comparer.Equals(_singleBucket.Value.Key, item.Key) && EqualityComparer.Default.Equals(_singleBucket.Value.Value, item.Value); + else + return false; + } + + /// + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (_multiBucket != null) + ((IDictionary)_multiBucket).CopyTo(array, arrayIndex); + else if (_singleBucket.HasValue) + array[arrayIndex] = _singleBucket.Value; + } + + /// Will always throw, as dictionary is readonly + public bool Remove(KeyValuePair item) + { + throw new NotSupportedException(); // Readonly + } + } + } +} diff --git a/src/NLog/Internal/StreamHelpers.cs b/src/NLog.Targets.ConcurrentFile/Internal/StreamHelpers.cs similarity index 70% rename from src/NLog/Internal/StreamHelpers.cs rename to src/NLog.Targets.ConcurrentFile/Internal/StreamHelpers.cs index 13b29e63d1..9f9186645c 100644 --- a/src/NLog/Internal/StreamHelpers.cs +++ b/src/NLog.Targets.ConcurrentFile/Internal/StreamHelpers.cs @@ -31,19 +31,24 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.IO; -using System.Linq; -using System.Text; -using NLog.Common; - namespace NLog.Internal { + using System; + using System.IO; + using System.Linq; + using System.Text; + using NLog.Common; + /// /// Stream helpers /// internal static class StreamHelpers { + /// + /// UTF-8 Byte Order Mark (BOM) bytes: EF BB BF (239, 187, 191). + /// + private static readonly byte[] Utf8BOM = Encoding.UTF8.GetPreamble(); + /// /// Copy to output stream and skip BOM if encoding is UTF8 /// @@ -52,13 +57,13 @@ internal static class StreamHelpers /// public static void CopyAndSkipBom(this Stream input, Stream output, Encoding encoding) { - var bomSize = EncodingHelpers.Utf8BOM.Length; + var bomSize = Utf8BOM.Length; var bomBuffer = new byte[bomSize]; var posBefore = input.Position; int bytesRead = input.Read(bomBuffer, 0, bomSize); //TODO support other BOMs, like UTF16 - if (bytesRead == bomSize && bomBuffer.SequenceEqual(EncodingHelpers.Utf8BOM)) + if (bytesRead == bomSize && bomBuffer.SequenceEqual(Utf8BOM)) { InternalLogger.Debug("input has UTF8 BOM"); //already skipped due to read @@ -111,5 +116,32 @@ public static void CopyWithOffset(this Stream input, Stream output, int offset) output.Write(buffer, 0, read); } } + + /// + /// Copies the contents of the StringBuilder to the MemoryStream using the specified encoding (Without BOM/Preamble) + /// + /// StringBuilder source + /// MemoryStream destination + /// Encoding used for converter string into byte-stream + /// Helper char-buffer to minimize memory allocations + public static void CopyToStream(this StringBuilder builder, MemoryStream ms, Encoding encoding, char[] transformBuffer) + { + int charCount; + int byteCount = encoding.GetMaxByteCount(builder.Length); + long position = ms.Position; + ms.SetLength(position + byteCount); + for (int i = 0; i < builder.Length; i += transformBuffer.Length) + { + charCount = Math.Min(builder.Length - i, transformBuffer.Length); + builder.CopyTo(i, transformBuffer, 0, charCount); + byteCount = encoding.GetBytes(transformBuffer, 0, charCount, ms.GetBuffer(), (int)position); + position += byteCount; + } + ms.Position = position; + if (position != ms.Length) + { + ms.SetLength(position); + } + } } } diff --git a/src/NLog.Targets.ConcurrentFile/Internal/StringHelpers.cs b/src/NLog.Targets.ConcurrentFile/Internal/StringHelpers.cs new file mode 100644 index 0000000000..29ec718e68 --- /dev/null +++ b/src/NLog.Targets.ConcurrentFile/Internal/StringHelpers.cs @@ -0,0 +1,73 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets +{ + using System.Text; + + internal static class StringHelpers + { + /// + /// IsNullOrWhiteSpace, including for .NET 3.5 + /// + internal static bool IsNullOrWhiteSpace(string value) + { +#if !NET35 + return string.IsNullOrWhiteSpace(value); +#else + if (value is null || value.Length == 0) + return true; + return string.IsNullOrEmpty(value.Trim()); +#endif + } + + /// + /// Compares the contents of a StringBuilder and a String + /// + /// True when content is the same + public static bool EqualTo(this StringBuilder builder, string other) + { + if (builder.Length != other.Length) + return false; + + int i = 0; + foreach (var chr in other) + { + if (builder[i++] != chr) + return false; + } + + return true; + } + } +} diff --git a/src/NLog/Internal/Win32FileNativeMethods.cs b/src/NLog.Targets.ConcurrentFile/Internal/Win32FileNativeMethods.cs similarity index 100% rename from src/NLog/Internal/Win32FileNativeMethods.cs rename to src/NLog.Targets.ConcurrentFile/Internal/Win32FileNativeMethods.cs diff --git a/src/NLog/Targets/ZipArchiveFileCompressor.cs b/src/NLog.Targets.ConcurrentFile/Internal/ZipArchiveFileCompressor.cs similarity index 93% rename from src/NLog/Targets/ZipArchiveFileCompressor.cs rename to src/NLog.Targets.ConcurrentFile/Internal/ZipArchiveFileCompressor.cs index 2fc0cd42e6..eaf67aa6d0 100644 --- a/src/NLog/Targets/ZipArchiveFileCompressor.cs +++ b/src/NLog.Targets.ConcurrentFile/Internal/ZipArchiveFileCompressor.cs @@ -40,8 +40,8 @@ namespace NLog.Targets /// /// Builtin IFileCompressor implementation utilizing the .Net4.5 specific - /// and is used as the default value for on .Net4.5. - /// So log files created via can be zipped when archived + /// and is used as the default value for on .Net4.5. + /// So log files created via can be zipped when archived /// w/o 3rd party zip library when run on .Net4.5 or higher. /// internal sealed class ZipArchiveFileCompressor : IArchiveFileCompressor diff --git a/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj b/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj new file mode 100644 index 0000000000..b30bdca3ad --- /dev/null +++ b/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj @@ -0,0 +1,93 @@ + + + + net46;netstandard2.0 + + NLog.Targets.ConcurrentFile + NLog + FileTarget with support for ConcurrentWrites where multiple processes can write to the same file + NLog.Targets.ConcurrentFile v$(ProductVersion) + $(ProductVersion) + Jarek Kowalski,Kim Christensen,Julian Verdurmen + $([System.DateTime]::Now.ToString(yyyy)) + Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ + + + FileTarget Docs: + https://github.com/NLog/NLog/wiki/File-target + + README.md + NLog;File;Archive;Compression;Zip;NTFS;logging;log + N.png + https://nlog-project.org/ + BSD-3-Clause + git + https://github.com/NLog/NLog.git + + true + 5.0.0.0 + ..\NLog.snk + true + + true + true + true + true + true + copyused + true + + + + NLog.Targets.ConcurrentFile for .NET Framework 4.6 + true + + + + NLog.Targets.ConcurrentFile for .NET Framework 4.5 + true + + + + NLog.Targets.ConcurrentFile for .NET Framework 3.5 + true + + + + NLog.Targets.ConcurrentFile for NetStandard 2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NLog.Targets.ConcurrentFile/Properties/AssemblyInfo.cs b/src/NLog.Targets.ConcurrentFile/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..b905c62788 --- /dev/null +++ b/src/NLog.Targets.ConcurrentFile/Properties/AssemblyInfo.cs @@ -0,0 +1,49 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +[assembly: AssemblyCulture("")] +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] +[assembly: InternalsVisibleTo("NLog.Targets.ConcurrentFile.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100ef8eab4fbdeb511eeb475e1659fe53f00ec1c1340700f1aa347bf3438455d71993b28b1efbed44c8d97a989e0cb6f01bcb5e78f0b055d311546f63de0a969e04cf04450f43834db9f909e566545a67e42822036860075a1576e90e1c43d43e023a24c22a427f85592ae56cac26f13b7ec2625cbc01f9490d60f16cfbb1bc34d9")] +[assembly: AllowPartiallyTrustedCallers] +#if !NET35 +[assembly: SecurityRules(SecurityRuleSet.Level1)] +#endif +// NSubstitute +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/NLog.Targets.ConcurrentFile/README.md b/src/NLog.Targets.ConcurrentFile/README.md new file mode 100644 index 0000000000..9cb8cba78e --- /dev/null +++ b/src/NLog.Targets.ConcurrentFile/README.md @@ -0,0 +1,25 @@ +# NLog Concurrent File Target + +NLog File Target with support for ConcurrentWrites where multiple processes can write to the same file. + +If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) + +See the [NLog Wiki](https://github.com/NLog/NLog/wiki/File-target) for available options and examples. + +## Register Extension + +NLog will only recognize type-alias `File` when loading from `NLog.config`-file, if having added extension to `NLog.config`-file: + +```xml + + + +``` + +Alternative register from code using [fluent configuration API](https://github.com/NLog/NLog/wiki/Fluent-Configuration-API): + +```csharp +LogManager.Setup().SetupExtensions(ext => { + ext.RegisterTarget(); +}); +``` diff --git a/src/NLog/Targets/Win32FileAttributes.cs b/src/NLog.Targets.ConcurrentFile/Win32FileAttributes.cs similarity index 100% rename from src/NLog/Targets/Win32FileAttributes.cs rename to src/NLog.Targets.ConcurrentFile/Win32FileAttributes.cs diff --git a/src/NLog.sln b/src/NLog.sln index 8d5144d3cb..a0a66686bb 100644 --- a/src/NLog.sln +++ b/src/NLog.sln @@ -30,9 +30,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\CHANGELOG.md = ..\CHANGELOG.md ..\.github\CONTRIBUTING.md = ..\.github\CONTRIBUTING.md ..\.github\dependabot.yml = ..\.github\dependabot.yml + NLog.proj = NLog.proj ..\README.md = ..\README.md ..\run-tests.ps1 = ..\run-tests.ps1 ..\SECURITY.md = ..\SECURITY.md + ..\Test-XmlFile.ps1 = ..\Test-XmlFile.ps1 EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.WindowsRegistry", "NLog.WindowsRegistry\NLog.WindowsRegistry.csproj", "{BFE365AA-A01F-4E8B-B540-3F8DF0466CE6}" @@ -67,6 +69,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.AutoReloadConfig", "NL EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.AutoReloadConfig.Tests", "..\tests\NLog.AutoReloadConfig.Tests\NLog.AutoReloadConfig.Tests.csproj", "{47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.ConcurrentFile", "NLog.Targets.ConcurrentFile\NLog.Targets.ConcurrentFile.csproj", "{C54C900A-2FAC-4482-BFFB-D4A9F6AB3619}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.ConcurrentFile.Tests", "..\tests\NLog.Targets.ConcurrentFile.Tests\NLog.Targets.ConcurrentFile.Tests.csproj", "{A869B720-AB81-4AA9-94B4-12C5CF490FFE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -161,6 +167,14 @@ Global {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}.Debug|Any CPU.Build.0 = Debug|Any CPU {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}.Release|Any CPU.ActiveCfg = Release|Any CPU {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}.Release|Any CPU.Build.0 = Release|Any CPU + {C54C900A-2FAC-4482-BFFB-D4A9F6AB3619}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C54C900A-2FAC-4482-BFFB-D4A9F6AB3619}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C54C900A-2FAC-4482-BFFB-D4A9F6AB3619}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C54C900A-2FAC-4482-BFFB-D4A9F6AB3619}.Release|Any CPU.Build.0 = Release|Any CPU + {A869B720-AB81-4AA9-94B4-12C5CF490FFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A869B720-AB81-4AA9-94B4-12C5CF490FFE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A869B720-AB81-4AA9-94B4-12C5CF490FFE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A869B720-AB81-4AA9-94B4-12C5CF490FFE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -186,6 +200,8 @@ Global {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {FB2DFD9F-5EB1-4BBF-99F3-38C2E997206E} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {C54C900A-2FAC-4482-BFFB-D4A9F6AB3619} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {A869B720-AB81-4AA9-94B4-12C5CF490FFE} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6B4C8E9F-1E2F-4AB3-993A-8776CFA08DC0} diff --git a/src/NLog/Config/LoggingConfigurationFileLoader.cs b/src/NLog/Config/LoggingConfigurationFileLoader.cs index ea2b137124..d94a2c4a84 100644 --- a/src/NLog/Config/LoggingConfigurationFileLoader.cs +++ b/src/NLog/Config/LoggingConfigurationFileLoader.cs @@ -41,7 +41,6 @@ namespace NLog.Config using NLog.Common; using NLog.Internal; using NLog.Internal.Fakeables; - using NLog.Targets; /// /// Enables loading of NLog configuration from a file @@ -66,7 +65,7 @@ public LoggingConfiguration Load(LogFactory logFactory, string filename = null) } #endif - if (string.IsNullOrEmpty(filename) || FilePathLayout.DetectFilePathKind(filename) == FilePathKind.Relative) + if (string.IsNullOrEmpty(filename) || FileInfoHelper.IsRelativeFilePath(filename)) { return TryLoadFromFilePaths(logFactory, filename); } diff --git a/src/NLog/Internal/FileInfoHelper.cs b/src/NLog/Internal/FileInfoHelper.cs index 4c57775f22..9cdd47636c 100644 --- a/src/NLog/Internal/FileInfoHelper.cs +++ b/src/NLog/Internal/FileInfoHelper.cs @@ -31,13 +31,19 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; - namespace NLog.Internal { + using System; + using System.IO; + internal static class FileInfoHelper { - internal static DateTime? LookupValidFileCreationTimeUtc(T fileInfo, Func primary, Func fallback, Func finalFallback = null) + public static DateTime? LookupValidFileCreationTimeUtc(this FileInfo fileInfo) + { + return LookupValidFileCreationTimeUtc(fileInfo, (f) => f.CreationTimeUtc, (f) => f.LastWriteTimeUtc); + } + + private static DateTime? LookupValidFileCreationTimeUtc(T fileInfo, Func primary, Func fallback, Func finalFallback = null) { DateTime? fileCreationTime = primary(fileInfo); @@ -52,5 +58,27 @@ internal static class FileInfoHelper } return fileCreationTime; } + + public static bool IsRelativeFilePath(string filepath) + { + if (filepath?.Length > 0) + filepath = filepath.TrimStart(ArrayHelper.Empty()); + + if (string.IsNullOrEmpty(filepath)) + return false; + + var firstchar = filepath[0]; + if (firstchar == Path.DirectorySeparatorChar || firstchar == Path.AltDirectorySeparatorChar) + return false; + + if (firstchar == '.') + return true; + + //on unix VolumeSeparatorChar == DirectorySeparatorChar + if (filepath.Length >= 2 && filepath[1] == Path.VolumeSeparatorChar && Path.VolumeSeparatorChar != Path.DirectorySeparatorChar) + return false; + + return true; + } } } diff --git a/src/NLog/Internal/StringHelpers.cs b/src/NLog/Internal/StringHelpers.cs index 8a2a0788e1..e403cc303a 100644 --- a/src/NLog/Internal/StringHelpers.cs +++ b/src/NLog/Internal/StringHelpers.cs @@ -57,7 +57,7 @@ internal static bool IsNullOrWhiteSpace(string value) #else if (value is null) return true; if (value.Length == 0) return true; - return String.IsNullOrEmpty(value.Trim()); + return string.IsNullOrEmpty(value.Trim()); #endif } diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index c112b77521..a1418cbc14 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -111,7 +111,6 @@ For all config options and platform support, check https://nlog-project.org/conf - @@ -119,7 +118,6 @@ For all config options and platform support, check https://nlog-project.org/conf - diff --git a/src/NLog/SetupLoadConfigurationExtensions.cs b/src/NLog/SetupLoadConfigurationExtensions.cs index 45ed67615e..f02870bab5 100644 --- a/src/NLog/SetupLoadConfigurationExtensions.cs +++ b/src/NLog/SetupLoadConfigurationExtensions.cs @@ -570,11 +570,10 @@ public static void WriteToDebugConditional(this ISetupConfigurationTargetBuilder /// Override the default Encoding for output (Default = UTF8) /// Override the default line ending characters (Ex. without CR) /// Keep log file open instead of opening and closing it on each logging event - /// Activate multi-process synchronization using global mutex on the operating system /// Size in bytes where log files will be automatically archived. /// Maximum number of archive files that should be kept. /// Maximum days of archive files that should be kept. - public static ISetupConfigurationTargetBuilder WriteToFile(this ISetupConfigurationTargetBuilder configBuilder, Layout fileName, Layout layout = null, System.Text.Encoding encoding = null, LineEndingMode lineEnding = null, bool keepFileOpen = true, bool concurrentWrites = false, long archiveAboveSize = 0, int maxArchiveFiles = 0, int maxArchiveDays = 0) + public static ISetupConfigurationTargetBuilder WriteToFile(this ISetupConfigurationTargetBuilder configBuilder, Layout fileName, Layout layout = null, System.Text.Encoding encoding = null, LineEndingMode lineEnding = null, bool keepFileOpen = true, long archiveAboveSize = 0, int maxArchiveFiles = 0, int maxArchiveDays = 0) { Guard.ThrowIfNull(fileName); @@ -587,7 +586,6 @@ public static ISetupConfigurationTargetBuilder WriteToFile(this ISetupConfigurat if (lineEnding != null) fileTarget.LineEnding = lineEnding; fileTarget.KeepFileOpen = keepFileOpen; - fileTarget.ConcurrentWrites = concurrentWrites; fileTarget.ArchiveAboveSize = archiveAboveSize; fileTarget.MaxArchiveFiles = maxArchiveFiles; fileTarget.MaxArchiveDays = maxArchiveDays; diff --git a/src/NLog/Targets/FileAppenders/DiscardAllFileAppender.cs b/src/NLog/Targets/FileAppenders/DiscardAllFileAppender.cs new file mode 100644 index 0000000000..868d6b1df0 --- /dev/null +++ b/src/NLog/Targets/FileAppenders/DiscardAllFileAppender.cs @@ -0,0 +1,75 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets.FileAppenders +{ + using System; + + internal sealed class DiscardAllFileAppender : IFileAppender + { + public string FilePath { get; } + + public DateTime OpenStreamTime { get; } + + public DateTime FileLastModified => OpenStreamTime; + + public DateTime NextArchiveTime => DateTime.MaxValue; + + public long FileSize => 0; + + public DiscardAllFileAppender(string filePath) + { + FilePath = filePath; + OpenStreamTime = Time.TimeSource.Current.Time; + } + + public void Write(byte[] buffer, int offset, int count) + { + // SONAR: Nothing to write + } + + public void Flush() + { + // SONAR: Nothing to flush + } + + public void Dispose() + { + // SONAR: Nothing to close + } + + public bool VerifyFileExists() => true; + + public override string ToString() => FilePath; + } +} diff --git a/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs new file mode 100644 index 0000000000..eec6870653 --- /dev/null +++ b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs @@ -0,0 +1,205 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets.FileAppenders +{ + using System; + using System.IO; + using NLog.Common; + using NLog.Internal; + +#if NETFRAMEWORK + [System.Security.SecuritySafeCritical] +#endif + internal sealed class ExclusiveFileLockingAppender : IFileAppender + { + private readonly FileTarget _fileTarget; + private readonly string _filePath; + private Stream _fileStream; + private int _lastFileDeletedCheck; + private long? _countedFileSize; + + public string FilePath => _filePath; + + public DateTime OpenStreamTime { get; } + + public DateTime FileLastModified { get; private set; } + + private DateTime FileBirthTime + { + get => _fileBirthTime ?? OpenStreamTime; + set => _fileBirthTime = value; + } + private DateTime? _fileBirthTime; + + public DateTime NextArchiveTime + { + get + { + var fileBirthTime = FileBirthTime; + if (_lastFileBirthTime != fileBirthTime) + { + _nextArchiveTime = FileTarget.CalculateNextArchiveEventTime(_fileTarget.ArchiveEvery, fileBirthTime); + _lastFileBirthTime = fileBirthTime; + } + return _nextArchiveTime; + } + } + private DateTime _nextArchiveTime; + private DateTime _lastFileBirthTime; + + public long FileSize => _countedFileSize ?? _fileStream.Length; + + public ExclusiveFileLockingAppender(FileTarget fileTarget, string filePath) + { + _fileTarget = fileTarget; + _filePath = filePath; + OpenStreamTime = Time.TimeSource.Current.Time; + _lastFileDeletedCheck = Environment.TickCount; + + RefreshFileBirthTimeUtc(); + + _fileStream = _fileTarget.CreateFileStreamWithRetry(this, fileTarget.BufferSize, initialFileOpen: true); + _countedFileSize = RefreshCountedFileSize(); + } + + private bool SkipRefreshFileBirthTime() + { + return (_fileTarget.ArchiveFileName is null && _fileTarget.ArchiveEvery == FileArchivePeriod.None); + } + + private void RefreshFileBirthTimeUtc() + { + FileLastModified = NLog.Time.TimeSource.Current.Time; + + if (SkipRefreshFileBirthTime()) + return; + + try + { + _fileBirthTime = null; + + FileInfo fileInfo = new FileInfo(_filePath); + if (fileInfo.Exists && fileInfo.Length != 0) + { + var fileBirthTimeUtc = FileInfoHelper.LookupValidFileCreationTimeUtc(fileInfo) ?? DateTime.MinValue; + var fileBirthTime = fileBirthTimeUtc != DateTime.MinValue ? NLog.Time.TimeSource.Current.FromSystemTime(fileBirthTimeUtc) : OpenStreamTime; + if (!_fileBirthTime.HasValue || _fileBirthTime.Value < fileBirthTime) + FileBirthTime = fileBirthTime; + FileLastModified = NLog.Time.TimeSource.Current.FromSystemTime(fileInfo.LastWriteTimeUtc); + } + } + catch (Exception ex) + { + InternalLogger.Debug(ex, "{0}: Failed to refresh BirthTime for file: '{1}'", _fileTarget, _filePath); + } + } + + public void Write(byte[] buffer, int offset, int count) + { + var lastFileSizeCheck = Environment.TickCount - _lastFileDeletedCheck; + if (lastFileSizeCheck > 1000 || lastFileSizeCheck < -1000) + { + MonitorFileHasBeenDeleted(); + _lastFileDeletedCheck = Environment.TickCount; + if (!SkipRefreshFileBirthTime()) + FileLastModified = NLog.Time.TimeSource.Current.Time; + } + + _fileStream.Write(buffer, offset, count); + if (_countedFileSize.HasValue) + _countedFileSize += count; + } + + public void Flush() + { + _fileStream.Flush(); + } + + public void Dispose() + { + SafeCloseFile(_filePath, ref _fileStream); + } + + public bool VerifyFileExists() + { + return SafeFileExists(_filePath); + } + + private void MonitorFileHasBeenDeleted() + { + if (!SafeFileExists(_filePath)) + { + InternalLogger.Debug("{0}: Recreating FileStream because no longer File.Exists: '{1}'", _fileTarget, _filePath); + SafeCloseFile(_filePath, ref _fileStream); + _fileStream = _fileTarget.CreateFileStreamWithRetry(this, _fileTarget.BufferSize, initialFileOpen: false); + _countedFileSize = RefreshCountedFileSize(); + RefreshFileBirthTimeUtc(); + } + } + + private long? RefreshCountedFileSize() + { + return (_fileTarget.ArchiveAboveSize > 0 && _fileStream is FileStream) ? _fileStream.Length : default(long?); + } + + private void SafeCloseFile(string filepath, ref Stream fileStream) + { + try + { + var stream = fileStream; + fileStream = null; + stream?.Dispose(); + } + catch (Exception ex) + { + InternalLogger.Error(ex, "{0}: Failed to close file: '{1}'", _fileTarget, filepath); + } + } + + private bool SafeFileExists(string filepath) + { + try + { + return File.Exists(filepath); + } + catch (Exception ex) + { + InternalLogger.Error(ex, "{0}: Failed to check if File.Exists: '{1}'", _fileTarget, filepath); + return false; + } + } + + public override string ToString() => _filePath; + } +} diff --git a/src/NLog/Targets/FileAppenders/IFileAppender.cs b/src/NLog/Targets/FileAppenders/IFileAppender.cs new file mode 100644 index 0000000000..26d42fbeaa --- /dev/null +++ b/src/NLog/Targets/FileAppenders/IFileAppender.cs @@ -0,0 +1,62 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets.FileAppenders +{ + using System; + + /// + /// Handles the actual file-operations on disk + /// + internal interface IFileAppender : IDisposable + { + string FilePath { get; } + long FileSize { get; } + + DateTime OpenStreamTime { get; } + DateTime FileLastModified { get; } + DateTime NextArchiveTime { get; } + + /// + /// Writes the specified bytes to a file. + /// + /// The bytes array. + /// The bytes array offset. + /// The number of bytes. + void Write(byte[] buffer, int offset, int count); + + void Flush(); + + bool VerifyFileExists(); + } +} diff --git a/src/NLog/Targets/FileAppenders/MinimalFileLockingAppender.cs b/src/NLog/Targets/FileAppenders/MinimalFileLockingAppender.cs new file mode 100644 index 0000000000..a1e336e9d2 --- /dev/null +++ b/src/NLog/Targets/FileAppenders/MinimalFileLockingAppender.cs @@ -0,0 +1,144 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets.FileAppenders +{ + using System; + using System.IO; + using NLog.Internal; + +#if NETFRAMEWORK + [System.Security.SecuritySafeCritical] +#endif + internal sealed class MinimalFileLockingAppender : IFileAppender + { + private readonly FileTarget _fileTarget; + private readonly string _filePath; + private bool _initialFileOpen; + + public string FilePath => _filePath; + + public DateTime OpenStreamTime { get; } + + public DateTime FileLastModified + { + get + { + var fileInfo = new FileInfo(_filePath); + if (fileInfo.Exists && fileInfo.Length != 0) + { + return Time.TimeSource.Current.FromSystemTime(fileInfo.LastWriteTimeUtc); + } + return OpenStreamTime; + } + } + + public DateTime NextArchiveTime + { + get + { + if (_nextArchiveTime < NLog.Time.TimeSource.Current.Time.AddMinutes(1) || _lastFileBirthTimeUtc == DateTime.MinValue) + { + var fileInfo = new FileInfo(_filePath); + var fileBirthTimeUtc = (fileInfo.Exists && fileInfo.Length != 0) ? (FileInfoHelper.LookupValidFileCreationTimeUtc(fileInfo) ?? DateTime.MinValue) : DateTime.MinValue; + if (fileBirthTimeUtc == DateTime.MinValue || _lastFileBirthTimeUtc < fileBirthTimeUtc) + { + var fileBirthTime = fileBirthTimeUtc != DateTime.MinValue ? NLog.Time.TimeSource.Current.FromSystemTime(fileBirthTimeUtc) : OpenStreamTime; + _nextArchiveTime = FileTarget.CalculateNextArchiveEventTime(_fileTarget.ArchiveEvery, fileBirthTime); + _lastFileBirthTimeUtc = fileBirthTimeUtc; + } + } + return _nextArchiveTime; + } + } + private DateTime _nextArchiveTime; + private DateTime _lastFileBirthTimeUtc; + + public long FileSize + { + get + { + var fileInfo = new FileInfo(_filePath); + var fileSize = fileInfo.Exists ? fileInfo.Length : 0; + return fileSize; + } + } + + public MinimalFileLockingAppender(FileTarget fileTarget, string filePath) + { + _fileTarget = fileTarget; + _filePath = filePath; + _initialFileOpen = true; + OpenStreamTime = Time.TimeSource.Current.Time; + } + + public void Write(byte[] buffer, int offset, int count) + { + int overrideBufferSize = Math.Min((count / 4096 + 1) * 4096, _fileTarget.BufferSize); + + var initialFileOpen = _initialFileOpen; + _initialFileOpen = false; + + using (var fileStream = _fileTarget.CreateFileStreamWithRetry(this, overrideBufferSize, initialFileOpen)) + { + fileStream.Write(buffer, offset, count); + + if (_fileTarget.ReplaceFileContentsOnEachWrite) + { + var footerBytes = _fileTarget.GetFooterLayoutBytes(); + if (footerBytes?.Length > 0) + { + fileStream.Write(footerBytes, 0, footerBytes.Length); + } + } + } + } + + public void Dispose() + { + // Nothing to dispose + } + + public void Flush() + { + // Nothing to flush + } + + public bool VerifyFileExists() + { + return FileSize != 0; + } + + public override string ToString() => _filePath; + } +} diff --git a/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs b/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs new file mode 100644 index 0000000000..14ae7cdb95 --- /dev/null +++ b/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs @@ -0,0 +1,378 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets.FileArchiveHandlers +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Threading; + using NLog.Common; + using NLog.Internal; + + internal class BaseFileArchiveHandler + { + protected readonly FileTarget _fileTarget; + + public BaseFileArchiveHandler(FileTarget fileTarget) + { + _fileTarget = fileTarget; + } + + protected bool DeleteOldFilesBeforeArchive(string filePath, bool initialFileOpen, string excludeFileName = null) + { + // Get all files matching the filename, order by timestamp, and when same timestamp then order by filename + // - First start with removing the oldest files + string fileDirectory = Path.GetDirectoryName(filePath); + // Replace all non-letter with '*' replace all '**' with single '*' + string fileWildcard = GetFileNameWildcard(filePath); + return DeleteOldFilesBeforeArchive(fileDirectory, fileWildcard, initialFileOpen, excludeFileName); + } + + protected bool DeleteOldFilesBeforeArchive(string fileDirectory, string fileWildcard, bool initialFileOpen, string excludeFileName = null, bool wildCardContainsSeqNo = false) + { + try + { + if (string.IsNullOrEmpty(fileWildcard)) + return false; + + if (string.IsNullOrEmpty(fileDirectory)) + return false; + + var directoryInfo = new DirectoryInfo(fileDirectory); + if (!directoryInfo.Exists) + return false; + + var fileInfos = directoryInfo.GetFiles(fileWildcard); + InternalLogger.Debug("{0}: Archive Cleanup found {1} files matching wildcard {2} in directory: {3}", _fileTarget, fileInfos.Length, fileWildcard, fileDirectory); + if (fileInfos.Length != 0) + { + int fileWildcardStartIndex = fileWildcard.IndexOf('*'); + int fileWildcardEndIndex = fileWildcard.Length - fileWildcardStartIndex; + var maxArchiveFiles = _fileTarget.MaxArchiveFiles; + if (initialFileOpen && (!_fileTarget.ArchiveOldFileOnStartup || _fileTarget.DeleteOldFileOnStartup)) + maxArchiveFiles = _fileTarget.DeleteOldFileOnStartup ? 0 : maxArchiveFiles; + else if (maxArchiveFiles > 0) + maxArchiveFiles -= 1; + + bool oldFilesDeleted = false; + foreach (var cleanupFileInfo in FileInfoDateTime.CleanupFiles(fileInfos, maxArchiveFiles, _fileTarget.MaxArchiveDays, fileWildcardStartIndex, fileWildcardEndIndex, excludeFileName, wildCardContainsSeqNo)) + { + oldFilesDeleted = true; + DeleteOldArchiveFile(cleanupFileInfo.FullName); + } + + return oldFilesDeleted; + } + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "{0}: Failed to cleanup archive folder: {1} {2}", _fileTarget, fileDirectory, fileWildcard ?? ""); + if (exception.MustBeRethrown(_fileTarget)) + throw; + } + + return false; + } + + protected static int? GetMaxArchiveSequenceNo(FileInfo[] fileInfos, int fileWildcardStartIndex, int fileWildcardEndIndex) + { + return FileInfoDateTime.GetMaxArchiveSequenceNo(fileInfos, fileWildcardStartIndex, fileWildcardEndIndex); + } + + struct FileInfoDateTime : IComparer + { + public FileInfo FileInfo { get; } + public DateTime FileCreatedTimeUtc { get; } + public int? ArchiveSequenceNumber { get; } + + public FileInfoDateTime(FileInfo fileInfo, DateTime fileCreatedTimeUtc, int? archiveSequenceNumber = null) + { + FileInfo = fileInfo; + ArchiveSequenceNumber = archiveSequenceNumber; + FileCreatedTimeUtc = fileCreatedTimeUtc; + } + + public int Compare(FileInfoDateTime x, FileInfoDateTime y) + { + if (x.ArchiveSequenceNumber.HasValue && y.ArchiveSequenceNumber.HasValue) + { + return x.ArchiveSequenceNumber.Value.CompareTo(y.ArchiveSequenceNumber.Value); + } + else if (x.FileCreatedTimeUtc == y.FileCreatedTimeUtc) + { + return StringComparer.OrdinalIgnoreCase.Compare(x.FileInfo.Name, y.FileInfo.Name); + } + else + { + return x.FileCreatedTimeUtc.CompareTo(y.FileCreatedTimeUtc); + } + } + + public override string ToString() + { + return FileInfo.Name; + } + + public static int? GetMaxArchiveSequenceNo(FileInfo[] fileInfos, int fileWildcardStartIndex, int fileWildcardEndIndex) + { + int? maxArchiveSequenceNo = null; + + foreach (var fileInfo in fileInfos) + { + var fileName = fileInfo.Name; + + if (ExcludeFileName(fileName, fileWildcardStartIndex, fileWildcardEndIndex, null)) + continue; + + if (TryParseStartSequenceNumber(fileName, fileWildcardStartIndex, out var archiveSequenceNo)) + { + if (!maxArchiveSequenceNo.HasValue || archiveSequenceNo > maxArchiveSequenceNo.Value) + maxArchiveSequenceNo = archiveSequenceNo; + } + } + + return maxArchiveSequenceNo; + } + + /// + /// - Only strict scan for sequence-number (GetTodaysArchiveFiles) when having input "fileLastWriteTime" + /// - Expect optional DateTime-part to be "sortable" (when missing birthtime) + /// - Trim away sequencer-number, so not part of sorting + /// - Use DateTime part from FileSystem for ordering by Date-only, and sort by FileName + /// + public static IEnumerable CleanupFiles(FileInfo[] fileInfos, int maxArchiveFiles, int maxArchiveDays, int fileWildcardStartIndex, int fileWildcardEndIndex, string excludeFileName = null, bool wildCardContainsSeqNo = false) + { + if (fileInfos.Length <= 1) + { + if (maxArchiveFiles == 0 && fileInfos.Length == 1 && !ExcludeFileName(fileInfos[0].Name, fileWildcardStartIndex, fileWildcardEndIndex, excludeFileName)) + yield return fileInfos[0]; + yield break; + } + + if (maxArchiveFiles >= fileInfos.Length && maxArchiveDays <= 0) + yield break; + + var fileInfoDates = new List(fileInfos.Length); + foreach (var fileInfo in fileInfos) + { + var fileName = fileInfo.Name; + if (ExcludeFileName(fileName, fileWildcardStartIndex, fileWildcardEndIndex, excludeFileName)) + continue; + + var fileCreatedTimeUtc = (FileInfoHelper.LookupValidFileCreationTimeUtc(fileInfo) ?? NLog.Time.TimeSource.Current.Time).Date; + if (wildCardContainsSeqNo && TryParseStartSequenceNumber(fileName, fileWildcardStartIndex, out var archiveSequenceNo)) + fileInfoDates.Add(new FileInfoDateTime(fileInfo, fileCreatedTimeUtc, archiveSequenceNo)); + else + fileInfoDates.Add(new FileInfoDateTime(fileInfo, fileCreatedTimeUtc)); + } + fileInfoDates.Sort((x, y) => x.Compare(x, y)); + + for (int i = 0; i < fileInfoDates.Count; i++) + { + if (ShouldDeleteFile(fileInfoDates[i], fileInfoDates.Count - i, maxArchiveFiles, maxArchiveDays)) + { + yield return fileInfoDates[i].FileInfo; + } + else + { + yield break; + } + } + } + + private static bool ExcludeFileName(string fileName, int fileWildcardStartIndex, int fileWildcardEndIndex, string excludeFileName) + { + if (fileWildcardStartIndex >= 0 && fileWildcardEndIndex >= 0 && fileName.Length > fileWildcardEndIndex) + { + for (int i = fileWildcardStartIndex; i < fileName.Length - fileWildcardEndIndex; ++i) + { + if (char.IsLetter(fileName[i])) + return true; + } + } + + if (excludeFileName is null) + return false; + else + return string.Equals(fileName, excludeFileName, StringComparison.OrdinalIgnoreCase); + } + + private static bool ShouldDeleteFile(FileInfoDateTime existingArchiveFile, int remainingFileCount, int maxArchiveFiles, int maxArchiveDays) + { + if (maxArchiveFiles >= 0 && remainingFileCount > maxArchiveFiles) + return true; + + if (maxArchiveDays > 0) + { + var currentDateUtc = NLog.Time.TimeSource.Current.Time.Date; + var fileAgeDays = (currentDateUtc - NLog.Time.TimeSource.Current.FromSystemTime(existingArchiveFile.FileCreatedTimeUtc).Date).TotalDays; + if (fileAgeDays > maxArchiveDays) + { + InternalLogger.Debug("FileTarget: Detected old file in archive. FileName={0}, FileDateUtc={1:u}, CurrentDateUtc={2:u}, Age={3} days", existingArchiveFile.FileInfo.FullName, existingArchiveFile.FileCreatedTimeUtc, currentDateUtc, Math.Round(fileAgeDays, 1)); + return true; + } + } + + return false; + } + + private static bool TryParseStartSequenceNumber(string archiveFileName, int seqStartIndex, out int archiveSequenceNo) + { + int? parsedSequenceNo = null; + + int startIndex = seqStartIndex; + for (int i = startIndex; i < archiveFileName.Length; ++i) + { + char chr = archiveFileName[i]; + if (!char.IsNumber(chr)) + break; + + parsedSequenceNo = parsedSequenceNo > 0 ? parsedSequenceNo * 10 : 0; + parsedSequenceNo += (chr - '0'); + } + archiveSequenceNo = parsedSequenceNo ?? 0; + return parsedSequenceNo.HasValue; + } + } + + private static string GetFileNameWildcard(string filepath) + { + var filename = Path.GetFileNameWithoutExtension(filepath) ?? string.Empty; + var fileext = Path.GetExtension(filepath) ?? string.Empty; + if (string.IsNullOrEmpty(filename) && string.IsNullOrEmpty(fileext)) + return string.Empty; + + int lastStart = 0; + int lastLength = 0; + int currentLength = 0; + int currentStart = 0; + for (int i = 0; i < filename.Length; ++i) + { + if (!char.IsLetter(filename[i])) + { + if (currentLength == 0) + currentStart = i; + ++currentLength; + } + else + { + if (currentLength != 0) + { + if (lastLength <= currentLength) + { + lastStart = currentStart; + lastLength = currentLength; + } + currentLength = 0; + } + } + } + + if (lastLength < currentLength) + { + lastStart = currentStart; + lastLength = currentLength; + } + + if (lastLength > 0) + { + return filename.Substring(0, lastStart) + "*" + filename.Substring(lastStart + lastLength, filename.Length - lastStart - lastLength) + fileext; + } + else + { + return filename + "*" + fileext; + } + } + + protected bool DeleteOldArchiveFile(string filepath) + { + for (int i = 1; i <= 3; ++i) + { + try + { + InternalLogger.Info("{0}: Deleting old archive file: '{1}'.", _fileTarget, filepath); + _fileTarget.CloseOpenFileBeforeArchiveCleanup(filepath); + File.Delete(filepath); + return true; + } + catch (DirectoryNotFoundException ex) + { + InternalLogger.Debug(ex, "{0}: Failed to delete old file as directory not found: '{1}'", _fileTarget, filepath); + return true; + } + catch (FileNotFoundException ex) + { + InternalLogger.Debug(ex, "{0}: Failed to delete old file as file not found: '{1}'", _fileTarget, filepath); + return true; + } + catch (IOException ex) + { + InternalLogger.Debug(ex, "{0}: Failed to delete old file, maybe file is locked: '{1}'", _fileTarget, filepath); + if (!File.Exists(filepath)) + return true; + if (i >= 3 && ex.MustBeRethrown(_fileTarget)) + throw; + } + catch (Exception ex) + { + InternalLogger.Warn(ex, "{0}: Failed to delete old archive file: '{1}'.", _fileTarget, filepath); + if (ex.MustBeRethrown(_fileTarget)) + throw; + + return false; + } + + Thread.Sleep(i * 10); + } + return false; + } + + protected void FixWindowsFileSystemTunneling(string newFilePath) + { + try + { + if (PlatformDetector.IsWin32 && !File.Exists(newFilePath)) + { + // Set the file's creation time to avoid being thwarted by Windows FileSystem Tunneling capabilities (https://support.microsoft.com/en-us/kb/172190). + File.Create(newFilePath).Dispose(); + File.SetCreationTimeUtc(newFilePath, DateTime.UtcNow); + } + } + catch (Exception ex) + { + InternalLogger.Debug(ex, "{0}: Failed to refresh CreationTimeUtc for FileName: {1}", _fileTarget, newFilePath); + } + } + } +} diff --git a/src/NLog/Targets/FileArchiveHandlers/DisabledFileArchiveHandler.cs b/src/NLog/Targets/FileArchiveHandlers/DisabledFileArchiveHandler.cs new file mode 100644 index 0000000000..f4a07d4fd0 --- /dev/null +++ b/src/NLog/Targets/FileArchiveHandlers/DisabledFileArchiveHandler.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +using System; + +namespace NLog.Targets.FileArchiveHandlers +{ + internal sealed class DisabledFileArchiveHandler : IFileArchiveHandler + { + public static readonly IFileArchiveHandler Default = new DisabledFileArchiveHandler(); + + public int ArchiveBeforeOpenFile(string newFileName, LogEventInfo firstLogEvent, DateTime? previousFileLastModified, int newSequenceNumber) + { + return 0; // Archive logic disabled, no rolling of active file + } + } +} diff --git a/src/NLog/Targets/FileArchiveHandlers/IFileArchiveHandler.cs b/src/NLog/Targets/FileArchiveHandlers/IFileArchiveHandler.cs new file mode 100644 index 0000000000..4d901b0a30 --- /dev/null +++ b/src/NLog/Targets/FileArchiveHandlers/IFileArchiveHandler.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets.FileArchiveHandlers +{ + using System; + + /// + /// Handles the actual file-operations on disk + /// + internal interface IFileArchiveHandler + { + /// + /// Called just before opening a new log-file + /// + /// File-name of the new log-file + /// The first LogEvent for the new log-file + /// Previous file-write-time + /// File-path-suffix for the new log-file + /// Updated for the new file. + int ArchiveBeforeOpenFile(string newFileName, LogEventInfo firstLogEvent, DateTime? previousFileLastModified, int newSequenceNumber); + } +} diff --git a/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs b/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs new file mode 100644 index 0000000000..85ee228ef9 --- /dev/null +++ b/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs @@ -0,0 +1,272 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets.FileArchiveHandlers +{ + using System; + using System.IO; + using NLog.Common; + using NLog.Internal; + using NLog.Layouts; + + /// + /// Legacy archive logic with file-move of active-file to file-path specified by + /// + /// + /// Kept mostly for legacy reasons, because archive operation can fail because of file-locks by other applications (or by multi-process logging). + /// + /// Avoid using when possible, and instead rely on only using and . + /// + internal sealed class LegacyArchiveFileNameHandler : RollingArchiveFileHandler, IFileArchiveHandler + { + public LegacyArchiveFileNameHandler(FileTarget fileTarget) + : base(fileTarget) + { + } + + public override int ArchiveBeforeOpenFile(string newFileName, LogEventInfo firstLogEvent, DateTime? previousFileLastModified, int newSequenceNumber) + { + var archiveFileName = _fileTarget.ArchiveFileName?.Render(firstLogEvent); + if (StringHelpers.IsNullOrWhiteSpace(archiveFileName)) + { + return base.ArchiveBeforeOpenFile(newFileName, firstLogEvent, previousFileLastModified, newSequenceNumber); + } + + var newFilePath = FileTarget.CleanFullFilePath(newFileName); + + bool initialFileOpen = newSequenceNumber == 0; + if (ArchiveBeforeOpenFile(archiveFileName, newFilePath, firstLogEvent, previousFileLastModified, initialFileOpen)) + { + FixWindowsFileSystemTunneling(newFilePath); + } + + return 0; + } + + private bool ArchiveBeforeOpenFile(string archiveFileName, string newFilePath, LogEventInfo firstLogEvent, DateTime? previousFileLastModified, bool initialFileOpen) + { + bool oldFilesDeleted = false; + if (_fileTarget.MaxArchiveFiles >= 0 || _fileTarget.MaxArchiveDays > 0 || (initialFileOpen && _fileTarget.DeleteOldFileOnStartup)) + { + bool wildCardStrictSeqNo = _fileTarget.ArchiveSuffixFormat.IndexOf("{0", StringComparison.Ordinal) >= 0 && _fileTarget.ArchiveSuffixFormat.IndexOf("{1", StringComparison.Ordinal) < 0 && + !(_fileTarget.ArchiveFileName is SimpleLayout simpleLayout && (simpleLayout.OriginalText.IndexOf("${date", StringComparison.OrdinalIgnoreCase) >= 0 || simpleLayout.OriginalText.IndexOf("${shortdate", StringComparison.OrdinalIgnoreCase) >= 0)); + + var excludeFileName = Path.GetFileName(newFilePath); + if (wildCardStrictSeqNo) + { + string archiveFilePath = BuildArchiveFilePath(archiveFileName, int.MaxValue, DateTime.MinValue); + string archiveFileWildcard = archiveFilePath.Replace(int.MaxValue.ToString(), "*"); + string archiveDirectory = Path.GetDirectoryName(archiveFilePath); + oldFilesDeleted = DeleteOldFilesBeforeArchive(archiveDirectory, Path.GetFileName(archiveFileWildcard), initialFileOpen, excludeFileName, true); + } + else + { + var archiveFilePath = FileTarget.CleanFullFilePath(archiveFileName); + oldFilesDeleted = DeleteOldFilesBeforeArchive(archiveFilePath, initialFileOpen, excludeFileName); + } + } + + if (initialFileOpen && !_fileTarget.ArchiveOldFileOnStartup) + return oldFilesDeleted; + + if (!ArchiveOldFileWithRetry(archiveFileName, newFilePath, firstLogEvent, previousFileLastModified)) + return oldFilesDeleted; + + return true; + } + + private bool ArchiveOldFileWithRetry(string archiveFileName, string newFilePath, LogEventInfo firstLogEvent, DateTime? previousFileLastModified) + { + DateTime? lastWriteTimeUtc = default(DateTime?); + long? lastFileLength = default(long?); + + bool oldFilesDeleted = false; + + for (int i = 1; i <= 3; ++i) + { + try + { + var newFileInfo = new FileInfo(newFilePath); + if (!newFileInfo.Exists) + return oldFilesDeleted; + + oldFilesDeleted = true; + if (lastWriteTimeUtc.HasValue && lastWriteTimeUtc.Value != newFileInfo.LastWriteTimeUtc) + return false; // File archive probably completed by someone else, and new file already created + + if (lastFileLength.HasValue && lastFileLength.Value != newFileInfo.Length) + return false; // File archive probably completed by someone else, and new file already created + + lastWriteTimeUtc = lastWriteTimeUtc ?? newFileInfo.LastWriteTimeUtc; + lastFileLength = lastFileLength ?? newFileInfo.Length; + if (ArchiveOldFile(archiveFileName, newFileInfo, firstLogEvent, previousFileLastModified)) + return true; + } + catch (IOException ex) + { + InternalLogger.Debug(ex, "{0}: Failed to archive file, maybe file is locked: '{1}'", _fileTarget, newFilePath); + if (!File.Exists(newFilePath)) + return oldFilesDeleted; + if (i >= 3 && LogManager.ThrowExceptions) + throw; + } + catch (Exception ex) + { + InternalLogger.Debug(ex, "{0}: Failed to archive file: '{1}'", _fileTarget, newFilePath); + if (LogManager.ThrowExceptions) + throw; + System.Threading.Thread.Sleep(i * 10); + } + } + + return oldFilesDeleted; + } + + private bool ArchiveOldFile(string archiveFileName, FileInfo newFileInfo, LogEventInfo firstLogEvent, DateTime? previousFileLastModified) + { + DateTime fileLastWriteTime = Time.TimeSource.Current.FromSystemTime(newFileInfo.LastWriteTimeUtc); + if (previousFileLastModified.HasValue && (previousFileLastModified > fileLastWriteTime || fileLastWriteTime >= firstLogEvent.TimeStamp)) + fileLastWriteTime = previousFileLastModified.Value; + + var archiveNextSequenceNo = ResolveNextArchiveSequenceNo(archiveFileName, fileLastWriteTime); + string archiveFullPath = BuildArchiveFilePath(archiveFileName, archiveNextSequenceNo, fileLastWriteTime); + + if (!File.Exists(archiveFullPath)) + { + // Move active file to archive + InternalLogger.Info("{0}: Move file from '{1}' to '{2}'", _fileTarget, newFileInfo.FullName, archiveFullPath); + File.Move(newFileInfo.FullName, archiveFullPath); + return true; + } + + if (!newFileInfo.Exists) + { + return true; // No active file to archive, we are done + } + + if (archiveNextSequenceNo == 0) + { + // Append to existing file, and delete old file + ArchiveFileAppendExisting(newFileInfo.FullName, archiveFullPath); + return true; + } + + // Retry (guess new sequence number) + return false; + } + + private string BuildArchiveFilePath(string archiveFileName, int archiveNextSequenceNo, DateTime fileLastWriteTime) + { + return _fileTarget.BuildFullFilePath(archiveFileName, archiveNextSequenceNo, fileLastWriteTime); + } + + private void ArchiveFileAppendExisting(string newFilePath, string archiveFilePath) + { + // TODO Handle double footer + InternalLogger.Info("{0}: Already exists, append to {1}", _fileTarget, archiveFilePath); + + var fileShare = FileShare.Read | FileShare.Delete; + using (FileStream newFileStream = File.Open(newFilePath, FileMode.Open, FileAccess.ReadWrite, fileShare)) + using (FileStream archiveFileStream = File.Open(archiveFilePath, FileMode.Append)) + { + if (_fileTarget.WriteBom) + { + var preamble = _fileTarget.Encoding.GetPreamble(); + if (preamble.Length > 0) + newFileStream.Seek(preamble.Length, SeekOrigin.Begin); + } + + byte[] buffer = new byte[4096]; + int read; + while ((read = newFileStream.Read(buffer, 0, buffer.Length)) > 0) + { + archiveFileStream.Write(buffer, 0, read); + } + + // Reset/Truncate the file + newFileStream.SetLength(0); + + // Attempt to delete file to reset File-Creation-Time (Delete under file-lock) + if (!DeleteOldArchiveFile(newFilePath)) + { + fileShare &= ~FileShare.Delete; // Retry after having released file-lock + } + + newFileStream.Close(); // This flushes the content + } + + if ((fileShare & FileShare.Delete) == 0) + { + DeleteOldArchiveFile(newFilePath); // Attempt to delete file to reset File-Creation-Time + } + } + + private int ResolveNextArchiveSequenceNo(string archiveFileName, DateTime fileLastWriteTime) + { + // Archive operation triggered, how to resolve the next archive-sequence-number ? + // - Old version was able to "parse" the file-names of the archive-folder and "guess" the next sequence number + var archiveFilePath = BuildArchiveFilePath(archiveFileName, int.MaxValue, fileLastWriteTime); + var archiveDirectory = Path.GetDirectoryName(archiveFilePath); + if (string.IsNullOrEmpty(archiveDirectory)) + return 0; + + var directoryInfo = new DirectoryInfo(archiveDirectory); + if (!directoryInfo.Exists) + { + directoryInfo.Create(); + } + + var archiveWildCardFileName = Path.GetFileName(archiveFilePath).Replace(int.MaxValue.ToString(), "*"); + int fileWildcardStartIndex = archiveWildCardFileName.IndexOf('*'); + if (fileWildcardStartIndex < 0) + return 0; + + int fileWildcardEndIndex = archiveWildCardFileName.Length - fileWildcardStartIndex; + if (fileWildcardStartIndex > 0 && !char.IsLetter(archiveWildCardFileName[fileWildcardStartIndex - 1]) && !char.IsDigit(archiveWildCardFileName[fileWildcardStartIndex - 1])) + { + archiveWildCardFileName = archiveWildCardFileName.Substring(0, fileWildcardStartIndex - 1) + archiveWildCardFileName.Substring(fileWildcardStartIndex); + } + + var archiveFiles = directoryInfo.GetFiles(archiveWildCardFileName); + InternalLogger.Debug("{0}: Archive Sequence Rolling found {1} files matching wildcard {2} in directory: {3}", _fileTarget, archiveFiles.Length, archiveWildCardFileName, archiveDirectory); + if (archiveFiles.Length == 0) + return 0; + + var sequenceNo = GetMaxArchiveSequenceNo(archiveFiles, fileWildcardStartIndex, fileWildcardEndIndex); + if (sequenceNo.HasValue) + return sequenceNo.Value + 1; + + return 0; + } + } +} diff --git a/src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs b/src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs new file mode 100644 index 0000000000..ea3c6ec6c5 --- /dev/null +++ b/src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs @@ -0,0 +1,143 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets.FileArchiveHandlers +{ + using System; + using System.IO; + using NLog.Common; + using NLog.Internal; + + /// + /// Rolls the active-file to the next sequence-number + /// + internal class RollingArchiveFileHandler : BaseFileArchiveHandler, IFileArchiveHandler + { + public RollingArchiveFileHandler(FileTarget fileTarget) + :base(fileTarget) + { + } + + public virtual int ArchiveBeforeOpenFile(string newFileName, LogEventInfo firstLogEvent, DateTime? previousFileLastModified, int newSequenceNumber) + { + bool initialFileOpen = newSequenceNumber == 0; + + if (_fileTarget.MaxArchiveFiles >= 0 || _fileTarget.MaxArchiveDays > 0 || (initialFileOpen && _fileTarget.DeleteOldFileOnStartup)) + { + var newFilePath = FileTarget.CleanFullFilePath(newFileName); + bool deletedOldFiles = DeleteOldFilesBeforeArchive(newFilePath, initialFileOpen); + + if (_fileTarget.MaxArchiveFiles == 0 || _fileTarget.MaxArchiveFiles == 1 || (initialFileOpen && _fileTarget.DeleteOldFileOnStartup)) + { + if (deletedOldFiles) + { + FixWindowsFileSystemTunneling(newFilePath); + } + return 0; + } + } + + if (initialFileOpen) + { + if (_fileTarget.ArchiveOldFileOnStartup || _fileTarget.ArchiveAboveSize != 0 || _fileTarget.ArchiveEvery != FileArchivePeriod.None) + { + var newFilePath = FileTarget.CleanFullFilePath(newFileName); + return RollToInitialSequenceNumber(newFilePath); + } + } + + return newSequenceNumber; + } + + private int RollToInitialSequenceNumber(string newFilePath) + { + int newSequenceNumber = 0; + + try + { + var filedir = Path.GetDirectoryName(newFilePath); + if (string.IsNullOrEmpty(filedir)) + return 0; + + var directoryInfo = new DirectoryInfo(filedir); + if (!directoryInfo.Exists) + return 0; + + var filename = Path.GetFileNameWithoutExtension(newFilePath); + var fileext = Path.GetExtension(newFilePath); + var fileWildCard = filename + "*" + fileext; + var fileInfos = directoryInfo.GetFiles(fileWildCard); + InternalLogger.Debug("{0}: Archive Sequence Rolling found {1} files matching wildcard {2} in directory: {3}", _fileTarget, fileInfos.Length, fileWildCard, filedir); + if (fileInfos.Length == 0) + return 0; + + if (_fileTarget.DeleteOldFileOnStartup) + { + // Delete all files in the directory with matching filename-wildcard + foreach (var fileInfo in fileInfos) + { + DeleteOldArchiveFile(fileInfo.FullName); + } + + return 0; + } + else + { + var archivePathWildCard = _fileTarget.BuildFullFilePath(newFilePath, int.MaxValue, DateTime.MinValue).Replace(int.MaxValue.ToString(), "*"); + var archiveFileName = Path.GetFileName(archivePathWildCard); + var fileWildcardStartIndex = archiveFileName.IndexOf('*'); + var fileWildcardEndIndex = fileWildcardStartIndex >= 0 ? archiveFileName.Length - fileWildcardStartIndex : filename.Length; + + // Search for matching files to find the highest sequence-number + newSequenceNumber = GetMaxArchiveSequenceNo(fileInfos, fileWildcardStartIndex, fileWildcardEndIndex) ?? 0; + + if (_fileTarget.ArchiveOldFileOnStartup) + { + // Search for matching files to find the highest sequence-number, and roll to next sequence-no + newSequenceNumber += 1; + } + + return newSequenceNumber; + } + } + catch (Exception ex) + { + InternalLogger.Warn(ex, "{0}: Failed to resolve initial archive sequence number for file: {1}", _fileTarget, newFilePath); + if (ex.MustBeRethrown(_fileTarget)) + throw; + + return newSequenceNumber; + } + } + } +} diff --git a/src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs b/src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs new file mode 100644 index 0000000000..58bb19d74a --- /dev/null +++ b/src/NLog/Targets/FileArchiveHandlers/ZeroFileArchiveHandler.cs @@ -0,0 +1,94 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets.FileArchiveHandlers +{ + using System; + using System.IO; + using System.Linq; + using NLog.Common; + using NLog.Internal; + + /// + /// Deletes/truncates the active logging-file when archive-roll-event is triggered + /// + internal sealed class ZeroFileArchiveHandler : BaseFileArchiveHandler, IFileArchiveHandler + { + public ZeroFileArchiveHandler(FileTarget fileTarget) + :base(fileTarget) + { + } + + public int ArchiveBeforeOpenFile(string newFileName, LogEventInfo firstLogEvent, DateTime? previousFileLastModified, int newSequenceNumber) + { + var newFilePath = FileTarget.CleanFullFilePath(newFileName); + + bool initialFileOpen = newSequenceNumber == 0; + if (DeleteOldArchiveFiles(newFilePath, initialFileOpen)) + { + FixWindowsFileSystemTunneling(newFilePath); + } + return 0; // No rolling of active file + } + + private bool DeleteOldArchiveFiles(string newFilePath, bool initialFileOpen) + { + try + { + if (initialFileOpen && _fileTarget.DeleteOldFileOnStartup) + { + var filePathWithoutExtension = Path.GetFileNameWithoutExtension(newFilePath); + if (filePathWithoutExtension.Any(chr => char.IsDigit(chr))) + { + // Delete with wildcard (also files from yesterday) + return DeleteOldFilesBeforeArchive(newFilePath, initialFileOpen); + } + } + + // Wildcard not detected as required, so just delete/truncate the active file + if (File.Exists(newFilePath)) + { + return DeleteOldArchiveFile(newFilePath); + } + } + catch (Exception ex) + { + InternalLogger.Debug(ex, "{0}: Failed to archive file: '{1}'", _fileTarget, newFilePath); + if (ex.MustBeRethrown(_fileTarget)) + throw; + } + + return false; + } + } +} diff --git a/src/NLog/Targets/FileArchivePeriod.cs b/src/NLog/Targets/FileArchivePeriod.cs index 3873739ed3..c4b7983d04 100644 --- a/src/NLog/Targets/FileArchivePeriod.cs +++ b/src/NLog/Targets/FileArchivePeriod.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen // // All rights reserved. @@ -44,63 +44,63 @@ public enum FileArchivePeriod None, /// - /// AddToArchive every year. + /// Archive every new year. /// Year, /// - /// AddToArchive every month. + /// Archive every new month. /// Month, /// - /// AddToArchive daily. + /// Archive every new day. /// Day, /// - /// AddToArchive every hour. + /// Archive every new hour. /// Hour, /// - /// AddToArchive every minute. + /// Archive every new minute. /// Minute, #region Weekdays /// - /// AddToArchive every Sunday. + /// Archive every Sunday. /// Sunday, /// - /// AddToArchive every Monday. + /// Archive every Monday. /// Monday, /// - /// AddToArchive every Tuesday. + /// Archive every Tuesday. /// Tuesday, /// - /// AddToArchive every Wednesday. + /// Archive every Wednesday. /// Wednesday, /// - /// AddToArchive every Thursday. + /// Archive every Thursday. /// Thursday, /// - /// AddToArchive every Friday. + /// Archive every Friday. /// Friday, /// - /// AddToArchive every Saturday. + /// Archive every Saturday. /// Saturday #endregion diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index 3878cc1c98..4b6f799f41 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -35,146 +35,27 @@ namespace NLog.Targets { using System; using System.Collections.Generic; - using System.ComponentModel; - using System.Globalization; using System.IO; + using System.Linq; using System.Text; using System.Threading; using NLog.Common; using NLog.Config; using NLog.Internal; - using NLog.Internal.FileAppenders; using NLog.Layouts; - using NLog.Targets.FileArchiveModes; - using NLog.Time; + using NLog.Targets.FileAppenders; + using NLog.Targets.FileArchiveHandlers; /// - /// Writes log messages to one or more files. + /// FileTarget for writing formatted messages to one or more text files. /// /// /// See NLog Wiki /// /// Documentation on NLog Wiki [Target("File")] - public class FileTarget : TargetWithLayoutHeaderAndFooter, ICreateFileParameters + public class FileTarget : TargetWithLayoutHeaderAndFooter { - /// - /// Default clean up period of the initialized files. When a file exceeds the clean up period is removed from the list. - /// - /// Clean up period is defined in days. - private const int InitializedFilesCleanupPeriod = 2; - - /// - /// This value disables file archiving based on the size. - /// - private const long ArchiveAboveSizeDisabled = -1L; - - /// - /// Holds the initialized files each given time by the instance. Against each file, the last write time is stored. - /// - /// Last write time is store in local time (no UTC). - private readonly Dictionary _initializedFiles = new Dictionary(StringComparer.OrdinalIgnoreCase); - - /// - /// List of the associated file appenders with the instance. - /// - private IFileAppenderCache _fileAppenderCache; - - IFileArchiveMode GetFileArchiveHelper(string archiveFilePattern) - { - return _fileArchiveHelper ?? (_fileArchiveHelper = FileArchiveModeFactory.CreateArchiveStyle(archiveFilePattern, ArchiveNumbering, GetArchiveDateFormatString(ArchiveDateFormat), ArchiveFileName != null, MaxArchiveFiles > 0 || MaxArchiveDays > 0)); - } - private IFileArchiveMode _fileArchiveHelper; - - private Timer _autoClosingTimer; - - /// - /// The number of initialized files at any one time. - /// - private int _initializedFilesCounter; - - /// - /// The maximum number of archive files that should be kept. - /// - private int _maxArchiveFiles; - - /// - /// The maximum days of archive files that should be kept. - /// - private int _maxArchiveDays; - - /// - /// The filename as target - /// - private FilePathLayout _fullFileName; - - /// - /// The archive file name as target - /// - private FilePathLayout _fullArchiveFileName; - - private FileArchivePeriod _archiveEvery; - private long _archiveAboveSize; - - private bool _enableArchiveFileCompression; - - /// - /// The date of the previous log event. - /// - private DateTime? _previousLogEventTimestamp; - - /// - /// The file name of the previous log event. - /// - private string _previousLogFileName; - - private bool _concurrentWrites; - private bool _cleanupFileName; - private FilePathKind _fileNameKind = FilePathKind.Unknown; - private FilePathKind _archiveFileKind; - - /// - /// Initializes a new instance of the class. - /// - /// - /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} - /// - public FileTarget() : this(FileAppenderCache.Empty) - { - } - - internal FileTarget(IFileAppenderCache fileAppenderCache) - { - ArchiveNumbering = ArchiveNumberingMode.Sequence; - _maxArchiveFiles = 0; - _maxArchiveDays = 0; - - ArchiveEvery = FileArchivePeriod.None; - ArchiveAboveSize = ArchiveAboveSizeDisabled; - _cleanupFileName = true; - - _fileAppenderCache = fileAppenderCache; - } - -#if !NET35 && !NET40 - static FileTarget() - { - FileCompressor = new ZipArchiveFileCompressor(); - } -#endif - - /// - /// Initializes a new instance of the class. - /// - /// - /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} - /// - /// Name of the target. - public FileTarget(string name) : this() - { - Name = name; - } - /// /// Gets or sets the name of the file to write to. /// @@ -193,63 +74,15 @@ public FileTarget(string name) : this() [RequiredParameter] public Layout FileName { - get - { - return _fullFileName?.GetLayout(); - } - set - { - _fullFileName = CreateFileNameLayout(value); - ResetFileAppenders("FileName Changed"); - } - } - - private FilePathLayout CreateFileNameLayout(Layout value) - { - if (value is null) - return null; - - return new FilePathLayout(value, CleanupFileName, FileNameKind); - } - - /// - /// Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. - /// If set to false, nothing gets written when the filename is wrong. - /// - /// - public bool CleanupFileName - { - get => _cleanupFileName; - set - { - if (_cleanupFileName != value) - { - _cleanupFileName = value; - _fullFileName = CreateFileNameLayout(FileName); - _fullArchiveFileName = CreateFileNameLayout(ArchiveFileName); - ResetFileAppenders("CleanupFileName Changed"); - } - } - } - - /// - /// Is the an absolute or relative path? - /// - /// - public FilePathKind FileNameKind - { - get => _fileNameKind; + get => _fileName; set { - if (_fileNameKind != value) - { - _fileNameKind = value; - _fullFileName = CreateFileNameLayout(FileName); - _fullArchiveFileName = CreateFileNameLayout(ArchiveFileName); - ResetFileAppenders("FileNameKind Changed"); - } + _fileName = value; + _fixedFileName = (value is SimpleLayout simpleLayout && simpleLayout.IsFixedText) ? simpleLayout.Text : null; } } + private Layout _fileName; + private string _fixedFileName; /// /// Gets or sets a value indicating whether to create directories if they do not exist. @@ -265,7 +98,7 @@ public FilePathKind FileNameKind /// Gets or sets a value indicating whether to delete old log file on startup. /// /// - /// This option works only when the "FileName" parameter denotes a single file. + /// When current log-file exists, then it is deleted (and resetting sequence number) /// /// public bool DeleteOldFileOnStartup { get; set; } @@ -284,19 +117,7 @@ public FilePathKind FileNameKind /// KeepFileOpen = false gives the best compability, but slow performance and lead to file-locking issues with other applications. /// /// - public bool KeepFileOpen - { - get => _keepFileOpen; - set - { - if (_keepFileOpen != value) - { - _keepFileOpen = value; - ResetFileAppenders("KeepFileOpen Changed"); - } - } - } - private bool _keepFileOpen = true; + public bool KeepFileOpen { get; set; } = true; /// /// Gets or sets a value indicating whether to enable log file(s) to be deleted. @@ -304,30 +125,6 @@ public bool KeepFileOpen /// public bool EnableFileDelete { get; set; } = true; - /// - /// Gets or sets the file attributes (Windows only). - /// - /// - public Win32FileAttributes FileAttributes - { - get => _fileAttributes; - set - { - if (value != Win32FileAttributes.Normal && PlatformDetector.IsWin32) - { - ForceManaged = false; - } - _fileAttributes = value; - } - } - Win32FileAttributes _fileAttributes = Win32FileAttributes.Normal; - - bool ICreateFileParameters.IsArchivingEnabled => IsArchivingEnabled; - - int ICreateFileParameters.FileOpenRetryCount => ConcurrentWrites ? ConcurrentWriteAttempts : (KeepFileOpen ? 0 : (_concurrentWriteAttempts ?? 2)); - - int ICreateFileParameters.FileOpenRetryDelay => ConcurrentWriteAttemptDelay; - /// /// Gets or sets the line ending mode. /// @@ -393,42 +190,9 @@ public Encoding Encoding /// Gets or sets whether or not this target should just discard all data that its asked to write. /// Mostly used for when testing NLog Stack except final write /// - /// + /// public bool DiscardAll { get; set; } - /// - /// Gets or sets a value indicating whether concurrent writes to the log file by multiple processes on the same host. - /// - /// - /// This makes multi-process logging possible. NLog uses a special technique - /// that lets it keep the files open for writing. - /// - /// - public bool ConcurrentWrites - { - get => _concurrentWrites; - set - { - if (_concurrentWrites != value) - { - _concurrentWrites = value; - ResetFileAppenders("ConcurrentWrites Changed"); - } - } - } - - /// - /// Obsolete and replaced by = false with NLog v5.3. - /// Gets or sets a value indicating whether concurrent writes to the log file by multiple processes on different network hosts. - /// - /// - /// This effectively prevents files from being kept open. - /// - /// - [Obsolete("Instead use KeepFileOpen = false. Marked obsolete with NLog v5.3")] - [EditorBrowsable(EditorBrowsableState.Never)] - public bool NetworkWrites { get => !KeepFileOpen; set => KeepFileOpen = !value; } - /// /// Gets or sets a value indicating whether to write BOM (byte order mark) in created files. /// @@ -442,48 +206,14 @@ public bool WriteBom } private bool? _writeBom; - /// - /// Gets or sets the number of times the write is appended on the file before NLog - /// discards the log message. - /// - /// - public int ConcurrentWriteAttempts { get => _concurrentWriteAttempts ?? 10; set => _concurrentWriteAttempts = value; } - private int? _concurrentWriteAttempts; - - /// - /// Gets or sets the delay in milliseconds to wait before attempting to write to the file again. - /// - /// - /// The actual delay is a random value between 0 and the value specified - /// in this parameter. On each failed attempt the delay base is doubled - /// up to times. - /// - /// - /// Assuming that ConcurrentWriteAttemptDelay is 10 the time to wait will be:

- /// a random value between 0 and 10 milliseconds - 1st attempt
- /// a random value between 0 and 20 milliseconds - 2nd attempt
- /// a random value between 0 and 40 milliseconds - 3rd attempt
- /// a random value between 0 and 80 milliseconds - 4th attempt
- /// ...

- /// and so on. - /// - /// - public int ConcurrentWriteAttemptDelay { get; set; } = 1; - ///

/// Gets or sets a value indicating whether to archive old log file on startup. /// /// - /// This option works only when the "FileName" parameter denotes a single file. - /// After archiving the old file, the current log file will be empty. + /// When current log-file exists, then roll to the next sequence number /// /// - public bool ArchiveOldFileOnStartup - { - get => _archiveOldFileOnStartup ?? false; - set => _archiveOldFileOnStartup = value; - } - private bool? _archiveOldFileOnStartup; + public bool ArchiveOldFileOnStartup { get; set; } /// /// Gets or sets whether to write the Header on initial creation of file appender, even if the file is not empty. @@ -492,36 +222,25 @@ public bool ArchiveOldFileOnStartup /// /// Alternative use to ensure each application session gets individual log-file. /// - public bool WriteHeaderWhenInitialFileNotEmpty { get; set; } - - /// - /// Gets or sets a value of the file size threshold to archive old log file on startup. - /// - /// - /// This option won't work if is set to false - /// Default value is 0 which means that the file is archived as soon as archival on - /// startup is enabled. - /// /// - public long ArchiveOldFileOnStartupAboveSize { get; set; } + public bool WriteHeaderWhenInitialFileNotEmpty { get; set; } /// - /// Gets or sets a value specifying the date format to use when archiving files. + /// Gets or sets a value specifying the date format when using . + /// Obsolete and only here for Legacy reasons, instead use . /// - /// - /// This option works only when the "ArchiveNumbering" parameter is set either to Date or DateAndSequence. - /// /// + [Obsolete("Instead use ArchiveSuffixFormat")] public string ArchiveDateFormat { get => _archiveDateFormat; set { - if (_archiveDateFormat != value) - { - _archiveDateFormat = value; - ResetFileAppenders("ArchiveDateFormat Changed"); // Reset archive file-monitoring - } + if (string.Equals(value, _archiveDateFormat)) + return; + + ArchiveSuffixFormat = string.IsNullOrEmpty(value) ? _archiveSuffixFormat : @"_{1:" + value + @"}_{0:00}"; + _archiveDateFormat = value; } } private string _archiveDateFormat = string.Empty; @@ -529,36 +248,25 @@ public string ArchiveDateFormat /// /// Gets or sets the size in bytes above which log files will be automatically archived. /// - /// - /// Notice when combined with then it will attempt to append to any existing - /// archive file if grown above size multiple times. New archive file will be created when using - /// /// public long ArchiveAboveSize { get => _archiveAboveSize; set { - var newValue = value > 0 ? value : ArchiveAboveSizeDisabled; - if ((_archiveAboveSize > 0) != (newValue > 0)) - { - _archiveAboveSize = newValue; - ResetFileAppenders("ArchiveAboveSize Changed"); // Reset archive file-monitoring - } - else - { - _archiveAboveSize = newValue; - } + _archiveAboveSize = value > 0 ? value : 0; + _fileArchiveHandler = null; } } + private long _archiveAboveSize; /// - /// Gets or sets a value indicating whether to automatically archive log files every time the specified time passes. + /// Gets or sets a value indicating whether to trigger archive operation based on time-period, by moving active-file to file-path specified by /// /// - /// Files are moved to the archive as part of the write operation if the current period of time changes. For example - /// if the current hour changes from 10 to 11, the first write that will occur - /// on or after 11:00 will trigger the archiving. + /// Archive move operation only works if is static in nature, and not rolling automatically because of ${date} or ${shortdate} + /// + /// NLog FileTarget probes the file-birthtime to recognize when time-period has passed, but file-birthtime is not supported by all filesystems. /// /// public FileArchivePeriod ArchiveEvery @@ -566,54 +274,60 @@ public FileArchivePeriod ArchiveEvery get => _archiveEvery; set { - if (_archiveEvery != value) - { - _archiveEvery = value; - ResetFileAppenders("ArchiveEvery Changed"); // Reset archive file-monitoring - } - } - } - - /// - /// Is the an absolute or relative path? - /// - /// - public FilePathKind ArchiveFileKind - { - get => _archiveFileKind; - set - { - if (_archiveFileKind != value) - { - _archiveFileKind = value; - _fullArchiveFileName = CreateFileNameLayout(ArchiveFileName); - ResetFileAppenders("ArchiveFileKind Changed"); // Reset archive file-monitoring - } + _archiveEvery = value; + _fileArchiveHandler = null; } } + FileArchivePeriod _archiveEvery; /// - /// Gets or sets the name of the file to be used for an archive. + /// Legacy archive logic where file-archive-logic moves active file to path specified by , and then recreates the active file. + /// + /// Use to control suffix format, instead of now obsolete token {#} /// /// - /// It may contain a special placeholder {#####} - /// that will be replaced with a sequence of numbers depending on - /// the archiving strategy. The number of hash characters used determines - /// the number of numerical digits to be used for numbering files. + /// Archive file-move operation only works if is static in nature, and not rolling automatically because of ${date} or ${shortdate} . + /// + /// Legacy archive file-move operation can fail because of file-locks, so file-archiving can stop working because of environment issues (Other applications locking files). + /// + /// Avoid using when possible, and instead rely on only using and . /// /// public Layout ArchiveFileName { - get - { - return _fullArchiveFileName?.GetLayout(); - } + get => _archiveFileName; set { - _fullArchiveFileName = CreateFileNameLayout(value); - ResetFileAppenders("ArchiveFileName Changed"); // Reset archive file-monitoring + var archiveSuffixFormat = _archiveSuffixFormat; + if (value is SimpleLayout simpleLayout) + { + if (simpleLayout.OriginalText.IndexOf("${date", StringComparison.OrdinalIgnoreCase) >= 0 || simpleLayout.OriginalText.IndexOf("${shortdate", StringComparison.OrdinalIgnoreCase) >= 0) + { + if (_archiveSuffixFormat is null || ReferenceEquals(_legacySequenceArchiveSuffixFormat, _archiveSuffixFormat) || ReferenceEquals(_legacyDateArchiveSuffixFormat, _archiveSuffixFormat)) + { + archiveSuffixFormat = "_{0}"; + } + } + + if (simpleLayout.OriginalText.Contains('#')) + { + var repairLegacyLayout = simpleLayout.OriginalText.Replace(".{#}", "").Replace("_{#}", "").Replace("-{#}", "").Replace("{#}", "").Replace(".{#", "").Replace("_{#", "").Replace("-{#", "").Replace("{#", "").Replace("#}", "").Replace("#", ""); + archiveSuffixFormat = _archiveSuffixFormat ?? _legacySequenceArchiveSuffixFormat; + value = new SimpleLayout(repairLegacyLayout); + } + } + + _archiveFileName = value; + if (!ReferenceEquals(_archiveSuffixFormat, archiveSuffixFormat)) + { + ArchiveSuffixFormat = archiveSuffixFormat; + } + _fileArchiveHandler = null; } } + private Layout _archiveFileName; + private static readonly string _legacyDateArchiveSuffixFormat = "_{1:yyyyMMdd}_{0:00}"; // Cater for ArchiveNumbering.DateAndSequence + private static readonly string _legacySequenceArchiveSuffixFormat = "_{0:00}"; // Cater for ArchiveNumbering.Sequence /// /// Gets or sets the maximum number of archive files that should be kept. @@ -624,13 +338,11 @@ public int MaxArchiveFiles get => _maxArchiveFiles; set { - if (_maxArchiveFiles != value) - { - _maxArchiveFiles = value; - ResetFileAppenders("MaxArchiveFiles Changed"); // Enforce archive cleanup - } + _maxArchiveFiles = value; + _fileArchiveHandler = null; } } + private int _maxArchiveFiles = -1; /// /// Gets or sets the maximum days of archive files that should be kept. @@ -641,69 +353,85 @@ public int MaxArchiveDays get => _maxArchiveDays; set { - if (_maxArchiveDays != value) - { - _maxArchiveDays = value; - ResetFileAppenders("MaxArchiveDays Changed"); // Enforce archive cleanup - } + _maxArchiveDays = value > 0 ? value : 0; + _fileArchiveHandler = null; } } + private int _maxArchiveDays; /// /// Gets or sets the way file archives are numbered. + /// Obsolete and only here for Legacy reasons, instead use . /// /// - public ArchiveNumberingMode ArchiveNumbering + [Obsolete("Instead use ArchiveSuffixFormat")] + public string ArchiveNumbering { - get => _archiveNumbering; + get => _archiveNumbering ?? "Sequence"; set { - if (_archiveNumbering != value) + if (string.Equals(value, _archiveNumbering)) + return; + + _archiveNumbering = string.IsNullOrEmpty(value) ? null : value.Trim(); + if (string.IsNullOrEmpty(_archiveNumbering)) + return; + + if (_archiveSuffixFormat is null || ReferenceEquals(_archiveSuffixFormat, _legacyDateArchiveSuffixFormat) || ReferenceEquals(_archiveSuffixFormat, _legacySequenceArchiveSuffixFormat)) { - _archiveNumbering = value; - ResetFileAppenders("ArchiveNumbering Changed"); // Reset archive file-monitoring + ArchiveSuffixFormat = _archiveNumbering.IndexOf("date", StringComparison.OrdinalIgnoreCase) >= 0 ? _legacyDateArchiveSuffixFormat : _legacySequenceArchiveSuffixFormat; } } } - private ArchiveNumberingMode _archiveNumbering; - - /// - /// Used to compress log files during archiving. - /// This may be used to provide your own implementation of a zip file compressor, - /// on platforms other than .Net4.5. - /// Defaults to ZipArchiveFileCompressor on .Net4.5 and to null otherwise. - /// - /// - public static IFileCompressor FileCompressor { get; set; } + private string _archiveNumbering; /// - /// Gets or sets a value indicating whether to compress archive files into the zip archive format. + /// Gets or sets the format-string to convert archive sequence-number by using string.Format /// + /// + /// Ex. to prefix with leading zero's then one can use _{0:000} . + /// + /// Legacy archive-logic with uses default suffix _{1:yyyyMMdd}_{0:00} . + /// /// - public bool EnableArchiveFileCompression + public string ArchiveSuffixFormat { - get => _enableArchiveFileCompression && FileCompressor != null; + get + { + if (ArchiveEvery != FileArchivePeriod.None && (_archiveSuffixFormat is null || ReferenceEquals(_legacyDateArchiveSuffixFormat, _archiveSuffixFormat) || ReferenceEquals(_legacySequenceArchiveSuffixFormat, _archiveSuffixFormat)) && ArchiveFileName != null) + { + switch (ArchiveEvery) + { + case FileArchivePeriod.Year: + return "_{1:yyyy}_{0:00}"; + case FileArchivePeriod.Month: + return "_{1:yyyyMM}_{0:00}"; + case FileArchivePeriod.Hour: + return "_{1:yyyyMMddHH}_{0:00}"; + case FileArchivePeriod.Minute: + return "_{1:yyyyMMddHHmm}_{0:00}"; + default: + return _legacyDateArchiveSuffixFormat; // Also for weekdays + } + } + + return _archiveSuffixFormat ?? _legacySequenceArchiveSuffixFormat; + } set { - if (_enableArchiveFileCompression != value) + if (!string.IsNullOrEmpty(value) && ArchiveFileName is SimpleLayout simpleLayout) { - _enableArchiveFileCompression = value; - ResetFileAppenders("EnableArchiveFileCompression Changed"); // Reset archive file-monitoring + var fileName = Path.GetFileNameWithoutExtension(simpleLayout.OriginalText); + if (StringHelpers.IsNullOrWhiteSpace(fileName) && value.IndexOf('_') == 0) + { + value = value.Substring(1); + } } + + _archiveSuffixFormat = value; } } - - /// - /// Gets or set a value indicating whether a managed file stream is forced, instead of using the native implementation. - /// - /// - public bool ForceManaged { get; set; } = true; - - /// - /// Gets or sets a value indicating whether file creation calls should be synchronized by a system global mutex. - /// - /// - public bool ForceMutexConcurrentWrites { get; set; } + private string _archiveSuffixFormat; /// /// Gets or sets a value indicating whether the footer should be written only when the file is archived. @@ -711,116 +439,84 @@ public bool EnableArchiveFileCompression /// public bool WriteFooterOnArchivingOnly { get; set; } - /// - /// Gets the characters that are appended after each line. - /// - protected internal string NewLineChars => LineEnding.NewLineCharacters; - - /// - /// Refresh the ArchiveFilePatternToWatch option of the . - /// The log file must be watched for archiving when multiple processes are writing to the same - /// open file. - /// - private void RefreshArchiveFilePatternToWatch(string fileName, LogEventInfo logEvent) + private int OpenFileMonitorTimerInterval { - _fileAppenderCache.CheckCloseAppenders -= AutoCloseAppendersAfterArchive; - - bool mustWatchArchiving = IsArchivingEnabled && KeepFileOpen && ConcurrentWrites; - bool mustWatchActiveFile = EnableFileDelete && ((KeepFileOpen && ConcurrentWrites) || (IsSimpleKeepFileOpen && !EnableFileDeleteSimpleMonitor)); - if (mustWatchArchiving || mustWatchActiveFile) + get { - _fileAppenderCache.CheckCloseAppenders += AutoCloseAppendersAfterArchive; // Activates FileSystemWatcher + if (OpenFileFlushTimeout <= 0 || AutoFlush || !KeepFileOpen) + return OpenFileCacheTimeout; + else if (OpenFileCacheTimeout <= 0) + return OpenFileFlushTimeout; + else + return Math.Min(OpenFileFlushTimeout, OpenFileCacheTimeout); } + } - if (mustWatchArchiving) - { - string archiveFilePattern = GetArchiveFileNamePattern(fileName, logEvent); - var fileArchiveStyle = !string.IsNullOrEmpty(archiveFilePattern) ? GetFileArchiveHelper(archiveFilePattern) : null; - string fileNameMask = fileArchiveStyle != null ? _fileArchiveHelper.GenerateFileNameMask(archiveFilePattern) : string.Empty; - string directoryMask = !string.IsNullOrEmpty(fileNameMask) ? Path.Combine(Path.GetDirectoryName(archiveFilePattern), fileNameMask) : string.Empty; - _fileAppenderCache.ArchiveFilePatternToWatch = directoryMask; - } - else + private IFileArchiveHandler FileAchiveHandler => _fileArchiveHandler ?? (_fileArchiveHandler = CreateFileArchiveHandler()); + private IFileArchiveHandler _fileArchiveHandler; + + struct OpenFileAppender + { + public IFileAppender FileAppender { get; } + public int SequenceNumber { get; } + + public OpenFileAppender(IFileAppender fileAppender, int sequenceNumber) { - _fileAppenderCache.ArchiveFilePatternToWatch = null; + FileAppender = fileAppender; + SequenceNumber = sequenceNumber; } } - /// - /// Removes records of initialized files that have not been - /// accessed in the last two days. + private readonly Dictionary _openFileCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + + private readonly ReusableStreamCreator _reusableFileWriteStream = new ReusableStreamCreator(); + private readonly ReusableStreamCreator _reusableBatchFileWriteStream = new ReusableStreamCreator(true); + private readonly ReusableBufferCreator _reusableEncodingBuffer = new ReusableBufferCreator(1024); + + private readonly SortHelpers.KeySelector _getFileNameFromLayout; + + private DateTime _lastWriteTime; + private Timer _openFileMonitorTimer; + + /// + /// Initializes a new instance of the class. /// /// - /// Files are marked 'initialized' for the purpose of writing footers when the logging finishes. + /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// - public void CleanupInitializedFiles() + public FileTarget() { - try - { - CleanupInitializedFiles(TimeSource.Current.Time.AddDays(-InitializedFilesCleanupPeriod)); - } - catch (Exception exception) - { - if (exception.MustBeRethrownImmediately()) - throw; - - InternalLogger.Error(exception, "{0}: Exception in CleanupInitializedFiles", this); - } + _getFileNameFromLayout = l => GetFileNameFromLayout(l.LogEvent); } /// - /// Removes records of initialized files that have not been - /// accessed after the specified date. + /// Initializes a new instance of the class. /// - /// The cleanup threshold. /// - /// Files are marked 'initialized' for the purpose of writing footers when the logging finishes. + /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// - public void CleanupInitializedFiles(DateTime cleanupThreshold) + /// Name of the target. + public FileTarget(string name) : this() { - InternalLogger.Trace("{0}: CleanupInitializedFiles with cleanupThreshold {1}", this, cleanupThreshold); - - List filesToFinalize = null; - - // Select the files require to be finalized. - foreach (var file in _initializedFiles) - { - if (file.Value < cleanupThreshold) - { - if (filesToFinalize is null) - { - filesToFinalize = new List(); - } - filesToFinalize.Add(file.Key); - } - } - - // Finalize the files. - if (filesToFinalize != null) - { - foreach (string fileName in filesToFinalize) - { - FinalizeFile(fileName); - } - } - - InternalLogger.Trace("{0}: CleanupInitializedFiles Completed and finalized {0} files", this, filesToFinalize?.Count ?? 0); + Name = name; } /// /// Flushes all pending file operations. /// /// The asynchronous continuation. - /// - /// The timeout parameter is ignored, because file APIs don't provide - /// the needed functionality. - /// protected override void FlushAsync(AsyncContinuation asyncContinuation) { try { InternalLogger.Trace("{0}: FlushAsync", this); - _fileAppenderCache.FlushAppenders(); + if (_openFileCache.Count > 0) + { + foreach (var openFile in _openFileCache) + { + openFile.Value.FileAppender.Flush(); + } + } asyncContinuation(null); InternalLogger.Trace("{0}: FlushAsync Done", this); } @@ -835,144 +531,43 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation) } } - /// - /// Returns the suitable appender factory ( ) to be used to generate the file - /// appenders associated with the instance. - /// - /// The type of the file appender factory returned depends on the values of various properties. - /// - /// suitable for this instance. - private IFileAppenderFactory GetFileAppenderFactory() - { - if (DiscardAll) - { - return NullAppender.TheFactory; - } - else if (!KeepFileOpen) - { - return RetryingMultiProcessFileAppender.TheFactory; - } - else if (ConcurrentWrites) - { - if (!ForceMutexConcurrentWrites) - { -#if MONO - if (PlatformDetector.IsUnix) - { - return UnixMultiProcessFileAppender.TheFactory; - } -#elif NETFRAMEWORK - if (PlatformDetector.IsWin32 && !PlatformDetector.IsMono) - { - return WindowsMultiProcessFileAppender.TheFactory; - } -#endif - } - - if (MutexDetector.SupportsSharableMutex) - { - return MutexMultiProcessFileAppender.TheFactory; - } - else - { - return RetryingMultiProcessFileAppender.TheFactory; - } - } - else if (IsArchivingEnabled) - return CountingSingleProcessFileAppender.TheFactory; - else - return SingleProcessFileAppender.TheFactory; - } - - private bool IsArchivingEnabled => ArchiveAboveSize != ArchiveAboveSizeDisabled || ArchiveEvery != FileArchivePeriod.None; - - private bool IsSimpleKeepFileOpen => KeepFileOpen && !ConcurrentWrites && !ReplaceFileContentsOnEachWrite; - - private bool EnableFileDeleteSimpleMonitor => EnableFileDelete && IsSimpleKeepFileOpen -#if NETFRAMEWORK - && !PlatformDetector.IsWin32 -#endif - ; - - bool ICreateFileParameters.EnableFileDeleteSimpleMonitor => EnableFileDeleteSimpleMonitor; - - /// - /// Initializes file logging by creating data structures that - /// enable efficient multi-file logging. - /// + /// protected override void InitializeTarget() { - base.InitializeTarget(); - - var appenderFactory = GetFileAppenderFactory(); - if (InternalLogger.IsTraceEnabled) + if (OpenFileMonitorTimerInterval > 0) { - InternalLogger.Trace("{0}: Using appenderFactory: {1}", this, appenderFactory.GetType()); + // Prepare Timer for periodic checking of flushing/closing open files (inactive until first file is opened) + _openFileMonitorTimer = new Timer(OpenFileMonitorTimer); } - _fileAppenderCache = new FileAppenderCache(OpenFileCacheSize, appenderFactory, this); - - if ((OpenFileCacheSize > 0 || EnableFileDelete) && (OpenFileCacheTimeout > 0 || OpenFileFlushTimeout > 0)) - { - int openFileAutoTimeoutSecs = (OpenFileCacheTimeout > 0 && OpenFileFlushTimeout > 0) ? Math.Min(OpenFileCacheTimeout, OpenFileFlushTimeout) : Math.Max(OpenFileCacheTimeout, OpenFileFlushTimeout); - InternalLogger.Trace("{0}: Start autoClosingTimer", this); - _autoClosingTimer = new Timer( - (state) => AutoClosingTimerCallback(this, EventArgs.Empty), - null, - openFileAutoTimeoutSecs * 1000, - openFileAutoTimeoutSecs * 1000); - } + base.InitializeTarget(); } - /// - /// Closes the file(s) opened for writing. - /// + /// protected override void CloseTarget() { - base.CloseTarget(); - - foreach (string fileName in new List(_initializedFiles.Keys)) - { - FinalizeFile(fileName); - } - - _fileArchiveHelper = null; + var openFileMonitorTimer = _openFileMonitorTimer; + _openFileMonitorTimer = null; + openFileMonitorTimer?.Change(Timeout.Infinite, Timeout.Infinite); + openFileMonitorTimer?.Dispose(); - var currentTimer = _autoClosingTimer; - if (currentTimer != null) + if (_openFileCache.Count > 0) { - InternalLogger.Trace("{0}: Stop autoClosingTimer", this); - _autoClosingTimer = null; - currentTimer.WaitForDispose(TimeSpan.Zero); + foreach (var openFile in _openFileCache.ToList()) + { + CloseFileWithFooter(openFile.Key, openFile.Value, false); + } + _openFileCache.Clear(); } - _fileAppenderCache.CloseAppenders("Dispose"); - _fileAppenderCache.Dispose(); - } - - private void ResetFileAppenders(string reason) - { - _fileArchiveHelper = null; - if (IsInitialized) - { - _fileAppenderCache.CloseAppenders(reason); - _initializedFiles.Clear(); - } + base.CloseTarget(); } - private readonly ReusableStreamCreator _reusableFileWriteStream = new ReusableStreamCreator(); - private readonly ReusableStreamCreator _reusableBatchFileWriteStream = new ReusableStreamCreator(true); - private readonly ReusableBufferCreator _reusableEncodingBuffer = new ReusableBufferCreator(1024); - - /// - /// Writes the specified logging event to a file specified in the FileName - /// parameter. - /// - /// The logging event. + /// protected override void Write(LogEventInfo logEvent) { - var logFileName = GetFullFileName(logEvent); - if (string.IsNullOrEmpty(logFileName)) + var filename = GetFileNameFromLayout(logEvent); + if (string.IsNullOrEmpty(filename)) { throw new ArgumentException("The path is not of a legal form."); } @@ -985,47 +580,14 @@ protected override void Write(LogEventInfo logEvent) RenderFormattedMessageToStream(logEvent, targetBuilder.Result, targetBuffer.Result, targetStream.Result); } - ProcessLogEvent(logEvent, logFileName, new ArraySegment(targetStream.Result.GetBuffer(), 0, (int)targetStream.Result.Length)); - } - } - - /// - /// Get full filename (=absolute) and cleaned if needed. - /// - /// - /// - internal string GetFullFileName(LogEventInfo logEvent) - { - if (_fullFileName is null) - return null; - - if (_fullFileName.IsFixedFilePath) - return _fullFileName.Render(logEvent); - - using (var targetBuilder = ReusableLayoutBuilder.Allocate()) - { - return _fullFileName.RenderWithBuilder(logEvent, targetBuilder.Result); + WriteToFile(filename, logEvent, targetStream.Result, out var _); } } - SortHelpers.KeySelector _getFullFileNameDelegate; - - /// - /// Writes the specified array of logging events to a file specified in the FileName - /// parameter. - /// - /// An array of objects. - /// - /// This function makes use of the fact that the events are batched by sorting - /// the requests by filename. This optimizes the number of open/close calls - /// and can help improve performance. - /// + /// protected override void Write(IList logEvents) { - if (_getFullFileNameDelegate is null) - _getFullFileNameDelegate = c => GetFullFileName(c.LogEvent); - - var buckets = logEvents.BucketSort(_getFullFileNameDelegate); + var buckets = logEvents.BucketSort(_getFileNameFromLayout); using (var reusableStream = _reusableBatchFileWriteStream.Allocate()) { @@ -1037,8 +599,8 @@ protected override void Write(IList logEvents) if (bucketCount <= 0) continue; - string fileName = bucket.Key; - if (string.IsNullOrEmpty(fileName)) + string filename = bucket.Key; + if (string.IsNullOrEmpty(filename)) { InternalLogger.Warn("{0}: FileName Layout returned empty string. The path is not of a legal form.", this); var emptyPathException = new ArgumentException("The path is not of a legal form."); @@ -1056,7 +618,7 @@ protected override void Write(IList logEvents) ms.SetLength(0); var written = WriteToMemoryStream(bucket.Value, currentIndex, ms); - AppendMemoryStreamToFile(fileName, bucket.Value[currentIndex].LogEvent, ms, out var lastException); + WriteToFile(filename, bucket.Value[currentIndex].LogEvent, ms, out var lastException); for (int i = 0; i < written; ++i) { bucket.Value[currentIndex++].Continuation(lastException); @@ -1066,6 +628,26 @@ protected override void Write(IList logEvents) } } + private string GetFileNameFromLayout(LogEventInfo logEvent) + { + if (_fixedFileName != null) + return _fixedFileName; + + var lastFileNameFromLayout = _lastFileNameFromLayout; + using (var targetBuilder = ReusableLayoutBuilder.Allocate()) + { + FileName.Render(logEvent, targetBuilder.Result); + if (targetBuilder.Result.EqualTo(lastFileNameFromLayout)) + return _lastFileNameFromLayout; + + lastFileNameFromLayout = targetBuilder.Result.ToString(); + } + _lastFileNameFromLayout = lastFileNameFromLayout; + return lastFileNameFromLayout; + } + + private string _lastFileNameFromLayout = string.Empty; + private int WriteToMemoryStream(IList logEvents, int startIndex, MemoryStream ms) { long maxBufferSize = BufferSize * 100; // Max Buffer Default = 30 KiloByte * 100 = 3 MegaByte @@ -1096,87 +678,11 @@ private int WriteToMemoryStream(IList logEvents, int startInd return logEvents.Count - startIndex; } - private void ProcessLogEvent(LogEventInfo logEvent, string fileName, ArraySegment bytesToWrite) - { - DateTime previousLogEventTimestamp = InitializeFile(fileName, logEvent); - bool initializedNewFile = previousLogEventTimestamp == DateTime.MinValue; - if (initializedNewFile && fileName == _previousLogFileName && _previousLogEventTimestamp.HasValue) - previousLogEventTimestamp = _previousLogEventTimestamp.Value; - - bool archiveOccurred = TryArchiveFile(fileName, logEvent, bytesToWrite.Count, previousLogEventTimestamp, initializedNewFile); - if (archiveOccurred) - initializedNewFile = InitializeFile(fileName, logEvent) == DateTime.MinValue || initializedNewFile; - - if (ReplaceFileContentsOnEachWrite) - { - ReplaceFileContent(fileName, bytesToWrite, true); - } - else - { - WriteToFile(fileName, bytesToWrite, initializedNewFile); - } - - _previousLogFileName = fileName; - _previousLogEventTimestamp = logEvent.TimeStamp; - } - - /// - /// Obsolete and replaced by with NLog v5. - /// Formats the log event for write. - /// - /// The log event to be formatted. - /// A string representation of the log event. - [Obsolete("No longer used and replaced by RenderFormattedMessage. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected virtual string GetFormattedMessage(LogEventInfo logEvent) - { - return Layout.Render(logEvent); - } - - /// - /// Obsolete and replaced by with NLog v5. - /// Gets the bytes to be written to the file. - /// - /// Log event. - /// Array of bytes that are ready to be written. - [Obsolete("No longer used and replaced by RenderFormattedMessage. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected virtual byte[] GetBytesToWrite(LogEventInfo logEvent) - { - string text = GetFormattedMessage(logEvent); - int textBytesCount = Encoding.GetByteCount(text); - int newLineBytesCount = Encoding.GetByteCount(NewLineChars); - byte[] bytes = new byte[textBytesCount + newLineBytesCount]; - Encoding.GetBytes(text, 0, text.Length, bytes, 0); - Encoding.GetBytes(NewLineChars, 0, NewLineChars.Length, bytes, textBytesCount); - return TransformBytes(bytes); - } - - /// - /// Obsolete and replaced by with NLog v5. - /// Modifies the specified byte array before it gets sent to a file. - /// - /// The byte array. - /// The modified byte array. The function can do the modification in-place. - [Obsolete("No longer used and replaced by TransformStream. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - protected virtual byte[] TransformBytes(byte[] value) - { - return value; - } - - /// - /// Gets the bytes to be written to the file. - /// - /// The log event to be formatted. - /// to help format log event. - /// Optional temporary char-array to help format log event. - /// Destination for the encoded result. - protected virtual void RenderFormattedMessageToStream(LogEventInfo logEvent, StringBuilder formatBuilder, char[] transformBuffer, MemoryStream streamTarget) + private void RenderFormattedMessageToStream(LogEventInfo logEvent, StringBuilder formatBuilder, char[] transformBuffer, MemoryStream streamTarget) { RenderFormattedMessage(logEvent, formatBuilder); - formatBuilder.Append(NewLineChars); - TransformBuilderToStream(logEvent, formatBuilder, transformBuffer, streamTarget); + formatBuilder.Append(LineEnding.NewLineCharacters); + formatBuilder.CopyToStream(streamTarget, Encoding, transformBuffer); } /// @@ -1189,1353 +695,624 @@ protected virtual void RenderFormattedMessage(LogEventInfo logEvent, StringBuild Layout.Render(logEvent, target); } - private void TransformBuilderToStream(LogEventInfo logEvent, StringBuilder builder, char[] transformBuffer, MemoryStream workStream) - { - builder.CopyToStream(workStream, Encoding, transformBuffer); - TransformStream(logEvent, workStream); - } - - /// - /// Modifies the specified byte array before it gets sent to a file. - /// - /// The LogEvent being written - /// The byte array. - protected virtual void TransformStream(LogEventInfo logEvent, MemoryStream stream) - { - } - - private void AppendMemoryStreamToFile(string currentFileName, LogEventInfo firstLogEvent, MemoryStream ms, out Exception lastException) + private void WriteToFile(string filename, LogEventInfo firstLogEvent, MemoryStream ms, out Exception lastException) { try { ArraySegment bytes = new ArraySegment(ms.GetBuffer(), 0, (int)ms.Length); - ProcessLogEvent(firstLogEvent, currentFileName, bytes); + WriteBytesToFile(filename, firstLogEvent, bytes); lastException = null; } - catch (Exception exception) + catch (Exception ex) { - if (ExceptionMustBeRethrown(exception)) - { + InternalLogger.Error(ex, "{0}: Failed writing to FileName: '{1}'", this, filename); + if (ExceptionMustBeRethrown(ex)) throw; - } - lastException = exception; + lastException = ex; } } - /// - /// Archives fileName to archiveFileName. - /// - /// File name to be archived. - /// Name of the archive file. - private void ArchiveFile(string fileName, string archiveFileName) + private void WriteBytesToFile(string filename, LogEventInfo firstLogEvent, ArraySegment bytes) { - string archiveFolderPath = Path.GetDirectoryName(archiveFileName); - if (archiveFolderPath != null && !Directory.Exists(archiveFolderPath)) - Directory.CreateDirectory(archiveFolderPath); - - if (string.Equals(fileName, archiveFileName, StringComparison.OrdinalIgnoreCase)) + bool hasWritten = true; + if (!_openFileCache.TryGetValue(filename, out var openFile)) { - InternalLogger.Info("{0}: Archiving {1} skipped as ArchiveFileName equals FileName", this, fileName); + hasWritten = false; + openFile = OpenFile(filename, firstLogEvent, null); } - else if (EnableArchiveFileCompression) + + try { - InternalLogger.Info("{0}: Archiving {1} to compressed {2}", this, fileName, archiveFileName); - if (File.Exists(archiveFileName)) + if (ArchiveAboveSize != 0 || ArchiveEvery != FileArchivePeriod.None) { - InternalLogger.Warn("{0}: Failed archiving because compressed file already exists: {1}", this, archiveFileName); + openFile = RollArchiveFile(filename, openFile, firstLogEvent, hasWritten); } - else + + openFile.FileAppender.Write(bytes.Array, bytes.Offset, bytes.Count); + + if (AutoFlush) { - ArchiveFileCompress(fileName, archiveFileName); + openFile.FileAppender.Flush(); } } - else + catch { - InternalLogger.Info("{0}: Archiving {1} to {2}", this, fileName, archiveFileName); - if (File.Exists(archiveFileName)) - { - ArchiveFileAppendExisting(fileName, archiveFileName); - } - else - { - ArchiveFileMove(fileName, archiveFileName); - } + // Close file (any retry-logic must happen inside the FileStream-implementation) + _openFileCache.Remove(filename); + openFile.FileAppender.Dispose(); + throw; + } + finally + { + _lastWriteTime = firstLogEvent.TimeStamp; } } - private void ArchiveFileCompress(string fileName, string archiveFileName) + private OpenFileAppender RollArchiveFile(string filename, OpenFileAppender openFile, LogEventInfo firstLogEvent, bool hasWritten) { - int fileCompressRetryCount = ConcurrentWrites ? ConcurrentWriteAttempts : 2; - for (int i = 1; i <= fileCompressRetryCount; ++i) + var lastSequenceNo = -1; + + bool skipFileLastModified = ArchiveFileName is null; + + while (lastSequenceNo != openFile.SequenceNumber && MustArchiveFile(openFile.FileAppender, firstLogEvent)) { - try - { - if (FileCompressor is IArchiveFileCompressor archiveFileCompressor) - { - string entryName = (ArchiveNumbering != ArchiveNumberingMode.Rolling) ? (Path.GetFileNameWithoutExtension(archiveFileName) + Path.GetExtension(fileName)) : Path.GetFileName(fileName); - archiveFileCompressor.CompressFile(fileName, archiveFileName, entryName); - } - else - { - FileCompressor.CompressFile(fileName, archiveFileName); - } + lastSequenceNo = openFile.SequenceNumber; - break; // Success - } - catch (DirectoryNotFoundException) - { - throw; // Skip retry when directory does not exist - } - catch (FileNotFoundException) - { - throw; // Skip retry when file does not exist - } - catch (IOException ex) - { - if (i == fileCompressRetryCount) - throw; + DateTime? previousFileLastModified = skipFileLastModified ? default(DateTime?) : openFile.FileAppender.FileLastModified; + if (_lastWriteTime > DateTime.MinValue && previousFileLastModified > _lastWriteTime && (previousFileLastModified == openFile.FileAppender.OpenStreamTime || firstLogEvent.TimeStamp.Date == previousFileLastModified?.Date)) + previousFileLastModified = _lastWriteTime; - if (File.Exists(archiveFileName)) - throw; + // Close file and roll to next file + if (hasWritten) + CloseFileWithFooter(filename, openFile, true); + else + CloseFile(filename, openFile); - int sleepTimeMs = i * 50; - InternalLogger.Warn("{0}: Archiving Attempt #{1} to compress {2} to {3} failed - {4} {5}. Sleeping for {6}ms", this, i, fileName, archiveFileName, ex.GetType(), ex.Message, sleepTimeMs); - AsyncHelpers.WaitForDelay(TimeSpan.FromMilliseconds(sleepTimeMs)); - } + hasWritten = false; + openFile = OpenFile(filename, firstLogEvent, previousFileLastModified, openFile.SequenceNumber + 1); } - DeleteAndWaitForFileDelete(fileName); + return openFile; } - private void ArchiveFileAppendExisting(string fileName, string archiveFileName) + private bool MustArchiveFile(IFileAppender fileAppender, LogEventInfo firstLogEvent) { - //todo handle double footer - InternalLogger.Info("{0}: Already exists, append to {1}", this, archiveFileName); + if (ArchiveAboveSize != 0 && MustArchiveBySize(fileAppender)) + return true; - //copy to archive file. - var fileShare = FileShare.ReadWrite; - if (EnableFileDelete) - { - fileShare |= FileShare.Delete; - } + if (ArchiveEvery != FileArchivePeriod.None && MustArchiveEveryTimePeriod(fileAppender, firstLogEvent)) + return true; - using (FileStream fileStream = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, fileShare)) - using (FileStream archiveFileStream = File.Open(archiveFileName, FileMode.Append)) - { - fileStream.CopyAndSkipBom(archiveFileStream, Encoding); - //clear old content - fileStream.SetLength(0); + return false; + } - if (EnableFileDelete && !DeleteOldArchiveFile(fileName)) - { - // Attempt to delete file to reset File-Creation-Time (Delete under file-lock) - fileShare &= ~FileShare.Delete; // Retry after having released file-lock - } + private bool MustArchiveBySize(IFileAppender fileAppender) + { + var currentFileSize = fileAppender.FileSize; + if (currentFileSize == 0 || currentFileSize + 1 < ArchiveAboveSize) + return false; - fileStream.Close(); // This flushes the content, too. -#if !NET35 - archiveFileStream.Flush(true); -#else - archiveFileStream.Flush(); -#endif - } + InternalLogger.Debug("{0}: Archive because of filesize={1} of file: {2}", this, currentFileSize, fileAppender.FilePath); + return true; + } - if ((fileShare & FileShare.Delete) == FileShare.None) - { - DeleteOldArchiveFile(fileName); // Attempt to delete file to reset File-Creation-Time - } + private bool MustArchiveEveryTimePeriod(IFileAppender fileAppender, LogEventInfo firstLogEvent) + { + var nextArchiveTime = fileAppender.NextArchiveTime; + if (nextArchiveTime >= firstLogEvent.TimeStamp) + return false; + + InternalLogger.Debug("{0}: Archive because of filetime of file: {1}", this, fileAppender.FilePath); + return true; } - private void ArchiveFileMove(string fileName, string archiveFileName) + internal static DateTime CalculateNextArchiveEventTime(FileArchivePeriod archiveEvery, DateTime fileBirthTime) { - try + switch (archiveEvery) { - InternalLogger.Debug("{0}: Move file from '{1}' to '{2}'", this, fileName, archiveFileName); - File.Move(fileName, archiveFileName); + case FileArchivePeriod.Year: return new DateTime(fileBirthTime.Year, 1, 1, 0, 0, 0, fileBirthTime.Kind).AddYears(1); + case FileArchivePeriod.Month: return new DateTime(fileBirthTime.Year, fileBirthTime.Month, 1, 0, 0, 0, fileBirthTime.Kind).AddMonths(1); + case FileArchivePeriod.Day: return new DateTime(fileBirthTime.Year, fileBirthTime.Month, fileBirthTime.Day, 0, 0, 0, fileBirthTime.Kind).AddDays(1); + case FileArchivePeriod.Hour: return new DateTime(fileBirthTime.Year, fileBirthTime.Month, fileBirthTime.Day, fileBirthTime.Hour, 0, 0, fileBirthTime.Kind).AddHours(1); + case FileArchivePeriod.Minute: return new DateTime(fileBirthTime.Year, fileBirthTime.Month, fileBirthTime.Day, fileBirthTime.Hour, fileBirthTime.Minute, 0, fileBirthTime.Kind).AddMinutes(1); + case FileArchivePeriod.Monday: return CalculateNextWeekday(fileBirthTime, DayOfWeek.Monday); + case FileArchivePeriod.Tuesday: return CalculateNextWeekday(fileBirthTime, DayOfWeek.Tuesday); + case FileArchivePeriod.Wednesday: return CalculateNextWeekday(fileBirthTime, DayOfWeek.Wednesday); + case FileArchivePeriod.Thursday: return CalculateNextWeekday(fileBirthTime, DayOfWeek.Thursday); + case FileArchivePeriod.Friday: return CalculateNextWeekday(fileBirthTime, DayOfWeek.Friday); + case FileArchivePeriod.Saturday: return CalculateNextWeekday(fileBirthTime, DayOfWeek.Saturday); + case FileArchivePeriod.Sunday: return CalculateNextWeekday(fileBirthTime, DayOfWeek.Sunday); + default: return DateTime.MaxValue; } - catch (IOException ex) - { - if (IsSimpleKeepFileOpen) - throw; // No need to retry, when only single process access + } - if (!EnableFileDelete && KeepFileOpen) - throw; // No need to retry when file delete has been disabled + /// + /// Calculate the DateTime of the requested day of the week. + /// + /// The DateTime of the previous log event. + /// The next occurring day of the week to return a DateTime for. + /// The DateTime of the next occurring dayOfWeek. + /// For example: if previousLogEventTimestamp is Thursday 2017-03-02 and dayOfWeek is Sunday, this will return + /// Sunday 2017-03-05. If dayOfWeek is Thursday, this will return *next* Thursday 2017-03-09. + public static DateTime CalculateNextWeekday(DateTime previousLogEventTimestamp, DayOfWeek dayOfWeek) + { + // Shamelessly taken from https://stackoverflow.com/a/7611480/1354930 + int start = (int)previousLogEventTimestamp.DayOfWeek; + int target = (int)dayOfWeek; + if (target <= start) + target += 7; + return previousLogEventTimestamp.Date.AddDays(target - start); + } - if (ConcurrentWrites && !MutexDetector.SupportsSharableMutex) - throw; // No need to retry when not having a real archive mutex to protect us + private OpenFileAppender OpenFile(string filename, LogEventInfo firstLogEvent, DateTime? previousFileLastModified, int sequenceNumber = 0) + { + bool createDirs = sequenceNumber == 0 && CreateDirs && _openFileCache.Count == 0; - // It is possible to move a file while other processes has open file-handles. - // Unless the other process is actively writing, then the file move might fail. - // We are already holding the archive-mutex, so lets retry if things are stable - InternalLogger.Warn(ex, "{0}: Archiving failed. Checking for retry move of {1} to {2}.", this, fileName, archiveFileName); - if (!File.Exists(fileName) || File.Exists(archiveFileName)) - throw; + PruneOpenFileCache(); - AsyncHelpers.WaitForDelay(TimeSpan.FromMilliseconds(50)); + sequenceNumber = FileAchiveHandler.ArchiveBeforeOpenFile(filename, firstLogEvent, previousFileLastModified, sequenceNumber); + var fullFilePath = BuildFullFilePath(filename, sequenceNumber); - if (!File.Exists(fileName) || File.Exists(archiveFileName)) - throw; + if (createDirs) + { + InternalLogger.Debug("{0}: Verify directory and creating writer to file: {1}", this, fullFilePath); - InternalLogger.Debug("{0}: Archiving retrying move of {1} to {2}.", this, fileName, archiveFileName); - File.Move(fileName, archiveFileName); + var directory = Path.GetDirectoryName(fullFilePath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } } - } - - private bool DeleteOldArchiveFile(string fileName) - { - try + else { - InternalLogger.Info("{0}: Deleting old archive file: '{1}'.", this, fileName); - CloseInvalidFileHandle(fileName); - File.Delete(fileName); - return true; + InternalLogger.Debug("{0}: Creating writer to file: {1}", this, fullFilePath); } - catch (DirectoryNotFoundException exception) + + var fileAppender = CreateFileAppender(fullFilePath); + var openFile = new OpenFileAppender(fileAppender, sequenceNumber); + _openFileCache[filename] = openFile; + + if (_openFileCache.Count == 1) { - //never rethrow this, as this isn't an exceptional case. - InternalLogger.Debug(exception, "{0}: Failed to delete old log file '{1}' as directory is missing.", this, fileName); - return false; + _openFileMonitorTimer?.Change(OpenFileMonitorTimerInterval * 1000, Timeout.Infinite); } - catch (Exception exception) - { - InternalLogger.Warn(exception, "{0}: Failed to delete old archive file: '{1}'.", this, fileName); - if (ExceptionMustBeRethrown(exception)) - { - throw; - } - return false; - } + return openFile; } - private void DeleteAndWaitForFileDelete(string fileName) + private void PruneOpenFileCache() { - try + while (_openFileCache.Count > 0) { - InternalLogger.Trace("{0}: Waiting for file delete of '{1}' for 12 sec", this, fileName); - var originalFileCreationTime = (new FileInfo(fileName)).CreationTime; - if (DeleteOldArchiveFile(fileName) && File.Exists(fileName)) + // Close files if the filepath no longer exists (without writing footer) + KeyValuePair openFileDeleted = default; + foreach (var openFile in _openFileCache) { - FileInfo currentFileInfo; - for (int i = 0; i < 120; ++i) + if (!openFile.Value.FileAppender.VerifyFileExists()) { - AsyncHelpers.WaitForDelay(TimeSpan.FromMilliseconds(100)); - currentFileInfo = new FileInfo(fileName); - if (!currentFileInfo.Exists || currentFileInfo.CreationTime != originalFileCreationTime) - return; + openFileDeleted = openFile; + break; } - - InternalLogger.Warn("{0}: Timeout while deleting old archive file: '{1}'.", this, fileName); } - } - catch (Exception exception) - { - InternalLogger.Warn(exception, "{0}: Failed to delete old archive file: '{1}'.", this, fileName); - if (ExceptionMustBeRethrown(exception)) - { - throw; - } - } - } - /// - /// Gets the correct formatting to be used based on the value of for converting values which will be inserting into file - /// names during archiving. - /// - /// This value will be computed only when a empty value or is passed into - /// - /// Date format to used irrespectively of value. - /// Formatting for dates. - private string GetArchiveDateFormatString(string defaultFormat) - { - // If archiveDateFormat is not set in the config file, use a default - // date format string based on the archive period. - if (!string.IsNullOrEmpty(defaultFormat)) - return defaultFormat; + if (string.IsNullOrEmpty(openFileDeleted.Key)) + break; - switch (ArchiveEvery) - { - case FileArchivePeriod.Year: - return "yyyy"; - case FileArchivePeriod.Month: - return "yyyyMM"; - case FileArchivePeriod.Hour: - return "yyyyMMddHH"; - case FileArchivePeriod.Minute: - return "yyyyMMddHHmm"; - default: - return "yyyyMMdd"; // Also for Weekdays + CloseFile(openFileDeleted.Key, openFileDeleted.Value); } - } - - private DateTime? GetArchiveDate(string fileName, LogEventInfo logEvent, DateTime previousLogEventTimestamp) - { - // Using File LastModified to handle FileArchivePeriod.Month (where file creation time is one month ago) - var fileLastModifiedUtc = _fileAppenderCache.GetFileLastWriteTimeUtc(fileName); - InternalLogger.Trace("{0}: Calculating archive date. File-LastModifiedUtc: {1}; Previous LogEvent-TimeStamp: {2}", this, fileLastModifiedUtc, previousLogEventTimestamp); - if (!fileLastModifiedUtc.HasValue) + while (_openFileCache.Count >= OpenFileCacheSize) { - if (previousLogEventTimestamp == DateTime.MinValue) + // Close the oldest filestream (not the least recently used) + DateTime oldestFileTime = DateTime.MaxValue; + KeyValuePair oldestOpenFile = default; + foreach (var oldOpenFile in _openFileCache) { - InternalLogger.Info("{0}: Unable to acquire useful timestamp to archive file: {1}", this, fileName); - return null; - } - return previousLogEventTimestamp; - } - - var lastWriteTimeSource = Time.TimeSource.Current.FromSystemTime(fileLastModifiedUtc.Value); - if (previousLogEventTimestamp != DateTime.MinValue) - { - if (previousLogEventTimestamp > lastWriteTimeSource) - { - InternalLogger.Trace("{0}: Using previous LogEvent-TimeStamp {1}, because more recent than File-LastModified {2}", this, previousLogEventTimestamp, lastWriteTimeSource); - return previousLogEventTimestamp; - } - - if (PreviousLogOverlappedPeriod(logEvent, previousLogEventTimestamp, lastWriteTimeSource)) - { - InternalLogger.Trace("{0}: Using previous LogEvent-TimeStamp {1}, because archive period is overlapping with File-LastModified {2}", this, previousLogEventTimestamp, lastWriteTimeSource); - return previousLogEventTimestamp; + if (oldOpenFile.Value.FileAppender.OpenStreamTime < oldestFileTime) + { + oldestOpenFile = oldOpenFile; + } } + if (!string.IsNullOrEmpty(oldestOpenFile.Key)) + break; - if (!AutoFlush && IsSimpleKeepFileOpen && previousLogEventTimestamp < lastWriteTimeSource) - { - InternalLogger.Trace("{0}: Using previous LogEvent-TimeStamp {1}, because AutoFlush=false affects File-LastModified {2}", this, previousLogEventTimestamp, lastWriteTimeSource); - return previousLogEventTimestamp; - } + CloseFileWithFooter(oldestOpenFile.Key, oldestOpenFile.Value, false); } - - InternalLogger.Trace("{0}: Using last write time: {1}", this, lastWriteTimeSource); - return lastWriteTimeSource; } - private bool PreviousLogOverlappedPeriod(LogEventInfo logEvent, DateTime previousLogEventTimestamp, DateTime lastFileWrite) + internal void CloseOpenFileBeforeArchiveCleanup(string filepath) { - string formatString = GetArchiveDateFormatString(string.Empty); - string lastWriteTimeString = lastFileWrite.ToString(formatString, CultureInfo.InvariantCulture); - string logEventTimeString = logEvent.TimeStamp.ToString(formatString, CultureInfo.InvariantCulture); + KeyValuePair foundOpenFile; - if (lastWriteTimeString != logEventTimeString) - return false; - - DateTime? periodAfterPreviousLogEventTime = CalculateNextArchiveEventTime(previousLogEventTimestamp); - if (!periodAfterPreviousLogEventTime.HasValue) - return false; + string fileName = _openFileCache.Count > 0 ? Path.GetFileName(filepath) : string.Empty; - string periodAfterPreviousLogEventTimeString = periodAfterPreviousLogEventTime.Value.ToString(formatString, CultureInfo.InvariantCulture); - return lastWriteTimeString == periodAfterPreviousLogEventTimeString; - } - - DateTime? CalculateNextArchiveEventTime(DateTime timestamp) - { - switch (ArchiveEvery) + do { - case FileArchivePeriod.Year: - return timestamp.AddYears(1); - case FileArchivePeriod.Month: - return timestamp.AddMonths(1); - case FileArchivePeriod.Day: - return timestamp.AddDays(1); - case FileArchivePeriod.Hour: - return timestamp.AddHours(1); - case FileArchivePeriod.Minute: - return timestamp.AddMinutes(1); - case FileArchivePeriod.Sunday: - return CalculateNextWeekday(timestamp, DayOfWeek.Sunday); - case FileArchivePeriod.Monday: - return CalculateNextWeekday(timestamp, DayOfWeek.Monday); - case FileArchivePeriod.Tuesday: - return CalculateNextWeekday(timestamp, DayOfWeek.Tuesday); - case FileArchivePeriod.Wednesday: - return CalculateNextWeekday(timestamp, DayOfWeek.Wednesday); - case FileArchivePeriod.Thursday: - return CalculateNextWeekday(timestamp, DayOfWeek.Thursday); - case FileArchivePeriod.Friday: - return CalculateNextWeekday(timestamp, DayOfWeek.Friday); - case FileArchivePeriod.Saturday: - return CalculateNextWeekday(timestamp, DayOfWeek.Saturday); - default: - return null; - } - } + foundOpenFile = default; - /// - /// Calculate the DateTime of the requested day of the week. - /// - /// The DateTime of the previous log event. - /// The next occurring day of the week to return a DateTime for. - /// The DateTime of the next occurring dayOfWeek. - /// For example: if previousLogEventTimestamp is Thursday 2017-03-02 and dayOfWeek is Sunday, this will return - /// Sunday 2017-03-05. If dayOfWeek is Thursday, this will return *next* Thursday 2017-03-09. - public static DateTime CalculateNextWeekday(DateTime previousLogEventTimestamp, DayOfWeek dayOfWeek) - { - // Shamelessly taken from https://stackoverflow.com/a/7611480/1354930 - int start = (int)previousLogEventTimestamp.DayOfWeek; - int target = (int)dayOfWeek; - if (target <= start) - target += 7; - return previousLogEventTimestamp.AddDays(target - start); - } - - /// - /// Invokes the archiving process after determining when and which type of archiving is required. - /// - /// File name to be checked and archived. - /// Log event that the instance is currently processing. - /// The DateTime of the previous log event for this file. - /// File has just been opened. - private void DoAutoArchive(string fileName, LogEventInfo eventInfo, DateTime previousLogEventTimestamp, bool initializedNewFile) - { - InternalLogger.Debug("{0}: Do archive file: '{1}'", this, fileName); - var fileInfo = new FileInfo(fileName); - if (!fileInfo.Exists) - { - CloseInvalidFileHandle(fileName); // Close possible stale file handles - return; - } - - string archiveFilePattern = GetArchiveFileNamePattern(fileName, eventInfo); - if (string.IsNullOrEmpty(archiveFilePattern)) - { - InternalLogger.Warn("{0}: Skip auto archive because archiveFilePattern is blank", this); - return; - } - - DateTime? archiveDate = GetArchiveDate(fileName, eventInfo, previousLogEventTimestamp); - - var archiveFileName = GenerateArchiveFileNameAfterCleanup(fileName, fileInfo, archiveFilePattern, archiveDate, initializedNewFile); - if (!string.IsNullOrEmpty(archiveFileName)) - { - ArchiveFile(fileInfo.FullName, archiveFileName); - } - } - - private string GenerateArchiveFileNameAfterCleanup(string fileName, FileInfo fileInfo, string archiveFilePattern, DateTime? archiveDate, bool initializedNewFile) - { - InternalLogger.Trace("{0}: Archive pattern '{1}'", this, archiveFilePattern); - - var fileArchiveStyle = GetFileArchiveHelper(archiveFilePattern); - var existingArchiveFiles = fileArchiveStyle.GetExistingArchiveFiles(archiveFilePattern); - - if (MaxArchiveFiles == 1) - { - InternalLogger.Trace("{0}: MaxArchiveFiles = 1", this); - // Perform archive cleanup before generating the next filename, - // as next archive-filename can be affected by existing files. - for (int i = existingArchiveFiles.Count - 1; i >= 0; i--) + foreach (var openFile in _openFileCache) { - var oldArchiveFile = existingArchiveFiles[i]; - if (!string.Equals(oldArchiveFile.FileName, fileInfo.FullName, StringComparison.OrdinalIgnoreCase)) + var openFileName = Path.GetFileName(openFile.Value.FileAppender.FilePath); + if (string.Equals(fileName, openFileName, StringComparison.OrdinalIgnoreCase)) { - DeleteOldArchiveFile(oldArchiveFile.FileName); - existingArchiveFiles.RemoveAt(i); + foundOpenFile = openFile; + break; } } - if (initializedNewFile && string.Equals(Path.GetDirectoryName(archiveFilePattern), fileInfo.DirectoryName, StringComparison.OrdinalIgnoreCase)) - { - DeleteOldArchiveFile(fileName); - return null; - } - } - - var archiveFileName = archiveDate.HasValue ? fileArchiveStyle.GenerateArchiveFileName(archiveFilePattern, archiveDate.Value, existingArchiveFiles) : null; - if (archiveFileName is null) - return null; - - if (!initializedNewFile) - { - FinalizeFile(fileName, isArchiving: true); - } - - if (existingArchiveFiles.Count > 0) - { - CleanupOldArchiveFiles(fileInfo, archiveFilePattern, existingArchiveFiles, archiveFileName); - } - - return archiveFileName.FileName; - } - - private void CleanupOldArchiveFiles(FileInfo currentFile, string archiveFilePattern, List existingArchiveFiles, DateAndSequenceArchive newArchiveFile = null) - { - var fileArchiveStyle = GetFileArchiveHelper(archiveFilePattern); - - if (fileArchiveStyle.IsArchiveCleanupEnabled) - { - if (currentFile != null) - ExcludeActiveFileFromOldArchiveFiles(currentFile, existingArchiveFiles); - - if (newArchiveFile != null) - existingArchiveFiles.Add(newArchiveFile); - - var cleanupArchiveFiles = fileArchiveStyle.CheckArchiveCleanup(archiveFilePattern, existingArchiveFiles, MaxArchiveFiles, MaxArchiveDays); - foreach (var oldArchiveFile in cleanupArchiveFiles) + if (foundOpenFile.Key != null) { - DeleteOldArchiveFile(oldArchiveFile.FileName); + InternalLogger.Debug("{0}: Archive cleanup closing file: {1}", this, filepath); + CloseFile(foundOpenFile.Key, foundOpenFile.Value); } - } + } while (foundOpenFile.Key != null); } - private static void ExcludeActiveFileFromOldArchiveFiles(FileInfo currentFile, List existingArchiveFiles) + private void CloseFile(string filename, OpenFileAppender openFile) { - if (existingArchiveFiles.Count > 0) + try { - var archiveDirectory = Path.GetDirectoryName(existingArchiveFiles[0].FileName); - if (string.Equals(archiveDirectory, currentFile.DirectoryName, StringComparison.OrdinalIgnoreCase)) + _openFileCache.Remove(filename); + if (_openFileCache.Count == 0) { - // Extra handling when archive-directory is the same as logging-directory - for (int i = 0; i < existingArchiveFiles.Count; ++i) - { - if (string.Equals(existingArchiveFiles[i].FileName, currentFile.FullName, StringComparison.OrdinalIgnoreCase)) - { - existingArchiveFiles.RemoveAt(i); - break; - } - } + _openFileMonitorTimer?.Change(Timeout.Infinite, Timeout.Infinite); } } - } - - /// - /// Gets the pattern that archive files will match - /// - /// Filename of the log file - /// Log event that the instance is currently processing. - /// A string with a pattern that will match the archive filenames - private string GetArchiveFileNamePattern(string fileName, LogEventInfo eventInfo) - { - if (_fullArchiveFileName is null) - { - if (EnableArchiveFileCompression) - return Path.ChangeExtension(fileName, ".zip"); - else - return fileName; - } - else + finally { - //The archive file name is given. There are two possibilities - //(1) User supplied the Filename with pattern - //(2) User supplied the normal filename - string archiveFileName = _fullArchiveFileName.Render(eventInfo); - return archiveFileName; + openFile.FileAppender.Dispose(); } } - /// - /// Archives the file if it should be archived. - /// - /// The file name to check for. - /// Log event that the instance is currently processing. - /// The size in bytes of the next chunk of data to be written in the file. - /// The DateTime of the previous log event for this file. - /// File has just been opened. - /// True when archive operation of the file was completed (by this target or a concurrent target) - private bool TryArchiveFile(string fileName, LogEventInfo ev, int upcomingWriteSize, DateTime previousLogEventTimestamp, bool initializedNewFile) + private void CloseFileWithFooter(string filename, OpenFileAppender openFile, bool archiveFile) { - if (!IsArchivingEnabled) - return false; - - string archiveFile = string.Empty; - - BaseFileAppender archivedAppender = null; - try { - archiveFile = GetArchiveFileName(fileName, ev, upcomingWriteSize, previousLogEventTimestamp, initializedNewFile); - if (!string.IsNullOrEmpty(archiveFile)) + if (!ReplaceFileContentsOnEachWrite && (!WriteFooterOnArchivingOnly || archiveFile)) { - archivedAppender = TryCloseFileAppenderBeforeArchive(fileName, archiveFile); + var footerBytes = GetFooterLayoutBytes(); + if (footerBytes?.Length > 0) + { + openFile.FileAppender.Write(footerBytes, 0, footerBytes.Length); + } } - - // Closes all file handles if any archive operation has been detected by file-watcher - _fileAppenderCache.InvalidateAppendersForArchivedFiles(); } - catch (Exception exception) + catch (Exception ex) { - InternalLogger.Warn(exception, "{0}: Failed to check archive for file '{1}'.", this, fileName); - if (ExceptionMustBeRethrown(exception)) - { + InternalLogger.Error(ex, "{0}: Failed closing file: '{1}'", this, filename); + if (ex.MustBeRethrownImmediately()) throw; - } - } - - if (string.IsNullOrEmpty(archiveFile)) - return false; - - try - { - try - { - if (archivedAppender is BaseMutexFileAppender mutexFileAppender && mutexFileAppender.ArchiveMutex != null) - { - mutexFileAppender.ArchiveMutex.WaitOne(); - } - else if (!IsSimpleKeepFileOpen) - { - InternalLogger.Debug("{0}: Archive mutex not available for file '{1}'", this, archiveFile); - } - } - catch (AbandonedMutexException) - { - // ignore the exception, another process was killed without properly releasing the mutex - // the mutex has been acquired, so proceed to writing - // See: https://msdn.microsoft.com/en-us/library/system.threading.abandonedmutexexception.aspx - } - - ArchiveFileAfterCloseFileAppender(archivedAppender, archiveFile, ev, upcomingWriteSize, previousLogEventTimestamp); - return true; } finally { - if (archivedAppender is BaseMutexFileAppender mutexFileAppender) - mutexFileAppender.ArchiveMutex?.ReleaseMutex(); - - archivedAppender?.Dispose(); // Dispose of Archive Mutex + CloseFile(filename, openFile); } } - /// - /// Closes any active file-appenders that matches the input filenames. - /// File-appender is requested to invalidate/close its filehandle, but keeping its archive-mutex alive - /// - private BaseFileAppender TryCloseFileAppenderBeforeArchive(string fileName, string archiveFile) + private void OpenFileMonitorTimer(object state) { - InternalLogger.Trace("{0}: Archive attempt for file '{1}'", this, archiveFile); - BaseFileAppender archivedAppender = _fileAppenderCache.InvalidateAppender(fileName); - if (fileName != archiveFile) - { - var fileAppender = _fileAppenderCache.InvalidateAppender(archiveFile); - archivedAppender = archivedAppender ?? fileAppender; - } - - if (!string.IsNullOrEmpty(_previousLogFileName) && _previousLogFileName != archiveFile && _previousLogFileName != fileName) - { - var fileAppender = _fileAppenderCache.InvalidateAppender(_previousLogFileName); - archivedAppender = archivedAppender ?? fileAppender; - } + bool startTimer = !(_openFileMonitorTimer is null); - return archivedAppender; - } - - private void ArchiveFileAfterCloseFileAppender(BaseFileAppender archivedAppender, string archiveFile, LogEventInfo ev, int upcomingWriteSize, DateTime previousLogEventTimestamp) - { try { - DateTime fallbackFileCreationTimeSource = previousLogEventTimestamp; - - if (archivedAppender != null && IsSimpleKeepFileOpen) + lock (SyncRoot) { - var fileCreationTimeUtc = archivedAppender.GetFileCreationTimeUtc(); - if (fileCreationTimeUtc > DateTime.MinValue) + startTimer = _openFileCache.Count != 0 && !(_openFileMonitorTimer is null); + + if (OpenFileCacheTimeout > 0) { - var fileCreationTimeSource = Time.TimeSource.Current.FromSystemTime(fileCreationTimeUtc.Value); - if (fileCreationTimeSource < fallbackFileCreationTimeSource || fallbackFileCreationTimeSource == DateTime.MinValue) + DateTime closeTime = Time.TimeSource.Current.Time.AddSeconds(-OpenFileCacheTimeout); + bool oldFilesMustBeClosed = false; + foreach (var openFile in _openFileCache) + { + if (openFile.Value.FileAppender.OpenStreamTime < closeTime) + { + oldFilesMustBeClosed = true; + break; + } + } + if (oldFilesMustBeClosed) { - fallbackFileCreationTimeSource = fileCreationTimeSource; + foreach (var openFile in _openFileCache.ToList()) + { + if (openFile.Value.FileAppender.OpenStreamTime < closeTime) + { + CloseFile(openFile.Key, openFile.Value); + } + } } } - } - // Check again if archive is needed. We could have been raced by another process - var validatedArchiveFile = GetArchiveFileName(archiveFile, ev, upcomingWriteSize, fallbackFileCreationTimeSource, false); - if (string.IsNullOrEmpty(validatedArchiveFile)) - { - InternalLogger.Debug("{0}: Skip archiving '{1}' because no longer necessary", this, archiveFile); - _initializedFiles.Remove(archiveFile); - } - else - { - if (archiveFile != validatedArchiveFile) + if (OpenFileFlushTimeout > 0 && !AutoFlush) { - _initializedFiles.Remove(archiveFile); - archiveFile = validatedArchiveFile; + DateTime flushTime = Time.TimeSource.Current.Time.AddSeconds(-(OpenFileFlushTimeout + 1) * 1.5); + if (_lastWriteTime > flushTime) + { + // Only Flush when something has been written + foreach (var openFile in _openFileCache) + { + openFile.Value.FileAppender.Flush(); + } + } } - _initializedFiles.Remove(archiveFile); - - DoAutoArchive(archiveFile, ev, previousLogEventTimestamp, false); - } - if (_previousLogFileName == archiveFile) - { - _previousLogFileName = null; - _previousLogEventTimestamp = null; + startTimer = startTimer && _openFileCache.Count != 0; } } - catch (Exception exception) - { - InternalLogger.Warn(exception, "{0}: Failed to archive file '{1}'.", this, archiveFile); - if (ExceptionMustBeRethrown(exception)) - { - throw; - } - } - } - - /// - /// Indicates if the automatic archiving process should be executed. - /// - /// File name to be written. - /// Log event that the instance is currently processing. - /// The size in bytes of the next chunk of data to be written in the file. - /// The DateTime of the previous log event for this file. - /// File has just been opened. - /// Filename to archive. If null, then nothing to archive. - private string GetArchiveFileName(string fileName, LogEventInfo ev, int upcomingWriteSize, DateTime previousLogEventTimestamp, bool initializedNewFile) - { - fileName = fileName ?? _previousLogFileName; - if (!string.IsNullOrEmpty(fileName)) - { - return GetArchiveFileNameBasedOnFileSize(fileName, upcomingWriteSize, initializedNewFile) ?? - GetArchiveFileNameBasedOnTime(fileName, ev, previousLogEventTimestamp, initializedNewFile); - } - - return null; - } - - /// - /// Returns the correct filename to archive - /// - private string GetPotentialFileForArchiving(string fileName) - { - if (!string.IsNullOrEmpty(fileName)) + catch (Exception ex) { - return fileName; + InternalLogger.Warn(ex, "{0}: Exception in OpenFileMonitorTimer", this); } - - if (!string.IsNullOrEmpty(_previousLogFileName)) + finally { - return _previousLogFileName; + if (startTimer) + _openFileMonitorTimer?.Change(OpenFileMonitorTimerInterval * 1000, Timeout.Infinite); } - - return fileName; } - /// - /// Gets the file name for archiving, or null if archiving should not occur based on file size. - /// - /// File name to be written. - /// The size in bytes of the next chunk of data to be written in the file. - /// File has just been opened. - /// Filename to archive. If null, then nothing to archive. - private string GetArchiveFileNameBasedOnFileSize(string fileName, int upcomingWriteSize, bool initializedNewFile) + internal string BuildFullFilePath(string newFileName, int sequenceNumber, DateTime fileLastModified = default) { - if (ArchiveAboveSize == ArchiveAboveSizeDisabled) - { - return null; - } - - var archiveFileName = GetPotentialFileForArchiving(fileName); - if (string.IsNullOrEmpty(archiveFileName)) + if (sequenceNumber > 0 || fileLastModified != default) { - return null; - } + var fileName = Path.GetFileName(newFileName) ?? string.Empty; + var fileExt = Path.GetExtension(fileName) ?? string.Empty; + newFileName = newFileName.Substring(0, newFileName.Length - fileName.Length); + if (!string.IsNullOrEmpty(fileExt)) + fileName = fileName.Substring(0, fileName.Length - fileExt.Length); - //this is an expensive call - var fileLength = _fileAppenderCache.GetFileLength(archiveFileName); - if (!fileLength.HasValue) - { - archiveFileName = TryFallbackToPreviousLogFileName(archiveFileName, initializedNewFile); - if (!string.IsNullOrEmpty(archiveFileName)) + object fileLastModifiedObj = fileLastModified == default ? "" : (object)fileLastModified; + try { - upcomingWriteSize = 0; - return GetArchiveFileNameBasedOnFileSize(archiveFileName, upcomingWriteSize, false); + newFileName = newFileName + fileName + string.Format(ArchiveSuffixFormat, sequenceNumber, fileLastModifiedObj) + fileExt; } - else + catch (Exception ex) { - return null; + InternalLogger.Error(ex, "{0}: Failed to apply ArchiveSuffixFormat={1} using SequenceNumber={2} for file: '{3}'", this, ArchiveSuffixFormat, sequenceNumber, newFileName); + if (ExceptionMustBeRethrown(ex)) + throw; + newFileName = newFileName + fileName + string.Format(_legacySequenceArchiveSuffixFormat, sequenceNumber) + fileExt; } } - if (archiveFileName != fileName) - { - upcomingWriteSize = 0; // Not going to write to this file - } - - var shouldArchive = (fileLength.Value + upcomingWriteSize) > ArchiveAboveSize; - if (shouldArchive) - { - InternalLogger.Debug("{0}: Start archiving '{1}' because FileSize={2} + {3} is larger than ArchiveAboveSize={4}", this, archiveFileName, fileLength.Value, upcomingWriteSize, ArchiveAboveSize); - return archiveFileName; // Will re-check if archive is still necessary after flush/close file - } - - return null; - } - - /// - /// Check if archive operation should check previous filename, because FileAppenderCache tells us current filename no longer exists - /// - private string TryFallbackToPreviousLogFileName(string archiveFileName, bool initializedNewFile) - { - if (!initializedNewFile && _initializedFiles.Remove(archiveFileName)) - { - // Register current filename needs re-initialization - InternalLogger.Debug("{0}: Invalidate appender as archive file no longer exists: '{1}'", this, archiveFileName); - _fileAppenderCache.InvalidateAppender(archiveFileName)?.Dispose(); - } - - if (!string.IsNullOrEmpty(_previousLogFileName) && !string.Equals(archiveFileName, _previousLogFileName, StringComparison.OrdinalIgnoreCase)) - { - return _previousLogFileName; - } - - return string.Empty; + var filepath = CleanFullFilePath(newFileName); + return filepath; } - /// - /// Returns the file name for archiving, or null if archiving should not occur based on date/time. - /// - /// File name to be written. - /// Log event that the instance is currently processing. - /// The DateTime of the previous log event for this file. - /// File has just been opened. - /// Filename to archive. If null, then nothing to archive. - private string GetArchiveFileNameBasedOnTime(string fileName, LogEventInfo logEvent, DateTime previousLogEventTimestamp, bool initializedNewFile) + internal static string CleanFullFilePath(string filename) { - if (ArchiveEvery == FileArchivePeriod.None) - { - return null; - } + var lastDirSeparator = filename.LastIndexOfAny(DirectorySeparatorChars); - var archiveFileName = GetPotentialFileForArchiving(fileName); - if (string.IsNullOrEmpty(archiveFileName)) - { - return null; - } + char[] fileNameChars = null; // defer char[] memory-allocation until detecting invalid char - DateTime? creationTimeSource = TryGetArchiveFileCreationTimeSource(archiveFileName, previousLogEventTimestamp); - if (!creationTimeSource.HasValue) + for (int i = lastDirSeparator + 1; i < filename.Length; i++) { - archiveFileName = TryFallbackToPreviousLogFileName(archiveFileName, initializedNewFile); - if (!string.IsNullOrEmpty(archiveFileName)) - { - return GetArchiveFileNameBasedOnTime(archiveFileName, logEvent, previousLogEventTimestamp, false); - } - else + if (InvalidFileNameChars.Contains(filename[i])) { - return null; + if (fileNameChars is null) + { + fileNameChars = filename.Substring(lastDirSeparator + 1).ToCharArray(); + } + fileNameChars[i - (lastDirSeparator + 1)] = '_'; } } - DateTime fileCreateTime = TruncateArchiveTime(creationTimeSource.Value, ArchiveEvery); - DateTime logEventTime = TruncateArchiveTime(logEvent.TimeStamp, ArchiveEvery); - if (fileCreateTime != logEventTime) + //only if an invalid char was replaced do we create a new string. + if (fileNameChars != null) { - string formatString = GetArchiveDateFormatString(string.Empty); - var validLogEventTime = EnsureValidLogEventTimeStamp(logEvent.TimeStamp, creationTimeSource.Value); - string fileCreated = creationTimeSource.Value.ToString(formatString, CultureInfo.InvariantCulture); - string logEventRecorded = validLogEventTime.ToString(formatString, CultureInfo.InvariantCulture); - var shouldArchive = fileCreated != logEventRecorded; - if (shouldArchive) - { - InternalLogger.Debug("{0}: Start archiving '{1}' because FileCreatedTime='{2}' is older than now '{3}' using ArchiveEvery='{4}'", this, archiveFileName, fileCreated, logEventRecorded, formatString); - return archiveFileName; // Will re-check if archive is still necessary after flush/close file - } + //keep the / in the dirname, because dirname could be c:/ and combine of c: and file name won't work well. + var dirName = lastDirSeparator > 0 ? filename.Substring(0, lastDirSeparator + 1) : string.Empty; + filename = Path.Combine(dirName, new string(fileNameChars)); } - return null; + var filepath = FileInfoHelper.IsRelativeFilePath(filename) ? Path.Combine(LogManager.LogFactory.CurrentAppEnvironment.AppDomainBaseDirectory, filename) : filename; + filepath = Path.GetFullPath(filepath); + return filepath; } + private static readonly char[] DirectorySeparatorChars = new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + private static readonly HashSet InvalidFileNameChars = new HashSet(Path.GetInvalidFileNameChars()); - private DateTime? TryGetArchiveFileCreationTimeSource(string fileName, DateTime previousLogEventTimestamp) + private static bool InitialValueBom(Encoding encoding) { - // Linux FileSystems doesn't always have file-birth-time, so NLog tries to provide a little help - DateTime? fallbackTimeSourceLinux = (previousLogEventTimestamp != DateTime.MinValue && IsSimpleKeepFileOpen) ? previousLogEventTimestamp : (DateTime?)null; - var creationTimeSource = _fileAppenderCache.GetFileCreationTimeSource(fileName, fallbackTimeSourceLinux); - if (!creationTimeSource.HasValue) - return null; - - if (previousLogEventTimestamp > DateTime.MinValue && previousLogEventTimestamp < creationTimeSource) - { - if (TruncateArchiveTime(previousLogEventTimestamp, FileArchivePeriod.Minute) < TruncateArchiveTime(creationTimeSource.Value, FileArchivePeriod.Minute) && PlatformDetector.IsUnix) - { - if (IsSimpleKeepFileOpen) - { - InternalLogger.Debug("{0}: Adjusted file creation time from {1} to {2}. Linux FileSystem probably don't support file birthtime.", this, creationTimeSource, previousLogEventTimestamp); - creationTimeSource = previousLogEventTimestamp; - } - else - { - InternalLogger.Debug("{0}: File creation time {1} newer than previous file write time {2}. Linux FileSystem probably don't support file birthtime, unless multiple applications are writing to the same file. Configure FileTarget.KeepFileOpen=true AND FileTarget.ConcurrentWrites=false, so NLog can fix this.", this, creationTimeSource, previousLogEventTimestamp); - } - } - } - - return creationTimeSource; + // Initial of true for UTF 16 and UTF 32 + const int utf16 = 1200; + const int utf16Be = 1201; + const int utf32 = 12000; + const int urf32Be = 12001; + var codePage = encoding?.CodePage ?? 0; + return codePage == utf16 + || codePage == utf16Be + || codePage == utf32 + || codePage == urf32Be; } /// - /// Truncates the input-time, so comparison of low resolution times (like dates) are not affected by ticks + /// The sequence of to be written in a file after applying any formatting and any + /// transformations required from the . /// - /// High resolution Time - /// Time Resolution Level - /// Truncated Low Resolution Time - private static DateTime TruncateArchiveTime(DateTime input, FileArchivePeriod resolution) + /// The layout used to render output message. + /// Sequence of to be written. + /// Usually it is used to render the header and hooter of the files. + private byte[] GetLayoutBytes(Layout layout) { - switch (resolution) + if (layout is null) { - case FileArchivePeriod.Year: - return new DateTime(input.Year, 1, 1, 0, 0, 0, 0, input.Kind); - case FileArchivePeriod.Month: - return new DateTime(input.Year, input.Month, 1, 0, 0, 0, input.Kind); - case FileArchivePeriod.Day: - return input.Date; - case FileArchivePeriod.Hour: - return input.AddTicks(-(input.Ticks % TimeSpan.TicksPerHour)); - case FileArchivePeriod.Minute: - return input.AddTicks(-(input.Ticks % TimeSpan.TicksPerMinute)); - case FileArchivePeriod.Sunday: - return CalculateNextWeekday(input.Date, DayOfWeek.Sunday); - case FileArchivePeriod.Monday: - return CalculateNextWeekday(input.Date, DayOfWeek.Monday); - case FileArchivePeriod.Tuesday: - return CalculateNextWeekday(input.Date, DayOfWeek.Tuesday); - case FileArchivePeriod.Wednesday: - return CalculateNextWeekday(input.Date, DayOfWeek.Wednesday); - case FileArchivePeriod.Thursday: - return CalculateNextWeekday(input.Date, DayOfWeek.Thursday); - case FileArchivePeriod.Friday: - return CalculateNextWeekday(input.Date, DayOfWeek.Friday); - case FileArchivePeriod.Saturday: - return CalculateNextWeekday(input.Date, DayOfWeek.Saturday); - default: - return input; // Unknown time-resolution-truncate, leave unchanged + return ArrayHelper.Empty(); } - } - - private void AutoCloseAppendersAfterArchive(object sender, EventArgs state) - { - bool lockTaken = Monitor.TryEnter(SyncRoot, TimeSpan.FromSeconds(2)); - if (!lockTaken) - return; // Archive events triggered by FileWatcher are important, but not life critical - try + using (var targetBuilder = ReusableLayoutBuilder.Allocate()) + using (var targetBuffer = _reusableEncodingBuffer.Allocate()) { - if (!IsInitialized) + layout.Render(LogEventInfo.CreateNullEvent(), targetBuilder.Result); + targetBuilder.Result.Append(LineEnding.NewLineCharacters); + using (MemoryStream ms = new MemoryStream(targetBuilder.Result.Length)) { - return; + targetBuilder.Result.CopyToStream(ms, Encoding, targetBuffer.Result); + return ms.ToArray(); } - - InternalLogger.Trace("{0}: Auto Close FileAppenders after archive", this); - _fileAppenderCache.CloseExpiredAppenders(DateTime.MinValue); } - catch (Exception exception) - { -#if DEBUG - if (exception.MustBeRethrownImmediately()) - { - throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) - } -#endif + } - InternalLogger.Warn(exception, "{0}: Exception in AutoCloseAppendersAfterArchive", this); - } - finally + internal byte[] GetFooterLayoutBytes() + { + if (Footer != null) { - Monitor.Exit(SyncRoot); + InternalLogger.Trace("{0}: Write footer", this); + return GetLayoutBytes(Footer); } + return ArrayHelper.Empty(); } - private void AutoClosingTimerCallback(object sender, EventArgs state) + internal Stream CreateFileStreamWithRetry(IFileAppender fileAppender, int bufferSize, bool initialFileOpen) { - bool lockTaken = Monitor.TryEnter(SyncRoot, TimeSpan.FromSeconds(0.5)); - if (!lockTaken) - return; // Timer will trigger again, no need for timers to queue up + int currentDelay = 1; + int retryCount = KeepFileOpen ? 0 : 5; + var filePath = fileAppender.FilePath; - try + for (int i = 0; i <= retryCount; ++i) { - if (!IsInitialized) - { - return; - } - - if (OpenFileCacheTimeout > 0) - { - DateTime expireTimeUtc = DateTime.UtcNow.AddSeconds(-OpenFileCacheTimeout); - InternalLogger.Trace("{0}: Auto Close FileAppenders", this); - _fileAppenderCache.CloseExpiredAppenders(expireTimeUtc); - } - - if (OpenFileFlushTimeout > 0 && !AutoFlush) + try { - ConditionalFlushOpenFileAppenders(); + return OpenNewFileStream(filePath, bufferSize, initialFileOpen); } - } - catch (Exception exception) - { -#if DEBUG - if (exception.MustBeRethrownImmediately()) + catch (DirectoryNotFoundException) { - throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) - } -#endif + if (!CreateDirs) + throw; - InternalLogger.Warn(exception, "{0}: Exception in AutoClosingTimerCallback", this); - } - finally - { - Monitor.Exit(SyncRoot); - } - } + InternalLogger.Debug("{0}: DirectoryNotFoundException - Attempting to create directory for FileName: {1}", this, filePath); - private void ConditionalFlushOpenFileAppenders() - { - DateTime flushTime = Time.TimeSource.Current.Time.AddSeconds(-Math.Max(OpenFileFlushTimeout, 5) * 2); + var directoryName = Path.GetDirectoryName(filePath); - bool flushAppenders = false; - foreach (var file in _initializedFiles) - { - if (file.Value > flushTime) - { - flushAppenders = true; - break; + try + { + Directory.CreateDirectory(directoryName); + } + catch (Exception ex) + { + // If creating a directory failed, don't retry for this message + throw new NLogRuntimeException($"Could not create directory {directoryName}", ex); + } } - } - - if (flushAppenders) - { - // Only request flush of file-handles, when something has been written - InternalLogger.Trace("{0}: Auto Flush FileAppenders", this); - _fileAppenderCache.FlushAppenders(); - } - } - - /// - /// Evaluates which parts of a file should be written (header, content, footer) based on various properties of - /// instance and writes them. - /// - /// File name to be written. - /// Raw sequence of to be written into the content part of the file. - /// File has just been opened. - private void WriteToFile(string fileName, ArraySegment bytes, bool initializedNewFile) - { - BaseFileAppender appender = _fileAppenderCache.AllocateAppender(fileName); - try - { - if (initializedNewFile) + catch (IOException ex) { - WriteHeaderAndBom(appender); - } - - appender.Write(bytes.Array, bytes.Offset, bytes.Count); + if (i >= retryCount) + { + throw; // rethrow + } - if (AutoFlush) - { - appender.Flush(); + var actualDelay = currentDelay > 4 ? new Random().Next(4, currentDelay) : currentDelay; + InternalLogger.Warn("{0}: Attempt #{1} to open {2} failed - {3} {4}. Sleeping for {5}ms", this, i, filePath, ex.GetType(), ex.Message, actualDelay); + currentDelay *= 4; + System.Threading.Thread.Sleep(actualDelay); } } - catch (Exception ex) - { - InternalLogger.Error(ex, "{0}: Failed write to file '{1}'.", this, fileName); - _fileAppenderCache.InvalidateAppender(fileName)?.Dispose(); - throw; - } + + throw new InvalidOperationException("Should not be reached."); } - /// - /// Initialize a file to be used by the instance. Based on the number of initialized - /// files and the values of various instance properties clean up and/or archiving processes can be invoked. - /// - /// File name to be written. - /// Log event that the instance is currently processing. - /// The DateTime of the previous log event for this file (DateTime.MinValue if just initialized). - private DateTime InitializeFile(string fileName, LogEventInfo logEvent) + private Stream OpenNewFileStream(string filePath, int bufferSize, bool initialFileOpen) { - if (_initializedFiles.Count != 0 && logEvent.TimeStamp == _previousLogEventTimestamp && _previousLogFileName == fileName) - { - return logEvent.TimeStamp; - } + var fileStream = CreateFileStream(filePath, bufferSize); - var now = logEvent.TimeStamp; - if (!_initializedFiles.TryGetValue(fileName, out var lastTime)) + try { - PrepareForNewFile(fileName, logEvent); + bool? fileWasEmpty = null; - _initializedFilesCounter++; - if (_initializedFilesCounter > OpenFileCacheSize) + if (WriteBom) { - // Attempt to write footer, before closing file appender - _initializedFilesCounter = 0; - CleanupInitializedFiles(); - _initializedFilesCounter = Math.Min(_initializedFiles.Count, OpenFileCacheSize / 2); + fileWasEmpty = ReplaceFileContentsOnEachWrite || fileStream.Length == 0; + if (fileWasEmpty == true) + { + InternalLogger.Trace("{0}: Write byte order mark from encoding={1}", this, Encoding); + var preamble = Encoding.GetPreamble(); + if (preamble.Length > 0) + { + fileStream.Write(preamble, 0, preamble.Length); + } + } } - _initializedFiles[fileName] = now; - return DateTime.MinValue; - } - else if (lastTime != now) - { - now = EnsureValidLogEventTimeStamp(now, lastTime); - _initializedFiles[fileName] = now; - } - - return lastTime; - } - - private static DateTime EnsureValidLogEventTimeStamp(DateTime logEventTimeStamp, DateTime previousTimeStamp) - { - // Truncating using DateTime.Date is "expensive", so first check if it look like it is from the past - if (logEventTimeStamp < previousTimeStamp && logEventTimeStamp.Date < previousTimeStamp.Date) - { - // Received LogEvent from the past when comparing to the previous timestamp - var currentTime = TimeSource.Current.Time; - if (logEventTimeStamp.Date < currentTime.AddMinutes(-1).Date) + if (Header != null) { - // It is not because the machine-time has changed. Probably a LogEvent from the past - if (currentTime.Date < previousTimeStamp.Date) - return currentTime; // Previous timestamp is from the future. We choose machine-time - else - return previousTimeStamp; + bool writeHeader = (initialFileOpen && WriteHeaderWhenInitialFileNotEmpty) || ReplaceFileContentsOnEachWrite || (fileWasEmpty ?? fileStream.Length == 0); + if (writeHeader) + { + InternalLogger.Trace("{0}: Write header", this); + var headerBytes = GetLayoutBytes(Header); + fileStream.Write(headerBytes, 0, headerBytes.Length); + } } - } - return logEventTimeStamp; - } - - private void CloseInvalidFileHandle(string fileName) - { - try - { - _fileAppenderCache.InvalidateAppender(fileName)?.Dispose(); - } - finally - { - _initializedFiles.Remove(fileName); // Skip finalize non-existing file - } - } - - /// - /// Writes the file footer and finalizes the file in instance internal structures. - /// - /// File name to close. - /// Indicates if the file is being finalized for archiving. - private void FinalizeFile(string fileName, bool isArchiving = false) - { - try - { - InternalLogger.Trace("{0}: FinalizeFile '{1}, isArchiving: {2}'", this, fileName, isArchiving); - if ((isArchiving) || (!WriteFooterOnArchivingOnly)) - WriteFooter(fileName); + return fileStream; } - finally + catch { - CloseInvalidFileHandle(fileName); - } - } - - /// - /// Writes the footer information to a file. - /// - /// The file path to write to. - private void WriteFooter(string fileName) - { - ArraySegment footerBytes = GetLayoutBytes(Footer); - if (footerBytes.Count > 0 && File.Exists(fileName)) - { - WriteToFile(fileName, footerBytes, false); + fileStream?.Dispose(); + throw; } } /// - /// Decision logic whether to archive logfile on startup. - /// and properties. + /// Creates stream for appending to the specified /// - /// File name to be written. - /// Decision whether to archive or not. - internal bool ShouldArchiveOldFileOnStartup(string fileName) + /// Path of the file to be written + /// Wanted internal buffer size for the stream + /// Stream for appending to the file + protected virtual Stream CreateFileStream(string filePath, int bufferSize) { - if (_archiveOldFileOnStartup == false) + var fileShare = FileShare.Read; + if (EnableFileDelete) { - // explicitly disabled and not the default - return false; + fileShare |= FileShare.Delete; } - var aboveSizeSet = ArchiveOldFileOnStartupAboveSize > 0; - if (aboveSizeSet) + var fileMode = FileMode.Append; + if (ReplaceFileContentsOnEachWrite) { - // Check whether size threshold exceeded - var length = _fileAppenderCache.GetFileLength(fileName); - return length.HasValue && length.Value > ArchiveOldFileOnStartupAboveSize; + fileMode = FileMode.Create; // Create or truncate } - // No size threshold specified, use archiveOldFileOnStartup flag - return _archiveOldFileOnStartup == true; + return new FileStream(filePath, fileMode, FileAccess.Write, fileShare, bufferSize); } - /// - /// Invokes the archiving and clean up of older archive file based on the values of - /// and - /// properties respectively. - /// - /// File name to be written. - /// Log event that the instance is currently processing. - private void PrepareForNewFile(string fileName, LogEventInfo logEvent) + private IFileAppender CreateFileAppender(string filePath) { - InternalLogger.Debug("{0}: Preparing for new file: '{1}'", this, fileName); - RefreshArchiveFilePatternToWatch(fileName, logEvent); - - try - { - if (ShouldArchiveOldFileOnStartup(fileName)) - { - DoAutoArchive(fileName, logEvent, DateTime.MinValue, true); - } - } - catch (Exception exception) - { - InternalLogger.Warn(exception, "{0}: Unable to archive old log file '{1}'.", this, fileName); - if (ExceptionMustBeRethrown(exception)) - { - throw; - } - } - - if (DeleteOldFileOnStartup) + if (DiscardAll) { - DeleteOldArchiveFile(fileName); + return new DiscardAllFileAppender(filePath); } - try - { - string archiveFilePattern = GetArchiveFileNamePattern(fileName, logEvent); - if (!string.IsNullOrEmpty(archiveFilePattern)) - { - var fileArchiveStyle = GetFileArchiveHelper(archiveFilePattern); - if (fileArchiveStyle.AttemptCleanupOnInitializeFile(archiveFilePattern, MaxArchiveFiles, MaxArchiveDays)) - { - var existingArchiveFiles = fileArchiveStyle.GetExistingArchiveFiles(archiveFilePattern); - if (existingArchiveFiles.Count > 0) - { - CleanupOldArchiveFiles(new FileInfo(fileName), archiveFilePattern, existingArchiveFiles); - } - } - } - } - catch (Exception exception) + if (ReplaceFileContentsOnEachWrite) { - InternalLogger.Warn(exception, "FileTarget(Name={0}): Failed to cleanup old archive files when starting on new file: '{1}'", Name, fileName); - - if (ExceptionMustBeRethrown(exception)) - { - throw; - } + return new MinimalFileLockingAppender(this, filePath); } - } - /// - /// Creates the file specified in and writes the file content in each entirety i.e. - /// Header, Content and Footer. - /// - /// The name of the file to be written. - /// Sequence of to be written in the content section of the file. - /// First attempt to write? - /// This method is used when the content of the log file is re-written on every write. - private void ReplaceFileContent(string fileName, ArraySegment bytes, bool firstAttempt) - { - try - { - using (FileStream fs = File.Create(fileName)) - { - ArraySegment headerBytes = GetLayoutBytes(Header); - if (headerBytes.Count > 0) - { - fs.Write(headerBytes.Array, headerBytes.Offset, headerBytes.Count); - } - - fs.Write(bytes.Array, bytes.Offset, bytes.Count); - - ArraySegment footerBytes = GetLayoutBytes(Footer); - if (footerBytes.Count > 0) - { - fs.Write(footerBytes.Array, footerBytes.Offset, footerBytes.Count); - } - } - } - catch (DirectoryNotFoundException) + if (KeepFileOpen) { - if (!CreateDirs || !firstAttempt) - { - throw; - } - Directory.CreateDirectory(Path.GetDirectoryName(fileName)); - //retry. - ReplaceFileContent(fileName, bytes, false); + return new ExclusiveFileLockingAppender(this, filePath); } - } - private static bool InitialValueBom(Encoding encoding) - { - // Initial of true for UTF 16 and UTF 32 - const int utf16 = 1200; - const int utf16Be = 1201; - const int utf32 = 12000; - const int urf32Be = 12001; - var codePage = encoding?.CodePage ?? 0; - return codePage == utf16 - || codePage == utf16Be - || codePage == utf32 - || codePage == urf32Be; + return new MinimalFileLockingAppender(this, filePath); } - /// - /// Writes the header information and byte order mark to a file. - /// - /// File appender associated with the file. - private void WriteHeaderAndBom(BaseFileAppender appender) + private IFileArchiveHandler CreateFileArchiveHandler() { - //performance: cheap check before checking file info - if (Header is null && !WriteBom) return; - - var length = appender.GetFileLength(); - // File is empty or file info cannot be obtained - var isNewOrEmptyFile = length is null || length == 0; - - if (isNewOrEmptyFile && WriteBom) + if (MaxArchiveFiles < 0 && MaxArchiveDays == 0 && ArchiveAboveSize == 0 && ArchiveEvery == FileArchivePeriod.None) { - InternalLogger.Trace("{0}: Write byte order mark from encoding={1}", this, Encoding); - var preamble = Encoding.GetPreamble(); - if (preamble.Length > 0) - appender.Write(preamble, 0, preamble.Length); + if (!DeleteOldFileOnStartup && !ArchiveOldFileOnStartup) + return DisabledFileArchiveHandler.Default; // Archive-logic disabled, always append to active file without rolling + else if (!ArchiveOldFileOnStartup) + return new ZeroFileArchiveHandler(this); // No archive but cleanup old files at startup } - if (Header != null && (isNewOrEmptyFile || WriteHeaderWhenInitialFileNotEmpty)) - { - InternalLogger.Trace("{0}: Write header", this); - ArraySegment headerBytes = GetLayoutBytes(Header); - if (headerBytes.Count > 0) - { - appender.Write(headerBytes.Array, headerBytes.Offset, headerBytes.Count); - } - } - } + if (MaxArchiveFiles == 0) + return new ZeroFileArchiveHandler(this); // MaxArchiveFiles = 0 means truncate active file on archive-event - /// - /// The sequence of to be written in a file after applying any formatting and any - /// transformations required from the . - /// - /// The layout used to render output message. - /// Sequence of to be written. - /// Usually it is used to render the header and hooter of the files. - private ArraySegment GetLayoutBytes(Layout layout) - { - if (layout is null) - { - return default(ArraySegment); - } + if (ArchiveFileName is null) + return new RollingArchiveFileHandler(this); // Updated dynamic sequence handling without file-move-logic - using (var targetBuilder = ReusableLayoutBuilder.Allocate()) - using (var targetBuffer = _reusableEncodingBuffer.Allocate()) - { - var nullEvent = LogEventInfo.CreateNullEvent(); - layout.Render(nullEvent, targetBuilder.Result); - targetBuilder.Result.Append(NewLineChars); - using (MemoryStream ms = new MemoryStream(targetBuilder.Result.Length)) - { - TransformBuilderToStream(nullEvent, targetBuilder.Result, targetBuffer.Result, ms); - return new ArraySegment(ms.ToArray()); - } - } + return new LegacyArchiveFileNameHandler(this); // Legacy / unstable because file-move can fail because of file-locks from other applications } } } diff --git a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs index 9e83ebf1fe..26b876528d 100644 --- a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs +++ b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs @@ -33,8 +33,8 @@ namespace NLog.Targets { - using Config; - using Layouts; + using NLog.Config; + using NLog.Layouts; /// /// Represents target that supports string formatting using layouts. diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs index 50b16c2ada..2c96bfc62c 100644 --- a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs @@ -37,10 +37,15 @@ namespace NLog.UnitTests.Targets using System.Collections.Generic; using System.Globalization; using System.IO; + using System.IO.Compression; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +#if NETFRAMEWORK + using Ionic.Zip; +#endif + using NLog.Common; using NLog.Config; using NLog.Layouts; using NLog.Targets; @@ -49,10 +54,18 @@ namespace NLog.UnitTests.Targets using NSubstitute; using Xunit; - public class FileTargetTests : NLogTestBase + public class ConcurrentFileTargetTests { private readonly Logger logger = LogManager.GetLogger("NLog.UnitTests.Targets.FileTargetTests"); + public ConcurrentFileTargetTests() + { + InternalLogger.Reset(); + LogManager.Configuration = null; + LogManager.ThrowExceptions = true; + LogManager.Setup().SetupExtensions(ext => ext.RegisterTarget("file")); + } + public static IEnumerable SimpleFileTest_TestParameters { get @@ -79,31 +92,6 @@ private static bool UniqueBaseAppender(bool concurrentWrites, bool keepFileOpen, return false; } - [Fact] - public void SetupBuilder_WriteToFile() - { - var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); - LogFactory logFactory = null; - - try - { - logFactory = new LogFactory().Setup().LoadConfiguration(c => - { - c.ForLogger().WriteToFile(Path.Combine(tempDir, "${logger}.txt"), "${message}", Encoding.UTF8, LineEndingMode.LF); - }).LogFactory; - - logFactory.GetLogger("SetupBuilder").Info("Hello"); - - AssertFileContents(Path.Combine(tempDir, "SetupBuilder.txt"), "Hello\n", Encoding.UTF8); - } - finally - { - logFactory?.Shutdown(); - if (Directory.Exists(tempDir)) - Directory.Delete(tempDir, true); - } - } - [Theory] [MemberData(nameof(SimpleFileTest_TestParameters))] public void SimpleFileTest(bool concurrentWrites, bool keepFileOpen, bool forceManaged, bool forceMutexConcurrentWrites) @@ -111,7 +99,7 @@ public void SimpleFileTest(bool concurrentWrites, bool keepFileOpen, bool forceM var logFile = Path.GetTempFileName(); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, @@ -159,7 +147,7 @@ public void SimpleFileDeleteTest(bool concurrentWrites, bool keepFileOpen, bool #endif foreach (var archiveSameFolder in new[] { true, false }) { - RetryingIntegrationTest(3, () => + for (int i = 1; i <= 3; ++i) { var logPath = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString(), "Archive"); var logFile = Path.GetFullPath(Path.Combine(logPath, "..", "nlogA.txt")); @@ -168,11 +156,11 @@ public void SimpleFileDeleteTest(bool concurrentWrites, bool keepFileOpen, bool try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = SimpleLayout.Escape(logFile), ArchiveFileName = SimpleLayout.Escape(arhiveFile), - ArchiveEvery = FileArchivePeriod.Year, + ArchiveEvery = FileArchiveEveryPeriod.Year, LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", OpenFileCacheTimeout = 0, @@ -204,6 +192,13 @@ public void SimpleFileDeleteTest(bool concurrentWrites, bool keepFileOpen, bool AssertFileContents(logFile, "Info bbb\n", Encoding.UTF8); } + catch + { + if (i == 3) + throw; + + System.Threading.Thread.Sleep(1000 * i); + } finally { if (File.Exists(arhiveFile)) @@ -226,7 +221,7 @@ public void SimpleFileDeleteTest(bool concurrentWrites, bool keepFileOpen, bool Directory.Delete(Path.GetDirectoryName(logFile)); } } - }); + } } } @@ -257,7 +252,7 @@ public void SimpleFileTestInRoot() [Fact] public void SimpleFileWithSpecialCharsTest() { - var logFile = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid() + "!@#$%^&()_-=+ .log"); + var logFile = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString() + "!@#$%^&()_-=+ .log"); SimpleFileWriteLogTest(logFile); } @@ -265,7 +260,7 @@ private void SimpleFileWriteLogTest(string logFile) { try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, @@ -295,7 +290,7 @@ public void SimpleFileTestWriteBom() var logFile = Path.GetTempFileName(); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, @@ -335,32 +330,33 @@ public void NonExistingDriveShouldNotDelayMuch(bool concurrentWrites, bool keepF try { - using (new NoThrowNLogExceptions()) + LogManager.ThrowExceptions = false; + + var fileTarget = new ConcurrentFileTarget { - var fileTarget = new FileTarget - { - FileName = logFile, - Layout = "${level} ${message}", - ConcurrentWrites = concurrentWrites, - KeepFileOpen = keepFileOpen, - ForceManaged = forceManaged, - ForceMutexConcurrentWrites = forceMutexConcurrentWrites, - }; + FileName = logFile, + Layout = "${level} ${message}", + ConcurrentWrites = concurrentWrites, + KeepFileOpen = keepFileOpen, + ForceManaged = forceManaged, + ForceMutexConcurrentWrites = forceMutexConcurrentWrites, + }; - LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); - for (int i = 0; i < 300; i++) - { - logger.Debug("aaa"); - } + for (int i = 0; i < 300; i++) + { + logger.Debug("aaa"); + } - LogManager.Configuration = null; // Flush + LogManager.Configuration = null; // Flush - Assert.True(DateTime.UtcNow - start < TimeSpan.FromSeconds(5)); - } + Assert.True(DateTime.UtcNow - start < TimeSpan.FromSeconds(5)); } finally { + LogManager.ThrowExceptions = true; + //should not be necessary if (File.Exists(logFile)) File.Delete(logFile); @@ -390,7 +386,7 @@ public void RollingArchiveEveryMonth() try { - var timeSource = new TimeSourceTests.ShiftedTimeSource(DateTimeKind.Local); + var timeSource = new ShiftedTimeSource(DateTimeKind.Local); if (timeSource.Time.Minute == 59) { // Avoid double-archive due to overflow of the hour. @@ -399,13 +395,13 @@ public void RollingArchiveEveryMonth() } TimeSource.Current = timeSource; - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = Path.Combine(tempDir, "${date:format=dd}_AppName.log"), LineEnding = LineEndingMode.LF, Layout = "${message}", ArchiveNumbering = ArchiveNumberingMode.Rolling, - ArchiveEvery = FileArchivePeriod.Month, + ArchiveEvery = FileArchiveEveryPeriod.Month, MaxArchiveFiles = 1, }; @@ -430,7 +426,7 @@ public void RollingArchiveEveryMonth() Assert.Equal(14, Path.GetFileName(file).Length); } - fileTarget.Close(); + LogManager.Configuration = null; // Flush and close } finally { @@ -470,7 +466,7 @@ public void DatedArchiveEveryMonth(bool archiveSubFolder, bool maxArchiveDays, A try { - var timeSource = new TimeSourceTests.ShiftedTimeSource(DateTimeKind.Local); + var timeSource = new ShiftedTimeSource(DateTimeKind.Local); if (timeSource.Time.Minute == 59) { // Avoid double-archive due to overflow of the hour. @@ -502,7 +498,7 @@ public void DatedArchiveEveryMonth(bool archiveSubFolder, bool maxArchiveDays, A timeSource.AddToSystemTime(TimeSpan.FromDays(32 * 3)); } - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, LineEnding = LineEndingMode.LF, @@ -511,7 +507,7 @@ public void DatedArchiveEveryMonth(bool archiveSubFolder, bool maxArchiveDays, A KeepFileOpen = i % 2 != 0, ArchiveFileName = archiveSubFolder ? Path.Combine(archiveDir, "AppName.{#}.log") : (Layout)null, ArchiveNumbering = archiveNumberingMode, - ArchiveEvery = FileArchivePeriod.Month, + ArchiveEvery = FileArchiveEveryPeriod.Month, ArchiveDateFormat = "yyyyMMdd", MaxArchiveFiles = maxArchiveDays ? 0 : 2, MaxArchiveDays = maxArchiveDays ? 5 * 30 : 0 @@ -590,13 +586,12 @@ public void CsvHeaderTest() } }; - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, Layout = layout, OpenFileCacheTimeout = 0, - ReplaceFileContentsOnEachWrite = false, ArchiveAboveSize = 120, // Only 2 LogEvents per file MaxArchiveFiles = 1, }; @@ -640,7 +635,7 @@ public void DeleteFileOnStartTest() var logFile = Path.GetTempFileName(); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { DeleteOldFileOnStartup = false, FileName = SimpleLayout.Escape(logFile), @@ -661,7 +656,7 @@ public void DeleteFileOnStartTest() // configure again, without // DeleteOldFileOnStartup - fileTarget = new FileTarget + fileTarget = new ConcurrentFileTarget { DeleteOldFileOnStartup = false, FileName = SimpleLayout.Escape(logFile), @@ -681,7 +676,7 @@ public void DeleteFileOnStartTest() // configure again, this time with // DeleteOldFileOnStartup - fileTarget = new FileTarget + fileTarget = new ConcurrentFileTarget { FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, @@ -774,19 +769,19 @@ from enableCompression in booleanValues public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompressor) { var logFile = Path.GetTempFileName() + ".txt"; - var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "Archive"); + var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString(), "Archive"); var archiveExtension = enableCompression ? "zip" : "txt"; IFileCompressor fileCompressor = null; try { if (customFileCompressor) { - fileCompressor = FileTarget.FileCompressor; - FileTarget.FileCompressor = new CustomFileCompressor(); + fileCompressor = ConcurrentFileTarget.FileCompressor; + ConcurrentFileTarget.FileCompressor = new CustomFileCompressor(); } // Configure first time with ArchiveOldFileOnStartup = false. - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { ArchiveOldFileOnStartup = false, FileName = SimpleLayout.Escape(logFile), @@ -806,7 +801,7 @@ public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompr // Configure second time with ArchiveOldFileOnStartup = false again. // Expected behavior: Extra content to be appended to the file. - fileTarget = new FileTarget + fileTarget = new ConcurrentFileTarget { ArchiveOldFileOnStartup = false, FileName = SimpleLayout.Escape(logFile), @@ -830,8 +825,8 @@ public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompr var archiveTempName = Path.Combine(tempArchiveFolder, "archive." + archiveExtension); - FileTarget ft; - fileTarget = ft = new FileTarget + ConcurrentFileTarget ft; + fileTarget = ft = new ConcurrentFileTarget { EnableArchiveFileCompression = enableCompression, FileName = SimpleLayout.Escape(logFile), @@ -853,9 +848,9 @@ public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompr AssertFileContents(logFile, "Debug ddd\nInfo eee\nWarn fff\n", Encoding.UTF8); Assert.True(File.Exists(archiveTempName)); - var assertFileContents = ft.EnableArchiveFileCompression ? - new Action(AssertZipFileContents) : - AssertFileContents; + Action assertFileContents = (f1, f2, content, encoding) => AssertFileContents(f1, content, encoding, false); + if (fileTarget.EnableArchiveFileCompression) + assertFileContents = AssertZipFileContents; #if !NET35 string expectedEntryName = Path.GetFileNameWithoutExtension(archiveTempName) + ".txt"; @@ -868,7 +863,7 @@ public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompr finally { if (customFileCompressor) - FileTarget.FileCompressor = fileCompressor; + ConcurrentFileTarget.FileCompressor = fileCompressor; if (File.Exists(logFile)) File.Delete(logFile); if (Directory.Exists(tempArchiveFolder)) @@ -880,11 +875,11 @@ public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompr public void ArchiveOldFileOnStartupAboveSize() { var logFile = Path.GetTempFileName(); - var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "Archive"); + var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString(), "Archive"); var archiveTempName = Path.Combine(tempArchiveFolder, "archive_size_threshold.txt"); - FileTarget CreateTestTarget(long threshold) + ConcurrentFileTarget CreateTestTarget(long threshold) { - return new FileTarget + return new ConcurrentFileTarget { FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, @@ -925,12 +920,12 @@ FileTarget CreateTestTarget(long threshold) public void ArchiveOldFileOnStartupAboveSizeWhenFileLocked() { var logFile = Path.GetTempFileName(); - var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "Archive"); + var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString(), "Archive"); var archiveTempName = Path.Combine(tempArchiveFolder, "archive_size_threshold.zip"); - FileTarget CreateTestTarget(long threshold) + ConcurrentFileTarget CreateTestTarget(long threshold) { - return new FileTarget + return new ConcurrentFileTarget { FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, @@ -980,7 +975,7 @@ public void RetryFileOpenWhenFileLocked() { var logFile = Path.GetTempFileName(); - var fileTarget = new FileTarget("file") + var fileTarget = new ConcurrentFileTarget("file") { FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, @@ -1029,7 +1024,7 @@ public void ReplaceFileContentsOnEachWriteTest(bool useHeader, bool useFooter) var logFile = Path.GetTempFileName(); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { DeleteOldFileOnStartup = false, FileName = SimpleLayout.Escape(logFile), @@ -1072,35 +1067,36 @@ public void ReplaceFileContentsOnEachWriteTest(bool useHeader, bool useFooter) [InlineData(false)] public void ReplaceFileContentsOnEachWrite_CreateDirs(bool createDirs) { - var tempDir = Path.Combine(Path.GetTempPath(), "dir_" + Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logfile = Path.Combine(tempDir, "log.log"); try { - using (new NoThrowNLogExceptions()) + LogManager.ThrowExceptions = false; + + var target = new ConcurrentFileTarget { - var target = new FileTarget - { - FileName = logfile, - ReplaceFileContentsOnEachWrite = true, - CreateDirs = createDirs - }; - var config = new LoggingConfiguration(); + FileName = logfile, + ReplaceFileContentsOnEachWrite = true, + CreateDirs = createDirs + }; + var config = new LoggingConfiguration(); - config.AddTarget("logfile", target); + config.AddTarget("logfile", target); - config.AddRuleForAllLevels(target); + config.AddRuleForAllLevels(target); - LogManager.Configuration = config; + LogManager.Configuration = config; - var logger = LogManager.GetLogger("A"); - logger.Info("a"); + var logger = LogManager.GetLogger("A"); + logger.Info("a"); - Assert.Equal(createDirs, Directory.Exists(tempDir)); - } + Assert.Equal(createDirs, Directory.Exists(tempDir)); } finally { + LogManager.ThrowExceptions = true; + if (File.Exists(logfile)) File.Delete(logfile); if (Directory.Exists(tempDir)) @@ -1111,11 +1107,11 @@ public void ReplaceFileContentsOnEachWrite_CreateDirs(bool createDirs) [Fact] public void CreateDirsTest() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, LineEnding = LineEndingMode.LF, @@ -1142,25 +1138,29 @@ public void CreateDirsTest() } [Theory] - [InlineData(true, 0)] - [InlineData(false, 0)] - [InlineData(false, 1)] - public void AutoFlushTest(bool autoFlush, int autoFlushTimeout) + [InlineData(true, true, 0)] + [InlineData(false, true, 0)] + [InlineData(false, true, 1)] + [InlineData(true, false, 0)] + [InlineData(false, false, 0)] + [InlineData(false, false, 1)] + public void AutoFlushTest(bool autoFlush, bool keepFileOpen, int autoFlushTimeout) { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", - KeepFileOpen = true, + KeepFileOpen = keepFileOpen, ConcurrentWrites = false, - AutoFlush = autoFlush, OpenFileFlushTimeout = autoFlushTimeout, }; + if (!autoFlush) + fileTarget.AutoFlush = autoFlush; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -1168,7 +1168,7 @@ public void AutoFlushTest(bool autoFlush, int autoFlushTimeout) logger.Info("bbb"); logger.Warn("ccc"); - if (autoFlush) + if (autoFlush || !keepFileOpen) { AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); } @@ -1198,12 +1198,12 @@ public void AutoFlushTest(bool autoFlush, int autoFlushTimeout) [Fact] public void SequentialArchiveTest() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { string archiveFolder = Path.Combine(tempDir, "archive"); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), @@ -1262,12 +1262,12 @@ public void SequentialArchiveTest() [Fact] public void SequentialArchiveTest_MaxArchiveFiles_0() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { string archiveFolder = Path.Combine(tempDir, "archive"); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), @@ -1328,12 +1328,12 @@ public void SequentialArchiveTest_MaxArchiveFiles_0() [Fact] public void ArchiveAboveSizeWithArchiveNumberingModeDate_maxfiles_o() { - var tempDir = Path.Combine(Path.GetTempPath(), "ArchiveEveryCombinedWithArchiveAboveSize_" + Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { string archiveFolder = Path.Combine(tempDir, "archive"); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), @@ -1401,12 +1401,12 @@ public void DeleteArchiveFilesByDate() { const int maxArchiveFiles = 3; - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { string archiveFolder = Path.Combine(tempDir, "archive"); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), @@ -1470,15 +1470,15 @@ public void DeleteArchiveFilesByDateWithDateName() { const int maxArchiveFiles = 3; LogManager.ThrowExceptions = true; - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); try { var logFile = Path.Combine(tempDir, "${date:format=yyyyMMddHHmmssfff}.txt"); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "{#}.txt"), - ArchiveEvery = FileArchivePeriod.Year, + ArchiveEvery = FileArchiveEveryPeriod.Year, LineEnding = LineEndingMode.LF, ArchiveNumbering = ArchiveNumberingMode.Date, ArchiveDateFormat = "yyyyMMddHHmmssfff", //make sure the milliseconds are set in the filename @@ -1562,24 +1562,24 @@ public void DateArchive_UsesDateFromCurrentTimeSource(DateTimeKind timeKind, boo const string archiveDateFormat = "yyyyMMdd"; const int maxArchiveFiles = 3; - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, includeDateInLogFilePath ? "file_${shortdate}.txt" : "file.txt"); var defaultTimeSource = TimeSource.Current; try { - var timeSource = new TimeSourceTests.ShiftedTimeSource(timeKind); + var timeSource = new ShiftedTimeSource(timeKind); TimeSource.Current = timeSource; string archiveFolder = Path.Combine(tempDir, "archive"); string archiveFileNameTemplate = Path.Combine(archiveFolder, "{#}.txt"); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = archiveFileNameTemplate, LineEnding = LineEndingMode.LF, ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, - ArchiveEvery = FileArchivePeriod.Day, + ArchiveEvery = FileArchiveEveryPeriod.Day, ArchiveDateFormat = archiveDateFormat, Layout = "${date:format=O}|${message}", MaxArchiveFiles = maxArhiveDays ? 0 : maxArchiveFiles, @@ -1708,14 +1708,14 @@ from forceManaged in booleanValues [MemberData(nameof(DateArchive_ArchiveOnceOnly_TestParameters))] public void DateArchive_ArchiveOnceOnly(bool concurrentWrites, bool keepFileOpen, bool dateInLogFilePath, bool includeSequenceInArchive, bool forceManaged, bool forceMutexConcurrentWrites) { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, dateInLogFilePath ? "file_${shortdate}.txt" : "file.txt"); var defaultTimeSource = TimeSource.Current; try { - var timeSource = new TimeSourceTests.ShiftedTimeSource(DateTimeKind.Local); + var timeSource = new ShiftedTimeSource(DateTimeKind.Local); if (timeSource.Time.Minute == 59) { // Avoid double-archive due to overflow of the hour. @@ -1725,13 +1725,13 @@ public void DateArchive_ArchiveOnceOnly(bool concurrentWrites, bool keepFileOpen TimeSource.Current = timeSource; string archiveFolder = Path.Combine(tempDir, "archive"); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), LineEnding = LineEndingMode.LF, ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, - ArchiveEvery = FileArchivePeriod.Day, + ArchiveEvery = FileArchiveEveryPeriod.Day, ArchiveDateFormat = "yyyyMMdd", Layout = "${message}", ConcurrentWrites = concurrentWrites, @@ -1784,7 +1784,7 @@ public static IEnumerable DateArchive_SkipPeriod_TestParameters get { var timeKindValues = new[] { DateTimeKind.Utc, DateTimeKind.Local }; - var archivePeriodValues = new[] { FileArchivePeriod.Day, FileArchivePeriod.Hour }; + var archivePeriodValues = new[] { FileArchiveEveryPeriod.Day, FileArchiveEveryPeriod.Hour }; var booleanValues = new[] { true, false }; return from timeKind in timeKindValues @@ -1797,9 +1797,9 @@ from includeSequenceInArchive in booleanValues [Theory] [MemberData(nameof(DateArchive_SkipPeriod_TestParameters))] - public void DateArchive_SkipPeriod(DateTimeKind timeKind, FileArchivePeriod archivePeriod, bool includeDateInLogFilePath, bool includeSequenceInArchive) + public void DateArchive_SkipPeriod(DateTimeKind timeKind, FileArchiveEveryPeriod archivePeriod, bool includeDateInLogFilePath, bool includeSequenceInArchive) { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, includeDateInLogFilePath ? "file_${date:format=yyyyMMddHHmm}.txt" : "file.txt"); var defaultTimeSource = TimeSource.Current; try @@ -1808,7 +1808,7 @@ public void DateArchive_SkipPeriod(DateTimeKind timeKind, FileArchivePeriod arch while (DateTime.Now.Second > 55) Thread.Sleep(1000); - var timeSource = new TimeSourceTests.ShiftedTimeSource(timeKind); + var timeSource = new ShiftedTimeSource(timeKind); if (timeSource.Time.Minute == 59) { // Avoid double-archive due to overflow of the hour. @@ -1817,7 +1817,7 @@ public void DateArchive_SkipPeriod(DateTimeKind timeKind, FileArchivePeriod arch } TimeSource.Current = timeSource; - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "archive", "{#}.txt"), @@ -1887,7 +1887,7 @@ public void DateArchive_AllLoggersTransferToCurrentLogFile(bool concurrentWrites return; // No need to test with compression #endif - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logfile = Path.Combine(tempDir, includeDateInLogFilePath ? "file_${shortdate}.txt" : "file.txt"); var defaultTimeSource = TimeSource.Current; @@ -1897,7 +1897,7 @@ public void DateArchive_AllLoggersTransferToCurrentLogFile(bool concurrentWrites try { - var timeSource = new TimeSourceTests.ShiftedTimeSource(DateTimeKind.Local); + var timeSource = new ShiftedTimeSource(DateTimeKind.Local); if (timeSource.Time.Minute == 59) { // Avoid double-archive due to overflow of the hour. @@ -1917,13 +1917,13 @@ public void DateArchive_AllLoggersTransferToCurrentLogFile(bool concurrentWrites #endif string archiveFolder = Path.Combine(tempDir, "archive"); - var fileTarget1 = new FileTarget + var fileTarget1 = new ConcurrentFileTarget { FileName = logfile, ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), LineEnding = LineEndingMode.LF, ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, - ArchiveEvery = FileArchivePeriod.Day, + ArchiveEvery = FileArchiveEveryPeriod.Day, ArchiveDateFormat = "yyyyMMdd", EnableArchiveFileCompression = enableArchiveCompression, Layout = "${message}", @@ -1935,13 +1935,13 @@ public void DateArchive_AllLoggersTransferToCurrentLogFile(bool concurrentWrites var logger1Rule = new LoggingRule("logger1", LogLevel.Debug, fileTarget1); config.LoggingRules.Add(logger1Rule); - var fileTarget2 = new FileTarget + var fileTarget2 = new ConcurrentFileTarget { FileName = logfile, ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), LineEnding = LineEndingMode.LF, ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, - ArchiveEvery = FileArchivePeriod.Day, + ArchiveEvery = FileArchiveEveryPeriod.Day, ArchiveDateFormat = "yyyyMMdd", EnableArchiveFileCompression = enableArchiveCompression, Layout = "${message}", @@ -2003,12 +2003,12 @@ public void DateArchive_AllLoggersTransferToCurrentLogFile(bool concurrentWrites [Fact] public void DeleteArchiveFilesByDate_MaxArchiveFiles_0() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { string archiveFolder = Path.Combine(tempDir, "archive"); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), @@ -2066,12 +2066,12 @@ public void DeleteArchiveFilesByDate_MaxArchiveFiles_0() [Fact] public void DeleteArchiveFilesByDate_AlteredMaxArchive() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { string archiveFolder = Path.Combine(tempDir, "archive"); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), @@ -2133,14 +2133,14 @@ public void DeleteArchiveFilesByDate_AlteredMaxArchive() [Fact] public void RepeatingHeaderTest() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { const string header = "Headerline"; string archiveFolder = Path.Combine(tempDir, "archive"); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), @@ -2184,14 +2184,14 @@ public void RepeatingHeaderTest() [InlineData(true)] public void RepeatingFooterTest(bool writeFooterOnArchivingOnly) { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { const string footer = "Footerline"; string archiveFolder = Path.Combine(tempDir, "archive"); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), @@ -2244,7 +2244,7 @@ public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty) const string header = "Headerline"; // Configure first time - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, @@ -2267,7 +2267,7 @@ public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty) AssertFileContents(logFile, headerPart + logPart, Encoding.UTF8, addBom: true); // Configure second time - fileTarget = new FileTarget + fileTarget = new ConcurrentFileTarget { FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, @@ -2316,7 +2316,7 @@ public void RollingArchiveCompressionTest(bool specifyArchiveFileName) private void RollingArchiveTests(bool enableCompression, bool specifyArchiveFileName) { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); var archiveExtension = enableCompression ? "zip" : "txt"; @@ -2326,7 +2326,7 @@ private void RollingArchiveTests(bool enableCompression, bool specifyArchiveFile try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { EnableArchiveFileCompression = enableCompression, FileName = logFile, @@ -2360,8 +2360,9 @@ private void RollingArchiveTests(bool enableCompression, bool specifyArchiveFile LogManager.Configuration = null; // Flush - var assertFileContents = - enableCompression ? new Action(AssertZipFileContents) : AssertFileContents; + Action assertFileContents = (f1, f2, content, encoding) => AssertFileContents(f1, content, encoding, false); + if (enableCompression) + assertFileContents = AssertZipFileContents; var times = 25; AssertFileContents(logFile, @@ -2412,11 +2413,11 @@ private void RollingArchiveTests(bool enableCompression, bool specifyArchiveFile [Theory] public void RollingArchiveTest_MaxArchiveFiles_0(string slash) { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "archive" + slash + "{####}.txt"), @@ -2482,10 +2483,10 @@ public void RollingArchiveTest_MaxArchiveFiles_0(string slash) [Fact] public void MultiFileWrite() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = Path.Combine(tempDir, "${level}.txt"), LineEnding = LineEndingMode.LF, @@ -2534,10 +2535,10 @@ public void MultiFileWrite() [Fact] public void BufferedMultiFileWrite() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = Path.Combine(tempDir, "${level}.txt"), LineEnding = LineEndingMode.LF, @@ -2586,10 +2587,10 @@ public void BufferedMultiFileWrite() [Fact] public void AsyncMultiFileWrite() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = Path.Combine(tempDir, "${level}.txt"), LineEnding = LineEndingMode.LF, @@ -2599,7 +2600,7 @@ public void AsyncMultiFileWrite() // this also checks that thread-volatile layouts // such as ${threadid} are properly cached and not recalculated // in logging threads. - var threadID = CurrentManagedThreadId.ToString(); + var threadID = Thread.CurrentThread.ManagedThreadId.ToString(); LogManager.Setup().LoadConfiguration(c => c.ForLogger(LogLevel.Debug).WriteTo(fileTarget).WithAsync()); @@ -2644,7 +2645,7 @@ public void AsyncMultiFileWrite() public void DisposingFileTarget_WhenNotIntialized_ShouldNotThrow() { var exceptionThrown = false; - var fileTarget = new FileTarget(); + var fileTarget = new ConcurrentFileTarget(); try { @@ -2691,7 +2692,7 @@ private void FileTarget_ArchiveNumbering_DateAndSequenceTests(bool enableCompres try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { EnableArchiveFileCompression = enableCompression, FileName = logFile, @@ -2702,7 +2703,7 @@ private void FileTarget_ArchiveNumbering_DateAndSequenceTests(bool enableCompres Layout = "${message}", MaxArchiveFiles = 3, ArchiveNumbering = ArchiveNumberingMode.DateAndSequence, - ArchiveEvery = FileArchivePeriod.Day + ArchiveEvery = FileArchiveEveryPeriod.Day }; @@ -2728,7 +2729,9 @@ private void FileTarget_ArchiveNumbering_DateAndSequenceTests(bool enableCompres LogManager.Configuration = null; - var assertFileContents = enableCompression ? new Action(AssertZipFileContents) : AssertFileContents; + Action assertFileContents = (f1, f2, content, encoding) => AssertFileContents(f1, content, encoding, false); + if (enableCompression) + assertFileContents = AssertZipFileContents; var extension = Path.GetExtension(renderedArchiveFileName); var fileNameWithoutExt = renderedArchiveFileName.Substring(0, renderedArchiveFileName.Length - extension.Length); @@ -2778,12 +2781,12 @@ private void FileTarget_ArchiveNumbering_DateAndSequenceTests(bool enableCompres [InlineData("file-{#}.txt", "file-${date:format=yyyyMMdd}.txt", ArchiveNumberingMode.Date)] public void FileTargetArchiveFileNameTest(string archiveFileName, string expectedArchiveFileName, ArchiveNumberingMode archiveNumbering) { - var subPath = Guid.NewGuid().ToString(); + var subPath = "nlog_" + Guid.NewGuid().ToString(); var tempDir = Path.Combine(Path.GetTempPath(), subPath); var logFile = Path.Combine(tempDir, "file-${date:format=yyyyMMdd}.txt"); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "..", subPath, archiveFileName), @@ -2825,7 +2828,7 @@ public void FileTarget_InvalidFileNameCorrection() try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = SimpleLayout.Escape(invalidLogFileName), LineEnding = LineEndingMode.LF, @@ -2853,7 +2856,7 @@ public void FileTarget_InvalidFileNameCorrection() [Fact] public void FileTarget_LogAndArchiveFilesWithSameName_ShouldArchive() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "Application.log"); var tempDirectory = new DirectoryInfo(tempDir); try @@ -2862,7 +2865,7 @@ public void FileTarget_LogAndArchiveFilesWithSameName_ShouldArchive() var archiveFile = Path.Combine(tempDir, "Application{#}.log"); var archiveFileMask = "Application*.log"; - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = archiveFile, @@ -2898,7 +2901,7 @@ public void FileTarget_LogAndArchiveFilesWithSameName_ShouldArchive() [Fact] public void FileTarget_Handle_Other_Files_That_Match_Archive_Format() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "Application.log"); var tempDirectory = new DirectoryInfo(tempDir); @@ -2906,14 +2909,14 @@ public void FileTarget_Handle_Other_Files_That_Match_Archive_Format() { string archiveFileLayout = Path.Combine(Path.GetDirectoryName(logFile), Path.GetFileNameWithoutExtension(logFile) + "{#}" + Path.GetExtension(logFile)); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, Layout = "${message}", EnableFileDelete = false, Encoding = Encoding.UTF8, ArchiveFileName = archiveFileLayout, - ArchiveEvery = FileArchivePeriod.Day, + ArchiveEvery = FileArchiveEveryPeriod.Day, ArchiveNumbering = ArchiveNumberingMode.Date, ArchiveDateFormat = "___________yyyyMMddHHmm", MaxArchiveFiles = 10 // Get past the optimization to avoid deleting old files. @@ -2944,11 +2947,11 @@ public void FileTarget_Handle_Other_Files_That_Match_Archive_Format() [Fact] public void SingleArchiveFileRollsCorrectly() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "archive", "file.txt2"), @@ -3009,11 +3012,11 @@ public void SingleArchiveFileRollsCorrectly() [Fact] public void ArchiveFileRollsCorrectly() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "archive", "file.txt2"), @@ -3087,7 +3090,7 @@ public void ArchiveFileRollsCorrectly() [Fact] public void ArchiveFileRollsCorrectly_ExistingArchives() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -3095,7 +3098,7 @@ public void ArchiveFileRollsCorrectly_ExistingArchives() File.Create(Path.Combine(tempDir, "archive", "file.10.txt2")).Dispose(); File.Create(Path.Combine(tempDir, "archive", "file.9.txt2")).Dispose(); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "archive", "file.txt2"), @@ -3153,7 +3156,7 @@ public void FileTarget_ArchiveNumbering_remove_correct_order() var archiveExtension = "txt"; try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "archive", "{#}." + archiveExtension), @@ -3210,7 +3213,7 @@ public void FileTarget_ArchiveNumbering_remove_correct_wildcard() var defaultTimeSource = TimeSource.Current; try { - var timeSource = new TimeSourceTests.ShiftedTimeSource(DateTimeKind.Local); + var timeSource = new ShiftedTimeSource(DateTimeKind.Local); if (timeSource.Time.Minute == 59) { // Avoid double-archive due to overflow of the hour. @@ -3219,7 +3222,7 @@ public void FileTarget_ArchiveNumbering_remove_correct_wildcard() } TimeSource.Current = timeSource; - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = string.Format(logFile, "${logger}", "${shortdate}"), ArchiveAboveSize = 100, @@ -3323,7 +3326,7 @@ public void FileTarget_SameDirectory_MaxArchiveFiles_One() var logFile1 = Path.Combine(tempDir, "Log{0}.txt"); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = string.Format(logFile1, ""), ArchiveAboveSize = 100, @@ -3403,7 +3406,7 @@ public string GetFullPath(int number) public static string GenerateTempPath() { - return Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + return Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); } } @@ -3414,7 +3417,7 @@ public static string GenerateTempPath() public void FileTarget_WithDateAndSequenceArchiveNumbering_ShouldPadSequenceNumberInArchiveFileName( string placeHolderSharps, int sequenceNumber, string expectedSequenceInArchiveFileName) { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); const string archiveDateFormat = "yyyy-MM-dd"; string archiveFileName = Path.Combine(tempDir, $"{{{placeHolderSharps}}}.log"); string expectedArchiveFullName = @@ -3434,7 +3437,7 @@ public void FileTarget_WithDateAndSequenceArchiveNumbering_ShouldPadSequenceNumb public void FileTarget_WithDateAndSequenceArchiveNumbering_ShouldRespectArchiveDateFormat( string archiveDateFormat) { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); string archiveFileName = Path.Combine(tempDir, "{#}.log"); string expectedDateInArchiveFileName = DateTime.Now.ToString(archiveDateFormat); string expectedArchiveFullName = $"{tempDir}/{expectedDateInArchiveFileName}.1.log"; @@ -3452,7 +3455,7 @@ private void GenerateArchives(int count, string archiveDateFormat, string archiv { string logFileName = Path.GetTempFileName(); const int logFileMaxSize = 1; - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFileName, ArchiveFileName = archiveFileName, @@ -3536,7 +3539,7 @@ public void DatedArchiveForFileTargetWithMultipleFiles() try { - var timeSource = new TimeSourceTests.ShiftedTimeSource(DateTimeKind.Local); + var timeSource = new ShiftedTimeSource(DateTimeKind.Local); if (timeSource.Time.Minute == 59) { // Avoid double-archive due to overflow of the hour. @@ -3666,7 +3669,7 @@ public void MaxArchiveFilesWithDateFormatTest(string archiveDateFormat) /// change file creation/last write date private static void TestMaxArchiveFilesWithDate(int maxArchiveFilesConfig, int expectedArchiveFiles, string dateFormat, bool changeCreationAndWriteTime) { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); string archivePath = Path.Combine(tempDir, "archive"); var archiveDir = new DirectoryInfo(archivePath); @@ -3762,7 +3765,7 @@ public void HandleArchiveFilesMultipleContextSingleTargetTest_ascii(bool changeC private static void HandleArchiveFilesMultipleContextMultipleTargetsTest( int maxArchiveFilesConfig, int expectedArchiveFiles, string dateFormat, bool changeCreationAndWriteTime) { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); string archivePath = Path.Combine(tempDir, "archive"); var archiveDir = new DirectoryInfo(archivePath); try @@ -3819,7 +3822,7 @@ private static void HandleArchiveFilesMultipleContextMultipleTargetsTest( } // Create same app1 Debug file as config defines. Will force archiving to happen on startup - File.WriteAllLines(tempDir + "\\" + app1DebugNm + fileExt, new[] { "Write first app debug target. Startup will archive this file" }, Encoding.ASCII); + File.WriteAllLines(Path.Combine(tempDir, app1DebugNm + fileExt), new[] { "Write first app debug target. Startup will archive this file" }, Encoding.ASCII); var app1Config = XmlLoggingConfiguration.CreateFromXmlString(@" @@ -3904,7 +3907,7 @@ private static void HandleArchiveFilesMultipleContextMultipleTargetsTest( private static void HandleArchiveFilesMultipleContextSingleTargetsTest( int maxArchiveFilesConfig, int expectedArchiveFiles, string dateFormat, bool changeCreationAndWriteTime) { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); string archivePath = Path.Combine(tempDir, "archive"); var archiveDir = new DirectoryInfo(archivePath); try @@ -4031,7 +4034,7 @@ public void RelativeFileNaming_ShouldSuccess() var fullFilePath = Path.GetFullPath(relativeFileName); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = fullFilePath, LineEnding = LineEndingMode.LF, @@ -4061,7 +4064,7 @@ public void RelativeFileNaming_DirectoryNavigation_ShouldSuccess() var fullFilePath = Path.GetFullPath(relativeFileName); try { - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = fullFilePath, LineEnding = LineEndingMode.LF, @@ -4087,12 +4090,12 @@ public void RelativeFileNaming_DirectoryNavigation_ShouldSuccess() [Fact] public void RelativeSequentialArchiveTest_MaxArchiveFiles_0() { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logfile = Path.Combine(tempDir, "file.txt"); try { string archiveFolder = Path.Combine(tempDir, "archive"); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logfile, ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), @@ -4174,7 +4177,7 @@ public void TestFilenameCleanup() //under mono this the invalid chars is sometimes only 1 char (so min width 2) Assert.True(invalidFileName.Length >= 2); //CleanupFileName is default true; - var fileTarget = new FileTarget(); + var fileTarget = new ConcurrentFileTarget(); fileTarget.FileName = invalidFileName; var filePathLayout = new NLog.Internal.FilePathLayout(invalidFileName, true, FilePathKind.Absolute); @@ -4192,7 +4195,7 @@ public void TestCalculateNextWeekday(DayOfWeek day, string todayString, string e { DateTime today = DateTime.Parse(todayString); DateTime expected = DateTime.Parse(expectedString); - DateTime actual = FileTarget.CalculateNextWeekday(today, day); + DateTime actual = ConcurrentFileTarget.CalculateNextWeekday(today, day); Assert.Equal(expected, actual); } @@ -4208,7 +4211,7 @@ public void TestCalculateNextWeekday(DayOfWeek day, string todayString, string e [InlineData("ASCII", false)] public void TestInitialBomValue(string encodingName, bool expected) { - var fileTarget = new FileTarget(); + var fileTarget = new ConcurrentFileTarget(); // Act fileTarget.Encoding = Encoding.GetEncoding(encodingName); @@ -4220,10 +4223,12 @@ public void TestInitialBomValue(string encodingName, bool expected) [Fact] public void BatchErrorHandlingTest() { - using (new NoThrowNLogExceptions()) + try { - var fileTarget = new FileTarget { FileName = "${logger}", Layout = "${message}", ArchiveAboveSize = 10, DiscardAll = true }; - fileTarget.Initialize(null); + LogManager.ThrowExceptions = false; + + var fileTarget = new ConcurrentFileTarget { FileName = "${logger}", Layout = "${message}", ArchiveAboveSize = 10, DiscardAll = true }; + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(fileTarget)).LogFactory; // make sure that when file names get sorted, the asynchronous continuations are sorted with them as well var exceptions = new List(); @@ -4237,7 +4242,7 @@ public void BatchErrorHandlingTest() }; fileTarget.WriteAsyncLogEvents(events); - LogManager.Flush(); + logFactory.Flush(); Assert.Equal(5, exceptions.Count); Assert.Null(exceptions[0]); @@ -4246,17 +4251,21 @@ public void BatchErrorHandlingTest() Assert.NotNull(exceptions[3]); Assert.NotNull(exceptions[4]); } + finally + { + LogManager.ThrowExceptions = true; + } } [Fact] public void BatchBufferOverflowTest() { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logfile = Path.Combine(tempDir, "file.txt"); try { // Arrange - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logfile, BufferSize = 5, @@ -4264,7 +4273,8 @@ public void BatchBufferOverflowTest() Layout = "${message}", Encoding = Encoding.UTF8, }; - fileTarget.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(fileTarget)).LogFactory; var result = new List(); var events = new List(); @@ -4277,7 +4287,8 @@ public void BatchBufferOverflowTest() // Act fileTarget.WriteAsyncLogEvents(events); - fileTarget.Close(); + logFactory.Flush(); + logFactory.Dispose(); // Assert Assert.Equal(Enumerable.Range(1, times).ToList(), result); @@ -4321,7 +4332,7 @@ public void HandleArchiveFileAlreadyExistsTest_ascii() private static void HandleArchiveFileAlreadyExistsTest(Encoding encoding, bool hasBom) { - var tempDir = Path.Combine(Path.GetTempPath(), "HandleArchiveFileAlreadyExistsTest-" + Guid.NewGuid()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); string logFile = Path.Combine(tempDir, "log.txt"); try { @@ -4343,10 +4354,10 @@ private static void HandleArchiveFileAlreadyExistsTest(Encoding encoding, bool h LogManager.ThrowExceptions = true; // configure nlog - var fileTarget = new FileTarget("file") + var fileTarget = new ConcurrentFileTarget("file") { FileName = logFile, - ArchiveEvery = FileArchivePeriod.Day, + ArchiveEvery = FileArchiveEveryPeriod.Day, ArchiveFileName = archiveFileNamePattern, ArchiveNumbering = ArchiveNumberingMode.Date, ArchiveDateFormat = archiveDateFormat, @@ -4383,7 +4394,7 @@ private static void HandleArchiveFileAlreadyExistsTest(Encoding encoding, bool h [Fact] public void DontCrashWhenDateAndSequenceDoesntMatchFiles() { - var tempDir = Path.Combine(Path.GetTempPath(), "DontCrashWhenDateAndSequenceDoesntMatchFiles-" + Guid.NewGuid()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); string logFile = Path.Combine(tempDir, "log.txt"); try { @@ -4405,10 +4416,10 @@ public void DontCrashWhenDateAndSequenceDoesntMatchFiles() LogManager.ThrowExceptions = true; // configure nlog - var fileTarget = new FileTarget("file") + var fileTarget = new ConcurrentFileTarget("file") { FileName = logFile, - ArchiveEvery = FileArchivePeriod.Day, + ArchiveEvery = FileArchiveEveryPeriod.Day, ArchiveFileName = "log-{#}.txt", ArchiveNumbering = ArchiveNumberingMode.DateAndSequence, ArchiveAboveSize = 50000, @@ -4453,7 +4464,7 @@ public void ShouldArchiveOldFileOnStartupTest(bool? archiveOldFileOnStartup, lon var filePath = "x:/somewhere/file.txt"; fileAppenderCacheMock.GetFileLength(filePath).Returns(101); - var target = new FileTarget(fileAppenderCacheMock) + var target = new ConcurrentFileTarget(fileAppenderCacheMock) { ArchiveOldFileOnStartupAboveSize = archiveOldFileOnStartupAboveSize }; @@ -4472,16 +4483,16 @@ public void ShouldArchiveOldFileOnStartupTest(bool? archiveOldFileOnStartup, lon [Fact] public void ShouldNotArchiveWhenMeetingOldLogEventTimestamps() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { string archiveFolder = Path.Combine(tempDir, "archive"); - var fileTarget = new FileTarget + var fileTarget = new ConcurrentFileTarget { FileName = logFile, ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), - ArchiveEvery = FileArchivePeriod.Day, + ArchiveEvery = FileArchiveEveryPeriod.Day, LineEnding = LineEndingMode.LF, ArchiveNumbering = ArchiveNumberingMode.Sequence, Layout = "${message}", @@ -4517,5 +4528,207 @@ public void ShouldNotArchiveWhenMeetingOldLogEventTimestamps() Directory.Delete(tempDir, true); } } + + private static string StringRepeat(int times, string s) + { + StringBuilder sb = new StringBuilder(s.Length * times); + for (int i = 0; i < times; ++i) + sb.Append(s); + return sb.ToString(); + } + + protected static bool IsLinux() + { + var val = Environment.GetEnvironmentVariable("WINDIR"); + return string.IsNullOrEmpty(val); + } + + private static void AssertFileContents(string fileName, string contents, Encoding encoding, bool addBom = false) + { + FileInfo fi = new FileInfo(fileName); + if (!fi.Exists) + Assert.Fail("File '" + fileName + "' doesn't exist."); + + byte[] encodedBuf = encoding.GetBytes(contents); + + //add bom if needed + if (addBom) + { + var preamble = encoding.GetPreamble(); + if (preamble.Length > 0) + { + //insert before + encodedBuf = preamble.Concat(encodedBuf).ToArray(); + } + } + + byte[] buf; + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) + { + int index = 0; + int count = (int)fs.Length; + buf = new byte[count]; + while (count > 0) + { + int n = fs.Read(buf, index, count); + if (n == 0) + break; + index += n; + count -= n; + } + } + + Assert.True(encodedBuf.Length == buf.Length, + $"File:{fileName} encodedBytes:{encodedBuf.Length} does not match file.content:{buf.Length}, file.length = {fi.Length}"); + + for (int i = 0; i < buf.Length; ++i) + { + if (encodedBuf[i] != buf[i]) + Assert.True(encodedBuf[i] == buf[i], + $"File:{fileName} content mismatch {(int)encodedBuf[i]} <> {(int)buf[i]} at index {i}"); + } + } + + private static void AssertFileContentsStartsWith(string fileName, string contents, Encoding encoding) + { + FileInfo fi = new FileInfo(fileName); + if (!fi.Exists) + Assert.Fail("File '" + fileName + "' doesn't exist."); + + byte[] encodedBuf = encoding.GetBytes(contents); + + byte[] buf = File.ReadAllBytes(fileName); + Assert.True(encodedBuf.Length <= buf.Length, + $"File:{fileName} encodedBytes:{encodedBuf.Length} does not match file.content:{buf.Length}, file.length = {fi.Length}"); + + for (int i = 0; i < encodedBuf.Length; ++i) + { + if (encodedBuf[i] != buf[i]) + Assert.True(encodedBuf[i] == buf[i], + $"File:{fileName} content mismatch {(int)encodedBuf[i]} <> {(int)buf[i]} at index {i}"); + } + } + + private static void AssertFileContentsEndsWith(string fileName, string contents, Encoding encoding) + { + if (!File.Exists(fileName)) + Assert.Fail("File '" + fileName + "' doesn't exist."); + + string fileText = File.ReadAllText(fileName, encoding); + Assert.True(fileText.Length >= contents.Length); + Assert.Equal(contents, fileText.Substring(fileText.Length - contents.Length)); + } + + protected sealed class CustomFileCompressor : IArchiveFileCompressor + { + public void CompressFile(string fileName, string archiveFileName) + { + string entryName = Path.GetFileNameWithoutExtension(archiveFileName) + Path.GetExtension(fileName); + CompressFile(fileName, archiveFileName, entryName); + } + + public void CompressFile(string fileName, string archiveFileName, string entryName) + { +#if NETFRAMEWORK + using (var zip = new Ionic.Zip.ZipFile()) + { + ZipEntry entry = zip.AddFile(fileName); + entry.FileName = entryName; + zip.Save(archiveFileName); + } +#endif + } + } + +#if NET35 || NET40 + protected static void AssertZipFileContents(string fileName, string expectedEntryName, string contents, Encoding encoding) + { + if (!File.Exists(fileName)) + Assert.True(false, "File '" + fileName + "' doesn't exist."); + + byte[] encodedBuf = encoding.GetBytes(contents); + + using (var zip = new Ionic.Zip.ZipFile(fileName)) + { + Assert.Equal(1, zip.Count); + Assert.Equal(encodedBuf.Length, zip[0].UncompressedSize); + + byte[] buf = new byte[zip[0].UncompressedSize]; + using (var fs = zip[0].OpenReader()) + { + fs.Read(buf, 0, buf.Length); + } + + for (int i = 0; i < buf.Length; ++i) + { + Assert.Equal(encodedBuf[i], buf[i]); + } + } + } +#else + protected static void AssertZipFileContents(string fileName, string expectedEntryName, string contents, Encoding encoding) + { + FileInfo fi = new FileInfo(fileName); + if (!fi.Exists) + Assert.Fail("File '" + fileName + "' doesn't exist."); + + byte[] encodedBuf = encoding.GetBytes(contents); + using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (var zip = new ZipArchive(stream, ZipArchiveMode.Read)) + { + Assert.Single(zip.Entries); + Assert.Equal(expectedEntryName, zip.Entries[0].Name); + Assert.Equal(encodedBuf.Length, zip.Entries[0].Length); + + byte[] buf = new byte[(int)zip.Entries[0].Length]; + using (var fs = zip.Entries[0].Open()) + { + fs.Read(buf, 0, buf.Length); + } + + for (int i = 0; i < buf.Length; ++i) + { + Assert.Equal(encodedBuf[i], buf[i]); + } + } + } +#endif + private sealed class ShiftedTimeSource : TimeSource + { + private readonly DateTimeKind kind; + private DateTimeOffset sourceTime; + private TimeSpan systemTimeDelta; + + public ShiftedTimeSource(DateTimeKind kind) + { + this.kind = kind; + sourceTime = DateTimeOffset.UtcNow; + systemTimeDelta = TimeSpan.Zero; + } + + public override DateTime Time => ConvertToKind(sourceTime); + + public DateTime SystemTime => ConvertToKind(sourceTime - systemTimeDelta); + + public override DateTime FromSystemTime(DateTime systemTime) + { + return ConvertToKind(systemTime + systemTimeDelta); + } + + private DateTime ConvertToKind(DateTimeOffset value) + { + return kind == DateTimeKind.Local ? value.LocalDateTime : value.UtcDateTime; + } + + public void AddToSystemTime(TimeSpan delta) + { + systemTimeDelta += delta; + } + + public void AddToLocalTime(TimeSpan delta) + { + sourceTime += delta; + } + } } } diff --git a/tests/NLog.UnitTests/Targets/ConcurrentFileTargetTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentWritesMultiProcessTests.cs similarity index 89% rename from tests/NLog.UnitTests/Targets/ConcurrentFileTargetTests.cs rename to tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentWritesMultiProcessTests.cs index 3eb0eacd96..56ffee0cc4 100644 --- a/tests/NLog.UnitTests/Targets/ConcurrentFileTargetTests.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentWritesMultiProcessTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen // // All rights reserved. @@ -43,21 +43,29 @@ namespace NLog.UnitTests.Targets using System.IO; using System.Linq; using System.Threading; + using NLog.Common; using NLog.Config; using NLog.Targets; using NLog.Targets.Wrappers; using Xunit; using Xunit.Extensions; - public class ConcurrentFileTargetTests : NLogTestBase + public class ConcurrentWritesMultiProcessTests { - private Logger _logger = LogManager.GetLogger("NLog.UnitTests.Targets.ConcurrentFileTargetTests"); + private readonly Logger _logger = LogManager.GetLogger("NLog.UnitTests.Targets.ConcurrentWritesMultiProcessTests"); + + public ConcurrentWritesMultiProcessTests() + { + InternalLogger.Reset(); + LogManager.Configuration = null; + LogManager.ThrowExceptions = true; + } private void ConfigureSharedFile(string mode, string fileName) { var modes = mode.Split('|'); - FileTarget ft = new FileTarget(); + ConcurrentFileTarget ft = new ConcurrentFileTarget(); ft.FileName = fileName; ft.Layout = "${message}"; ft.ConcurrentWrites = true; @@ -76,6 +84,8 @@ private void ConfigureSharedFile(string mode, string fileName) var name = "ConfigureSharedFile_" + mode.Replace('|', '_') + "-wrapper"; + LogManager.Setup().SetupExtensions(ext => ext.RegisterTarget("file")); + switch (modes[0]) { case "async": @@ -100,7 +110,7 @@ private void ConfigureSharedFile(string mode, string fileName) } #pragma warning disable xUnit1013 // Needed for test - public void Process(string processIndex, string fileName, string numLogsString, string mode) + public void MultiProcessExecutor(string processIndex, string fileName, string numLogsString, string mode) #pragma warning restore xUnit1013 { Thread.CurrentThread.Name = processIndex; @@ -125,17 +135,17 @@ public void Process(string processIndex, string fileName, string numLogsString, try #endif { - using (new NoThrowNLogExceptions()) - { - Thread.Sleep(Math.Max((10 - idxProcess), 1) * 5); // Delay to wait for the other processes + LogManager.ThrowExceptions = false; - for (int i = 0; i < numLogs; ++i) - { - _logger.Debug(format, i); - } + Thread.Sleep(Math.Max((10 - idxProcess), 1) * 5); // Delay to wait for the other processes - LogManager.Configuration = null; // Flush + Close + for (int i = 0; i < numLogs; ++i) + { + _logger.Debug(format, i); } + + LogManager.Configuration = null; // Flush + Close + LogManager.ThrowExceptions = true; } #if !DISABLE_FILE_INTERNAL_LOGGING catch (Exception ex) @@ -147,6 +157,10 @@ public void Process(string processIndex, string fileName, string numLogsString, } throw; } + finally + { + LogManager.ThrowExceptions = true; + } using (var textWriter = File.AppendText(Path.Combine(Path.GetDirectoryName(fileName), string.Format("Internal_{0}.txt", processIndex)))) { @@ -163,7 +177,7 @@ private string MakeFileName(int numProcesses, int numLogs, string mode) private void DoConcurrentTest(int numProcesses, int numLogs, string mode) { - string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempPath = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); string archivePath = Path.Combine(tempPath, "Archive"); try @@ -183,7 +197,7 @@ private void DoConcurrentTest(int numProcesses, int numLogs, string mode) { processes[i] = ProcessRunner.SpawnMethod( GetType(), - "Process", + nameof(MultiProcessExecutor), i.ToString(), logFile, numLogs.ToString(), @@ -215,10 +229,8 @@ private void DoConcurrentTest(int numProcesses, int numLogs, string mode) //Console.WriteLine("Verifying output file {0}", logFile); foreach (var file in files) { - using (StreamReader sr = File.OpenText(file)) { - string line; while ((line = sr.ReadLine()) != null) { @@ -232,7 +244,6 @@ private void DoConcurrentTest(int numProcesses, int numLogs, string mode) receivedNumbersSet[thread].Add(number); } - if (verifyFileSize) { if (sr.BaseStream.Length > 100) @@ -278,7 +289,6 @@ private void DoConcurrentTest(int numProcesses, int numLogs, string mode) var reoderProblem = equalsWhenReorderd is null ? "Dunno" : (equalsWhenReorderd == true ? "Yes" : "No"); throw new InvalidOperationException($"Error when comparing path {tempPath} for process {currentProcess}. Is this a recording problem? {reoderProblem}", ex); } - } finally { @@ -309,7 +319,19 @@ private void DoConcurrentTest(int numProcesses, int numLogs, string mode) #endif public void SimpleConcurrentTest(int numProcesses, int numLogs, string mode) { - RetryingIntegrationTest(3, () => DoConcurrentTest(numProcesses, numLogs, mode)); + for (int i = 1; i <= 3; ++i) + { + try + { + DoConcurrentTest(numProcesses, numLogs, mode); + return; + } + catch + { + if (i == 3) + throw; + } + } } [Theory] diff --git a/tests/NLog.UnitTests/Internal/FileAppenders/FileAppenderCacheTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/FileAppenderCacheTests.cs similarity index 81% rename from tests/NLog.UnitTests/Internal/FileAppenders/FileAppenderCacheTests.cs rename to tests/NLog.Targets.ConcurrentFile.Tests/FileAppenderCacheTests.cs index 11943b3f7b..2c330c9d84 100644 --- a/tests/NLog.UnitTests/Internal/FileAppenders/FileAppenderCacheTests.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/FileAppenderCacheTests.cs @@ -35,13 +35,20 @@ namespace NLog.UnitTests.Internal.FileAppenders { using System; using System.IO; + using System.Linq; using System.Text; using NLog.Internal.FileAppenders; using NLog.Targets; using Xunit; - public class FileAppenderCacheTests : NLogTestBase + public class FileAppenderCacheTests { + public FileAppenderCacheTests() + { + LogManager.ThrowExceptions = true; + LogManager.Setup().SetupExtensions(ext => ext.RegisterTarget("file")); + } + [Fact] public void FileAppenderCache_Empty() { @@ -57,7 +64,7 @@ public void FileAppenderCache_Empty() public void FileAppenderCache_Construction() { IFileAppenderFactory appenderFactory = SingleProcessFileAppender.TheFactory; - ICreateFileParameters fileTarget = new FileTarget(); + ICreateFileParameters fileTarget = new ConcurrentFileTarget(); FileAppenderCache cache = new FileAppenderCache(3, appenderFactory, fileTarget); Assert.Equal(3, cache.Size); @@ -74,7 +81,7 @@ public void FileAppenderCache_Allocate() // Construct a on non-empty FileAppenderCache. IFileAppenderFactory appenderFactory = SingleProcessFileAppender.TheFactory; - ICreateFileParameters fileTarget = new FileTarget(); + ICreateFileParameters fileTarget = new ConcurrentFileTarget(); String tempFile = Path.Combine( Path.GetTempPath(), Path.Combine(Guid.NewGuid().ToString(), "file.txt") @@ -107,7 +114,7 @@ public void FileAppenderCache_InvalidateAppender() // Construct a on non-empty FileAppenderCache. IFileAppenderFactory appenderFactory = SingleProcessFileAppender.TheFactory; - ICreateFileParameters fileTarget = new FileTarget(); + ICreateFileParameters fileTarget = new ConcurrentFileTarget(); String tempFile = Path.Combine( Path.GetTempPath(), Path.Combine(Guid.NewGuid().ToString(), "file.txt") @@ -139,7 +146,7 @@ public void FileAppenderCache_CloseAppenders() emptyCache.CloseExpiredAppenders(DateTime.UtcNow); IFileAppenderFactory appenderFactory = RetryingMultiProcessFileAppender.TheFactory; - ICreateFileParameters fileTarget = new FileTarget(); + ICreateFileParameters fileTarget = new ConcurrentFileTarget(); FileAppenderCache cache = new FileAppenderCache(3, appenderFactory, fileTarget); // Invoke CloseAppenders() on non-empty FileAppenderCache - Before allocating any appenders. cache.CloseAppenders(string.Empty); @@ -182,7 +189,7 @@ public void FileAppenderCache_CloseAppenders() public void FileAppenderCache_GetFileCharacteristics_Single() { IFileAppenderFactory appenderFactory = SingleProcessFileAppender.TheFactory; - ICreateFileParameters fileTarget = new FileTarget() { ArchiveNumbering = ArchiveNumberingMode.Date }; + ICreateFileParameters fileTarget = new ConcurrentFileTarget() { ArchiveNumbering = ArchiveNumberingMode.Date }; FileAppenderCache_GetFileCharacteristics(appenderFactory, fileTarget); } @@ -190,7 +197,7 @@ public void FileAppenderCache_GetFileCharacteristics_Single() public void FileAppenderCache_GetFileCharacteristics_Multi() { IFileAppenderFactory appenderFactory = MutexMultiProcessFileAppender.TheFactory; - ICreateFileParameters fileTarget = new FileTarget() { ArchiveNumbering = ArchiveNumberingMode.Date, ForceManaged = true }; + ICreateFileParameters fileTarget = new ConcurrentFileTarget() { ArchiveNumbering = ArchiveNumberingMode.Date, ForceManaged = true }; FileAppenderCache_GetFileCharacteristics(appenderFactory, fileTarget); } @@ -201,7 +208,7 @@ public void FileAppenderCache_GetFileCharacteristics_Windows() if (NLog.Internal.PlatformDetector.IsWin32) { IFileAppenderFactory appenderFactory = WindowsMultiProcessFileAppender.TheFactory; - ICreateFileParameters fileTarget = new FileTarget() { ArchiveNumbering = ArchiveNumberingMode.Date }; + ICreateFileParameters fileTarget = new ConcurrentFileTarget() { ArchiveNumbering = ArchiveNumberingMode.Date }; FileAppenderCache_GetFileCharacteristics(appenderFactory, fileTarget); } } @@ -262,5 +269,51 @@ private static byte[] StringToBytes(string text) Buffer.BlockCopy(text.ToCharArray(), 0, bytes, 0, bytes.Length); return bytes; } + + private static void AssertFileContents(string fileName, string contents, Encoding encoding, bool addBom = false) + { + FileInfo fi = new FileInfo(fileName); + if (!fi.Exists) + Assert.Fail("File '" + fileName + "' doesn't exist."); + + byte[] encodedBuf = encoding.GetBytes(contents); + + //add bom if needed + if (addBom) + { + var preamble = encoding.GetPreamble(); + if (preamble.Length > 0) + { + //insert before + encodedBuf = preamble.Concat(encodedBuf).ToArray(); + } + } + + byte[] buf; + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) + { + int index = 0; + int count = (int)fs.Length; + buf = new byte[count]; + while (count > 0) + { + int n = fs.Read(buf, index, count); + if (n == 0) + break; + index += n; + count -= n; + } + } + + Assert.True(encodedBuf.Length == buf.Length, + $"File:{fileName} encodedBytes:{encodedBuf.Length} does not match file.content:{buf.Length}, file.length = {fi.Length}"); + + for (int i = 0; i < buf.Length; ++i) + { + if (encodedBuf[i] != buf[i]) + Assert.True(encodedBuf[i] == buf[i], + $"File:{fileName} content mismatch {(int)encodedBuf[i]} <> {(int)buf[i]} at index {i}"); + } + } } } diff --git a/tests/NLog.UnitTests/Internal/FilePathLayoutTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/FilePathLayoutTests.cs similarity index 100% rename from tests/NLog.UnitTests/Internal/FilePathLayoutTests.cs rename to tests/NLog.Targets.ConcurrentFile.Tests/FilePathLayoutTests.cs diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/NLog.Targets.ConcurrentFile.Tests.csproj b/tests/NLog.Targets.ConcurrentFile.Tests/NLog.Targets.ConcurrentFile.Tests.csproj new file mode 100644 index 0000000000..3bf00b6c70 --- /dev/null +++ b/tests/NLog.Targets.ConcurrentFile.Tests/NLog.Targets.ConcurrentFile.Tests.csproj @@ -0,0 +1,41 @@ + + + + 17.0 + net462 + net462;net6.0 + + false + + NLogTests.snk + false + true + true + + Full + true + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/NLogTests.snk b/tests/NLog.Targets.ConcurrentFile.Tests/NLogTests.snk new file mode 100644 index 0000000000000000000000000000000000000000..f168e4dbecfd20aae45a58ff7c8146fd9437ca8c GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098+j;l|->ro!-M_v|L{!{P{!ND{K0P(6c zd-FqtRo5AlvWp)3?L^4gdYGOJw(uLvUU=}ZRnrkvZ)4sHmYxL91Vs-+gH5^l3FT%~ zT4&@aA_Hh(2U-<&=?)x2)II__B*H2}e}!2p6a#Iu;d48bO^8HhTl;j8?*d<1a_R|q8E50kv?eP0U3&ZlGn@^pP7iWL$P<_mA z`9zel$Knfm4TsO>!T$Xq=dg4ToMpb)#Ve)# literal 0 HcmV?d00001 diff --git a/tests/NLog.UnitTests/ProcessRunner.cs b/tests/NLog.Targets.ConcurrentFile.Tests/ProcessRunner.cs similarity index 100% rename from tests/NLog.UnitTests/ProcessRunner.cs rename to tests/NLog.Targets.ConcurrentFile.Tests/ProcessRunner.cs diff --git a/src/NLog/Internal/EncodingHelpers.cs b/tests/NLog.Targets.ConcurrentFile.Tests/Properties/AssemblyInfo.cs similarity index 87% rename from src/NLog/Internal/EncodingHelpers.cs rename to tests/NLog.Targets.ConcurrentFile.Tests/Properties/AssemblyInfo.cs index efab1e9469..23035655fa 100644 --- a/src/NLog/Internal/EncodingHelpers.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/Properties/AssemblyInfo.cs @@ -31,13 +31,4 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.Internal -{ - internal static class EncodingHelpers - { - /// - /// UTF-8 BOM 239, 187, 191 - /// - internal static readonly byte[] Utf8BOM = { 0xEF, 0xBB, 0xBF }; - } -} +[assembly: Xunit.CollectionBehavior(DisableTestParallelization = true)] diff --git a/tests/NLog.UnitTests/Config/TargetConfigurationTests.cs b/tests/NLog.UnitTests/Config/TargetConfigurationTests.cs index e09a4ecbb1..c1fc96b2a7 100644 --- a/tests/NLog.UnitTests/Config/TargetConfigurationTests.cs +++ b/tests/NLog.UnitTests/Config/TargetConfigurationTests.cs @@ -521,7 +521,6 @@ public void DontThrowExceptionWhenArchiveEverySetByDefaultParameters() - + "); var target = configuration.FindTargetByName("test") as FileTarget; Assert.NotNull(target); //dont change the ${test} as it isn't a Layout - Assert.NotEqual(typeof(Layout), target.ArchiveDateFormat.GetType()); - Assert.Equal("hello", target.ArchiveDateFormat); + Assert.Equal(typeof(string), target.ArchiveSuffixFormat.GetType()); + Assert.Equal("hello", target.ArchiveSuffixFormat); } [Fact] diff --git a/tests/NLog.UnitTests/Internal/FileInfoHelperTests.cs b/tests/NLog.UnitTests/Internal/FileInfoHelperTests.cs new file mode 100644 index 0000000000..629f2cc5cd --- /dev/null +++ b/tests/NLog.UnitTests/Internal/FileInfoHelperTests.cs @@ -0,0 +1,79 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.UnitTests.Internal +{ + using NLog.Internal; + using Xunit; + + public class FileInfoHelperTests // Not needed as not using NLog-Core -> : NLogTestBase + { + [Theory] + [InlineData(@"", false)] + [InlineData(@" ", false)] + [InlineData(null, false)] + [InlineData(@"/ test\a", false)] + [InlineData(@"test.log", true)] + [InlineData(@"test", true)] + [InlineData(@" test.log ", true)] + [InlineData(@" a/test.log ", true)] + [InlineData(@".test.log ", true)] + [InlineData(@"..test.log ", true)] + [InlineData(@" .. test.log ", true)] + [InlineData(@"dir$/test ", true)] + public void DetectFilePathKind(string path, bool expected) + { + var result = FileInfoHelper.IsRelativeFilePath(path); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(@"d:\test.log", false)] + [InlineData(@"d:\test", false)] + [InlineData(@" d:\test", false)] + [InlineData(@" d:\ test", false)] + [InlineData(@" d:\ test\a", false)] + [InlineData(@"\\test\a", false)] + [InlineData(@"\\test/a", false)] + [InlineData(@"\ test\a", false)] + [InlineData(@" a\test.log ", true)] + public void DetectFilePathKindWindowsPath(string path, bool expected) + { + if (System.IO.Path.DirectorySeparatorChar != '\\') + return; //no backward-slash on linux + + var result = FileInfoHelper.IsRelativeFilePath(path); + Assert.Equal(expected, result); + } + } +} diff --git a/tests/NLog.UnitTests/NLogTestBase.cs b/tests/NLog.UnitTests/NLogTestBase.cs index 9c4f40c6b9..44aea81046 100644 --- a/tests/NLog.UnitTests/NLogTestBase.cs +++ b/tests/NLog.UnitTests/NLogTestBase.cs @@ -46,9 +46,6 @@ namespace NLog.UnitTests using NLog.Layouts; using NLog.Targets; using Xunit; -#if NETFRAMEWORK - using Ionic.Zip; -#endif public abstract class NLogTestBase { @@ -121,116 +118,6 @@ protected static DebugTarget GetDebugTarget(string targetName, LogFactory logFac return LogFactoryTestExtensions.GetDebugTarget(targetName, logFactory.Configuration); } - protected static void AssertFileContentsStartsWith(string fileName, string contents, Encoding encoding) - { - FileInfo fi = new FileInfo(fileName); - if (!fi.Exists) - Assert.Fail("File '" + fileName + "' doesn't exist."); - - byte[] encodedBuf = encoding.GetBytes(contents); - - byte[] buf = File.ReadAllBytes(fileName); - Assert.True(encodedBuf.Length <= buf.Length, - $"File:{fileName} encodedBytes:{encodedBuf.Length} does not match file.content:{buf.Length}, file.length = {fi.Length}"); - - for (int i = 0; i < encodedBuf.Length; ++i) - { - if (encodedBuf[i] != buf[i]) - Assert.True(encodedBuf[i] == buf[i], - $"File:{fileName} content mismatch {(int)encodedBuf[i]} <> {(int)buf[i]} at index {i}"); - } - } - - protected static void AssertFileContentsEndsWith(string fileName, string contents, Encoding encoding) - { - if (!File.Exists(fileName)) - Assert.Fail("File '" + fileName + "' doesn't exist."); - - string fileText = File.ReadAllText(fileName, encoding); - Assert.True(fileText.Length >= contents.Length); - Assert.Equal(contents, fileText.Substring(fileText.Length - contents.Length)); - } - - protected sealed class CustomFileCompressor : IArchiveFileCompressor - { - public void CompressFile(string fileName, string archiveFileName) - { - string entryName = Path.GetFileNameWithoutExtension(archiveFileName) + Path.GetExtension(fileName); - CompressFile(fileName, archiveFileName, entryName); - } - - public void CompressFile(string fileName, string archiveFileName, string entryName) - { -#if NETFRAMEWORK - using (var zip = new Ionic.Zip.ZipFile()) - { - ZipEntry entry = zip.AddFile(fileName); - entry.FileName = entryName; - zip.Save(archiveFileName); - } -#endif - } - } - -#if NET35 || NET40 - protected static void AssertZipFileContents(string fileName, string expectedEntryName, string contents, Encoding encoding) - { - if (!File.Exists(fileName)) - Assert.True(false, "File '" + fileName + "' doesn't exist."); - - byte[] encodedBuf = encoding.GetBytes(contents); - - using (var zip = new Ionic.Zip.ZipFile(fileName)) - { - Assert.Equal(1, zip.Count); - Assert.Equal(encodedBuf.Length, zip[0].UncompressedSize); - - byte[] buf = new byte[zip[0].UncompressedSize]; - using (var fs = zip[0].OpenReader()) - { - fs.Read(buf, 0, buf.Length); - } - - for (int i = 0; i < buf.Length; ++i) - { - Assert.Equal(encodedBuf[i], buf[i]); - } - } - } -#else - protected static void AssertZipFileContents(string fileName, string expectedEntryName, string contents, Encoding encoding) - { - FileInfo fi = new FileInfo(fileName); - if (!fi.Exists) - Assert.Fail("File '" + fileName + "' doesn't exist."); - - byte[] encodedBuf = encoding.GetBytes(contents); - using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) - using (var zip = new ZipArchive(stream, ZipArchiveMode.Read)) - { - Assert.Single(zip.Entries); - Assert.Equal(expectedEntryName, zip.Entries[0].Name); - Assert.Equal(encodedBuf.Length, zip.Entries[0].Length); - - byte[] buf = new byte[(int)zip.Entries[0].Length]; - using (var fs = zip.Entries[0].Open()) - { - fs.Read(buf, 0, buf.Length); - } - - for (int i = 0; i < buf.Length; ++i) - { - Assert.Equal(encodedBuf[i], buf[i]); - } - } - } -#endif - - protected static void AssertFileContents(string fileName, string expectedEntryName, string contents, Encoding encoding) - { - AssertFileContents(fileName, contents, encoding, false); - } - protected static void AssertFileContents(string fileName, string contents, Encoding encoding) { AssertFileContents(fileName, contents, encoding, false); diff --git a/tests/NLog.UnitTests/Targets/FileTargetTests.cs b/tests/NLog.UnitTests/Targets/FileTargetTests.cs index 50b16c2ada..7cd4bc95d2 100644 --- a/tests/NLog.UnitTests/Targets/FileTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/FileTargetTests.cs @@ -46,39 +46,12 @@ namespace NLog.UnitTests.Targets using NLog.Targets; using NLog.Targets.Wrappers; using NLog.Time; - using NSubstitute; using Xunit; public class FileTargetTests : NLogTestBase { private readonly Logger logger = LogManager.GetLogger("NLog.UnitTests.Targets.FileTargetTests"); - public static IEnumerable SimpleFileTest_TestParameters - { - get - { - var booleanValues = new[] { true, false }; - return - from concurrentWrites in booleanValues - from keepFileOpen in booleanValues - from forceMutexConcurrentWrites in booleanValues - where UniqueBaseAppender(concurrentWrites, keepFileOpen, forceMutexConcurrentWrites) - from forceManaged in booleanValues - select new object[] { concurrentWrites, keepFileOpen, forceManaged, forceMutexConcurrentWrites }; - } - } - - private static bool UniqueBaseAppender(bool concurrentWrites, bool keepFileOpen, bool forceMutexConcurrentWrites) - { - if (!keepFileOpen && !concurrentWrites && !forceMutexConcurrentWrites) - return true; - if (!keepFileOpen && concurrentWrites && !forceMutexConcurrentWrites) - return true; - if (keepFileOpen && !forceMutexConcurrentWrites) - return true; - return false; - } - [Fact] public void SetupBuilder_WriteToFile() { @@ -105,8 +78,9 @@ public void SetupBuilder_WriteToFile() } [Theory] - [MemberData(nameof(SimpleFileTest_TestParameters))] - public void SimpleFileTest(bool concurrentWrites, bool keepFileOpen, bool forceManaged, bool forceMutexConcurrentWrites) + [InlineData(true)] + [InlineData(false)] + public void SimpleFileTest(bool keepFileOpen) { var logFile = Path.GetTempFileName(); try @@ -117,10 +91,7 @@ public void SimpleFileTest(bool concurrentWrites, bool keepFileOpen, bool forceM LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", OpenFileCacheTimeout = 0, - ConcurrentWrites = concurrentWrites, KeepFileOpen = keepFileOpen, - ForceManaged = forceManaged, - ForceMutexConcurrentWrites = forceMutexConcurrentWrites, }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -141,22 +112,10 @@ public void SimpleFileTest(bool concurrentWrites, bool keepFileOpen, bool forceM } [Theory] - [MemberData(nameof(SimpleFileTest_TestParameters))] - public void SimpleFileDeleteTest(bool concurrentWrites, bool keepFileOpen, bool forceManaged, bool forceMutexConcurrentWrites) + [InlineData(true)] + [InlineData(false)] + public void SimpleFileDeleteTest(bool keepFileOpen) { - bool isSimpleKeepFileOpen = keepFileOpen && !concurrentWrites -#if NETFRAMEWORK && !MONO - && IsLinux() -#endif - ; - -#if MONO - if (IsLinux() && concurrentWrites && keepFileOpen) - { - Console.WriteLine("[SKIP] FileTargetTests.SimpleFileDeleteTest Not supported on MONO on Travis, because of FileSystemWatcher not working"); - return; - } -#endif foreach (var archiveSameFolder in new[] { true, false }) { RetryingIntegrationTest(3, () => @@ -177,10 +136,7 @@ public void SimpleFileDeleteTest(bool concurrentWrites, bool keepFileOpen, bool Layout = "${level} ${message}", OpenFileCacheTimeout = 0, EnableFileDelete = true, - ConcurrentWrites = concurrentWrites, KeepFileOpen = keepFileOpen, - ForceManaged = forceManaged, - ForceMutexConcurrentWrites = forceMutexConcurrentWrites, ArchiveAboveSize = archiveSameFolder ? 1000000 : 0, }; @@ -193,10 +149,8 @@ public void SimpleFileDeleteTest(bool concurrentWrites, bool keepFileOpen, bool Directory.CreateDirectory(Path.GetDirectoryName(arhiveFile)); File.Move(logFile, arhiveFile); - if (isSimpleKeepFileOpen) + if (keepFileOpen) Thread.Sleep(1500); // Ensure EnableFileDeleteSimpleMonitor will trigger - else if (keepFileOpen) - Thread.Sleep(150); // Allow AutoClose-Timer-Thread to react (FileWatcher schedules timer after 50 msec) logger.Info("bbb"); @@ -257,7 +211,7 @@ public void SimpleFileTestInRoot() [Fact] public void SimpleFileWithSpecialCharsTest() { - var logFile = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid() + "!@#$%^&()_-=+ .log"); + var logFile = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString() + "!@#$%^&()_-=+ .log"); SimpleFileWriteLogTest(logFile); } @@ -324,8 +278,9 @@ public void SimpleFileTestWriteBom() /// If a drive doesn't existing, before repeatably creating a dir was tried. This test was taking +60 seconds /// [Theory] - [MemberData(nameof(SimpleFileTest_TestParameters))] - public void NonExistingDriveShouldNotDelayMuch(bool concurrentWrites, bool keepFileOpen, bool forceManaged, bool forceMutexConcurrentWrites) + [InlineData(true)] + [InlineData(false)] + public void NonExistingDriveShouldNotDelayMuch(bool keepFileOpen) { var nonExistingDrive = GetFirstNonExistingDriveWindows(); @@ -341,10 +296,7 @@ public void NonExistingDriveShouldNotDelayMuch(bool concurrentWrites, bool keepF { FileName = logFile, Layout = "${level} ${message}", - ConcurrentWrites = concurrentWrites, KeepFileOpen = keepFileOpen, - ForceManaged = forceManaged, - ForceMutexConcurrentWrites = forceMutexConcurrentWrites, }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -379,11 +331,12 @@ private static char GetFirstNonExistingDriveWindows() "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToList().First(driveLetter => !existingDrives.Contains(driveLetter.ToString())); return nonExistingDrive; } - #endif - [Fact] - public void RollingArchiveEveryMonth() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RollingArchiveEveryMonday(bool keepFileOpen) { var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var defaultTimeSource = TimeSource.Current; @@ -401,36 +354,35 @@ public void RollingArchiveEveryMonth() var fileTarget = new FileTarget { - FileName = Path.Combine(tempDir, "${date:format=dd}_AppName.log"), + FileName = Path.Combine(tempDir, "${message}_AppName.log"), LineEnding = LineEndingMode.LF, Layout = "${message}", - ArchiveNumbering = ArchiveNumberingMode.Rolling, - ArchiveEvery = FileArchivePeriod.Month, + ArchiveEvery = FileArchivePeriod.Monday, MaxArchiveFiles = 1, + KeepFileOpen = keepFileOpen, }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); - for (int i = 0; i < 12; ++i) + for (int i = 0; i < 3; ++i) { - for (int j = 0; j < 31; ++j) + for (char j = 'A'; j < 'Z'; ++j) { - logger.Debug("aaa"); + logger.Debug(new string(j, 2)); timeSource.AddToLocalTime(TimeSpan.FromDays(1)); - timeSource.AddToSystemTime(TimeSpan.FromDays(1)); + //timeSource.AddToSystemTime(TimeSpan.FromDays(1)); } } - var files = Directory.GetFiles(tempDir); - // Cleanup doesn't work, as all file names has the same timestamp - if (files.Length < 28 || files.Length > 31) - Assert.Equal(30, files.Length); + LogManager.Configuration = null; // Flush and close + + var files = new DirectoryInfo(tempDir).GetFiles(); + Assert.Equal(25, files.Length); foreach (var file in files) { - Assert.Equal(14, Path.GetFileName(file).Length); + Assert.Equal(14, file.Name.Length); + Assert.Equal(3, file.Length); } - - fileTarget.Close(); } finally { @@ -446,15 +398,15 @@ public void RollingArchiveEveryMonth() #else [Theory(Skip="Not supported on MONO on Travis, because of File birthtime not working")] #endif - [InlineData(false, false, ArchiveNumberingMode.DateAndSequence)] - [InlineData(false, true, ArchiveNumberingMode.DateAndSequence)] - [InlineData(false, false, ArchiveNumberingMode.Sequence)] - [InlineData(false, true, ArchiveNumberingMode.Sequence)] - [InlineData(true, false, ArchiveNumberingMode.DateAndSequence)] - [InlineData(true, true, ArchiveNumberingMode.DateAndSequence)] - [InlineData(true, false, ArchiveNumberingMode.Sequence)] - [InlineData(true, true, ArchiveNumberingMode.Sequence)] - public void DatedArchiveEveryMonth(bool archiveSubFolder, bool maxArchiveDays, ArchiveNumberingMode archiveNumberingMode) + [InlineData(false, false, true)] + [InlineData(false, true, true)] + [InlineData(false, false, false)] + [InlineData(false, true, false)] + [InlineData(true, false, true)] + [InlineData(true, true, true)] + [InlineData(true, false, false)] + [InlineData(true, true, false)] + public void DatedArchiveEveryMonth(bool archiveSubFolder, bool maxArchiveDays, bool archiveWithDateAndSequence) { if (IsLinux()) { @@ -509,13 +461,14 @@ public void DatedArchiveEveryMonth(bool archiveSubFolder, bool maxArchiveDays, A Encoding = Encoding.ASCII, Layout = "${message}", KeepFileOpen = i % 2 != 0, - ArchiveFileName = archiveSubFolder ? Path.Combine(archiveDir, "AppName.{#}.log") : (Layout)null, - ArchiveNumbering = archiveNumberingMode, - ArchiveEvery = FileArchivePeriod.Month, - ArchiveDateFormat = "yyyyMMdd", - MaxArchiveFiles = maxArchiveDays ? 0 : 2, - MaxArchiveDays = maxArchiveDays ? 5 * 30 : 0 + ArchiveFileName = archiveSubFolder ? Path.Combine(archiveDir, "AppName.{#}.log") : Path.Combine(tempDir, "AppName.{#}.log"), + ArchiveSuffixFormat = archiveWithDateAndSequence ? "_{1:yyyyMMdd}_{0:0}" : "_{0:0}", + ArchiveEvery = FileArchivePeriod.Month }; + if (maxArchiveDays) + fileTarget.MaxArchiveDays = 5 * 30; + else + fileTarget.MaxArchiveFiles = 2; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); logger.Debug($"{i.ToString()}{i.ToString()}{i.ToString()}"); @@ -533,13 +486,13 @@ public void DatedArchiveEveryMonth(bool archiveSubFolder, bool maxArchiveDays, A Assert.Empty(newFile); newFile = fileName; - if (archiveNumberingMode == ArchiveNumberingMode.DateAndSequence && createdFiles.Count > 1) + if (archiveWithDateAndSequence && createdFiles.Count > 1) { // Verify it used the last-modified-time (And not file-creation-time) string dateName = string.Empty; dateName = Path.GetFileName(fileName); - dateName = dateName.Replace("AppName.", ""); - dateName = dateName.Replace(".0.log", ""); + dateName = dateName.Replace("AppName_", ""); + dateName = dateName.Replace("_0.log", ""); dateName = dateName.Replace("log", ""); Assert.NotEmpty(dateName); Assert.Equal(timeSource.Time.Month, DateTime.ParseExact(dateName, "yyyyMMdd", null).Month); @@ -596,8 +549,7 @@ public void CsvHeaderTest() LineEnding = LineEndingMode.LF, Layout = layout, OpenFileCacheTimeout = 0, - ReplaceFileContentsOnEachWrite = false, - ArchiveAboveSize = 120, // Only 2 LogEvents per file + ArchiveAboveSize = 100, // Only 2 LogEvents per file MaxArchiveFiles = 1, }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -744,51 +696,20 @@ public void DeleteFileOnStartTest_noExceptionWhenMissing() } } -#if NETFRAMEWORK - public static IEnumerable ArchiveFileOnStartTests_TestParameters - { - get - { - var booleanValues = new[] { true, false }; - return - from enableCompression in booleanValues - from customFileCompressor in booleanValues - select new object[] { enableCompression, customFileCompressor }; - } - } -#else - public static IEnumerable ArchiveFileOnStartTests_TestParameters - { - get - { - var booleanValues = new[] { true, false }; - return - from enableCompression in booleanValues - select new object[] { enableCompression, false }; - } - } -#endif - - [Theory] - [MemberData(nameof(ArchiveFileOnStartTests_TestParameters))] - public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompressor) + [Fact] + public void ArchiveFileOnStartTests() { var logFile = Path.GetTempFileName() + ".txt"; - var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "Archive"); - var archiveExtension = enableCompression ? "zip" : "txt"; - IFileCompressor fileCompressor = null; + var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString(), "Archive"); + var archiveExtension = "txt"; + try { - if (customFileCompressor) - { - fileCompressor = FileTarget.FileCompressor; - FileTarget.FileCompressor = new CustomFileCompressor(); - } - // Configure first time with ArchiveOldFileOnStartup = false. var fileTarget = new FileTarget { ArchiveOldFileOnStartup = false, + ArchiveSuffixFormat = "", FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}" @@ -833,13 +754,12 @@ public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompr FileTarget ft; fileTarget = ft = new FileTarget { - EnableArchiveFileCompression = enableCompression, FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", ArchiveOldFileOnStartup = true, ArchiveFileName = archiveTempName, - ArchiveNumbering = ArchiveNumberingMode.Sequence, + ArchiveSuffixFormat = "", MaxArchiveFiles = 1 }; @@ -853,9 +773,7 @@ public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompr AssertFileContents(logFile, "Debug ddd\nInfo eee\nWarn fff\n", Encoding.UTF8); Assert.True(File.Exists(archiveTempName)); - var assertFileContents = ft.EnableArchiveFileCompression ? - new Action(AssertZipFileContents) : - AssertFileContents; + Action assertFileContents = (f1, f2, content, encoding) => AssertFileContents(f1, content, encoding, false); #if !NET35 string expectedEntryName = Path.GetFileNameWithoutExtension(archiveTempName) + ".txt"; @@ -867,8 +785,6 @@ public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompr } finally { - if (customFileCompressor) - FileTarget.FileCompressor = fileCompressor; if (File.Exists(logFile)) File.Delete(logFile); if (Directory.Exists(tempArchiveFolder)) @@ -880,7 +796,7 @@ public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompr public void ArchiveOldFileOnStartupAboveSize() { var logFile = Path.GetTempFileName(); - var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "Archive"); + var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var archiveTempName = Path.Combine(tempArchiveFolder, "archive_size_threshold.txt"); FileTarget CreateTestTarget(long threshold) { @@ -889,9 +805,9 @@ FileTarget CreateTestTarget(long threshold) FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", - ArchiveOldFileOnStartupAboveSize = threshold, + ArchiveAboveSize = threshold, ArchiveFileName = archiveTempName, - ArchiveNumbering = ArchiveNumberingMode.Sequence, + ArchiveSuffixFormat = "", MaxArchiveFiles = 1 }; } @@ -925,7 +841,7 @@ FileTarget CreateTestTarget(long threshold) public void ArchiveOldFileOnStartupAboveSizeWhenFileLocked() { var logFile = Path.GetTempFileName(); - var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "Archive"); + var tempArchiveFolder = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString(), "Archive"); var archiveTempName = Path.Combine(tempArchiveFolder, "archive_size_threshold.zip"); FileTarget CreateTestTarget(long threshold) @@ -935,10 +851,8 @@ FileTarget CreateTestTarget(long threshold) FileName = SimpleLayout.Escape(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", - ArchiveOldFileOnStartupAboveSize = threshold, + ArchiveAboveSize = threshold, ArchiveFileName = archiveTempName, - ArchiveNumbering = ArchiveNumberingMode.Sequence, - EnableArchiveFileCompression = true, MaxArchiveFiles = 1 }; } @@ -986,7 +900,6 @@ public void RetryFileOpenWhenFileLocked() LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", KeepFileOpen = false, - ConcurrentWriteAttempts = 100, }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -1072,35 +985,36 @@ public void ReplaceFileContentsOnEachWriteTest(bool useHeader, bool useFooter) [InlineData(false)] public void ReplaceFileContentsOnEachWrite_CreateDirs(bool createDirs) { - var tempDir = Path.Combine(Path.GetTempPath(), "dir_" + Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logfile = Path.Combine(tempDir, "log.log"); try { - using (new NoThrowNLogExceptions()) + LogManager.ThrowExceptions = false; + + var target = new FileTarget { - var target = new FileTarget - { - FileName = logfile, - ReplaceFileContentsOnEachWrite = true, - CreateDirs = createDirs - }; - var config = new LoggingConfiguration(); + FileName = logfile, + ReplaceFileContentsOnEachWrite = true, + CreateDirs = createDirs + }; + var config = new LoggingConfiguration(); - config.AddTarget("logfile", target); + config.AddTarget("logfile", target); - config.AddRuleForAllLevels(target); + config.AddRuleForAllLevels(target); - LogManager.Configuration = config; + LogManager.Configuration = config; - var logger = LogManager.GetLogger("A"); - logger.Info("a"); + var logger = LogManager.GetLogger("A"); + logger.Info("a"); - Assert.Equal(createDirs, Directory.Exists(tempDir)); - } + Assert.Equal(createDirs, Directory.Exists(tempDir)); } finally { + LogManager.ThrowExceptions = true; + if (File.Exists(logfile)) File.Delete(logfile); if (Directory.Exists(tempDir)) @@ -1111,7 +1025,7 @@ public void ReplaceFileContentsOnEachWrite_CreateDirs(bool createDirs) [Fact] public void CreateDirsTest() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -1142,12 +1056,15 @@ public void CreateDirsTest() } [Theory] - [InlineData(true, 0)] - [InlineData(false, 0)] - [InlineData(false, 1)] - public void AutoFlushTest(bool autoFlush, int autoFlushTimeout) + [InlineData(true, true, 0)] + [InlineData(false, true, 0)] + [InlineData(false, true, 1)] + [InlineData(true, false, 0)] + [InlineData(false, false, 0)] + [InlineData(false, false, 1)] + public void AutoFlushTest(bool autoFlush, bool keepFileOpen, int autoFlushTimeout) { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -1156,11 +1073,11 @@ public void AutoFlushTest(bool autoFlush, int autoFlushTimeout) FileName = logFile, LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", - KeepFileOpen = true, - ConcurrentWrites = false, - AutoFlush = autoFlush, + KeepFileOpen = keepFileOpen, OpenFileFlushTimeout = autoFlushTimeout, }; + if (!autoFlush) + fileTarget.AutoFlush = autoFlush; // Also ensures default-value is valid LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -1168,7 +1085,7 @@ public void AutoFlushTest(bool autoFlush, int autoFlushTimeout) logger.Info("bbb"); logger.Warn("ccc"); - if (autoFlush) + if (autoFlush || !keepFileOpen) { AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); } @@ -1198,7 +1115,7 @@ public void AutoFlushTest(bool autoFlush, int autoFlushTimeout) [Fact] public void SequentialArchiveTest() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -1206,12 +1123,11 @@ public void SequentialArchiveTest() var fileTarget = new FileTarget { FileName = logFile, - ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveFileName = Path.Combine(archiveFolder, "{##}.txt"), ArchiveAboveSize = 100, LineEnding = LineEndingMode.LF, Layout = "${message}", MaxArchiveFiles = 3, - ArchiveNumbering = ArchiveNumberingMode.Sequence }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -1232,23 +1148,23 @@ public void SequentialArchiveTest() Encoding.UTF8); AssertFileContents( - Path.Combine(archiveFolder, "0001.txt"), + Path.Combine(archiveFolder, "01.txt"), StringRepeat(times, "bbb\n"), Encoding.UTF8); AssertFileContents( - Path.Combine(archiveFolder, "0002.txt"), + Path.Combine(archiveFolder, "02.txt"), StringRepeat(times, "ccc\n"), Encoding.UTF8); AssertFileContents( - Path.Combine(archiveFolder, "0003.txt"), + Path.Combine(archiveFolder, "03.txt"), StringRepeat(times, "ddd\n"), Encoding.UTF8); //0000 should not exists because of MaxArchiveFiles=3 - Assert.False(File.Exists(Path.Combine(archiveFolder, "0000.txt"))); - Assert.False(File.Exists(Path.Combine(archiveFolder, "0004.txt"))); + Assert.False(File.Exists(Path.Combine(archiveFolder, "00.txt"))); + Assert.False(File.Exists(Path.Combine(archiveFolder, "04.txt"))); } finally { @@ -1260,9 +1176,9 @@ public void SequentialArchiveTest() } [Fact] - public void SequentialArchiveTest_MaxArchiveFiles_0() + public void SequentialArchiveTest_MaxArchiveFiles_NoLimit() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -1270,12 +1186,10 @@ public void SequentialArchiveTest_MaxArchiveFiles_0() var fileTarget = new FileTarget { FileName = logFile, - ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveFileName = Path.Combine(archiveFolder, "{##}.txt"), ArchiveAboveSize = 100, LineEnding = LineEndingMode.LF, - ArchiveNumbering = ArchiveNumberingMode.Sequence, Layout = "${message}", - MaxArchiveFiles = 0 }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -1295,26 +1209,26 @@ public void SequentialArchiveTest_MaxArchiveFiles_0() Encoding.UTF8); AssertFileContents( - Path.Combine(archiveFolder, "0000.txt"), + Path.Combine(archiveFolder, "00.txt"), StringRepeat(25, "aaa\n"), Encoding.UTF8); AssertFileContents( - Path.Combine(archiveFolder, "0001.txt"), + Path.Combine(archiveFolder, "01.txt"), StringRepeat(25, "bbb\n"), Encoding.UTF8); AssertFileContents( - Path.Combine(archiveFolder, "0002.txt"), + Path.Combine(archiveFolder, "02.txt"), StringRepeat(25, "ccc\n"), Encoding.UTF8); AssertFileContents( - Path.Combine(archiveFolder, "0003.txt"), + Path.Combine(archiveFolder, "03.txt"), StringRepeat(25, "ddd\n"), Encoding.UTF8); - Assert.False(File.Exists(Path.Combine(archiveFolder, "0004.txt"))); + Assert.False(File.Exists(Path.Combine(archiveFolder, "04.txt"))); } finally { @@ -1326,27 +1240,29 @@ public void SequentialArchiveTest_MaxArchiveFiles_0() } [Fact] - public void ArchiveAboveSizeWithArchiveNumberingModeDate_maxfiles_o() + public void ArchiveAboveSize_AppendSameArchiveDate() { - var tempDir = Path.Combine(Path.GetTempPath(), "ArchiveEveryCombinedWithArchiveAboveSize_" + Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { + var archiveDateFormat = "yyyyMMdd"; + string archiveFolder = Path.Combine(tempDir, "archive"); var fileTarget = new FileTarget { FileName = logFile, - ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveFileName = Path.Combine(archiveFolder, ".txt"), + ArchiveSuffixFormat = "{1:" + archiveDateFormat + "}", ArchiveAboveSize = 100, LineEnding = LineEndingMode.LF, Layout = "${message}", - ArchiveNumbering = ArchiveNumberingMode.Date }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); //e.g. 20150804 - var archiveFileName = DateTime.Now.ToString("yyyyMMdd"); + var archiveFileName = DateTime.Now.ToString(archiveDateFormat); // we emit 5 * 25 *(3 x aaa + \n) bytes // so that we should get a full file + 3 archives @@ -1355,7 +1271,6 @@ public void ArchiveAboveSizeWithArchiveNumberingModeDate_maxfiles_o() { logger.Debug("aaa"); } - for (var i = 0; i < times; ++i) { logger.Debug("bbb"); @@ -1401,7 +1316,7 @@ public void DeleteArchiveFilesByDate() { const int maxArchiveFiles = 3; - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -1412,16 +1327,17 @@ public void DeleteArchiveFilesByDate() ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), ArchiveAboveSize = 50, LineEnding = LineEndingMode.LF, - ArchiveNumbering = ArchiveNumberingMode.Date, +#pragma warning disable CS0618 // Type or member is obsolete ArchiveDateFormat = "yyyyMMddHHmmssfff", //make sure the milliseconds are set in the filename +#pragma warning restore CS0618 // Type or member is obsolete Layout = "${message}", MaxArchiveFiles = maxArchiveFiles }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); - //writing 19 times 10 bytes (9 char + linefeed) will result in 3 archive files and 1 current file - for (var i = 0; i < 19; ++i) + //writing 20 times 10 bytes (9 char + linefeed) will result in 3 archive files and 1 current file + for (var i = 0; i < 20; ++i) { logger.Debug("123456789"); //build in a small sleep to make sure the current time is reflected in the filename @@ -1470,7 +1386,7 @@ public void DeleteArchiveFilesByDateWithDateName() { const int maxArchiveFiles = 3; LogManager.ThrowExceptions = true; - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); try { var logFile = Path.Combine(tempDir, "${date:format=yyyyMMddHHmmssfff}.txt"); @@ -1480,10 +1396,11 @@ public void DeleteArchiveFilesByDateWithDateName() ArchiveFileName = Path.Combine(tempDir, "{#}.txt"), ArchiveEvery = FileArchivePeriod.Year, LineEnding = LineEndingMode.LF, - ArchiveNumbering = ArchiveNumberingMode.Date, +#pragma warning disable CS0618 // Type or member is obsolete ArchiveDateFormat = "yyyyMMddHHmmssfff", //make sure the milliseconds are set in the filename +#pragma warning restore CS0618 // Type or member is obsolete Layout = "${message}", - MaxArchiveFiles = maxArchiveFiles + MaxArchiveFiles = maxArchiveFiles, }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -1531,28 +1448,22 @@ public static IEnumerable DateArchive_UsesDateFromCurrentTimeSource_Te { get { - var maxArchiveDays = false; var booleanValues = new[] { true, false }; var timeKindValues = new[] { DateTimeKind.Utc, DateTimeKind.Local }; return from timeKind in timeKindValues - from includeDateInLogFilePath in booleanValues - from concurrentWrites in booleanValues from keepFileOpen in booleanValues - from forceMutexConcurrentWrites in booleanValues - where UniqueBaseAppender(concurrentWrites, keepFileOpen, forceMutexConcurrentWrites) - from includeSequenceInArchive in booleanValues - from forceManaged in booleanValues - select new object[] { timeKind, includeDateInLogFilePath, concurrentWrites, keepFileOpen, includeSequenceInArchive, forceManaged, forceMutexConcurrentWrites, maxArchiveDays }; + from maxArchiveDays in booleanValues + select new object[] { timeKind, keepFileOpen, maxArchiveDays }; } } [Theory] [MemberData(nameof(DateArchive_UsesDateFromCurrentTimeSource_TestParameters))] - public void DateArchive_UsesDateFromCurrentTimeSource(DateTimeKind timeKind, bool includeDateInLogFilePath, bool concurrentWrites, bool keepFileOpen, bool includeSequenceInArchive, bool forceManaged, bool forceMutexConcurrentWrites, bool maxArhiveDays) + public void DateArchive_UsesDateFromCurrentTimeSource(DateTimeKind timeKind, bool keepFileOpen, bool maxArchiveDays) { #if !NETFRAMEWORK || MONO - if (IsLinux()) + if (IsLinux() && !maxArchiveDays) { Console.WriteLine("[SKIP] FileTargetTests.DateArchive_UsesDateFromCurrentTimeSource because SetLastWriteTime is not working on Travis"); return; @@ -1562,8 +1473,8 @@ public void DateArchive_UsesDateFromCurrentTimeSource(DateTimeKind timeKind, boo const string archiveDateFormat = "yyyyMMdd"; const int maxArchiveFiles = 3; - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - var logFile = Path.Combine(tempDir, includeDateInLogFilePath ? "file_${shortdate}.txt" : "file.txt"); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); var defaultTimeSource = TimeSource.Current; try { @@ -1578,18 +1489,18 @@ public void DateArchive_UsesDateFromCurrentTimeSource(DateTimeKind timeKind, boo FileName = logFile, ArchiveFileName = archiveFileNameTemplate, LineEnding = LineEndingMode.LF, - ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, ArchiveEvery = FileArchivePeriod.Day, +#pragma warning disable CS0618 // Type or member is obsolete ArchiveDateFormat = archiveDateFormat, +#pragma warning restore CS0618 // Type or member is obsolete Layout = "${date:format=O}|${message}", - MaxArchiveFiles = maxArhiveDays ? 0 : maxArchiveFiles, - MaxArchiveDays = maxArhiveDays ? maxArchiveFiles : 0, - ConcurrentWrites = concurrentWrites, KeepFileOpen = keepFileOpen, - ForceManaged = forceManaged, - ForceMutexConcurrentWrites = forceMutexConcurrentWrites, Header = "header", }; + if (maxArchiveDays) + fileTarget.MaxArchiveDays = maxArchiveFiles; + else + fileTarget.MaxArchiveFiles = maxArchiveFiles; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -1605,24 +1516,20 @@ public void DateArchive_UsesDateFromCurrentTimeSource(DateTimeKind timeKind, boo if (timeSource.Time.Date != previousWriteTime.Date) { - string currentLogFile = includeDateInLogFilePath - ? logFile.Replace("${shortdate}", timeSource.Time.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)) - : logFile; // Simulate that previous file write began in previous day and ended on current day. try { - File.SetLastWriteTime(currentLogFile, timeSource.SystemTime); + File.SetLastWriteTime(logFile, timeSource.SystemTime); } catch { } } var eventInfo = new LogEventInfo(LogLevel.Debug, logger.Name, "123456789"); logger.Log(eventInfo); - LogManager.Flush(); var dayIsChanged = eventInfo.TimeStamp.Date != previousWriteTime.Date; // ensure new archive is created only when the day part of time is changed - var archiveFileName = archiveFileNameTemplate.Replace("{#}", previousWriteTime.ToString(archiveDateFormat) + (includeSequenceInArchive ? ".0" : string.Empty)); + var archiveFileName = archiveFileNameTemplate.Replace("{#}", previousWriteTime.ToString(archiveDateFormat) + "_00"); var archiveExists = File.Exists(archiveFileName); if (dayIsChanged) Assert.True(archiveExists, @@ -1631,7 +1538,7 @@ public void DateArchive_UsesDateFromCurrentTimeSource(DateTimeKind timeKind, boo Assert.False(archiveExists, $"new archive should not be create when day part of {timeKind} time is unchanged"); - previousWriteTime = eventInfo.TimeStamp.Date; + previousWriteTime = eventInfo.TimeStamp; if (dayIsChanged) timeSource.AddToSystemTime(TimeSpan.FromDays(1)); } @@ -1648,21 +1555,24 @@ public void DateArchive_UsesDateFromCurrentTimeSource(DateTimeKind timeKind, boo LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); //writing one line on a new day will trigger the cleanup of old archived files //as stated by the MaxArchiveFiles property, but will only delete the oldest file - timeSource.AddToLocalTime(TimeSpan.FromDays(1)); + timeSource.AddToLocalTime(TimeSpan.FromDays(3)); logger.Debug("1234567890"); LogManager.Configuration = null; // Flush - var files2 = Directory.GetFiles(archiveFolder); - Assert.Equal(maxArchiveFiles, files2.Length); - - //the oldest file should be deleted - Assert.DoesNotContain(files[0], files2); - //two files should still be there - Assert.Equal(files[1], files2[0]); - Assert.Equal(files[2], files2[1]); - //one new archive file should be created - Assert.DoesNotContain(files2[2], files); + if (!maxArchiveDays) + { + var files2 = Directory.GetFiles(archiveFolder); + Assert.Equal(maxArchiveFiles, files2.Length); + + //the oldest file should be deleted + Assert.DoesNotContain(files[0], files2); + //two files should still be there + Assert.Equal(files[1], files2[0]); + Assert.Equal(files[2], files2[1]); + //one new archive file should be created + Assert.DoesNotContain(files2[2], files); + } } finally { @@ -1672,44 +1582,24 @@ public void DateArchive_UsesDateFromCurrentTimeSource(DateTimeKind timeKind, boo } } - [Theory] - [InlineData(DateTimeKind.Utc, false, false)] - [InlineData(DateTimeKind.Local, false, false)] - [InlineData(DateTimeKind.Utc, true, false)] - [InlineData(DateTimeKind.Local, true, false)] - [InlineData(DateTimeKind.Utc, false, true)] - [InlineData(DateTimeKind.Local, false, true)] - [InlineData(DateTimeKind.Utc, true, true)] - [InlineData(DateTimeKind.Local, true, true)] - public void DateArchive_UsesDateFromCurrentTimeSource_MaxArchiveDays(DateTimeKind timeKind, bool includeDateInLogFilePath, bool includeSequenceInArchive) - { - const bool MaxArchiveDays = true; - DateArchive_UsesDateFromCurrentTimeSource(timeKind, includeDateInLogFilePath, false, false, includeSequenceInArchive, false, false, MaxArchiveDays); - } - public static IEnumerable DateArchive_ArchiveOnceOnly_TestParameters { get { var booleanValues = new[] { true, false }; return - from concurrentWrites in booleanValues from keepFileOpen in booleanValues - from forceMutexConcurrentWrites in booleanValues - where UniqueBaseAppender(concurrentWrites, keepFileOpen, forceMutexConcurrentWrites) - from includeDateInLogFilePath in booleanValues from includeSequenceInArchive in booleanValues - from forceManaged in booleanValues - select new object[] { concurrentWrites, keepFileOpen, includeDateInLogFilePath, includeSequenceInArchive, forceManaged, forceMutexConcurrentWrites }; + select new object[] { keepFileOpen, includeSequenceInArchive }; } } [Theory] [MemberData(nameof(DateArchive_ArchiveOnceOnly_TestParameters))] - public void DateArchive_ArchiveOnceOnly(bool concurrentWrites, bool keepFileOpen, bool dateInLogFilePath, bool includeSequenceInArchive, bool forceManaged, bool forceMutexConcurrentWrites) + public void DateArchive_ArchiveOnceOnly(bool keepFileOpen, bool includeSequenceInArchive) { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - var logFile = Path.Combine(tempDir, dateInLogFilePath ? "file_${shortdate}.txt" : "file.txt"); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); var defaultTimeSource = TimeSource.Current; @@ -1730,15 +1620,15 @@ public void DateArchive_ArchiveOnceOnly(bool concurrentWrites, bool keepFileOpen FileName = logFile, ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), LineEnding = LineEndingMode.LF, - ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, ArchiveEvery = FileArchivePeriod.Day, +#pragma warning disable CS0618 // Type or member is obsolete ArchiveDateFormat = "yyyyMMdd", +#pragma warning restore CS0618 // Type or member is obsolete Layout = "${message}", - ConcurrentWrites = concurrentWrites, KeepFileOpen = keepFileOpen, - ForceManaged = forceManaged, - ForceMutexConcurrentWrites = forceMutexConcurrentWrites, }; + if (!includeSequenceInArchive) + fileTarget.ArchiveSuffixFormat = @"_{1:yyyyMMdd}"; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -1747,8 +1637,7 @@ public void DateArchive_ArchiveOnceOnly(bool concurrentWrites, bool keepFileOpen LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); timeSource.AddToLocalTime(TimeSpan.FromDays(1)); - if (!dateInLogFilePath) - File.SetCreationTimeUtc(logFile, timeSource.Time.ToUniversalTime()); + File.SetCreationTimeUtc(logFile, timeSource.Time.ToUniversalTime()); timeSource.AddToLocalTime(TimeSpan.FromDays(1)); // This should archive the log before logging. @@ -1789,18 +1678,17 @@ public static IEnumerable DateArchive_SkipPeriod_TestParameters return from timeKind in timeKindValues from archivePeriod in archivePeriodValues - from includeDateInLogFilePath in booleanValues from includeSequenceInArchive in booleanValues - select new object[] { timeKind, archivePeriod, includeDateInLogFilePath, includeSequenceInArchive }; + select new object[] { timeKind, archivePeriod, includeSequenceInArchive }; } } [Theory] [MemberData(nameof(DateArchive_SkipPeriod_TestParameters))] - public void DateArchive_SkipPeriod(DateTimeKind timeKind, FileArchivePeriod archivePeriod, bool includeDateInLogFilePath, bool includeSequenceInArchive) + public void DateArchive_SkipPeriod(DateTimeKind timeKind, FileArchivePeriod archivePeriod, bool includeSequenceInArchive) { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - var logFile = Path.Combine(tempDir, includeDateInLogFilePath ? "file_${date:format=yyyyMMddHHmm}.txt" : "file.txt"); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "file.txt"); var defaultTimeSource = TimeSource.Current; try { @@ -1822,12 +1710,18 @@ public void DateArchive_SkipPeriod(DateTimeKind timeKind, FileArchivePeriod arch FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "archive", "{#}.txt"), LineEnding = LineEndingMode.LF, - ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, ArchiveEvery = archivePeriod, +#pragma warning disable CS0618 // Type or member is obsolete ArchiveDateFormat = "yyyyMMddHHmm", +#pragma warning restore CS0618 // Type or member is obsolete Layout = "${date:format=O}|${message}", }; + if (!includeSequenceInArchive) + fileTarget.ArchiveSuffixFormat = @"_{1:yyyyMMddHHmm}"; + +#pragma warning disable CS0618 // Type or member is obsolete string archiveDateFormat = fileTarget.ArchiveDateFormat; +#pragma warning restore CS0618 // Type or member is obsolete LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); logger.Debug("1234567890"); @@ -1835,7 +1729,7 @@ public void DateArchive_SkipPeriod(DateTimeKind timeKind, FileArchivePeriod arch logger.Debug("1234567890"); // The archive file name must be based on the last time the file was written. string archiveFileName = - $"{timeSource.Time.ToString(archiveDateFormat) + (includeSequenceInArchive ? ".0" : string.Empty)}.txt"; + $"{timeSource.Time.ToString(archiveDateFormat) + (includeSequenceInArchive ? "_00" : string.Empty)}.txt"; // Effectively update the file's last-write-time. timeSource.AddToSystemTime(TimeSpan.FromMinutes(1)); @@ -1857,44 +1751,15 @@ public void DateArchive_SkipPeriod(DateTimeKind timeKind, FileArchivePeriod arch } } - public static IEnumerable DateArchive_AllLoggersTransferToCurrentLogFile_TestParameters - { - get - { - var booleanValues = new[] { true, false }; - return - from concurrentWrites in booleanValues - from keepFileOpen in booleanValues - from forceMutexConcurrentWrites in booleanValues - where UniqueBaseAppender(concurrentWrites, keepFileOpen, forceMutexConcurrentWrites) - from includeDateInLogFilePath in booleanValues - from includeSequenceInArchive in booleanValues - from enableArchiveCompression in booleanValues - from forceManaged in booleanValues - select new object[] { concurrentWrites, keepFileOpen, includeDateInLogFilePath, includeSequenceInArchive, enableArchiveCompression, forceManaged, forceMutexConcurrentWrites }; - } - } - [Theory] - [MemberData(nameof(DateArchive_AllLoggersTransferToCurrentLogFile_TestParameters))] - public void DateArchive_AllLoggersTransferToCurrentLogFile(bool concurrentWrites, bool keepFileOpen, bool includeDateInLogFilePath, bool includeSequenceInArchive, bool enableArchiveCompression, bool forceManaged, bool forceMutexConcurrentWrites) + [InlineData(true)] + [InlineData(false)] + public void DateArchive_AllLoggersTransferToCurrentLogFile(bool includeSequenceInArchive) { - if (keepFileOpen && !concurrentWrites) - return; // This combination do not support two local FileTargets to the same file - -#if NET35 || NET40 - if (enableArchiveCompression) - return; // No need to test with compression -#endif - - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - var logfile = Path.Combine(tempDir, includeDateInLogFilePath ? "file_${shortdate}.txt" : "file.txt"); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logfile = Path.Combine(tempDir, "file.txt"); var defaultTimeSource = TimeSource.Current; -#if NET35 || NET40 - IFileCompressor fileCompressor = null; -#endif - try { var timeSource = new TimeSourceTests.ShiftedTimeSource(DateTimeKind.Local); @@ -1908,30 +1773,22 @@ public void DateArchive_AllLoggersTransferToCurrentLogFile(bool concurrentWrites var config = new LoggingConfiguration(); -#if NET35 || NET40 - if (enableArchiveCompression) - { - fileCompressor = FileTarget.FileCompressor; - FileTarget.FileCompressor = new CustomFileCompressor(); - } -#endif - string archiveFolder = Path.Combine(tempDir, "archive"); var fileTarget1 = new FileTarget { FileName = logfile, ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), LineEnding = LineEndingMode.LF, - ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, ArchiveEvery = FileArchivePeriod.Day, +#pragma warning disable CS0618 // Type or member is obsolete ArchiveDateFormat = "yyyyMMdd", - EnableArchiveFileCompression = enableArchiveCompression, +#pragma warning restore CS0618 // Type or member is obsolete Layout = "${message}", - ConcurrentWrites = concurrentWrites, - KeepFileOpen = keepFileOpen, - ForceManaged = forceManaged, - ForceMutexConcurrentWrites = forceMutexConcurrentWrites, + KeepFileOpen = false, // KeepFileOpen = false is required when 2 file-targets to the same file }; + if (!includeSequenceInArchive) + fileTarget1.ArchiveSuffixFormat = @"_{1:yyyyMMdd}"; + var logger1Rule = new LoggingRule("logger1", LogLevel.Debug, fileTarget1); config.LoggingRules.Add(logger1Rule); @@ -1940,16 +1797,16 @@ public void DateArchive_AllLoggersTransferToCurrentLogFile(bool concurrentWrites FileName = logfile, ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), LineEnding = LineEndingMode.LF, - ArchiveNumbering = includeSequenceInArchive ? ArchiveNumberingMode.DateAndSequence : ArchiveNumberingMode.Date, ArchiveEvery = FileArchivePeriod.Day, +#pragma warning disable CS0618 // Type or member is obsolete ArchiveDateFormat = "yyyyMMdd", - EnableArchiveFileCompression = enableArchiveCompression, +#pragma warning restore CS0618 // Type or member is obsolete Layout = "${message}", - ConcurrentWrites = concurrentWrites, - KeepFileOpen = keepFileOpen, - ForceManaged = forceManaged, - ForceMutexConcurrentWrites = forceMutexConcurrentWrites, + KeepFileOpen = false, // KeepFileOpen = false is required when 2 file-targets to the same file }; + if (!includeSequenceInArchive) + fileTarget2.ArchiveSuffixFormat = @"_{1:yyyyMMdd}"; + var logger2Rule = new LoggingRule("logger2", LogLevel.Debug, fileTarget2); config.LoggingRules.Add(logger2Rule); @@ -1960,27 +1817,25 @@ public void DateArchive_AllLoggersTransferToCurrentLogFile(bool concurrentWrites logger1.Debug("123456789"); logger2.Debug("123456789"); - LogManager.Flush(); timeSource.AddToLocalTime(TimeSpan.FromDays(1)); + Thread.Sleep(50); // This should archive the log before logging. logger1.Debug("123456789"); timeSource.AddToSystemTime(TimeSpan.FromDays(1)); // Archive only once - Thread.Sleep(10); logger2.Debug("123456789"); LogManager.Configuration = null; // Flush var files = Directory.GetFiles(archiveFolder); Assert.Single(files); - if (!enableArchiveCompression) - { - string prevLogFile = Directory.GetFiles(archiveFolder)[0]; - AssertFileContents(prevLogFile, StringRepeat(2, "123456789\n"), Encoding.UTF8); - } + + string prevLogFile = Directory.GetFiles(archiveFolder)[0]; + AssertFileContents(prevLogFile, StringRepeat(2, "123456789\n"), Encoding.UTF8); + string currentLogFile = Directory.GetFiles(tempDir)[0]; AssertFileContents(currentLogFile, StringRepeat(2, "123456789\n"), Encoding.UTF8); } @@ -1988,22 +1843,15 @@ public void DateArchive_AllLoggersTransferToCurrentLogFile(bool concurrentWrites { TimeSource.Current = defaultTimeSource; // restore default time source -#if NET35 || NET40 - if (enableArchiveCompression) - { - FileTarget.FileCompressor = fileCompressor; - } -#endif - if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); } } [Fact] - public void DeleteArchiveFilesByDate_MaxArchiveFiles_0() + public void DeleteArchiveFilesByDate_MaxArchiveFiles_NoLimit() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -2014,15 +1862,15 @@ public void DeleteArchiveFilesByDate_MaxArchiveFiles_0() ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), ArchiveAboveSize = 50, LineEnding = LineEndingMode.LF, - ArchiveNumbering = ArchiveNumberingMode.Date, +#pragma warning disable CS0618 // Type or member is obsolete ArchiveDateFormat = "yyyyMMddHHmmssfff", //make sure the milliseconds are set in the filename +#pragma warning restore CS0618 // Type or member is obsolete Layout = "${message}", - MaxArchiveFiles = 0 }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); //writing 19 times 10 bytes (9 char + linefeed) will result in 3 archive files and 1 current file - for (var i = 0; i < 19; ++i) + for (var i = 0; i < 20; ++i) { logger.Debug("123456789"); //build in a small sleep to make sure the current time is reflected in the filename @@ -2066,7 +1914,7 @@ public void DeleteArchiveFilesByDate_MaxArchiveFiles_0() [Fact] public void DeleteArchiveFilesByDate_AlteredMaxArchive() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -2077,15 +1925,16 @@ public void DeleteArchiveFilesByDate_AlteredMaxArchive() ArchiveFileName = Path.Combine(archiveFolder, "{#}.txt"), ArchiveAboveSize = 50, LineEnding = LineEndingMode.LF, - ArchiveNumbering = ArchiveNumberingMode.Date, +#pragma warning disable CS0618 // Type or member is obsolete ArchiveDateFormat = "yyyyMMddHHmmssfff", //make sure the milliseconds are set in the filename +#pragma warning restore CS0618 // Type or member is obsolete Layout = "${message}", MaxArchiveFiles = 5 }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); //writing 29 times 10 bytes (9 char + linefeed) will result in 3 archive files and 1 current file - for (var i = 0; i < 29; ++i) + for (var i = 0; i < 30; ++i) { logger.Debug("123456789"); //build in a small sleep to make sure the current time is reflected in the filename @@ -2133,7 +1982,7 @@ public void DeleteArchiveFilesByDate_AlteredMaxArchive() [Fact] public void RepeatingHeaderTest() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -2143,10 +1992,9 @@ public void RepeatingHeaderTest() var fileTarget = new FileTarget { FileName = logFile, - ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveFileName = Path.Combine(archiveFolder, "{##}.txt"), ArchiveAboveSize = 51, LineEnding = LineEndingMode.LF, - ArchiveNumbering = ArchiveNumberingMode.Sequence, Layout = "${message}", Header = header, MaxArchiveFiles = 2, @@ -2164,11 +2012,11 @@ public void RepeatingHeaderTest() AssertFileContentsStartsWith(logFile, header, Encoding.UTF8); - AssertFileContentsStartsWith(Path.Combine(archiveFolder, "0002.txt"), header, Encoding.UTF8); + AssertFileContentsStartsWith(Path.Combine(archiveFolder, "02.txt"), header, Encoding.UTF8); - AssertFileContentsStartsWith(Path.Combine(archiveFolder, "0001.txt"), header, Encoding.UTF8); + AssertFileContentsStartsWith(Path.Combine(archiveFolder, "01.txt"), header, Encoding.UTF8); - Assert.False(File.Exists(Path.Combine(archiveFolder, "0000.txt"))); // MaxArchiveFiles = 2 (Removes the first file) + Assert.False(File.Exists(Path.Combine(archiveFolder, "00.txt"))); // MaxArchiveFiles = 2 (Removes the first file) } finally { @@ -2184,7 +2032,7 @@ public void RepeatingHeaderTest() [InlineData(true)] public void RepeatingFooterTest(bool writeFooterOnArchivingOnly) { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -2194,10 +2042,9 @@ public void RepeatingFooterTest(bool writeFooterOnArchivingOnly) var fileTarget = new FileTarget { FileName = logFile, - ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveFileName = Path.Combine(archiveFolder, "{##}.txt"), ArchiveAboveSize = 51, LineEnding = LineEndingMode.LF, - ArchiveNumbering = ArchiveNumberingMode.Sequence, Layout = "${message}", Footer = footer, MaxArchiveFiles = 2, @@ -2219,9 +2066,9 @@ public void RepeatingFooterTest(bool writeFooterOnArchivingOnly) Assert.False(File.ReadAllText(logFile).EndsWith(expectedEnding), "Footer was unexpectedly written to log file."); else AssertFileContentsEndsWith(logFile, expectedEnding, Encoding.UTF8); - AssertFileContentsEndsWith(Path.Combine(archiveFolder, "0002.txt"), expectedEnding, Encoding.UTF8); - AssertFileContentsEndsWith(Path.Combine(archiveFolder, "0001.txt"), expectedEnding, Encoding.UTF8); - Assert.False(File.Exists(Path.Combine(archiveFolder, "0000.txt"))); // MaxArchiveFiles = 2 (Removes the first file) + AssertFileContentsEndsWith(Path.Combine(archiveFolder, "02.txt"), expectedEnding, Encoding.UTF8); + AssertFileContentsEndsWith(Path.Combine(archiveFolder, "01.txt"), expectedEnding, Encoding.UTF8); + Assert.False(File.Exists(Path.Combine(archiveFolder, "00.txt"))); // MaxArchiveFiles = 2 (Removes the first file) } finally { @@ -2234,9 +2081,15 @@ public void RepeatingFooterTest(bool writeFooterOnArchivingOnly) } [Theory] - [InlineData(false)] - [InlineData(true)] - public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty) + [InlineData(false, false, true)] + [InlineData(true, false, true)] + [InlineData(false, false, false)] + [InlineData(true, false, false)] + [InlineData(false, true, true)] + [InlineData(true, true, true)] + [InlineData(false, true, false)] + [InlineData(true, true, false)] + public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty, bool writeBom, bool keepFileOpen) { var logFile = Path.GetTempFileName() + ".txt"; try @@ -2250,7 +2103,8 @@ public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty) LineEnding = LineEndingMode.LF, Layout = "${message}", Header = header, - WriteBom = true, + KeepFileOpen = keepFileOpen, + WriteBom = writeBom, WriteHeaderWhenInitialFileNotEmpty = true }; @@ -2264,7 +2118,7 @@ public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty) string headerPart = header + LineEndingMode.LF.NewLineCharacters; string logPart = "aaa\nbbb\nccc\n"; - AssertFileContents(logFile, headerPart + logPart, Encoding.UTF8, addBom: true); + AssertFileContents(logFile, headerPart + logPart, Encoding.UTF8, addBom: writeBom); // Configure second time fileTarget = new FileTarget @@ -2273,7 +2127,8 @@ public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty) LineEnding = LineEndingMode.LF, Layout = "${message}", Header = header, - WriteBom = true, + KeepFileOpen = keepFileOpen, + WriteBom = writeBom, WriteHeaderWhenInitialFileNotEmpty = writeHeaderWhenInitialFileNotEmpty }; @@ -2286,9 +2141,9 @@ public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty) LogManager.Configuration = null; // Flush if (writeHeaderWhenInitialFileNotEmpty) - AssertFileContents(logFile, headerPart + logPart + headerPart + logPart, Encoding.UTF8, addBom: true); + AssertFileContents(logFile, headerPart + logPart + headerPart + logPart, Encoding.UTF8, addBom: writeBom); else - AssertFileContents(logFile, headerPart + logPart + logPart, Encoding.UTF8, addBom: true); + AssertFileContents(logFile, headerPart + logPart + logPart, Encoding.UTF8, addBom: writeBom); } finally { @@ -2301,52 +2156,28 @@ public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty) [Theory] [InlineData(false)] [InlineData(true)] - public void RollingArchiveTest(bool specifyArchiveFileName) - { - RollingArchiveTests(enableCompression: false, specifyArchiveFileName: specifyArchiveFileName); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void RollingArchiveCompressionTest(bool specifyArchiveFileName) + public void RollingArchiveTest_MaxArchiveFiles(bool specifyArchiveFileName) { - RollingArchiveTests(enableCompression: true, specifyArchiveFileName: specifyArchiveFileName); - } - - private void RollingArchiveTests(bool enableCompression, bool specifyArchiveFileName) - { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); - var archiveExtension = enableCompression ? "zip" : "txt"; - -#if NET35 || NET40 - IFileCompressor fileCompressor = null; -#endif + var archiveExtension = "txt"; try { var fileTarget = new FileTarget { - EnableArchiveFileCompression = enableCompression, FileName = logFile, ArchiveAboveSize = 100, LineEnding = LineEndingMode.LF, - ArchiveNumbering = ArchiveNumberingMode.Rolling, Layout = "${message}", MaxArchiveFiles = 3 }; -#if NET35 || NET40 - if (enableCompression) + if (specifyArchiveFileName) { - fileCompressor = FileTarget.FileCompressor; - FileTarget.FileCompressor = new CustomFileCompressor(); + fileTarget.ArchiveFileName = Path.Combine(tempDir, "archive", "." + archiveExtension); + fileTarget.ArchiveSuffixFormat = "{0:00}"; } -#endif - - if (specifyArchiveFileName) - fileTarget.ArchiveFileName = Path.Combine(tempDir, "archive", "{####}." + archiveExtension); LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -2360,46 +2191,47 @@ private void RollingArchiveTests(bool enableCompression, bool specifyArchiveFile LogManager.Configuration = null; // Flush - var assertFileContents = - enableCompression ? new Action(AssertZipFileContents) : AssertFileContents; + Action assertFileContents = (f1, content, encoding) => AssertFileContents(f1, content, encoding, false); + + string archiveFileNameFormat = specifyArchiveFileName + ? Path.Combine("archive", "0{0}." + archiveExtension) + : "file_0{0}." + archiveExtension; + + var currentFile = specifyArchiveFileName ? logFile : Path.Combine(tempDir, string.Format(archiveFileNameFormat, 4)); var times = 25; - AssertFileContents(logFile, + AssertFileContents(currentFile, StringRepeat(times, "eee\n"), Encoding.UTF8); - string archiveFileNameFormat = specifyArchiveFileName - ? Path.Combine("archive", "000{0}." + archiveExtension) - : "file.{0}." + archiveExtension; + if (!specifyArchiveFileName) + { + Assert.False(File.Exists(logFile)); + Assert.False(File.Exists(Path.Combine(tempDir, string.Format(archiveFileNameFormat, 1)))); + } + else + { + Assert.False(File.Exists(Path.Combine(tempDir, string.Format(archiveFileNameFormat, 0)))); + Assert.False(File.Exists(Path.Combine(tempDir, string.Format(archiveFileNameFormat, 4)))); - assertFileContents( - Path.Combine(tempDir, string.Format(archiveFileNameFormat, 0)), - "file.txt", - StringRepeat(times, "ddd\n"), - Encoding.UTF8); + assertFileContents( + Path.Combine(tempDir, string.Format(archiveFileNameFormat, 1)), + StringRepeat(times, "bbb\n"), + Encoding.UTF8); + } assertFileContents( - Path.Combine(tempDir, string.Format(archiveFileNameFormat, 1)), - "file.txt", + Path.Combine(tempDir, string.Format(archiveFileNameFormat, 2)), StringRepeat(times, "ccc\n"), Encoding.UTF8); assertFileContents( - Path.Combine(tempDir, string.Format(archiveFileNameFormat, 2)), - "file.txt", - StringRepeat(times, "bbb\n"), + Path.Combine(tempDir, string.Format(archiveFileNameFormat, 3)), + StringRepeat(times, "ddd\n"), Encoding.UTF8); - - Assert.False(File.Exists(Path.Combine(tempDir, string.Format(archiveFileNameFormat, 3)))); } finally { -#if NET35 || NET40 - if (enableCompression) - { - FileTarget.FileCompressor = fileCompressor; - } -#endif if (File.Exists(logFile)) File.Delete(logFile); if (Directory.Exists(tempDir)) @@ -2410,21 +2242,19 @@ private void RollingArchiveTests(bool enableCompression, bool specifyArchiveFile [InlineData("/")] [InlineData("\\")] [Theory] - public void RollingArchiveTest_MaxArchiveFiles_0(string slash) + public void RollingArchiveTest_MaxArchiveFiles_NoLimit(string slash) { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { var fileTarget = new FileTarget { FileName = logFile, - ArchiveFileName = Path.Combine(tempDir, "archive" + slash + "{####}.txt"), + ArchiveFileName = Path.Combine(tempDir, "archive" + slash + "log_{##}.txt"), ArchiveAboveSize = 100, LineEnding = LineEndingMode.LF, - ArchiveNumbering = ArchiveNumberingMode.Rolling, Layout = "${message}", - MaxArchiveFiles = 0 }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -2445,28 +2275,27 @@ public void RollingArchiveTest_MaxArchiveFiles_0(string slash) Encoding.UTF8); AssertFileContents( - Path.Combine(tempDir, "archive" + slash + "0000.txt"), - StringRepeat(times, "ddd\n"), + Path.Combine(tempDir, "archive" + slash + "log_00.txt"), + StringRepeat(times, "aaa\n"), Encoding.UTF8); AssertFileContents( - Path.Combine(tempDir, "archive" + slash + "0001.txt"), - StringRepeat(times, "ccc\n"), + Path.Combine(tempDir, "archive" + slash + "log_01.txt"), + StringRepeat(times, "bbb\n"), Encoding.UTF8); AssertFileContents( - Path.Combine(tempDir, "archive" + slash + "0002.txt"), - StringRepeat(times, "bbb\n"), + Path.Combine(tempDir, "archive" + slash + "log_02.txt"), + StringRepeat(times, "ccc\n"), Encoding.UTF8); AssertFileContents( - Path.Combine(tempDir, "archive" + slash + "0003.txt"), - StringRepeat(times, "aaa\n"), + Path.Combine(tempDir, "archive" + slash + "log_03.txt"), + StringRepeat(times, "ddd\n"), Encoding.UTF8); } finally { - if (File.Exists(logFile)) { File.Delete(logFile); @@ -2482,7 +2311,7 @@ public void RollingArchiveTest_MaxArchiveFiles_0(string slash) [Fact] public void MultiFileWrite() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); try { var fileTarget = new FileTarget @@ -2534,7 +2363,7 @@ public void MultiFileWrite() [Fact] public void BufferedMultiFileWrite() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); try { var fileTarget = new FileTarget @@ -2586,7 +2415,7 @@ public void BufferedMultiFileWrite() [Fact] public void AsyncMultiFileWrite() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); try { var fileTarget = new FileTarget @@ -2599,7 +2428,7 @@ public void AsyncMultiFileWrite() // this also checks that thread-volatile layouts // such as ${threadid} are properly cached and not recalculated // in logging threads. - var threadID = CurrentManagedThreadId.ToString(); + var threadID = Thread.CurrentThread.ManagedThreadId.ToString(); LogManager.Setup().LoadConfiguration(c => c.ForLogger(LogLevel.Debug).WriteTo(fileTarget).WithAsync()); @@ -2661,22 +2490,16 @@ public void DisposingFileTarget_WhenNotIntialized_ShouldNotThrow() [Fact] public void FileTarget_ArchiveNumbering_DateAndSequence() { - FileTarget_ArchiveNumbering_DateAndSequenceTests(enableCompression: false, fileTxt: "file.txt", archiveFileName: Path.Combine("archive", "{#}.txt")); + FileTarget_ArchiveNumbering_DateAndSequenceTests(fileTxt: "file.txt", archiveFileName: Path.Combine("archive", "{#}.txt")); } [Fact] public void FileTarget_ArchiveNumbering_DateAndSequence_archive_same_as_log_name() { - FileTarget_ArchiveNumbering_DateAndSequenceTests(enableCompression: false, fileTxt: "file-${date:format=yyyy-MM-dd}.txt", archiveFileName: "file-{#}.txt"); - } - - [Fact] - public void FileTarget_ArchiveNumbering_DateAndSequence_WithCompression() - { - FileTarget_ArchiveNumbering_DateAndSequenceTests(enableCompression: true, fileTxt: "file.txt", archiveFileName: Path.Combine("archive", "{#}.zip")); + FileTarget_ArchiveNumbering_DateAndSequenceTests(fileTxt: "file${date:format=yyyy-MM-dd}.txt", archiveFileName: "file_{#}.txt"); } - private void FileTarget_ArchiveNumbering_DateAndSequenceTests(bool enableCompression, string fileTxt, string archiveFileName) + private void FileTarget_ArchiveNumbering_DateAndSequenceTests(string fileTxt, string archiveFileName) { const string archiveDateFormat = "yyyy-MM-dd"; const int archiveAboveSize = 100; @@ -2685,35 +2508,22 @@ private void FileTarget_ArchiveNumbering_DateAndSequenceTests(bool enableCompres Layout logFile = Path.Combine(tempDir, fileTxt); var logFileName = logFile.Render(LogEventInfo.CreateNullEvent()); -#if NET35 || NET40 - IFileCompressor fileCompressor = null; -#endif - try { var fileTarget = new FileTarget { - EnableArchiveFileCompression = enableCompression, FileName = logFile, ArchiveFileName = Path.Combine(tempDir, archiveFileName), +#pragma warning disable CS0618 // Type or member is obsolete ArchiveDateFormat = archiveDateFormat, +#pragma warning restore CS0618 // Type or member is obsolete ArchiveAboveSize = archiveAboveSize, LineEnding = LineEndingMode.LF, Layout = "${message}", MaxArchiveFiles = 3, - ArchiveNumbering = ArchiveNumberingMode.DateAndSequence, ArchiveEvery = FileArchivePeriod.Day }; - -#if NET35 || NET40 - if (enableCompression) - { - fileCompressor = FileTarget.FileCompressor; - FileTarget.FileCompressor = new CustomFileCompressor(); - } -#endif - LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); // we emit 5 * 25 *(3 x aaa + \n) bytes @@ -2728,7 +2538,7 @@ private void FileTarget_ArchiveNumbering_DateAndSequenceTests(bool enableCompres LogManager.Configuration = null; - var assertFileContents = enableCompression ? new Action(AssertZipFileContents) : AssertFileContents; + Action assertFileContents = (f1, content, encoding) => AssertFileContents(f1, content, encoding, false); var extension = Path.GetExtension(renderedArchiveFileName); var fileNameWithoutExt = renderedArchiveFileName.Substring(0, renderedArchiveFileName.Length - extension.Length); @@ -2739,31 +2549,15 @@ private void FileTarget_ArchiveNumbering_DateAndSequenceTests(bool enableCompres StringRepeat(times, "eee\n"), Encoding.UTF8); -#if !NET35 - string expectedEntry1Name = Path.GetFileNameWithoutExtension(helper.GetFullPath(1)) + ".txt"; - string expectedEntry2Name = Path.GetFileNameWithoutExtension(helper.GetFullPath(2)) + ".txt"; - string expectedEntry3Name = Path.GetFileNameWithoutExtension(helper.GetFullPath(3)) + ".txt"; -#else - string expectedEntry1Name = fileTxt; - string expectedEntry2Name = fileTxt; - string expectedEntry3Name = fileTxt; -#endif - assertFileContents(helper.GetFullPath(1), expectedEntry1Name, StringRepeat(times, "bbb\n"), Encoding.UTF8); - assertFileContents(helper.GetFullPath(2), expectedEntry2Name, StringRepeat(times, "ccc\n"), Encoding.UTF8); - assertFileContents(helper.GetFullPath(3), expectedEntry3Name, StringRepeat(times, "ddd\n"), Encoding.UTF8); + assertFileContents(helper.GetFullPath(1), StringRepeat(times, "bbb\n"), Encoding.UTF8); + assertFileContents(helper.GetFullPath(2), StringRepeat(times, "ccc\n"), Encoding.UTF8); + assertFileContents(helper.GetFullPath(3), StringRepeat(times, "ddd\n"), Encoding.UTF8); Assert.False(helper.Exists(0), "First archive should have been deleted due to max archive count."); Assert.False(helper.Exists(4), "Fifth archive must not have been created yet."); } finally { -#if NET35 || NET40 - if (enableCompression) - { - FileTarget.FileCompressor = fileCompressor; - } -#endif - if (File.Exists(logFileName)) File.Delete(logFileName); if (Directory.Exists(tempDir)) @@ -2772,13 +2566,13 @@ private void FileTarget_ArchiveNumbering_DateAndSequenceTests(bool enableCompres } [Theory] - [InlineData("archive/test.log.{####}", "archive/test.log.0000", ArchiveNumberingMode.Sequence)] - [InlineData("archive\\test.log.{####}", "archive\\test.log.0000", ArchiveNumberingMode.Sequence)] - [InlineData("file-${date:format=yyyyMMdd}.txt", "file-${date:format=yyyyMMdd}.txt", ArchiveNumberingMode.Sequence)] - [InlineData("file-{#}.txt", "file-${date:format=yyyyMMdd}.txt", ArchiveNumberingMode.Date)] - public void FileTargetArchiveFileNameTest(string archiveFileName, string expectedArchiveFileName, ArchiveNumberingMode archiveNumbering) + [InlineData("archive/test.log_{##}.txt", "archive/test.log_00.txt", null)] + [InlineData("archive\\test.log_{##}.txt", "archive\\test.log_00.txt", null)] + [InlineData("file_${date:format=yyyyMMdd}.txt", "file_${date:format=yyyyMMdd}_0.txt", null)] + [InlineData("file_{#}.txt", "file_${date:format=yyyyMMdd}_00.txt", "yyyyMMdd")] + public void FileTargetArchiveFileNameTest(string archiveFileName, string expectedArchiveFileName, string archiveDateFormat) { - var subPath = Guid.NewGuid().ToString(); + var subPath = "nlog_" + Guid.NewGuid().ToString(); var tempDir = Path.Combine(Path.GetTempPath(), subPath); var logFile = Path.Combine(tempDir, "file-${date:format=yyyyMMdd}.txt"); try @@ -2787,10 +2581,13 @@ public void FileTargetArchiveFileNameTest(string archiveFileName, string expecte { FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "..", subPath, archiveFileName), - ArchiveNumbering = archiveNumbering, ArchiveAboveSize = 1000, MaxArchiveFiles = 1000, }; +#pragma warning disable CS0618 // Type or member is obsolete + if (!string.IsNullOrEmpty(archiveDateFormat)) + fileTarget.ArchiveDateFormat = archiveDateFormat; +#pragma warning restore CS0618 // Type or member is obsolete LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -2853,7 +2650,7 @@ public void FileTarget_InvalidFileNameCorrection() [Fact] public void FileTarget_LogAndArchiveFilesWithSameName_ShouldArchive() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "Application.log"); var tempDirectory = new DirectoryInfo(tempDir); try @@ -2867,7 +2664,6 @@ public void FileTarget_LogAndArchiveFilesWithSameName_ShouldArchive() FileName = logFile, ArchiveFileName = archiveFile, ArchiveAboveSize = 1, //Force immediate archival - ArchiveNumbering = ArchiveNumberingMode.DateAndSequence, MaxArchiveFiles = 5 }; @@ -2895,10 +2691,56 @@ public void FileTarget_LogAndArchiveFilesWithSameName_ShouldArchive() } } + [Fact] + public void FileTarget_ArchiveAboveSize_RollWhenFull() + { + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "Application"); + var tempDirectory = new DirectoryInfo(tempDir); + var maxArchiveFiles = 2; + + try + { + var fileTarget = new FileTarget + { + FileName = logFile + ".log", + Layout = "${message}", + LineEnding = LineEndingMode.LF, + ArchiveAboveSize = 7, + MaxArchiveFiles = maxArchiveFiles, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaaa"); + logger.Debug("bbbb"); // Not roll (new style so all agree when rolling) + logger.Debug("cccc"); // Roll + logger.Debug("dddd"); // Not roll (new style so all agree when rolling) + logger.Debug("eeee"); // Roll + logger.Debug("ffff"); // Not roll (new style so all agree when rolling) + logger.Debug("gggg"); // Roll + + LogManager.Configuration = null; // Flush + + Assert.False(File.Exists(logFile + ".log")); + Assert.False(File.Exists(logFile + "_01.log")); + AssertFileContents(logFile + "_02.log", "eeee\nffff\n", Encoding.UTF8); + AssertFileContents(logFile + "_03.log", "gggg\n", Encoding.UTF8); + Assert.Equal(maxArchiveFiles, tempDirectory.GetFiles().Length); + } + finally + { + if (tempDirectory.Exists) + { + tempDirectory.Delete(true); + } + } + } + [Fact] public void FileTarget_Handle_Other_Files_That_Match_Archive_Format() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "Application.log"); var tempDirectory = new DirectoryInfo(tempDir); @@ -2914,8 +2756,9 @@ public void FileTarget_Handle_Other_Files_That_Match_Archive_Format() Encoding = Encoding.UTF8, ArchiveFileName = archiveFileLayout, ArchiveEvery = FileArchivePeriod.Day, - ArchiveNumbering = ArchiveNumberingMode.Date, +#pragma warning disable CS0618 // Type or member is obsolete ArchiveDateFormat = "___________yyyyMMddHHmm", +#pragma warning restore CS0618 // Type or member is obsolete MaxArchiveFiles = 10 // Get past the optimization to avoid deleting old files. }; @@ -2944,7 +2787,7 @@ public void FileTarget_Handle_Other_Files_That_Match_Archive_Format() [Fact] public void SingleArchiveFileRollsCorrectly() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -2952,6 +2795,7 @@ public void SingleArchiveFileRollsCorrectly() { FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "archive", "file.txt2"), + ArchiveSuffixFormat = "", ArchiveAboveSize = 100, LineEnding = LineEndingMode.LF, Layout = "${message}", @@ -3009,7 +2853,7 @@ public void SingleArchiveFileRollsCorrectly() [Fact] public void ArchiveFileRollsCorrectly() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -3017,6 +2861,7 @@ public void ArchiveFileRollsCorrectly() { FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "archive", "file.txt2"), + ArchiveSuffixFormat = ".{0}", ArchiveAboveSize = 100, LineEnding = LineEndingMode.LF, Layout = "${message}", @@ -3051,7 +2896,7 @@ public void ArchiveFileRollsCorrectly() StringRepeat(times, "bbb\n"), Encoding.UTF8); AssertFileContents( - Path.Combine(tempDir, "archive", "file.txt2"), + Path.Combine(tempDir, "archive", "file.0.txt2"), StringRepeat(times, "aaa\n"), Encoding.UTF8); @@ -3087,7 +2932,7 @@ public void ArchiveFileRollsCorrectly() [Fact] public void ArchiveFileRollsCorrectly_ExistingArchives() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -3099,6 +2944,7 @@ public void ArchiveFileRollsCorrectly_ExistingArchives() { FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "archive", "file.txt2"), + ArchiveSuffixFormat = ".{0}", ArchiveAboveSize = 100, LineEnding = LineEndingMode.LF, Layout = "${message}", @@ -3151,23 +2997,26 @@ public void FileTarget_ArchiveNumbering_remove_correct_order() var tempDir = ArchiveFileNameHelper.GenerateTempPath(); var logFile = Path.Combine(tempDir, "file.txt"); var archiveExtension = "txt"; + var archiveDateFormat = "yyyy-MM-dd"; + try { var fileTarget = new FileTarget { FileName = logFile, ArchiveFileName = Path.Combine(tempDir, "archive", "{#}." + archiveExtension), - ArchiveDateFormat = "yyyy-MM-dd", +#pragma warning disable CS0618 // Type or member is obsolete + ArchiveDateFormat = archiveDateFormat, +#pragma warning restore CS0618 // Type or member is obsolete ArchiveAboveSize = 100, LineEnding = LineEndingMode.LF, Layout = "${message}", MaxArchiveFiles = maxArchiveFiles, - ArchiveNumbering = ArchiveNumberingMode.DateAndSequence, }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); - ArchiveFileNameHelper helper = new ArchiveFileNameHelper(Path.Combine(tempDir, "archive"), DateTime.Now.ToString(fileTarget.ArchiveDateFormat), archiveExtension); + ArchiveFileNameHelper helper = new ArchiveFileNameHelper(Path.Combine(tempDir, "archive"), DateTime.Now.ToString(archiveDateFormat), archiveExtension); Generate100BytesLog('a'); @@ -3199,13 +3048,15 @@ public void FileTarget_ArchiveNumbering_remove_correct_order() /// /// Allow multiple archives within the same directory /// - [Fact] - public void FileTarget_ArchiveNumbering_remove_correct_wildcard() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void FileTarget_ArchiveNumbering_remove_correct_wildcard(bool keepFileOpen) { const int maxArchiveFiles = 5; var tempDir = ArchiveFileNameHelper.GenerateTempPath(); - var logFile = Path.Combine(tempDir, "{0}{1}.txt"); + var logFile = Path.Combine(tempDir, "{0}_{1:00}.txt"); var defaultTimeSource = TimeSource.Current; try @@ -3226,6 +3077,7 @@ public void FileTarget_ArchiveNumbering_remove_correct_wildcard() LineEnding = LineEndingMode.LF, Layout = "${message}", MaxArchiveFiles = maxArchiveFiles, + KeepFileOpen = keepFileOpen, }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -3247,8 +3099,8 @@ public void FileTarget_ArchiveNumbering_remove_correct_wildcard() $"{logFile1} is missing"); Assert.True(File.Exists(logFile2), $"{logFile2} is missing"); - logFile1 = string.Format(logFile, logger1.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + i.ToString()); - logFile2 = string.Format(logFile, logger2.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + i.ToString()); + logFile1 = string.Format(logFile, logger1.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + $"_{(i + 1):00}"); + logFile2 = string.Format(logFile, logger2.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + $"_{(i + 1):00}"); Assert.True(File.Exists(logFile1), $"{logFile1} is missing"); Assert.True(File.Exists(logFile2), @@ -3262,8 +3114,8 @@ public void FileTarget_ArchiveNumbering_remove_correct_wildcard() { Generate100BytesLog((char)('b' + i), logger1); Generate100BytesLog((char)('b' + i), logger2); - var logFile1 = string.Format(logFile, logger1.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + i.ToString()); - var logFile2 = string.Format(logFile, logger2.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + i.ToString()); + var logFile1 = string.Format(logFile, logger1.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + $"_{(i + 1):00}"); + var logFile2 = string.Format(logFile, logger2.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + $"_{(i + 1):00}"); Assert.True(File.Exists(logFile1), $"{logFile1} is missing"); Assert.True(File.Exists(logFile2), @@ -3274,18 +3126,18 @@ public void FileTarget_ArchiveNumbering_remove_correct_wildcard() { Generate100BytesLog((char)('b' + i), logger1); Generate100BytesLog((char)('b' + i), logger2); - var numberToBeRemoved = i - maxArchiveFiles; + var numberToBeRemoved = i - maxArchiveFiles + 1; - var logFile1 = string.Format(logFile, logger1.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + numberToBeRemoved.ToString()); - var logFile2 = string.Format(logFile, logger2.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + numberToBeRemoved.ToString()); + var logFile1 = string.Format(logFile, logger1.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + $"_{numberToBeRemoved:00}"); + var logFile2 = string.Format(logFile, logger2.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + $"_{numberToBeRemoved:00}"); Assert.False(File.Exists(logFile1), $"archive FirstFile {numberToBeRemoved} has not been removed! We are created file {i}"); Assert.False(File.Exists(logFile2), $"archive SecondFile {numberToBeRemoved} has not been removed! We are created file {i}"); - logFile1 = string.Format(logFile, logger1.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + i.ToString()); - logFile2 = string.Format(logFile, logger2.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + "." + i.ToString()); + logFile1 = string.Format(logFile, logger1.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + $"_{(i + 1):00}"); + logFile2 = string.Format(logFile, logger2.Name, TimeSource.Current.Time.Date.ToString("yyyy-MM-dd") + $"_{(i + 1):00}"); Assert.True(File.Exists(logFile1), $"{logFile1} is missing"); @@ -3293,12 +3145,15 @@ public void FileTarget_ArchiveNumbering_remove_correct_wildcard() $"{logFile2} is missing"); } + LogManager.Configuration = null; + Assert.Equal(10, Directory.GetFiles(tempDir).Length); + // Verify that archieve-cleanup after startup handles same folder archive correctly fileTarget.ArchiveAboveSize = 200; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); logger1.Info("Bye"); logger2.Info("Bye"); - Assert.Equal(12, Directory.GetFiles(tempDir).Length); + Assert.Equal(10, Directory.GetFiles(tempDir).Length); LogManager.Configuration = null; } @@ -3306,6 +3161,8 @@ public void FileTarget_ArchiveNumbering_remove_correct_wildcard() { TimeSource.Current = defaultTimeSource; // restore default time source + LogManager.Configuration = null; + if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); } @@ -3314,11 +3171,12 @@ public void FileTarget_ArchiveNumbering_remove_correct_wildcard() /// /// See that dynamic sequence archive supports same-folder archiving. /// - [Fact] - public void FileTarget_SameDirectory_MaxArchiveFiles_One() + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + public void FileTarget_SameDirectory_MaxArchiveFiles(int maxArchiveFiles) { - const int maxArchiveFiles = 1; - var tempDir = ArchiveFileNameHelper.GenerateTempPath(); var logFile1 = Path.Combine(tempDir, "Log{0}.txt"); try @@ -3340,13 +3198,24 @@ public void FileTarget_SameDirectory_MaxArchiveFiles_One() Generate100BytesLog('c'); var times = 25; - AssertFileContents(string.Format(logFile1, ".0"), - StringRepeat(times, "bbb\n"), - Encoding.ASCII); + if (maxArchiveFiles > 1) + { + Assert.False(File.Exists(string.Format(logFile1, ""))); - AssertFileContents(string.Format(logFile1, ""), - StringRepeat(times, "ccc\n"), - Encoding.ASCII); + AssertFileContents(string.Format(logFile1, "_01"), + StringRepeat(times, "bbb\n"), + Encoding.ASCII); + + AssertFileContents(string.Format(logFile1, "_02"), + StringRepeat(times, "ccc\n"), + Encoding.ASCII); + } + else + { + AssertFileContents(string.Format(logFile1, ""), + StringRepeat(times, "ccc\n"), + Encoding.ASCII); + } LogManager.Configuration = null; } @@ -3398,32 +3267,31 @@ public bool Exists(int number) public string GetFullPath(int number) { - return Path.Combine($"{FolderName}/{FileName}.{number}.{Ext}"); + return Path.Combine(FolderName, $"{FileName}_{number:00}.{Ext}"); } public static string GenerateTempPath() { - return Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + return Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); } } [Theory] [InlineData("##", 0, "00")] - [InlineData("###", 1, "001")] + [InlineData("##", 1, "01")] [InlineData("#", 20, "20")] public void FileTarget_WithDateAndSequenceArchiveNumbering_ShouldPadSequenceNumberInArchiveFileName( string placeHolderSharps, int sequenceNumber, string expectedSequenceInArchiveFileName) { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); const string archiveDateFormat = "yyyy-MM-dd"; string archiveFileName = Path.Combine(tempDir, $"{{{placeHolderSharps}}}.log"); string expectedArchiveFullName = - $"{tempDir}/{DateTime.Now.ToString(archiveDateFormat)}.{expectedSequenceInArchiveFileName}.log"; + $"{tempDir}/{DateTime.Now.ToString(archiveDateFormat)}_{expectedSequenceInArchiveFileName}.log"; - GenerateArchives(count: sequenceNumber + 1, archiveDateFormat: archiveDateFormat, - archiveFileName: archiveFileName, archiveNumbering: ArchiveNumberingMode.DateAndSequence); + GenerateArchives(count: sequenceNumber + 2, archiveDateFormat: archiveDateFormat, + archiveFileName: archiveFileName); bool resultArchiveWithExpectedNameExists = File.Exists(expectedArchiveFullName); - Assert.True(resultArchiveWithExpectedNameExists); } @@ -3434,21 +3302,19 @@ public void FileTarget_WithDateAndSequenceArchiveNumbering_ShouldPadSequenceNumb public void FileTarget_WithDateAndSequenceArchiveNumbering_ShouldRespectArchiveDateFormat( string archiveDateFormat) { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - string archiveFileName = Path.Combine(tempDir, "{#}.log"); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + string archiveFileName = Path.Combine(tempDir, "{##}.log"); string expectedDateInArchiveFileName = DateTime.Now.ToString(archiveDateFormat); - string expectedArchiveFullName = $"{tempDir}/{expectedDateInArchiveFileName}.1.log"; + string expectedArchiveFullName = $"{tempDir}/{expectedDateInArchiveFileName}_00.log"; // We generate 2 archives so that the algorithm that seeks old archives is also tested. - GenerateArchives(count: 2, archiveDateFormat: archiveDateFormat, archiveFileName: archiveFileName, - archiveNumbering: ArchiveNumberingMode.DateAndSequence); + GenerateArchives(count: 2, archiveDateFormat: archiveDateFormat, archiveFileName: archiveFileName); bool resultArchiveWithExpectedNameExists = File.Exists(expectedArchiveFullName); Assert.True(resultArchiveWithExpectedNameExists); } - private void GenerateArchives(int count, string archiveDateFormat, string archiveFileName, - ArchiveNumberingMode archiveNumbering) + private void GenerateArchives(int count, string archiveDateFormat, string archiveFileName) { string logFileName = Path.GetTempFileName(); const int logFileMaxSize = 1; @@ -3456,15 +3322,16 @@ private void GenerateArchives(int count, string archiveDateFormat, string archiv { FileName = logFileName, ArchiveFileName = archiveFileName, +#pragma warning disable CS0618 // Type or member is obsolete ArchiveDateFormat = archiveDateFormat, - ArchiveNumbering = archiveNumbering, +#pragma warning restore CS0618 // Type or member is obsolete ArchiveAboveSize = logFileMaxSize }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); for (int currentSequenceNumber = 0; currentSequenceNumber < count; currentSequenceNumber++) logger.Debug("Test {0}", currentSequenceNumber); - LogManager.Flush(); + LogManager.Configuration = null; } [Fact] @@ -3478,7 +3345,7 @@ public void Dont_throw_Exception_when_archiving_is_enabled() throwExceptions='true' > - + @@ -3508,7 +3375,7 @@ public void Dont_throw_Exception_when_archiving_is_enabled_with_async() throwExceptions='true' > - + @@ -3554,9 +3421,8 @@ public void DatedArchiveForFileTargetWithMultipleFiles() @@ -3648,7 +3514,6 @@ public void LoggingShouldNotTriggerTypeResolveEventTest() } [Theory] - [InlineData("yyyyMMdd-HHmm")] [InlineData("yyyyMMdd")] [InlineData("yyyy-MM-dd")] public void MaxArchiveFilesWithDateFormatTest(string archiveDateFormat) @@ -3666,7 +3531,7 @@ public void MaxArchiveFilesWithDateFormatTest(string archiveDateFormat) /// change file creation/last write date private static void TestMaxArchiveFilesWithDate(int maxArchiveFilesConfig, int expectedArchiveFiles, string dateFormat, bool changeCreationAndWriteTime) { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); string archivePath = Path.Combine(tempDir, "archive"); var archiveDir = new DirectoryInfo(archivePath); @@ -3679,7 +3544,7 @@ private static void TestMaxArchiveFilesWithDate(int maxArchiveFilesConfig, int e string fileExt = ".log"; DateTime now = DateTime.Now; int i = 0; - foreach (string filePath in ArchiveFileNamesGenerator(archivePath, dateFormat, fileExt).Take(30)) + foreach (string filePath in ArchiveFileNamesGenerator(archivePath, "{0:" + dateFormat + "}" + fileExt).Take(30)) { File.WriteAllLines(filePath, new[] { "test archive ", "=====", filePath }); var time = now.AddDays(i); @@ -3701,8 +3566,7 @@ private static void TestMaxArchiveFilesWithDate(int maxArchiveFilesConfig, int e archiveEvery='minute' maxArchiveFiles='" + maxArchiveFilesConfig + @"' archiveFileName='" + archivePath + @"/{#}.log' - archiveDateFormat='" + dateFormat + @"' - archiveNumbering='Date'/> + archiveDateFormat='" + dateFormat + @"' /> @@ -3762,7 +3626,7 @@ public void HandleArchiveFilesMultipleContextSingleTargetTest_ascii(bool changeC private static void HandleArchiveFilesMultipleContextMultipleTargetsTest( int maxArchiveFilesConfig, int expectedArchiveFiles, string dateFormat, bool changeCreationAndWriteTime) { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); string archivePath = Path.Combine(tempDir, "archive"); var archiveDir = new DirectoryInfo(archivePath); try @@ -3780,7 +3644,7 @@ private static void HandleArchiveFilesMultipleContextMultipleTargetsTest( var now = DateTime.Now; var i = 0; // create mock app1_trace archives (matches app1 config for trace target) - foreach (string filePath in ArchiveFileNamesGenerator(archivePath, dateFormat, app1TraceNm + fileExt).Take(numberFilesCreatedPerTargetArchive)) + foreach (string filePath in ArchiveFileNamesGenerator(archivePath, app1TraceNm + "_{0:" + dateFormat + "}" + fileExt).Take(numberFilesCreatedPerTargetArchive)) { File.WriteAllLines(filePath, new[] { "test archive ", "=====", filePath }); var time = now.AddDays(i); @@ -3793,7 +3657,7 @@ private static void HandleArchiveFilesMultipleContextMultipleTargetsTest( } i = 0; // create mock app1_debug archives (matches app1 config for debug target) - foreach (string filePath in ArchiveFileNamesGenerator(archivePath, dateFormat, app1DebugNm + fileExt).Take(numberFilesCreatedPerTargetArchive)) + foreach (string filePath in ArchiveFileNamesGenerator(archivePath, app1DebugNm + "_{0:" + dateFormat + "}" + fileExt).Take(numberFilesCreatedPerTargetArchive)) { File.WriteAllLines(filePath, new[] { "test archive ", "=====", filePath }); var time = now.AddDays(i); @@ -3806,7 +3670,7 @@ private static void HandleArchiveFilesMultipleContextMultipleTargetsTest( } i = 0; // create mock app2 archives (matches app2 config for target) - foreach (string filePath in ArchiveFileNamesGenerator(archivePath, dateFormat, app2Nm + fileExt).Take(numberFilesCreatedPerTargetArchive)) + foreach (string filePath in ArchiveFileNamesGenerator(archivePath, app2Nm + "_{0:" + dateFormat + "}" + fileExt).Take(numberFilesCreatedPerTargetArchive)) { File.WriteAllLines(filePath, new[] { "test archive ", "=====", filePath }); var time = now.AddDays(i); @@ -3819,26 +3683,26 @@ private static void HandleArchiveFilesMultipleContextMultipleTargetsTest( } // Create same app1 Debug file as config defines. Will force archiving to happen on startup - File.WriteAllLines(tempDir + "\\" + app1DebugNm + fileExt, new[] { "Write first app debug target. Startup will archive this file" }, Encoding.ASCII); + File.WriteAllLines(Path.Combine(tempDir, app1TraceNm + fileExt), new[] { "Write first app debug target. Startup will archive this file" }, Encoding.ASCII); var app1Config = XmlLoggingConfiguration.CreateFromXmlString(@" + keepFileOpen='false' /> + keepFileOpen='false' /> @@ -3850,12 +3714,12 @@ private static void HandleArchiveFilesMultipleContextMultipleTargetsTest( + keepFileOpen='false' /> ; @@ -3904,7 +3768,7 @@ private static void HandleArchiveFilesMultipleContextMultipleTargetsTest( private static void HandleArchiveFilesMultipleContextSingleTargetsTest( int maxArchiveFilesConfig, int expectedArchiveFiles, string dateFormat, bool changeCreationAndWriteTime) { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); string archivePath = Path.Combine(tempDir, "archive"); var archiveDir = new DirectoryInfo(archivePath); try @@ -3914,13 +3778,13 @@ private static void HandleArchiveFilesMultipleContextSingleTargetsTest( // use same config vars for mock files, as for nlog config var fileExt = ".log"; - var app1Nm = "App1"; - var app2Nm = "App2"; + var app1Nm = "AppA"; + var app2Nm = "AppB"; var now = DateTime.Now; var i = 0; // create mock app1 archives (matches app1 config for target) - foreach (string filePath in ArchiveFileNamesGenerator(archivePath, dateFormat, app1Nm + fileExt).Take(numberFilesCreatedPerTargetArchive)) + foreach (string filePath in ArchiveFileNamesGenerator(archivePath, app1Nm + "_{0:" + dateFormat + "}" + fileExt).Take(numberFilesCreatedPerTargetArchive)) { File.WriteAllLines(filePath, new[] { "test archive ", "=====", filePath }); var time = now.AddDays(i); @@ -3933,7 +3797,7 @@ private static void HandleArchiveFilesMultipleContextSingleTargetsTest( } i = 0; // create mock app2 archives (matches app2 config for target) - foreach (string filePath in ArchiveFileNamesGenerator(archivePath, dateFormat, app2Nm + fileExt).Take(numberFilesCreatedPerTargetArchive)) + foreach (string filePath in ArchiveFileNamesGenerator(archivePath, app2Nm + "_{0:" + dateFormat + "}" + fileExt).Take(numberFilesCreatedPerTargetArchive)) { File.WriteAllLines(filePath, new[] { "test archive ", "=====", filePath }); var time = now.AddDays(i); @@ -3952,12 +3816,12 @@ private static void HandleArchiveFilesMultipleContextSingleTargetsTest( + keepFileOpen='false' /> ; @@ -3968,12 +3832,12 @@ private static void HandleArchiveFilesMultipleContextSingleTargetsTest( + keepFileOpen='false' /> ; @@ -4012,7 +3876,7 @@ private static void HandleArchiveFilesMultipleContextSingleTargetsTest( /// /// fileext with . /// - private static IEnumerable ArchiveFileNamesGenerator(string path, string dateFormat, string fileExt) + private static IEnumerable ArchiveFileNamesGenerator(string path, string fileFormat) { //yyyyMMdd-HHmm int dateOffset = 1; @@ -4020,7 +3884,7 @@ private static IEnumerable ArchiveFileNamesGenerator(string path, string while (true) { dateOffset--; - yield return Path.Combine(path, now.AddDays(dateOffset).ToString(dateFormat) + fileExt); + yield return Path.Combine(path, string.Format(fileFormat, now.AddDays(dateOffset))); } } @@ -4085,9 +3949,9 @@ public void RelativeFileNaming_DirectoryNavigation_ShouldSuccess() } [Fact] - public void RelativeSequentialArchiveTest_MaxArchiveFiles_0() + public void RelativeSequentialArchiveTest_MaxArchiveFiles_NoLimit() { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logfile = Path.Combine(tempDir, "file.txt"); try { @@ -4095,13 +3959,11 @@ public void RelativeSequentialArchiveTest_MaxArchiveFiles_0() var fileTarget = new FileTarget { FileName = logfile, - ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveFileName = Path.Combine(archiveFolder, "{##}.txt"), ArchiveAboveSize = 100, ArchiveOldFileOnStartup = true, // Verify ArchiveOldFileOnStartup works together with ArchiveAboveSize LineEnding = LineEndingMode.LF, - ArchiveNumbering = ArchiveNumberingMode.Sequence, Layout = "${message}", - MaxArchiveFiles = 0 }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -4122,26 +3984,26 @@ public void RelativeSequentialArchiveTest_MaxArchiveFiles_0() Encoding.UTF8); AssertFileContents( - Path.Combine(archiveFolder, "0000.txt"), + Path.Combine(archiveFolder, "00.txt"), StringRepeat(times, "aaa\n"), Encoding.UTF8); AssertFileContents( - Path.Combine(archiveFolder, "0001.txt"), + Path.Combine(archiveFolder, "01.txt"), StringRepeat(times, "bbb\n"), Encoding.UTF8); AssertFileContents( - Path.Combine(archiveFolder, "0002.txt"), + Path.Combine(archiveFolder, "02.txt"), StringRepeat(times, "ccc\n"), Encoding.UTF8); AssertFileContents( - Path.Combine(archiveFolder, "0003.txt"), + Path.Combine(archiveFolder, "03.txt"), StringRepeat(times, "ddd\n"), Encoding.UTF8); - Assert.False(File.Exists(Path.Combine(archiveFolder, "0004.txt"))); + Assert.False(File.Exists(Path.Combine(archiveFolder, "04.txt"))); } finally { @@ -4171,23 +4033,22 @@ public void TestFilenameCleanup() //underscore is used for clean expectedFileName += i + "_"; } + expectedFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, expectedFileName); + //under mono this the invalid chars is sometimes only 1 char (so min width 2) Assert.True(invalidFileName.Length >= 2); //CleanupFileName is default true; var fileTarget = new FileTarget(); fileTarget.FileName = invalidFileName; - var filePathLayout = new NLog.Internal.FilePathLayout(invalidFileName, true, FilePathKind.Absolute); - - - var path = filePathLayout.Render(LogEventInfo.CreateNullEvent()); + var path = FileTarget.CleanFullFilePath(fileTarget.FileName.Render(LogEventInfo.CreateNullEvent())); Assert.Equal(expectedFileName, path); } [Theory] - [InlineData(DayOfWeek.Sunday, "2017-03-02 15:27:34.651", "2017-03-05 15:27:34.651")] // On a Thursday, finding next Sunday - [InlineData(DayOfWeek.Thursday, "2017-03-02 15:27:34.651", "2017-03-09 15:27:34.651")] // On a Thursday, finding next Thursday - [InlineData(DayOfWeek.Monday, "2017-03-02 00:00:00.000", "2017-03-06 00:00:00.000")] // On Thursday at Midnight, finding next Monday + [InlineData(DayOfWeek.Sunday, "2017-03-02 15:27:34.651", "2017-03-05")] // On a Thursday, finding next Sunday + [InlineData(DayOfWeek.Thursday, "2017-03-02 15:27:34.651", "2017-03-09")] // On a Thursday, finding next Thursday + [InlineData(DayOfWeek.Monday, "2017-03-02 00:00:00.000", "2017-03-06")] // On Thursday at Midnight, finding next Monday public void TestCalculateNextWeekday(DayOfWeek day, string todayString, string expectedString) { DateTime today = DateTime.Parse(todayString); @@ -4220,10 +4081,12 @@ public void TestInitialBomValue(string encodingName, bool expected) [Fact] public void BatchErrorHandlingTest() { - using (new NoThrowNLogExceptions()) + try { + LogManager.ThrowExceptions = false; + var fileTarget = new FileTarget { FileName = "${logger}", Layout = "${message}", ArchiveAboveSize = 10, DiscardAll = true }; - fileTarget.Initialize(null); + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(fileTarget)).LogFactory; // make sure that when file names get sorted, the asynchronous continuations are sorted with them as well var exceptions = new List(); @@ -4237,7 +4100,7 @@ public void BatchErrorHandlingTest() }; fileTarget.WriteAsyncLogEvents(events); - LogManager.Flush(); + logFactory.Flush(); Assert.Equal(5, exceptions.Count); Assert.Null(exceptions[0]); @@ -4246,12 +4109,16 @@ public void BatchErrorHandlingTest() Assert.NotNull(exceptions[3]); Assert.NotNull(exceptions[4]); } + finally + { + LogManager.ThrowExceptions = true; + } } [Fact] public void BatchBufferOverflowTest() { - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + string tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logfile = Path.Combine(tempDir, "file.txt"); try { @@ -4264,7 +4131,8 @@ public void BatchBufferOverflowTest() Layout = "${message}", Encoding = Encoding.UTF8, }; - fileTarget.Initialize(null); + + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(fileTarget)).LogFactory; var result = new List(); var events = new List(); @@ -4277,7 +4145,8 @@ public void BatchBufferOverflowTest() // Act fileTarget.WriteAsyncLogEvents(events); - fileTarget.Close(); + logFactory.Flush(); + logFactory.Dispose(); // Assert Assert.Equal(Enumerable.Range(1, times).ToList(), result); @@ -4321,7 +4190,7 @@ public void HandleArchiveFileAlreadyExistsTest_ascii() private static void HandleArchiveFileAlreadyExistsTest(Encoding encoding, bool hasBom) { - var tempDir = Path.Combine(Path.GetTempPath(), "HandleArchiveFileAlreadyExistsTest-" + Guid.NewGuid()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); string logFile = Path.Combine(tempDir, "log.txt"); try { @@ -4336,7 +4205,7 @@ private static void HandleArchiveFileAlreadyExistsTest(Encoding encoding, bool h //write to archive directly var archiveDateFormat = "yyyy-MM-dd"; - var archiveFileNamePattern = Path.Combine(tempDir, "log-{#}.txt"); + var archiveFileNamePattern = Path.Combine(tempDir, "log_{#}.txt"); var archiveFileName = archiveFileNamePattern.Replace("{#}", oldTime.ToString(archiveDateFormat)); File.WriteAllText(archiveFileName, "message already in archive" + Environment.NewLine, encoding); @@ -4348,8 +4217,7 @@ private static void HandleArchiveFileAlreadyExistsTest(Encoding encoding, bool h FileName = logFile, ArchiveEvery = FileArchivePeriod.Day, ArchiveFileName = archiveFileNamePattern, - ArchiveNumbering = ArchiveNumberingMode.Date, - ArchiveDateFormat = archiveDateFormat, + ArchiveSuffixFormat = "_{1:" + archiveDateFormat + "}", Encoding = encoding, Layout = "${message}", WriteBom = hasBom, @@ -4383,7 +4251,7 @@ private static void HandleArchiveFileAlreadyExistsTest(Encoding encoding, bool h [Fact] public void DontCrashWhenDateAndSequenceDoesntMatchFiles() { - var tempDir = Path.Combine(Path.GetTempPath(), "DontCrashWhenDateAndSequenceDoesntMatchFiles-" + Guid.NewGuid()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); string logFile = Path.Combine(tempDir, "log.txt"); try { @@ -4410,7 +4278,6 @@ public void DontCrashWhenDateAndSequenceDoesntMatchFiles() FileName = logFile, ArchiveEvery = FileArchivePeriod.Day, ArchiveFileName = "log-{#}.txt", - ArchiveNumbering = ArchiveNumberingMode.DateAndSequence, ArchiveAboveSize = 50000, MaxArchiveFiles = 7 }; @@ -4436,43 +4303,10 @@ public void DontCrashWhenDateAndSequenceDoesntMatchFiles() } } - [Theory] - [InlineData(true, 100, true)] // archive, as size of file is 101 - [InlineData(true, 101, false)] //equals is not above - [InlineData(true, 102, false)] // don;t archive, we didn't reach the aboveSize - [InlineData(false, 100, false)] - [InlineData(null, 0, false)] - [InlineData(null, 99, true)] - [InlineData(null, 100, true)] - [InlineData(null, 101, false)] - public void ShouldArchiveOldFileOnStartupTest(bool? archiveOldFileOnStartup, long archiveOldFileOnStartupAboveSize, bool expected) - { - // Arrange - var fileAppenderCacheMock = Substitute.For(); - - var filePath = "x:/somewhere/file.txt"; - fileAppenderCacheMock.GetFileLength(filePath).Returns(101); - - var target = new FileTarget(fileAppenderCacheMock) - { - ArchiveOldFileOnStartupAboveSize = archiveOldFileOnStartupAboveSize - }; - if (archiveOldFileOnStartup.HasValue) - { - target.ArchiveOldFileOnStartup = archiveOldFileOnStartup.Value; - } - - // Act - var result = target.ShouldArchiveOldFileOnStartup(filePath); - - // Assert - Assert.Equal(expected, result); - } - [Fact] public void ShouldNotArchiveWhenMeetingOldLogEventTimestamps() { - var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "file.txt"); try { @@ -4480,10 +4314,10 @@ public void ShouldNotArchiveWhenMeetingOldLogEventTimestamps() var fileTarget = new FileTarget { FileName = logFile, - ArchiveFileName = Path.Combine(archiveFolder, "{####}.txt"), + ArchiveFileName = Path.Combine(archiveFolder, ".txt"), + ArchiveSuffixFormat = "{0:0000}", ArchiveEvery = FileArchivePeriod.Day, LineEnding = LineEndingMode.LF, - ArchiveNumbering = ArchiveNumberingMode.Sequence, Layout = "${message}", }; @@ -4517,5 +4351,35 @@ public void ShouldNotArchiveWhenMeetingOldLogEventTimestamps() Directory.Delete(tempDir, true); } } + + private static void AssertFileContentsStartsWith(string fileName, string contents, Encoding encoding) + { + FileInfo fi = new FileInfo(fileName); + if (!fi.Exists) + Assert.Fail("File '" + fileName + "' doesn't exist."); + + byte[] encodedBuf = encoding.GetBytes(contents); + + byte[] buf = File.ReadAllBytes(fileName); + Assert.True(encodedBuf.Length <= buf.Length, + $"File:{fileName} encodedBytes:{encodedBuf.Length} does not match file.content:{buf.Length}, file.length = {fi.Length}"); + + for (int i = 0; i < encodedBuf.Length; ++i) + { + if (encodedBuf[i] != buf[i]) + Assert.True(encodedBuf[i] == buf[i], + $"File:{fileName} content mismatch {(int)encodedBuf[i]} <> {(int)buf[i]} at index {i}"); + } + } + + private static void AssertFileContentsEndsWith(string fileName, string contents, Encoding encoding) + { + if (!File.Exists(fileName)) + Assert.Fail("File '" + fileName + "' doesn't exist."); + + string fileText = File.ReadAllText(fileName, encoding); + Assert.True(fileText.Length >= contents.Length); + Assert.Equal(contents, fileText.Substring(fileText.Length - contents.Length)); + } } } From 484fbf776dc605f877c04739ce416b3be12803b8 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 29 Jan 2025 23:39:46 +0100 Subject: [PATCH 036/224] Fixed Sonar code smells (#5675) --- .../BaseFileArchiveHandler.cs | 4 +- .../LegacyArchiveFileNameHandler.cs | 16 +++++-- src/NLog/Targets/FileTarget.cs | 47 +++++++++++-------- src/NLog/Targets/TraceTarget.cs | 25 ++++++---- 4 files changed, 57 insertions(+), 35 deletions(-) diff --git a/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs b/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs index 14ae7cdb95..affb479729 100644 --- a/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs +++ b/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs @@ -107,7 +107,7 @@ protected bool DeleteOldFilesBeforeArchive(string fileDirectory, string fileWild protected static int? GetMaxArchiveSequenceNo(FileInfo[] fileInfos, int fileWildcardStartIndex, int fileWildcardEndIndex) { - return FileInfoDateTime.GetMaxArchiveSequenceNo(fileInfos, fileWildcardStartIndex, fileWildcardEndIndex); + return FileInfoDateTime.ScanFileNamesForMaxSequenceNo(fileInfos, fileWildcardStartIndex, fileWildcardEndIndex); } struct FileInfoDateTime : IComparer @@ -144,7 +144,7 @@ public override string ToString() return FileInfo.Name; } - public static int? GetMaxArchiveSequenceNo(FileInfo[] fileInfos, int fileWildcardStartIndex, int fileWildcardEndIndex) + public static int? ScanFileNamesForMaxSequenceNo(FileInfo[] fileInfos, int fileWildcardStartIndex, int fileWildcardEndIndex) { int? maxArchiveSequenceNo = null; diff --git a/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs b/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs index 85ee228ef9..08d93de1c1 100644 --- a/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs +++ b/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs @@ -121,10 +121,7 @@ private bool ArchiveOldFileWithRetry(string archiveFileName, string newFilePath, return oldFilesDeleted; oldFilesDeleted = true; - if (lastWriteTimeUtc.HasValue && lastWriteTimeUtc.Value != newFileInfo.LastWriteTimeUtc) - return false; // File archive probably completed by someone else, and new file already created - - if (lastFileLength.HasValue && lastFileLength.Value != newFileInfo.Length) + if (HasFileInfoChanged(newFileInfo, lastWriteTimeUtc, lastFileLength)) return false; // File archive probably completed by someone else, and new file already created lastWriteTimeUtc = lastWriteTimeUtc ?? newFileInfo.LastWriteTimeUtc; @@ -152,6 +149,17 @@ private bool ArchiveOldFileWithRetry(string archiveFileName, string newFilePath, return oldFilesDeleted; } + private static bool HasFileInfoChanged(FileInfo newFileInfo, DateTime? lastWriteTimeUtc, long? lastFileLength) + { + if (lastWriteTimeUtc.HasValue && lastWriteTimeUtc.Value != newFileInfo.LastWriteTimeUtc) + return true; + + if (lastFileLength.HasValue && lastFileLength.Value != newFileInfo.Length) + return true; + + return false; + } + private bool ArchiveOldFile(string archiveFileName, FileInfo newFileInfo, LogEventInfo firstLogEvent, DateTime? previousFileLastModified) { DateTime fileLastWriteTime = Time.TimeSource.Current.FromSystemTime(newFileInfo.LastWriteTimeUtc); diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index 4b6f799f41..7ca23e2a08 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -1002,26 +1002,7 @@ private void OpenFileMonitorTimer(object state) if (OpenFileCacheTimeout > 0) { - DateTime closeTime = Time.TimeSource.Current.Time.AddSeconds(-OpenFileCacheTimeout); - bool oldFilesMustBeClosed = false; - foreach (var openFile in _openFileCache) - { - if (openFile.Value.FileAppender.OpenStreamTime < closeTime) - { - oldFilesMustBeClosed = true; - break; - } - } - if (oldFilesMustBeClosed) - { - foreach (var openFile in _openFileCache.ToList()) - { - if (openFile.Value.FileAppender.OpenStreamTime < closeTime) - { - CloseFile(openFile.Key, openFile.Value); - } - } - } + PruneOpenFileCacheUsingTimeout(); } if (OpenFileFlushTimeout > 0 && !AutoFlush) @@ -1051,6 +1032,32 @@ private void OpenFileMonitorTimer(object state) } } + private void PruneOpenFileCacheUsingTimeout() + { + DateTime closeTime = Time.TimeSource.Current.Time.AddSeconds(-OpenFileCacheTimeout); + bool oldFilesMustBeClosed = false; + + foreach (var openFile in _openFileCache) + { + if (openFile.Value.FileAppender.OpenStreamTime < closeTime) + { + oldFilesMustBeClosed = true; + break; + } + } + + if (oldFilesMustBeClosed) + { + foreach (var openFile in _openFileCache.ToList()) + { + if (openFile.Value.FileAppender.OpenStreamTime < closeTime) + { + CloseFile(openFile.Key, openFile.Value); + } + } + } + } + internal string BuildFullFilePath(string newFileName, int sequenceNumber, DateTime fileLastModified = default) { if (sequenceNumber > 0 || fileLastModified != default) diff --git a/src/NLog/Targets/TraceTarget.cs b/src/NLog/Targets/TraceTarget.cs index 9a9ce2019b..5fc91e9c81 100644 --- a/src/NLog/Targets/TraceTarget.cs +++ b/src/NLog/Targets/TraceTarget.cs @@ -103,7 +103,8 @@ protected override void InitializeTarget() if (Header != null) { - Trace.WriteLine(RenderLogEvent(Footer, LogEventInfo.CreateNullEvent())); + string logMessage = RenderLogEvent(Header, LogEventInfo.CreateNullEvent()); + TraceWrite(LogLevel.Debug, logMessage); } } @@ -112,7 +113,8 @@ protected override void CloseTarget() { if (Footer != null) { - Trace.WriteLine(RenderLogEvent(Footer, LogEventInfo.CreateNullEvent())); + string logMessage = RenderLogEvent(Footer, LogEventInfo.CreateNullEvent()); + TraceWrite(LogLevel.Debug, logMessage); } base.CloseTarget(); @@ -134,23 +136,28 @@ protected override void CloseTarget() protected override void Write(LogEventInfo logEvent) { string logMessage = RenderLogEvent(Layout, logEvent); - if (RawWrite || logEvent.Level <= LogLevel.Debug) + TraceWrite(logEvent.Level, logMessage); + } + + private void TraceWrite(LogLevel logLevel, string logMessage) + { + if (RawWrite || logLevel <= LogLevel.Debug) { - Trace.WriteLine(logMessage); + Trace.WriteLine(logMessage); // NOSONAR } - else if (logEvent.Level == LogLevel.Info) + else if (logLevel == LogLevel.Info) { Trace.TraceInformation(logMessage); } - else if (logEvent.Level == LogLevel.Warn) + else if (logLevel == LogLevel.Warn) { Trace.TraceWarning(logMessage); } - else if (logEvent.Level == LogLevel.Error) + else if (logLevel == LogLevel.Error) { Trace.TraceError(logMessage); } - else if (logEvent.Level >= LogLevel.Fatal) + else if (logLevel >= LogLevel.Fatal) { if (EnableTraceFail) Trace.Fail(logMessage); // Can throw exceptions, show message dialog or perform Environment.FailFast @@ -159,7 +166,7 @@ protected override void Write(LogEventInfo logEvent) } else { - Trace.WriteLine(logMessage); + Trace.WriteLine(logMessage); // NOSONAR } } } From c888322f72bf57c8a12899205b2df97b75ea33fd Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 30 Jan 2025 17:01:30 +0100 Subject: [PATCH 037/224] Fixed Sonar Code smell (#5676) --- .../Targets/FileAppenders/ExclusiveFileLockingAppender.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs index eec6870653..56f51fcf4d 100644 --- a/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs +++ b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs @@ -113,9 +113,7 @@ private void RefreshFileBirthTimeUtc() if (fileInfo.Exists && fileInfo.Length != 0) { var fileBirthTimeUtc = FileInfoHelper.LookupValidFileCreationTimeUtc(fileInfo) ?? DateTime.MinValue; - var fileBirthTime = fileBirthTimeUtc != DateTime.MinValue ? NLog.Time.TimeSource.Current.FromSystemTime(fileBirthTimeUtc) : OpenStreamTime; - if (!_fileBirthTime.HasValue || _fileBirthTime.Value < fileBirthTime) - FileBirthTime = fileBirthTime; + FileBirthTime = fileBirthTimeUtc != DateTime.MinValue ? NLog.Time.TimeSource.Current.FromSystemTime(fileBirthTimeUtc) : OpenStreamTime; FileLastModified = NLog.Time.TimeSource.Current.FromSystemTime(fileInfo.LastWriteTimeUtc); } } From 2d0d8b9f02dc598e4aef3e60a111750441aabc10 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 30 Jan 2025 18:53:02 +0100 Subject: [PATCH 038/224] ExclusiveFileLockingAppender - Ensure FileBirthTime cannot move backward (#5677) --- .../FileAppenders/ExclusiveFileLockingAppender.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs index 56f51fcf4d..5561c1e471 100644 --- a/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs +++ b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs @@ -87,7 +87,7 @@ public ExclusiveFileLockingAppender(FileTarget fileTarget, string filePath) OpenStreamTime = Time.TimeSource.Current.Time; _lastFileDeletedCheck = Environment.TickCount; - RefreshFileBirthTimeUtc(); + RefreshFileBirthTimeUtc(true); _fileStream = _fileTarget.CreateFileStreamWithRetry(this, fileTarget.BufferSize, initialFileOpen: true); _countedFileSize = RefreshCountedFileSize(); @@ -98,7 +98,7 @@ private bool SkipRefreshFileBirthTime() return (_fileTarget.ArchiveFileName is null && _fileTarget.ArchiveEvery == FileArchivePeriod.None); } - private void RefreshFileBirthTimeUtc() + private void RefreshFileBirthTimeUtc(bool forceRefresh) { FileLastModified = NLog.Time.TimeSource.Current.Time; @@ -107,13 +107,14 @@ private void RefreshFileBirthTimeUtc() try { - _fileBirthTime = null; - FileInfo fileInfo = new FileInfo(_filePath); if (fileInfo.Exists && fileInfo.Length != 0) { var fileBirthTimeUtc = FileInfoHelper.LookupValidFileCreationTimeUtc(fileInfo) ?? DateTime.MinValue; - FileBirthTime = fileBirthTimeUtc != DateTime.MinValue ? NLog.Time.TimeSource.Current.FromSystemTime(fileBirthTimeUtc) : OpenStreamTime; + var fileBirthTime = fileBirthTimeUtc != DateTime.MinValue ? NLog.Time.TimeSource.Current.FromSystemTime(fileBirthTimeUtc) : OpenStreamTime; + if (!forceRefresh && fileBirthTime.Date < FileBirthTime.Date) + fileBirthTime = FileBirthTime; + FileBirthTime = fileBirthTime; FileLastModified = NLog.Time.TimeSource.Current.FromSystemTime(fileInfo.LastWriteTimeUtc); } } @@ -162,7 +163,7 @@ private void MonitorFileHasBeenDeleted() SafeCloseFile(_filePath, ref _fileStream); _fileStream = _fileTarget.CreateFileStreamWithRetry(this, _fileTarget.BufferSize, initialFileOpen: false); _countedFileSize = RefreshCountedFileSize(); - RefreshFileBirthTimeUtc(); + RefreshFileBirthTimeUtc(false); } } From f29f8c34f2baa18c83905e9a53967618449eafed Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 30 Jan 2025 19:42:00 +0100 Subject: [PATCH 039/224] System.Linq.Expressions not available with AOT (#5678) --- src/NLog/Internal/ReflectionHelpers.cs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/NLog/Internal/ReflectionHelpers.cs b/src/NLog/Internal/ReflectionHelpers.cs index 420fbf70b1..df1e0a77ca 100644 --- a/src/NLog/Internal/ReflectionHelpers.cs +++ b/src/NLog/Internal/ReflectionHelpers.cs @@ -36,7 +36,9 @@ namespace NLog.Internal using System; using System.Collections.Generic; using System.Linq; +#if NETFRAMEWORK using System.Linq.Expressions; +#endif using System.Reflection; using JetBrains.Annotations; @@ -65,12 +67,22 @@ public static bool IsStaticClass(this Type type) /// Complete list of parameters that matches the method, including optional/default parameters. public delegate object LateBoundMethod(object target, object[] arguments); - /// - /// Optimized delegate for calling a constructor - /// - /// Complete list of parameters that matches the constructor, including optional/default parameters. Could be null for no parameters. - public delegate object LateBoundConstructor([CanBeNull] object[] arguments); - +#if !NETFRAMEWORK + public static LateBoundMethod CreateLateBoundMethod(MethodInfo methodInfo) + { + return (target, args) => + { + try + { + return methodInfo.Invoke(target, args); + } + catch (TargetInvocationException exception) + { + throw exception.InnerException ?? exception; + } + }; + } +#else /// /// Creates an optimized delegate for calling the MethodInfo using Expression-Trees /// @@ -142,7 +154,7 @@ private static UnaryExpression CreateParameterExpression(ParameterInfo parameter var valueCast = Expression.Convert(expression, parameterType); return valueCast; } - +#endif [CanBeNull] public static TAttr GetFirstCustomAttribute(this Type type) where TAttr : Attribute { From 389f8c58e956329cfd78a4032de12feb3b6c91b2 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 30 Jan 2025 20:25:16 +0100 Subject: [PATCH 040/224] StringBuilderExt - Updated EqualTo for StringBuilder to foreach-loop (#5679) --- src/NLog.Targets.Network/NetworkTarget.cs | 2 +- src/NLog/Internal/StringBuilderExt.cs | 5 +++-- src/NLog/LayoutRenderers/LongDateLayoutRenderer.cs | 5 +---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/NLog.Targets.Network/NetworkTarget.cs b/src/NLog.Targets.Network/NetworkTarget.cs index 6387ae656c..38fa51261b 100644 --- a/src/NLog.Targets.Network/NetworkTarget.cs +++ b/src/NLog.Targets.Network/NetworkTarget.cs @@ -42,7 +42,7 @@ namespace NLog.Targets using NLog.Internal.NetworkSenders; /// - /// Sends log messages over the network. + /// NetworkTarget for sending messages over the network using TCP / UDP sockets /// /// /// See NLog Wiki diff --git a/src/NLog/Internal/StringBuilderExt.cs b/src/NLog/Internal/StringBuilderExt.cs index f6ebc405d2..a032bc6b84 100644 --- a/src/NLog/Internal/StringBuilderExt.cs +++ b/src/NLog/Internal/StringBuilderExt.cs @@ -370,9 +370,10 @@ public static bool EqualTo(this StringBuilder builder, string other) if (builder.Length != other.Length) return false; - for (int i = 0; i < other.Length; ++i) + int i = 0; + foreach (var chr in other) { - if (builder[i] != other[i]) + if (builder[i++] != chr) return false; } diff --git a/src/NLog/LayoutRenderers/LongDateLayoutRenderer.cs b/src/NLog/LayoutRenderers/LongDateLayoutRenderer.cs index 9a1dcbba1f..a2f59adc82 100644 --- a/src/NLog/LayoutRenderers/LongDateLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LongDateLayoutRenderer.cs @@ -89,10 +89,7 @@ private DateTime GetValue(LogEventInfo logEvent) DateTime timestamp = logEvent.TimeStamp; if (_universalTime.HasValue) { - if (_universalTime.Value) - timestamp = timestamp.ToUniversalTime(); - else - timestamp = timestamp.ToLocalTime(); + return _universalTime.Value ? timestamp.ToUniversalTime() : timestamp.ToLocalTime(); } return timestamp; } From f4f5e93c91aecf526799f8a49d66cb14ba97dee3 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 31 Jan 2025 20:48:14 +0100 Subject: [PATCH 041/224] FileTarget - Introduced WriteToFile without concurrentWrites (#5688) --- src/NLog/SetupLoadConfigurationExtensions.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/NLog/SetupLoadConfigurationExtensions.cs b/src/NLog/SetupLoadConfigurationExtensions.cs index f02870bab5..4b3cc5dd5b 100644 --- a/src/NLog/SetupLoadConfigurationExtensions.cs +++ b/src/NLog/SetupLoadConfigurationExtensions.cs @@ -573,22 +573,25 @@ public static void WriteToDebugConditional(this ISetupConfigurationTargetBuilder /// Size in bytes where log files will be automatically archived. /// Maximum number of archive files that should be kept. /// Maximum days of archive files that should be kept. - public static ISetupConfigurationTargetBuilder WriteToFile(this ISetupConfigurationTargetBuilder configBuilder, Layout fileName, Layout layout = null, System.Text.Encoding encoding = null, LineEndingMode lineEnding = null, bool keepFileOpen = true, long archiveAboveSize = 0, int maxArchiveFiles = 0, int maxArchiveDays = 0) + public static ISetupConfigurationTargetBuilder WriteToFile(this ISetupConfigurationTargetBuilder configBuilder, Layout fileName, Layout layout = null, System.Text.Encoding encoding = null, LineEndingMode lineEnding = null, bool keepFileOpen = true, long archiveAboveSize = -1, int maxArchiveFiles = -1, int maxArchiveDays = -1) { Guard.ThrowIfNull(fileName); var fileTarget = new FileTarget(); fileTarget.FileName = fileName; + fileTarget.KeepFileOpen = keepFileOpen; if (layout != null) fileTarget.Layout = layout; if (encoding != null) fileTarget.Encoding = encoding; if (lineEnding != null) fileTarget.LineEnding = lineEnding; - fileTarget.KeepFileOpen = keepFileOpen; - fileTarget.ArchiveAboveSize = archiveAboveSize; - fileTarget.MaxArchiveFiles = maxArchiveFiles; - fileTarget.MaxArchiveDays = maxArchiveDays; + if (archiveAboveSize > 0) + fileTarget.ArchiveAboveSize = archiveAboveSize; + if (maxArchiveFiles >= 0) + fileTarget.MaxArchiveFiles = maxArchiveFiles; + if (maxArchiveDays > 0) + fileTarget.MaxArchiveDays = maxArchiveDays; return configBuilder.WriteTo(fileTarget); } From 83b18ed0db93601d337ef867d1ad37bab67af315 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 1 Feb 2025 16:27:20 +0100 Subject: [PATCH 042/224] Fix AppDomain BaseDirectory on NET9 when it returns Long UNC (#5699) --- src/NLog/Common/InternalLogger.cs | 5 +++ src/NLog/Internal/AppEnvironmentWrapper.cs | 18 ++++++++++ .../LayoutRenderers/BaseDirLayoutRenderer.cs | 35 +++++++++++-------- src/NLog/Targets/FileTarget.cs | 3 +- .../LayoutRenderers/BaseDirTests.cs | 19 +++++++--- 5 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/NLog/Common/InternalLogger.cs b/src/NLog/Common/InternalLogger.cs index c8503b1dde..adccdc61c6 100644 --- a/src/NLog/Common/InternalLogger.cs +++ b/src/NLog/Common/InternalLogger.cs @@ -39,6 +39,7 @@ namespace NLog.Common using System.IO; using JetBrains.Annotations; using NLog.Internal; + using NLog.Internal.Fakeables; using NLog.Time; /// @@ -430,6 +431,10 @@ private static string ExpandFilePathVariables(string internalLogFile) internalLogFile = internalLogFile.Replace(localapplicationdatadir, NLog.LayoutRenderers.SpecialFolderLayoutRenderer.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + System.IO.Path.DirectorySeparatorChar.ToString()); if (internalLogFile.IndexOf('%') >= 0) internalLogFile = Environment.ExpandEnvironmentVariables(internalLogFile); + + if (!string.IsNullOrEmpty(internalLogFile) && internalLogFile.IndexOf('.') >= 0) + internalLogFile = AppEnvironmentWrapper.FixFilePathWithLongUNC(internalLogFile); + return internalLogFile; } catch diff --git a/src/NLog/Internal/AppEnvironmentWrapper.cs b/src/NLog/Internal/AppEnvironmentWrapper.cs index 4773596fa3..ece69e151b 100644 --- a/src/NLog/Internal/AppEnvironmentWrapper.cs +++ b/src/NLog/Internal/AppEnvironmentWrapper.cs @@ -42,6 +42,8 @@ namespace NLog.Internal.Fakeables internal sealed class AppEnvironmentWrapper : IAppEnvironment { + const string LongUNCPrefix = @"\\?\UNC\"; + private const string UnknownProcessName = ""; private string _entryAssemblyLocation; @@ -104,9 +106,25 @@ public bool FileExists(string path) /// public XmlReader LoadXmlFile(string path) { + path = FixFilePathWithLongUNC(path); return XmlReader.Create(path); } + /// + /// Long UNC paths does not allow relative-path-logic using '..', and also cannot be loaded into Uri by XmlReader + /// + internal static string FixFilePathWithLongUNC(string filepath) + { + if (!string.IsNullOrEmpty(filepath) && filepath.StartsWith(LongUNCPrefix, StringComparison.Ordinal) && filepath.Length < 260) + { + // Workaround .NET 9 regression returning long-form UNC + // path from AppDomain.CurrentDomain.BaseDirectory + // https://github.com/dotnet/runtime/issues/109846 + filepath = @"\\" + filepath.Substring(LongUNCPrefix.Length); + } + return filepath; + } + private static string LookupAppDomainBaseDirectory() { try diff --git a/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs index e574faaa33..9d2d3a32a3 100644 --- a/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs @@ -52,12 +52,9 @@ namespace NLog.LayoutRenderers [ThreadAgnostic] public class BaseDirLayoutRenderer : LayoutRenderer { - private readonly string _baseDir; + private readonly string _appDomainDirectory; + private string _baseDir; - /// - /// cached - /// - private string _processDir; private readonly IAppEnvironment _appEnvironment; /// @@ -84,7 +81,7 @@ public BaseDirLayoutRenderer() : this(LogFactory.DefaultAppEnvironment) /// internal BaseDirLayoutRenderer(IAppEnvironment appEnvironment) { - _baseDir = appEnvironment.AppDomainBaseDirectory; + _baseDir = _appDomainDirectory = appEnvironment.AppDomainBaseDirectory; _appEnvironment = appEnvironment; } @@ -101,24 +98,32 @@ internal BaseDirLayoutRenderer(IAppEnvironment appEnvironment) public string Dir { get; set; } /// - protected override void Append(StringBuilder builder, LogEventInfo logEvent) + protected override void InitializeLayoutRenderer() { - var dir = _baseDir; + base.InitializeLayoutRenderer(); + + _baseDir = _appDomainDirectory; if (ProcessDir) { - dir = _processDir ?? (_processDir = GetProcessDir()); + var processDir = GetProcessDir(); + if (!string.IsNullOrEmpty(processDir)) + _baseDir = processDir; } else if (FixTempDir) { - dir = _processDir ?? (_processDir = GetFixedTempBaseDir(_baseDir)); + var fixTempDir = GetFixedTempBaseDir(_appDomainDirectory); + if (!string.IsNullOrEmpty(fixTempDir)) + _baseDir = fixTempDir; } + _baseDir = AppEnvironmentWrapper.FixFilePathWithLongUNC(_baseDir); + _baseDir = PathHelpers.CombinePaths(_baseDir, Dir, File); + } - if (dir != null) - { - var path = PathHelpers.CombinePaths(dir, Dir, File); - builder.Append(path); - } + /// + protected override void Append(StringBuilder builder, LogEventInfo logEvent) + { + builder.Append(_baseDir); } private string GetFixedTempBaseDir(string baseDir) diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index 7ca23e2a08..a3e88778be 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -42,6 +42,7 @@ namespace NLog.Targets using NLog.Common; using NLog.Config; using NLog.Internal; + using NLog.Internal.Fakeables; using NLog.Layouts; using NLog.Targets.FileAppenders; using NLog.Targets.FileArchiveHandlers; @@ -1112,7 +1113,7 @@ internal static string CleanFullFilePath(string filename) filename = Path.Combine(dirName, new string(fileNameChars)); } - var filepath = FileInfoHelper.IsRelativeFilePath(filename) ? Path.Combine(LogManager.LogFactory.CurrentAppEnvironment.AppDomainBaseDirectory, filename) : filename; + var filepath = FileInfoHelper.IsRelativeFilePath(filename) ? Path.Combine(AppEnvironmentWrapper.FixFilePathWithLongUNC(LogManager.LogFactory.CurrentAppEnvironment.AppDomainBaseDirectory), filename) : filename; filepath = Path.GetFullPath(filepath); return filepath; } diff --git a/tests/NLog.UnitTests/LayoutRenderers/BaseDirTests.cs b/tests/NLog.UnitTests/LayoutRenderers/BaseDirTests.cs index bc10cd12ab..edd10f76d5 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/BaseDirTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/BaseDirTests.cs @@ -106,14 +106,25 @@ public void BaseDir_FixTempDir_ChoosesProcessDir() appEnvironment.AppDomainBaseDirectory = tempDir; appEnvironment.UserTempFilePath = tempDir; appEnvironment.CurrentProcessFilePath = processPath; - var baseLayoutRenderer = new NLog.LayoutRenderers.BaseDirLayoutRenderer(appEnvironment); // test1 - Assert.Equal(tempDir, baseLayoutRenderer.Render(LogEventInfo.CreateNullEvent())); + var baseLayoutRenderer1 = new NLog.LayoutRenderers.BaseDirLayoutRenderer(appEnvironment); + Assert.Equal(tempDir, baseLayoutRenderer1.Render(LogEventInfo.CreateNullEvent())); // test2 - baseLayoutRenderer.FixTempDir = true; - Assert.Equal(Path.GetDirectoryName(processPath), baseLayoutRenderer.Render(LogEventInfo.CreateNullEvent())); + var baseLayoutRenderer2 = new NLog.LayoutRenderers.BaseDirLayoutRenderer(appEnvironment); + baseLayoutRenderer2.FixTempDir = true; + Assert.Equal(Path.GetDirectoryName(processPath), baseLayoutRenderer2.Render(LogEventInfo.CreateNullEvent())); + } + + [Fact] + public void BaseDir_FixFilePathForNet9_WhenLongUNC() + { + var appEnvironment = new Mocks.AppEnvironmentMock(null, null); + appEnvironment.AppDomainBaseDirectory = @"\\?\UNC\major\tom\groundcontrol\bin\Development\net9.0\NLog.config"; + var baseLayoutRenderer = new NLog.LayoutRenderers.BaseDirLayoutRenderer(appEnvironment); + + Assert.Equal(@"\\major\tom\groundcontrol\bin\Development\net9.0\NLog.config", baseLayoutRenderer.Render(LogEventInfo.CreateNullEvent())); } } } From 96a728fcdb524e408707b39cfa442b394a07fc0b Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 2 Feb 2025 00:24:00 +0100 Subject: [PATCH 043/224] Target reports when disabled because failed to initialize (#5697) --- src/NLog/Targets/Target.cs | 13 +++++++++++++ .../LayoutRenderers/ScopeNestedTests.cs | 8 ++++---- .../NLog.UnitTests/Targets/TargetWithContextTest.cs | 4 ++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/NLog/Targets/Target.cs b/src/NLog/Targets/Target.cs index be46cc4650..9f9cb4b216 100644 --- a/src/NLog/Targets/Target.cs +++ b/src/NLog/Targets/Target.cs @@ -403,6 +403,15 @@ public void WriteAsyncLogEvents(IList logEvents) /// protected virtual void WriteFailedNotInitialized(AsyncLogEventInfo logEvent, Exception initializeException) { + if (!_scannedForLayouts) + { + _scannedForLayouts = true; + InternalLogger.Error(_initializeException, "{0}: Disabled because NLog Target failed to initialize.", this); + } + else + { + InternalLogger.Debug("{0}: Disabled because NLog Target failed to initialize. {1} {2}", this, _initializeException?.GetType(), _initializeException?.Message); + } var initializeFailedException = new NLogRuntimeException($"Target {this} failed to initialize.", initializeException); logEvent.Continuation(initializeFailedException); } @@ -421,6 +430,8 @@ internal void Initialize(LoggingConfiguration configuration) { try { + _scannedForLayouts = false; + PropertyHelper.CheckRequiredParameters(ConfigurationItemFactory.Default, this); InitializeTarget(); @@ -436,6 +447,7 @@ internal void Initialize(LoggingConfiguration configuration) { // Target is now in disabled state, and cannot be used for writing LogEvents _initializeException = exception; + _scannedForLayouts = false; if (ExceptionMustBeRethrown(exception)) throw; } @@ -443,6 +455,7 @@ internal void Initialize(LoggingConfiguration configuration) { // Target is now in disabled state, and cannot be used for writing LogEvents _initializeException = exception; + _scannedForLayouts = false; if (ExceptionMustBeRethrown(exception)) throw; diff --git a/tests/NLog.UnitTests/LayoutRenderers/ScopeNestedTests.cs b/tests/NLog.UnitTests/LayoutRenderers/ScopeNestedTests.cs index 0c2ead34fe..66ecfda3db 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/ScopeNestedTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/ScopeNestedTests.cs @@ -395,7 +395,7 @@ public void ScopeNestedTimingTest() measurements = messageFirstScopeSleep.Split(new[] { '|' }, System.StringSplitOptions.RemoveEmptyEntries); Assert.Equal(7, measurements.Length); Assert.Equal("ala", measurements[0]); - Assert.InRange(double.Parse(measurements[1], System.Globalization.CultureInfo.InvariantCulture), 10, 999); + Assert.InRange(double.Parse(measurements[1], System.Globalization.CultureInfo.InvariantCulture), 9, 999); Assert.InRange(int.Parse(measurements[3]), 10, 999); Assert.InRange(int.Parse(measurements[5]), 100000, 9999999); Assert.Equal("b", measurements[measurements.Length - 1]); @@ -403,7 +403,7 @@ public void ScopeNestedTimingTest() measurements = messageSecondScope.Split(new[] { '|' }, System.StringSplitOptions.RemoveEmptyEntries); Assert.Equal(7, measurements.Length); Assert.Equal("ala ma", measurements[0]); - Assert.InRange(double.Parse(measurements[1], System.Globalization.CultureInfo.InvariantCulture), 10, 999); + Assert.InRange(double.Parse(measurements[1], System.Globalization.CultureInfo.InvariantCulture), 9, 999); Assert.InRange(int.Parse(measurements[3]), 10, 999); Assert.InRange(int.Parse(measurements[5]), 0, 9999999); Assert.Equal("a", measurements[measurements.Length - 1]); @@ -411,7 +411,7 @@ public void ScopeNestedTimingTest() measurements = messageSecondScopeSleep.Split(new[] { '|' }, System.StringSplitOptions.RemoveEmptyEntries); Assert.Equal(7, measurements.Length); Assert.Equal("ala ma", measurements[0]); - Assert.InRange(double.Parse(measurements[1], System.Globalization.CultureInfo.InvariantCulture), 20, 999); + Assert.InRange(double.Parse(measurements[1], System.Globalization.CultureInfo.InvariantCulture), 19, 999); Assert.InRange(int.Parse(measurements[3]), 20, 999); Assert.InRange(int.Parse(measurements[5]), 100000, 9999999); Assert.Equal("b", measurements[measurements.Length - 1]); @@ -419,7 +419,7 @@ public void ScopeNestedTimingTest() measurements = messageFirstScopeExit.Split(new[] { '|' }, System.StringSplitOptions.RemoveEmptyEntries); Assert.Equal(7, measurements.Length); Assert.Equal("ala", measurements[0]); - Assert.InRange(double.Parse(measurements[1], System.Globalization.CultureInfo.InvariantCulture), 20, 999); + Assert.InRange(double.Parse(measurements[1], System.Globalization.CultureInfo.InvariantCulture), 19, 999); Assert.InRange(int.Parse(measurements[3]), 20, 999); Assert.InRange(int.Parse(measurements[5]), 200000, 9999999); Assert.Equal("c", measurements[measurements.Length - 1]); diff --git a/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs b/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs index 0c74f39b11..c17be6bf27 100644 --- a/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs +++ b/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs @@ -127,7 +127,7 @@ public void TargetWithContextAsyncTest() private static bool WaitForLastMessage(CustomTargetWithContext target) { System.Threading.Thread.Sleep(1); - for (int i = 0; i < 1000; ++i) + for (int i = 0; i < 5000; ++i) { if (target.LastMessage != null) return true; @@ -328,7 +328,7 @@ public void TargetWithContextJsonTest() logger.Error("log message"); var target = logFactory.Configuration.AllTargets.OfType().FirstOrDefault(); System.Threading.Thread.Sleep(1); - for (int i = 0; i < 1000; ++i) + for (int i = 0; i < 5000; ++i) { if (target.LastMessage != null) break; From d6c7b82cb84aedc96f23d687ee5151a935348edf Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 2 Feb 2025 00:28:54 +0100 Subject: [PATCH 044/224] AsyncTaskTarget - Reduce default throttle after failure to 50ms instead of 500ms (#5701) --- src/NLog/Targets/AsyncTaskTarget.cs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/NLog/Targets/AsyncTaskTarget.cs b/src/NLog/Targets/AsyncTaskTarget.cs index 56bac1abfb..720b63d3d6 100644 --- a/src/NLog/Targets/AsyncTaskTarget.cs +++ b/src/NLog/Targets/AsyncTaskTarget.cs @@ -111,7 +111,8 @@ public abstract class AsyncTaskTarget : TargetWithContext /// How many milliseconds to wait before next retry (will double with each retry) /// /// - public int RetryDelayMilliseconds { get; set; } = 500; + public int RetryDelayMilliseconds { get => _retryDelayMilliseconds ?? ((RetryCount > 0 || OverflowAction != AsyncTargetWrapperOverflowAction.Discard) ? 500 : 50); set => _retryDelayMilliseconds = value; } + private int? _retryDelayMilliseconds; /// /// Gets or sets whether to use the locking queue, instead of a lock-free concurrent queue @@ -686,6 +687,8 @@ private void TaskCompletion(Task completedTask, object continuation) bool success = true; bool fullBatchCompleted = true; + TimeSpan retryOnFailureDelay = TimeSpan.Zero; + try { if (ReferenceEquals(completedTask, _previousTask)) @@ -719,10 +722,9 @@ private void TaskCompletion(Task completedTask, object continuation) success = false; if (RetryCount <= 0) { - if (RetryFailedAsyncTask(actualException, CancellationToken.None, 0, out var retryDelay)) + if (RetryFailedAsyncTask(actualException, CancellationToken.None, 0, out retryOnFailureDelay)) { - InternalLogger.Warn(actualException, "{0}: WriteAsyncTask failed on completion. Sleep {1} ms", this, retryDelay.TotalMilliseconds); - AsyncHelpers.WaitForDelay(retryDelay); + InternalLogger.Warn(actualException, "{0}: WriteAsyncTask failed on completion. Delay {1} ms", this, retryOnFailureDelay.TotalMilliseconds); } } else @@ -748,7 +750,10 @@ private void TaskCompletion(Task completedTask, object continuation) } finally { - TaskStartNext(completedTask, fullBatchCompleted); + if (retryOnFailureDelay > TimeSpan.Zero) + _lazyWriterTimer.Change((int)retryOnFailureDelay.TotalMilliseconds, Timeout.Infinite); + else + TaskStartNext(completedTask, fullBatchCompleted); } } @@ -820,9 +825,11 @@ private static bool WaitTaskIsCompleted(Task task, TimeSpan timeout) private static Exception ExtractActualException(AggregateException taskException) { + if (taskException?.InnerExceptions?.Count == 1 && !(taskException.InnerExceptions[0] is AggregateException)) + return taskException.InnerExceptions[0]; // Skip Flatten() + var flattenExceptions = taskException?.Flatten()?.InnerExceptions; - Exception actualException = flattenExceptions?.Count == 1 ? flattenExceptions[0] : taskException; - return actualException; + return flattenExceptions?.Count == 1 ? flattenExceptions[0] : taskException; } private void TaskCancelledTokenReInit() From 7bfbef03303cbbb925a3ccae54803a3627f90b65 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 2 Feb 2025 00:29:15 +0100 Subject: [PATCH 045/224] ColoredConsoleTarget - Recognize NO_COLOR environment variable (#5703) --- src/NLog/Targets/ColoredConsoleTarget.cs | 17 +++++++++++++++-- src/NLog/Targets/ConsoleTarget.cs | 2 +- .../Targets/ColoredConsoleTargetTests.cs | 8 ++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/NLog/Targets/ColoredConsoleTarget.cs b/src/NLog/Targets/ColoredConsoleTarget.cs index 2607b22441..c4315d2a62 100644 --- a/src/NLog/Targets/ColoredConsoleTarget.cs +++ b/src/NLog/Targets/ColoredConsoleTarget.cs @@ -37,9 +37,11 @@ namespace NLog.Targets using System.Collections.Generic; using System.ComponentModel; using System.IO; + using System.Linq; using System.Text; using NLog.Common; using NLog.Config; + using NLog.Layouts; /// /// Writes log messages to the console with customizable coloring. @@ -213,6 +215,11 @@ public Encoding Encoding /// public bool EnableAnsiOutput { get; set; } + /// + /// Support NO_COLOR=1 environment variable. See also + /// + public Layout NoColor { get; set; } = Layout.FromMethod((evt) => new string[] { "1", "TRUE" }.Contains(NLog.Internal.EnvironmentHelper.GetSafeEnvironmentVariable("NO_COLOR")?.Trim().ToUpper())); + /// /// Gets the row highlighting rules. /// @@ -239,7 +246,7 @@ protected override void InitializeTarget() _pauseLogging = !ConsoleTargetHelper.IsConsoleAvailable(out reason); if (_pauseLogging) { - InternalLogger.Info("{0}: Console detected as turned off. Disable DetectConsoleAvailable to skip detection. Reason: {1}", this, reason); + InternalLogger.Info("{0}: Console detected as turned off. Set DetectConsoleAvailable=false to skip detection. Reason: {1}", this, reason); } } @@ -254,7 +261,7 @@ protected override void InitializeTarget() _disableColors = StdErr ? Console.IsErrorRedirected : Console.IsOutputRedirected; if (_disableColors) { - InternalLogger.Info("{0}: Console output is redirected so no colors. Disable DetectOutputRedirected to skip detection.", this); + InternalLogger.Info("{0}: Console output is redirected so no colors. Set DetectOutputRedirected=false to skip detection.", this); if (!AutoFlush && GetOutput() is StreamWriter streamWriter && !streamWriter.AutoFlush) { AutoFlush = true; @@ -268,6 +275,12 @@ protected override void InitializeTarget() } #endif + if (!_disableColors && NoColor?.RenderValue(LogEventInfo.CreateNullEvent()) == true) + { + _disableColors = true; + InternalLogger.Info("{0}: Environment with NO_COLOR, so colors are disabled. Set NoColor=false to skip detection.", this); + } + base.InitializeTarget(); if (Header != null) diff --git a/src/NLog/Targets/ConsoleTarget.cs b/src/NLog/Targets/ConsoleTarget.cs index 140a9f0219..01dd343e1c 100644 --- a/src/NLog/Targets/ConsoleTarget.cs +++ b/src/NLog/Targets/ConsoleTarget.cs @@ -169,7 +169,7 @@ protected override void InitializeTarget() _pauseLogging = !ConsoleTargetHelper.IsConsoleAvailable(out reason); if (_pauseLogging) { - InternalLogger.Info("{0}: Console has been detected as turned off. Disable DetectConsoleAvailable to skip detection. Reason: {1}", this, reason); + InternalLogger.Info("{0}: Console detected as turned off. Set DetectConsoleAvailable=false to skip detection. Reason: {1}", this, reason); } } diff --git a/tests/NLog.UnitTests/Targets/ColoredConsoleTargetTests.cs b/tests/NLog.UnitTests/Targets/ColoredConsoleTargetTests.cs index 3c657683bc..60c50c96bd 100644 --- a/tests/NLog.UnitTests/Targets/ColoredConsoleTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/ColoredConsoleTargetTests.cs @@ -297,6 +297,14 @@ public void ColoredConsoleDetectOutputRedirectedTest() } #endif + [Fact] + public void ColoredConsoleNoColor() + { + var target = new ColoredConsoleTarget { Layout = "${logger} ${message}", NoColor = true }; + AssertOutput(target, "The Cat Sat At The Bar.", + new string[] { "The Cat Sat At The Bar." }); + } + private static void AssertOutput(Target target, string message, string[] expectedParts, string loggerName = "Logger ") { var consoleOutWriter = new PartsWriter(); From e5de8d32fe10c265ec7d796f12dee101c4e91a02 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 16 Feb 2025 12:42:18 +0100 Subject: [PATCH 046/224] Restored SetupFromEnvironmentVariables (#5709) --- .../SetupInternalLoggerBuilderExtensions.cs | 139 ++++++++++++++++++ .../Config/LogFactorySetupTests.cs | 46 ++++++ 2 files changed, 185 insertions(+) diff --git a/src/NLog/SetupInternalLoggerBuilderExtensions.cs b/src/NLog/SetupInternalLoggerBuilderExtensions.cs index 94920d6142..62e71acdd0 100644 --- a/src/NLog/SetupInternalLoggerBuilderExtensions.cs +++ b/src/NLog/SetupInternalLoggerBuilderExtensions.cs @@ -33,9 +33,12 @@ namespace NLog { + using System; + using System.Globalization; using System.IO; using NLog.Common; using NLog.Config; + using NLog.Internal; /// /// Extension methods to setup NLog options @@ -104,5 +107,141 @@ public static ISetupInternalLoggerBuilder RemoveEventSubscription(this ISetupInt InternalLogger.InternalEventOccurred -= eventSubscriber; return setupBuilder; } + + /// + /// Resets the InternalLogger configuration without resolving default values from Environment-varialbes or App.config + /// + public static ISetupInternalLoggerBuilder ResetConfig(this ISetupInternalLoggerBuilder setupBuilder) + { + InternalLogger.Reset(); + return setupBuilder; + } + + /// + /// Configure the InternalLogger properties from Environment-variables and App.config using + /// + /// + /// Recognizes the following environment-variables: + /// + /// - NLOG_INTERNAL_LOG_LEVEL + /// - NLOG_INTERNAL_LOG_FILE + /// - NLOG_INTERNAL_LOG_TO_CONSOLE + /// - NLOG_INTERNAL_LOG_TO_CONSOLE_ERROR + /// - NLOG_INTERNAL_INCLUDE_TIMESTAMP + /// + /// Legacy .NetFramework platform will also recognizes the following app.config settings: + /// + /// - nlog.internalLogLevel + /// - nlog.internalLogFile + /// - nlog.internalLogToConsole + /// - nlog.internalLogToConsoleError + /// - nlog.internalLogIncludeTimestamp + /// + public static ISetupInternalLoggerBuilder SetupFromEnvironmentVariables(this ISetupInternalLoggerBuilder setupBuilder) + { + InternalLogger.LogLevel = GetSetting("nlog.internalLogLevel", "NLOG_INTERNAL_LOG_LEVEL", LogLevel.Off); + InternalLogger.IncludeTimestamp = GetSetting("nlog.internalLogIncludeTimestamp", "NLOG_INTERNAL_INCLUDE_TIMESTAMP", true); + InternalLogger.LogToConsole = GetSetting("nlog.internalLogToConsole", "NLOG_INTERNAL_LOG_TO_CONSOLE", false); + InternalLogger.LogToConsoleError = GetSetting("nlog.internalLogToConsoleError", "NLOG_INTERNAL_LOG_TO_CONSOLE_ERROR", false); + InternalLogger.LogFile = GetSetting("nlog.internalLogFile", "NLOG_INTERNAL_LOG_FILE", string.Empty); + return setupBuilder; + } + + private static string GetAppSettings(string configName) + { +#if NETFRAMEWORK + try + { + return System.Configuration.ConfigurationManager.AppSettings[configName]; + } + catch (Exception ex) + { + if (ex.MustBeRethrownImmediately()) + { + throw; + } + } +#endif + return null; + } + + private static string GetSettingString(string configName, string envName) + { + try + { + string settingValue = GetAppSettings(configName); + if (settingValue != null) + return settingValue; + } + catch (Exception ex) + { + if (ex.MustBeRethrownImmediately()) + { + throw; + } + } + + try + { + string settingValue = EnvironmentHelper.GetSafeEnvironmentVariable(envName); + if (!string.IsNullOrEmpty(settingValue)) + return settingValue; + } + catch (Exception ex) + { + if (ex.MustBeRethrownImmediately()) + { + throw; + } + } + + return null; + } + + private static LogLevel GetSetting(string configName, string envName, LogLevel defaultValue) + { + string value = GetSettingString(configName, envName); + if (value is null) + { + return defaultValue; + } + + try + { + return LogLevel.FromString(value); + } + catch (Exception exception) + { + if (exception.MustBeRethrownImmediately()) + { + throw; + } + + return defaultValue; + } + } + + private static T GetSetting(string configName, string envName, T defaultValue) + { + string value = GetSettingString(configName, envName); + if (value is null) + { + return defaultValue; + } + + try + { + return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture); + } + catch (Exception exception) + { + if (exception.MustBeRethrownImmediately()) + { + throw; + } + + return defaultValue; + } + } } } diff --git a/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs b/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs index d0a5158e02..13eb8fbec8 100644 --- a/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs +++ b/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs @@ -564,6 +564,52 @@ public void SetupInternalLoggerLogToWriterTest() } } + [Fact] + public void SetupInternalLoggerSetupFromEnvironmentVariablesTest() + { + try + { + // Arrange + InternalLogger.Reset(); + var logFactory = new LogFactory(); + InternalLogger.IncludeTimestamp = false; + + // Act + logFactory.Setup().SetupInternalLogger(b => b.SetupFromEnvironmentVariables().SetMinimumLogLevel(LogLevel.Fatal)); + + // Assert + Assert.True(InternalLogger.IncludeTimestamp); + Assert.Equal(LogLevel.Fatal, InternalLogger.LogLevel); + } + finally + { + InternalLogger.Reset(); + } + } + + [Fact] + public void SetupInternalLoggerResetConfigTest() + { + try + { + // Arrange + InternalLogger.Reset(); + var logFactory = new LogFactory(); + InternalLogger.IncludeTimestamp = false; + + // Act + logFactory.Setup().SetupInternalLogger(b => b.ResetConfig().SetMinimumLogLevel(LogLevel.Fatal)); + + // Assert + Assert.True(InternalLogger.IncludeTimestamp); + Assert.Equal(LogLevel.Fatal, InternalLogger.LogLevel); + } + finally + { + InternalLogger.Reset(); + } + } + [Fact] public void SetupExtensionsRegisterConditionMethodTest() { From 49ad0af781fcf1d805cdd770977415484a38384f Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 16 Feb 2025 13:55:55 +0100 Subject: [PATCH 047/224] Marked SetStackTrace with UserStackFrameNumber as obsolete (#5711) --- src/NLog/LogEventInfo.cs | 13 ++++++++++++- src/NLog/NLogTraceListener.cs | 4 +++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index d668f30600..f9415b3d3b 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -221,13 +221,14 @@ public int SequenceID /// /// Gets the stack frame of the method that did the logging. /// - [Obsolete("Instead use ${callsite} or CallerMemberName. Marked obsolete on NLog 5.3")] + [Obsolete("Instead use ${callsite} or CallerMemberName. Marked obsolete with NLog 5.3")] public StackFrame UserStackFrame => CallSiteInformation?.UserStackFrame; /// /// Gets the number index of the stack frame that represents the user /// code (not the NLog code). /// + [Obsolete("Instead use ${callsite} or CallerMemberName. Marked obsolete with NLog 5.4")] public int UserStackFrameNumber => CallSiteInformation?.UserStackFrameNumberLegacy ?? CallSiteInformation?.UserStackFrameNumber ?? 0; /// @@ -543,11 +544,21 @@ public override string ToString() return $"Log Event: Logger='{LoggerName}' Level={Level} Message='{FormattedMessage}'"; } + /// + /// Sets the stack trace for the event info. + /// + /// The stack trace. + public void SetStackTrace(StackTrace stackTrace) + { + GetCallSiteInformationInternal().SetStackTrace(stackTrace, default(int?)); + } + /// /// Sets the stack trace for the event info. /// /// The stack trace. /// Index of the first user stack frame within the stack trace (Negative means NLog should skip stackframes from System-assemblies). + [Obsolete("Instead use SetStackTrace or SetCallerInfo. Marked obsolete with NLog 5.4")] public void SetStackTrace(StackTrace stackTrace, int userStackFrame) { GetCallSiteInformationInternal().SetStackTrace(stackTrace, userStackFrame >= 0 ? userStackFrame : (int?)null); diff --git a/src/NLog/NLogTraceListener.cs b/src/NLog/NLogTraceListener.cs index 84cfed5b36..c5a4595eca 100644 --- a/src/NLog/NLogTraceListener.cs +++ b/src/NLog/NLogTraceListener.cs @@ -484,9 +484,11 @@ protected virtual void ProcessLogEventInfo(LogLevel logLevel, string loggerName, ev.Properties.Add("EventID", ResolvedBoxedEventId(eventId.Value)); } - if (stackTrace != null && userFrameIndex >= 0) + if (stackTrace != null) { +#pragma warning disable CS0618 // Type or member is obsolete ev.SetStackTrace(stackTrace, userFrameIndex); +#pragma warning restore CS0618 // Type or member is obsolete } logger.Log(typeof(System.Diagnostics.Trace), ev); From 308b551c85b7cd51c2dce3a42fa5b5563324754c Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 3 Mar 2025 21:20:31 +0100 Subject: [PATCH 048/224] Fix AOT warnings by not using MakeGenericType (#5715) --- src/NLog/Config/AssemblyExtensionLoader.cs | 2 +- .../Config/LoggingConfigurationFileLoader.cs | 3 +- src/NLog/Internal/AppEnvironmentWrapper.cs | 14 +- src/NLog/Internal/AssemblyHelpers.cs | 35 ++-- src/NLog/Internal/ObjectReflectionCache.cs | 175 +++++++++++++----- src/NLog/Internal/PropertyHelper.cs | 170 +++++++++++------ .../LayoutRenderers/NLogDirLayoutRenderer.cs | 9 +- src/NLog/Layouts/LayoutParser.cs | 5 +- .../SetupInternalLoggerBuilderExtensions.cs | 2 +- src/NLog/Targets/DefaultJsonSerializer.cs | 4 +- .../CallSiteFileNameLayoutTests.cs | 2 +- .../CallSiteLineNumberTests.cs | 2 +- .../LayoutRenderers/CallSiteTests.cs | 2 +- .../StackTraceRendererTests.cs | 2 +- .../Targets/DefaultJsonSerializerTestsBase.cs | 2 +- tests/NLog.UnitTests/Targets/TargetTests.cs | 25 ++- tests/TestTrimPublish/TestTrimPublish.csproj | 4 + 17 files changed, 310 insertions(+), 148 deletions(-) diff --git a/src/NLog/Config/AssemblyExtensionLoader.cs b/src/NLog/Config/AssemblyExtensionLoader.cs index 0627c3bf90..c761ceb09f 100644 --- a/src/NLog/Config/AssemblyExtensionLoader.cs +++ b/src/NLog/Config/AssemblyExtensionLoader.cs @@ -402,7 +402,7 @@ private static bool IncludeAsHiddenAssembly(string assemblyFullName) internal static IEnumerable> GetAutoLoadingFileLocations() { var nlogAssembly = typeof(LogFactory).Assembly; - var nlogAssemblyLocation = PathHelpers.TrimDirectorySeparators(AssemblyHelpers.GetAssemblyFileLocation(nlogAssembly)); + var nlogAssemblyLocation = PathHelpers.TrimDirectorySeparators(System.IO.Path.GetDirectoryName(AssemblyHelpers.GetAssemblyFileLocation(nlogAssembly))); InternalLogger.Debug("Auto loading based on NLog-Assembly found location: {0}", nlogAssemblyLocation); if (!string.IsNullOrEmpty(nlogAssemblyLocation)) yield return new KeyValuePair(nlogAssemblyLocation, nameof(nlogAssemblyLocation)); diff --git a/src/NLog/Config/LoggingConfigurationFileLoader.cs b/src/NLog/Config/LoggingConfigurationFileLoader.cs index d94a2c4a84..04dad2f995 100644 --- a/src/NLog/Config/LoggingConfigurationFileLoader.cs +++ b/src/NLog/Config/LoggingConfigurationFileLoader.cs @@ -259,7 +259,8 @@ private static string LookupNLogAssemblyLocation() { var nlogAssembly = typeof(LogFactory).Assembly; // Get path to NLog.dll.nlog only if the assembly is not in the GAC - var nlogAssemblyLocation = nlogAssembly.Location; + // Notice NLog.dll can be loaded from nuget-cache using NTFS-hard-link, and return unexpected file-location. + var nlogAssemblyLocation = AssemblyHelpers.GetAssemblyFileLocation(nlogAssembly); if (!string.IsNullOrEmpty(nlogAssemblyLocation)) { #if NETFRAMEWORK diff --git a/src/NLog/Internal/AppEnvironmentWrapper.cs b/src/NLog/Internal/AppEnvironmentWrapper.cs index ece69e151b..ead79971b0 100644 --- a/src/NLog/Internal/AppEnvironmentWrapper.cs +++ b/src/NLog/Internal/AppEnvironmentWrapper.cs @@ -249,15 +249,11 @@ private static string LookupEntryAssemblyFriendlyName() private static string LookupEntryAssemblyLocation() { var entryAssembly = System.Reflection.Assembly.GetEntryAssembly(); - var entryLocation = entryAssembly?.Location; + var entryLocation = AssemblyHelpers.GetAssemblyFileLocation(entryAssembly); if (!string.IsNullOrEmpty(entryLocation)) - { return Path.GetDirectoryName(entryLocation); - } - -#pragma warning disable CS0618 // Type or member is obsolete - return AssemblyHelpers.GetAssemblyFileLocation(entryAssembly); -#pragma warning restore CS0618 // Type or member is obsolete + else + return string.Empty; } private static string LookupEntryAssemblyFileName() @@ -265,11 +261,9 @@ private static string LookupEntryAssemblyFileName() try { var entryAssembly = System.Reflection.Assembly.GetEntryAssembly(); - var entryLocation = entryAssembly?.Location; + var entryLocation = AssemblyHelpers.GetAssemblyFileLocation(entryAssembly); if (!string.IsNullOrEmpty(entryLocation)) - { return Path.GetFileName(entryLocation); - } // Fallback to the Assembly-Name when unable to extract FileName from Location var assemblyName = entryAssembly?.GetName()?.Name; diff --git a/src/NLog/Internal/AssemblyHelpers.cs b/src/NLog/Internal/AssemblyHelpers.cs index a58fd47d32..3c1cbc79a9 100644 --- a/src/NLog/Internal/AssemblyHelpers.cs +++ b/src/NLog/Internal/AssemblyHelpers.cs @@ -33,7 +33,6 @@ namespace NLog.Internal { using System; - using System.IO; using System.Reflection; using NLog.Common; @@ -42,28 +41,20 @@ namespace NLog.Internal /// internal static class AssemblyHelpers { + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Returns empty string for assemblies embedded in a single-file app", "IL3000")] public static string GetAssemblyFileLocation(Assembly assembly) { - string assemblyFullName = string.Empty; +#if !NETFRAMEWORK + // Notice assembly can be loaded from nuget-cache using NTFS-hard-link, and return unexpected file-location. + return assembly?.Location ?? string.Empty; +#else + if (assembly is null) + return string.Empty; + + var assemblyFullName = assembly.FullName; try { - if (assembly is null) - { - return string.Empty; - } - - assemblyFullName = assembly.FullName; - -#if NETSTANDARD - if (string.IsNullOrEmpty(assembly.Location)) - { - // Assembly with no actual location should be skipped (Avoid PlatformNotSupportedException) - InternalLogger.Debug("Ignoring assembly location because location is empty: {0}", assemblyFullName); - return string.Empty; - } -#endif - Uri assemblyCodeBase; if (!Uri.TryCreate(assembly.CodeBase, UriKind.RelativeOrAbsolute, out assemblyCodeBase)) { @@ -71,14 +62,14 @@ public static string GetAssemblyFileLocation(Assembly assembly) return string.Empty; } - var assemblyLocation = Path.GetDirectoryName(assemblyCodeBase.LocalPath); + var assemblyLocation = System.IO.Path.GetDirectoryName(assemblyCodeBase.LocalPath); if (string.IsNullOrEmpty(assemblyLocation)) { InternalLogger.Debug("Ignoring assembly location because it is not a valid directory: '{0}' ({1})", assemblyCodeBase.LocalPath, assemblyFullName); return string.Empty; } - DirectoryInfo directoryInfo = new DirectoryInfo(assemblyLocation); + var directoryInfo = new System.IO.DirectoryInfo(assemblyLocation); if (!directoryInfo.Exists) { InternalLogger.Debug("Ignoring assembly location because directory doesn't exists: '{0}' ({1})", assemblyLocation, assemblyFullName); @@ -86,7 +77,8 @@ public static string GetAssemblyFileLocation(Assembly assembly) } InternalLogger.Debug("Found assembly location directory: '{0}' ({1})", directoryInfo.FullName, assemblyFullName); - return directoryInfo.FullName; + var assemblyFileName = string.IsNullOrEmpty(assembly.Location) ? System.IO.Path.GetFileName(assemblyCodeBase.LocalPath) : System.IO.Path.GetFileName(assembly.Location); + return System.IO.Path.Combine(directoryInfo.FullName, assemblyFileName); } catch (System.PlatformNotSupportedException ex) { @@ -115,6 +107,7 @@ public static string GetAssemblyFileLocation(Assembly assembly) } return string.Empty; } +#endif } /// diff --git a/src/NLog/Internal/ObjectReflectionCache.cs b/src/NLog/Internal/ObjectReflectionCache.cs index 054bb27246..02af6dfd92 100644 --- a/src/NLog/Internal/ObjectReflectionCache.cs +++ b/src/NLog/Internal/ObjectReflectionCache.cs @@ -37,7 +37,7 @@ namespace NLog.Internal using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -#if !NET35 && !NET40 +#if !NET35 && !NET40 && NETFRAMEWORK using System.Dynamic; #endif using System.Linq; @@ -133,8 +133,6 @@ public bool TryGetObjectProperty(object value, string[] objectPath, out object f return true; } - [UnconditionalSuppressMessage("Trimming - Allow reflection of message args", "IL2072")] - [UnconditionalSuppressMessage("Trimming - Allow reflection of message args", "IL2075")] public bool TryLookupExpandoObject(object value, out ObjectPropertyList objectPropertyList) { if (value is IDictionary expando) @@ -143,7 +141,7 @@ public bool TryLookupExpandoObject(object value, out ObjectPropertyList objectPr return true; } -#if !NET35 && !NET40 +#if !NET35 && !NET40 && NETFRAMEWORK if (value is DynamicObject d) { var dictionary = DynamicObjectToDict(d); @@ -165,17 +163,13 @@ public bool TryLookupExpandoObject(object value, out ObjectPropertyList objectPr return true; } - foreach (var interfaceType in value.GetType().GetInterfaces()) + var dictionaryEnumerator = TryGetDictionaryEnumerator(value); + if (dictionaryEnumerator != null) { - if (IsGenericDictionaryEnumeratorType(interfaceType)) - { - var dictionaryEnumerator = (IDictionaryEnumerator)Activator.CreateInstance(typeof(DictionaryEnumerator<,>).MakeGenericType(interfaceType.GetGenericArguments())); - propertyInfos = new ObjectPropertyInfos(null, new[] { new FastPropertyLookup(string.Empty, TypeCode.Object, (o, p) => dictionaryEnumerator.GetEnumerator(o)) }); - - ObjectTypeCache.TryAddValue(objectType, propertyInfos); - objectPropertyList = new ObjectPropertyList(value, propertyInfos.Properties, propertyInfos.FastLookup); - return true; - } + propertyInfos = new ObjectPropertyInfos(null, new[] { new FastPropertyLookup(string.Empty, TypeCode.Object, (o, p) => dictionaryEnumerator.GetEnumerator(o)) }); + ObjectTypeCache.TryAddValue(objectType, propertyInfos); + objectPropertyList = new ObjectPropertyList(value, propertyInfos.Properties, propertyInfos.FastLookup); + return true; } objectPropertyList = default(ObjectPropertyList); @@ -237,7 +231,7 @@ private static PropertyInfo[] GetPublicProperties([DynamicallyAccessedMembers(Dy try { - properties = type.GetProperties(PublicProperties); + properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); } catch (Exception ex) { @@ -280,8 +274,6 @@ private static FastPropertyLookup[] BuildFastLookup(PropertyInfo[] properties, b return fastLookup; } - private const BindingFlags PublicProperties = BindingFlags.Instance | BindingFlags.Public; - internal struct ObjectPropertyList : IEnumerable { internal static readonly StringComparer NameComparer = StringComparer.Ordinal; @@ -544,7 +536,7 @@ public bool Equals(ObjectPropertyInfos other) } } -#if !NET35 && !NET40 +#if !NET35 && !NET40 && NETFRAMEWORK private static Dictionary DynamicObjectToDict(DynamicObject d) { var newVal = new Dictionary(); @@ -576,25 +568,56 @@ public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, Dy } } #endif + private static IDictionaryEnumerator TryGetDictionaryEnumerator(object value) + { + if (!(value is IEnumerable) || value is string) + return null; + + if (value is IDictionary) + return new DictionaryEnumerator(); + if (value is IDictionary) + return new DictionaryEnumerator(); +#if !NET35 + if (value is IReadOnlyDictionary) + return new DictionaryEnumerator(); + if (value is IReadOnlyDictionary) + return new DictionaryEnumerator(); +#endif + + if (value is IDictionary && value.GetType().IsGenericType) + { + if (value.GetType().GetGenericArguments()[0] == typeof(string)) + return new DictionaryEnumerator(); + else + return null; + } + + return TryBuildDictionaryEnumerator(value); + } - private static bool IsGenericDictionaryEnumeratorType(Type interfaceType) + [UnconditionalSuppressMessage("Trimming - Allow reflection of message args", "IL2075")] + private static IDictionaryEnumerator TryBuildDictionaryEnumerator(object value) { - if (interfaceType.IsGenericType) + foreach (var interfaceType in value.GetType().GetInterfaces()) { - if (interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>) + if (interfaceType.IsGenericType + && (interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>) #if !NET35 - || interfaceType.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>) + || interfaceType.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>) #endif - ) + ) + && interfaceType.GetGenericArguments()[0] == typeof(string)) { - if (interfaceType.GetGenericArguments()[0] == typeof(string)) - { - return true; - } +#if NETFRAMEWORK + return (IDictionaryEnumerator)Activator.CreateInstance(typeof(DictionaryEnumerator<,>).MakeGenericType(interfaceType.GetGenericArguments())); +#else + // AOT does not support creating new types at runtime + return new DictionaryEnumerator(); +#endif } } - return false; + return null; } private interface IDictionaryEnumerator @@ -602,7 +625,73 @@ private interface IDictionaryEnumerator IEnumerator> GetEnumerator(object value); } - internal sealed class DictionaryEnumerator : IDictionaryEnumerator + private sealed class DictionaryEnumerator : IDictionaryEnumerator + { + private Func>> _enumerateCollection; + + IEnumerator> IDictionaryEnumerator.GetEnumerator(object value) + { + if (value is IDictionary dictionary) + { + if (dictionary.Count > 0) + return YieldEnumerator(dictionary); + } + else if (value is IEnumerable collection) + { + return YieldEnumerator(collection); + } + return EmptyDictionaryEnumerator.Default; + } + + private static IEnumerator> YieldEnumerator(IDictionary dictionary) + { + foreach (var item in new DictionaryEntryEnumerable(dictionary)) + yield return new KeyValuePair(item.Key.ToString(), item.Value); + } + + private IEnumerator> YieldEnumerator(IEnumerable collection) + { + if (_enumerateCollection is null) + { + var enumerator = collection.GetEnumerator(); + if (!enumerator.MoveNext()) + return EmptyDictionaryEnumerator.Default; + _enumerateCollection = BuildEnumerator(enumerator.Current); + } + + return _enumerateCollection(collection); + } + + [UnconditionalSuppressMessage("Trimming - Allow reflection of message args", "IL2075")] + private Func>> BuildEnumerator(object firstItem) + { + if (firstItem.GetType().IsGenericType && firstItem.GetType().GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) + { + var getKeyProperty = firstItem.GetType().GetProperty(nameof(KeyValuePair.Key)); + var getValueProperty = firstItem.GetType().GetProperty(nameof(KeyValuePair.Value)); + return (collection) => + { + return EnumerateItems(collection, getKeyProperty, getValueProperty); + }; + } + else + { + return (collection) => EmptyDictionaryEnumerator.Default; + } + } + + private static IEnumerator> EnumerateItems(IEnumerable collection, PropertyInfo getKeyProperty, PropertyInfo getValueProperty) + { + foreach (var item in collection) + { + var keyString = getKeyProperty.GetValue(item, null); + var valueObject = getValueProperty.GetValue(item, null); + yield return new KeyValuePair(keyString.ToString(), valueObject); + } + } + } + + private sealed class DictionaryEnumerator : IDictionaryEnumerator { public IEnumerator> GetEnumerator(object value) { @@ -634,26 +723,26 @@ private static IEnumerator> YieldEnumerator(IReadOn yield return new KeyValuePair(item.Key.ToString(), item.Value); } #endif + } - private sealed class EmptyDictionaryEnumerator : IEnumerator> - { - public static readonly EmptyDictionaryEnumerator Default = new EmptyDictionaryEnumerator(); + private sealed class EmptyDictionaryEnumerator : IEnumerator> + { + public static readonly EmptyDictionaryEnumerator Default = new EmptyDictionaryEnumerator(); - KeyValuePair IEnumerator>.Current => default(KeyValuePair); + KeyValuePair IEnumerator>.Current => default(KeyValuePair); - object IEnumerator.Current => default(KeyValuePair); + object IEnumerator.Current => default(KeyValuePair); - bool IEnumerator.MoveNext() => false; + bool IEnumerator.MoveNext() => false; - void IDisposable.Dispose() - { - // Nothing here on purpose - } + void IDisposable.Dispose() + { + // Nothing here on purpose + } - void IEnumerator.Reset() - { - // Nothing here on purpose - } + void IEnumerator.Reset() + { + // Nothing here on purpose } } } diff --git a/src/NLog/Internal/PropertyHelper.cs b/src/NLog/Internal/PropertyHelper.cs index 0da94fefb8..e68fd38df7 100644 --- a/src/NLog/Internal/PropertyHelper.cs +++ b/src/NLog/Internal/PropertyHelper.cs @@ -299,6 +299,7 @@ private static bool TryImplicitConversion(Type resultType, string value, out obj return false; } + [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2067")] private static bool TryNLogSpecificConversion(Type propertyType, string value, ConfigurationItemFactory configurationItemFactory, out object newValue) { if (_propertyConversionMapper.TryGetValue(propertyType, out var objectConverter)) @@ -310,8 +311,7 @@ private static bool TryNLogSpecificConversion(Type propertyType, string value, C if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Layout<>)) { var simpleLayout = new SimpleLayout(value, configurationItemFactory); - var concreteType = typeof(Layout<>).MakeGenericType(propertyType.GetGenericArguments()); - newValue = Activator.CreateInstance(concreteType, BindingFlags.Instance | BindingFlags.Public, null, new object[] { simpleLayout }, null); + newValue = Activator.CreateInstance(propertyType, BindingFlags.Instance | BindingFlags.Public, null, new object[] { simpleLayout }, null); return true; } @@ -390,7 +390,7 @@ private static object TryParseConditionValue(string stringValue, ConfigurationIt /// private static bool TryFlatListConversion(object obj, PropertyInfo propInfo, string valueRaw, ConfigurationItemFactory configurationItemFactory, out object newValue) { - if (propInfo.PropertyType.IsGenericType && TryCreateCollectionObject(obj, propInfo, valueRaw, out var newList, out var collectionAddMethod, out var propertyType)) + if (TryCreateCollectionObject(obj, propInfo, out var newList, out var collectionAddMethod, out var propertyType)) { var values = valueRaw.SplitQuoted(',', '\'', '\\'); foreach (var value in values) @@ -414,89 +414,147 @@ private static bool TryFlatListConversion(object obj, PropertyInfo propInfo, str return false; } - private static bool TryCreateCollectionObject(object obj, PropertyInfo propInfo, string valueRaw, out object collectionObject, out MethodInfo collectionAddMethod, out Type collectionItemType) + private static bool TryCreateCollectionObject(object obj, PropertyInfo propInfo, out object collectionObject, out MethodInfo collectionAddMethod, out Type collectionItemType) { - var collectionType = propInfo.PropertyType; - var typeDefinition = collectionType.GetGenericTypeDefinition(); -#if !NET35 - var isSet = typeDefinition == typeof(ISet<>) || typeDefinition == typeof(HashSet<>); -#else - var isSet = typeDefinition == typeof(HashSet<>); -#endif - //not checking "implements" interface as we are creating HashSet or List and also those checks are expensive - if (isSet || typeDefinition == typeof(List<>) || typeDefinition == typeof(IList<>) || typeDefinition == typeof(IEnumerable<>)) //set or list/array etc - { - object hashsetComparer = isSet ? ExtractHashSetComparer(obj, propInfo) : null; + collectionItemType = null; + collectionObject = null; - //note: type.GenericTypeArguments is .NET 4.5+ - collectionItemType = collectionType.GetGenericArguments()[0]; - collectionObject = isSet ? CreateCollectionHashSetInstance(collectionItemType, hashsetComparer, out collectionAddMethod) : CreateCollectionListInstance(collectionItemType, out collectionAddMethod); - //no support for array - if (collectionObject is null) - { - throw new NLogConfigurationException($"Cannot create instance of {collectionType.ToString()} for value {valueRaw}"); - } - if (collectionAddMethod is null) + try + { + if (TryResolveCollectionType(obj, propInfo, out var collectionType, out collectionAddMethod, out var hashsetComparer)) { - throw new NLogConfigurationException($"Add method on type {collectionType.ToString()} for value {valueRaw} not found"); - } + if (collectionAddMethod is null) + throw new NLogConfigurationException($"Cannot resolve Add-method for collection type {collectionType} for property '{propInfo.Name}' on {obj.GetType()}"); - return true; + collectionObject = CreateCollectionInstance(collectionType, hashsetComparer); + if (collectionObject is null) + throw new NLogConfigurationException($"Cannot create instance of collection type {collectionType} for property '{propInfo.Name}' on {obj.GetType()}"); + + collectionItemType = collectionType.GetGenericArguments()[0]; + return true; + } + } + catch (Exception ex) + { + InternalLogger.Error(ex, "Failed to resolve collection type {0} for property '{1}' on {2}", propInfo.PropertyType, propInfo.Name, obj.GetType()); + if (ex.MustBeRethrown()) + throw; } - collectionObject = null; collectionAddMethod = null; - collectionItemType = null; return false; } - private static object CreateCollectionHashSetInstance(Type collectionItemType, object hashSetComparer, out MethodInfo collectionAddMethod) + [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2067")] + [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2070")] + private static object CreateCollectionInstance(Type collectionType, object hashSetComparer) { - var concreteType = typeof(HashSet<>).MakeGenericType(collectionItemType); - - collectionAddMethod = typeof(HashSet<>).MakeGenericType(collectionItemType).GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); - if (hashSetComparer != null) { - var constructor = concreteType.GetConstructor(new[] { hashSetComparer.GetType() }); + var constructor = collectionType.GetConstructor(new[] { hashSetComparer.GetType() }); if (constructor != null) - { return constructor.Invoke(new[] { hashSetComparer }); - } } - - return Activator.CreateInstance(concreteType); + return Activator.CreateInstance(collectionType); } - private static object CreateCollectionListInstance(Type collectionItemType, out MethodInfo collectionAddMethod) + [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2065")] + [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2075")] + private static bool TryResolveCollectionType(object obj, PropertyInfo propInfo, out Type collectionType, out MethodInfo collectionAddMethod, out object hashSetComparer) { - var concreteType = typeof(List<>).MakeGenericType(collectionItemType); + collectionType = null; + collectionAddMethod = null; + hashSetComparer = null; - collectionAddMethod = typeof(List<>).MakeGenericType(collectionItemType).GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + collectionType = propInfo.PropertyType; + if (!collectionType.IsGenericType || !typeof(IEnumerable).IsAssignableFrom(collectionType)) + return false; - return Activator.CreateInstance(concreteType); - } + var typeDefinition = collectionType.GetGenericTypeDefinition(); + if (typeDefinition == typeof(List<>)) + { + collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + return true; + } - /// - /// Attempt to reuse the HashSet.Comparer from the original HashSet-object (Ex. StringComparer.OrdinalIgnoreCase) - /// - private static object ExtractHashSetComparer(object obj, PropertyInfo propInfo) - { - var exitingValue = propInfo.IsValidPublicProperty() ? propInfo.GetPropertyValue(obj) : null; - if (exitingValue != null) + var existingValue = propInfo.IsValidPublicProperty() ? propInfo.GetPropertyValue(obj) : null; + if (existingValue?.GetType().IsGenericType == true) { - // Found original HashSet-object. See if we can extract the Comparer - if (exitingValue.GetType().GetGenericTypeDefinition() == typeof(HashSet<>)) + if (existingValue.GetType().GetGenericTypeDefinition() == typeof(List<>)) { - var comparerPropInfo = typeof(HashSet<>).MakeGenericType(exitingValue.GetType().GetGenericArguments()[0]).GetProperty("Comparer", BindingFlags.Instance | BindingFlags.Public); + collectionType = existingValue.GetType(); + collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + return true; + } + + if (existingValue.GetType().GetGenericTypeDefinition() == typeof(HashSet<>)) + { + collectionType = existingValue.GetType(); + collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + var comparerPropInfo = collectionType.GetProperty("Comparer", BindingFlags.Public | BindingFlags.Instance); if (comparerPropInfo.IsValidPublicProperty()) { - return comparerPropInfo.GetPropertyValue(exitingValue); + hashSetComparer = comparerPropInfo.GetPropertyValue(existingValue); } + return true; } } - return null; + if (typeDefinition == typeof(HashSet<>)) + { + collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + return true; + } + + if (collectionType.IsAssignableFrom(typeof(List))) + { + collectionType = typeof(List); + collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + return true; + } + + if (collectionType.IsAssignableFrom(typeof(List))) + { + collectionType = typeof(List); + collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + return true; + } + + if (collectionType.IsAssignableFrom(typeof(HashSet))) + { + collectionType = typeof(HashSet); + collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + return true; + } + + if (collectionType.IsAssignableFrom(typeof(HashSet))) + { + collectionType = typeof(HashSet); + collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + return true; + } + + InternalLogger.Debug("Object type reflection needed to configure instance of type: {0}", collectionType); + return TryMakeGenericCollectionType(collectionType, out collectionType, out collectionAddMethod); + } + + [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2065")] + [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL3050")] + private static bool TryMakeGenericCollectionType(Type propertyType, out Type collectionType, out MethodInfo collectionAddMethod) + { +#if !NET35 + var typeDefinition = propertyType.GetGenericTypeDefinition(); + if (typeDefinition == typeof(ISet<>)) + { + collectionType = typeof(HashSet<>).MakeGenericType(propertyType.GetGenericArguments()); + collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + return true; + } +#endif + + collectionType = typeof(List<>).MakeGenericType(propertyType.GetGenericArguments()); + collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + return true; } [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2026")] diff --git a/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs index 1c988bd4ea..facd70f9f7 100644 --- a/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs @@ -91,15 +91,14 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) private static string ResolveNLogDir() { var nlogAssembly = typeof(LogFactory).Assembly; - if (!string.IsNullOrEmpty(nlogAssembly.Location)) + var nlogLocation = AssemblyHelpers.GetAssemblyFileLocation(nlogAssembly); + if (!string.IsNullOrEmpty(nlogLocation)) { - return Path.GetDirectoryName(nlogAssembly.Location); + return Path.GetDirectoryName(nlogLocation); } else { -#pragma warning disable CS0618 // Type or member is obsolete - return AssemblyHelpers.GetAssemblyFileLocation(nlogAssembly) ?? string.Empty; -#pragma warning restore CS0618 // Type or member is obsolete + return string.Empty; } } } diff --git a/src/NLog/Layouts/LayoutParser.cs b/src/NLog/Layouts/LayoutParser.cs index faf2be1acb..d110abdcbc 100644 --- a/src/NLog/Layouts/LayoutParser.cs +++ b/src/NLog/Layouts/LayoutParser.cs @@ -503,6 +503,8 @@ private static LayoutRenderer BuildCompleteLayoutRenderer(ConfigurationItemFacto return layoutRenderer; } + + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2072")] private static object ParseLayoutRendererPropertyValue(ConfigurationItemFactory configurationItemFactory, SimpleStringReader stringReader, bool? throwConfigExceptions, string targetTypeName, PropertyInfo propertyInfo) { if (typeof(Layout).IsAssignableFrom(propertyInfo.PropertyType)) @@ -512,8 +514,7 @@ private static object ParseLayoutRendererPropertyValue(ConfigurationItemFactory if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Layout<>)) { - var concreteType = typeof(Layout<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments()); - nestedLayout = (Layout)Activator.CreateInstance(concreteType, BindingFlags.Instance | BindingFlags.Public, null, new object[] { nestedLayout }, null); + nestedLayout = (Layout)Activator.CreateInstance(propertyInfo.PropertyType, BindingFlags.Instance | BindingFlags.Public, null, new object[] { nestedLayout }, null); } return nestedLayout; diff --git a/src/NLog/SetupInternalLoggerBuilderExtensions.cs b/src/NLog/SetupInternalLoggerBuilderExtensions.cs index 62e71acdd0..6755d0279d 100644 --- a/src/NLog/SetupInternalLoggerBuilderExtensions.cs +++ b/src/NLog/SetupInternalLoggerBuilderExtensions.cs @@ -109,7 +109,7 @@ public static ISetupInternalLoggerBuilder RemoveEventSubscription(this ISetupInt } /// - /// Resets the InternalLogger configuration without resolving default values from Environment-varialbes or App.config + /// Resets the InternalLogger configuration without resolving default values from Environment-variables or App.config /// public static ISetupInternalLoggerBuilder ResetConfig(this ISetupInternalLoggerBuilder setupBuilder) { diff --git a/src/NLog/Targets/DefaultJsonSerializer.cs b/src/NLog/Targets/DefaultJsonSerializer.cs index b95d77c210..a52f5e0fc5 100644 --- a/src/NLog/Targets/DefaultJsonSerializer.cs +++ b/src/NLog/Targets/DefaultJsonSerializer.cs @@ -218,7 +218,7 @@ private bool SerializeObjectWithReflection(object value, StringBuilder destinati } } - if (value is IEnumerable enumerable) + if (value is IEnumerable collection) { if (_objectReflectionCache.TryLookupExpandoObject(value, out var objectPropertyList)) { @@ -228,7 +228,7 @@ private bool SerializeObjectWithReflection(object value, StringBuilder destinati { using (StartCollectionScope(ref objectsInPath, value)) { - SerializeCollectionObject(enumerable, destination, options, objectsInPath, depth); + SerializeCollectionObject(collection, destination, options, objectsInPath, depth); return true; } } diff --git a/tests/NLog.UnitTests/LayoutRenderers/CallSiteFileNameLayoutTests.cs b/tests/NLog.UnitTests/LayoutRenderers/CallSiteFileNameLayoutTests.cs index 409a135731..5bb7fe3d1a 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/CallSiteFileNameLayoutTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/CallSiteFileNameLayoutTests.cs @@ -105,7 +105,7 @@ public void CallSiteFileNameNoCaptureStackTraceWithStackTraceTest() // Act var logEvent = new LogEventInfo(LogLevel.Info, null, "msg"); - logEvent.SetStackTrace(new System.Diagnostics.StackTrace(true), 0); + logEvent.SetStackTrace(new System.Diagnostics.StackTrace(true)); logFactory.GetLogger("A").Log(logEvent); // Assert diff --git a/tests/NLog.UnitTests/LayoutRenderers/CallSiteLineNumberTests.cs b/tests/NLog.UnitTests/LayoutRenderers/CallSiteLineNumberTests.cs index 40c12d9fa1..4cee18920f 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/CallSiteLineNumberTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/CallSiteLineNumberTests.cs @@ -127,7 +127,7 @@ public void LineNumberNoCaptureStackTraceWithStackTraceTest() // Act var logEvent = new LogEventInfo(LogLevel.Info, null, "msg"); - logEvent.SetStackTrace(new System.Diagnostics.StackTrace(true), 0); + logEvent.SetStackTrace(new System.Diagnostics.StackTrace(true)); logFactory.GetLogger("A").Log(logEvent); // Assert diff --git a/tests/NLog.UnitTests/LayoutRenderers/CallSiteTests.cs b/tests/NLog.UnitTests/LayoutRenderers/CallSiteTests.cs index df778c9814..c95673422a 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/CallSiteTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/CallSiteTests.cs @@ -253,7 +253,7 @@ public void MethodNameNoCaptureStackTraceWithStackTraceTest() // Act var logEvent = new LogEventInfo(LogLevel.Info, null, "msg"); - logEvent.SetStackTrace(new System.Diagnostics.StackTrace(true), 0); + logEvent.SetStackTrace(new System.Diagnostics.StackTrace(true)); logFactory.GetLogger("A").Log(logEvent); // Assert diff --git a/tests/NLog.UnitTests/LayoutRenderers/StackTraceRendererTests.cs b/tests/NLog.UnitTests/LayoutRenderers/StackTraceRendererTests.cs index ec616bf614..624a297dbb 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/StackTraceRendererTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/StackTraceRendererTests.cs @@ -111,7 +111,7 @@ public void RenderStackTraceNoCaptureStackTraceWithStackTrace() ").LogFactory; var logEvent = new LogEventInfo(LogLevel.Info, null, "I am:"); - logEvent.SetStackTrace(new System.Diagnostics.StackTrace(true), 0); + logEvent.SetStackTrace(new System.Diagnostics.StackTrace(true)); logFactory.GetCurrentClassLogger().Log(logEvent); logFactory.AssertDebugLastMessageContains($" => {nameof(StackTraceRendererTests)}.{nameof(RenderStackTraceNoCaptureStackTraceWithStackTrace)}"); } diff --git a/tests/NLog.UnitTests/Targets/DefaultJsonSerializerTestsBase.cs b/tests/NLog.UnitTests/Targets/DefaultJsonSerializerTestsBase.cs index df86044cbe..743a32f4af 100644 --- a/tests/NLog.UnitTests/Targets/DefaultJsonSerializerTestsBase.cs +++ b/tests/NLog.UnitTests/Targets/DefaultJsonSerializerTestsBase.cs @@ -563,7 +563,7 @@ private class CustomNullProperty : IConvertible public override string ToString() => "nullValue"; } -#if !NET35 && !NET40 +#if !NET35 && !NET40 && NETFRAMEWORK [Fact] public void SerializeExpandoObject_Test() diff --git a/tests/NLog.UnitTests/Targets/TargetTests.cs b/tests/NLog.UnitTests/Targets/TargetTests.cs index 99d4e8d3b4..5cb977cf02 100644 --- a/tests/NLog.UnitTests/Targets/TargetTests.cs +++ b/tests/NLog.UnitTests/Targets/TargetTests.cs @@ -762,7 +762,6 @@ protected override void InitializeTarget() } } - [Fact] public void TypedLayoutTargetTest() { @@ -789,6 +788,9 @@ public void TypedLayoutTargetTest() typeProperty='System.Int32' uriProperty='https://nlog-project.org' lineEndingModeProperty='default' + shortSetProperty='1,2,3' + shortListProperty='1,2,3' + longListProperty='1,2,3' /> "); @@ -811,6 +813,18 @@ public void TypedLayoutTargetTest() Assert.Equal(typeof(int), myTarget.TypeProperty.FixedValue); Assert.Equal(new Uri("https://nlog-project.org"), myTarget.UriProperty.FixedValue); Assert.Equal(LineEndingMode.Default, myTarget.LineEndingModeProperty.FixedValue); + Assert.Equal(3, myTarget.ShortSetProperty.Count); + Assert.Contains((short)1, myTarget.ShortSetProperty); + Assert.Contains((short)2, myTarget.ShortSetProperty); + Assert.Contains((short)3, myTarget.ShortSetProperty); + Assert.Equal(3, myTarget.ShortListProperty.Count); + Assert.Contains((short)1, myTarget.ShortListProperty); + Assert.Contains((short)2, myTarget.ShortListProperty); + Assert.Contains((short)3, myTarget.ShortListProperty); + Assert.Equal(3, myTarget.LongListProperty.Count); + Assert.Contains(1L, myTarget.LongListProperty); + Assert.Contains(2L, myTarget.LongListProperty); + Assert.Contains(3L, myTarget.LongListProperty); } [Fact] @@ -1022,6 +1036,15 @@ public class MyTypedLayoutTarget : Target public Layout LineEndingModeProperty { get; set; } +#if NET35 + public HashSet ShortSetProperty { get; set; } +#else + public IList ShortSetProperty { get; set; } +#endif + + public IList ShortListProperty { get; set; } + public IList LongListProperty { get; set; } = new List(); + protected override void Write(LogEventInfo logEvent) { logEvent.Properties[nameof(ByteProperty)] = RenderLogEvent(ByteProperty, logEvent); diff --git a/tests/TestTrimPublish/TestTrimPublish.csproj b/tests/TestTrimPublish/TestTrimPublish.csproj index e0e0b802f4..1498144bea 100644 --- a/tests/TestTrimPublish/TestTrimPublish.csproj +++ b/tests/TestTrimPublish/TestTrimPublish.csproj @@ -10,12 +10,16 @@ enable enable true + true true win-x64 true true + true + false From 489cb81124c2a3112b1063a197ce4a16578d46d4 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 5 Mar 2025 19:20:58 +0100 Subject: [PATCH 049/224] Fix AOT warnings by not using MakeGenericType (refactor) (#5718) --- src/NLog/Internal/PropertyHelper.cs | 191 +++++++++++++++------------- 1 file changed, 102 insertions(+), 89 deletions(-) diff --git a/src/NLog/Internal/PropertyHelper.cs b/src/NLog/Internal/PropertyHelper.cs index e68fd38df7..97bb56b59d 100644 --- a/src/NLog/Internal/PropertyHelper.cs +++ b/src/NLog/Internal/PropertyHelper.cs @@ -390,170 +390,183 @@ private static object TryParseConditionValue(string stringValue, ConfigurationIt /// private static bool TryFlatListConversion(object obj, PropertyInfo propInfo, string valueRaw, ConfigurationItemFactory configurationItemFactory, out object newValue) { - if (TryCreateCollectionObject(obj, propInfo, out var newList, out var collectionAddMethod, out var propertyType)) + var collectionType = propInfo.PropertyType; + if (!collectionType.IsGenericType || !typeof(IEnumerable).IsAssignableFrom(collectionType)) { - var values = valueRaw.SplitQuoted(',', '\'', '\\'); - foreach (var value in values) - { - if (!(TryGetEnumValue(propertyType, value, out newValue) - || TryNLogSpecificConversion(propertyType, value, configurationItemFactory, out newValue) - || TryImplicitConversion(propertyType, value, out newValue) - || TryTypeConverterConversion(propertyType, value, out newValue))) - { - newValue = Convert.ChangeType(value, propertyType, CultureInfo.InvariantCulture); - } - - collectionAddMethod.Invoke(newList, new object[] { newValue }); - } - - newValue = newList; - return true; + newValue = null; + return false; } - newValue = null; - return false; - } - - private static bool TryCreateCollectionObject(object obj, PropertyInfo propInfo, out object collectionObject, out MethodInfo collectionAddMethod, out Type collectionItemType) - { - collectionItemType = null; - collectionObject = null; - try { - if (TryResolveCollectionType(obj, propInfo, out var collectionType, out collectionAddMethod, out var hashsetComparer)) + if (TryCreateCollectionObject(obj, propInfo, out var newList, out var collectionAddMethod, out var propertyType)) { - if (collectionAddMethod is null) - throw new NLogConfigurationException($"Cannot resolve Add-method for collection type {collectionType} for property '{propInfo.Name}' on {obj.GetType()}"); + var values = valueRaw.SplitQuoted(',', '\'', '\\'); + foreach (var value in values) + { + if (!(TryGetEnumValue(propertyType, value, out newValue) + || TryNLogSpecificConversion(propertyType, value, configurationItemFactory, out newValue) + || TryImplicitConversion(propertyType, value, out newValue) + || TryTypeConverterConversion(propertyType, value, out newValue))) + { + newValue = Convert.ChangeType(value, propertyType, CultureInfo.InvariantCulture); + } - collectionObject = CreateCollectionInstance(collectionType, hashsetComparer); - if (collectionObject is null) - throw new NLogConfigurationException($"Cannot create instance of collection type {collectionType} for property '{propInfo.Name}' on {obj.GetType()}"); + collectionAddMethod.Invoke(newList, new object[] { newValue }); + } - collectionItemType = collectionType.GetGenericArguments()[0]; + newValue = newList; return true; } + + newValue = null; + return false; } catch (Exception ex) { - InternalLogger.Error(ex, "Failed to resolve collection type {0} for property '{1}' on {2}", propInfo.PropertyType, propInfo.Name, obj.GetType()); - if (ex.MustBeRethrown()) - throw; - } - - collectionAddMethod = null; - return false; - } - - [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2067")] - [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2070")] - private static object CreateCollectionInstance(Type collectionType, object hashSetComparer) - { - if (hashSetComparer != null) - { - var constructor = collectionType.GetConstructor(new[] { hashSetComparer.GetType() }); - if (constructor != null) - return constructor.Invoke(new[] { hashSetComparer }); + throw new NLogConfigurationException($"Failed to parse collection for property '{propInfo.Name}' on {obj.GetType()} with value '{valueRaw}'", ex); } - return Activator.CreateInstance(collectionType); } - [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2065")] + [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2072")] [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2075")] - private static bool TryResolveCollectionType(object obj, PropertyInfo propInfo, out Type collectionType, out MethodInfo collectionAddMethod, out object hashSetComparer) + private static bool TryCreateCollectionObject(object obj, PropertyInfo propInfo, out object collectionObject, out MethodInfo collectionAddMethod, out Type collectionItemType) { - collectionType = null; + collectionObject = null; collectionAddMethod = null; - hashSetComparer = null; + collectionItemType = null; - collectionType = propInfo.PropertyType; - if (!collectionType.IsGenericType || !typeof(IEnumerable).IsAssignableFrom(collectionType)) - return false; + var collectionType = propInfo.PropertyType; + if (TryCreateListCollection(collectionType, out collectionObject, out collectionAddMethod, out collectionItemType)) + return true; + + if (TryCreateListCollection(collectionType, out collectionObject, out collectionAddMethod, out collectionItemType)) + return true; var typeDefinition = collectionType.GetGenericTypeDefinition(); if (typeDefinition == typeof(List<>)) { + collectionObject = Activator.CreateInstance(collectionType); collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + collectionItemType = collectionType.GetGenericArguments()[0]; return true; } + if (typeDefinition != typeof(IList<>) && typeDefinition != typeof(IEnumerable<>) && typeDefinition != typeof(HashSet<>) +#if !NET35 + && typeDefinition != typeof(ISet<>) +#endif + ) + return false; + var existingValue = propInfo.IsValidPublicProperty() ? propInfo.GetPropertyValue(obj) : null; if (existingValue?.GetType().IsGenericType == true) { if (existingValue.GetType().GetGenericTypeDefinition() == typeof(List<>)) { - collectionType = existingValue.GetType(); - collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + var existingType = existingValue.GetType(); + collectionObject = Activator.CreateInstance(existingType); + collectionAddMethod = existingType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + collectionItemType = existingType.GetGenericArguments()[0]; return true; } if (existingValue.GetType().GetGenericTypeDefinition() == typeof(HashSet<>)) { - collectionType = existingValue.GetType(); - collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); - var comparerPropInfo = collectionType.GetProperty("Comparer", BindingFlags.Public | BindingFlags.Instance); + object hashSetComparer = null; + var existingType = existingValue.GetType(); + var comparerPropInfo = existingType.GetProperty("Comparer", BindingFlags.Public | BindingFlags.Instance); if (comparerPropInfo.IsValidPublicProperty()) { hashSetComparer = comparerPropInfo.GetPropertyValue(existingValue); } + if (hashSetComparer != null) + { + var constructor = existingType.GetConstructor(new[] { hashSetComparer.GetType() }); + if (constructor != null) + collectionObject = constructor.Invoke(new[] { hashSetComparer }); + else + collectionObject = Activator.CreateInstance(existingType); + } + else + { + collectionObject = Activator.CreateInstance(existingType); + } + collectionAddMethod = existingType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + collectionItemType = existingType.GetGenericArguments()[0]; return true; } } - if (typeDefinition == typeof(HashSet<>)) - { - collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + if (TryCreateHashSetCollection(collectionType, out collectionObject, out collectionAddMethod, out collectionItemType)) return true; - } - if (collectionType.IsAssignableFrom(typeof(List))) - { - collectionType = typeof(List); - collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + if (TryCreateHashSetCollection(collectionType, out collectionObject, out collectionAddMethod, out collectionItemType)) return true; - } - if (collectionType.IsAssignableFrom(typeof(List))) + if (typeDefinition == typeof(HashSet<>)) { - collectionType = typeof(List); + collectionObject = Activator.CreateInstance(collectionType); collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + collectionItemType = collectionType.GetGenericArguments()[0]; return true; } - if (collectionType.IsAssignableFrom(typeof(HashSet))) + InternalLogger.Debug("Object type reflection needed to configure instance of type: {0}", collectionType); + return TryCreateTypeCollection(collectionType, out collectionObject, out collectionAddMethod, out collectionItemType); + } + + private static bool TryCreateListCollection(Type collectionType, out object collectionObject, out MethodInfo collectionAddMethod, out Type collectionItemType) + { + if (collectionType.IsAssignableFrom(typeof(List))) { - collectionType = typeof(HashSet); - collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + collectionObject = new List(); + collectionAddMethod = typeof(List).GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + collectionItemType = typeof(T); return true; } - if (collectionType.IsAssignableFrom(typeof(HashSet))) + collectionObject = null; + collectionAddMethod = null; + collectionItemType = null; + return false; + } + + private static bool TryCreateHashSetCollection(Type collectionType, out object collectionObject, out MethodInfo collectionAddMethod, out Type collectionItemType) + { + if (collectionType.IsAssignableFrom(typeof(HashSet))) { - collectionType = typeof(HashSet); - collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + collectionObject = new HashSet(); + collectionAddMethod = typeof(HashSet).GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + collectionItemType = typeof(T); return true; } - InternalLogger.Debug("Object type reflection needed to configure instance of type: {0}", collectionType); - return TryMakeGenericCollectionType(collectionType, out collectionType, out collectionAddMethod); + collectionObject = null; + collectionAddMethod = null; + collectionItemType = null; + return false; } - [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2065")] [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL3050")] - private static bool TryMakeGenericCollectionType(Type propertyType, out Type collectionType, out MethodInfo collectionAddMethod) + private static bool TryCreateTypeCollection(Type propertyType, out object collectionObject, out MethodInfo collectionAddMethod, out Type collectionItemType) { #if !NET35 var typeDefinition = propertyType.GetGenericTypeDefinition(); if (typeDefinition == typeof(ISet<>)) { - collectionType = typeof(HashSet<>).MakeGenericType(propertyType.GetGenericArguments()); - collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + var setType = typeof(HashSet<>).MakeGenericType(propertyType.GetGenericArguments()); + collectionAddMethod = setType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + collectionItemType = propertyType.GetGenericArguments()[0]; + collectionObject = Activator.CreateInstance(setType); return true; } #endif - collectionType = typeof(List<>).MakeGenericType(propertyType.GetGenericArguments()); - collectionAddMethod = collectionType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + var listType = typeof(List<>).MakeGenericType(propertyType.GetGenericArguments()); + collectionAddMethod = listType.GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); + collectionItemType = propertyType.GetGenericArguments()[0]; + collectionObject = Activator.CreateInstance(listType); return true; } From 483aedbb79e7f9d3b467d07369d7eb7254da7246 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 5 Mar 2025 20:55:11 +0100 Subject: [PATCH 050/224] Move LocalIpAddressLayoutRenderer to NLog.Targets.Network (#5719) --- .../INetworkInterfaceRetriever.cs | 0 .../LocalIpAddressLayoutRenderer.cs | 0 .../NetworkInterfaceRetriever.cs | 0 src/NLog/Config/AssemblyExtensionTypes.cs | 1 - src/NLog/Properties/AssemblyInfo.cs | 2 -- .../LocalIpAddressLayoutRendererTests.cs | 22 ++++++++++++++----- tests/NLog.UnitTests/NLog.UnitTests.csproj | 1 - 7 files changed, 17 insertions(+), 9 deletions(-) rename src/{NLog/Internal => NLog.Targets.Network}/INetworkInterfaceRetriever.cs (100%) rename src/{NLog/LayoutRenderers => NLog.Targets.Network}/LocalIpAddressLayoutRenderer.cs (100%) rename src/{NLog/Internal => NLog.Targets.Network}/NetworkInterfaceRetriever.cs (100%) rename tests/{NLog.UnitTests/LayoutRenderers => NLog.Targets.Network.Tests}/LocalIpAddressLayoutRendererTests.cs (95%) diff --git a/src/NLog/Internal/INetworkInterfaceRetriever.cs b/src/NLog.Targets.Network/INetworkInterfaceRetriever.cs similarity index 100% rename from src/NLog/Internal/INetworkInterfaceRetriever.cs rename to src/NLog.Targets.Network/INetworkInterfaceRetriever.cs diff --git a/src/NLog/LayoutRenderers/LocalIpAddressLayoutRenderer.cs b/src/NLog.Targets.Network/LocalIpAddressLayoutRenderer.cs similarity index 100% rename from src/NLog/LayoutRenderers/LocalIpAddressLayoutRenderer.cs rename to src/NLog.Targets.Network/LocalIpAddressLayoutRenderer.cs diff --git a/src/NLog/Internal/NetworkInterfaceRetriever.cs b/src/NLog.Targets.Network/NetworkInterfaceRetriever.cs similarity index 100% rename from src/NLog/Internal/NetworkInterfaceRetriever.cs rename to src/NLog.Targets.Network/NetworkInterfaceRetriever.cs diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index 058fb86beb..060677d9e7 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -96,7 +96,6 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.LayoutRendererFactory.RegisterType("loglevel"); factory.LayoutRendererFactory.RegisterType("literal"); factory.RegisterTypeProperties(() => null); - factory.LayoutRendererFactory.RegisterType("local-ip"); factory.LayoutRendererFactory.RegisterType("loggername"); factory.LayoutRendererFactory.RegisterType("logger"); factory.LayoutRendererFactory.RegisterType("longdate"); diff --git a/src/NLog/Properties/AssemblyInfo.cs b/src/NLog/Properties/AssemblyInfo.cs index 18f79e01df..21ba8882a0 100644 --- a/src/NLog/Properties/AssemblyInfo.cs +++ b/src/NLog/Properties/AssemblyInfo.cs @@ -47,5 +47,3 @@ #if !NET35 [assembly: SecurityRules(SecurityRuleSet.Level1)] #endif -// NSubstitute -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/tests/NLog.UnitTests/LayoutRenderers/LocalIpAddressLayoutRendererTests.cs b/tests/NLog.Targets.Network.Tests/LocalIpAddressLayoutRendererTests.cs similarity index 95% rename from tests/NLog.UnitTests/LayoutRenderers/LocalIpAddressLayoutRendererTests.cs rename to tests/NLog.Targets.Network.Tests/LocalIpAddressLayoutRendererTests.cs index 2e59306a05..697de21abf 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/LocalIpAddressLayoutRendererTests.cs +++ b/tests/NLog.Targets.Network.Tests/LocalIpAddressLayoutRendererTests.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.UnitTests.LayoutRenderers +namespace NLog.Targets.Network { using System; using System.Collections.Generic; @@ -45,7 +45,7 @@ namespace NLog.UnitTests.LayoutRenderers using NSubstitute.ExceptionExtensions; using Xunit; - public class LocalIpAddressLayoutRendererTests : NLogTestBase + public class LocalIpAddressLayoutRendererTests { private const string Mac1 = "F0-E1-D2-C3-B4-A5"; @@ -250,7 +250,14 @@ internal sealed class NetworkInterfaceRetrieverBuilder { private readonly IDictionary>> _ips = new Dictionary>>(); - private IList<(NetworkInterfaceType networkInterfaceType, string mac, OperationalStatus status)> _networkInterfaces = new List<(NetworkInterfaceType networkInterfaceType, string mac, OperationalStatus status)>(); + struct NetworkInfo + { + public NetworkInterfaceType NetworkType { get; set; } + public string MacAddress { get; set; } + public OperationalStatus NetworkStatus { get; set; } + } + + private List _networkInterfaces = new List(); private readonly INetworkInterfaceRetriever _networkInterfaceRetrieverMock; /// @@ -261,7 +268,12 @@ public NetworkInterfaceRetrieverBuilder() public NetworkInterfaceRetrieverBuilder WithInterface(NetworkInterfaceType networkInterfaceType, string mac, OperationalStatus status = OperationalStatus.Up) { - _networkInterfaces.Add((networkInterfaceType, mac, status)); + _networkInterfaces.Add(new NetworkInfo() + { + NetworkType = networkInterfaceType, + MacAddress = mac, + NetworkStatus = status + }); return this; } @@ -301,7 +313,7 @@ private IEnumerable BuildAllNetworkInterfaces() var networkInterface = _networkInterfaces[i]; if (_ips.TryGetValue(i, out var ips)) { - var networkInterfaceMock = BuildNetworkInterfaceMock(ips, networkInterface.mac, networkInterface.networkInterfaceType, networkInterface.status); + var networkInterfaceMock = BuildNetworkInterfaceMock(ips, networkInterface.MacAddress, networkInterface.NetworkType, networkInterface.NetworkStatus); networkInterfaceMock.Id.Returns($"#{i}"); networkInterfaceMock.Description.Returns("ips: " + string.Join(";", ips.ToArray())); yield return networkInterfaceMock; diff --git a/tests/NLog.UnitTests/NLog.UnitTests.csproj b/tests/NLog.UnitTests/NLog.UnitTests.csproj index 5e87e33bb3..91513b9247 100644 --- a/tests/NLog.UnitTests/NLog.UnitTests.csproj +++ b/tests/NLog.UnitTests/NLog.UnitTests.csproj @@ -37,7 +37,6 @@ - all From a0671abd82e1e3f2e22968d72e519c3e1c29d83a Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 5 Mar 2025 22:38:19 +0100 Subject: [PATCH 051/224] netstandard2.1 recognizes RuntimeFeature.IsDynamicCodeSupported (#5720) --- build.ps1 | 2 +- src/NLog/Internal/ReflectionHelpers.cs | 42 ++++++++++++++------------ src/NLog/NLog.csproj | 10 ++++-- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/build.ps1 b/build.ps1 index 0fd5a45d97..b7a74a3fdf 100644 --- a/build.ps1 +++ b/build.ps1 @@ -23,7 +23,7 @@ if (-Not (test-path $targetNugetExe)) Invoke-WebRequest $sourceNugetExe -OutFile $targetNugetExe } -msbuild /t:Restore,Pack .\src\NLog\ /p:targetFrameworks='"net46;net45;net35;netstandard2.0"' /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount +msbuild /t:Restore,Pack .\src\NLog\ /p:targetFrameworks='"net46;net45;net35;netstandard2.0;netstandard2.1"' /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount if (-Not $LastExitCode -eq 0) { exit $LastExitCode } diff --git a/src/NLog/Internal/ReflectionHelpers.cs b/src/NLog/Internal/ReflectionHelpers.cs index df1e0a77ca..81bc739f34 100644 --- a/src/NLog/Internal/ReflectionHelpers.cs +++ b/src/NLog/Internal/ReflectionHelpers.cs @@ -36,9 +36,7 @@ namespace NLog.Internal using System; using System.Collections.Generic; using System.Linq; -#if NETFRAMEWORK using System.Linq.Expressions; -#endif using System.Reflection; using JetBrains.Annotations; @@ -67,22 +65,6 @@ public static bool IsStaticClass(this Type type) /// Complete list of parameters that matches the method, including optional/default parameters. public delegate object LateBoundMethod(object target, object[] arguments); -#if !NETFRAMEWORK - public static LateBoundMethod CreateLateBoundMethod(MethodInfo methodInfo) - { - return (target, args) => - { - try - { - return methodInfo.Invoke(target, args); - } - catch (TargetInvocationException exception) - { - throw exception.InnerException ?? exception; - } - }; - } -#else /// /// Creates an optimized delegate for calling the MethodInfo using Expression-Trees /// @@ -90,6 +72,28 @@ public static LateBoundMethod CreateLateBoundMethod(MethodInfo methodInfo) /// Optimized delegate for invoking the MethodInfo public static LateBoundMethod CreateLateBoundMethod(MethodInfo methodInfo) { +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER + if (!System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported) + { + return (target, args) => + { + try + { + return methodInfo.Invoke(target, args); + } + catch (TargetInvocationException exception) + { + throw exception.InnerException ?? exception; + } + }; + } +#endif + + return CompileLateBoundMethod(methodInfo); + } + + private static LateBoundMethod CompileLateBoundMethod(MethodInfo methodInfo) + { var instanceParameter = Expression.Parameter(typeof(object), "instance"); var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); @@ -154,7 +158,7 @@ private static UnaryExpression CreateParameterExpression(ParameterInfo parameter var valueCast = Expression.Convert(expression, parameterType); return valueCast; } -#endif + [CanBeNull] public static TAttr GetFirstCustomAttribute(this Type type) where TAttr : Attribute { diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index a1418cbc14..5e26773086 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -1,7 +1,7 @@ - net35;net46;netstandard2.0 + net35;net46;netstandard2.0;netstandard2.1 NLog for .NET Framework and .NET Standard NLog @@ -65,8 +65,8 @@ For all config options and platform support, check https://nlog-project.org/conf true true - true - true + true + true copyused @@ -92,6 +92,10 @@ For all config options and platform support, check https://nlog-project.org/conf NLog for NetStandard 2.0 + + NLog for NetStandard 2.1 + + $(Title) - Mono $(DefineConstants);MONO From 4928fb582a83958672f81c97058d9544133fff61 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 5 Mar 2025 22:48:49 +0100 Subject: [PATCH 052/224] Removed reference to System.Net (#5721) --- src/NLog/Annotations.cs | 671 ------------------ src/NLog/Internal/ObjectReflectionCache.cs | 3 - .../LayoutRenderers/HostNameLayoutRenderer.cs | 8 +- .../HostNameLayoutRendererTests.cs | 5 +- 4 files changed, 7 insertions(+), 680 deletions(-) diff --git a/src/NLog/Annotations.cs b/src/NLog/Annotations.cs index 724a56d405..2284e2f4f1 100644 --- a/src/NLog/Annotations.cs +++ b/src/NLog/Annotations.cs @@ -64,45 +64,6 @@ internal sealed class CanBeNullAttribute : Attribute { } AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] internal sealed class NotNullAttribute : Attribute { } - /// - /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task - /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property - /// or of the Lazy.Value property can never be null. - /// - /// - /// public void Foo([ItemNotNull]List<string> books) - /// { - /// foreach (var book in books) { - /// if (book != null) // Warning: Expression is always true - /// Console.WriteLine(book.ToUpper()); - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field)] - internal sealed class ItemNotNullAttribute : Attribute { } - - /// - /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task - /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property - /// or of the Lazy.Value property can be null. - /// - /// - /// public void Foo([ItemCanBeNull]List<string> books) - /// { - /// foreach (var book in books) - /// { - /// // Warning: Possible 'System.NullReferenceException' - /// Console.WriteLine(book.ToUpper()); - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field)] - internal sealed class ItemCanBeNullAttribute : Attribute { } - /// /// Indicates that the marked method builds string by the format pattern and (optional) arguments. /// The parameter, which contains the format string, should be given in the constructor. The format string @@ -146,169 +107,6 @@ public StringFormatMethodAttribute([NotNull] string formatParameterName) [AttributeUsage(AttributeTargets.Parameter)] internal sealed class StructuredMessageTemplateAttribute : Attribute { } - /// - /// Use this annotation to specify a type that contains static or const fields - /// with values for the annotated property/field/parameter. - /// The specified type will be used to improve completion suggestions. - /// - /// - /// namespace TestNamespace - /// { - /// public class Constants - /// { - /// public static int INT_CONST = 1; - /// public const string STRING_CONST = "1"; - /// } - /// - /// public class Class1 - /// { - /// [ValueProvider("TestNamespace.Constants")] public int myField; - /// public void Foo([ValueProvider("TestNamespace.Constants")] string str) { } - /// - /// public void Test() - /// { - /// Foo(/*try completion here*/);// - /// myField = /*try completion here*/ - /// } - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, - AllowMultiple = true)] - internal sealed class ValueProviderAttribute : Attribute - { - public ValueProviderAttribute([NotNull] string name) - { - Name = name; - } - - [NotNull] public string Name { get; } - } - - /// - /// Indicates that the integral value falls into the specified interval. - /// It's allowed to specify multiple non-intersecting intervals. - /// Values of interval boundaries are inclusive. - /// - /// - /// void Foo([ValueRange(0, 100)] int value) { - /// if (value == -1) { // Warning: Expression is always 'false' - /// ... - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | - AttributeTargets.Method | AttributeTargets.Delegate, - AllowMultiple = true)] - internal sealed class ValueRangeAttribute : Attribute - { - public object From { get; } - public object To { get; } - - public ValueRangeAttribute(long from, long to) - { - From = from; - To = to; - } - - public ValueRangeAttribute(ulong from, ulong to) - { - From = from; - To = to; - } - - public ValueRangeAttribute(long value) - { - From = To = value; - } - - public ValueRangeAttribute(ulong value) - { - From = To = value; - } - } - - /// - /// Indicates that the integral value never falls below zero. - /// - /// - /// void Foo([NonNegativeValue] int value) { - /// if (value == -1) { // Warning: Expression is always 'false' - /// ... - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | - AttributeTargets.Method | AttributeTargets.Delegate)] - internal sealed class NonNegativeValueAttribute : Attribute { } - - /// - /// Indicates that the function argument should be a string literal and match - /// one of the parameters of the caller function. This annotation is used for parameters - /// like 'string paramName' parameter of the constructor. - /// - /// - /// void Foo(string param) { - /// if (param == null) - /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol - /// } - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class InvokerParameterNameAttribute : Attribute { } - - /// - /// Indicates that the method is contained in a type that implements - /// System.ComponentModel.INotifyPropertyChanged interface and this method - /// is used to notify that some property value changed. - /// - /// - /// The method should be non-static and conform to one of the supported signatures: - /// - /// NotifyChanged(string) - /// NotifyChanged(params string[]) - /// NotifyChanged{T}(Expression{Func{T}}) - /// NotifyChanged{T,U}(Expression{Func{T,U}}) - /// SetProperty{T}(ref T, T, string) - /// - /// - /// - /// public class Foo : INotifyPropertyChanged { - /// public event PropertyChangedEventHandler PropertyChanged; - /// - /// [NotifyPropertyChangedInvocator] - /// protected virtual void NotifyChanged(string propertyName) { ... } - /// - /// string _name; - /// - /// public string Name { - /// get { return _name; } - /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } - /// } - /// } - /// - /// Examples of generated notifications: - /// - /// NotifyChanged("Property") - /// NotifyChanged(() => Property) - /// NotifyChanged((VM x) => x.Property) - /// SetProperty(ref myField, value, "Property") - /// - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute - { - public NotifyPropertyChangedInvocatorAttribute() { } - public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) - { - ParameterName = parameterName; - } - - [CanBeNull] public string ParameterName { get; } - } - /// /// Describes dependency between method input and output. /// @@ -369,75 +167,6 @@ public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStat public bool ForceFullStates { get; } } - - /// - /// Indicates whether the marked element should be localized. - /// - /// - /// [LocalizationRequiredAttribute(true)] - /// class Foo { - /// string str = "my string"; // Warning: Localizable string - /// } - /// - [AttributeUsage(AttributeTargets.All)] - internal sealed class LocalizationRequiredAttribute : Attribute - { - public LocalizationRequiredAttribute() : this(true) { } - - public LocalizationRequiredAttribute(bool required) - { - Required = required; - } - - public bool Required { get; } - } - - /// - /// Indicates that the value of the marked type (or its derivatives) - /// cannot be compared using '==' or '!=' operators and Equals() - /// should be used instead. However, using '==' or '!=' for comparison - /// with null is always permitted. - /// - /// - /// [CannotApplyEqualityOperator] - /// class NoEquality { } - /// - /// class UsesNoEquality { - /// void Test() { - /// var ca1 = new NoEquality(); - /// var ca2 = new NoEquality(); - /// if (ca1 != null) { // OK - /// bool condition = ca1 == ca2; // Warning - /// } - /// } - /// } - /// - [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] - internal sealed class CannotApplyEqualityOperatorAttribute : Attribute { } - - /// - /// When applied to a target attribute, specifies a requirement for any type marked - /// with the target attribute to implement or inherit specific type or types. - /// - /// - /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement - /// class ComponentAttribute : Attribute { } - /// - /// [Component] // ComponentAttribute requires implementing IComponent interface - /// class MyComponent : IComponent { } - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - [BaseTypeRequired(typeof(Attribute))] - internal sealed class BaseTypeRequiredAttribute : Attribute - { - public BaseTypeRequiredAttribute([NotNull] Type baseType) - { - BaseType = baseType; - } - - [NotNull] public Type BaseType { get; } - } - /// /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), /// so this symbol will be ignored by usage-checking inspections.
@@ -479,7 +208,6 @@ public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTar public ImplicitUseTargetFlags TargetFlags { get; } } - /// /// Can be applied to attributes, type parameters, and parameters of a type assignable from . /// When applied to an attribute, the decorated attribute behaves the same as . @@ -546,403 +274,4 @@ internal enum ImplicitUseTargetFlags /// Entity marked with the attribute and all its members considered used. WithMembers = Itself | Members } - - /// - /// This attribute is intended to mark publicly available API, - /// which should not be removed and so is treated as used. - /// - [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] - [AttributeUsage(AttributeTargets.All, Inherited = false)] - internal sealed class PublicAPIAttribute : Attribute - { - public PublicAPIAttribute() { } - - public PublicAPIAttribute([NotNull] string comment) - { - Comment = comment; - } - - [CanBeNull] public string Comment { get; } - } - - /// - /// Tells the code analysis engine if the parameter is completely handled when the invoked method is on stack. - /// If the parameter is a delegate, indicates that delegate can only be invoked during method execution - /// (the delegate can be invoked zero or multiple times, but not stored to some field and invoked later, - /// when the containing method is no longer on the execution stack). - /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. - /// If is true, the attribute will only takes effect if the method invocation is located under the 'await' expression. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class InstantHandleAttribute : Attribute - { - /// - /// Require the method invocation to be used under the 'await' expression for this attribute to take effect on code analysis engine. - /// Can be used for delegate/enumerable parameters of 'async' methods. - /// - public bool RequireAwait { get; set; } - } - - /// - /// Indicates that a method does not make any observable state changes. - /// The same as System.Diagnostics.Contracts.PureAttribute. - /// - /// - /// [Pure] int Multiply(int x, int y) => x * y; - /// - /// void M() { - /// Multiply(123, 42); // Warning: Return value of pure method is not used - /// } - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class PureAttribute : Attribute { } - - /// - /// Indicates that the return value of the method invocation must be used. - /// - /// - /// Methods decorated with this attribute (in contrast to pure methods) might change state, - /// but make no sense without using their return value.
- /// Similarly to , this attribute - /// will help to detect usages of the method when the return value is not used. - /// Optionally, you can specify a message to use when showing warnings, e.g. - /// [MustUseReturnValue("Use the return value to...")]. - ///
- [AttributeUsage(AttributeTargets.Method)] - internal sealed class MustUseReturnValueAttribute : Attribute - { - public MustUseReturnValueAttribute() { } - - public MustUseReturnValueAttribute([NotNull] string justification) - { - Justification = justification; - } - - [CanBeNull] public string Justification { get; } - } - - /// - /// This annotation allows to enforce allocation-less usage patterns of delegates for performance-critical APIs. - /// When this annotation is applied to the parameter of delegate type, IDE checks the input argument of this parameter: - /// * When lambda expression or anonymous method is passed as an argument, IDE verifies that the passed closure - /// has no captures of the containing local variables and the compiler is able to cache the delegate instance - /// to avoid heap allocations. Otherwise the warning is produced. - /// * IDE warns when method name or local function name is passed as an argument as this always results - /// in heap allocation of the delegate instance. - /// - /// - /// In C# 9.0 code IDE would also suggest to annotate the anonymous function with 'static' modifier - /// to make use of the similar analysis provided by the language/compiler. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class RequireStaticDelegateAttribute : Attribute - { - public bool IsError { get; set; } - } - - /// - /// Indicates the type member or parameter of some type, that should be used instead of all other ways - /// to get the value of that type. This annotation is useful when you have some "context" value evaluated - /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. - /// - /// - /// class Foo { - /// [ProvidesContext] IBarService _barService = ...; - /// - /// void ProcessNode(INode node) { - /// DoSomething(node, node.GetGlobalServices().Bar); - /// // ^ Warning: use value of '_barService' field - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] - internal sealed class ProvidesContextAttribute : Attribute { } - - /// - /// Indicates that a parameter is a path to a file or a folder within a web project. - /// Path can be relative or absolute, starting from web root (~). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class PathReferenceAttribute : Attribute - { - public PathReferenceAttribute() { } - - public PathReferenceAttribute([NotNull, PathReference] string basePath) - { - BasePath = basePath; - } - - [CanBeNull] public string BasePath { get; } - } - - /// - /// An extension method marked with this attribute is processed by code completion - /// as a 'Source Template'. When the extension method is completed over some expression, its source code - /// is automatically expanded like a template at call site. - /// - /// - /// Template method body can contain valid source code and/or special comments starting with '$'. - /// Text inside these comments is added as source code when the template is applied. Template parameters - /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. - /// Use the attribute to specify macros for parameters. - /// - /// - /// In this example, the 'forEach' method is a source template available over all values - /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: - /// - /// [SourceTemplate] - /// public static void forEach<T>(this IEnumerable<T> xs) { - /// foreach (var x in xs) { - /// //$ $END$ - /// } - /// } - /// - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class SourceTemplateAttribute : Attribute { } - - /// - /// Allows specifying a macro for a parameter of a source template. - /// - /// - /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression - /// is defined in the property. When applied on a method, the target - /// template parameter is defined in the property. To apply the macro silently - /// for the parameter, set the property value = -1. - /// - /// - /// Applying the attribute on a source template method: - /// - /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] - /// public static void forEach<T>(this IEnumerable<T> collection) { - /// foreach (var item in collection) { - /// //$ $END$ - /// } - /// } - /// - /// Applying the attribute on a template method parameter: - /// - /// [SourceTemplate] - /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { - /// /*$ var $x$Id = "$newguid$" + x.ToString(); - /// x.DoSomething($x$Id); */ - /// } - /// - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] - internal sealed class MacroAttribute : Attribute - { - /// - /// Allows specifying a macro that will be executed for a source template - /// parameter when the template is expanded. - /// - [CanBeNull] public string Expression { get; set; } - - /// - /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. - /// - /// - /// If the target parameter is used several times in the template, only one occurrence becomes editable; - /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, - /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. - /// - public int Editable { get; set; } - - /// - /// Identifies the target parameter of a source template if the - /// is applied on a template method. - /// - [CanBeNull] public string Target { get; set; } - } - - /// - /// Indicates how method, constructor invocation, or property access - /// over collection type affects the contents of the collection. - /// When applied to a return value of a method indicates if the returned collection - /// is created exclusively for the caller (CollectionAccessType.UpdatedContent) or - /// can be read/updated from outside (CollectionAccessType.Read | CollectionAccessType.UpdatedContent) - /// Use to specify the access type. - /// - /// - /// Using this attribute only makes sense if all collection methods are marked with this attribute. - /// - /// - /// public class MyStringCollection : List<string> - /// { - /// [CollectionAccess(CollectionAccessType.Read)] - /// public string GetFirstString() - /// { - /// return this.ElementAt(0); - /// } - /// } - /// class Test - /// { - /// public void Foo() - /// { - /// // Warning: Contents of the collection is never updated - /// var col = new MyStringCollection(); - /// string x = col.GetFirstString(); - /// } - /// } - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property | AttributeTargets.ReturnValue)] - internal sealed class CollectionAccessAttribute : Attribute - { - public CollectionAccessAttribute(CollectionAccessType collectionAccessType) - { - CollectionAccessType = collectionAccessType; - } - - public CollectionAccessType CollectionAccessType { get; } - } - - /// - /// Provides a value for the to define - /// how the collection method invocation affects the contents of the collection. - /// - [Flags] - internal enum CollectionAccessType - { - /// Method does not use or modify content of the collection. - None = 0, - /// Method only reads content of the collection but does not modify it. - Read = 1, - /// Method can change content of the collection but does not add new elements. - ModifyExistingContent = 2, - /// Method can add new elements to the collection. - UpdatedContent = ModifyExistingContent | 4 - } - - /// - /// Indicates that the marked method is assertion method, i.e. it halts the control flow if - /// one of the conditions is satisfied. To set the condition, mark one of the parameters with - /// attribute. - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class AssertionMethodAttribute : Attribute { } - - /// - /// Indicates the condition parameter of the assertion method. The method itself should be - /// marked by attribute. The mandatory argument of - /// the attribute is the assertion type. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AssertionConditionAttribute : Attribute - { - public AssertionConditionAttribute(AssertionConditionType conditionType) - { - ConditionType = conditionType; - } - - public AssertionConditionType ConditionType { get; } - } - - /// - /// Specifies assertion type. If the assertion method argument satisfies the condition, - /// then the execution continues. Otherwise, execution is assumed to be halted. - /// - internal enum AssertionConditionType - { - /// Marked parameter should be evaluated to true. - IS_TRUE = 0, - /// Marked parameter should be evaluated to false. - IS_FALSE = 1, - /// Marked parameter should be evaluated to null value. - IS_NULL = 2, - /// Marked parameter should be evaluated to not null value. - IS_NOT_NULL = 3, - } - - /// - /// Indicates that the marked method unconditionally terminates control flow execution. - /// For example, it could unconditionally throw exception. - /// - [Obsolete("Use [ContractAnnotation('=> halt')] instead")] - [AttributeUsage(AttributeTargets.Method)] - internal sealed class TerminatesProgramAttribute : Attribute { } - - /// - /// Indicates that the method is a pure LINQ method, with postponed enumeration (like Enumerable.Select, - /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters - /// of delegate type by analyzing LINQ method chains. - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class LinqTunnelAttribute : Attribute { } - - /// - /// Indicates that IEnumerable passed as a parameter is not enumerated. - /// Use this annotation to suppress the 'Possible multiple enumeration of IEnumerable' inspection. - /// - /// - /// static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class - /// { - /// // custom check for null but no enumeration - /// } - /// - /// void Foo(IEnumerable<string> values) - /// { - /// ThrowIfNull(values, nameof(values)); - /// var x = values.ToList(); // No warnings about multiple enumeration - /// } - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class NoEnumerationAttribute : Attribute { } - - /// - /// Indicates that the marked parameter, field, or property is a regular expression pattern. - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class RegexPatternAttribute : Attribute { } - - /// - /// Language of injected code fragment inside marked by string literal. - /// - internal enum InjectedLanguage - { - CSS, - HTML, - JAVASCRIPT, - JSON, - XML - } - - /// - /// Indicates that the marked parameter, field, or property is accepting a string literal - /// containing code fragment in a language specified by the . - /// - /// - /// void Foo([LanguageInjection(InjectedLanguage.CSS, Prefix = "body{", Suffix = "}")] string cssProps) - /// { - /// // cssProps should only contains a list of CSS properties - /// } - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class LanguageInjectionAttribute : Attribute - { - public LanguageInjectionAttribute(InjectedLanguage injectedLanguage) - { - InjectedLanguage = injectedLanguage; - } - - /// Specify a language of injected code fragment. - public InjectedLanguage InjectedLanguage { get; } - - /// Specify a string that "precedes" injected string literal. - [CanBeNull] public string Prefix { get; set; } - - /// Specify a string that "follows" injected string literal. - [CanBeNull] public string Suffix { get; set; } - } - - /// - /// Prevents the Member Reordering feature from tossing members of the marked class. - /// - /// - /// The attribute must be mentioned in your member reordering patterns. - /// - [AttributeUsage( - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] - internal sealed class NoReorderAttribute : Attribute { } } diff --git a/src/NLog/Internal/ObjectReflectionCache.cs b/src/NLog/Internal/ObjectReflectionCache.cs index 02af6dfd92..3020aefcf9 100644 --- a/src/NLog/Internal/ObjectReflectionCache.cs +++ b/src/NLog/Internal/ObjectReflectionCache.cs @@ -219,9 +219,6 @@ private static bool ConvertSimpleToString(Type objectType) if (typeof(System.IO.Stream).IsAssignableFrom(objectType)) return true; // Skip serializing properties that often throws exceptions - if (typeof(System.Net.IPAddress).IsAssignableFrom(objectType)) - return true; // Skip serializing properties that often throws exceptions - return false; } diff --git a/src/NLog/LayoutRenderers/HostNameLayoutRenderer.cs b/src/NLog/LayoutRenderers/HostNameLayoutRenderer.cs index 681fbdd2dd..7e965310ff 100644 --- a/src/NLog/LayoutRenderers/HostNameLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/HostNameLayoutRenderer.cs @@ -78,14 +78,14 @@ protected override void InitializeLayoutRenderer() } /// - /// Gets the host name and falls back to computer name if not available + /// Resolves the hostname from environment-variables with fallback to /// private static string GetHostName() { return TryLookupValue(() => Environment.GetEnvironmentVariable("HOSTNAME"), "HOSTNAME") - ?? TryLookupValue(() => System.Net.Dns.GetHostName(), "DnsHostName") - ?? TryLookupValue(() => Environment.MachineName, "MachineName") - ?? TryLookupValue(() => Environment.GetEnvironmentVariable("MACHINENAME"), "MachineName"); + ?? TryLookupValue(() => Environment.GetEnvironmentVariable("COMPUTERNAME"), "COMPUTERNAME") + ?? TryLookupValue(() => Environment.GetEnvironmentVariable("MACHINENAME"), "MACHINENAME") + ?? TryLookupValue(() => Environment.MachineName, "MachineName"); } /// diff --git a/tests/NLog.UnitTests/LayoutRenderers/HostNameLayoutRendererTests.cs b/tests/NLog.UnitTests/LayoutRenderers/HostNameLayoutRendererTests.cs index 6675638a87..53a1fbbea1 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/HostNameLayoutRendererTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/HostNameLayoutRendererTests.cs @@ -51,8 +51,9 @@ public void HostNameTest() // Get the actual hostname that the code would use string h = Environment.GetEnvironmentVariable("HOSTNAME") - ?? System.Net.Dns.GetHostName() - ?? Environment.GetEnvironmentVariable("COMPUTERNAME"); + ?? Environment.GetEnvironmentVariable("COMPUTERNAME") + ?? Environment.GetEnvironmentVariable("MACHINENAME") + ?? Environment.MachineName; logFactory.GetLogger("A").Debug("a log message"); logFactory.AssertDebugLastMessage(h + " a log message"); } From 90f0132caa4d7433821103bd89dc6473f1f484a7 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 6 Mar 2025 18:39:57 +0100 Subject: [PATCH 053/224] Extracted NLogTraceListener into NLog.Targets.Trace (#5722) --- Test-XmlFile.ps1 | 2 +- build.ps1 | 1 + run-tests.ps1 | 8 + .../NLog.Targets.ConcurrentFile.csproj | 2 +- src/NLog.Targets.Mail/MailTarget.cs | 1 - src/NLog.Targets.Trace/N.png | Bin 0 -> 1903 bytes .../NLog.Targets.Trace.csproj | 75 ++++++++ .../NLogTraceListener.cs | 15 +- .../Properties/AssemblyInfo.cs | 46 +++++ src/NLog.Targets.Trace/README.md | 25 +++ .../StackTraceUsageUtils.cs | 162 ++++++++++++++++++ .../TraceActivityIdLayoutRenderer.cs | 5 +- .../TraceTarget.cs | 0 src/NLog.sln | 14 ++ src/NLog/Config/AssemblyExtensionTypes.cs | 3 - src/NLog/Config/ConfigurationItemFactory.cs | 12 ++ src/NLog/SetupLoadConfigurationExtensions.cs | 15 -- .../NLog.Targets.Trace.Tests.csproj | 31 ++++ tests/NLog.Targets.Trace.Tests/NLogTests.snk | Bin 0 -> 596 bytes .../NLogTraceListenerTests.cs | 92 ++++++---- tests/NLog.UnitTests/Targets/TargetTests.cs | 10 +- 21 files changed, 443 insertions(+), 76 deletions(-) create mode 100644 src/NLog.Targets.Trace/N.png create mode 100644 src/NLog.Targets.Trace/NLog.Targets.Trace.csproj rename src/{NLog => NLog.Targets.Trace}/NLogTraceListener.cs (98%) create mode 100644 src/NLog.Targets.Trace/Properties/AssemblyInfo.cs create mode 100644 src/NLog.Targets.Trace/README.md create mode 100644 src/NLog.Targets.Trace/StackTraceUsageUtils.cs rename src/{NLog/LayoutRenderers => NLog.Targets.Trace}/TraceActivityIdLayoutRenderer.cs (93%) rename src/{NLog/Targets => NLog.Targets.Trace}/TraceTarget.cs (100%) create mode 100644 tests/NLog.Targets.Trace.Tests/NLog.Targets.Trace.Tests.csproj create mode 100644 tests/NLog.Targets.Trace.Tests/NLogTests.snk rename tests/{NLog.UnitTests => NLog.Targets.Trace.Tests}/NLogTraceListenerTests.cs (82%) diff --git a/Test-XmlFile.ps1 b/Test-XmlFile.ps1 index a01674384b..bac2425902 100644 --- a/Test-XmlFile.ps1 +++ b/Test-XmlFile.ps1 @@ -54,7 +54,7 @@ function Test-XmlFile $pwd = get-location; $testFilesDir = "$pwd\examples\targets\Configuration File" $xsdFilePath = "$pwd\src\NLog\bin\Release\NLog.xsd" -$excludedTests = ("MessageBox","RichTextBox","FormControl","PerfCounter","OutputDebugString","MSMQ","Database","Mail","Network","Chainsaw","NLogViewer","WebService") +$excludedTests = ("MessageBox","RichTextBox","FormControl","PerfCounter","OutputDebugString","MSMQ","Database","Mail","Network","Chainsaw","NLogViewer","WebService","Trace") # Returns true if all selected tests in examples directory are valid $ret = $true Get-ChildItem -Path $testFilesDir -Directory -Exclude $excludedTests | Get-ChildItem -Recurse -File -Filter NLog.config | % { diff --git a/build.ps1 b/build.ps1 index b7a74a3fdf..13ad8f5aec 100644 --- a/build.ps1 +++ b/build.ps1 @@ -40,6 +40,7 @@ create-package 'NLog.Database' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.ConcurrentFile' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.Mail' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.Network' '"net45;net46;netstandard2.0"' +create-package 'NLog.Targets.Trace' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.WebService' '"net45;net46;netstandard2.0"' create-package 'NLog.OutputDebugString' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.RegEx' '"net35;net45;net46;netstandard2.0"' diff --git a/run-tests.ps1 b/run-tests.ps1 index 521b084db5..ee963a07b9 100644 --- a/run-tests.ps1 +++ b/run-tests.ps1 @@ -40,6 +40,10 @@ if ($isWindows -or $Env:WinDir) if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.Trace.Tests/ --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.WebService.Tests/ --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } @@ -91,6 +95,10 @@ else if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.Trace.Tests/ --framework net6.0 --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.WebService.Tests/ --framework net6.0 --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } diff --git a/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj b/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj index b30bdca3ad..4004a33995 100644 --- a/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj +++ b/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj @@ -1,7 +1,7 @@  - net46;netstandard2.0 + net35;net46;netstandard2.0 NLog.Targets.ConcurrentFile NLog diff --git a/src/NLog.Targets.Mail/MailTarget.cs b/src/NLog.Targets.Mail/MailTarget.cs index 8d7e29cbb5..bfd095c5a7 100644 --- a/src/NLog.Targets.Mail/MailTarget.cs +++ b/src/NLog.Targets.Mail/MailTarget.cs @@ -84,7 +84,6 @@ namespace NLog.Targets [Target("Mail")] [Target("Email")] [Target("Smtp")] - [Target("SmtpClient")] public class MailTarget : TargetWithLayoutHeaderAndFooter { private const string RequiredPropertyIsEmptyFormat = "After the processing of the MailTarget's '{0}' property it appears to be empty. The email message will not be sent."; diff --git a/src/NLog.Targets.Trace/N.png b/src/NLog.Targets.Trace/N.png new file mode 100644 index 0000000000000000000000000000000000000000..4b8869e2e6d147f32c525bce0c5937d52e3dd1c5 GIT binary patch literal 1903 zcmV-#2ax!QP)1^@s6%GAg-00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGxhX4Q_hXIe}@nrx202p*dSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E;VI^GGzb&2H;6VK~#8N?OLIBBsUN|7xtDvn zmm4pq(?6$MhfVck%klVjI(+-%^z!uE>Gk2u>FxJlw7x~WJ$})7zCC|E9bRAO3k22C zIR;XQ7(Wbr&NBaL?m1|_Q4%(5<}F zSDm6c^(sg02601q7YrX#6wP@oYHZ@{P&2$rw?PAC^ETD2nPuf#W}p5|83+N z;H)@rU)*V@PeaiuRpIoQz92>?8t(7>c*7!Z&MC0p!X>sxktpLNA|Oh2|Kpb{3ln1& zW^+!V1)h^r92lhNl=By?dOrdrq9ExODmq2C4~t^h<&?}Nm?Y||Q$PeCYuWt> zJ*G*J7`t1Bzf%%n_T>}@38HZ&r^vuc=urq2S9dkPusGGeZ2RcEiOY1SU}9PqoT4jl zM3hW1@gD7GKorB{a-O1jcRU1hPaktj*(pQ{D~7i0*@(^|4)15=^l?E@I~R%PI7K?b zq!8ES)_Y;rXZu;UekSweJ+)K6t0`5a{-i`$n~1@X})Y08kt&J|c<< z(nwX0RB;5q*WMpFeE`fkaM-+kq9{#qs9(q_IEp($OtA1ipvN@5t6LCGDQrV*AD%OF znI?(?okQsXr?B;zT!k2((f9pb^f!3=bb=t3+%oO-an*HV)kG`DaTKJ*#qXDPOxt_q z(+69}gn+QlM`x_Y(0I5!%gIrZVM2~+en~&;GJrsUO9b$sX0Y$2&D^7 z0a+y<#qev?_}YE`wIoc-Q6r}hJH#Pv({hRqV9F0~pI9@b4Zo7JJ|gt*+r?+se*1ve zW~a1{>`TyHGMrNc&H7enB9+`H7}-85xbtubhj~t+PKR>JiU{!@1S{p2$}@u1L@U_m zl=#~RoeM^s!tPkaK_skh*4Gxa?-ZXq{q~uYXu}p7ataf-_$-JCLE<2;ZjWnd-zgFJ zc$51^wO;U2x_t(mg4J*#oVeE9X0g^9+r46bxrDXlhGv*pJr}+E{;I-@YLB|Wnh%lN{3QqAq zjCUf~b&Iq6hCTU3w~xCdV6#)S1F~F$3{-Lo5-k(Wh_g6+pW4w+*8er=_Mv&V#l`%$ zin36e_hfPVxKkQIBfMy%+V2DUp}6sw?!HqP!UOk>YQ5m4#kL_-MDgtdvbs7>IuSsx zSH#kzwfJez=$yii-{TY(CFYmv_F-aPN77!$=d^C3;*VG+EzFlD}(>pcX3!qKbudVOW7NC#>s%6@s7W~)3w~wU$pYR6H`*xh*SOU zlJk373&A)pz+-ES@w0i$;({0W8P%~8vp2wsNA-J%Lp(O3PV7wBp0SvPh;3ST@13FJ zhqi{`>~<~BgV$zJbfu|7-J@}^OTgs9q!&)1XS#NNk7K?@osA3Ne%1~MFQYhK^z@+4 zI%iyH>kvtxcG&gOLH&KyDpc106}0*JVoD8OMsZt#s*yOZ7s(5_<{}V8ZDbo!kF7oC zk*<$HEZi-8Hg6p-=s-L6PI&-{H;FS5KK5dvYz{m(`rO + + + net35;net46;netstandard2.0 + + NLog.Targets.Trace + NLog + Trace Target for System.Diagnostics.Trace + NLog.Targets.Trace v$(ProductVersion) + $(ProductVersion) + Jarek Kowalski,Kim Christensen,Julian Verdurmen + $([System.DateTime]::Now.ToString(yyyy)) + Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ + + + Trace-Target Docs: + https://github.com/NLog/NLog/wiki/Trace-target + + README.md + NLog;Trace;Diagnostics;logging;log + N.png + https://nlog-project.org/ + BSD-3-Clause + git + https://github.com/NLog/NLog.git + + true + 5.0.0.0 + ..\NLog.snk + true + + true + true + true + true + true + copyused + + + + NLog.Targets.Trace for .NET Standard 2.0 + + + + NLog.Targets.Trace for .NET Framework 4.6 + true + + + + NLog.Targets.Trace for .NET Framework 4.5 + true + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NLog/NLogTraceListener.cs b/src/NLog.Targets.Trace/NLogTraceListener.cs similarity index 98% rename from src/NLog/NLogTraceListener.cs rename to src/NLog.Targets.Trace/NLogTraceListener.cs index c5a4595eca..d4e4420fff 100644 --- a/src/NLog/NLogTraceListener.cs +++ b/src/NLog.Targets.Trace/NLogTraceListener.cs @@ -40,7 +40,6 @@ namespace NLog using System.Diagnostics; using System.Linq; using System.Text; - using System.Xml; using NLog.Internal; /// @@ -312,7 +311,7 @@ public override void TraceData(TraceEventCache eventCache, string source, TraceE } sb.Append('{'); - sb.AppendInvariant(i); + sb.Append(i); sb.Append('}'); } message = sb.ToString(); @@ -471,10 +470,6 @@ protected virtual void ProcessLogEventInfo(LogLevel logLevel, string loggerName, if (arguments?.Length == 1 && arguments[0] is Exception exception) { ev.Exception = exception; - if (message == "{0}") - { - ev.FormatProvider = ExceptionMessageFormatProvider.Instance; - } } ev.Level = _forceLogLevel ?? logLevel; @@ -563,8 +558,8 @@ private void InitAttributes() foreach (DictionaryEntry de in Attributes) { - var key = (string)de.Key; - var value = (string)de.Value; + var key = de.Key?.ToString()?.Trim() ?? string.Empty; + var value = de.Value?.ToString()?.Trim() ?? string.Empty; switch (key.ToUpperInvariant()) { @@ -577,11 +572,11 @@ private void InitAttributes() break; case "AUTOLOGGERNAME": - AutoLoggerName = XmlConvert.ToBoolean(value); + AutoLoggerName = value?.Length == 1 ? value[0] == '1' : bool.Parse(value); break; case "DISABLEFLUSH": - DisableFlush = bool.Parse(value); + DisableFlush = value?.Length == 1 ? value[0] == '1' : bool.Parse(value); break; } } diff --git a/src/NLog.Targets.Trace/Properties/AssemblyInfo.cs b/src/NLog.Targets.Trace/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..5abf4e6c0f --- /dev/null +++ b/src/NLog.Targets.Trace/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +[assembly: AssemblyCulture("")] +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] +[assembly: AllowPartiallyTrustedCallers] +#if !NET35 +[assembly: SecurityRules(SecurityRuleSet.Level1)] +#endif diff --git a/src/NLog.Targets.Trace/README.md b/src/NLog.Targets.Trace/README.md new file mode 100644 index 0000000000..cb32e66f2c --- /dev/null +++ b/src/NLog.Targets.Trace/README.md @@ -0,0 +1,25 @@ +# NLog WebService Target + +NLog Trace Target for forwarding to System.Diagnostic.Trace for each logevent, with support for also subscribing using TraceListener. + +If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) + +See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Trace-target) for available options and examples. + +## Register Extension + +NLog will only recognize type-alias `Trace` when loading from `NLog.config`-file, if having added extension to `NLog.config`-file: + +```xml + + + +``` + +Alternative register from code using [fluent configuration API](https://github.com/NLog/NLog/wiki/Fluent-Configuration-API): + +```csharp +LogManager.Setup().SetupExtensions(ext => { + ext.RegisterTarget(); +}); +``` diff --git a/src/NLog.Targets.Trace/StackTraceUsageUtils.cs b/src/NLog.Targets.Trace/StackTraceUsageUtils.cs new file mode 100644 index 0000000000..5e494522f1 --- /dev/null +++ b/src/NLog.Targets.Trace/StackTraceUsageUtils.cs @@ -0,0 +1,162 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Internal +{ + using System; + using System.Diagnostics; + using System.Reflection; + using NLog.Config; + + /// + /// Utilities for dealing with values. + /// + internal static class StackTraceUsageUtils + { + private static readonly Assembly nlogAssembly = typeof(StackTraceUsageUtils).Assembly; + private static readonly Assembly mscorlibAssembly = typeof(string).Assembly; + private static readonly Assembly systemAssembly = typeof(Debug).Assembly; + + /// + /// Returns the classname from the provided StackFrame (If not from internal assembly) + /// + /// + /// Valid class name, or empty string if assembly was internal + public static string LookupClassNameFromStackFrame(StackFrame stackFrame) + { + var method = stackFrame?.GetMethod(); + if (method != null && LookupAssemblyFromMethod(method) != null) + { + string className = GetStackFrameMethodClassName(method, true, true, true); + if (!string.IsNullOrEmpty(className)) + { + if (!className.StartsWith("System.", StringComparison.Ordinal)) + return className; + } + else + { + className = method.Name ?? string.Empty; + if (className != "lambda_method" && className != "MoveNext") + return className; + } + } + + return string.Empty; + } + + private static string GetStackFrameMethodClassName(MethodBase method, bool includeNameSpace, bool cleanAsyncMoveNext, bool cleanAnonymousDelegates) + { + if (method is null) + return null; + + var callerClassType = method.DeclaringType; + if (cleanAsyncMoveNext + && method.Name == "MoveNext" + && callerClassType?.DeclaringType != null + && callerClassType.Name?.IndexOf('<') == 0 + && callerClassType.Name.IndexOf('>', 1) > 1) + { + // NLog.UnitTests.LayoutRenderers.CallSiteTests+d_3'1 + callerClassType = callerClassType.DeclaringType; + } + + string className = includeNameSpace ? callerClassType?.FullName : callerClassType?.Name; + if (cleanAnonymousDelegates && className?.IndexOf("<>", StringComparison.Ordinal) >= 0) + { + if (!includeNameSpace && callerClassType.DeclaringType != null && callerClassType.IsNested) + { + className = callerClassType.DeclaringType.Name; + } + else + { + // NLog.UnitTests.LayoutRenderers.CallSiteTests+<>c__DisplayClassa + int index = className.IndexOf("+<>", StringComparison.Ordinal); + if (index >= 0) + { + className = className.Substring(0, index); + } + } + } + + if (includeNameSpace && className?.IndexOf('.') == -1) + { + var typeNamespace = GetNamespaceFromTypeAssembly(callerClassType); + className = string.IsNullOrEmpty(typeNamespace) ? className : string.Concat(typeNamespace, ".", className); + } + + return className; + } + + private static string GetNamespaceFromTypeAssembly(Type callerClassType) + { + var classAssembly = callerClassType.Assembly; + if (classAssembly != null && classAssembly != mscorlibAssembly && classAssembly != systemAssembly) + { + var assemblyFullName = classAssembly.FullName; + if (assemblyFullName?.IndexOf(',') >= 0 && !assemblyFullName.StartsWith("System.", StringComparison.Ordinal) && !assemblyFullName.StartsWith("Microsoft.", StringComparison.Ordinal)) + { + return assemblyFullName.Substring(0, assemblyFullName.IndexOf(',')); + } + } + + return null; + } + + /// + /// Returns the assembly from the provided StackFrame (If not internal assembly) + /// + /// Valid assembly, or null if assembly was internal + private static Assembly LookupAssemblyFromMethod(MethodBase method) + { + var assembly = method?.DeclaringType?.Assembly ?? method?.Module?.Assembly; + + // skip stack frame if the method declaring type assembly is from hidden assemblies list + if (assembly == nlogAssembly) + { + return null; + } + + if (assembly == mscorlibAssembly) + { + return null; + } + + if (assembly == systemAssembly) + { + return null; + } + + return assembly; + } + } +} diff --git a/src/NLog/LayoutRenderers/TraceActivityIdLayoutRenderer.cs b/src/NLog.Targets.Trace/TraceActivityIdLayoutRenderer.cs similarity index 93% rename from src/NLog/LayoutRenderers/TraceActivityIdLayoutRenderer.cs rename to src/NLog.Targets.Trace/TraceActivityIdLayoutRenderer.cs index 9407e9c552..b3725407de 100644 --- a/src/NLog/LayoutRenderers/TraceActivityIdLayoutRenderer.cs +++ b/src/NLog.Targets.Trace/TraceActivityIdLayoutRenderer.cs @@ -37,7 +37,6 @@ namespace NLog.LayoutRenderers using System.Diagnostics; using System.Globalization; using System.Text; - using NLog.Internal; /// /// A renderer that puts into log a System.Diagnostics trace correlation id. @@ -47,7 +46,7 @@ namespace NLog.LayoutRenderers /// /// Documentation on NLog Wiki [LayoutRenderer("activityid")] - public class TraceActivityIdLayoutRenderer : LayoutRenderer, IStringValueRenderer + public class TraceActivityIdLayoutRenderer : LayoutRenderer { /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) @@ -55,8 +54,6 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) builder.Append(GetStringValue()); } - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) => GetStringValue(); - private static string GetStringValue() { var activityId = GetValue(); diff --git a/src/NLog/Targets/TraceTarget.cs b/src/NLog.Targets.Trace/TraceTarget.cs similarity index 100% rename from src/NLog/Targets/TraceTarget.cs rename to src/NLog.Targets.Trace/TraceTarget.cs diff --git a/src/NLog.sln b/src/NLog.sln index a0a66686bb..ec9422a53f 100644 --- a/src/NLog.sln +++ b/src/NLog.sln @@ -73,6 +73,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.ConcurrentFile EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.ConcurrentFile.Tests", "..\tests\NLog.Targets.ConcurrentFile.Tests\NLog.Targets.ConcurrentFile.Tests.csproj", "{A869B720-AB81-4AA9-94B4-12C5CF490FFE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.Trace", "NLog.Targets.Trace\NLog.Targets.Trace.csproj", "{68CE5BB3-DF9D-4774-9B66-6F7378BC04F5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.Trace.Tests", "..\tests\NLog.Targets.Trace.Tests\NLog.Targets.Trace.Tests.csproj", "{877F835E-4D1E-45D8-95AD-AC2F0E3764C7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -175,6 +179,14 @@ Global {A869B720-AB81-4AA9-94B4-12C5CF490FFE}.Debug|Any CPU.Build.0 = Debug|Any CPU {A869B720-AB81-4AA9-94B4-12C5CF490FFE}.Release|Any CPU.ActiveCfg = Release|Any CPU {A869B720-AB81-4AA9-94B4-12C5CF490FFE}.Release|Any CPU.Build.0 = Release|Any CPU + {68CE5BB3-DF9D-4774-9B66-6F7378BC04F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68CE5BB3-DF9D-4774-9B66-6F7378BC04F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68CE5BB3-DF9D-4774-9B66-6F7378BC04F5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68CE5BB3-DF9D-4774-9B66-6F7378BC04F5}.Release|Any CPU.Build.0 = Release|Any CPU + {877F835E-4D1E-45D8-95AD-AC2F0E3764C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {877F835E-4D1E-45D8-95AD-AC2F0E3764C7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {877F835E-4D1E-45D8-95AD-AC2F0E3764C7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {877F835E-4D1E-45D8-95AD-AC2F0E3764C7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -202,6 +214,8 @@ Global {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {C54C900A-2FAC-4482-BFFB-D4A9F6AB3619} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {A869B720-AB81-4AA9-94B4-12C5CF490FFE} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {68CE5BB3-DF9D-4774-9B66-6F7378BC04F5} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {877F835E-4D1E-45D8-95AD-AC2F0E3764C7} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6B4C8E9F-1E2F-4AB3-993A-8776CFA08DC0} diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index 060677d9e7..03568e4c92 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -129,7 +129,6 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.LayoutRendererFactory.RegisterType("threadname"); factory.LayoutRendererFactory.RegisterType("ticks"); factory.LayoutRendererFactory.RegisterType("time"); - factory.LayoutRendererFactory.RegisterType("activityid"); factory.LayoutRendererFactory.RegisterType("var"); factory.LayoutRendererFactory.RegisterType("cached"); factory.AmbientRendererFactory.RegisterType("Cached"); @@ -202,8 +201,6 @@ public static void RegisterTypes(ConfigurationItemFactory factory) factory.TargetFactory.RegisterType("MethodCall"); factory.TargetFactory.RegisterType("Null"); factory.RegisterType(); - factory.TargetFactory.RegisterType("Trace"); - factory.TargetFactory.RegisterType("TraceSystem"); factory.TargetFactory.RegisterType("AsyncWrapper"); factory.TargetFactory.RegisterType("AutoFlushWrapper"); factory.TargetFactory.RegisterType("BufferingWrapper"); diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index 3af904490c..c671570646 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -366,16 +366,19 @@ private static ConfigurationItemFactory BuildDefaultFactory() [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] private void RegisterExternalItems() { + _layouts.RegisterNamedType("log4jxmleventlayout", "NLog.Layouts.Log4JXmlEventLayout, NLog.Targets.Network"); #if !NET35 && !NET40 _layouts.RegisterNamedType("microsoftconsolejsonlayout", "NLog.Extensions.Logging.MicrosoftConsoleJsonLayout, NLog.Extensions.Logging"); _layoutRenderers.RegisterNamedType("configsetting", "NLog.Extensions.Logging.ConfigSettingLayoutRenderer, NLog.Extensions.Logging"); _layoutRenderers.RegisterNamedType("microsoftconsolelayout", "NLog.Extensions.Logging.MicrosoftConsoleLayoutRenderer, NLog.Extensions.Logging"); #endif + _layoutRenderers.RegisterNamedType("log4jxmlevent", "NLog.LayoutRenderers.Log4JXmlEventLayoutRenderer, NLog.Targets.Network"); _layoutRenderers.RegisterNamedType("performancecounter", "NLog.LayoutRenderers.PerformanceCounterLayoutRenderer, NLog.PerformanceCounter"); _layoutRenderers.RegisterNamedType("registry", "NLog.LayoutRenderers.RegistryLayoutRenderer, NLog.WindowsRegistry"); _layoutRenderers.RegisterNamedType("windows-identity", "NLog.LayoutRenderers.WindowsIdentityLayoutRenderer, NLog.WindowsIdentity"); _layoutRenderers.RegisterNamedType("rtblink", "NLog.Windows.Forms.RichTextBoxLinkLayoutRenderer, NLog.Windows.Forms"); _layoutRenderers.RegisterNamedType("activity", "NLog.LayoutRenderers.ActivityTraceLayoutRenderer, NLog.DiagnosticSource"); + _layoutRenderers.RegisterNamedType("activityid", "NLog.LayoutRenderers.TraceActivityIdLayoutRenderer, NLog.Targets.Trace"); _targets.RegisterNamedType("diagnosticlistener", "NLog.Targets.DiagnosticListenerTarget, NLog.DiagnosticSource"); _targets.RegisterNamedType("database", "NLog.Targets.DatabaseTarget, NLog.Database"); #if NETSTANDARD @@ -384,11 +387,20 @@ private void RegisterExternalItems() _targets.RegisterNamedType("impersonatingwrapper", "NLog.Targets.Wrappers.ImpersonatingTargetWrapper, NLog.WindowsIdentity"); _targets.RegisterNamedType("logreceiverservice", "NLog.Targets.LogReceiverWebServiceTarget, NLog.Wcf"); _targets.RegisterNamedType("outputdebugstring", "NLog.Targets.OutputDebugStringTarget, NLog.OutputDebugString"); + _targets.RegisterNamedType("network", "NLog.Targets.NetworkTarget, NLog.Targets.Network"); + _targets.RegisterNamedType("chainsaw", "NLog.Targets.ChainsawTarget, NLog.Targets.Network"); + _targets.RegisterNamedType("nlogviewer", "NLog.Targets.ChainsawTarget, NLog.Targets.Network"); + _targets.RegisterNamedType("mail", "NLog.Targets.MailTarget, NLog.Targets.Mail"); + _targets.RegisterNamedType("email", "NLog.Targets.MailTarget, NLog.Targets.Mail"); + _targets.RegisterNamedType("smtp", "NLog.Targets.MailTarget, NLog.Targets.Mail"); _targets.RegisterNamedType("performancecounter", "NLog.Targets.PerformanceCounterTarget, NLog.PerformanceCounter"); _targets.RegisterNamedType("richtextbox", "NLog.Windows.Forms.RichTextBoxTarget, NLog.Windows.Forms"); _targets.RegisterNamedType("messagebox", "NLog.Windows.Forms.MessageBoxTarget, NLog.Windows.Forms"); _targets.RegisterNamedType("formcontrol", "NLog.Windows.Forms.FormControlTarget, NLog.Windows.Forms"); _targets.RegisterNamedType("toolstripitem", "NLog.Windows.Forms.ToolStripItemTarget, NLog.Windows.Forms"); + _targets.RegisterNamedType("trace", "NLog.Targets.TraceTarget, NLog.Targets.Trace"); + _targets.RegisterNamedType("tracesystem", "NLog.Targets.TraceTarget, NLog.Targets.Trace"); + _targets.RegisterNamedType("webservice", "NLog.Targets.WebServiceTarget, NLog.Targets.WebService"); } } } diff --git a/src/NLog/SetupLoadConfigurationExtensions.cs b/src/NLog/SetupLoadConfigurationExtensions.cs index 4b3cc5dd5b..736dc4d1df 100644 --- a/src/NLog/SetupLoadConfigurationExtensions.cs +++ b/src/NLog/SetupLoadConfigurationExtensions.cs @@ -522,21 +522,6 @@ public static ISetupConfigurationTargetBuilder WriteToColoredConsole(this ISetup return configBuilder.WriteTo(consoleTarget); } - /// - /// Write to - /// - /// - /// Override the default Layout for output - /// Force use independent of - public static ISetupConfigurationTargetBuilder WriteToTrace(this ISetupConfigurationTargetBuilder configBuilder, Layout layout = null, bool rawWrite = true) - { - var traceTarget = new TraceTarget(); - traceTarget.RawWrite = rawWrite; - if (layout != null) - traceTarget.Layout = layout; - return configBuilder.WriteTo(traceTarget); - } - /// /// Write to /// diff --git a/tests/NLog.Targets.Trace.Tests/NLog.Targets.Trace.Tests.csproj b/tests/NLog.Targets.Trace.Tests/NLog.Targets.Trace.Tests.csproj new file mode 100644 index 0000000000..fc11970e7a --- /dev/null +++ b/tests/NLog.Targets.Trace.Tests/NLog.Targets.Trace.Tests.csproj @@ -0,0 +1,31 @@ + + + + 17.0 + net462 + net462;net6.0 + + false + + NLogTests.snk + false + true + true + + Full + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/tests/NLog.Targets.Trace.Tests/NLogTests.snk b/tests/NLog.Targets.Trace.Tests/NLogTests.snk new file mode 100644 index 0000000000000000000000000000000000000000..f168e4dbecfd20aae45a58ff7c8146fd9437ca8c GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098+j;l|->ro!-M_v|L{!{P{!ND{K0P(6c zd-FqtRo5AlvWp)3?L^4gdYGOJw(uLvUU=}ZRnrkvZ)4sHmYxL91Vs-+gH5^l3FT%~ zT4&@aA_Hh(2U-<&=?)x2)II__B*H2}e}!2p6a#Iu;d48bO^8HhTl;j8?*d<1a_R|q8E50kv?eP0U3&ZlGn@^pP7iWL$P<_mA z`9zel$Knfm4TsO>!T$Xq=dg4ToMpb)#Ve)# literal 0 HcmV?d00001 diff --git a/tests/NLog.UnitTests/NLogTraceListenerTests.cs b/tests/NLog.Targets.Trace.Tests/NLogTraceListenerTests.cs similarity index 82% rename from tests/NLog.UnitTests/NLogTraceListenerTests.cs rename to tests/NLog.Targets.Trace.Tests/NLogTraceListenerTests.cs index 042fb000b0..96e4935817 100644 --- a/tests/NLog.UnitTests/NLogTraceListenerTests.cs +++ b/tests/NLog.Targets.Trace.Tests/NLogTraceListenerTests.cs @@ -32,22 +32,31 @@ // #define DEBUG +#define TRACE -namespace NLog.UnitTests +namespace NLog.Targets.Trace { using System; using System.Diagnostics; using System.Globalization; using System.Threading; using NLog.Config; + using NLog.LayoutRenderers; using Xunit; - public sealed class NLogTraceListenerTests : NLogTestBase, IDisposable + public sealed class NLogTraceListenerTests : IDisposable { private readonly CultureInfo previousCultureInfo; public NLogTraceListenerTests() { + LogManager.ThrowExceptions = true; + LogManager.Setup().SetupExtensions(ext => + { + ext.RegisterLayoutRenderer(); + ext.RegisterTarget(); + }); + previousCultureInfo = Thread.CurrentThread.CurrentCulture; // set the culture info with the decimal separator (comma) different from InvariantCulture separator (point) Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); @@ -74,16 +83,16 @@ public void TraceWriteTest() Trace.Listeners.Add(new NLogTraceListener { Name = "Logger1" }); Trace.Write("Hello"); - AssertDebugLastMessage("debug", "Logger1 Debug Hello "); + AssertDebugLastMessage("Logger1 Debug Hello "); Trace.Write("Hello", "Cat1"); - AssertDebugLastMessage("debug", "Logger1 Debug Cat1: Hello "); + AssertDebugLastMessage("Logger1 Debug Cat1: Hello "); Trace.Write(3.1415); - AssertDebugLastMessage("debug", $"Logger1 Debug {3.1415} "); + AssertDebugLastMessage($"Logger1 Debug {3.1415} "); Trace.Write(3.1415, "Cat2"); - AssertDebugLastMessage("debug", $"Logger1 Debug Cat2: {3.1415} "); + AssertDebugLastMessage($"Logger1 Debug Cat2: {3.1415} "); } [Fact] @@ -101,16 +110,16 @@ public void TraceWriteLineTest() Trace.Listeners.Add(new NLogTraceListener { Name = "Logger1" }); Trace.WriteLine("Hello"); - AssertDebugLastMessage("debug", "Logger1 Debug Hello "); + AssertDebugLastMessage("Logger1 Debug Hello "); Trace.WriteLine("Hello", "Cat1"); - AssertDebugLastMessage("debug", "Logger1 Debug Cat1: Hello "); + AssertDebugLastMessage("Logger1 Debug Cat1: Hello "); Trace.WriteLine(3.1415); - AssertDebugLastMessage("debug", $"Logger1 Debug {3.1415} "); + AssertDebugLastMessage($"Logger1 Debug {3.1415} "); Trace.WriteLine(3.1415, "Cat2"); - AssertDebugLastMessage("debug", $"Logger1 Debug Cat2: {3.1415} "); + AssertDebugLastMessage($"Logger1 Debug Cat2: {3.1415} "); } [Fact] @@ -128,7 +137,7 @@ public void TraceWriteNonDefaultLevelTest() Trace.Listeners.Add(new NLogTraceListener { Name = "Logger1", DefaultLogLevel = LogLevel.Trace }); Trace.Write("Hello"); - AssertDebugLastMessage("debug", "Logger1 Trace Hello "); + AssertDebugLastMessage("Logger1 Trace Hello "); } [Fact] @@ -161,10 +170,10 @@ public void TraceFailTest() Trace.Listeners.Add(new NLogTraceListener { Name = "Logger1" }); Trace.Fail("Message"); - AssertDebugLastMessage("debug", "Logger1 Error Message Error"); + AssertDebugLastMessage("Logger1 Error Message Error"); Trace.Fail("Message", "Detailed Message"); - AssertDebugLastMessage("debug", "Logger1 Error Message Detailed Message Error"); + AssertDebugLastMessage("Logger1 Error Message Detailed Message Error"); } [Fact] @@ -182,7 +191,7 @@ public void AutoLoggerNameTest() Trace.Listeners.Add(new NLogTraceListener { Name = "Logger1", AutoLoggerName = true }); Trace.Write("Hello"); - AssertDebugLastMessage("debug", GetType().FullName + " Debug Hello "); + AssertDebugLastMessage(GetType().FullName + " Debug Hello "); } [Fact] @@ -200,10 +209,10 @@ public void TraceDataTests() ts.Listeners.Add(new NLogTraceListener { Name = "Logger1", DefaultLogLevel = LogLevel.Trace }); ts.TraceData(TraceEventType.Critical, 123, 42); - AssertDebugLastMessage("debug", "MySource1 Fatal 42 123 Critical"); + AssertDebugLastMessage("MySource1 Fatal 42 123 Critical"); ts.TraceData(TraceEventType.Critical, 145, 42, 3.14, "foo"); - AssertDebugLastMessage("debug", $"MySource1 Fatal 42, {3.14.ToString(CultureInfo.CurrentCulture)}, foo 145 Critical"); + AssertDebugLastMessage($"MySource1 Fatal 42, {3.14.ToString(CultureInfo.CurrentCulture)}, foo 145 Critical"); } #if MONO @@ -225,10 +234,10 @@ public void LogInformationTest() ts.Listeners.Add(new NLogTraceListener { Name = "Logger1", DefaultLogLevel = LogLevel.Trace }); ts.TraceInformation("Quick brown fox"); - AssertDebugLastMessage("debug", "MySource1 Info Quick brown fox Information"); + AssertDebugLastMessage("MySource1 Info Quick brown fox Information"); ts.TraceInformation("Mary had {0} lamb", "a little"); - AssertDebugLastMessage("debug", "MySource1 Info Mary had a little lamb Information"); + AssertDebugLastMessage("MySource1 Info Mary had a little lamb Information"); } [Fact] @@ -246,28 +255,28 @@ public void TraceEventTests() ts.Listeners.Add(new NLogTraceListener { Name = "Logger1", DefaultLogLevel = LogLevel.Trace }); ts.TraceEvent(TraceEventType.Information, 123, "Quick brown {0} jumps over the lazy {1}.", "fox", "dog"); - AssertDebugLastMessage("debug", "MySource1 Info Quick brown fox jumps over the lazy dog. 123 Information"); + AssertDebugLastMessage("MySource1 Info Quick brown fox jumps over the lazy dog. 123 Information"); ts.TraceEvent(TraceEventType.Information, 123); - AssertDebugLastMessage("debug", "MySource1 Info 123 Information"); + AssertDebugLastMessage("MySource1 Info 123 Information"); ts.TraceEvent(TraceEventType.Verbose, 145, "Bar"); - AssertDebugLastMessage("debug", "MySource1 Trace Bar 145 "); + AssertDebugLastMessage("MySource1 Trace Bar 145 "); ts.TraceEvent(TraceEventType.Error, 145, "Foo"); - AssertDebugLastMessage("debug", "MySource1 Error Foo 145 Error"); + AssertDebugLastMessage("MySource1 Error Foo 145 Error"); ts.TraceEvent(TraceEventType.Suspend, 145, "Bar"); - AssertDebugLastMessage("debug", "MySource1 Debug Bar 145 Suspend"); + AssertDebugLastMessage("MySource1 Debug Bar 145 Suspend"); ts.TraceEvent(TraceEventType.Resume, 145, "Foo"); - AssertDebugLastMessage("debug", "MySource1 Debug Foo 145 Resume"); + AssertDebugLastMessage("MySource1 Debug Foo 145 Resume"); ts.TraceEvent(TraceEventType.Warning, 145, "Bar"); - AssertDebugLastMessage("debug", "MySource1 Warn Bar 145 Warning"); + AssertDebugLastMessage("MySource1 Warn Bar 145 Warning"); ts.TraceEvent(TraceEventType.Critical, 145, "Foo"); - AssertDebugLastMessage("debug", "MySource1 Fatal Foo 145 Critical"); + AssertDebugLastMessage("MySource1 Fatal Foo 145 Critical"); } #if MONO @@ -290,10 +299,10 @@ public void ForceLogLevelTest() // force all logs to be Warn, DefaultLogLevel has no effect on TraceSource ts.TraceInformation("Quick brown fox"); - AssertDebugLastMessage("debug", "MySource1 Warn Quick brown fox Information"); + AssertDebugLastMessage("MySource1 Warn Quick brown fox Information"); ts.TraceInformation("Mary had {0} lamb", "a little"); - AssertDebugLastMessage("debug", "MySource1 Warn Mary had a little lamb Information"); + AssertDebugLastMessage("MySource1 Warn Mary had a little lamb Information"); } [Fact] @@ -312,10 +321,10 @@ public void FilterTraceTest() // force all logs to be Warn, DefaultLogLevel has no effect on TraceSource ts.TraceEvent(TraceEventType.Error, 0, "Quick brown fox"); - AssertDebugLastMessage("debug", "MySource1 Warn Quick brown fox Error"); + AssertDebugLastMessage("MySource1 Warn Quick brown fox Error"); ts.TraceInformation("Mary had {0} lamb", "a little"); - AssertDebugLastMessage("debug", "MySource1 Warn Quick brown fox Error"); + AssertDebugLastMessage("MySource1 Warn Quick brown fox Error"); } [Fact] @@ -335,13 +344,13 @@ public void GlobalAllFilterTraceTest() "); Trace.WriteLine("Quick brown fox"); - AssertDebugLastMessage("debug", "Logger1 Debug Quick brown fox "); + AssertDebugLastMessage("Logger1 Debug Quick brown fox "); Trace.WriteLine(new ArgumentException("Mary had a little lamb")); - AssertDebugLastMessage("debug", "Logger1 Debug System.ArgumentException: Mary had a little lamb "); + AssertDebugLastMessage("Logger1 Debug System.ArgumentException: Mary had a little lamb "); Trace.Write("Quick brown fox"); - AssertDebugLastMessage("debug", "Logger1 Debug Quick brown fox "); + AssertDebugLastMessage("Logger1 Debug Quick brown fox "); Trace.Write(new ArgumentException("Mary had a little lamb")); - AssertDebugLastMessage("debug", "Logger1 Debug System.ArgumentException: Mary had a little lamb "); + AssertDebugLastMessage("Logger1 Debug System.ArgumentException: Mary had a little lamb "); Trace.Flush(); } finally @@ -389,7 +398,7 @@ public void TraceTargetWriteLineTest() { var logger = new LogFactory().Setup().LoadConfiguration(builder => { - builder.ForLogger().WriteToTrace(layout: "${logger} ${level} ${message} ${event-properties:EventID} ${event-properties:EventType}", rawWrite: true); + builder.ForLogger().WriteTo(new TraceTarget() { Layout = "${logger} ${level} ${message} ${event-properties:EventID} ${event-properties:EventType}", RawWrite = true }); }).GetLogger("MySource1"); var sw = new System.IO.StringWriter(); @@ -463,6 +472,19 @@ public void TraceTargetEnableTraceFailTest(bool enableTraceFail) } } + private static void AssertDebugLastMessage(string msg) + { + var debugTarget = LogManager.Configuration.FindTargetByName("debug"); + Assert.Equal(msg, debugTarget.LastMessage); + } + + private static void AssertDebugLastMessageContains(string targetName, string msg) + { + var debugTarget = LogManager.Configuration.FindTargetByName("debug"); + Assert.True(debugTarget.LastMessage.Contains(msg), + $"Expected to find '{msg}' in last message value on '{targetName}', but found '{debugTarget.LastMessage}'"); + } + private static TraceSource CreateTraceSource() { var ts = new TraceSource("MySource1", SourceLevels.All); diff --git a/tests/NLog.UnitTests/Targets/TargetTests.cs b/tests/NLog.UnitTests/Targets/TargetTests.cs index 5cb977cf02..c5534c6a4e 100644 --- a/tests/NLog.UnitTests/Targets/TargetTests.cs +++ b/tests/NLog.UnitTests/Targets/TargetTests.cs @@ -535,11 +535,9 @@ public void FlushWithoutInitializeTest() } [Theory] - [InlineData("Trace")] - [InlineData("TRACE")] - [InlineData("TraceSystem")] - [InlineData("TraceSYSTEM")] - [InlineData("Trace--SYSTEM")] + [InlineData("DebugSystem")] + [InlineData("Debug-System")] + [InlineData("DEBUGSYSTEM")] public void TargetAliasShouldWork(string typeName) { LoggingConfiguration c = XmlLoggingConfiguration.CreateFromXmlString($@" @@ -549,7 +547,7 @@ public void TargetAliasShouldWork(string typeName) "); - var t = c.FindTargetByName("d"); + var t = c.FindTargetByName("d"); Assert.NotNull(t); } From c62721cc35a54ea346c22da2436e83d4d0ee5d2f Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 6 Mar 2025 20:32:09 +0100 Subject: [PATCH 054/224] Replaced empty strings with string.Empty (#5723) --- .../FileArchiveModes/FileArchiveModeDate.cs | 2 +- .../FileArchiveModes/FileArchiveModeSequence.cs | 2 +- src/NLog/Common/InternalLogger.cs | 6 +++--- src/NLog/Internal/SimpleStringReader.cs | 2 +- src/NLog/Targets/FileTarget.cs | 4 ++-- src/NLog/Targets/Wrappers/WrapperTargetBase.cs | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDate.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDate.cs index 7b51203185..0649ef2c02 100644 --- a/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDate.cs +++ b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeDate.cs @@ -65,7 +65,7 @@ public override List GetExistingArchiveFiles(string arch protected override DateAndSequenceArchive GenerateArchiveFileInfo(FileInfo archiveFile, FileNameTemplate fileTemplate) { - string archiveFileName = Path.GetFileName(archiveFile.FullName) ?? ""; + string archiveFileName = Path.GetFileName(archiveFile.FullName) ?? string.Empty; string fileNameMask = fileTemplate.ReplacePattern("*"); int lastIndexOfStar = fileNameMask.LastIndexOf('*'); diff --git a/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeSequence.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeSequence.cs index 08712ed0d9..897b786644 100644 --- a/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeSequence.cs +++ b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeSequence.cs @@ -62,7 +62,7 @@ public override bool AttemptCleanupOnInitializeFile(string archiveFilePath, int protected override DateAndSequenceArchive GenerateArchiveFileInfo(FileInfo archiveFile, FileNameTemplate fileTemplate) { - string baseName = Path.GetFileName(archiveFile.FullName) ?? ""; + string baseName = Path.GetFileName(archiveFile.FullName) ?? string.Empty; int trailerLength = fileTemplate.Template.Length - fileTemplate.EndAt; string number = baseName.Substring(fileTemplate.BeginAt, baseName.Length - trailerLength - fileTemplate.BeginAt); int num; diff --git a/src/NLog/Common/InternalLogger.cs b/src/NLog/Common/InternalLogger.cs index adccdc61c6..5c35af7790 100644 --- a/src/NLog/Common/InternalLogger.cs +++ b/src/NLog/Common/InternalLogger.cs @@ -339,7 +339,7 @@ private static string CreateLogLine([CanBeNull] Exception ex, LogLevel level, st level.ToString(), fieldSeparator, fullMessage, - ex != null ? " Exception: " : "", + ex != null ? " Exception: " : string.Empty, ex?.ToString() ?? ""); } else @@ -348,8 +348,8 @@ private static string CreateLogLine([CanBeNull] Exception ex, LogLevel level, st level.ToString(), fieldSeparator, fullMessage, - ex != null ? " Exception: " : "", - ex?.ToString() ?? ""); + ex != null ? " Exception: " : string.Empty, + ex?.ToString() ?? string.Empty); } } diff --git a/src/NLog/Internal/SimpleStringReader.cs b/src/NLog/Internal/SimpleStringReader.cs index c6f523cd52..a30522619f 100644 --- a/src/NLog/Internal/SimpleStringReader.cs +++ b/src/NLog/Internal/SimpleStringReader.cs @@ -79,7 +79,7 @@ internal string CurrentState return BuildCurrentState(done: "INVALID_CURRENT_STATE", current: char.MaxValue, todo: "INVALID_CURRENT_STATE"); } var done = Substring(0, Position); - var todo = ((Position < _text.Length - 1) ? Text.Substring(Position + 1) : ""); + var todo = ((Position < _text.Length - 1) ? Text.Substring(Position + 1) : string.Empty); return BuildCurrentState(done: done, current: current, todo: todo); } } diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index a3e88778be..c015f1ea0a 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -312,7 +312,7 @@ public Layout ArchiveFileName if (simpleLayout.OriginalText.Contains('#')) { - var repairLegacyLayout = simpleLayout.OriginalText.Replace(".{#}", "").Replace("_{#}", "").Replace("-{#}", "").Replace("{#}", "").Replace(".{#", "").Replace("_{#", "").Replace("-{#", "").Replace("{#", "").Replace("#}", "").Replace("#", ""); + var repairLegacyLayout = simpleLayout.OriginalText.Replace(".{#}", string.Empty).Replace("_{#}", "").Replace("-{#}", "").Replace("{#}", "").Replace(".{#", "").Replace("_{#", "").Replace("-{#", "").Replace("{#", "").Replace("#}", "").Replace("#", ""); archiveSuffixFormat = _archiveSuffixFormat ?? _legacySequenceArchiveSuffixFormat; value = new SimpleLayout(repairLegacyLayout); } @@ -1069,7 +1069,7 @@ internal string BuildFullFilePath(string newFileName, int sequenceNumber, DateTi if (!string.IsNullOrEmpty(fileExt)) fileName = fileName.Substring(0, fileName.Length - fileExt.Length); - object fileLastModifiedObj = fileLastModified == default ? "" : (object)fileLastModified; + object fileLastModifiedObj = fileLastModified == default ? string.Empty : (object)fileLastModified; try { newFileName = newFileName + fileName + string.Format(ArchiveSuffixFormat, sequenceNumber, fileLastModifiedObj) + fileExt; diff --git a/src/NLog/Targets/Wrappers/WrapperTargetBase.cs b/src/NLog/Targets/Wrappers/WrapperTargetBase.cs index 215dce99ed..fe93d7890c 100644 --- a/src/NLog/Targets/Wrappers/WrapperTargetBase.cs +++ b/src/NLog/Targets/Wrappers/WrapperTargetBase.cs @@ -69,9 +69,9 @@ private string GenerateTargetToString() if (WrappedTarget is null) return GenerateTargetToString(true); else if (string.IsNullOrEmpty(Name)) - return $"{GenerateTargetToString(true, "")}_{WrappedTarget}"; + return $"{GenerateTargetToString(true, string.Empty)}_{WrappedTarget}"; else - return $"{GenerateTargetToString(true, "")}_{WrappedTarget.GenerateTargetToString(false, Name)}"; + return $"{GenerateTargetToString(true, string.Empty)}_{WrappedTarget.GenerateTargetToString(false, Name)}"; } /// From 9d7fd2e300ba79aa8a269f5b6cb182fd0dbf8e08 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 16 Mar 2025 18:18:19 +0100 Subject: [PATCH 055/224] ConfigurationItemFactory - Lazy initialize factory types until requested by config (#5724) --- src/NLog.Database/DatabaseTarget.cs | 4 +- src/NLog/Config/AssemblyExtensionTypes.cs | 529 ++++++++++++------ src/NLog/Config/AssemblyExtensionTypes.tt | 188 +++++-- src/NLog/Config/ConfigurationItemFactory.cs | 253 ++++++--- src/NLog/Config/Factory.cs | 43 +- src/NLog/Config/IFactory.cs | 4 - src/NLog/Config/LoggingConfigurationParser.cs | 27 + src/NLog/Config/MethodFactory.cs | 23 + src/NLog/ILLink.Descriptors.xml | 3 - .../Internal/Xamarin/PreserveAttribute.cs | 56 -- .../ScopeContextIndentLayoutRenderer.cs | 2 +- .../Wrappers/CachedLayoutRendererWrapper.cs | 6 +- ...ileSystemNormalizeLayoutRendererWrapper.cs | 2 +- .../JsonEncodeLayoutRendererWrapper.cs | 2 +- .../Wrappers/LeftLayoutRendererWrapper.cs | 2 +- .../NoRawValueLayoutRendererWrapper.cs | 2 +- .../ReplaceNewLinesLayoutRendererWrapper.cs | 9 +- .../TrimWhiteSpaceLayoutRendererWrapper.cs | 2 +- src/NLog/Layouts/CSV/CsvLayout.cs | 2 +- src/NLog/Layouts/Layout.cs | 21 +- src/NLog/Layouts/LayoutParser.cs | 16 +- src/NLog/Layouts/SimpleLayout.cs | 125 ++--- src/NLog/LogFactory.cs | 3 +- src/NLog/NLog.csproj | 7 - src/NLog/Properties/AssemblyInfo.cs | 2 - src/NLog/SetupExtensionsBuilderExtensions.cs | 6 +- src/NLog/Targets/FileTarget.cs | 2 +- src/NLog/Targets/TargetWithLayout.cs | 2 +- .../ConcurrentFileTargetTests.cs | 38 +- tests/NLog.UnitTests/ApiTests.cs | 6 - .../Conditions/ConditionParserTests.cs | 4 +- .../Config/ConfigurationItemFactoryTests.cs | 2 +- .../Config/LogFactorySetupTests.cs | 34 +- .../Layouts/LayoutTypedTests.cs | 2 +- .../Layouts/SimpleLayoutOutputTests.cs | 6 +- .../Layouts/SimpleLayoutParserTests.cs | 14 +- .../Layouts/ThreadAgnosticTests.cs | 4 +- .../NLog.UnitTests/Targets/FileTargetTests.cs | 38 +- 38 files changed, 933 insertions(+), 558 deletions(-) delete mode 100644 src/NLog/ILLink.Descriptors.xml delete mode 100644 src/NLog/Internal/Xamarin/PreserveAttribute.cs diff --git a/src/NLog.Database/DatabaseTarget.cs b/src/NLog.Database/DatabaseTarget.cs index 3332042c31..9ad6aab660 100644 --- a/src/NLog.Database/DatabaseTarget.cs +++ b/src/NLog.Database/DatabaseTarget.cs @@ -393,7 +393,7 @@ protected override void InitializeTarget() if (!string.IsNullOrEmpty(cs.ConnectionString?.Trim())) { - ConnectionString = SimpleLayout.Escape(cs.ConnectionString.Trim()); + ConnectionString = Layout.FromLiteral(cs.ConnectionString); } providerName = cs.ProviderName?.Trim() ?? string.Empty; } @@ -450,7 +450,7 @@ private string InitConnectionString(string providerName) } // ConnectionString was overriden by ConnectionString :) - ConnectionString = SimpleLayout.Escape(connectionStringValue.ToString()); + ConnectionString = Layout.FromLiteral(connectionStringValue.ToString()); } } catch (Exception ex) diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index 03568e4c92..fa4b8465af 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -38,199 +38,364 @@ namespace NLog.Config /// internal static class AssemblyExtensionTypes { - public static void RegisterTypes(ConfigurationItemFactory factory) + public static void RegisterTargetTypes(ConfigurationItemFactory factory, bool skipCheckExists) { -#pragma warning disable CS0618 // Type or member is obsolete factory.RegisterTypeProperties(() => null); - factory.RegisterTypeProperties(() => null); - factory.RegisterTypeProperties(() => null); - factory.RegisterTypeProperties(() => null); - factory.RegisterTypeProperties(() => null); - factory.RegisterTypeProperties(() => null); - factory.RegisterTypeProperties(() => null); - factory.RegisterTypeProperties(() => null); - factory.RegisterTypeProperties(() => null); - factory.RegisterTypeProperties(() => null); - factory.RegisterTypeProperties(() => null); - factory.RegisterTypeProperties(() => null); - factory.RegisterTypeProperties(() => null); - factory.RegisterType(); - factory.FilterFactory.RegisterType("when"); - factory.FilterFactory.RegisterType("whenContains"); - factory.FilterFactory.RegisterType("whenEqual"); - factory.FilterFactory.RegisterType("whenNotContains"); - factory.FilterFactory.RegisterType("whenNotEqual"); - factory.FilterFactory.RegisterType("whenRepeated"); - factory.LayoutRendererFactory.RegisterType("all-event-properties"); - factory.LayoutRendererFactory.RegisterType("appdomain"); #if NETFRAMEWORK - factory.LayoutRendererFactory.RegisterType("appsetting"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("EventLog")) + factory.GetTargetFactory().RegisterType("EventLog"); #endif - factory.LayoutRendererFactory.RegisterType("assembly-version"); - factory.LayoutRendererFactory.RegisterType("basedir"); - factory.LayoutRendererFactory.RegisterType("callsite-filename"); - factory.LayoutRendererFactory.RegisterType("callsite"); - factory.LayoutRendererFactory.RegisterType("callsite-linenumber"); - factory.LayoutRendererFactory.RegisterType("counter"); - factory.LayoutRendererFactory.RegisterType("currentdir"); - factory.LayoutRendererFactory.RegisterType("date"); - factory.LayoutRendererFactory.RegisterType("db-null"); - factory.LayoutRendererFactory.RegisterType("dir-separator"); - factory.LayoutRendererFactory.RegisterType("environment"); - factory.LayoutRendererFactory.RegisterType("environment-user"); - factory.LayoutRendererFactory.RegisterType("event-properties"); - factory.LayoutRendererFactory.RegisterType("event-property"); - factory.LayoutRendererFactory.RegisterType("event-context"); - factory.LayoutRendererFactory.RegisterType("exceptiondata"); - factory.LayoutRendererFactory.RegisterType("exception-data"); - factory.LayoutRendererFactory.RegisterType("exception"); - factory.LayoutRendererFactory.RegisterType("file-contents"); - factory.RegisterTypeProperties(() => null); - factory.LayoutRendererFactory.RegisterType("gc"); - factory.LayoutRendererFactory.RegisterType("gdc"); - factory.LayoutRendererFactory.RegisterType("guid"); - factory.LayoutRendererFactory.RegisterType("hostname"); - factory.LayoutRendererFactory.RegisterType("identity"); - factory.LayoutRendererFactory.RegisterType("install-context"); - factory.LayoutRendererFactory.RegisterType("level"); - factory.LayoutRendererFactory.RegisterType("loglevel"); - factory.LayoutRendererFactory.RegisterType("literal"); - factory.RegisterTypeProperties(() => null); - factory.LayoutRendererFactory.RegisterType("loggername"); - factory.LayoutRendererFactory.RegisterType("logger"); - factory.LayoutRendererFactory.RegisterType("longdate"); - factory.LayoutRendererFactory.RegisterType("machinename"); - factory.LayoutRendererFactory.RegisterType("message"); - factory.LayoutRendererFactory.RegisterType("newline"); - factory.LayoutRendererFactory.RegisterType("nlogdir"); - factory.LayoutRendererFactory.RegisterType("processdir"); - factory.LayoutRendererFactory.RegisterType("processid"); - factory.LayoutRendererFactory.RegisterType("processinfo"); - factory.LayoutRendererFactory.RegisterType("processname"); - factory.LayoutRendererFactory.RegisterType("processtime"); - factory.LayoutRendererFactory.RegisterType("scopeindent"); - factory.LayoutRendererFactory.RegisterType("scopenested"); - factory.LayoutRendererFactory.RegisterType("ndc"); - factory.LayoutRendererFactory.RegisterType("ndlc"); - factory.LayoutRendererFactory.RegisterType("scopeproperty"); - factory.LayoutRendererFactory.RegisterType("mdc"); - factory.LayoutRendererFactory.RegisterType("mdlc"); - factory.LayoutRendererFactory.RegisterType("scopetiming"); - factory.LayoutRendererFactory.RegisterType("ndlctiming"); - factory.LayoutRendererFactory.RegisterType("sequenceid"); - factory.LayoutRendererFactory.RegisterType("shortdate"); - factory.LayoutRendererFactory.RegisterType("userApplicationDataDir"); - factory.LayoutRendererFactory.RegisterType("commonApplicationDataDir"); - factory.LayoutRendererFactory.RegisterType("specialfolder"); - factory.LayoutRendererFactory.RegisterType("userLocalApplicationDataDir"); - factory.LayoutRendererFactory.RegisterType("stacktrace"); - factory.LayoutRendererFactory.RegisterType("tempdir"); - factory.LayoutRendererFactory.RegisterType("threadid"); - factory.LayoutRendererFactory.RegisterType("threadname"); - factory.LayoutRendererFactory.RegisterType("ticks"); - factory.LayoutRendererFactory.RegisterType("time"); - factory.LayoutRendererFactory.RegisterType("var"); - factory.LayoutRendererFactory.RegisterType("cached"); - factory.AmbientRendererFactory.RegisterType("Cached"); - factory.AmbientRendererFactory.RegisterType("ClearCache"); - factory.AmbientRendererFactory.RegisterType("CachedSeconds"); - factory.LayoutRendererFactory.RegisterType("filesystem-normalize"); - factory.AmbientRendererFactory.RegisterType("FSNormalize"); - factory.LayoutRendererFactory.RegisterType("json-encode"); - factory.AmbientRendererFactory.RegisterType("JsonEncode"); - factory.LayoutRendererFactory.RegisterType("left"); - factory.AmbientRendererFactory.RegisterType("Truncate"); - factory.LayoutRendererFactory.RegisterType("lowercase"); - factory.AmbientRendererFactory.RegisterType("Lowercase"); - factory.AmbientRendererFactory.RegisterType("ToLower"); - factory.LayoutRendererFactory.RegisterType("norawvalue"); - factory.AmbientRendererFactory.RegisterType("NoRawValue"); - factory.LayoutRendererFactory.RegisterType("Object-Path"); - factory.AmbientRendererFactory.RegisterType("ObjectPath"); - factory.LayoutRendererFactory.RegisterType("onexception"); - factory.LayoutRendererFactory.RegisterType("onhasproperties"); - factory.LayoutRendererFactory.RegisterType("pad"); - factory.AmbientRendererFactory.RegisterType("Padding"); - factory.AmbientRendererFactory.RegisterType("PadCharacter"); - factory.AmbientRendererFactory.RegisterType("FixedLength"); - factory.AmbientRendererFactory.RegisterType("AlignmentOnTruncation"); - factory.LayoutRendererFactory.RegisterType("replace"); - factory.LayoutRendererFactory.RegisterType("replace-newlines"); - factory.AmbientRendererFactory.RegisterType("ReplaceNewLines"); - factory.LayoutRendererFactory.RegisterType("right"); - factory.LayoutRendererFactory.RegisterType("rot13"); - factory.LayoutRendererFactory.RegisterType("substring"); - factory.LayoutRendererFactory.RegisterType("trim-whitespace"); - factory.AmbientRendererFactory.RegisterType("TrimWhiteSpace"); - factory.LayoutRendererFactory.RegisterType("uppercase"); - factory.AmbientRendererFactory.RegisterType("Uppercase"); - factory.AmbientRendererFactory.RegisterType("ToUpper"); - factory.LayoutRendererFactory.RegisterType("url-encode"); - factory.LayoutRendererFactory.RegisterType("whenEmpty"); - factory.AmbientRendererFactory.RegisterType("WhenEmpty"); - factory.LayoutRendererFactory.RegisterType("when"); - factory.AmbientRendererFactory.RegisterType("When"); - factory.LayoutRendererFactory.RegisterType("wrapline"); - factory.AmbientRendererFactory.RegisterType("WrapLine"); - factory.LayoutRendererFactory.RegisterType("xml-encode"); - factory.AmbientRendererFactory.RegisterType("XmlEncode"); - factory.LayoutFactory.RegisterType("CompoundLayout"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("ColoredConsole")) + factory.GetTargetFactory().RegisterType("ColoredConsole"); + factory.RegisterType(); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("Console")) + factory.GetTargetFactory().RegisterType("Console"); + factory.RegisterType(); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("Debugger")) + factory.GetTargetFactory().RegisterType("Debugger"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("DebugSystem")) + factory.GetTargetFactory().RegisterType("DebugSystem"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("Debug")) + factory.GetTargetFactory().RegisterType("Debug"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("File")) + factory.GetTargetFactory().RegisterType("File"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("Memory")) + factory.GetTargetFactory().RegisterType("Memory"); + factory.RegisterType(); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("MethodCall")) + factory.GetTargetFactory().RegisterType("MethodCall"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("Null")) + factory.GetTargetFactory().RegisterType("Null"); + factory.RegisterType(); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("AsyncWrapper")) + factory.GetTargetFactory().RegisterType("AsyncWrapper"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("AutoFlushWrapper")) + factory.GetTargetFactory().RegisterType("AutoFlushWrapper"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("BufferingWrapper")) + factory.GetTargetFactory().RegisterType("BufferingWrapper"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("FallbackGroup")) + factory.GetTargetFactory().RegisterType("FallbackGroup"); + factory.RegisterType(); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("FilteringWrapper")) + factory.GetTargetFactory().RegisterType("FilteringWrapper"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("GroupByWrapper")) + factory.GetTargetFactory().RegisterType("GroupByWrapper"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("LimitingWrapper")) + factory.GetTargetFactory().RegisterType("LimitingWrapper"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("PostFilteringWrapper")) + factory.GetTargetFactory().RegisterType("PostFilteringWrapper"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("RandomizeGroup")) + factory.GetTargetFactory().RegisterType("RandomizeGroup"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("RepeatingWrapper")) + factory.GetTargetFactory().RegisterType("RepeatingWrapper"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("RetryingWrapper")) + factory.GetTargetFactory().RegisterType("RetryingWrapper"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("RoundRobinGroup")) + factory.GetTargetFactory().RegisterType("RoundRobinGroup"); + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("SplitGroup")) + factory.GetTargetFactory().RegisterType("SplitGroup"); + } + + public static void RegisterLayoutTypes(ConfigurationItemFactory factory, bool skipCheckExists) + { + factory.RegisterTypeProperties(() => null); + if (skipCheckExists || !factory.GetLayoutFactory().CheckTypeAliasExists("CompoundLayout")) + factory.GetLayoutFactory().RegisterType("CompoundLayout"); factory.RegisterType(); - factory.LayoutFactory.RegisterType("CsvLayout"); - factory.LayoutFactory.RegisterType("JsonArrayLayout"); + if (skipCheckExists || !factory.GetLayoutFactory().CheckTypeAliasExists("CsvLayout")) + factory.GetLayoutFactory().RegisterType("CsvLayout"); + if (skipCheckExists || !factory.GetLayoutFactory().CheckTypeAliasExists("JsonArrayLayout")) + factory.GetLayoutFactory().RegisterType("JsonArrayLayout"); factory.RegisterType(); - factory.LayoutFactory.RegisterType("JsonLayout"); - factory.LayoutFactory.RegisterType("LayoutWithHeaderAndFooter"); - factory.LayoutFactory.RegisterType("SimpleLayout"); + if (skipCheckExists || !factory.GetLayoutFactory().CheckTypeAliasExists("JsonLayout")) + factory.GetLayoutFactory().RegisterType("JsonLayout"); + if (skipCheckExists || !factory.GetLayoutFactory().CheckTypeAliasExists("LayoutWithHeaderAndFooter")) + factory.GetLayoutFactory().RegisterType("LayoutWithHeaderAndFooter"); + if (skipCheckExists || !factory.GetLayoutFactory().CheckTypeAliasExists("SimpleLayout")) + factory.GetLayoutFactory().RegisterType("SimpleLayout"); factory.RegisterType(); factory.RegisterType(); - factory.LayoutFactory.RegisterType("XmlLayout"); - factory.TargetFactory.RegisterType("ColoredConsole"); - factory.RegisterType(); - factory.TargetFactory.RegisterType("Console"); - factory.RegisterType(); - factory.TargetFactory.RegisterType("Debugger"); - factory.TargetFactory.RegisterType("DebugSystem"); - factory.TargetFactory.RegisterType("Debug"); + if (skipCheckExists || !factory.GetLayoutFactory().CheckTypeAliasExists("XmlLayout")) + factory.GetLayoutFactory().RegisterType("XmlLayout"); + } + + public static void RegisterLayoutRendererTypes(ConfigurationItemFactory factory, bool skipCheckExists) + { #if NETFRAMEWORK - factory.TargetFactory.RegisterType("EventLog"); + factory.GetLayoutRendererFactory().RegisterType("appsetting"); #endif - factory.TargetFactory.RegisterType("File"); - factory.TargetFactory.RegisterType("Memory"); - factory.RegisterType(); - factory.TargetFactory.RegisterType("MethodCall"); - factory.TargetFactory.RegisterType("Null"); - factory.RegisterType(); - factory.TargetFactory.RegisterType("AsyncWrapper"); - factory.TargetFactory.RegisterType("AutoFlushWrapper"); - factory.TargetFactory.RegisterType("BufferingWrapper"); - factory.TargetFactory.RegisterType("FallbackGroup"); - factory.RegisterType(); - factory.TargetFactory.RegisterType("FilteringWrapper"); - factory.TargetFactory.RegisterType("GroupByWrapper"); - factory.TargetFactory.RegisterType("LimitingWrapper"); - factory.TargetFactory.RegisterType("PostFilteringWrapper"); - factory.TargetFactory.RegisterType("RandomizeGroup"); - factory.TargetFactory.RegisterType("RepeatingWrapper"); - factory.TargetFactory.RegisterType("RetryingWrapper"); - factory.TargetFactory.RegisterType("RoundRobinGroup"); - factory.TargetFactory.RegisterType("SplitGroup"); - factory.TimeSourceFactory.RegisterType("AccurateLocal"); - factory.TimeSourceFactory.RegisterType("AccurateUTC"); - factory.TimeSourceFactory.RegisterType("FastLocal"); - factory.TimeSourceFactory.RegisterType("FastUTC"); - factory.ConditionMethodFactory.RegisterOneParameter("length", (logEvent, arg1) => NLog.Conditions.ConditionMethods.Length(arg1?.ToString())); - factory.ConditionMethodFactory.RegisterTwoParameters("equals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString())); - factory.ConditionMethodFactory.RegisterTwoParameters("strequals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString())); - factory.ConditionMethodFactory.RegisterThreeParameters("strequals", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); - factory.ConditionMethodFactory.RegisterTwoParameters("contains", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString())); - factory.ConditionMethodFactory.RegisterThreeParameters("contains", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); - factory.ConditionMethodFactory.RegisterTwoParameters("starts-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString())); - factory.ConditionMethodFactory.RegisterThreeParameters("starts-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); - factory.ConditionMethodFactory.RegisterTwoParameters("ends-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString())); - factory.ConditionMethodFactory.RegisterThreeParameters("ends-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("all-event-properties")) + factory.GetLayoutRendererFactory().RegisterType("all-event-properties"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("appdomain")) + factory.GetLayoutRendererFactory().RegisterType("appdomain"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("assembly-version")) + factory.GetLayoutRendererFactory().RegisterType("assembly-version"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("basedir")) + factory.GetLayoutRendererFactory().RegisterType("basedir"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("callsite-filename")) + factory.GetLayoutRendererFactory().RegisterType("callsite-filename"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("callsite")) + factory.GetLayoutRendererFactory().RegisterType("callsite"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("callsite-linenumber")) + factory.GetLayoutRendererFactory().RegisterType("callsite-linenumber"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("counter")) + factory.GetLayoutRendererFactory().RegisterType("counter"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("currentdir")) + factory.GetLayoutRendererFactory().RegisterType("currentdir"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("date")) + factory.GetLayoutRendererFactory().RegisterType("date"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("db-null")) + factory.GetLayoutRendererFactory().RegisterType("db-null"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("dir-separator")) + factory.GetLayoutRendererFactory().RegisterType("dir-separator"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("environment")) + factory.GetLayoutRendererFactory().RegisterType("environment"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("environment-user")) + factory.GetLayoutRendererFactory().RegisterType("environment-user"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("event-properties")) + factory.GetLayoutRendererFactory().RegisterType("event-properties"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("event-property")) + factory.GetLayoutRendererFactory().RegisterType("event-property"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("event-context")) + factory.GetLayoutRendererFactory().RegisterType("event-context"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("exceptiondata")) + factory.GetLayoutRendererFactory().RegisterType("exceptiondata"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("exception-data")) + factory.GetLayoutRendererFactory().RegisterType("exception-data"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("exception")) + factory.GetLayoutRendererFactory().RegisterType("exception"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("file-contents")) + factory.GetLayoutRendererFactory().RegisterType("file-contents"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("gc")) + factory.GetLayoutRendererFactory().RegisterType("gc"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("gdc")) + factory.GetLayoutRendererFactory().RegisterType("gdc"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("guid")) + factory.GetLayoutRendererFactory().RegisterType("guid"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("hostname")) + factory.GetLayoutRendererFactory().RegisterType("hostname"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("identity")) + factory.GetLayoutRendererFactory().RegisterType("identity"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("install-context")) + factory.GetLayoutRendererFactory().RegisterType("install-context"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("level")) + factory.GetLayoutRendererFactory().RegisterType("level"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("loglevel")) + factory.GetLayoutRendererFactory().RegisterType("loglevel"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("literal")) + factory.GetLayoutRendererFactory().RegisterType("literal"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("loggername")) + factory.GetLayoutRendererFactory().RegisterType("loggername"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("logger")) + factory.GetLayoutRendererFactory().RegisterType("logger"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("longdate")) + factory.GetLayoutRendererFactory().RegisterType("longdate"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("machinename")) + factory.GetLayoutRendererFactory().RegisterType("machinename"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("message")) + factory.GetLayoutRendererFactory().RegisterType("message"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("newline")) + factory.GetLayoutRendererFactory().RegisterType("newline"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("nlogdir")) + factory.GetLayoutRendererFactory().RegisterType("nlogdir"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("processdir")) + factory.GetLayoutRendererFactory().RegisterType("processdir"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("processid")) + factory.GetLayoutRendererFactory().RegisterType("processid"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("processinfo")) + factory.GetLayoutRendererFactory().RegisterType("processinfo"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("processname")) + factory.GetLayoutRendererFactory().RegisterType("processname"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("processtime")) + factory.GetLayoutRendererFactory().RegisterType("processtime"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("scopeindent")) + factory.GetLayoutRendererFactory().RegisterType("scopeindent"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("scopenested")) + factory.GetLayoutRendererFactory().RegisterType("scopenested"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("ndc")) + factory.GetLayoutRendererFactory().RegisterType("ndc"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("ndlc")) + factory.GetLayoutRendererFactory().RegisterType("ndlc"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("scopeproperty")) + factory.GetLayoutRendererFactory().RegisterType("scopeproperty"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("mdc")) + factory.GetLayoutRendererFactory().RegisterType("mdc"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("mdlc")) + factory.GetLayoutRendererFactory().RegisterType("mdlc"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("scopetiming")) + factory.GetLayoutRendererFactory().RegisterType("scopetiming"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("ndlctiming")) + factory.GetLayoutRendererFactory().RegisterType("ndlctiming"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("sequenceid")) + factory.GetLayoutRendererFactory().RegisterType("sequenceid"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("shortdate")) + factory.GetLayoutRendererFactory().RegisterType("shortdate"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("userApplicationDataDir")) + factory.GetLayoutRendererFactory().RegisterType("userApplicationDataDir"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("commonApplicationDataDir")) + factory.GetLayoutRendererFactory().RegisterType("commonApplicationDataDir"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("specialfolder")) + factory.GetLayoutRendererFactory().RegisterType("specialfolder"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("userLocalApplicationDataDir")) + factory.GetLayoutRendererFactory().RegisterType("userLocalApplicationDataDir"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("stacktrace")) + factory.GetLayoutRendererFactory().RegisterType("stacktrace"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("tempdir")) + factory.GetLayoutRendererFactory().RegisterType("tempdir"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("threadid")) + factory.GetLayoutRendererFactory().RegisterType("threadid"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("threadname")) + factory.GetLayoutRendererFactory().RegisterType("threadname"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("ticks")) + factory.GetLayoutRendererFactory().RegisterType("ticks"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("time")) + factory.GetLayoutRendererFactory().RegisterType("time"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("var")) + factory.GetLayoutRendererFactory().RegisterType("var"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("cached")) + factory.GetLayoutRendererFactory().RegisterType("cached"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("Cached")) + factory.GetAmbientPropertyFactory().RegisterType("Cached"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("ClearCache")) + factory.GetAmbientPropertyFactory().RegisterType("ClearCache"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("CachedSeconds")) + factory.GetAmbientPropertyFactory().RegisterType("CachedSeconds"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("filesystem-normalize")) + factory.GetLayoutRendererFactory().RegisterType("filesystem-normalize"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("FSNormalize")) + factory.GetAmbientPropertyFactory().RegisterType("FSNormalize"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("json-encode")) + factory.GetLayoutRendererFactory().RegisterType("json-encode"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("JsonEncode")) + factory.GetAmbientPropertyFactory().RegisterType("JsonEncode"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("left")) + factory.GetLayoutRendererFactory().RegisterType("left"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("Truncate")) + factory.GetAmbientPropertyFactory().RegisterType("Truncate"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("lowercase")) + factory.GetLayoutRendererFactory().RegisterType("lowercase"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("Lowercase")) + factory.GetAmbientPropertyFactory().RegisterType("Lowercase"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("ToLower")) + factory.GetAmbientPropertyFactory().RegisterType("ToLower"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("norawvalue")) + factory.GetLayoutRendererFactory().RegisterType("norawvalue"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("NoRawValue")) + factory.GetAmbientPropertyFactory().RegisterType("NoRawValue"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("Object-Path")) + factory.GetLayoutRendererFactory().RegisterType("Object-Path"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("ObjectPath")) + factory.GetAmbientPropertyFactory().RegisterType("ObjectPath"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("onexception")) + factory.GetLayoutRendererFactory().RegisterType("onexception"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("onhasproperties")) + factory.GetLayoutRendererFactory().RegisterType("onhasproperties"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("pad")) + factory.GetLayoutRendererFactory().RegisterType("pad"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("Padding")) + factory.GetAmbientPropertyFactory().RegisterType("Padding"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("PadCharacter")) + factory.GetAmbientPropertyFactory().RegisterType("PadCharacter"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("FixedLength")) + factory.GetAmbientPropertyFactory().RegisterType("FixedLength"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("AlignmentOnTruncation")) + factory.GetAmbientPropertyFactory().RegisterType("AlignmentOnTruncation"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("replace")) + factory.GetLayoutRendererFactory().RegisterType("replace"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("replace-newlines")) + factory.GetLayoutRendererFactory().RegisterType("replace-newlines"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("ReplaceNewLines")) + factory.GetAmbientPropertyFactory().RegisterType("ReplaceNewLines"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("right")) + factory.GetLayoutRendererFactory().RegisterType("right"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("rot13")) + factory.GetLayoutRendererFactory().RegisterType("rot13"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("substring")) + factory.GetLayoutRendererFactory().RegisterType("substring"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("trim-whitespace")) + factory.GetLayoutRendererFactory().RegisterType("trim-whitespace"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("TrimWhiteSpace")) + factory.GetAmbientPropertyFactory().RegisterType("TrimWhiteSpace"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("uppercase")) + factory.GetLayoutRendererFactory().RegisterType("uppercase"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("Uppercase")) + factory.GetAmbientPropertyFactory().RegisterType("Uppercase"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("ToUpper")) + factory.GetAmbientPropertyFactory().RegisterType("ToUpper"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("url-encode")) + factory.GetLayoutRendererFactory().RegisterType("url-encode"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("whenEmpty")) + factory.GetLayoutRendererFactory().RegisterType("whenEmpty"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("WhenEmpty")) + factory.GetAmbientPropertyFactory().RegisterType("WhenEmpty"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("when")) + factory.GetLayoutRendererFactory().RegisterType("when"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("When")) + factory.GetAmbientPropertyFactory().RegisterType("When"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("wrapline")) + factory.GetLayoutRendererFactory().RegisterType("wrapline"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("WrapLine")) + factory.GetAmbientPropertyFactory().RegisterType("WrapLine"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("xml-encode")) + factory.GetLayoutRendererFactory().RegisterType("xml-encode"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("XmlEncode")) + factory.GetAmbientPropertyFactory().RegisterType("XmlEncode"); + } -#pragma warning restore CS0618 // Type or member is obsolete + public static void RegisterFilterTypes(ConfigurationItemFactory factory, bool skipCheckExists) + { + if (skipCheckExists || !factory.GetFilterFactory().CheckTypeAliasExists("when")) + factory.GetFilterFactory().RegisterType("when"); + if (skipCheckExists || !factory.GetFilterFactory().CheckTypeAliasExists("whenContains")) + factory.GetFilterFactory().RegisterType("whenContains"); + if (skipCheckExists || !factory.GetFilterFactory().CheckTypeAliasExists("whenEqual")) + factory.GetFilterFactory().RegisterType("whenEqual"); + if (skipCheckExists || !factory.GetFilterFactory().CheckTypeAliasExists("whenNotContains")) + factory.GetFilterFactory().RegisterType("whenNotContains"); + if (skipCheckExists || !factory.GetFilterFactory().CheckTypeAliasExists("whenNotEqual")) + factory.GetFilterFactory().RegisterType("whenNotEqual"); + if (skipCheckExists || !factory.GetFilterFactory().CheckTypeAliasExists("whenRepeated")) + factory.GetFilterFactory().RegisterType("whenRepeated"); + } + + public static void RegisterTimeSourceTypes(ConfigurationItemFactory factory, bool skipCheckExists) + { + if (skipCheckExists || !factory.GetTimeSourceFactory().CheckTypeAliasExists("AccurateLocal")) + factory.GetTimeSourceFactory().RegisterType("AccurateLocal"); + if (skipCheckExists || !factory.GetTimeSourceFactory().CheckTypeAliasExists("AccurateUTC")) + factory.GetTimeSourceFactory().RegisterType("AccurateUTC"); + if (skipCheckExists || !factory.GetTimeSourceFactory().CheckTypeAliasExists("FastLocal")) + factory.GetTimeSourceFactory().RegisterType("FastLocal"); + if (skipCheckExists || !factory.GetTimeSourceFactory().CheckTypeAliasExists("FastUTC")) + factory.GetTimeSourceFactory().RegisterType("FastUTC"); + } + + public static void RegisterConditionTypes(ConfigurationItemFactory factory, bool skipCheckExists) + { + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("length")) + factory.GetConditionMethodFactory().RegisterOneParameter("length", (logEvent, arg1) => NLog.Conditions.ConditionMethods.Length(arg1?.ToString())); + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("equals")) + factory.GetConditionMethodFactory().RegisterTwoParameters("equals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString())); + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("strequals")) + { + factory.GetConditionMethodFactory().RegisterTwoParameters("strequals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString())); + factory.GetConditionMethodFactory().RegisterThreeParameters("strequals", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); + } + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("contains")) + { + factory.GetConditionMethodFactory().RegisterTwoParameters("contains", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString())); + factory.GetConditionMethodFactory().RegisterThreeParameters("contains", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); + } + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("starts-with")) + { + factory.GetConditionMethodFactory().RegisterTwoParameters("starts-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString())); + factory.GetConditionMethodFactory().RegisterThreeParameters("starts-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); + } + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("ends-with")) + { + factory.GetConditionMethodFactory().RegisterTwoParameters("ends-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString())); + factory.GetConditionMethodFactory().RegisterThreeParameters("ends-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); + } } } } diff --git a/src/NLog/Config/AssemblyExtensionTypes.tt b/src/NLog/Config/AssemblyExtensionTypes.tt index 92212e2def..31225c2ff0 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.tt +++ b/src/NLog/Config/AssemblyExtensionTypes.tt @@ -48,72 +48,98 @@ namespace NLog.Config /// internal static class AssemblyExtensionTypes { - public static void RegisterTypes(ConfigurationItemFactory factory) - { -#pragma warning disable CS0618 // Type or member is obsolete - factory.RegisterTypeProperties(() => null); - factory.RegisterTypeProperties(() => null); <# - var types = typeof(NLog.LogFactory).Assembly.GetTypes().OrderBy(t => t.ToString()); - - var netFramework = new string[] - { + string[] NetFrameworkOnly = new [] { "NLog.LayoutRenderers.AppSettingLayoutRenderer", "NLog.Targets.EventLogTarget", }; - - foreach(var type in types) + Type[] AllTypes = typeof(NLog.LogFactory).Assembly.GetTypes().Where(t => !t.IsAbstract && !t.IsPrimitive && t.IsClass && !t.IsNested && !NetFrameworkOnly.Contains(t.ToString())).OrderBy(t => t.ToString()).ToArray(); +#> + public static void RegisterTargetTypes(ConfigurationItemFactory factory, bool skipCheckExists) + { + factory.RegisterTypeProperties(() => null); +#if NETFRAMEWORK + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("EventLog")) + factory.GetTargetFactory().RegisterType("EventLog"); +#endif +<# + foreach(var type in AllTypes) { - if (type.IsAbstract || type.IsPrimitive || !type.IsClass || type.IsNested) - continue; - - if (netFramework.Contains(type.ToString())) + if (typeof(NLog.Targets.Target).IsAssignableFrom(type)) { + var targetAttributes = type.GetCustomAttributes(false); + foreach (var targetAlias in targetAttributes) + { + var targetAliasName = targetAlias.Name; #> -#if NETFRAMEWORK + if (skipCheckExists || !factory.GetTargetFactory().CheckTypeAliasExists("<#= targetAliasName #>")) + factory.GetTargetFactory().RegisterType<<#= type #>>("<#= targetAliasName #>"); <# + } } - - if (!type.IsPublic) + else if (type.ToString().StartsWith("NLog.Targets")) { var configAttribute = type.GetCustomAttributes(true); if (configAttribute?.Any() == true) { #> - factory.RegisterTypeProperties<<#= type #>>(() => null); + factory.RegisterType<<#= type #>>(); <# } } - else if (typeof(NLog.Targets.Target).IsAssignableFrom(type)) + } +#> + } + + public static void RegisterLayoutTypes(ConfigurationItemFactory factory, bool skipCheckExists) { - var targetAttributes = type.GetCustomAttributes(false); - foreach (var targetAlias in targetAttributes) + factory.RegisterTypeProperties(() => null); +<# + foreach(var type in AllTypes) + { + if (typeof(NLog.Layouts.Layout).IsAssignableFrom(type)) + { + var layoutAttributes = type.GetCustomAttributes(false); + foreach (var layoutAlias in layoutAttributes) { - var targetAliasName = targetAlias.Name; + var layoutAliasName = layoutAlias.Name; #> - factory.TargetFactory.RegisterType<<#= type #>>("<#= targetAliasName #>"); + if (skipCheckExists || !factory.GetLayoutFactory().CheckTypeAliasExists("<#= layoutAliasName #>")) + factory.GetLayoutFactory().RegisterType<<#= type #>>("<#= layoutAliasName #>"); <# } } - else if (typeof(NLog.Layouts.Layout).IsAssignableFrom(type)) + else if (type.ToString().StartsWith("NLog.Layouts")) { - var layoutAttributes = type.GetCustomAttributes(false); - foreach (var layoutAlias in layoutAttributes) + var configAttribute = type.GetCustomAttributes(true); + if (configAttribute?.Any() == true) { - var layoutAliasName = layoutAlias.Name; #> - factory.LayoutFactory.RegisterType<<#= type #>>("<#= layoutAliasName #>"); + factory.RegisterType<<#= type #>>(); <# } } - else if (typeof(NLog.LayoutRenderers.LayoutRenderer).IsAssignableFrom(type)) + } +#> + } + + public static void RegisterLayoutRendererTypes(ConfigurationItemFactory factory, bool skipCheckExists) + { +#if NETFRAMEWORK + factory.GetLayoutRendererFactory().RegisterType("appsetting"); +#endif +<# + foreach(var type in AllTypes) + { + if (typeof(NLog.LayoutRenderers.LayoutRenderer).IsAssignableFrom(type)) { var layoutAttributes = type.GetCustomAttributes(false); foreach (var layoutAlias in layoutAttributes) { var layoutAliasName = layoutAlias.Name; #> - factory.LayoutRendererFactory.RegisterType<<#= type #>>("<#= layoutAliasName #>"); + if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("<#= layoutAliasName #>")) + factory.GetLayoutRendererFactory().RegisterType<<#= type #>>("<#= layoutAliasName #>"); <# } @@ -122,33 +148,43 @@ namespace NLog.Config { var layoutAliasName = layoutAlias.Name; #> - factory.AmbientRendererFactory.RegisterType<<#= type #>>("<#= layoutAliasName #>"); + if (skipCheckExists || !factory.GetAmbientPropertyFactory().CheckTypeAliasExists("<#= layoutAliasName #>")) + factory.GetAmbientPropertyFactory().RegisterType<<#= type #>>("<#= layoutAliasName #>"); <# } } - else if (typeof(NLog.Filters.Filter).IsAssignableFrom(type)) + else if (type.ToString().StartsWith("NLog.LayoutRenderers")) { - var filterAttributes = type.GetCustomAttributes(false); - foreach (var filterAlias in filterAttributes) + var configAttribute = type.GetCustomAttributes(true); + if (configAttribute?.Any() == true) { - var filterName = filterAlias.Name; #> - factory.FilterFactory.RegisterType<<#= type #>>("<#= filterName #>"); + factory.RegisterType<<#= type #>>(); <# } } - else if (typeof(NLog.Time.TimeSource).IsAssignableFrom(type)) + } +#> + } + + public static void RegisterFilterTypes(ConfigurationItemFactory factory, bool skipCheckExists) { - var timeSourceAttribute = type.GetCustomAttributes(false); - foreach (var timeAlias in timeSourceAttribute) +<# + foreach(var type in AllTypes) + { + if (typeof(NLog.Filters.Filter).IsAssignableFrom(type)) + { + var filterAttributes = type.GetCustomAttributes(false); + foreach (var filterAlias in filterAttributes) { - var timeSourceName = timeAlias.Name; + var filterAliasName = filterAlias.Name; #> - factory.TimeSourceFactory.RegisterType<<#= type #>>("<#= timeSourceName #>"); + if (skipCheckExists || !factory.GetFilterFactory().CheckTypeAliasExists("<#= filterAliasName #>")) + factory.GetFilterFactory().RegisterType<<#= type #>>("<#= filterAliasName #>"); <# } } - else + else if (type.ToString().StartsWith("NLog.Filters")) { var configAttribute = type.GetCustomAttributes(true); if (configAttribute?.Any() == true) @@ -158,27 +194,67 @@ namespace NLog.Config <# } } + } +#> + } - if (netFramework.Contains(type.ToString())) + public static void RegisterTimeSourceTypes(ConfigurationItemFactory factory, bool skipCheckExists) + { +<# + foreach(var type in AllTypes) + { + if (typeof(NLog.Time.TimeSource).IsAssignableFrom(type)) { + var timeSourceAttribute = type.GetCustomAttributes(false); + foreach (var timeAlias in timeSourceAttribute) + { + var timeAliasName = timeAlias.Name; #> -#endif + if (skipCheckExists || !factory.GetTimeSourceFactory().CheckTypeAliasExists("<#= timeAliasName #>")) + factory.GetTimeSourceFactory().RegisterType<<#= type #>>("<#= timeAliasName #>"); +<# + } + } + else if (type.ToString().StartsWith("NLog.Time")) + { + var configAttribute = type.GetCustomAttributes(true); + if (configAttribute?.Any() == true) + { +#> + factory.RegisterType<<#= type #>>(); <# + } } } #> - factory.ConditionMethodFactory.RegisterOneParameter("length", (logEvent, arg1) => NLog.Conditions.ConditionMethods.Length(arg1?.ToString())); - factory.ConditionMethodFactory.RegisterTwoParameters("equals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString())); - factory.ConditionMethodFactory.RegisterTwoParameters("strequals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString())); - factory.ConditionMethodFactory.RegisterThreeParameters("strequals", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); - factory.ConditionMethodFactory.RegisterTwoParameters("contains", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString())); - factory.ConditionMethodFactory.RegisterThreeParameters("contains", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); - factory.ConditionMethodFactory.RegisterTwoParameters("starts-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString())); - factory.ConditionMethodFactory.RegisterThreeParameters("starts-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); - factory.ConditionMethodFactory.RegisterTwoParameters("ends-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString())); - factory.ConditionMethodFactory.RegisterThreeParameters("ends-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); + } -#pragma warning restore CS0618 // Type or member is obsolete + public static void RegisterConditionTypes(ConfigurationItemFactory factory, bool skipCheckExists) + { + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("length")) + factory.GetConditionMethodFactory().RegisterOneParameter("length", (logEvent, arg1) => NLog.Conditions.ConditionMethods.Length(arg1?.ToString())); + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("equals")) + factory.GetConditionMethodFactory().RegisterTwoParameters("equals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString())); + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("strequals")) + { + factory.GetConditionMethodFactory().RegisterTwoParameters("strequals", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString())); + factory.GetConditionMethodFactory().RegisterThreeParameters("strequals", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Equals2(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); + } + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("contains")) + { + factory.GetConditionMethodFactory().RegisterTwoParameters("contains", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString())); + factory.GetConditionMethodFactory().RegisterThreeParameters("contains", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.Contains(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); + } + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("starts-with")) + { + factory.GetConditionMethodFactory().RegisterTwoParameters("starts-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString())); + factory.GetConditionMethodFactory().RegisterThreeParameters("starts-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.StartsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); + } + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("ends-with")) + { + factory.GetConditionMethodFactory().RegisterTwoParameters("ends-with", (logEvent, arg1, arg2) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString())); + factory.GetConditionMethodFactory().RegisterThreeParameters("ends-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); + } } } } diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index c671570646..e282a00311 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -52,7 +52,7 @@ namespace NLog.Config /// /// Supports creating item-instance from their type-alias, when parsing NLog configuration /// - public class ConfigurationItemFactory + public sealed class ConfigurationItemFactory { private static ConfigurationItemFactory _defaultInstance; @@ -88,16 +88,16 @@ public ItemFactory(Func> itemProperties, Func class. ///
public ConfigurationItemFactory() - : this(LogManager.LogFactory.ServiceRepository, null) + : this(LogManager.LogFactory.ServiceRepository) { } - internal ConfigurationItemFactory(ServiceRepository serviceRepository, ConfigurationItemFactory globalDefaultFactory) + internal ConfigurationItemFactory(ServiceRepository serviceRepository) { _serviceRepository = Guard.ThrowIfNull(serviceRepository); _targets = new Factory(this); _filters = new Factory(this); - _layoutRenderers = new LayoutRendererFactory(this, globalDefaultFactory?._layoutRenderers); + _layoutRenderers = new LayoutRendererFactory(this); _layouts = new Factory(this); _conditionMethods = new MethodFactory(); _ambientProperties = new Factory(this); @@ -112,6 +112,7 @@ internal ConfigurationItemFactory(ServiceRepository serviceRepository, Configura _ambientProperties, _timeSources, }; + RegisterType(); } /// @@ -123,39 +124,121 @@ internal ConfigurationItemFactory(ServiceRepository serviceRepository, Configura /// public static ConfigurationItemFactory Default { - get => _defaultInstance ?? (_defaultInstance = BuildDefaultFactory()); + get => _defaultInstance ?? (_defaultInstance = new ConfigurationItemFactory(LogManager.LogFactory.ServiceRepository)); set => _defaultInstance = value; } /// /// Gets the factory. /// - public IFactory TargetFactory => _targets; + public IFactory TargetFactory + { + get + { + if (!_targets.Initialized) + _targets.Initialize(skipCheckExists => RegisterAllTargets(skipCheckExists)); + // Targets can depend on filters + if (!_filters.Initialized) + _filters.Initialize(skipCheckExists => RegisterAllFilters(skipCheckExists)); + // Targets can depend on conditions + if (!_conditionMethods.Initialized) + _conditionMethods.Initialize(skipCheckExists => RegisterAllConditionMethods(skipCheckExists)); + return _targets; + } + } + /// /// Gets the factory. /// - public IFactory LayoutFactory => _layouts; + public IFactory LayoutFactory + { + get + { + if (!_layouts.Initialized) + _layouts.Initialize(skipCheckExists => RegisterAllLayouts(skipCheckExists)); + return _layouts; + } + } + /// /// Gets the factory. /// - public IFactory LayoutRendererFactory => _layoutRenderers; + public IFactory LayoutRendererFactory + { + get + { + if (!_layoutRenderers.Initialized) + _layoutRenderers.Initialize(skipCheckExists => RegisterAllLayoutRenderers(skipCheckExists)); + // When-LayoutRenderers depends on conditions + if (!_conditionMethods.Initialized) + _conditionMethods.Initialize(skipCheckExists => RegisterAllConditionMethods(skipCheckExists)); + return _layoutRenderers; + } + } + /// /// Gets the ambient property factory. /// - public IFactory AmbientRendererFactory => _ambientProperties; + public IFactory AmbientRendererFactory + { + get + { + if (!_layoutRenderers.Initialized) + _layoutRenderers.Initialize(skipCheckExists => RegisterAllLayoutRenderers(skipCheckExists)); + // When-LayoutRenderers depends on conditions + if (!_conditionMethods.Initialized) + _conditionMethods.Initialize(skipCheckExists => RegisterAllConditionMethods(skipCheckExists)); + return _ambientProperties; + } + } + /// /// Gets the factory. /// - public IFactory FilterFactory => _filters; + public IFactory FilterFactory + { + get + { + if (!_filters.Initialized) + _filters.Initialize(skipCheckExists => RegisterAllFilters(skipCheckExists)); + // Filters can depend on conditions + if (!_conditionMethods.Initialized) + _conditionMethods.Initialize(skipCheckExists => RegisterAllConditionMethods(skipCheckExists)); + return _filters; + } + } + /// /// Gets the factory. /// - public IFactory TimeSourceFactory => _timeSources; - internal MethodFactory ConditionMethodFactory => _conditionMethods; + public IFactory TimeSourceFactory + { + get + { + if (!_timeSources.Initialized) + _timeSources.Initialize(skipCheckExists => RegisterAllTimeSources(skipCheckExists)); + return _timeSources; + } + } + + internal MethodFactory ConditionMethodFactory + { + get + { + if (!_conditionMethods.Initialized) + _conditionMethods.Initialize(skipCheckExists => RegisterAllConditionMethods(skipCheckExists)); + return _conditionMethods; + } + } internal Factory GetTargetFactory() => _targets; internal Factory GetLayoutFactory() => _layouts; internal LayoutRendererFactory GetLayoutRendererFactory() => _layoutRenderers; + internal Factory GetAmbientPropertyFactory() => _ambientProperties; + internal Factory GetFilterFactory() => _filters; + internal Factory GetTimeSourceFactory() => _timeSources; + internal MethodFactory GetConditionMethodFactory() => _conditionMethods; + internal ICollection ItemTypes { get @@ -282,6 +365,7 @@ internal Dictionary TryGetTypeProperties(Type itemType) return new Dictionary(); #pragma warning disable CS0618 // Type or member is obsolete + InternalLogger.Debug("Object reflection needed to configure instance of type: {0}", itemType); return ResolveTypePropertiesLegacy(itemType); #pragma warning restore CS0618 // Type or member is obsolete } @@ -292,7 +376,6 @@ internal Dictionary TryGetTypeProperties(Type itemType) [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] private Dictionary ResolveTypePropertiesLegacy(Type itemType) { - InternalLogger.Debug("Object reflection needed to configure instance of type: {0}", itemType); Dictionary properties = itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase); lock (SyncRoot) { @@ -314,6 +397,7 @@ internal bool TryCreateInstance(Type itemType, out object instance) if (itemCreator is null) { #pragma warning disable CS0618 // Type or member is obsolete + InternalLogger.Debug("Object reflection needed to create instance of type: {0}", itemType); instance = ResolveCreateInstanceLegacy(itemType); #pragma warning restore CS0618 // Type or member is obsolete } @@ -331,7 +415,6 @@ internal bool TryCreateInstance(Type itemType, out object instance) [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] private object ResolveCreateInstanceLegacy(Type itemType) { - InternalLogger.Debug("Object reflection needed to create instance of type: {0}", itemType); Dictionary properties = null; var itemProperties = new Func>(() => properties ?? (properties = itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase))); var itemFactory = new ItemFactory(itemProperties, () => Activator.CreateInstance(itemType)); @@ -343,64 +426,108 @@ private object ResolveCreateInstanceLegacy(Type itemType) return itemFactory.ItemCreator.Invoke(); } - /// - /// Builds the default configuration item factory. - /// - /// Default factory. - private static ConfigurationItemFactory BuildDefaultFactory() + private void RegisterAllTargets(bool skipCheckExists) { - var factory = new ConfigurationItemFactory(LogManager.LogFactory.ServiceRepository, null); - lock (SyncRoot) - { - AssemblyExtensionTypes.RegisterTypes(factory); + AssemblyExtensionTypes.RegisterTargetTypes(this, skipCheckExists); #pragma warning disable CS0618 // Type or member is obsolete - factory.RegisterExternalItems(); + if (skipCheckExists || !_targets.CheckTypeAliasExists("diagnosticlistener")) + _targets.RegisterNamedType("diagnosticlistener", "NLog.Targets.DiagnosticListenerTarget, NLog.DiagnosticSource"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("database")) + _targets.RegisterNamedType("database", "NLog.Targets.DatabaseTarget, NLog.Database"); +#if NETSTANDARD + if (skipCheckExists || !_targets.CheckTypeAliasExists("eventlog")) + _targets.RegisterNamedType("eventlog", "NLog.Targets.EventLogTarget, NLog.WindowsEventLog"); +#endif + if (skipCheckExists || !_targets.CheckTypeAliasExists("impersonatingwrapper")) + _targets.RegisterNamedType("impersonatingwrapper", "NLog.Targets.Wrappers.ImpersonatingTargetWrapper, NLog.WindowsIdentity"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("logreceiverservice")) + _targets.RegisterNamedType("logreceiverservice", "NLog.Targets.LogReceiverWebServiceTarget, NLog.Wcf"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("outputdebugstring")) + _targets.RegisterNamedType("outputdebugstring", "NLog.Targets.OutputDebugStringTarget, NLog.OutputDebugString"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("network")) + _targets.RegisterNamedType("network", "NLog.Targets.NetworkTarget, NLog.Targets.Network"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("chainsaw")) + _targets.RegisterNamedType("chainsaw", "NLog.Targets.ChainsawTarget, NLog.Targets.Network"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("nlogviewer")) + _targets.RegisterNamedType("nlogviewer", "NLog.Targets.ChainsawTarget, NLog.Targets.Network"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("mail")) + _targets.RegisterNamedType("mail", "NLog.Targets.MailTarget, NLog.Targets.Mail"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("email")) + _targets.RegisterNamedType("email", "NLog.Targets.MailTarget, NLog.Targets.Mail"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("smtp")) + _targets.RegisterNamedType("smtp", "NLog.Targets.MailTarget, NLog.Targets.Mail"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("performancecounter")) + _targets.RegisterNamedType("performancecounter", "NLog.Targets.PerformanceCounterTarget, NLog.PerformanceCounter"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("richtextbox")) + _targets.RegisterNamedType("richtextbox", "NLog.Windows.Forms.RichTextBoxTarget, NLog.Windows.Forms"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("messagebox")) + _targets.RegisterNamedType("messagebox", "NLog.Windows.Forms.MessageBoxTarget, NLog.Windows.Forms"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("formcontrol")) + _targets.RegisterNamedType("formcontrol", "NLog.Windows.Forms.FormControlTarget, NLog.Windows.Forms"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("toolstripitem")) + _targets.RegisterNamedType("toolstripitem", "NLog.Windows.Forms.ToolStripItemTarget, NLog.Windows.Forms"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("trace")) + _targets.RegisterNamedType("trace", "NLog.Targets.TraceTarget, NLog.Targets.Trace"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("tracesystem")) + _targets.RegisterNamedType("tracesystem", "NLog.Targets.TraceTarget, NLog.Targets.Trace"); + if (skipCheckExists || !_targets.CheckTypeAliasExists("webservice")) + _targets.RegisterNamedType("webservice", "NLog.Targets.WebServiceTarget, NLog.Targets.WebService"); #pragma warning restore CS0618 // Type or member is obsolete - } - return factory; } - /// - /// Registers items in using late-bound types, so that we don't need a reference to the dll. - /// - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - private void RegisterExternalItems() + private void RegisterAllLayouts(bool skipCheckExists) { - _layouts.RegisterNamedType("log4jxmleventlayout", "NLog.Layouts.Log4JXmlEventLayout, NLog.Targets.Network"); + AssemblyExtensionTypes.RegisterLayoutTypes(this, skipCheckExists); +#pragma warning disable CS0618 // Type or member is obsolete + if (skipCheckExists || !_layouts.CheckTypeAliasExists("log4jxmleventlayout")) + _layouts.RegisterNamedType("log4jxmleventlayout", "NLog.Layouts.Log4JXmlEventLayout, NLog.Targets.Network"); #if !NET35 && !NET40 - _layouts.RegisterNamedType("microsoftconsolejsonlayout", "NLog.Extensions.Logging.MicrosoftConsoleJsonLayout, NLog.Extensions.Logging"); - _layoutRenderers.RegisterNamedType("configsetting", "NLog.Extensions.Logging.ConfigSettingLayoutRenderer, NLog.Extensions.Logging"); - _layoutRenderers.RegisterNamedType("microsoftconsolelayout", "NLog.Extensions.Logging.MicrosoftConsoleLayoutRenderer, NLog.Extensions.Logging"); + if (skipCheckExists || !_layouts.CheckTypeAliasExists("microsoftconsolejsonlayout")) + _layouts.RegisterNamedType("microsoftconsolejsonlayout", "NLog.Extensions.Logging.MicrosoftConsoleJsonLayout, NLog.Extensions.Logging"); #endif - _layoutRenderers.RegisterNamedType("log4jxmlevent", "NLog.LayoutRenderers.Log4JXmlEventLayoutRenderer, NLog.Targets.Network"); - _layoutRenderers.RegisterNamedType("performancecounter", "NLog.LayoutRenderers.PerformanceCounterLayoutRenderer, NLog.PerformanceCounter"); - _layoutRenderers.RegisterNamedType("registry", "NLog.LayoutRenderers.RegistryLayoutRenderer, NLog.WindowsRegistry"); - _layoutRenderers.RegisterNamedType("windows-identity", "NLog.LayoutRenderers.WindowsIdentityLayoutRenderer, NLog.WindowsIdentity"); - _layoutRenderers.RegisterNamedType("rtblink", "NLog.Windows.Forms.RichTextBoxLinkLayoutRenderer, NLog.Windows.Forms"); - _layoutRenderers.RegisterNamedType("activity", "NLog.LayoutRenderers.ActivityTraceLayoutRenderer, NLog.DiagnosticSource"); - _layoutRenderers.RegisterNamedType("activityid", "NLog.LayoutRenderers.TraceActivityIdLayoutRenderer, NLog.Targets.Trace"); - _targets.RegisterNamedType("diagnosticlistener", "NLog.Targets.DiagnosticListenerTarget, NLog.DiagnosticSource"); - _targets.RegisterNamedType("database", "NLog.Targets.DatabaseTarget, NLog.Database"); -#if NETSTANDARD - _targets.RegisterNamedType("eventlog", "NLog.Targets.EventLogTarget, NLog.WindowsEventLog"); +#pragma warning restore CS0618 // Type or member is obsolete + } + + private void RegisterAllLayoutRenderers(bool skipCheckExists) + { + AssemblyExtensionTypes.RegisterLayoutRendererTypes(this, skipCheckExists); +#pragma warning disable CS0618 // Type or member is obsolete +#if !NET35 && !NET40 + if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("configsetting")) + _layoutRenderers.RegisterNamedType("configsetting", "NLog.Extensions.Logging.ConfigSettingLayoutRenderer, NLog.Extensions.Logging"); + if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("microsoftconsolelayout")) + _layoutRenderers.RegisterNamedType("microsoftconsolelayout", "NLog.Extensions.Logging.MicrosoftConsoleLayoutRenderer, NLog.Extensions.Logging"); #endif - _targets.RegisterNamedType("impersonatingwrapper", "NLog.Targets.Wrappers.ImpersonatingTargetWrapper, NLog.WindowsIdentity"); - _targets.RegisterNamedType("logreceiverservice", "NLog.Targets.LogReceiverWebServiceTarget, NLog.Wcf"); - _targets.RegisterNamedType("outputdebugstring", "NLog.Targets.OutputDebugStringTarget, NLog.OutputDebugString"); - _targets.RegisterNamedType("network", "NLog.Targets.NetworkTarget, NLog.Targets.Network"); - _targets.RegisterNamedType("chainsaw", "NLog.Targets.ChainsawTarget, NLog.Targets.Network"); - _targets.RegisterNamedType("nlogviewer", "NLog.Targets.ChainsawTarget, NLog.Targets.Network"); - _targets.RegisterNamedType("mail", "NLog.Targets.MailTarget, NLog.Targets.Mail"); - _targets.RegisterNamedType("email", "NLog.Targets.MailTarget, NLog.Targets.Mail"); - _targets.RegisterNamedType("smtp", "NLog.Targets.MailTarget, NLog.Targets.Mail"); - _targets.RegisterNamedType("performancecounter", "NLog.Targets.PerformanceCounterTarget, NLog.PerformanceCounter"); - _targets.RegisterNamedType("richtextbox", "NLog.Windows.Forms.RichTextBoxTarget, NLog.Windows.Forms"); - _targets.RegisterNamedType("messagebox", "NLog.Windows.Forms.MessageBoxTarget, NLog.Windows.Forms"); - _targets.RegisterNamedType("formcontrol", "NLog.Windows.Forms.FormControlTarget, NLog.Windows.Forms"); - _targets.RegisterNamedType("toolstripitem", "NLog.Windows.Forms.ToolStripItemTarget, NLog.Windows.Forms"); - _targets.RegisterNamedType("trace", "NLog.Targets.TraceTarget, NLog.Targets.Trace"); - _targets.RegisterNamedType("tracesystem", "NLog.Targets.TraceTarget, NLog.Targets.Trace"); - _targets.RegisterNamedType("webservice", "NLog.Targets.WebServiceTarget, NLog.Targets.WebService"); + if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("log4jxmlevent")) + _layoutRenderers.RegisterNamedType("log4jxmlevent", "NLog.LayoutRenderers.Log4JXmlEventLayoutRenderer, NLog.Targets.Network"); + if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("performancecounter")) + _layoutRenderers.RegisterNamedType("performancecounter", "NLog.LayoutRenderers.PerformanceCounterLayoutRenderer, NLog.PerformanceCounter"); + if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("registry")) + _layoutRenderers.RegisterNamedType("registry", "NLog.LayoutRenderers.RegistryLayoutRenderer, NLog.WindowsRegistry"); + if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("windowsidentity")) + _layoutRenderers.RegisterNamedType("windowsidentity", "NLog.LayoutRenderers.WindowsIdentityLayoutRenderer, NLog.WindowsIdentity"); + if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("rtblink")) + _layoutRenderers.RegisterNamedType("rtblink", "NLog.Windows.Forms.RichTextBoxLinkLayoutRenderer, NLog.Windows.Forms"); + if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("activity")) + _layoutRenderers.RegisterNamedType("activity", "NLog.LayoutRenderers.ActivityTraceLayoutRenderer, NLog.DiagnosticSource"); + if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("activityid")) + _layoutRenderers.RegisterNamedType("activityid", "NLog.LayoutRenderers.TraceActivityIdLayoutRenderer, NLog.Targets.Trace"); +#pragma warning restore CS0618 // Type or member is obsolete + } + + private void RegisterAllFilters(bool skipCheckExists) + { + AssemblyExtensionTypes.RegisterFilterTypes(this, skipCheckExists); + } + + private void RegisterAllConditionMethods(bool skipCheckExists) + { + AssemblyExtensionTypes.RegisterConditionTypes(this, skipCheckExists); + } + + private void RegisterAllTimeSources(bool skipCheckExists) + { + AssemblyExtensionTypes.RegisterTimeSourceTypes(this, skipCheckExists); } } } diff --git a/src/NLog/Config/Factory.cs b/src/NLog/Config/Factory.cs index c8f42636ff..6cd347d839 100644 --- a/src/NLog/Config/Factory.cs +++ b/src/NLog/Config/Factory.cs @@ -36,7 +36,6 @@ namespace NLog.Config using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; - using System.Reflection; using NLog.Common; using NLog.Internal; using NLog.LayoutRenderers; @@ -54,6 +53,8 @@ internal class Factory : private readonly Dictionary> _items; private readonly ConfigurationItemFactory _parentFactory; + public bool Initialized { get; private set; } + internal Factory(ConfigurationItemFactory parentFactory) { _parentFactory = parentFactory; @@ -62,6 +63,27 @@ internal Factory(ConfigurationItemFactory parentFactory) private delegate Type GetTypeDelegate(); + public void Initialize(Action itemRegistration) + { + lock (ConfigurationItemFactory.SyncRoot) + { + if (Initialized) + return; + + try + { + var skipCheckExists = _items.Count == 0; + itemRegistration.Invoke(skipCheckExists); + } + finally + { + Initialized = true; + } + } + } + + public bool CheckTypeAliasExists(string typeAlias) => _items.ContainsKey(typeAlias); + /// /// Registers the type. /// @@ -285,12 +307,10 @@ public static string NormalizeName(string itemName) internal sealed class LayoutRendererFactory : Factory { private readonly Dictionary _funcRenderers = new Dictionary(StringComparer.OrdinalIgnoreCase); - private readonly LayoutRendererFactory _globalDefaultFactory; - public LayoutRendererFactory(ConfigurationItemFactory parentFactory, LayoutRendererFactory globalDefaultFactory) + public LayoutRendererFactory(ConfigurationItemFactory parentFactory) : base(parentFactory) { - _globalDefaultFactory = globalDefaultFactory; } /// @@ -318,26 +338,13 @@ public void RegisterFuncLayout(string itemName, FuncLayoutRenderer renderer) public override bool TryCreateInstance(string typeAlias, out LayoutRenderer result) { //first try func renderers, as they should have the possibility to overwrite a current one. - FuncLayoutRenderer funcResult; typeAlias = FactoryExtensions.NormalizeName(typeAlias); if (_funcRenderers.Count > 0) { lock (ConfigurationItemFactory.SyncRoot) { - if (_funcRenderers.TryGetValue(typeAlias, out funcResult)) - { - result = funcResult; - return true; - } - } - } - - if (_globalDefaultFactory?._funcRenderers?.Count > 0) - { - lock (ConfigurationItemFactory.SyncRoot) - { - if (_globalDefaultFactory._funcRenderers.TryGetValue(typeAlias, out funcResult)) + if (_funcRenderers.TryGetValue(typeAlias, out var funcResult)) { result = funcResult; return true; diff --git a/src/NLog/Config/IFactory.cs b/src/NLog/Config/IFactory.cs index 4466792de6..4e7321c853 100644 --- a/src/NLog/Config/IFactory.cs +++ b/src/NLog/Config/IFactory.cs @@ -51,10 +51,6 @@ internal interface IFactory /// public interface IFactory where TBaseType : class { - /// - /// Registers type-creation with type-alias - /// - void RegisterType<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] TType>(string typeAlias) where TType : TBaseType, new(); /// /// Tries to create an item instance with type-alias /// diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index b6a4d40d5e..1c4198aff2 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -1278,6 +1278,11 @@ private Layout TryCreateLayoutInstance(ValidatedConfigurationElement element, Ty } } + if (nameof(SimpleLayout).Equals(expandedClassType, StringComparison.OrdinalIgnoreCase) && TryCreateSimpleLayoutInstance(element, out var simpleLayout)) + { + return simpleLayout; + } + var layoutInstance = FactoryCreateInstance(expandedClassType, ConfigurationItemFactory.Default.LayoutFactory); if (layoutInstance != null) { @@ -1288,6 +1293,28 @@ private Layout TryCreateLayoutInstance(ValidatedConfigurationElement element, Ty return null; } + private bool TryCreateSimpleLayoutInstance(ValidatedConfigurationElement element, out SimpleLayout simpleLayout) + { + if (!element.ValidChildren.Any()) + { + var valueLookup = element.ValueLookup; + if (valueLookup.Count == 2) + { + var simpleLayoutValue = (nameof(SimpleLayout.Text).Equals(valueLookup.First().Key, StringComparison.OrdinalIgnoreCase) ? (valueLookup.First().Value ?? string.Empty) : null) ?? + (nameof(SimpleLayout.Text).Equals(valueLookup.Last().Key, StringComparison.OrdinalIgnoreCase) ? (valueLookup.Last().Value ?? string.Empty) : null); + if (simpleLayoutValue != null) + { + var simpleLayoutText = ExpandSimpleVariables(simpleLayoutValue); + simpleLayout = new SimpleLayout(simpleLayoutText, ConfigurationItemFactory.Default); + return true; + } + } + } + + simpleLayout = null; + return false; + } + private Filter TryCreateFilterInstance(ValidatedConfigurationElement element, Type type) { var filter = TryCreateInstance(element, type, ConfigurationItemFactory.Default.FilterFactory); diff --git a/src/NLog/Config/MethodFactory.cs b/src/NLog/Config/MethodFactory.cs index c0a8aeb74b..ec4265dc36 100644 --- a/src/NLog/Config/MethodFactory.cs +++ b/src/NLog/Config/MethodFactory.cs @@ -82,6 +82,29 @@ public MethodDetails( } } + public bool Initialized { get; private set; } + + public void Initialize(Action itemRegistration) + { + lock (ConfigurationItemFactory.SyncRoot) + { + if (Initialized) + return; + + try + { + var skipCheckExists = _nameToMethodDetails.Count == 0; + itemRegistration.Invoke(skipCheckExists); + } + finally + { + Initialized = true; + } + } + } + + public bool CheckTypeAliasExists(string typeAlias) => _nameToMethodDetails.ContainsKey(typeAlias); + /// /// Registers the type. /// diff --git a/src/NLog/ILLink.Descriptors.xml b/src/NLog/ILLink.Descriptors.xml deleted file mode 100644 index 9ebc2155b1..0000000000 --- a/src/NLog/ILLink.Descriptors.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/src/NLog/Internal/Xamarin/PreserveAttribute.cs b/src/NLog/Internal/Xamarin/PreserveAttribute.cs deleted file mode 100644 index 4730b71ca3..0000000000 --- a/src/NLog/Internal/Xamarin/PreserveAttribute.cs +++ /dev/null @@ -1,56 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.Internal.Xamarin -{ - using System; - - /// - /// Prevents the Xamarin linker from linking the target. - /// - /// - /// By applying this attribute all of the members of the target will be kept as if they had been referenced by the code. - /// - [AttributeUsage(AttributeTargets.All)] - public sealed class PreserveAttribute : Attribute - { - /// - /// Ensures that all members of this type are preserved - /// - public bool AllMembers { get; set; } = true; - /// - /// Flags the method as a method to preserve during linking if the container class is pulled in. - /// - public bool Conditional { get; set; } - } -} diff --git a/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs index 99d028be8d..a1098657b8 100644 --- a/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs @@ -52,7 +52,7 @@ public sealed class ScopeContextIndentLayoutRenderer : LayoutRenderer ///
/// [DefaultParameter] - public Layout Indent { get; set; } = " "; + public Layout Indent { get; set; } = Layout.FromLiteral(" "); /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs index a559cc5307..60b063bd50 100644 --- a/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs @@ -48,9 +48,9 @@ namespace NLog.LayoutRenderers.Wrappers /// /// Documentation on NLog Wiki [LayoutRenderer("cached")] - [AmbientProperty("Cached")] - [AmbientProperty("ClearCache")] - [AmbientProperty("CachedSeconds")] + [AmbientProperty(nameof(Cached))] + [AmbientProperty(nameof(ClearCache))] + [AmbientProperty(nameof(CachedSeconds))] [ThreadAgnostic] public sealed class CachedLayoutRendererWrapper : WrapperLayoutRendererBase, IStringValueRenderer { diff --git a/src/NLog/LayoutRenderers/Wrappers/FileSystemNormalizeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/FileSystemNormalizeLayoutRendererWrapper.cs index 6e6f55204e..7a6e69d15e 100644 --- a/src/NLog/LayoutRenderers/Wrappers/FileSystemNormalizeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/FileSystemNormalizeLayoutRendererWrapper.cs @@ -45,7 +45,7 @@ namespace NLog.LayoutRenderers.Wrappers /// /// Documentation on NLog Wiki [LayoutRenderer("filesystem-normalize")] - [AmbientProperty("FSNormalize")] + [AmbientProperty(nameof(FSNormalize))] [AppDomainFixedOutput] [ThreadAgnostic] public sealed class FileSystemNormalizeLayoutRendererWrapper : WrapperLayoutRendererBase diff --git a/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs index f29a60a902..fe2ea0b106 100644 --- a/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs @@ -45,7 +45,7 @@ namespace NLog.LayoutRenderers.Wrappers /// /// Documentation on NLog Wiki [LayoutRenderer("json-encode")] - [AmbientProperty("JsonEncode")] + [AmbientProperty(nameof(JsonEncode))] [AppDomainFixedOutput] [ThreadAgnostic] public sealed class JsonEncodeLayoutRendererWrapper : WrapperLayoutRendererBase diff --git a/src/NLog/LayoutRenderers/Wrappers/LeftLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/LeftLayoutRendererWrapper.cs index ce69a43772..abb0569626 100644 --- a/src/NLog/LayoutRenderers/Wrappers/LeftLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/LeftLayoutRendererWrapper.cs @@ -45,7 +45,7 @@ namespace NLog.LayoutRenderers.Wrappers /// /// Documentation on NLog Wiki [LayoutRenderer("left")] - [AmbientProperty("Truncate")] + [AmbientProperty(nameof(Truncate))] [AppDomainFixedOutput] [ThreadAgnostic] public sealed class LeftLayoutRendererWrapper : WrapperLayoutRendererBase diff --git a/src/NLog/LayoutRenderers/Wrappers/NoRawValueLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/NoRawValueLayoutRendererWrapper.cs index 81370f44a9..59d5d4b7c2 100644 --- a/src/NLog/LayoutRenderers/Wrappers/NoRawValueLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/NoRawValueLayoutRendererWrapper.cs @@ -42,7 +42,7 @@ namespace NLog.LayoutRenderers.Wrappers ///
/// For performance and/or full (formatted) control of the output. [LayoutRenderer("norawvalue")] - [AmbientProperty("NoRawValue")] + [AmbientProperty(nameof(NoRawValue))] [AppDomainFixedOutput] [ThreadAgnostic] public sealed class NoRawValueLayoutRendererWrapper : WrapperLayoutRendererBase diff --git a/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs index a53646292d..35a7db1f56 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs @@ -46,7 +46,7 @@ namespace NLog.LayoutRenderers.Wrappers /// /// Documentation on NLog Wiki [LayoutRenderer("replace-newlines")] - [AmbientProperty("ReplaceNewLines")] + [AmbientProperty(nameof(ReplaceNewLines))] [AppDomainFixedOutput] [ThreadAgnostic] public sealed class ReplaceNewLinesLayoutRendererWrapper : WrapperLayoutRendererBase @@ -55,7 +55,7 @@ public sealed class ReplaceNewLinesLayoutRendererWrapper : WrapperLayoutRenderer private const string UnixNewLine = "\n"; /// - /// Gets or sets a value indicating the string that should be used for separating lines. + /// Gets or sets a value indicating the string that should be used to replace newlines. /// /// public string Replacement @@ -70,6 +70,11 @@ public string Replacement private string _replacement = " "; private bool _replaceWithNewLines; + /// + /// Gets or sets a value indicating the string that should be used to replace newlines. + /// + public string ReplaceNewLines { get => Replacement; set => Replacement = value; } + /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) { diff --git a/src/NLog/LayoutRenderers/Wrappers/TrimWhiteSpaceLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/TrimWhiteSpaceLayoutRendererWrapper.cs index 467d5286c0..19f7e9631e 100644 --- a/src/NLog/LayoutRenderers/Wrappers/TrimWhiteSpaceLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/TrimWhiteSpaceLayoutRendererWrapper.cs @@ -46,7 +46,7 @@ namespace NLog.LayoutRenderers.Wrappers /// /// Documentation on NLog Wiki [LayoutRenderer("trim-whitespace")] - [AmbientProperty("TrimWhiteSpace")] + [AmbientProperty(nameof(TrimWhiteSpace))] [AppDomainFixedOutput] [ThreadAgnostic] public sealed class TrimWhiteSpaceLayoutRendererWrapper : WrapperLayoutRendererBase diff --git a/src/NLog/Layouts/CSV/CsvLayout.cs b/src/NLog/Layouts/CSV/CsvLayout.cs index 275b45b133..4a27d34008 100644 --- a/src/NLog/Layouts/CSV/CsvLayout.cs +++ b/src/NLog/Layouts/CSV/CsvLayout.cs @@ -231,7 +231,7 @@ private void RenderHeader(StringBuilder sb) for (int i = 0; i < Columns.Count; i++) { CsvColumn col = Columns[i]; - var columnLayout = new SimpleLayout(new LayoutRenderers.LayoutRenderer[] { new LayoutRenderers.LiteralLayoutRenderer(col.Name) }, col.Name, ConfigurationItemFactory.Default); + var columnLayout = Layout.FromLiteral(col.Name); columnLayout.Initialize(LoggingConfiguration); RenderColumnLayout(logEvent, columnLayout, col._quoting ?? Quoting, sb, i); } diff --git a/src/NLog/Layouts/Layout.cs b/src/NLog/Layouts/Layout.cs index a393f76b65..6e578309c0 100644 --- a/src/NLog/Layouts/Layout.cs +++ b/src/NLog/Layouts/Layout.cs @@ -82,7 +82,7 @@ public abstract class Layout : ISupportsInitialize, IRenderable protected internal LoggingConfiguration LoggingConfiguration { get; private set; } /// - /// Converts a given text to a . + /// Implicitly converts the specified string as LayoutRenderer-expression into a . /// /// Text to be converted. /// object represented by the text. @@ -92,7 +92,7 @@ public static implicit operator Layout([Localizable(false)] string text) } /// - /// Implicitly converts the specified string to a . + /// Parses the specified string as LayoutRenderer-expression into a . /// /// The layout string. /// Instance of .' @@ -102,7 +102,7 @@ public static Layout FromString([Localizable(false)] string layoutText) } /// - /// Implicitly converts the specified string to a . + /// Parses the specified string as LayoutRenderer-expression into a . /// /// The layout string. /// The NLog factories to use when resolving layout renderers. @@ -113,7 +113,7 @@ public static Layout FromString([Localizable(false)] string layoutText, Configur } /// - /// Implicitly converts the specified string to a . + /// Parses the specified string as LayoutRenderer-expression into a . /// /// The layout string. /// Whether should be thrown on parse errors (false = replace unrecognized tokens with a space). @@ -137,6 +137,17 @@ public static Layout FromString([Localizable(false)] string layoutText, bool thr } } + /// + /// Create a containing literal value + /// + public static Layout FromLiteral([Localizable(false)] string literalText) + { + if (string.IsNullOrEmpty(literalText)) + return new SimpleLayout(ArrayHelper.Empty(), string.Empty); + else + return new SimpleLayout(new[] { new NLog.LayoutRenderers.LiteralLayoutRenderer(literalText) }, literalText); + } + /// /// Create a from a lambda method. /// @@ -149,7 +160,7 @@ public static Layout FromMethod(Func layoutMethod, LayoutR var name = $"{layoutMethod.Method?.DeclaringType?.ToString()}.{layoutMethod.Method?.Name}"; var layoutRenderer = CreateFuncLayoutRenderer((l, c) => layoutMethod(l), options, name); - return new SimpleLayout(new[] { layoutRenderer }, layoutRenderer.LayoutRendererName, ConfigurationItemFactory.Default); + return new SimpleLayout(new[] { layoutRenderer }, layoutRenderer.LayoutRendererName); } internal static LayoutRenderers.FuncLayoutRenderer CreateFuncLayoutRenderer(Func layoutMethod, LayoutRenderOptions options, string name) diff --git a/src/NLog/Layouts/LayoutParser.cs b/src/NLog/Layouts/LayoutParser.cs index d110abdcbc..c382da21fb 100644 --- a/src/NLog/Layouts/LayoutParser.cs +++ b/src/NLog/Layouts/LayoutParser.cs @@ -52,21 +52,21 @@ internal static class LayoutParser { private static readonly char[] SpecialTokens = new char[] { '$', '\\', '}', ':' }; - internal static LayoutRenderer[] CompileLayout(string value, ConfigurationItemFactory configurationItemFactory, bool? throwConfigExceptions, out string text) + internal static LayoutRenderer[] CompileLayout(string value, ConfigurationItemFactory configurationItemFactory, bool? throwConfigExceptions, out string parsedText) { - if (value is null) + if (string.IsNullOrEmpty(value)) { - text = string.Empty; + parsedText = string.Empty; return ArrayHelper.Empty(); } else if (value.Length < 128 && value.IndexOfAny(SpecialTokens) < 0) { - text = value; + parsedText = value; return new LayoutRenderer[] { new LiteralLayoutRenderer(value) }; } else { - return CompileLayout(configurationItemFactory, new SimpleStringReader(value), throwConfigExceptions, false, out text); + return CompileLayout(configurationItemFactory, new SimpleStringReader(value), throwConfigExceptions, false, out parsedText); } } @@ -509,8 +509,8 @@ private static object ParseLayoutRendererPropertyValue(ConfigurationItemFactory { if (typeof(Layout).IsAssignableFrom(propertyInfo.PropertyType)) { - LayoutRenderer[] renderers = CompileLayout(configurationItemFactory, stringReader, throwConfigExceptions, true, out var txt); - Layout nestedLayout = new SimpleLayout(renderers, txt, configurationItemFactory); + LayoutRenderer[] renderers = CompileLayout(configurationItemFactory, stringReader, throwConfigExceptions, true, out var parsedTxt); + Layout nestedLayout = new SimpleLayout(renderers, parsedTxt); if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Layout<>)) { @@ -654,7 +654,7 @@ private static LayoutRenderer ApplyWrappers(ConfigurationItemFactory configurati lr = ConvertToLiteral(lr); } - newRenderer.Inner = new SimpleLayout(new[] { lr }, string.Empty, configurationItemFactory); + newRenderer.Inner = new SimpleLayout(new[] { lr }, string.Empty); lr = newRenderer; } diff --git a/src/NLog/Layouts/SimpleLayout.cs b/src/NLog/Layouts/SimpleLayout.cs index d3a9b3701d..73a0633cfc 100644 --- a/src/NLog/Layouts/SimpleLayout.cs +++ b/src/NLog/Layouts/SimpleLayout.cs @@ -59,11 +59,8 @@ namespace NLog.Layouts [AppDomainFixedOutput] public class SimpleLayout : Layout, IUsesStackTrace { - private string _fixedText; - private string _layoutText; - private IRawValue _rawValueRenderer; + private readonly IRawValue _rawValueRenderer; private IStringValueRenderer _stringValueRenderer; - private readonly ConfigurationItemFactory _configurationItemFactory; /// /// Initializes a new instance of the class. @@ -99,49 +96,65 @@ public SimpleLayout([Localizable(false)] string txt, ConfigurationItemFactory co /// The NLog factories to use when creating references to layout renderers. /// Whether should be thrown on parse errors. internal SimpleLayout([Localizable(false)] string txt, ConfigurationItemFactory configurationItemFactory, bool? throwConfigExceptions) + : this(LayoutParser.CompileLayout(txt, configurationItemFactory, throwConfigExceptions, out var parsedTxt), parsedTxt) { - _configurationItemFactory = configurationItemFactory; - SetLayoutText(txt, throwConfigExceptions); + OriginalText = txt ?? string.Empty; } - internal SimpleLayout(LayoutRenderer[] renderers, [Localizable(false)] string text, ConfigurationItemFactory configurationItemFactory) + internal SimpleLayout(LayoutRenderer[] layoutRenderers, [Localizable(false)] string txt) { - _configurationItemFactory = configurationItemFactory; - OriginalText = text; - SetLayoutRenderers(renderers, text); + Text = txt ?? string.Empty; + OriginalText = txt ?? string.Empty; + + _layoutRenderers = layoutRenderers ?? ArrayHelper.Empty(); + _renderers = null; + + FixedText = null; + _rawValueRenderer = null; + _stringValueRenderer = null; + + if (_layoutRenderers.Length == 0) + { + FixedText = string.Empty; + } + else if (_layoutRenderers.Length == 1) + { + if (_layoutRenderers[0] is LiteralLayoutRenderer renderer) + { + FixedText = renderer.Text; + } + else if (_layoutRenderers[0] is IStringValueRenderer stringValueRenderer) + { + _stringValueRenderer = stringValueRenderer; + } + + if (_layoutRenderers[0] is IRawValue rawValueRenderer) + { + _rawValueRenderer = rawValueRenderer; + } + } } /// - /// Original text before compile to Layout renderes + /// Original text before parsing as Layout renderes. /// - public string OriginalText { get; private set; } + public string OriginalText { get; } /// - /// Gets or sets the layout text. + /// Gets or sets the layout text that could be parsed. /// /// - public string Text - { - get => _layoutText; - set => SetLayoutText(value); - } - - private void SetLayoutText(string value, bool? throwConfigExceptions = null) - { - OriginalText = value; - var renderers = LayoutParser.CompileLayout(value, _configurationItemFactory, throwConfigExceptions, out var txt); - SetLayoutRenderers(renderers, txt); - } + public string Text { get; } /// /// Is the message fixed? (no Layout renderers used) /// - public bool IsFixedText => _fixedText != null; + public bool IsFixedText => FixedText != null; /// /// Get the fixed text. Only set when is true /// - public string FixedText => _fixedText; + public string FixedText { get; } /// /// Is the message a simple formatted string? (Can skip StringBuilder) @@ -154,7 +167,7 @@ private void SetLayoutText(string value, bool? throwConfigExceptions = null) [NLogConfigurationIgnoreProperty] public ReadOnlyCollection Renderers => _renderers ?? (_renderers = new ReadOnlyCollection(_layoutRenderers)); private ReadOnlyCollection _renderers; - private LayoutRenderer[] _layoutRenderers; + private readonly LayoutRenderer[] _layoutRenderers; /// /// Gets a collection of objects that make up this layout. @@ -167,7 +180,7 @@ private void SetLayoutText(string value, bool? throwConfigExceptions = null) public new StackTraceUsage StackTraceUsage => base.StackTraceUsage; /// - /// Converts a text to a simple layout. + /// Implicitly converts the specified string as LayoutRenderer-expression into a . /// /// Text to be converted. /// A object. @@ -190,6 +203,7 @@ public static implicit operator SimpleLayout([Localizable(false)] string text) /// Escaping is done by replacing all occurrences of /// '${' with '${literal:text=${}' /// + [Obsolete("Instead use Layout.FromLiteral()")] public static string Escape([Localizable(false)] string text) { return text.Replace("${", @"${literal:text=\$\{}"); @@ -228,45 +242,7 @@ public override string ToString() return ToStringWithNestedItems(_layoutRenderers, r => r.ToString()); } - return Text ?? _fixedText ?? string.Empty; - } - - internal void SetLayoutRenderers(LayoutRenderer[] layoutRenderers, string text) - { - _layoutRenderers = layoutRenderers ?? ArrayHelper.Empty(); - _renderers = null; - - _fixedText = null; - _rawValueRenderer = null; - _stringValueRenderer = null; - - if (_layoutRenderers.Length == 0) - { - _fixedText = string.Empty; - } - else if (_layoutRenderers.Length == 1) - { - if (_layoutRenderers[0] is LiteralLayoutRenderer renderer) - { - _fixedText = renderer.Text; - } - else if (_layoutRenderers[0] is IStringValueRenderer stringValueRenderer) - { - _stringValueRenderer = stringValueRenderer; - } - - if (_layoutRenderers[0] is IRawValue rawValueRenderer) - { - _rawValueRenderer = rawValueRenderer; - } - } - - _layoutText = text; - - if (LoggingConfiguration != null) - { - PerformObjectScanning(); - } + return Text; } /// @@ -419,7 +395,7 @@ protected override string GetFormattedMessage(LogEventInfo logEvent) { if (IsFixedText) { - return _fixedText; + return FixedText; } if (_stringValueRenderer != null) @@ -480,13 +456,14 @@ private void RenderAllRenderers(LogEventInfo logEvent, StringBuilder target) /// protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) { - if (IsFixedText) + if (FixedText is null) { - target.Append(_fixedText); - return; + RenderAllRenderers(logEvent, target); + } + else + { + target.Append(FixedText); } - - RenderAllRenderers(logEvent, target); } } } diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 72cfb20547..753ff0f841 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -251,9 +251,7 @@ private void ActivateLoggingConfiguration(LoggingConfiguration config) { if (_config is null) { -#pragma warning disable CS0618 // Type or member is obsolete LogNLogAssemblyVersion(); -#pragma warning restore CS0618 // Type or member is obsolete } _config = config; @@ -354,6 +352,7 @@ internal static void LogNLogAssemblyVersion() InternalLogger.Debug(ex, "Not running in full trust"); } } + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting /// unmanaged resources. diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index 5e26773086..483c8051e0 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -67,7 +67,6 @@ For all config options and platform support, check https://nlog-project.org/conf true true true - copyused @@ -105,12 +104,6 @@ For all config options and platform support, check https://nlog-project.org/conf - - - ILLink.Descriptors.xml - - - diff --git a/src/NLog/Properties/AssemblyInfo.cs b/src/NLog/Properties/AssemblyInfo.cs index 21ba8882a0..722c7070dd 100644 --- a/src/NLog/Properties/AssemblyInfo.cs +++ b/src/NLog/Properties/AssemblyInfo.cs @@ -36,9 +36,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; -using NLog.Internal.Xamarin; -[assembly: Preserve] // Automatic --linkskip=NLog [assembly: AssemblyCulture("")] [assembly: CLSCompliant(true)] [assembly: ComVisible(false)] diff --git a/src/NLog/SetupExtensionsBuilderExtensions.cs b/src/NLog/SetupExtensionsBuilderExtensions.cs index 8e1bf9ada3..dbb490c263 100644 --- a/src/NLog/SetupExtensionsBuilderExtensions.cs +++ b/src/NLog/SetupExtensionsBuilderExtensions.cs @@ -342,7 +342,7 @@ public static ISetupExtensionsBuilder RegisterConditionMethod(this ISetupExtensi if (!conditionMethod.IsStatic) throw new ArgumentException($"{conditionMethod.Name} must be static", nameof(conditionMethod)); - ConfigurationItemFactory.Default.ConditionMethodFactory.RegisterDefinition(name, conditionMethod); + ConfigurationItemFactory.Default.GetConditionMethodFactory().RegisterDefinition(name, conditionMethod); return setupBuilder; } @@ -355,7 +355,7 @@ public static ISetupExtensionsBuilder RegisterConditionMethod(this ISetupExtensi public static ISetupExtensionsBuilder RegisterConditionMethod(this ISetupExtensionsBuilder setupBuilder, string name, Func conditionMethod) { Guard.ThrowIfNull(conditionMethod); - ConfigurationItemFactory.Default.ConditionMethodFactory.RegisterNoParameters(name, (logEvent) => conditionMethod(logEvent)); + ConfigurationItemFactory.Default.GetConditionMethodFactory().RegisterNoParameters(name, (logEvent) => conditionMethod(logEvent)); return setupBuilder; } @@ -368,7 +368,7 @@ public static ISetupExtensionsBuilder RegisterConditionMethod(this ISetupExtensi public static ISetupExtensionsBuilder RegisterConditionMethod(this ISetupExtensionsBuilder setupBuilder, string name, Func conditionMethod) { Guard.ThrowIfNull(conditionMethod); - ConfigurationItemFactory.Default.ConditionMethodFactory.RegisterNoParameters(name, (logEvent) => conditionMethod()); + ConfigurationItemFactory.Default.GetConditionMethodFactory().RegisterNoParameters(name, (logEvent) => conditionMethod()); return setupBuilder; } diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index c015f1ea0a..bef6d53c27 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -79,7 +79,7 @@ public Layout FileName set { _fileName = value; - _fixedFileName = (value is SimpleLayout simpleLayout && simpleLayout.IsFixedText) ? simpleLayout.Text : null; + _fixedFileName = (value is SimpleLayout simpleLayout && simpleLayout.IsFixedText) ? simpleLayout.FixedText : null; } } private Layout _fileName; diff --git a/src/NLog/Targets/TargetWithLayout.cs b/src/NLog/Targets/TargetWithLayout.cs index 0ef8baa6da..d36579ac2e 100644 --- a/src/NLog/Targets/TargetWithLayout.cs +++ b/src/NLog/Targets/TargetWithLayout.cs @@ -65,7 +65,7 @@ public abstract class TargetWithLayout : Target /// protected TargetWithLayout() { - Layout = new SimpleLayout(DefaultLayout, DefaultLayoutText, ConfigurationItemFactory.Default); + Layout = new SimpleLayout(DefaultLayout, DefaultLayoutText); } /// diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs index 2c96bfc62c..256140004b 100644 --- a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs @@ -101,7 +101,7 @@ public void SimpleFileTest(bool concurrentWrites, bool keepFileOpen, bool forceM { var fileTarget = new ConcurrentFileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", OpenFileCacheTimeout = 0, @@ -158,8 +158,8 @@ public void SimpleFileDeleteTest(bool concurrentWrites, bool keepFileOpen, bool { var fileTarget = new ConcurrentFileTarget { - FileName = SimpleLayout.Escape(logFile), - ArchiveFileName = SimpleLayout.Escape(arhiveFile), + FileName = Layout.FromLiteral(logFile), + ArchiveFileName = Layout.FromLiteral(arhiveFile), ArchiveEvery = FileArchiveEveryPeriod.Year, LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", @@ -262,7 +262,7 @@ private void SimpleFileWriteLogTest(string logFile) { var fileTarget = new ConcurrentFileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", }; @@ -292,7 +292,7 @@ public void SimpleFileTestWriteBom() { var fileTarget = new ConcurrentFileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Encoding = Encoding.UTF8, WriteBom = true, @@ -588,7 +588,7 @@ public void CsvHeaderTest() var fileTarget = new ConcurrentFileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = layout, OpenFileCacheTimeout = 0, @@ -638,7 +638,7 @@ public void DeleteFileOnStartTest() var fileTarget = new ConcurrentFileTarget { DeleteOldFileOnStartup = false, - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}" }; @@ -659,7 +659,7 @@ public void DeleteFileOnStartTest() fileTarget = new ConcurrentFileTarget { DeleteOldFileOnStartup = false, - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}" }; @@ -678,7 +678,7 @@ public void DeleteFileOnStartTest() fileTarget = new ConcurrentFileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", DeleteOldFileOnStartup = true @@ -784,7 +784,7 @@ public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompr var fileTarget = new ConcurrentFileTarget { ArchiveOldFileOnStartup = false, - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}" }; @@ -804,7 +804,7 @@ public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompr fileTarget = new ConcurrentFileTarget { ArchiveOldFileOnStartup = false, - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}" }; @@ -829,7 +829,7 @@ public void ArchiveFileOnStartTests(bool enableCompression, bool customFileCompr fileTarget = ft = new ConcurrentFileTarget { EnableArchiveFileCompression = enableCompression, - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", ArchiveOldFileOnStartup = true, @@ -881,7 +881,7 @@ ConcurrentFileTarget CreateTestTarget(long threshold) { return new ConcurrentFileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", ArchiveOldFileOnStartupAboveSize = threshold, @@ -927,7 +927,7 @@ ConcurrentFileTarget CreateTestTarget(long threshold) { return new ConcurrentFileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", ArchiveOldFileOnStartupAboveSize = threshold, @@ -977,7 +977,7 @@ public void RetryFileOpenWhenFileLocked() var fileTarget = new ConcurrentFileTarget("file") { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", KeepFileOpen = false, @@ -1027,7 +1027,7 @@ public void ReplaceFileContentsOnEachWriteTest(bool useHeader, bool useFooter) var fileTarget = new ConcurrentFileTarget { DeleteOldFileOnStartup = false, - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), ReplaceFileContentsOnEachWrite = true, LineEnding = LineEndingMode.LF, Layout = "${level} ${message}" @@ -2246,7 +2246,7 @@ public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty) // Configure first time var fileTarget = new ConcurrentFileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${message}", Header = header, @@ -2269,7 +2269,7 @@ public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty) // Configure second time fileTarget = new ConcurrentFileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${message}", Header = header, @@ -2830,7 +2830,7 @@ public void FileTarget_InvalidFileNameCorrection() { var fileTarget = new ConcurrentFileTarget { - FileName = SimpleLayout.Escape(invalidLogFileName), + FileName = Layout.FromLiteral(invalidLogFileName), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", OpenFileCacheTimeout = 0 diff --git a/tests/NLog.UnitTests/ApiTests.cs b/tests/NLog.UnitTests/ApiTests.cs index 8b70d87484..2e1ac16471 100644 --- a/tests/NLog.UnitTests/ApiTests.cs +++ b/tests/NLog.UnitTests/ApiTests.cs @@ -135,15 +135,9 @@ public void PublicEnumsTest() [Fact] public void TypesInInternalNamespaceShouldBeInternalTest() { - var excludes = new HashSet - { - typeof(NLog.Internal.Xamarin.PreserveAttribute), - }; - var notInternalTypes = allTypes .Where(t => t.Namespace != null && t.Namespace.Contains(".Internal")) .Where(t => !t.IsNested && (t.IsVisible || t.IsPublic)) - .Where(n => !excludes.Contains(n)) .Select(t => t.FullName) .ToList(); diff --git a/tests/NLog.UnitTests/Conditions/ConditionParserTests.cs b/tests/NLog.UnitTests/Conditions/ConditionParserTests.cs index 54b8993dd2..f8a81430c0 100644 --- a/tests/NLog.UnitTests/Conditions/ConditionParserTests.cs +++ b/tests/NLog.UnitTests/Conditions/ConditionParserTests.cs @@ -205,7 +205,7 @@ public void ConditionFunctionTests() public void CustomNLogFactoriesTest() { var configurationItemFactory = new ConfigurationItemFactory(); - configurationItemFactory.LayoutRendererFactory.RegisterType("foo"); + configurationItemFactory.GetLayoutRendererFactory().RegisterType("foo"); configurationItemFactory.ConditionMethodFactory.RegisterDefinition("check", typeof(MyConditionMethods).GetMethod("CheckIt")); var result = ConditionParser.ParseExpression("check('${foo}')", configurationItemFactory); @@ -216,7 +216,7 @@ public void CustomNLogFactoriesTest() public void MethodNameWithUnderscores() { var configurationItemFactory = new ConfigurationItemFactory(); - configurationItemFactory.LayoutRendererFactory.RegisterType("foo"); + configurationItemFactory.GetLayoutRendererFactory().RegisterType("foo"); configurationItemFactory.ConditionMethodFactory.RegisterDefinition("__check__", typeof(MyConditionMethods).GetMethod("CheckIt")); var result = ConditionParser.ParseExpression("__check__('${foo}')", configurationItemFactory); diff --git a/tests/NLog.UnitTests/Config/ConfigurationItemFactoryTests.cs b/tests/NLog.UnitTests/Config/ConfigurationItemFactoryTests.cs index f342267e17..d22bc4e455 100644 --- a/tests/NLog.UnitTests/Config/ConfigurationItemFactoryTests.cs +++ b/tests/NLog.UnitTests/Config/ConfigurationItemFactoryTests.cs @@ -44,7 +44,7 @@ public class ConfigurationItemFactoryTests : NLogTestBase public void ConfigurationItemFactoryTargetTest() { var itemFactory = new ConfigurationItemFactory(); - itemFactory.TargetFactory.RegisterType(nameof(MemoryTarget)); + itemFactory.GetTargetFactory().RegisterType(nameof(MemoryTarget)); itemFactory.TargetFactory.TryCreateInstance(nameof(MemoryTarget), out var result); Assert.IsType(result); } diff --git a/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs b/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs index 13eb8fbec8..77d9142c4f 100644 --- a/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs +++ b/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs @@ -307,6 +307,36 @@ public void SetupExtensionsRegisterTargetTypeTest() Assert.NotNull(logFactory.Configuration.FindTargetByName("t")); } + [Fact] + public void SetupExtensionsRegisterTargetTypeOverrideTest() + { + try + { + // Arrange + NLog.Config.ConfigurationItemFactory.Default = null; + var logFactory = new LogFactory(); + + // Act + logFactory.Setup().SetupExtensions(ext => ext.RegisterTarget("Debug")); + logFactory.Configuration = new XmlLoggingConfiguration(@" + + + + + + + + ", null, logFactory); + + // Assert + Assert.NotNull(logFactory.Configuration.FindTargetByName("t")); + } + finally + { + NLog.Config.ConfigurationItemFactory.Default = null; + } + } + [Fact] public void SetupExtensionsRegisterLayoutTest() { @@ -394,7 +424,7 @@ public void SetupExtensionsRegisterLayoutMethodThreadUnsafeTest() logFactory.GetLogger("Hello").Info("World"); ConfigurationItemFactory.Default.LayoutRendererFactory.TryCreateInstance("mylayout", out var layoutRenderer); - var layout = new SimpleLayout(new LayoutRenderer[] { layoutRenderer }, "mylayout", ConfigurationItemFactory.Default); + var layout = new SimpleLayout(new LayoutRenderer[] { layoutRenderer }, "mylayout"); layout.Render(LogEventInfo.CreateNullEvent()); // Assert @@ -422,7 +452,7 @@ public void SetupExtensionsRegisterLayoutMethodThreadAgnosticTest() logFactory.GetLogger("Hello").Info("World"); ConfigurationItemFactory.Default.LayoutRendererFactory.TryCreateInstance("mylayout", out var layoutRenderer); - var layout = new SimpleLayout(new LayoutRenderer[] { layoutRenderer }, "mylayout", ConfigurationItemFactory.Default); + var layout = new SimpleLayout(new LayoutRenderer[] { layoutRenderer }, "mylayout"); layout.Render(LogEventInfo.CreateNullEvent()); // Assert diff --git a/tests/NLog.UnitTests/Layouts/LayoutTypedTests.cs b/tests/NLog.UnitTests/Layouts/LayoutTypedTests.cs index 5225cdd003..fa6156448b 100644 --- a/tests/NLog.UnitTests/Layouts/LayoutTypedTests.cs +++ b/tests/NLog.UnitTests/Layouts/LayoutTypedTests.cs @@ -1119,7 +1119,7 @@ public void RenderShouldRecognizeStackTraceUsage() public void LayoutRendererSupportTypedLayout() { var cif = new NLog.Config.ConfigurationItemFactory(); - cif.LayoutRendererFactory.RegisterType(nameof(LayoutTypedTestLayoutRenderer)); + cif.GetLayoutRendererFactory().RegisterType(nameof(LayoutTypedTestLayoutRenderer)); Layout l = new SimpleLayout("${LayoutTypedTestLayoutRenderer:IntProperty=42}", cif); l.Initialize(null); diff --git a/tests/NLog.UnitTests/Layouts/SimpleLayoutOutputTests.cs b/tests/NLog.UnitTests/Layouts/SimpleLayoutOutputTests.cs index 954284c4c0..f247bf635e 100644 --- a/tests/NLog.UnitTests/Layouts/SimpleLayoutOutputTests.cs +++ b/tests/NLog.UnitTests/Layouts/SimpleLayoutOutputTests.cs @@ -62,7 +62,7 @@ public void LayoutRendererThrows() using (new NoThrowNLogExceptions()) { ConfigurationItemFactory configurationItemFactory = new ConfigurationItemFactory(); - configurationItemFactory.LayoutRendererFactory.RegisterType("throwsException"); + configurationItemFactory.GetLayoutRendererFactory().RegisterType("throwsException"); SimpleLayout l = new SimpleLayout("xx${throwsException}yy", configurationItemFactory); string output = l.Render(LogEventInfo.CreateNullEvent()); @@ -86,7 +86,7 @@ public void SimpleLayoutToStringTest() var l = new SimpleLayout("xx${level}yy"); Assert.Equal("xx${level}yy", l.ToString()); - var l2 = new SimpleLayout(ArrayHelper.Empty(), "someFakeText", ConfigurationItemFactory.Default); + var l2 = new SimpleLayout(ArrayHelper.Empty(), "someFakeText"); Assert.Equal("someFakeText", l2.ToString()); var l3 = new SimpleLayout(""); @@ -102,7 +102,7 @@ public void LayoutRendererThrows2() using (new NoThrowNLogExceptions()) { ConfigurationItemFactory configurationItemFactory = new ConfigurationItemFactory(); - configurationItemFactory.LayoutRendererFactory.RegisterType("throwsException"); + configurationItemFactory.GetLayoutRendererFactory().RegisterType("throwsException"); SimpleLayout l = new SimpleLayout("xx${throwsException:msg1}yy${throwsException:msg2}zz", configurationItemFactory); string output = l.Render(LogEventInfo.CreateNullEvent()); diff --git a/tests/NLog.UnitTests/Layouts/SimpleLayoutParserTests.cs b/tests/NLog.UnitTests/Layouts/SimpleLayoutParserTests.cs index 80a8ae419a..5c969278f4 100644 --- a/tests/NLog.UnitTests/Layouts/SimpleLayoutParserTests.cs +++ b/tests/NLog.UnitTests/Layouts/SimpleLayoutParserTests.cs @@ -297,10 +297,16 @@ public void EvaluateTest2() private static void AssertEscapeRoundTrips(string originalString) { +#pragma warning disable CS0618 // Type or member is obsolete string escapedString = SimpleLayout.Escape(originalString); +#pragma warning restore CS0618 // Type or member is obsolete SimpleLayout l = escapedString; string renderedString = l.Render(LogEventInfo.CreateNullEvent()); Assert.Equal(originalString, renderedString); + + var stringLiteral = Layout.FromLiteral(originalString); + renderedString = stringLiteral.Render(LogEventInfo.CreateNullEvent()); + Assert.Equal(originalString, renderedString); } [Fact] @@ -551,7 +557,7 @@ public void InvalidLayoutWillThrowIfExceptionThrowingIsOn() [Fact] public void InvalidLayoutWithExistingRenderer_WillThrowIfExceptionThrowingIsOn() { - ConfigurationItemFactory.Default.LayoutRendererFactory.RegisterType("layoutrenderer-with-list"); + ConfigurationItemFactory.Default.GetLayoutRendererFactory().RegisterType("layoutrenderer-with-list"); LogManager.ThrowConfigExceptions = true; Assert.Throws(() => { @@ -563,7 +569,7 @@ public void InvalidLayoutWithExistingRenderer_WillThrowIfExceptionThrowingIsOn() [Fact] public void UnknownPropertyInLayout_WillThrowIfExceptionThrowingIsOn() { - ConfigurationItemFactory.Default.LayoutRendererFactory.RegisterType("layoutrenderer-with-list"); + ConfigurationItemFactory.Default.GetLayoutRendererFactory().RegisterType("layoutrenderer-with-list"); LogManager.ThrowConfigExceptions = true; Assert.Throws(() => @@ -612,7 +618,7 @@ public void UnknownPropertyInLayout_WillThrowIfExceptionThrowingIsOn() #endif public void LayoutWithListParamTest(string input, string propname, string expected) { - ConfigurationItemFactory.Default.LayoutRendererFactory.RegisterType("layoutrenderer-with-list"); + ConfigurationItemFactory.Default.GetLayoutRendererFactory().RegisterType("layoutrenderer-with-list"); SimpleLayout l = $@"${{layoutrenderer-with-list:{propname}={input}}}"; var le = LogEventInfo.Create(LogLevel.Info, "logger", "message"); @@ -629,7 +635,7 @@ public void LayoutWithListParamTest_incorrect(string input, string propname) //note flags enum already supported //can;t convert empty to int - ConfigurationItemFactory.Default.LayoutRendererFactory.RegisterType("layoutrenderer-with-list"); + ConfigurationItemFactory.Default.GetLayoutRendererFactory().RegisterType("layoutrenderer-with-list"); Assert.Throws(() => { SimpleLayout l = $@"${{layoutrenderer-with-list:{propname}={input}}}"; diff --git a/tests/NLog.UnitTests/Layouts/ThreadAgnosticTests.cs b/tests/NLog.UnitTests/Layouts/ThreadAgnosticTests.cs index cb5088dbad..7e644ac864 100644 --- a/tests/NLog.UnitTests/Layouts/ThreadAgnosticTests.cs +++ b/tests/NLog.UnitTests/Layouts/ThreadAgnosticTests.cs @@ -158,7 +158,7 @@ public void CsvNonAgnostic() public void CustomNotAgnosticTests() { var cif = new ConfigurationItemFactory(); - cif.LayoutRendererFactory.RegisterType("customNotAgnostic"); + cif.GetLayoutRendererFactory().RegisterType("customNotAgnostic"); Layout l = new SimpleLayout("${customNotAgnostic}", cif); l.Initialize(null); @@ -169,7 +169,7 @@ public void CustomNotAgnosticTests() public void CustomAgnosticTests() { var cif = new ConfigurationItemFactory(); - cif.LayoutRendererFactory.RegisterType("customAgnostic"); + cif.GetLayoutRendererFactory().RegisterType("customAgnostic"); Layout l = new SimpleLayout("${customAgnostic}", cif); diff --git a/tests/NLog.UnitTests/Targets/FileTargetTests.cs b/tests/NLog.UnitTests/Targets/FileTargetTests.cs index 7cd4bc95d2..5991e86158 100644 --- a/tests/NLog.UnitTests/Targets/FileTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/FileTargetTests.cs @@ -87,7 +87,7 @@ public void SimpleFileTest(bool keepFileOpen) { var fileTarget = new FileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", OpenFileCacheTimeout = 0, @@ -129,8 +129,8 @@ public void SimpleFileDeleteTest(bool keepFileOpen) { var fileTarget = new FileTarget { - FileName = SimpleLayout.Escape(logFile), - ArchiveFileName = SimpleLayout.Escape(arhiveFile), + FileName = Layout.FromLiteral(logFile), + ArchiveFileName = Layout.FromLiteral(arhiveFile), ArchiveEvery = FileArchivePeriod.Year, LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", @@ -221,7 +221,7 @@ private void SimpleFileWriteLogTest(string logFile) { var fileTarget = new FileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", }; @@ -251,7 +251,7 @@ public void SimpleFileTestWriteBom() { var fileTarget = new FileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Encoding = Encoding.UTF8, WriteBom = true, @@ -545,7 +545,7 @@ public void CsvHeaderTest() var fileTarget = new FileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = layout, OpenFileCacheTimeout = 0, @@ -595,7 +595,7 @@ public void DeleteFileOnStartTest() var fileTarget = new FileTarget { DeleteOldFileOnStartup = false, - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}" }; @@ -616,7 +616,7 @@ public void DeleteFileOnStartTest() fileTarget = new FileTarget { DeleteOldFileOnStartup = false, - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}" }; @@ -635,7 +635,7 @@ public void DeleteFileOnStartTest() fileTarget = new FileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", DeleteOldFileOnStartup = true @@ -710,7 +710,7 @@ public void ArchiveFileOnStartTests() { ArchiveOldFileOnStartup = false, ArchiveSuffixFormat = "", - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}" }; @@ -730,7 +730,7 @@ public void ArchiveFileOnStartTests() fileTarget = new FileTarget { ArchiveOldFileOnStartup = false, - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}" }; @@ -754,7 +754,7 @@ public void ArchiveFileOnStartTests() FileTarget ft; fileTarget = ft = new FileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", ArchiveOldFileOnStartup = true, @@ -802,7 +802,7 @@ FileTarget CreateTestTarget(long threshold) { return new FileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", ArchiveAboveSize = threshold, @@ -848,7 +848,7 @@ FileTarget CreateTestTarget(long threshold) { return new FileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", ArchiveAboveSize = threshold, @@ -896,7 +896,7 @@ public void RetryFileOpenWhenFileLocked() var fileTarget = new FileTarget("file") { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", KeepFileOpen = false, @@ -945,7 +945,7 @@ public void ReplaceFileContentsOnEachWriteTest(bool useHeader, bool useFooter) var fileTarget = new FileTarget { DeleteOldFileOnStartup = false, - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), ReplaceFileContentsOnEachWrite = true, LineEnding = LineEndingMode.LF, Layout = "${level} ${message}" @@ -2099,7 +2099,7 @@ public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty, bo // Configure first time var fileTarget = new FileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${message}", Header = header, @@ -2123,7 +2123,7 @@ public void WriteHeaderOnStartupTest(bool writeHeaderWhenInitialFileNotEmpty, bo // Configure second time fileTarget = new FileTarget { - FileName = SimpleLayout.Escape(logFile), + FileName = Layout.FromLiteral(logFile), LineEnding = LineEndingMode.LF, Layout = "${message}", Header = header, @@ -2624,7 +2624,7 @@ public void FileTarget_InvalidFileNameCorrection() { var fileTarget = new FileTarget { - FileName = SimpleLayout.Escape(invalidLogFileName), + FileName = Layout.FromLiteral(invalidLogFileName), LineEnding = LineEndingMode.LF, Layout = "${level} ${message}", OpenFileCacheTimeout = 0 From 5a8203438d773b8bfd8040c26c026506678ccb6e Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 17 Mar 2025 20:04:29 +0100 Subject: [PATCH 056/224] ColoredConsoleTarget - Default row color is red for error (#5729) --- src/NLog/SetupLoadConfigurationExtensions.cs | 13 ++++--------- src/NLog/Targets/ColoredConsoleAnsiPrinter.cs | 9 +++------ src/NLog/Targets/ColoredConsoleSystemPrinter.cs | 12 ++++++------ src/NLog/Targets/ColoredConsoleTarget.cs | 4 ++-- 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/NLog/SetupLoadConfigurationExtensions.cs b/src/NLog/SetupLoadConfigurationExtensions.cs index 736dc4d1df..f31054a520 100644 --- a/src/NLog/SetupLoadConfigurationExtensions.cs +++ b/src/NLog/SetupLoadConfigurationExtensions.cs @@ -495,9 +495,8 @@ public static ISetupConfigurationTargetBuilder WriteToColoredConsole(this ISetup } else { - consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule(conditionLogLevelFatal, ConsoleOutputColor.DarkRed, ConsoleOutputColor.NoChange)); - consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule(conditionLogLevelError, ConsoleOutputColor.DarkRed, ConsoleOutputColor.NoChange)); - consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule(conditionLogLevelWarn, ConsoleOutputColor.DarkYellow, ConsoleOutputColor.NoChange)); + foreach (var defaultRowHighlight in new ColoredConsoleAnsiPrinter().DefaultConsoleRowHighlightingRules) + consoleTarget.RowHighlightingRules.Add(defaultRowHighlight); } } else @@ -510,12 +509,8 @@ public static ISetupConfigurationTargetBuilder WriteToColoredConsole(this ISetup } else { - consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule(conditionLogLevelFatal, ConsoleOutputColor.Red, ConsoleOutputColor.NoChange)); - consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule(conditionLogLevelError, ConsoleOutputColor.Red, ConsoleOutputColor.NoChange)); - consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule(conditionLogLevelWarn, ConsoleOutputColor.Yellow, ConsoleOutputColor.NoChange)); - consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule(ConditionMethodExpression.CreateMethodNoParameters("level == LogLevel.Info", (evt) => evt.Level == LogLevel.Info), ConsoleOutputColor.White, ConsoleOutputColor.NoChange)); - consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule(ConditionMethodExpression.CreateMethodNoParameters("level == LogLevel.Debug", (evt) => evt.Level == LogLevel.Debug), ConsoleOutputColor.Gray, ConsoleOutputColor.NoChange)); - consoleTarget.RowHighlightingRules.Add(new ConsoleRowHighlightingRule(ConditionMethodExpression.CreateMethodNoParameters("level == LogLevel.Trace", (evt) => evt.Level == LogLevel.Trace), ConsoleOutputColor.Gray, ConsoleOutputColor.NoChange)); + foreach (var defaultRowHighlight in new ColoredConsoleSystemPrinter().DefaultConsoleRowHighlightingRules) + consoleTarget.RowHighlightingRules.Add(defaultRowHighlight); } } diff --git a/src/NLog/Targets/ColoredConsoleAnsiPrinter.cs b/src/NLog/Targets/ColoredConsoleAnsiPrinter.cs index ddf210f32b..fb5607e4ad 100644 --- a/src/NLog/Targets/ColoredConsoleAnsiPrinter.cs +++ b/src/NLog/Targets/ColoredConsoleAnsiPrinter.cs @@ -211,12 +211,9 @@ private static string TerminalDefaultColorEscapeCode /// public IList DefaultConsoleRowHighlightingRules { get; } = new List() { - new ConsoleRowHighlightingRule("level == LogLevel.Fatal", ConsoleOutputColor.DarkRed, ConsoleOutputColor.NoChange), - new ConsoleRowHighlightingRule("level == LogLevel.Error", ConsoleOutputColor.DarkYellow, ConsoleOutputColor.NoChange), - new ConsoleRowHighlightingRule("level == LogLevel.Warn", ConsoleOutputColor.DarkMagenta, ConsoleOutputColor.NoChange), - new ConsoleRowHighlightingRule("level == LogLevel.Info", ConsoleOutputColor.NoChange, ConsoleOutputColor.NoChange), - new ConsoleRowHighlightingRule("level == LogLevel.Debug", ConsoleOutputColor.NoChange, ConsoleOutputColor.NoChange), - new ConsoleRowHighlightingRule("level == LogLevel.Trace", ConsoleOutputColor.NoChange, ConsoleOutputColor.NoChange), + new ConsoleRowHighlightingRule(Conditions.ConditionMethodExpression.CreateMethodNoParameters("level == LogLevel.Fatal", (evt) => evt.Level == LogLevel.Fatal), ConsoleOutputColor.DarkRed, ConsoleOutputColor.NoChange), + new ConsoleRowHighlightingRule(Conditions.ConditionMethodExpression.CreateMethodNoParameters("level == LogLevel.Error", (evt) => evt.Level == LogLevel.Error), ConsoleOutputColor.DarkRed, ConsoleOutputColor.NoChange), + new ConsoleRowHighlightingRule(Conditions.ConditionMethodExpression.CreateMethodNoParameters("level == LogLevel.Warn", (evt) => evt.Level == LogLevel.Warn), ConsoleOutputColor.DarkYellow, ConsoleOutputColor.NoChange), }; } } diff --git a/src/NLog/Targets/ColoredConsoleSystemPrinter.cs b/src/NLog/Targets/ColoredConsoleSystemPrinter.cs index 45ee06a081..68cf175589 100644 --- a/src/NLog/Targets/ColoredConsoleSystemPrinter.cs +++ b/src/NLog/Targets/ColoredConsoleSystemPrinter.cs @@ -101,12 +101,12 @@ public void WriteLine(TextWriter consoleWriter, string text) public IList DefaultConsoleRowHighlightingRules { get; } = new List() { - new ConsoleRowHighlightingRule("level == LogLevel.Fatal", ConsoleOutputColor.Red, ConsoleOutputColor.NoChange), - new ConsoleRowHighlightingRule("level == LogLevel.Error", ConsoleOutputColor.Yellow, ConsoleOutputColor.NoChange), - new ConsoleRowHighlightingRule("level == LogLevel.Warn", ConsoleOutputColor.Magenta, ConsoleOutputColor.NoChange), - new ConsoleRowHighlightingRule("level == LogLevel.Info", ConsoleOutputColor.White, ConsoleOutputColor.NoChange), - new ConsoleRowHighlightingRule("level == LogLevel.Debug", ConsoleOutputColor.Gray, ConsoleOutputColor.NoChange), - new ConsoleRowHighlightingRule("level == LogLevel.Trace", ConsoleOutputColor.DarkGray, ConsoleOutputColor.NoChange), + new ConsoleRowHighlightingRule(Conditions.ConditionMethodExpression.CreateMethodNoParameters("level == LogLevel.Fatal", (evt) => evt.Level == LogLevel.Fatal), ConsoleOutputColor.Red, ConsoleOutputColor.NoChange), + new ConsoleRowHighlightingRule(Conditions.ConditionMethodExpression.CreateMethodNoParameters("level == LogLevel.Error", (evt) => evt.Level == LogLevel.Error), ConsoleOutputColor.Red, ConsoleOutputColor.NoChange), + new ConsoleRowHighlightingRule(Conditions.ConditionMethodExpression.CreateMethodNoParameters("level == LogLevel.Warn", (evt) => evt.Level == LogLevel.Warn), ConsoleOutputColor.Yellow, ConsoleOutputColor.NoChange), + new ConsoleRowHighlightingRule(Conditions.ConditionMethodExpression.CreateMethodNoParameters("level == LogLevel.Info", (evt) => evt.Level == LogLevel.Info), ConsoleOutputColor.White, ConsoleOutputColor.NoChange), + new ConsoleRowHighlightingRule(Conditions.ConditionMethodExpression.CreateMethodNoParameters("level == LogLevel.Debug", (evt) => evt.Level == LogLevel.Debug), ConsoleOutputColor.Gray, ConsoleOutputColor.NoChange), + new ConsoleRowHighlightingRule(Conditions.ConditionMethodExpression.CreateMethodNoParameters("level == LogLevel.Trace", (evt) => evt.Level == LogLevel.Trace), ConsoleOutputColor.Gray, ConsoleOutputColor.NoChange), }; } } diff --git a/src/NLog/Targets/ColoredConsoleTarget.cs b/src/NLog/Targets/ColoredConsoleTarget.cs index c4315d2a62..8445391efb 100644 --- a/src/NLog/Targets/ColoredConsoleTarget.cs +++ b/src/NLog/Targets/ColoredConsoleTarget.cs @@ -141,12 +141,12 @@ public ColoredConsoleTarget(string name) : this() /// /// /// level == LogLevel.Error - /// Yellow + /// Red /// NoChange /// /// /// level == LogLevel.Warn - /// Magenta + /// Yellow /// NoChange /// /// From 5726c30ca01a179b433478eaf61264c3b497a1f2 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 17 Mar 2025 20:04:57 +0100 Subject: [PATCH 057/224] ConsoleTarget - Changed StdErr to Layout-typed (#5728) --- src/NLog/Targets/ColoredConsoleTarget.cs | 31 ++++++++++++------ src/NLog/Targets/ConsoleTarget.cs | 41 ++++++++++++++++++------ 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/NLog/Targets/ColoredConsoleTarget.cs b/src/NLog/Targets/ColoredConsoleTarget.cs index 8445391efb..8d1a4c3eee 100644 --- a/src/NLog/Targets/ColoredConsoleTarget.cs +++ b/src/NLog/Targets/ColoredConsoleTarget.cs @@ -115,13 +115,13 @@ public ColoredConsoleTarget(string name) : this() /// [Obsolete("Replaced by StdErr to align with ConsoleTarget. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] - public bool ErrorStream { get => StdErr; set => StdErr = value; } + public bool ErrorStream { get => StdErr?.IsFixed == true && StdErr.FixedValue; set => StdErr = value; } /// /// Gets or sets a value indicating whether to send the log messages to the standard error instead of the standard output. /// /// - public bool StdErr { get; set; } + public Layout StdErr { get; set; } /// /// Gets or sets a value indicating whether to use default row highlighting rules. @@ -258,11 +258,12 @@ protected override void InitializeTarget() { try { - _disableColors = StdErr ? Console.IsErrorRedirected : Console.IsOutputRedirected; + var stdErr = RenderLogEvent(StdErr, LogEventInfo.CreateNullEvent()); + _disableColors = stdErr ? Console.IsErrorRedirected : Console.IsOutputRedirected; if (_disableColors) { InternalLogger.Info("{0}: Console output is redirected so no colors. Set DetectOutputRedirected=false to skip detection.", this); - if (!AutoFlush && GetOutput() is StreamWriter streamWriter && !streamWriter.AutoFlush) + if (!AutoFlush && GetOutput(stdErr) is StreamWriter streamWriter && !streamWriter.AutoFlush) { AutoFlush = true; } @@ -330,8 +331,19 @@ private void ExplicitConsoleFlush() { if (!_pauseLogging && !AutoFlush) { - var output = GetOutput(); - output.Flush(); + if ((StdErr?.IsFixed ?? true)) + { + var stdErr = StdErr?.FixedValue ?? false; + var output = GetOutput(stdErr); + output.Flush(); + } + else + { + var output = GetOutput(false); + output.Flush(); + output = GetOutput(true); + output.Flush(); + } } } @@ -380,7 +392,8 @@ private void WriteToOutputWithColor(LogEventInfo logEvent, string message) newBackgroundColor = matchingRule.BackgroundColor != ConsoleOutputColor.NoChange ? (ConsoleColor)matchingRule.BackgroundColor : default(ConsoleColor?); } - var consoleStream = GetOutput(); + var stdErr = RenderLogEvent(StdErr, logEvent); + var consoleStream = GetOutput(stdErr); if (ReferenceEquals(colorMessage, message) && !newForegroundColor.HasValue && !newBackgroundColor.HasValue) { ConsoleTargetHelper.WriteLineThreadSafe(consoleStream, message, AutoFlush); @@ -644,9 +657,9 @@ private static void ColorizeEscapeSequences( } } - private TextWriter GetOutput() + private static TextWriter GetOutput(bool stdErr) { - return StdErr ? Console.Error : Console.Out; + return stdErr ? Console.Error : Console.Out; } } } diff --git a/src/NLog/Targets/ConsoleTarget.cs b/src/NLog/Targets/ConsoleTarget.cs index 01dd343e1c..b813d86a21 100644 --- a/src/NLog/Targets/ConsoleTarget.cs +++ b/src/NLog/Targets/ConsoleTarget.cs @@ -89,13 +89,13 @@ public sealed class ConsoleTarget : TargetWithLayoutHeaderAndFooter /// [Obsolete("Replaced by StdErr to align with ColoredConsoleTarget. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] - public bool Error { get => StdErr; set => StdErr = value; } + public bool Error { get => StdErr?.IsFixed == true && StdErr.FixedValue; set => StdErr = value; } /// /// Gets or sets a value indicating whether to send the log messages to the standard error instead of the standard output. /// /// - public bool StdErr { get; set; } + public Layout StdErr { get; set; } /// /// The encoding for writing messages to the . @@ -212,8 +212,19 @@ private void ExplicitConsoleFlush() { if (!_pauseLogging && !AutoFlush) { - var output = GetOutput(); - output.Flush(); + if ((StdErr?.IsFixed ?? true)) + { + var stdErr = StdErr?.FixedValue ?? false; + var output = GetOutput(stdErr); + output.Flush(); + } + else + { + var output = GetOutput(false); + output.Flush(); + output = GetOutput(true); + output.Flush(); + } } } @@ -254,7 +265,8 @@ private void RenderToOutput(Layout layout, LogEventInfo logEvent) return; } - var output = GetOutput(); + var stdErr = RenderLogEvent(StdErr, logEvent); + var output = GetOutput(stdErr); if (WriteBuffer) { WriteBufferToOutput(output, layout, logEvent); @@ -281,17 +293,28 @@ private void WriteBufferToOutput(TextWriter output, Layout layout, LogEventInfo private void WriteBufferToOutput(IList logEvents) { - var output = GetOutput(); using (var targetBuffer = _reusableEncodingBuffer.Allocate()) using (var targetBuilder = ReusableLayoutBuilder.Allocate()) { int targetBufferPosition = 0; + bool stdErr = false; + var output = GetOutput(stdErr); + try { + for (int i = 0; i < logEvents.Count; ++i) { + var logEvent = logEvents[i].LogEvent; + if (stdErr != RenderLogEvent(StdErr, logEvent)) + { + if (targetBufferPosition > 0) + WriteBufferToOutput(output, targetBuffer.Result, targetBufferPosition); + stdErr = !stdErr; + output = GetOutput(stdErr); + } targetBuilder.Result.ClearBuilder(); - RenderLogEventToWriteBuffer(output, Layout, logEvents[i].LogEvent, targetBuilder.Result, targetBuffer.Result, ref targetBufferPosition); + RenderLogEventToWriteBuffer(output, Layout, logEvent, targetBuilder.Result, targetBuffer.Result, ref targetBufferPosition); logEvents[i].Continuation(null); } } @@ -358,9 +381,9 @@ private void WriteBufferToOutput(TextWriter output, char[] buffer, int length) } } - private TextWriter GetOutput() + private static TextWriter GetOutput(bool stdErr) { - return StdErr ? Console.Error : Console.Out; + return stdErr ? Console.Error : Console.Out; } } } From 39b74e564942578382d70ed8ce78205c4b9efd2a Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 17 Mar 2025 20:55:16 +0100 Subject: [PATCH 058/224] Make TimeSource class sealed (#5727) --- src/NLog/Layouts/JSON/JsonLayout.cs | 8 ++++---- src/NLog/Time/AccurateLocalTimeSource.cs | 2 +- src/NLog/Time/AccurateUtcTimeSource.cs | 2 +- src/NLog/Time/FastLocalTimeSource.cs | 2 +- src/NLog/Time/FastUtcTimeSource.cs | 2 +- tests/NLog.UnitTests/TimeSourceTests.cs | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/NLog/Layouts/JSON/JsonLayout.cs b/src/NLog/Layouts/JSON/JsonLayout.cs index fe27503525..f64ff3be8a 100644 --- a/src/NLog/Layouts/JSON/JsonLayout.cs +++ b/src/NLog/Layouts/JSON/JsonLayout.cs @@ -67,11 +67,11 @@ private IValueFormatter ValueFormatter } private IValueFormatter _valueFormatter; - class LimitRecursionJsonConvert : IJsonConverter + private sealed class LimitRecursionJsonConvert : IJsonConverter { - readonly IJsonConverter _converter; - readonly Targets.DefaultJsonSerializer _serializer; - readonly Targets.JsonSerializeOptions _serializerOptions; + private readonly IJsonConverter _converter; + private readonly Targets.DefaultJsonSerializer _serializer; + private readonly Targets.JsonSerializeOptions _serializerOptions; public LimitRecursionJsonConvert(int maxRecursionLimit, bool escapeForwardSlash, IJsonConverter converter) { diff --git a/src/NLog/Time/AccurateLocalTimeSource.cs b/src/NLog/Time/AccurateLocalTimeSource.cs index 434444b44d..20798109e4 100644 --- a/src/NLog/Time/AccurateLocalTimeSource.cs +++ b/src/NLog/Time/AccurateLocalTimeSource.cs @@ -39,7 +39,7 @@ namespace NLog.Time /// Current local time retrieved directly from DateTime.Now. /// [TimeSource("AccurateLocal")] - public class AccurateLocalTimeSource : TimeSource + public sealed class AccurateLocalTimeSource : TimeSource { /// /// Gets current local time directly from DateTime.Now. diff --git a/src/NLog/Time/AccurateUtcTimeSource.cs b/src/NLog/Time/AccurateUtcTimeSource.cs index 40abb67135..9f45c12cc5 100644 --- a/src/NLog/Time/AccurateUtcTimeSource.cs +++ b/src/NLog/Time/AccurateUtcTimeSource.cs @@ -39,7 +39,7 @@ namespace NLog.Time /// Current UTC time retrieved directly from DateTime.UtcNow. /// [TimeSource("AccurateUTC")] - public class AccurateUtcTimeSource : TimeSource + public sealed class AccurateUtcTimeSource : TimeSource { /// /// Gets current UTC time directly from DateTime.UtcNow. diff --git a/src/NLog/Time/FastLocalTimeSource.cs b/src/NLog/Time/FastLocalTimeSource.cs index 16fe590a9a..07bfc29f1f 100644 --- a/src/NLog/Time/FastLocalTimeSource.cs +++ b/src/NLog/Time/FastLocalTimeSource.cs @@ -39,7 +39,7 @@ namespace NLog.Time /// Fast local time source that is updated once per tick (15.6 milliseconds). /// [TimeSource("FastLocal")] - public class FastLocalTimeSource : CachedTimeSource + public sealed class FastLocalTimeSource : CachedTimeSource { /// /// Gets uncached local time directly from DateTime.Now. diff --git a/src/NLog/Time/FastUtcTimeSource.cs b/src/NLog/Time/FastUtcTimeSource.cs index 4dc04626fe..d3bd786d0e 100644 --- a/src/NLog/Time/FastUtcTimeSource.cs +++ b/src/NLog/Time/FastUtcTimeSource.cs @@ -39,7 +39,7 @@ namespace NLog.Time /// Fast UTC time source that is updated once per tick (15.6 milliseconds). /// [TimeSource("FastUTC")] - public class FastUtcTimeSource : CachedTimeSource + public sealed class FastUtcTimeSource : CachedTimeSource { /// /// Gets uncached UTC time directly from DateTime.UtcNow. diff --git a/tests/NLog.UnitTests/TimeSourceTests.cs b/tests/NLog.UnitTests/TimeSourceTests.cs index 3ec9e7c76e..f97a328b81 100644 --- a/tests/NLog.UnitTests/TimeSourceTests.cs +++ b/tests/NLog.UnitTests/TimeSourceTests.cs @@ -99,7 +99,7 @@ public void ToStringNoImplementationTest(Type timeSourceType) Assert.Equal(expected, actual); } - class CustomTimeSource : TimeSource + private sealed class CustomTimeSource : TimeSource { public override DateTime Time => FromSystemTime(DateTime.UtcNow); From a8c7c057ad5837c64c45483fffe1e8ba320a1eaf Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 17 Mar 2025 22:01:27 +0100 Subject: [PATCH 059/224] Removed FileContentsLayoutRenderer as too exotic (#5732) --- src/NLog/Config/AssemblyExtensionTypes.cs | 2 - .../FileContentsLayoutRenderer.cs | 118 ------------------ .../LayoutRenderers/FileContentsTests.cs | 82 ------------ 3 files changed, 202 deletions(-) delete mode 100644 src/NLog/LayoutRenderers/FileContentsLayoutRenderer.cs delete mode 100644 tests/NLog.UnitTests/LayoutRenderers/FileContentsTests.cs diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index fa4b8465af..627f2d42bd 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -164,8 +164,6 @@ public static void RegisterLayoutRendererTypes(ConfigurationItemFactory factory, factory.GetLayoutRendererFactory().RegisterType("exception-data"); if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("exception")) factory.GetLayoutRendererFactory().RegisterType("exception"); - if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("file-contents")) - factory.GetLayoutRendererFactory().RegisterType("file-contents"); if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("gc")) factory.GetLayoutRendererFactory().RegisterType("gc"); if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("gdc")) diff --git a/src/NLog/LayoutRenderers/FileContentsLayoutRenderer.cs b/src/NLog/LayoutRenderers/FileContentsLayoutRenderer.cs deleted file mode 100644 index db0f74d4c0..0000000000 --- a/src/NLog/LayoutRenderers/FileContentsLayoutRenderer.cs +++ /dev/null @@ -1,118 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.LayoutRenderers -{ - using System; - using System.IO; - using System.Text; - using NLog.Common; - using NLog.Config; - using NLog.Internal; - using NLog.Layouts; - - /// - /// Renders contents of the specified file. - /// - /// - /// See NLog Wiki - /// - /// Documentation on NLog Wiki - [LayoutRenderer("file-contents")] - public class FileContentsLayoutRenderer : LayoutRenderer - { - private readonly object _lockObject = new object(); - - private string _lastFileName; - private string _currentFileContents; - - /// - /// Initializes a new instance of the class. - /// - public FileContentsLayoutRenderer() - { - Encoding = Encoding.Default; - _lastFileName = string.Empty; - } - - /// - /// Gets or sets the name of the file. - /// - /// - [DefaultParameter] - public Layout FileName { get; set; } - - /// - /// Gets or sets the encoding used in the file. - /// - /// The encoding. - /// - public Encoding Encoding { get; set; } - - /// - protected override void Append(StringBuilder builder, LogEventInfo logEvent) - { - lock (_lockObject) - { - string fileName = FileName.Render(logEvent); - - if (fileName != _lastFileName) - { - _currentFileContents = ReadFileContents(fileName); - _lastFileName = fileName; - } - } - - builder.Append(_currentFileContents); - } - - private string ReadFileContents(string fileName) - { - try - { - return File.ReadAllText(fileName, Encoding); - } - catch (Exception exception) - { - InternalLogger.Error(exception, "Cannot read file contents of '{0}'.", fileName); - - if (exception.MustBeRethrown()) - { - throw; - } - - return string.Empty; - } - } - } -} diff --git a/tests/NLog.UnitTests/LayoutRenderers/FileContentsTests.cs b/tests/NLog.UnitTests/LayoutRenderers/FileContentsTests.cs deleted file mode 100644 index fe77fff374..0000000000 --- a/tests/NLog.UnitTests/LayoutRenderers/FileContentsTests.cs +++ /dev/null @@ -1,82 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.UnitTests.LayoutRenderers -{ - using System; - using System.IO; - using System.Text; - using Xunit; - - public class FileContentsTests : NLogTestBase - { - [Fact] - public void FileContentUnicodeTest() - { - string content = "12345"; - string fileName = Guid.NewGuid().ToString("N") + ".txt"; - using (StreamWriter sw = new StreamWriter(fileName, false, Encoding.Unicode)) - { - sw.Write(content); - } - - AssertLayoutRendererOutput("${file-contents:" + fileName + ":encoding=utf-16}", content); - - File.Delete(fileName); - } - - [Fact] - public void FileContentUTF8Test() - { - string content = "12345"; - string fileName = Guid.NewGuid().ToString("N") + ".txt"; - using (StreamWriter sw = new StreamWriter(fileName, false, Encoding.UTF8)) - { - sw.Write(content); - } - - AssertLayoutRendererOutput("${file-contents:" + fileName + ":encoding=utf-8}", content); - - File.Delete(fileName); - } - - [Fact] - public void FileContentTest2() - { - using (new NoThrowNLogExceptions()) - { - AssertLayoutRendererOutput("${file-contents:nosuchfile.txt}", string.Empty); - } - } - } -} From e60decbf27a895bbf8f6b7fc6d33a2f43bd58e39 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 18 Mar 2025 07:50:05 +0100 Subject: [PATCH 060/224] EventLogTarget - Use Layout for Log + MachineName + MaxMessageLength + MaxKilobytes (#5733) --- src/NLog/Targets/EventLogTarget.cs | 140 +++++++++--------- .../Targets/EventLogTargetTests.cs | 60 ++------ 2 files changed, 86 insertions(+), 114 deletions(-) diff --git a/src/NLog/Targets/EventLogTarget.cs b/src/NLog/Targets/EventLogTarget.cs index d9e3f3ae19..4d7ae17009 100644 --- a/src/NLog/Targets/EventLogTarget.cs +++ b/src/NLog/Targets/EventLogTarget.cs @@ -101,7 +101,7 @@ internal EventLogTarget(IEventLogWrapper eventLogWrapper, string sourceName) /// Gets or sets the name of the machine on which Event Log service is running. /// /// - public string MachineName { get; set; } = "."; + public Layout MachineName { get; set; } /// /// Gets or sets the layout that renders event ID. @@ -135,25 +135,13 @@ internal EventLogTarget(IEventLogWrapper eventLogWrapper, string sourceName) /// Gets or sets the name of the Event Log to write to. This can be System, Application or any user-defined name. /// /// - public string Log { get; set; } = "Application"; + public Layout Log { get; set; } = "Application"; /// /// Gets or sets the message length limit to write to the Event Log. /// - /// MaxMessageLength cannot be zero or negative /// - public int MaxMessageLength - { - get => _maxMessageLength; - set - { - if (value <= 0) - throw new ArgumentException("MaxMessageLength cannot be zero or negative."); - - _maxMessageLength = value; - } - } - private int _maxMessageLength = EventLogMaxMessageLength; + public Layout MaxMessageLength { get; set; } = EventLogMaxMessageLength; /// /// Gets or sets the maximum Event log size in kilobytes. @@ -163,18 +151,7 @@ public int MaxMessageLength /// If null, the value will not be specified while creating the Event log. /// /// - public long? MaxKilobytes - { - get => _maxKilobytes; - set - { - if (value != null && (value < 64 || value > 4194240 || (value % 64 != 0))) // Event log API restrictions - throw new ArgumentException("MaxKilobytes must be a multiple of 64, and between 64 and 4194240"); - - _maxKilobytes = value; - } - } - private long? _maxKilobytes; + public Layout MaxKilobytes { get; set; } /// /// Gets or sets the action to take if the message is larger than the option. @@ -199,14 +176,13 @@ public void Install(InstallationContext installationContext) public void Uninstall(InstallationContext installationContext) { var fixedSource = GetFixedSource(); - if (string.IsNullOrEmpty(fixedSource)) { InternalLogger.Debug("{0}: Skipping removing of event source because it contains layout renderers", this); } else { - _eventLogWrapper.DeleteEventSource(fixedSource, MachineName); + _eventLogWrapper.DeleteEventSource(fixedSource, "."); } } @@ -220,13 +196,15 @@ public void Uninstall(InstallationContext installationContext) public bool? IsInstalled(InstallationContext installationContext) { var fixedSource = GetFixedSource(); - - if (!string.IsNullOrEmpty(fixedSource)) + if (string.IsNullOrEmpty(fixedSource)) + { + InternalLogger.Debug("{0}: Unclear if event source exists because it contains layout renderers", this); + } + else { - return _eventLogWrapper.SourceExists(fixedSource, MachineName); + return _eventLogWrapper.SourceExists(fixedSource, "."); } - InternalLogger.Debug("{0}: Unclear if event source exists because it contains layout renderers", this); - return null; //unclear! + return null; } /// @@ -234,6 +212,10 @@ protected override void InitializeTarget() { base.InitializeTarget(); + var maxKilobytes = MaxKilobytes?.IsFixed == true ? MaxKilobytes.FixedValue : 0; + if (maxKilobytes > 0 && (maxKilobytes < 64 || maxKilobytes > 4194240 || (maxKilobytes % 64 != 0))) // Event log API restrictions + throw new NLogConfigurationException("EventLog MaxKilobytes must be a multiple of 64, and between 64 and 4194240"); + CreateEventSourceIfNeeded(GetFixedSource(), false); } @@ -254,20 +236,32 @@ protected override void Write(LogEventInfo logEvent) return; } + var eventLogName = RenderLogEvent(Log, logEvent); + if (string.IsNullOrEmpty(eventLogName)) + { + InternalLogger.Warn("{0}: WriteEntry discarded because Log rendered as empty string", this); + return; + } + + var eventLogMachine = RenderLogEvent(MachineName, logEvent); + if (string.IsNullOrEmpty(eventLogMachine)) + eventLogMachine = "."; + // limitation of EventLog API - if (message.Length > MaxMessageLength) + var maxMessageLength = RenderLogEvent(MaxMessageLength, logEvent, EventLogMaxMessageLength); + if (maxMessageLength > 0 && message.Length > maxMessageLength) { if (OnOverflow == EventLogTargetOverflowAction.Truncate) { - message = message.Substring(0, MaxMessageLength); - WriteEntry(eventLogSource, message, entryType, eventId, category); + message = message.Substring(0, maxMessageLength); + WriteEntry(eventLogSource, eventLogName, eventLogMachine, message, entryType, eventId, category); } else if (OnOverflow == EventLogTargetOverflowAction.Split) { - for (int offset = 0; offset < message.Length; offset += MaxMessageLength) + for (int offset = 0; offset < message.Length; offset += maxMessageLength) { - string chunk = message.Substring(offset, Math.Min(MaxMessageLength, (message.Length - offset))); - WriteEntry(eventLogSource, chunk, entryType, eventId, category); + string chunk = message.Substring(offset, Math.Min(maxMessageLength, (message.Length - offset))); + WriteEntry(eventLogSource, eventLogName, eventLogMachine, chunk, entryType, eventId, category); } } else if (OnOverflow == EventLogTargetOverflowAction.Discard) @@ -278,34 +272,34 @@ protected override void Write(LogEventInfo logEvent) } else { - WriteEntry(eventLogSource, message, entryType, eventId, category); + WriteEntry(eventLogSource, eventLogName, eventLogMachine, message, entryType, eventId, category); } } - private void WriteEntry(string eventLogSource, string message, EventLogEntryType entryType, int eventId, short category) + private void WriteEntry(string eventLogSource, string eventLogName, string eventLogMachine, string message, EventLogEntryType entryType, int eventId, short category) { var isCacheUpToDate = _eventLogWrapper.IsEventLogAssociated && - _eventLogWrapper.Log == Log && - _eventLogWrapper.MachineName == MachineName && - _eventLogWrapper.Source == eventLogSource; + _eventLogWrapper.Source == eventLogSource && + string.Equals(_eventLogWrapper.Log, eventLogName, StringComparison.OrdinalIgnoreCase) && + string.Equals(_eventLogWrapper.MachineName, eventLogMachine, StringComparison.OrdinalIgnoreCase); if (!isCacheUpToDate) { - InternalLogger.Debug("{0}: Refresh EventLog Source {1} and Log {2}", this, eventLogSource, Log); + InternalLogger.Debug("{0}: Refresh EventLog Source {1} and Log {2}", this, eventLogSource, eventLogName); - _eventLogWrapper.AssociateNewEventLog(Log, MachineName, eventLogSource); + _eventLogWrapper.AssociateNewEventLog(eventLogName, eventLogMachine, eventLogSource); try { - if (!_eventLogWrapper.SourceExists(eventLogSource, MachineName)) + if (!_eventLogWrapper.SourceExists(eventLogSource, eventLogMachine)) { InternalLogger.Warn("{0}: Source {1} does not exist", this, eventLogSource); } else { - var currentLogName = _eventLogWrapper.LogNameFromSourceName(eventLogSource, MachineName); - if (!currentLogName.Equals(Log, StringComparison.OrdinalIgnoreCase)) + var currentLogName = _eventLogWrapper.LogNameFromSourceName(eventLogSource, eventLogMachine); + if (!currentLogName.Equals(eventLogName, StringComparison.OrdinalIgnoreCase)) { - InternalLogger.Debug("{0}: Source {1} should be mapped to Log {2}, but EventLog.LogNameFromSourceName returns {3}", this, eventLogSource, Log, currentLogName); + InternalLogger.Debug("{0}: Source {1} should be mapped to Log {2}, but EventLog.LogNameFromSourceName returns {3}", this, eventLogSource, eventLogName, currentLogName); } } } @@ -314,7 +308,7 @@ private void WriteEntry(string eventLogSource, string message, EventLogEntryType if (LogManager.ThrowExceptions) throw; - InternalLogger.Warn(ex, "{0}: Exception thrown when checking if Source {1} and LogName {2} are valid", this, eventLogSource, Log); + InternalLogger.Warn(ex, "{0}: Exception thrown when checking if Source {1} and Log {2} are valid", this, eventLogSource, eventLogName); } } @@ -355,7 +349,13 @@ internal string GetFixedSource() { if (Source is SimpleLayout simpleLayout && simpleLayout.IsFixedText) { - return simpleLayout.FixedText; + if (MachineName is null || (MachineName is SimpleLayout machineLayout && machineLayout.IsFixedText && ".".Equals(machineLayout.FixedText))) + { + if (Log is SimpleLayout logNameLayout && logNameLayout.IsFixedText) + { + return simpleLayout.FixedText; + } + } } return null; } @@ -374,46 +374,52 @@ private void CreateEventSourceIfNeeded(string fixedSource, bool alwaysThrowError return; } + var eventLogName = RenderLogEvent(Log, LogEventInfo.CreateNullEvent()); + var eventLogMachine = RenderLogEvent(MachineName, LogEventInfo.CreateNullEvent()); + if (string.IsNullOrEmpty(eventLogMachine)) + eventLogMachine = "."; + var maxKilobytes = RenderLogEvent(MaxKilobytes, LogEventInfo.CreateNullEvent(), 0); + // if we throw anywhere, we remain non-operational try { - if (_eventLogWrapper.SourceExists(fixedSource, MachineName)) + if (_eventLogWrapper.SourceExists(fixedSource, eventLogMachine)) { - string currentLogName = _eventLogWrapper.LogNameFromSourceName(fixedSource, MachineName); - if (!currentLogName.Equals(Log, StringComparison.OrdinalIgnoreCase)) + string currentLogName = _eventLogWrapper.LogNameFromSourceName(fixedSource, eventLogMachine); + if (!currentLogName.Equals(eventLogName, StringComparison.OrdinalIgnoreCase)) { - InternalLogger.Debug("{0}: Updating source {1} to use log {2}, instead of {3} (Computer restart is needed)", this, fixedSource, Log, currentLogName); + InternalLogger.Debug("{0}: Updating source {1} to use log {2}, instead of {3} (Computer restart is needed)", this, fixedSource, eventLogName, currentLogName); // re-create the association between Log and Source - _eventLogWrapper.DeleteEventSource(fixedSource, MachineName); + _eventLogWrapper.DeleteEventSource(fixedSource, eventLogMachine); - var eventSourceCreationData = new EventSourceCreationData(fixedSource, Log) + var eventSourceCreationData = new EventSourceCreationData(fixedSource, eventLogName) { - MachineName = MachineName + MachineName = eventLogMachine }; _eventLogWrapper.CreateEventSource(eventSourceCreationData); } } else { - InternalLogger.Debug("{0}: Creating source {1} to use log {2}", this, fixedSource, Log); - var eventSourceCreationData = new EventSourceCreationData(fixedSource, Log) + InternalLogger.Debug("{0}: Creating source {1} to use log {2}", this, fixedSource, eventLogName); + var eventSourceCreationData = new EventSourceCreationData(fixedSource, eventLogName) { - MachineName = MachineName + MachineName = eventLogMachine }; _eventLogWrapper.CreateEventSource(eventSourceCreationData); } - _eventLogWrapper.AssociateNewEventLog(Log, MachineName, fixedSource); + _eventLogWrapper.AssociateNewEventLog(eventLogName, eventLogMachine, fixedSource); - if (MaxKilobytes.HasValue && _eventLogWrapper.MaximumKilobytes < MaxKilobytes) + if (maxKilobytes > 0 && _eventLogWrapper.MaximumKilobytes < maxKilobytes) { - _eventLogWrapper.MaximumKilobytes = MaxKilobytes.Value; + _eventLogWrapper.MaximumKilobytes = maxKilobytes; } } catch (Exception exception) { - InternalLogger.Error(exception, "{0}: Error when connecting to EventLog. Source={1} in Log={2}", this, fixedSource, Log); + InternalLogger.Error(exception, "{0}: Error when connecting to EventLog. Source={1} in Log={2}", this, fixedSource, eventLogName); if (alwaysThrowError || LogManager.ThrowExceptions) { throw; diff --git a/tests/NLog.UnitTests/Targets/EventLogTargetTests.cs b/tests/NLog.UnitTests/Targets/EventLogTargetTests.cs index 21fa393856..d4740e9e1f 100644 --- a/tests/NLog.UnitTests/Targets/EventLogTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/EventLogTargetTests.cs @@ -96,27 +96,6 @@ public void MaxMessageLengthShouldBeAsSpecifiedOption() } [Theory] - [InlineData(0)] - [InlineData(-1)] - public void ConfigurationShouldThrowException_WhenMaxMessageLengthIsNegativeOrZero(int maxMessageLength) - { - string configrationText = $@" - - - - - - - - - "; - - NLogConfigurationException ex = Assert.Throws(() => XmlLoggingConfiguration.CreateFromXmlString(configrationText)); - Assert.Equal("MaxMessageLength cannot be zero or negative.", ex.InnerException.InnerException.Message); - } - - [Theory] - [InlineData(0)] // Is multiple of 64, but less than the min value of 64 [InlineData(65)] // Isn't multiple of 64 [InlineData(4194304)] // Is multiple of 64, but bigger than the max value of 4194240 public void Configuration_ShouldThrowException_WhenMaxKilobytesIsInvalid(long maxKilobytes) @@ -131,23 +110,23 @@ public void Configuration_ShouldThrowException_WhenMaxKilobytesIsInvalid(long ma "; - NLogConfigurationException ex = Assert.Throws(() => XmlLoggingConfiguration.CreateFromXmlString(configrationText)); - Assert.Equal("MaxKilobytes must be a multiple of 64, and between 64 and 4194240", ex.InnerException.InnerException.Message); + NLogConfigurationException ex = Assert.Throws(() => new LogFactory().Setup().LoadConfigurationFromXml(configrationText)); + Assert.Equal("EventLog MaxKilobytes must be a multiple of 64, and between 64 and 4194240", ex.Message); } [Theory] - [InlineData(0)] // Is multiple of 64, but less than the min value of 64 [InlineData(65)] // Isn't multiple of 64 [InlineData(4194304)] // Is multiple of 64, but bigger than the max value of 4194240 public void MaxKilobytes_ShouldThrowException_WhenMaxKilobytesIsInvalid(long maxKilobytes) { - ArgumentException ex = Assert.Throws(() => + NLogConfigurationException ex = Assert.Throws(() => { var target = new EventLogTarget(); target.MaxKilobytes = maxKilobytes; + target.Initialize(null); }); - Assert.Equal("MaxKilobytes must be a multiple of 64, and between 64 and 4194240", ex.Message); + Assert.Equal("EventLog MaxKilobytes must be a multiple of 64, and between 64 and 4194240", ex.Message); } [Theory] @@ -211,27 +190,14 @@ public void ShouldSetMaxKilobytes_WhenNeeded(long? newValue, long initialValue, MaxMessageLength = MaxMessageLength, MaxKilobytes = newValue, }; - eventLogMock.AssociateNewEventLog(target.Log, target.MachineName, target.GetFixedSource()); + + eventLogMock.AssociateNewEventLog(targetLog, ".", target.GetFixedSource()); target.Install(new InstallationContext()); Assert.Equal(expectedValue, eventLogMock.MaximumKilobytes); } - [Theory] - [InlineData(0)] - [InlineData(-1)] - public void ShouldThrowException_WhenMaxMessageLengthSetNegativeOrZero(int maxMessageLength) - { - ArgumentException ex = Assert.Throws(() => - { - var target = new EventLogTarget(); - target.MaxMessageLength = maxMessageLength; - }); - - Assert.Equal("MaxMessageLength cannot be zero or negative.", ex.Message); - } - [Theory] [InlineData(0, EventLogEntryType.Information, "AtInformationLevel_WhenNLogLevelIsTrace", null)] [InlineData(1, EventLogEntryType.Information, "AtInformationLevel_WhenNLogLevelIsDebug", null)] @@ -424,9 +390,9 @@ public void WriteEventLogEntry_WillRecreate_WhenWrongLogName() // Assert Assert.Equal(sourceName, deletedSourceName); - Assert.Equal(target.Log, createdLogName); + Assert.Equal(target.Log.ToString(), createdLogName); Assert.Single(eventLogMock.WrittenEntries); - Assert.Equal(target.Log, eventLogMock.WrittenEntries[0].Log); + Assert.Equal(target.Log.ToString(), eventLogMock.WrittenEntries[0].Log); Assert.Equal(sourceName, eventLogMock.WrittenEntries[0].Source); Assert.Equal("Hello", eventLogMock.WrittenEntries[0].Message); } @@ -460,7 +426,7 @@ public void WriteEventLogEntry_WillComplain_WhenWrongLogName() // Assert Assert.Equal(string.Empty, deletedSourceName); Assert.Equal(string.Empty, createdLogName); - Assert.Equal(target.Log, eventLogMock.WrittenEntries[0].Log); + Assert.Equal(target.Log.ToString(), eventLogMock.WrittenEntries[0].Log); Assert.Equal(sourceName, eventLogMock.WrittenEntries[0].Source); Assert.Equal($"Hello {sourceName}", eventLogMock.WrittenEntries[0].Message); } @@ -484,7 +450,7 @@ public void WriteEventLogEntryWithDynamicSource() logger.Log(logEvent); - var eventLog = new EventLog(target.Log); + var eventLog = new EventLog(target.Log.ToString()); var entries = GetEventRecords(eventLog.Log).ToList(); entries = entries.Where(a => a.ProviderName == sourceName).ToList(); @@ -515,7 +481,7 @@ public void LogEntryWithStaticEventIdAndCategoryInTargetLayout() LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(target)); var logger = LogManager.GetLogger("WriteEventLogEntry"); logger.Log(LogLevel.Error, "Simple Test Message"); - var eventLog = new EventLog(target.Log); + var eventLog = new EventLog(target.Log.ToString()); var entries = GetEventRecords(eventLog.Log).ToList(); var expectedProviderName = target.GetFixedSource(); var filtered = entries.Where(entry => @@ -543,7 +509,7 @@ public void LogEntryWithDynamicEventIdAndCategory() theEvent.Properties["EventId"] = eventId; theEvent.Properties["Category"] = category; logger.Log(theEvent); - var eventLog = new EventLog(target.Log); + var eventLog = new EventLog(target.Log.ToString()); var entries = GetEventRecords(eventLog.Log).ToList(); var expectedProviderName = target.GetFixedSource(); var filtered = entries.Where(entry => From 4f44e8b9cb9d8e81bae2fd5194e93b9c9ee9597b Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 22 Mar 2025 08:20:38 +0100 Subject: [PATCH 061/224] LogFactory - Refactor BuildLoggerConfiguration to return ITargetWithFilterChain (#5736) --- src/NLog/Config/LoggingConfiguration.cs | 21 +++++++++--------- src/NLog/Config/LoggingRule.cs | 6 +++-- src/NLog/Internal/TargetWithFilterChain.cs | 7 ++---- src/NLog/LogFactory.cs | 22 ++++--------------- .../Config/RuleConfigurationTests.cs | 4 ++-- 5 files changed, 23 insertions(+), 37 deletions(-) diff --git a/src/NLog/Config/LoggingConfiguration.cs b/src/NLog/Config/LoggingConfiguration.cs index 3d2d131640..d280a830c5 100644 --- a/src/NLog/Config/LoggingConfiguration.cs +++ b/src/NLog/Config/LoggingConfiguration.cs @@ -108,10 +108,11 @@ public LoggingConfiguration(LogFactory logFactory) /// /// Gets the collection of logging rules. /// - public IList LoggingRules { get; } = new List(); + public IList LoggingRules => _loggingRules; + private readonly List _loggingRules = new List(); - internal List GetLoggingRulesThreadSafe() { lock (LoggingRules) return LoggingRules.ToList(); } - private void AddLoggingRulesThreadSafe(LoggingRule rule) { lock (LoggingRules) LoggingRules.Add(rule); } + internal LoggingRule[] GetLoggingRulesThreadSafe() { lock (_loggingRules) return _loggingRules.ToArray(); } + private void AddLoggingRulesThreadSafe(LoggingRule rule) { lock (_loggingRules) _loggingRules.Add(rule); } private bool TryGetTargetThreadSafe(string name, out Target target) { lock (_targets) return _targets.TryGetValue(name, out target); } private List GetAllTargetsThreadSafe() { lock (_targets) return _targets.Values.ToList(); } @@ -442,7 +443,7 @@ public LoggingRule FindRuleByName(string ruleName) return null; var loggingRules = GetLoggingRulesThreadSafe(); - for (int i = loggingRules.Count - 1; i >= 0; i--) + for (int i = loggingRules.Length - 1; i >= 0; i--) { if (string.Equals(loggingRules[i].RuleName, ruleName, StringComparison.OrdinalIgnoreCase)) { @@ -1089,18 +1090,18 @@ private static bool FlushAllTargetsSync(LoggingConfiguration oldConfig, TimeSpan return true; } - /// - /// Change this method with NLog v6 to disconnect LogFactory from Targets/Layouts - /// - Remove LoggingRule-List-parameter - /// - Return ITargetWithFilterChain[] - /// - internal TargetWithFilterChain[] BuildLoggerConfiguration(string loggerName, LogLevel globalLogLevel, List loggingRules) + internal ITargetWithFilterChain[] BuildLoggerConfiguration(string loggerName, LogLevel globalLogLevel) { + if (LoggingRules.Count == 0 || LogLevel.Off.Equals(globalLogLevel)) + return TargetWithFilterChain.NoTargetsByLevel; + + var loggingRules = GetLoggingRulesThreadSafe(); var targetsByLevel = TargetWithFilterChain.BuildLoggerConfiguration(loggerName, loggingRules, globalLogLevel); if (InternalLogger.IsDebugEnabled && !DumpTargetConfigurationForLogger(loggerName, targetsByLevel)) { InternalLogger.Debug("Targets not configured for Logger: {0}", loggerName); } + return targetsByLevel ?? TargetWithFilterChain.NoTargetsByLevel; } diff --git a/src/NLog/Config/LoggingRule.cs b/src/NLog/Config/LoggingRule.cs index 9af48387b6..b297a9ffc6 100644 --- a/src/NLog/Config/LoggingRule.cs +++ b/src/NLog/Config/LoggingRule.cs @@ -127,9 +127,11 @@ public LoggingRule(string loggerNamePattern, Target target) /// [Obsolete("Very exotic feature without any unit-tests, not sure if it works. Marked obsolete with NLog v5.3")] [EditorBrowsable(EditorBrowsableState.Never)] - public IList ChildRules { get; } = new List(); + public IList ChildRules => _childRules; [Obsolete("Very exotic feature without any unit-tests, not sure if it works. Marked obsolete with NLog v5.3")] - internal List GetChildRulesThreadSafe() { lock (ChildRules) return ChildRules.ToList(); } + private readonly List _childRules = new List(); + [Obsolete("Very exotic feature without any unit-tests, not sure if it works. Marked obsolete with NLog v5.3")] + internal LoggingRule[] GetChildRulesThreadSafe() { lock (_childRules) return _childRules.ToArray(); } internal Target[] GetTargetsThreadSafe() { lock (_targets) return _targets.Count == 0 ? NLog.Internal.ArrayHelper.Empty() : _targets.ToArray(); } internal bool RemoveTargetThreadSafe(Target target) { lock (_targets) return _targets.Remove(target); } diff --git a/src/NLog/Internal/TargetWithFilterChain.cs b/src/NLog/Internal/TargetWithFilterChain.cs index 600e0f6682..7c9fafe31a 100644 --- a/src/NLog/Internal/TargetWithFilterChain.cs +++ b/src/NLog/Internal/TargetWithFilterChain.cs @@ -117,11 +117,8 @@ internal StackTraceUsage PrecalculateStackTraceUsage() return stackTraceUsage; } - static internal TargetWithFilterChain[] BuildLoggerConfiguration(string loggerName, List loggingRules, LogLevel globalLogLevel) + static internal TargetWithFilterChain[] BuildLoggerConfiguration(string loggerName, LoggingRule[] loggingRules, LogLevel globalLogLevel) { - if (loggingRules is null || loggingRules.Count == 0 || LogLevel.Off.Equals(globalLogLevel)) - return TargetWithFilterChain.NoTargetsByLevel; - TargetWithFilterChain[] targetsByLevel = TargetWithFilterChain.CreateLoggerConfiguration(); TargetWithFilterChain[] lastTargetsByLevel = TargetWithFilterChain.CreateLoggerConfiguration(); bool[] suppressedLevels = new bool[LogLevel.MaxLevel.Ordinal + 1]; @@ -130,7 +127,7 @@ static internal TargetWithFilterChain[] BuildLoggerConfiguration(string loggerNa return targetsFound ? targetsByLevel : TargetWithFilterChain.NoTargetsByLevel; } - static private bool GetTargetsByLevelForLogger(string name, List loggingRules, LogLevel globalLogLevel, TargetWithFilterChain[] targetsByLevel, TargetWithFilterChain[] lastTargetsByLevel, bool[] suppressedLevels) + static private bool GetTargetsByLevelForLogger(string name, LoggingRule[] loggingRules, LogLevel globalLogLevel, TargetWithFilterChain[] targetsByLevel, TargetWithFilterChain[] lastTargetsByLevel, bool[] suppressedLevels) { IList>> finalMinLevelWithFilters = null; bool targetsFound = false; diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 753ff0f841..87f0d5e886 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -493,10 +493,9 @@ private bool RefreshExistingLoggers() purgeObsoleteLoggers = loggers.Count != _loggerCache.Count; } - var loggingRules = _config?.GetLoggingRulesThreadSafe(); foreach (var logger in loggers) { - logger.SetConfiguration(BuildLoggerConfiguration(logger.Name, loggingRules)); + logger.SetConfiguration(BuildLoggerConfiguration(logger.Name)); } return purgeObsoleteLoggers; @@ -678,15 +677,10 @@ protected virtual void OnConfigurationChanged(LoggingConfigurationChangedEventAr ConfigurationChanged?.Invoke(this, e); } - /// - /// Change this method with NLog v6 to completely disconnect LogFactory from Targets/Layouts - /// - Remove LoggingRule-List-parameter - /// - Return ITargetWithFilterChain[] - /// - internal TargetWithFilterChain[] BuildLoggerConfiguration(string loggerName, List loggingRules) + internal ITargetWithFilterChain[] BuildLoggerConfiguration(string loggerName) { var globalThreshold = IsLoggingEnabled() ? GlobalThreshold : LogLevel.Off; - return _config?.BuildLoggerConfiguration(loggerName, globalThreshold, loggingRules) ?? TargetWithFilterChain.NoTargetsByLevel; + return _config?.BuildLoggerConfiguration(loggerName, globalThreshold) ?? TargetWithFilterChain.NoTargetsByLevel; } /// @@ -885,8 +879,7 @@ private Logger GetLoggerThreadSafe(string name, Type loggerType, Func ").LogFactory; - var loggerConfig = logFactory.BuildLoggerConfiguration("AAA", logFactory.Configuration?.GetLoggingRulesThreadSafe()); - var targets = loggerConfig[LogLevel.Warn.Ordinal]; + var loggerConfig = logFactory.BuildLoggerConfiguration("AAA"); + var targets = loggerConfig[LogLevel.Warn.Ordinal] as TargetWithFilterChain; Assert.Equal("d1", targets.Target.Name); Assert.Equal("d2", targets.NextInChain.Target.Name); Assert.Equal("d3", targets.NextInChain.NextInChain.Target.Name); From ce588539770890ddcfa57251c29f264f93fa8a29 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 22 Mar 2025 16:00:03 +0100 Subject: [PATCH 062/224] LayoutRenderer - Culture can now override LogEvent FormatProvider (#5737) --- src/NLog/Config/PropertyTypeConverter.cs | 14 ++++++++- src/NLog/Internal/FormatHelper.cs | 31 ++++++------------- .../LayoutRenderers/DateLayoutRenderer.cs | 2 +- .../EventPropertiesLayoutRenderer.cs | 3 +- .../ExceptionDataLayoutRenderer.cs | 3 +- .../LayoutRenderers/FuncLayoutRenderer.cs | 3 +- src/NLog/LayoutRenderers/GdcLayoutRenderer.cs | 3 +- src/NLog/LayoutRenderers/LayoutRenderer.cs | 17 ++++++++-- .../ProcessInfoLayoutRenderer.cs | 5 ++- .../ProcessTimeLayoutRenderer.cs | 3 +- .../ScopeContextNestedStatesLayoutRenderer.cs | 7 ++--- .../ScopeContextPropertyLayoutRenderer.cs | 3 +- .../ScopeContextTimingLayoutRenderer.cs | 2 +- .../LayoutRenderers/TimeLayoutRenderer.cs | 3 +- .../LowercaseLayoutRendererWrapper.cs | 8 ++--- .../Wrappers/ObjectPathRendererWrapper.cs | 5 ++- .../UppercaseLayoutRendererWrapper.cs | 8 ++--- .../NLog.UnitTests/Config/CultureInfoTests.cs | 12 +++++-- .../Config/ServiceRepositoryTests.cs | 7 ----- 19 files changed, 72 insertions(+), 67 deletions(-) diff --git a/src/NLog/Config/PropertyTypeConverter.cs b/src/NLog/Config/PropertyTypeConverter.cs index a2c0823fe0..d06b82e942 100644 --- a/src/NLog/Config/PropertyTypeConverter.cs +++ b/src/NLog/Config/PropertyTypeConverter.cs @@ -36,6 +36,7 @@ namespace NLog.Config using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; + using System.Globalization; using NLog.Internal; /// @@ -56,7 +57,7 @@ private static Dictionary> B return new Dictionary>() { { typeof(System.Text.Encoding), (stringvalue, format, formatProvider) => ConvertToEncoding(stringvalue) }, - { typeof(System.Globalization.CultureInfo), (stringvalue, format, formatProvider) => new System.Globalization.CultureInfo(stringvalue) }, + { typeof(System.Globalization.CultureInfo), (stringvalue, format, formatProvider) => ConvertToCultureInfo(stringvalue) }, { typeof(Type), (stringvalue, format, formatProvider) => ConvertToType(stringvalue, true) }, { typeof(NLog.Targets.LineEndingMode), (stringvalue, format, formatProvider) => NLog.Targets.LineEndingMode.FromString(stringvalue) }, { typeof(LogLevel), (stringvalue, format, formatProvider) => LogLevel.FromString(stringvalue) }, @@ -195,6 +196,17 @@ private static object ConvertGuid(string format, string propertyString) #endif } + private static object ConvertToCultureInfo(string stringValue) + { + if (StringHelpers.IsNullOrWhiteSpace(stringValue)) + return null; + if (nameof(CultureInfo.InvariantCulture).Equals(stringValue, StringComparison.OrdinalIgnoreCase)) + return CultureInfo.InvariantCulture; + if (nameof(CultureInfo.CurrentCulture).Equals(stringValue, StringComparison.CurrentCulture)) + return CultureInfo.CurrentCulture; + return new CultureInfo(stringValue); + } + private static object ConvertToEncoding(string stringValue) { stringValue = stringValue.Trim(); diff --git a/src/NLog/Internal/FormatHelper.cs b/src/NLog/Internal/FormatHelper.cs index cb0b1edf89..0e5846eab6 100644 --- a/src/NLog/Internal/FormatHelper.cs +++ b/src/NLog/Internal/FormatHelper.cs @@ -40,44 +40,33 @@ internal static class FormatHelper /// /// Convert object to string /// - /// value + /// value /// format for conversion. /// /// - /// If is null and isn't a already, then the will get a locked by + /// If is null and isn't a already, then the will get a locked by /// - internal static string ConvertToString(object o, IFormatProvider formatProvider) + internal static string ConvertToString(object value, IFormatProvider formatProvider) { + if (value is string stringValue) + return stringValue; + if (value is null) + return string.Empty; + // if no IFormatProvider is specified, use the Configuration.DefaultCultureInfo value. if (formatProvider is null) { - if (SkipFormattableToString(o)) - return o?.ToString() ?? string.Empty; - - if (o is IFormattable) + if (value is IFormattable) { formatProvider = LogManager.LogFactory.DefaultCultureInfo; } } - return Convert.ToString(o, formatProvider); - } - - private static bool SkipFormattableToString(object value) - { - switch (Convert.GetTypeCode(value)) - { - case TypeCode.String: return true; - case TypeCode.Empty: return true; - default: return false; - } + return Convert.ToString(value, formatProvider); } internal static string TryFormatToString(object value, string format, IFormatProvider formatProvider) { - if (SkipFormattableToString(value)) - return value?.ToString() ?? string.Empty; - if (value is IFormattable formattable) { return formattable.ToString(format, formatProvider); diff --git a/src/NLog/LayoutRenderers/DateLayoutRenderer.cs b/src/NLog/LayoutRenderers/DateLayoutRenderer.cs index d286cedc2d..b24f908642 100644 --- a/src/NLog/LayoutRenderers/DateLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/DateLayoutRenderer.cs @@ -62,7 +62,7 @@ public DateLayoutRenderer() /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + public CultureInfo Culture { get; set; } /// /// Gets or sets the date format. Can be any argument accepted by DateTime.ToString(format). diff --git a/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs b/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs index 78c58679d4..8a7a15d362 100644 --- a/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs @@ -109,8 +109,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) { if (TryGetValue(logEvent, out var value)) { - var formatProvider = GetFormatProvider(logEvent, Culture); - builder.AppendFormattedValue(value, Format, formatProvider, ValueFormatter); + AppendFormattedValue(builder, logEvent, value, Format, Culture); } } diff --git a/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs b/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs index 06204d6770..2d2ffd4b59 100644 --- a/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs @@ -93,8 +93,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) var value = primaryException.Data[Item]; if (value != null) { - var formatProvider = GetFormatProvider(logEvent, Culture); - builder.AppendFormattedValue(value, Format, formatProvider, ValueFormatter); + AppendFormattedValue(builder, logEvent, value, Format, Culture); } } } diff --git a/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs b/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs index f948c55ccd..0309d6ed63 100644 --- a/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs @@ -94,8 +94,7 @@ public FuncLayoutRenderer(string layoutRendererName, Func GetStringValue(logEvent); diff --git a/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs b/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs index d79c26b74a..11d4b85c8e 100644 --- a/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs @@ -77,8 +77,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) object value = GetValue(); if (value != null || !string.IsNullOrEmpty(Format)) { - var formatProvider = GetFormatProvider(logEvent, Culture); - builder.AppendFormattedValue(value, Format, formatProvider, ValueFormatter); + AppendFormattedValue(builder, logEvent, value, Format, Culture); } } diff --git a/src/NLog/LayoutRenderers/LayoutRenderer.cs b/src/NLog/LayoutRenderers/LayoutRenderer.cs index b740de120f..2d8b84f274 100644 --- a/src/NLog/LayoutRenderers/LayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LayoutRenderer.cs @@ -183,6 +183,19 @@ internal void RenderAppendBuilder(LogEventInfo logEvent, StringBuilder builder) /// Logging event. protected abstract void Append(StringBuilder builder, LogEventInfo logEvent); + internal void AppendFormattedValue(StringBuilder builder, LogEventInfo logEvent, object value, string format, CultureInfo culture) + { + if (format is null && value is string stringValue) + { + builder.Append(stringValue); + } + else + { + var formatProvider = GetFormatProvider(logEvent, culture); + builder.AppendFormattedValue(value, format, formatProvider, ValueFormatter); + } + } + /// /// Initializes the layout renderer. /// @@ -205,7 +218,7 @@ protected virtual void CloseLayoutRenderer() /// protected IFormatProvider GetFormatProvider(LogEventInfo logEvent, IFormatProvider layoutCulture = null) { - return logEvent.FormatProvider ?? layoutCulture ?? LoggingConfiguration?.DefaultCultureInfo; + return layoutCulture ?? logEvent.FormatProvider ?? LoggingConfiguration?.DefaultCultureInfo; } /// @@ -219,7 +232,7 @@ protected IFormatProvider GetFormatProvider(LogEventInfo logEvent, IFormatProvid /// protected CultureInfo GetCulture(LogEventInfo logEvent, CultureInfo layoutCulture = null) { - return logEvent.FormatProvider as CultureInfo ?? layoutCulture ?? LoggingConfiguration?.DefaultCultureInfo; + return layoutCulture ?? logEvent.FormatProvider as CultureInfo ?? LoggingConfiguration?.DefaultCultureInfo ?? CultureInfo.CurrentCulture; } /// diff --git a/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs index cd1c781001..967bbf2a59 100644 --- a/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs @@ -70,7 +70,7 @@ public class ProcessInfoLayoutRenderer : LayoutRenderer /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + public CultureInfo Culture { get; set; } /// protected override void InitializeLayoutRenderer() @@ -101,8 +101,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) var value = GetValue(); if (value != null) { - var formatProvider = GetFormatProvider(logEvent, Culture); - builder.AppendFormattedValue(value, Format, formatProvider, ValueFormatter); + AppendFormattedValue(builder, logEvent, value, Format, Culture); } } diff --git a/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs index 5b3cd50b85..fd839cffdc 100644 --- a/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs @@ -60,8 +60,7 @@ public class ProcessTimeLayoutRenderer : LayoutRenderer, IRawValue /// Gets or sets the culture used for rendering. /// /// - [RequiredParameter] - public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + public CultureInfo Culture { get; set; } /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs index 754d9fdc16..3798f063c1 100644 --- a/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs @@ -91,7 +91,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) // Allows fast rendering of topframes=1 var topFrame = ScopeContext.PeekNestedState(); if (topFrame != null) - builder.AppendFormattedValue(topFrame, Format, GetFormatProvider(logEvent, Culture), ValueFormatter); + AppendFormattedValue(builder, logEvent, topFrame, Format, Culture); return; } @@ -117,7 +117,6 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) private void AppendNestedStates(StringBuilder builder, IList messages, int startPos, int endPos, LogEventInfo logEvent) { bool formatAsJson = MessageTemplates.ValueFormatter.FormatAsJson.Equals(Format, StringComparison.Ordinal); - var formatProvider = GetFormatProvider(logEvent, Culture); string separator = null; string itemSeparator = null; @@ -136,11 +135,11 @@ private void AppendNestedStates(StringBuilder builder, IList messages, i { builder.Append(currentSeparator); if (formatAsJson) - AppendJsonFormattedValue(messages[i], formatProvider, builder, separator, itemSeparator); + AppendJsonFormattedValue(messages[i], Culture ?? CultureInfo.InvariantCulture, builder, separator, itemSeparator); else if (messages[i] is IEnumerable>) builder.Append(Convert.ToString(messages[i])); // Special support for Microsoft Extension Logging ILogger.BeginScope else - builder.AppendFormattedValue(messages[i], Format, formatProvider, ValueFormatter); + AppendFormattedValue(builder, logEvent, messages[i], Format, Culture); currentSeparator = itemSeparator ?? _separator?.Render(logEvent) ?? string.Empty; } } diff --git a/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs index 2a63a7891e..a00357fac6 100644 --- a/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs @@ -74,8 +74,7 @@ public sealed class ScopeContextPropertyLayoutRenderer : LayoutRenderer, IString protected override void Append(StringBuilder builder, LogEventInfo logEvent) { var value = GetValue(); - var formatProvider = GetFormatProvider(logEvent, Culture); - builder.AppendFormattedValue(value, Format, formatProvider, ValueFormatter); + AppendFormattedValue(builder, logEvent, value, Format, Culture); } string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) => GetStringValue(logEvent); diff --git a/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs index 17bf73a235..33b9251168 100644 --- a/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs @@ -80,7 +80,7 @@ public sealed class ScopeContextTimingLayoutRenderer : LayoutRenderer /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + public CultureInfo Culture { get; set; } /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs b/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs index dde0e14bd1..04162c425a 100644 --- a/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs @@ -67,8 +67,7 @@ public class TimeLayoutRenderer : LayoutRenderer, IRawValue /// Gets or sets the culture used for rendering. /// /// - [RequiredParameter] - public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + public CultureInfo Culture { get; set; } /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs index ff1d4637ea..00523cc98d 100644 --- a/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs @@ -72,7 +72,7 @@ public sealed class LowercaseLayoutRendererWrapper : WrapperLayoutRendererBase /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + public CultureInfo Culture { get; set; } /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) @@ -80,7 +80,7 @@ protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBui Inner.Render(logEvent, builder); if (Lowercase && builder.Length > orgLength) { - TransformToLowerCase(builder, orgLength); + TransformToLowerCase(builder, logEvent, orgLength); } } @@ -90,9 +90,9 @@ protected override string Transform(string text) throw new NotSupportedException(); } - private void TransformToLowerCase(StringBuilder target, int startPos) + private void TransformToLowerCase(StringBuilder target, LogEventInfo logEvent, int startPos) { - CultureInfo culture = Culture; + CultureInfo culture = GetCulture(logEvent, Culture); for (int i = startPos; i < target.Length; ++i) { target[i] = char.ToLower(target[i], culture); diff --git a/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs index 71bf520e65..d36caeca15 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs @@ -87,7 +87,7 @@ public string ObjectPath /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + public CultureInfo Culture { get; set; } /// protected override string Transform(string text) @@ -100,8 +100,7 @@ protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBui { if (TryGetRawPropertyValue(logEvent, out object propertyValue)) { - var formatProvider = GetFormatProvider(logEvent, Culture); - builder.AppendFormattedValue(propertyValue, Format, formatProvider, ValueFormatter); + AppendFormattedValue(builder, logEvent, propertyValue, Format, Culture); } } diff --git a/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs index c1ff52fa1c..a5b2117efc 100644 --- a/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs @@ -77,7 +77,7 @@ public sealed class UppercaseLayoutRendererWrapper : WrapperLayoutRendererBase /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + public CultureInfo Culture { get; set; } /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) @@ -85,7 +85,7 @@ protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBui Inner.Render(logEvent, builder); if (Uppercase && builder.Length > orgLength) { - TransformToUpperCase(builder, orgLength); + TransformToUpperCase(builder, logEvent, orgLength); } } @@ -95,9 +95,9 @@ protected override string Transform(string text) throw new NotSupportedException(); } - private void TransformToUpperCase(StringBuilder target, int startPos) + private void TransformToUpperCase(StringBuilder target, LogEventInfo logEvent, int startPos) { - CultureInfo culture = Culture; + CultureInfo culture = GetCulture(logEvent, Culture); for (int i = startPos; i < target.Length; ++i) { target[i] = char.ToUpper(target[i], culture); diff --git a/tests/NLog.UnitTests/Config/CultureInfoTests.cs b/tests/NLog.UnitTests/Config/CultureInfoTests.cs index 72d4e94776..73909cea83 100644 --- a/tests/NLog.UnitTests/Config/CultureInfoTests.cs +++ b/tests/NLog.UnitTests/Config/CultureInfoTests.cs @@ -116,9 +116,13 @@ public void EventPropRendererCultureTest() var renderer = new EventPropertiesLayoutRenderer(); renderer.Item = "ADouble"; + renderer.Culture = null; string output = renderer.Render(logEventInfo); - Assert.Equal(expected, output); + + renderer.Culture = System.Globalization.CultureInfo.InvariantCulture; // Wins over LogEventInfo.FormatProvider + output = renderer.Render(logEventInfo); + Assert.Equal("1.23", output); } [Fact] @@ -164,9 +168,13 @@ public void AllEventPropRendererCultureTest() logEventInfo.Properties["ADouble"] = 1.23; var renderer = new AllEventPropertiesLayoutRenderer(); + renderer.Culture = null; string output = renderer.Render(logEventInfo); - Assert.Equal(expected, output); + + renderer.Culture = System.Globalization.CultureInfo.InvariantCulture; // Wins over LogEventInfo.FormatProvider + output = renderer.Render(logEventInfo); + Assert.Equal("ADouble=1.23", output); } private static LogEventInfo CreateLogEventInfo(string cultureName) diff --git a/tests/NLog.UnitTests/Config/ServiceRepositoryTests.cs b/tests/NLog.UnitTests/Config/ServiceRepositoryTests.cs index 2f58b2cfe3..6374c22dd5 100644 --- a/tests/NLog.UnitTests/Config/ServiceRepositoryTests.cs +++ b/tests/NLog.UnitTests/Config/ServiceRepositoryTests.cs @@ -170,13 +170,6 @@ public void ResolveShouldCheckExternalServiceProvider() Assert.NotNull(otherDependency); } - private static void AssertCycleException(LogFactory logFactory) where T : class - { - var ex = Assert.Throws(() => logFactory.ServiceRepository.ResolveService()); - Assert.Contains("cycle", ex.Message, StringComparison.OrdinalIgnoreCase); - Assert.Equal(typeof(T), ex.ServiceType); - } - private static void InitializeLogFactoryJsonConverter(LogFactory logFactory, string testValue, out Logger logger, out DebugTarget target) { logFactory.ServiceRepository.RegisterService(typeof(IJsonConverter), new MySimpleJsonConverter { Test = testValue }); From 7e19539e8994b8546b4ee77275c4f66952b88d26 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 23 Mar 2025 18:51:59 +0100 Subject: [PATCH 063/224] LogFactory - FlushAsync + IAsyncDisposable DisposeAsync (#5739) --- src/NLog/Common/AsyncHelpers.cs | 2 + src/NLog/Config/LoggingConfiguration.cs | 139 +++++--------- src/NLog/Internal/TimeoutContinuation.cs | 2 + src/NLog/LogFactory.cs | 176 ++++++++++++++---- src/NLog/Targets/Target.cs | 39 ++-- .../Targets/Wrappers/AsyncTargetWrapper.cs | 42 +++-- .../Wrappers/BufferingTargetWrapper.cs | 53 ++++-- tests/NLog.UnitTests/AsyncHelperTests.cs | 4 +- tests/NLog.UnitTests/LogFactoryTests.cs | 82 ++++++++ 9 files changed, 366 insertions(+), 173 deletions(-) diff --git a/src/NLog/Common/AsyncHelpers.cs b/src/NLog/Common/AsyncHelpers.cs index 9143ce432d..b91779ee36 100644 --- a/src/NLog/Common/AsyncHelpers.cs +++ b/src/NLog/Common/AsyncHelpers.cs @@ -165,6 +165,8 @@ public static AsyncContinuation PrecededBy(AsyncContinuation asyncContinuation, /// The asynchronous continuation. /// The timeout. /// Wrapped continuation. + [Obsolete("Marked obsolete on NLog 6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public static AsyncContinuation WithTimeout(AsyncContinuation asyncContinuation, TimeSpan timeout) { return new TimeoutContinuation(asyncContinuation, timeout).Function; diff --git a/src/NLog/Config/LoggingConfiguration.cs b/src/NLog/Config/LoggingConfiguration.cs index d280a830c5..d16b433021 100644 --- a/src/NLog/Config/LoggingConfiguration.cs +++ b/src/NLog/Config/LoggingConfiguration.cs @@ -979,115 +979,74 @@ bool IsUnusedInList(Target target1, ILookup targets) InternalLogger.Debug("Unused target checking is completed. Total Rule Count: {0}, Total Target Count: {1}, Unused Target Count: {2}", LoggingRules.Count, configuredNamedTargets.Count, unusedCount); } - internal bool FlushAllTargets(TimeSpan timeout, AsyncContinuation asyncContinuation, bool throwExceptions) + internal AsyncContinuation FlushAllTargets(AsyncContinuation flushCompletion) { - if (asyncContinuation != null) + var pendingTargets = GetAllTargetsToFlush(); + if (pendingTargets.Count == 0) { - FlushAllTargetsAsync(this, asyncContinuation, timeout); - return true; - } - else - { - return FlushAllTargetsSync(this, timeout, throwExceptions); + flushCompletion.Invoke(null); + return null; } - } - /// - /// Flushes any pending log messages on all appenders. - /// - /// Config containing Targets to Flush - /// Flush completed notification (success / timeout) - /// Optional timeout that guarantees that completed notification is called. - /// - private static AsyncContinuation FlushAllTargetsAsync(LoggingConfiguration loggingConfiguration, AsyncContinuation asyncContinuation, TimeSpan? asyncTimeout) - { - var pendingTargets = loggingConfiguration.GetAllTargetsToFlush(); + InternalLogger.Trace("Flushing all {0} targets...", pendingTargets.Count); + + Exception lastException = null; - AsynchronousAction flushAction = (target, cont) => + Action flushAction = (t, ex) => { - target.Flush(ex => + if (ex != null) { - if (ex != null) - InternalLogger.Warn(ex, "Flush failed for target {0}(Name={1})", target.GetType(), target.Name); - lock (pendingTargets) - { - pendingTargets.Remove(target); - } - cont(ex); - }); - }; - AsyncContinuation flushContinuation = (ex) => - { + InternalLogger.Warn(ex, "Flush failed for target {0}(Name={1})", t.GetType(), t.Name); + } + bool completed = false; lock (pendingTargets) { - foreach (var pendingTarget in pendingTargets) - InternalLogger.Debug("Flush timeout for target {0}(Name={1})", pendingTarget.GetType(), pendingTarget.Name); - pendingTargets.Clear(); + if (ex != null) + lastException = ex; + if (pendingTargets.Remove(t) && pendingTargets.Count == 0) + completed = true; + } + if (completed) + { + if (lastException != null) + InternalLogger.Warn("Flush completed with errors"); + else + InternalLogger.Debug("Flush completed"); + flushCompletion.Invoke(lastException); } - if (ex != null) - InternalLogger.Warn(ex, "Flush completed with errors"); - else - InternalLogger.Debug("Flush completed"); - asyncContinuation(ex); }; - if (asyncTimeout.HasValue) - { - flushContinuation = AsyncHelpers.WithTimeout(flushContinuation, asyncTimeout.Value); - } - else - { - flushContinuation = AsyncHelpers.PreventMultipleCalls(flushContinuation); - } - - InternalLogger.Trace("Flushing all {0} targets...", pendingTargets.Count); - AsyncHelpers.ForEachItemInParallel(pendingTargets, flushContinuation, flushAction); - return flushContinuation; - } - - private static bool FlushAllTargetsSync(LoggingConfiguration oldConfig, TimeSpan timeout, bool throwExceptions) - { - Exception lastException = null; - - try + foreach (var target in pendingTargets.ToArray()) { - ManualResetEvent flushCompletedEvent = new ManualResetEvent(false); - - var flushContinuation = FlushAllTargetsAsync(oldConfig, (ex) => + var flushTarget = target; + AsyncHelpers.StartAsyncTask(s => { - if (ex != null) - lastException = ex; - flushCompletedEvent.Set(); + try + { + flushTarget.Flush(ex => + { + flushAction(flushTarget, ex); + }); + } + catch (Exception ex) + { + flushAction(flushTarget, ex); + throw; + } }, null); - - bool flushCompleted = flushCompletedEvent.WaitOne(timeout); - if (!flushCompleted) - flushContinuation(new TimeoutException($"Timeout when flushing all targets, after waiting {timeout.TotalSeconds} seconds.")); - } - catch (Exception ex) - { -#if DEBUG - if (ex.MustBeRethrownImmediately()) - throw; // Throwing exceptions here might crash the entire application (.NET 2.0 behavior) -#endif - - InternalLogger.Error(ex, "LogFactory failed to flush targets."); - - if (throwExceptions) - throw new NLogRuntimeException("Asynchronous exception has occurred.", ex); - - return false; } - if (lastException != null) + AsyncContinuation flushTimeoutHandler = (ex) => { - if (throwExceptions) - throw new NLogRuntimeException("Asynchronous exception has occurred.", lastException); - else - return false; - } + lock (pendingTargets) + { + foreach (var pendingTarget in pendingTargets) + InternalLogger.Warn("Flush timeout for target {0}(Name={1})", pendingTarget.GetType(), pendingTarget.Name); + pendingTargets.Clear(); + } + }; - return true; + return flushTimeoutHandler; } internal ITargetWithFilterChain[] BuildLoggerConfiguration(string loggerName, LogLevel globalLogLevel) diff --git a/src/NLog/Internal/TimeoutContinuation.cs b/src/NLog/Internal/TimeoutContinuation.cs index 07739073be..21ac604e45 100644 --- a/src/NLog/Internal/TimeoutContinuation.cs +++ b/src/NLog/Internal/TimeoutContinuation.cs @@ -34,12 +34,14 @@ namespace NLog.Internal { using System; + using System.ComponentModel; using System.Threading; using NLog.Common; /// /// Wraps with a timeout. /// + [Obsolete("Marked obsolete on NLog 6.0")] internal sealed class TimeoutContinuation : IDisposable { private AsyncContinuation _asyncContinuation; diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 87f0d5e886..12ef1f9070 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -51,6 +51,9 @@ namespace NLog /// Creates and manages instances of objects. /// public class LogFactory : IDisposable +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER +, IAsyncDisposable +#endif { private static readonly TimeSpan DefaultFlushTimeout = TimeSpan.FromSeconds(15); @@ -354,8 +357,7 @@ internal static void LogNLogAssemblyVersion() } /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting - /// unmanaged resources. + /// Shutdown logging /// public void Dispose() { @@ -363,6 +365,43 @@ public void Dispose() GC.SuppressFinalize(this); } +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER + /// + /// Flush any pending log messages, and shutdown logging + /// + public async System.Threading.Tasks.ValueTask DisposeAsync() + { + lock (_syncRoot) + { + if (_config is null || !_configLoaded || _isDisposing) + { + DisposeInternal(false); + return; + } + } + + try + { + await FlushAsync(CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + InternalLogger.Error(ex, "LogFactory DisposeAsync Flush Failed."); + } + + try + { + var disposeTimeout = System.Threading.Tasks.Task.Delay(DefaultFlushTimeout).ContinueWith(prevTask => throw new TimeoutException("LogFactory Dispose Timeout")); + await System.Threading.Tasks.TaskExtensions.Unwrap(System.Threading.Tasks.Task.WhenAny(System.Threading.Tasks.Task.Run(() => DisposeInternal()), disposeTimeout)).ConfigureAwait(false); + GC.SuppressFinalize(this); + } + catch (Exception ex) + { + InternalLogger.Error(ex, "LogFactory DisposeAsync Failed."); + } + } +#endif + /// /// Begins configuration of the LogFactory options using fluent interface /// @@ -582,35 +621,97 @@ public void Flush(AsyncContinuation asyncContinuation, int timeoutMilliseconds) /// Maximum time to allow for the flush. Any messages after that time will be discarded. public void Flush(AsyncContinuation asyncContinuation, TimeSpan timeout) { - FlushInternal(timeout, asyncContinuation ?? (ex => { })); + FlushInternal(timeout, asyncContinuation); } - private void FlushInternal(TimeSpan flushTimeout, AsyncContinuation asyncContinuation) + private bool FlushInternal(TimeSpan flushTimeout, AsyncContinuation asyncContinuation) { - InternalLogger.Debug("LogFactory Flush with timeout={0} secs", flushTimeout.TotalSeconds); + InternalLogger.Debug("LogFactory Starting Flush with timeout={0} secs", flushTimeout.TotalSeconds); try { LoggingConfiguration config; lock (_syncRoot) { - config = _config; // Flush should not attempt to auto-load Configuration + config = _config; // Flush should not attempt to auto-load Configuration + if (!_configLoaded) + config = null; } + if (config is null) { asyncContinuation?.Invoke(null); + return true; } - else + + asyncContinuation = asyncContinuation is null ? null : AsyncHelpers.PreventMultipleCalls(asyncContinuation); + + using (var waitForFlush = new ManualResetEvent(false)) { - config.FlushAllTargets(flushTimeout, asyncContinuation, ThrowExceptions); + var ev = waitForFlush; + var flushTimeoutHandler = config.FlushAllTargets((ex) => { asyncContinuation?.Invoke(ex); ev?.Set(); }); + if (flushTimeoutHandler is null) + { + asyncContinuation?.Invoke(null); + return true; + } + else + { + bool flushCompleted = waitForFlush.WaitOne(flushTimeout); + ev = null; // Avoid object-disposed on late flush completion + flushTimeoutHandler.Invoke(null); + asyncContinuation?.Invoke(flushCompleted ? null : new NLogRuntimeException("LogFactory Flush Timeout")); + return flushCompleted; + } } } catch (Exception ex) { InternalLogger.Error(ex, "LogFactory failed to flush targets."); asyncContinuation?.Invoke(ex); + return false; + } + } + +#if !NET35 && !NET40 + /// + /// Flush any pending log messages + /// + public System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) + { + InternalLogger.Debug("LogFactory Starting Flush Async"); + + LoggingConfiguration config; + lock (_syncRoot) + { + config = _config; + if (config is null || !_configLoaded) + { +#if NET45 + return System.Threading.Tasks.Task.FromResult(null); +#else + return System.Threading.Tasks.Task.CompletedTask; +#endif + } } + + var flushCompleted = new System.Threading.Tasks.TaskCompletionSource(); + var flushTimeoutHandler = config.FlushAllTargets((ex) => flushCompleted.SetResult(true)); + if (flushTimeoutHandler is null) + { +#if NET45 + return System.Threading.Tasks.Task.FromResult(null); +#else + return System.Threading.Tasks.Task.CompletedTask; +#endif + } + + var timeout = cancellationToken.CanBeCanceled ? -1 : (int)DefaultFlushTimeout.TotalMilliseconds; + var flushTimeout = System.Threading.Tasks.Task.Delay(timeout, cancellationToken).ContinueWith(prevTask => throw new TimeoutException("LogFactory Flush Timeout")); + flushTimeout.ContinueWith(prevTask => { flushTimeoutHandler.Invoke(null); flushCompleted.TrySetCanceled(); }); + return System.Threading.Tasks.TaskExtensions.Unwrap(System.Threading.Tasks.Task.WhenAny(flushCompleted.Task, flushTimeout)); } +#endif /// /// Suspends the logging, and returns object for using-scope so scope-exit calls @@ -688,7 +789,7 @@ internal ITargetWithFilterChain[] BuildLoggerConfiguration(string loggerName) /// private bool _isDisposing; - private void Close(TimeSpan flushTimeout) + private void DisposeInternal(bool closeConfig = true) { if (_isDisposing) { @@ -710,45 +811,38 @@ private void Close(TimeSpan flushTimeout) var oldConfig = _config; if (_configLoaded && oldConfig != null) { - CloseOldConfig(flushTimeout, oldConfig); + if (closeConfig) + CloseOldConfig(oldConfig); + else + InternalLogger.Warn("Target flush timeout. One or more targets did not complete flush operation, skipping target close."); } } finally { Monitor.Exit(_syncRoot); + ConfigurationChanged = null; // Release event listeners } } + else + { + ConfigurationChanged = null; // Release event listeners + } - ConfigurationChanged = null; // Release event listeners + InternalLogger.Info("LogFactory has been disposed."); } - private void CloseOldConfig(TimeSpan flushTimeout, LoggingConfiguration oldConfig) + private void CloseOldConfig(LoggingConfiguration oldConfig) { try { oldConfig.OnConfigurationAssigned(null); - bool attemptClose = true; - - if (flushTimeout > TimeSpan.Zero) - { - attemptClose = oldConfig.FlushAllTargets(flushTimeout, null, false); - } - // Disable all loggers, so things become quiet _config = null; _configLoaded = true; ReconfigExistingLoggers(); - if (!attemptClose) - { - InternalLogger.Warn("Target flush timeout. One or more targets did not complete flush operation, skipping target close."); - } - else - { - // Flush completed within timeout, lets try and close down - oldConfig.Close(); - } + oldConfig.Close(); OnConfigurationChanged(new LoggingConfigurationChangedEventArgs(null, oldConfig)); } @@ -759,7 +853,7 @@ private void CloseOldConfig(TimeSpan flushTimeout, LoggingConfiguration oldConfi } /// - /// Releases unmanaged and - optionally - managed resources. + /// Shutdown logging without flushing async /// /// True to release both managed and unmanaged resources; /// false to release only unmanaged resources. @@ -767,7 +861,7 @@ protected virtual void Dispose(bool disposing) { if (disposing) { - Close(DefaultFlushTimeout); + DisposeInternal(); } } @@ -776,7 +870,7 @@ protected virtual void Dispose(bool disposing) /// public void Shutdown() { - InternalLogger.Info("Shutdown() called. Logger closing..."); + InternalLogger.Info("LogFactory shutting down ..."); if (!_isDisposing && _configLoaded) { lock (_syncRoot) @@ -789,7 +883,7 @@ public void Shutdown() ReconfigExistingLoggers(); // Disable all loggers, so things become quiet } } - InternalLogger.Info("Logger has been closed down."); + InternalLogger.Info("LogFactory shutdown completed."); } /// @@ -1180,20 +1274,22 @@ private void OnStopLogging(object sender, EventArgs args) //Exception: System.AppDomainUnloadedException //Message: Attempted to access an unloaded AppDomain. InternalLogger.Info("AppDomain Shutting down. LogFactory closing..."); - // Domain-Unload has to complete in about 2 secs on Windows-platform, before being terminated. - // Other platforms like Linux will fail when trying to spin up new threads at domain unload. - var flushTimeout = - PlatformDetector.IsWin32 ? TimeSpan.FromMilliseconds(1500) : - TimeSpan.Zero; - Close(flushTimeout); - InternalLogger.Info("LogFactory has been closed."); + bool closeConfig = true; + if (PlatformDetector.IsWin32) + { + // Domain-Unload has to complete in about 2 secs on Windows-platform, before being terminated. + // Other platforms like Linux will fail when trying to spin up new threads at domain unload. + closeConfig = FlushInternal(TimeSpan.FromMilliseconds(1500), null); + } + + DisposeInternal(closeConfig); } catch (Exception ex) { if (ex.MustBeRethrownImmediately()) throw; - InternalLogger.Error(ex, "LogFactory failed to close properly."); + InternalLogger.Error(ex, "LogFactory failed to close down."); } } } diff --git a/src/NLog/Targets/Target.cs b/src/NLog/Targets/Target.cs index 9f9cb4b216..fc6bf2c5b9 100644 --- a/src/NLog/Targets/Target.cs +++ b/src/NLog/Targets/Target.cs @@ -185,27 +185,38 @@ public void Flush(AsyncContinuation asyncContinuation) asyncContinuation = AsyncHelpers.PreventMultipleCalls(asyncContinuation); - lock (SyncRoot) + if (System.Threading.Monitor.TryEnter(SyncRoot, 15000)) { - if (!IsInitialized) - { - // In case target was Closed - asyncContinuation(null); - return; - } - try { - FlushAsync(asyncContinuation); + if (!IsInitialized) + { + // In case target was Closed + asyncContinuation(null); + return; + } + + try + { + FlushAsync(asyncContinuation); + } + catch (Exception exception) + { + if (ExceptionMustBeRethrown(exception)) + throw; + + asyncContinuation(exception); + } } - catch (Exception exception) + finally { - if (ExceptionMustBeRethrown(exception)) - throw; - - asyncContinuation(exception); + System.Threading.Monitor.Exit(SyncRoot); } } + else + { + asyncContinuation(new NLogRuntimeException($"Target {this} failed to flush after lock timeout.")); + } } /// diff --git a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs index 806df4eb65..2f717fa9fa 100644 --- a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs @@ -304,7 +304,8 @@ protected override void CloseTarget() { StopLazyWriterThread(); - if (Monitor.TryEnter(_writeLockObject, 500)) + var lockTimeoutMs = OverflowAction == AsyncTargetWrapperOverflowAction.Discard ? 500 : 1500; + if (Monitor.TryEnter(_writeLockObject, lockTimeoutMs)) { try { @@ -315,6 +316,10 @@ protected override void CloseTarget() Monitor.Exit(_writeLockObject); } } + else + { + InternalLogger.Debug("{0}: Failed to flush after lock timeout", this); + } if (OverflowAction == AsyncTargetWrapperOverflowAction.Block) { @@ -518,32 +523,37 @@ private void ProcessPendingEvents(object state) private void FlushEventsInQueue(object state) { - try + var asyncContinuation = state as AsyncContinuation; + if (Monitor.TryEnter(_writeLockObject, 1500)) { - var asyncContinuation = state as AsyncContinuation; - lock (_writeLockObject) + try { WriteLogEventsInQueue(int.MaxValue, "Flush Async"); if (asyncContinuation != null) base.FlushAsync(asyncContinuation); } - } - catch (Exception exception) - { + catch (Exception exception) + { #if DEBUG - if (exception.MustBeRethrownImmediately()) + if (exception.MustBeRethrownImmediately()) + { + throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) + } +#endif + InternalLogger.Error(exception, "{0}: Error in flush procedure.", this); + } + finally { - throw; // Throwing exceptions here will crash the entire application (.NET 2.0 behavior) + Monitor.Exit(_writeLockObject); + if (TimeToSleepBetweenBatches <= 1 && !_requestQueue.IsEmpty) + { + StartLazyWriterTimer(); + } } -#endif - InternalLogger.Error(exception, "{0}: Error in flush procedure.", this); } - finally + else { - if (TimeToSleepBetweenBatches <= 1 && !_requestQueue.IsEmpty) - { - StartLazyWriterTimer(); - } + asyncContinuation(new NLogRuntimeException($"Target {this} failed to flush after lock timeout.")); } } diff --git a/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs b/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs index a6668271e3..f3f21bd793 100644 --- a/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs @@ -160,8 +160,22 @@ public BufferingTargetWrapper(Target wrappedTarget, int bufferSize, int flushTim /// The asynchronous continuation. protected override void FlushAsync(AsyncContinuation asyncContinuation) { - WriteEventsInBuffer("Flush Async"); - base.FlushAsync(asyncContinuation); + if (Monitor.TryEnter(_lockObject, 1500)) + { + try + { + WriteEventsInBuffer("Flush Async"); + base.FlushAsync(asyncContinuation); + } + finally + { + Monitor.Exit(_lockObject); + } + } + else + { + asyncContinuation(new NLogRuntimeException($"Target {this} failed to flush after lock timeout.")); + } } /// @@ -195,7 +209,22 @@ protected override void CloseTarget() } else { - WriteEventsInBuffer("Closing Target"); + var lockTimeoutMs = OverflowAction == BufferingTargetWrapperOverflowAction.Discard ? 500 : 1500; + if (Monitor.TryEnter(_lockObject, lockTimeoutMs)) + { + try + { + WriteEventsInBuffer("Closing Target"); + } + finally + { + Monitor.Exit(_lockObject); + } + } + else + { + InternalLogger.Debug("{0}: Failed to flush after lock timeout", this); + } } } } @@ -219,7 +248,10 @@ protected override void Write(AsyncLogEventInfo logEvent) // roll over the oldest item. if (OverflowAction == BufferingTargetWrapperOverflowAction.Flush) { - WriteEventsInBuffer("Exceeding BufferSize"); + lock (_lockObject) + { + WriteEventsInBuffer("Exceeding BufferSize"); + } } } else @@ -285,15 +317,12 @@ private void WriteEventsInBuffer(string reason) return; } - lock (_lockObject) + AsyncLogEventInfo[] logEvents = _buffer.DequeueBatch(int.MaxValue); + if (logEvents.Length > 0) { - AsyncLogEventInfo[] logEvents = _buffer.DequeueBatch(int.MaxValue); - if (logEvents.Length > 0) - { - if (reason != null) - InternalLogger.Trace("{0}: Writing {1} events ({2})", this, logEvents.Length, reason); - WrappedTarget.WriteAsyncLogEvents(logEvents); - } + if (reason != null) + InternalLogger.Trace("{0}: Writing {1} events ({2})", this, logEvents.Length, reason); + WrappedTarget.WriteAsyncLogEvents(logEvents); } } } diff --git a/tests/NLog.UnitTests/AsyncHelperTests.cs b/tests/NLog.UnitTests/AsyncHelperTests.cs index 3a3617686d..2f58c5ed9c 100644 --- a/tests/NLog.UnitTests/AsyncHelperTests.cs +++ b/tests/NLog.UnitTests/AsyncHelperTests.cs @@ -133,6 +133,7 @@ public void OneTimeOnlyExceptionInHandlerTest_RethrowExceptionEnabled() } [Fact] + [Obsolete("Marked obsolete on NLog 6.0")] public void ContinuationTimeoutTest() { RetryingIntegrationTest(3, () => @@ -165,6 +166,7 @@ public void ContinuationTimeoutTest() } [Fact] + [Obsolete("Marked obsolete on NLog 6.0")] public void ContinuationTimeoutNotHitTest() { var exceptions = new List(); @@ -192,8 +194,8 @@ public void ContinuationTimeoutNotHitTest() Assert.Null(exceptions[0]); } - [Fact] + [Obsolete("Marked obsolete on NLog 6.0")] public void ContinuationErrorTimeoutNotHitTest() { var exceptions = new List(); diff --git a/tests/NLog.UnitTests/LogFactoryTests.cs b/tests/NLog.UnitTests/LogFactoryTests.cs index 2b20f687c0..a1d0e0ab86 100644 --- a/tests/NLog.UnitTests/LogFactoryTests.cs +++ b/tests/NLog.UnitTests/LogFactoryTests.cs @@ -72,6 +72,88 @@ public void Flush_DoNotThrowExceptionsAndTimeout_DoesNotThrow() Assert.NotNull(timeoutException); } +#if !NET35 && !NET40 + [Fact] + public void FlushAsync_NoConfig_IsCompleted() + { + var logFactory = new LogFactory(); + var task = logFactory.FlushAsync(CancellationToken.None); + Assert.True(task.IsCompleted); + Assert.False(task.IsCanceled); + Assert.False(task.IsFaulted); + } + + [Fact] + public void FlushAsync_EmptyConfig_IsCompleted() + { + var logFactory = new LogFactory(); + logFactory.Configuration = new LoggingConfiguration(); + var task = logFactory.FlushAsync(CancellationToken.None); + Assert.True(task.IsCompleted); + Assert.False(task.IsCanceled); + Assert.False(task.IsFaulted); + } + + [Fact] + public void FlushAsync_SingleTarget_Async() + { + var logFactory = new LogFactory(); + logFactory.Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteToMethodCall((evt, parms) => { })); + var task = logFactory.FlushAsync(CancellationToken.None); + Assert.True(task.Wait(5000)); + } + + [Fact] + public void FlushAsync_TargetTimeout_Async() + { + var logFactory = new LogFactory(); + logFactory.Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteToMethodCall((evt, parms) => { System.Threading.Thread.Sleep(5000); })); + System.Threading.Tasks.Task.Run(() => logFactory.GetCurrentClassLogger().Info("Sleep")).Wait(25); + var task = logFactory.FlushAsync(new CancellationTokenSource(100).Token); + Assert.Throws(() => task.GetAwaiter().GetResult()); + } +#endif + +#if NET6_0_OR_GREATER + [Fact] + public void DisposeAsync_NoConfig_IsCompleted() + { + var logFactory = new LogFactory(); + var task = logFactory.DisposeAsync(); + Assert.True(task.IsCompleted); + Assert.False(task.IsCanceled); + Assert.False(task.IsFaulted); + } + + [Fact] + public void DisposeAsync_EmptyConfig_IsCompleted() + { + var logFactory = new LogFactory(); + logFactory.Configuration = new LoggingConfiguration(); + var task = logFactory.DisposeAsync(); + Assert.True(task.AsTask().Wait(5000)); + } + + [Fact] + public void DisposeAsync_SingleTarget_Async() + { + var logFactory = new LogFactory(); + logFactory.Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteToMethodCall((evt, parms) => { })); + var task = logFactory.DisposeAsync(); + Assert.True(task.AsTask().Wait(5000)); + } + + [Fact] + public void DisposeAsync_TargetTimeout_Async() + { + var logFactory = new LogFactory(); + logFactory.Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteToMethodCall((evt, parms) => { System.Threading.Thread.Sleep(500); })); + System.Threading.Tasks.Task.Run(() => logFactory.GetCurrentClassLogger().Info("Sleep")).Wait(25); + var task = logFactory.DisposeAsync(); + Assert.True(task.AsTask().Wait(5000)); + } +#endif + [Fact] public void InvalidXMLConfiguration_DoesNotThrowErrorWhen_ThrowExceptionFlagIsNotSet() { From 6dd4bb63e51b51d68dfe097ac4874e632a5d53a1 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 23 Mar 2025 21:15:57 +0100 Subject: [PATCH 064/224] ValueFormatter - Skip string-quotes by default (#5740) --- src/NLog/Config/PropertyTypeConverter.cs | 4 ++-- .../Config/ServiceRepositoryExtensions.cs | 2 +- src/NLog/Internal/PropertyHelper.cs | 22 ++----------------- .../LayoutRenderers/DateLayoutRenderer.cs | 2 +- .../ProcessInfoLayoutRenderer.cs | 2 +- .../ProcessTimeLayoutRenderer.cs | 2 +- .../ScopeContextTimingLayoutRenderer.cs | 2 +- .../LayoutRenderers/TimeLayoutRenderer.cs | 2 +- .../LowercaseLayoutRendererWrapper.cs | 2 +- .../Wrappers/ObjectPathRendererWrapper.cs | 2 +- .../UppercaseLayoutRendererWrapper.cs | 2 +- src/NLog/MessageTemplates/ValueFormatter.cs | 11 ++++++---- .../SetupSerializationBuilderExtensions.cs | 11 +++++++++- .../NLog.UnitTests/Config/CultureInfoTests.cs | 1 + .../AllEventPropertiesTests.cs | 2 +- .../ProcessTimeLayoutRendererTests.cs | 2 +- tests/NLog.UnitTests/LogFactoryTests.cs | 2 +- tests/NLog.UnitTests/LoggerTests.cs | 21 ++++++------------ .../MessageTemplates/RendererTests.cs | 10 ++++----- .../MessageTemplates/ValueFormatterTest.cs | 8 +++---- 20 files changed, 50 insertions(+), 62 deletions(-) diff --git a/src/NLog/Config/PropertyTypeConverter.cs b/src/NLog/Config/PropertyTypeConverter.cs index d06b82e942..6b649410d9 100644 --- a/src/NLog/Config/PropertyTypeConverter.cs +++ b/src/NLog/Config/PropertyTypeConverter.cs @@ -196,7 +196,7 @@ private static object ConvertGuid(string format, string propertyString) #endif } - private static object ConvertToCultureInfo(string stringValue) + internal static object ConvertToCultureInfo(string stringValue) { if (StringHelpers.IsNullOrWhiteSpace(stringValue)) return null; @@ -207,7 +207,7 @@ private static object ConvertToCultureInfo(string stringValue) return new CultureInfo(stringValue); } - private static object ConvertToEncoding(string stringValue) + internal static object ConvertToEncoding(string stringValue) { stringValue = stringValue.Trim(); if (string.Equals(stringValue, nameof(System.Text.Encoding.UTF8), StringComparison.OrdinalIgnoreCase)) diff --git a/src/NLog/Config/ServiceRepositoryExtensions.cs b/src/NLog/Config/ServiceRepositoryExtensions.cs index 715d449097..6040d074b1 100644 --- a/src/NLog/Config/ServiceRepositoryExtensions.cs +++ b/src/NLog/Config/ServiceRepositoryExtensions.cs @@ -202,7 +202,7 @@ internal static ServiceRepository RegisterDefaults(this ServiceRepository servic serviceRepository.RegisterSingleton(serviceRepository); serviceRepository.RegisterSingleton(new LogMessageTemplateFormatter(serviceRepository, false, false)); serviceRepository.RegisterJsonConverter(new DefaultJsonSerializer(serviceRepository)); - serviceRepository.RegisterValueFormatter(new MessageTemplates.ValueFormatter(serviceRepository)); + serviceRepository.RegisterValueFormatter(new MessageTemplates.ValueFormatter(serviceRepository, legacyStringQuotes: false)); serviceRepository.RegisterPropertyTypeConverter(PropertyTypeConverter.Instance); serviceRepository.RegisterObjectTypeTransformer(new ObjectReflectionCache(serviceRepository)); return serviceRepository; diff --git a/src/NLog/Internal/PropertyHelper.cs b/src/NLog/Internal/PropertyHelper.cs index 97bb56b59d..0cf0918d7b 100644 --- a/src/NLog/Internal/PropertyHelper.cs +++ b/src/NLog/Internal/PropertyHelper.cs @@ -71,11 +71,11 @@ private static Dictionary> { typeof(Layout), TryParseLayoutValue }, { typeof(SimpleLayout), TryParseLayoutValue }, { typeof(ConditionExpression), TryParseConditionValue }, - { typeof(Encoding), TryParseEncodingValue }, + { typeof(Encoding), (stringvalue, factory) => PropertyTypeConverter.ConvertToEncoding(stringvalue) }, { typeof(string), (stringvalue, factory) => stringvalue }, { typeof(int), (stringvalue, factory) => Convert.ChangeType(stringvalue.Trim(), TypeCode.Int32, CultureInfo.InvariantCulture) }, { typeof(bool), (stringvalue, factory) => Convert.ChangeType(stringvalue.Trim(), TypeCode.Boolean, CultureInfo.InvariantCulture) }, - { typeof(CultureInfo), (stringvalue, factory) => TryParseCultureInfo(stringvalue) }, + { typeof(CultureInfo), (stringvalue, factory) => PropertyTypeConverter.ConvertToCultureInfo(stringvalue) }, { typeof(Type), (stringvalue, factory) => PropertyTypeConverter.ConvertToType(stringvalue.Trim(), true) }, { typeof(LineEndingMode), (stringvalue, factory) => LineEndingMode.FromString(stringvalue.Trim()) }, { typeof(Uri), (stringvalue, factory) => new Uri(stringvalue.Trim()) } @@ -347,24 +347,6 @@ private static bool TryGetEnumValue(Type resultType, string value, out object re } } - private static object TryParseCultureInfo(string stringValue) - { - stringValue = stringValue?.Trim(); - if (string.IsNullOrEmpty(stringValue)) - return CultureInfo.InvariantCulture; - else - return new CultureInfo(stringValue); - } - - private static object TryParseEncodingValue(string stringValue, ConfigurationItemFactory configurationItemFactory) - { - _ = configurationItemFactory; // Discard unreferenced parameter - stringValue = stringValue.Trim(); - if (string.Equals(stringValue, nameof(Encoding.UTF8), StringComparison.OrdinalIgnoreCase)) - stringValue = Encoding.UTF8.WebName; // Support utf8 without hyphen (And not just Utf-8) - return Encoding.GetEncoding(stringValue); - } - private static object TryParseLayoutValue(string stringValue, ConfigurationItemFactory configurationItemFactory) { return new SimpleLayout(stringValue, configurationItemFactory); diff --git a/src/NLog/LayoutRenderers/DateLayoutRenderer.cs b/src/NLog/LayoutRenderers/DateLayoutRenderer.cs index b24f908642..d286cedc2d 100644 --- a/src/NLog/LayoutRenderers/DateLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/DateLayoutRenderer.cs @@ -62,7 +62,7 @@ public DateLayoutRenderer() /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } + public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; /// /// Gets or sets the date format. Can be any argument accepted by DateTime.ToString(format). diff --git a/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs index 967bbf2a59..da5e7e780b 100644 --- a/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs @@ -70,7 +70,7 @@ public class ProcessInfoLayoutRenderer : LayoutRenderer /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } + public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; /// protected override void InitializeLayoutRenderer() diff --git a/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs index fd839cffdc..d8e62a73ce 100644 --- a/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs @@ -60,7 +60,7 @@ public class ProcessTimeLayoutRenderer : LayoutRenderer, IRawValue /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } + public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs index 33b9251168..17bf73a235 100644 --- a/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs @@ -80,7 +80,7 @@ public sealed class ScopeContextTimingLayoutRenderer : LayoutRenderer /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } + public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs b/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs index 04162c425a..d6664f793d 100644 --- a/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs @@ -67,7 +67,7 @@ public class TimeLayoutRenderer : LayoutRenderer, IRawValue /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } + public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs index 00523cc98d..ce258f58be 100644 --- a/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs @@ -72,7 +72,7 @@ public sealed class LowercaseLayoutRendererWrapper : WrapperLayoutRendererBase /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } + public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) diff --git a/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs index d36caeca15..a3b1ceaf1e 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs @@ -87,7 +87,7 @@ public string ObjectPath /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } + public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; /// protected override string Transform(string text) diff --git a/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs index a5b2117efc..521b9afb6e 100644 --- a/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs @@ -77,7 +77,7 @@ public sealed class UppercaseLayoutRendererWrapper : WrapperLayoutRendererBase /// Gets or sets the culture used for rendering. /// /// - public CultureInfo Culture { get; set; } + public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) diff --git a/src/NLog/MessageTemplates/ValueFormatter.cs b/src/NLog/MessageTemplates/ValueFormatter.cs index 58179896dc..6a7c5996bb 100644 --- a/src/NLog/MessageTemplates/ValueFormatter.cs +++ b/src/NLog/MessageTemplates/ValueFormatter.cs @@ -50,12 +50,15 @@ internal sealed class ValueFormatter : IValueFormatter private static readonly IEqualityComparer _referenceEqualsComparer = SingleItemOptimizedHashSet.ReferenceEqualityComparer.Default; private readonly MruCache _enumCache = new MruCache(2000); private readonly IServiceProvider _serviceProvider; + private readonly bool _legacyStringQuotes; + private IJsonConverter JsonConverter => _jsonConverter ?? (_jsonConverter = _serviceProvider.GetService()); private IJsonConverter _jsonConverter; - public ValueFormatter([NotNull] IServiceProvider serviceProvider) + public ValueFormatter([NotNull] IServiceProvider serviceProvider, bool legacyStringQuotes) { _serviceProvider = serviceProvider; + _legacyStringQuotes = legacyStringQuotes; } private const int MaxRecursionDepth = 2; @@ -185,7 +188,7 @@ private void SerializeConvertibleObject(IConvertible value, string format, IForm } case TypeCode.Char: { - bool includeQuotes = format != LiteralFormatSymbol; + bool includeQuotes = _legacyStringQuotes && format != LiteralFormatSymbol; if (includeQuotes) builder.Append('"'); builder.Append(value.ToChar(CultureInfo.InvariantCulture)); if (includeQuotes) builder.Append('"'); @@ -229,9 +232,9 @@ private static void SerializeConvertToString(object value, IFormatProvider forma builder.Append(Convert.ToString(value, formatProvider)); } - private static void SerializeStringObject(string stringValue, string format, StringBuilder builder) + private void SerializeStringObject(string stringValue, string format, StringBuilder builder) { - bool includeQuotes = format != LiteralFormatSymbol; + bool includeQuotes = _legacyStringQuotes && format != LiteralFormatSymbol; if (includeQuotes) builder.Append('"'); builder.Append(stringValue); if (includeQuotes) builder.Append('"'); diff --git a/src/NLog/SetupSerializationBuilderExtensions.cs b/src/NLog/SetupSerializationBuilderExtensions.cs index 61d55cc921..8c06395fa3 100644 --- a/src/NLog/SetupSerializationBuilderExtensions.cs +++ b/src/NLog/SetupSerializationBuilderExtensions.cs @@ -68,7 +68,16 @@ public static ISetupSerializationBuilder RegisterJsonConverter(this ISetupSerial /// public static ISetupSerializationBuilder RegisterValueFormatter(this ISetupSerializationBuilder setupBuilder, IValueFormatter valueFormatter) { - setupBuilder.LogFactory.ServiceRepository.RegisterValueFormatter(valueFormatter ?? new MessageTemplates.ValueFormatter(setupBuilder.LogFactory.ServiceRepository)); + setupBuilder.LogFactory.ServiceRepository.RegisterValueFormatter(valueFormatter ?? new MessageTemplates.ValueFormatter(setupBuilder.LogFactory.ServiceRepository, legacyStringQuotes: false)); + return setupBuilder; + } + + /// + /// Overrides the active to use legacy-mode string-quotes (Before NLog v6) + /// + public static ISetupSerializationBuilder RegisterValueFormatterWithStringQuotes(this ISetupSerializationBuilder setupBuilder) + { + setupBuilder.LogFactory.ServiceRepository.RegisterValueFormatter(new MessageTemplates.ValueFormatter(setupBuilder.LogFactory.ServiceRepository, legacyStringQuotes: true)); return setupBuilder; } diff --git a/tests/NLog.UnitTests/Config/CultureInfoTests.cs b/tests/NLog.UnitTests/Config/CultureInfoTests.cs index 73909cea83..f598821c18 100644 --- a/tests/NLog.UnitTests/Config/CultureInfoTests.cs +++ b/tests/NLog.UnitTests/Config/CultureInfoTests.cs @@ -143,6 +143,7 @@ public void ProcessInfoLayoutRendererCultureTest() var renderer = new ProcessInfoLayoutRenderer(); renderer.Property = ProcessInfoProperty.StartTime; renderer.Format = "d"; + renderer.Culture = null; output = renderer.Render(logEventInfo); Assert.Contains(expected, output); diff --git a/tests/NLog.UnitTests/LayoutRenderers/AllEventPropertiesTests.cs b/tests/NLog.UnitTests/LayoutRenderers/AllEventPropertiesTests.cs index 8b879e405e..ab6a3f242d 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/AllEventPropertiesTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/AllEventPropertiesTests.cs @@ -110,7 +110,7 @@ public void StructuredLoggingProperties() var ev = new LogEventInfo(LogLevel.Info, null, null, "Hello Planet {@planet}", new object[] { planetProperties }); var result = renderer.Render(ev); - Assert.Equal(@"planet=""Name""=""Earth"", ""PlanetType""=""Water-world""", result); + Assert.Equal(@"planet=Name=Earth, PlanetType=Water-world", result); } [Fact] diff --git a/tests/NLog.UnitTests/LayoutRenderers/ProcessTimeLayoutRendererTests.cs b/tests/NLog.UnitTests/LayoutRenderers/ProcessTimeLayoutRendererTests.cs index b34dd7cde0..68ae0fe10e 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/ProcessTimeLayoutRendererTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/ProcessTimeLayoutRendererTests.cs @@ -64,7 +64,7 @@ public void RenderTimeSpanTest(int day, int hour, int min, int sec, int milisec, [Fact] public void RenderProcessTimeLayoutRenderer() { - var layout = "${processtime}"; + var layout = "${processtime:Culture=InvariantCulture}"; var timestamp = LogEventInfo.ZeroDate; System.Threading.Thread.Sleep(16); var logEvent = new LogEventInfo(LogLevel.Debug, "logger1", "message1"); diff --git a/tests/NLog.UnitTests/LogFactoryTests.cs b/tests/NLog.UnitTests/LogFactoryTests.cs index a1d0e0ab86..139df670c8 100644 --- a/tests/NLog.UnitTests/LogFactoryTests.cs +++ b/tests/NLog.UnitTests/LogFactoryTests.cs @@ -108,7 +108,7 @@ public void FlushAsync_TargetTimeout_Async() { var logFactory = new LogFactory(); logFactory.Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteToMethodCall((evt, parms) => { System.Threading.Thread.Sleep(5000); })); - System.Threading.Tasks.Task.Run(() => logFactory.GetCurrentClassLogger().Info("Sleep")).Wait(25); + System.Threading.Tasks.Task.Run(() => logFactory.GetCurrentClassLogger().Info("Sleep")).Wait(50); var task = logFactory.FlushAsync(new CancellationTokenSource(100).Token); Assert.Throws(() => task.GetAwaiter().GetResult()); } diff --git a/tests/NLog.UnitTests/LoggerTests.cs b/tests/NLog.UnitTests/LoggerTests.cs index efe18744d6..8caf3b9eb1 100644 --- a/tests/NLog.UnitTests/LoggerTests.cs +++ b/tests/NLog.UnitTests/LoggerTests.cs @@ -2365,16 +2365,15 @@ public void StructuredEventsTest1() if (enabled == 1) AssertDebugLastMessage("debug", "A|hello from {\"Name\":\"Jane\", \"Childs\":[{\"Name\":\"James\"},{\"Name\":\"Mike\"}]}"); logger.Error("Test structured logging in {NLogVersion} for .NET {NETVersion}", "4.5-alpha01", new[] { 3.5, 4, 4.5 }); - if (enabled == 1) AssertDebugLastMessage("debug", "A|Test structured logging in \"4.5-alpha01\" for .NET 3.5, 4, 4.5"); + if (enabled == 1) AssertDebugLastMessage("debug", "A|Test structured logging in 4.5-alpha01 for .NET 3.5, 4, 4.5"); logger.Error("hello from {FamilyNames}", new Dictionary() { { 1, "James" }, { 2, "Mike" }, { 3, "Jane" } }); - if (enabled == 1) AssertDebugLastMessage("debug", "A|hello from 1=\"James\", 2=\"Mike\", 3=\"Jane\""); + if (enabled == 1) AssertDebugLastMessage("debug", "A|hello from 1=James, 2=Mike, 3=Jane"); logger.Error("message {a} {b}", 1, 2); if (enabled == 1) { AssertDebugLastMessage("debug", "A|message 1 2"); - } logger.Error("message{a}{b}{c}", 1, 2, 3); @@ -2383,20 +2382,17 @@ public void StructuredEventsTest1() AssertDebugLastMessage("debug", "A|message123"); } - logger.Error("message {a} {b} {c}", "1", "2", "3"); if (enabled == 1) { - //todo single quotes - AssertDebugLastMessage("debug", "A|message \"1\" \"2\" \"3\""); + AssertDebugLastMessage("debug", "A|message 1 2 3"); } - logger.Error("message{a}{b}{c}", 1, 2, 3); if (enabled == 1) AssertDebugLastMessage("debug", "A|message123"); logger.Error("message{a,2}{b,-2}{c,1}{d,-1}{f,1}", 1, 2, 3, 4, ""); - if (enabled == 1) AssertDebugLastMessage("debug", "A|message 12 34\"\""); + if (enabled == 1) AssertDebugLastMessage("debug", "A|message 12 34 "); //todo other tests @@ -2518,10 +2514,7 @@ public void StructuredEventsTest1() if (enabled == 1) AssertDebugLastMessage("debug", "A|messagetest"); logger.Error(new Exception("test"), "message {Exception}", "from parameter"); - if (enabled == 1) AssertDebugLastMessage("debug", "A|message \"from parameter\"test"); - - - + if (enabled == 1) AssertDebugLastMessage("debug", "A|message from parametertest"); // logger.Error(delegate { return "message from lambda"; }); // if (enabled == 1) AssertDebugLastMessage("debug", "A|message from lambda"); @@ -2614,7 +2607,7 @@ public void TestStructuredProperties_json_compound() logger.Error("Login request from {Username} for {Application}", new Person("\"John\""), "BestApplicationEver"); - AssertDebugLastMessage("debug", "Login request from \"John\" for \"BestApplicationEver\"{ \"Username\": \"\\\"John\\\"\", \"Application\": \"BestApplicationEver\" }"); + AssertDebugLastMessage("debug", "Login request from \"John\" for BestApplicationEver{ \"Username\": \"\\\"John\\\"\", \"Application\": \"BestApplicationEver\" }"); } [Fact] @@ -2708,7 +2701,7 @@ public void LogEventTemplateHandleTrickyDictionary() dictionary.Add("key 2", 1.3m); LogEventInfo logEventInfo = new LogEventInfo(LogLevel.Info, "Logger", null, "{mybad}", new object[] { dictionary }); var result = logEventInfo.FormattedMessage; - Assert.Contains("\"key1\"=13, \"key 2\"", result); + Assert.Contains("key1=13, key 2", result); } [Fact] diff --git a/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs b/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs index 675a213636..2aad7fb23f 100644 --- a/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs +++ b/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs @@ -48,18 +48,18 @@ public class RendererTests [InlineData(" {1} {0} {0}", new object[] { "a", "b" }, " b a a")] [InlineData(" message {1} {0} {0}", new object[] { "a", "b" }, " message b a a")] [InlineData(" message {1} {0} {0}", new object[] { 'a', 'b' }, " message b a a")] - [InlineData("char {one}", new object[] { 'X' }, "char \"X\"")] + [InlineData("char {one}", new object[] { 'X' }, "char X")] [InlineData("char {one:l}", new object[] { 'X' }, "char X")] [InlineData(" message {{{1}}} {0} {0}", new object[] { "a", "b" }, " message {b} a a")] - [InlineData(" message {{{one}}} {two} {three}", new object[] { "a", "b", "c" }, " message {\"a\"} \"b\" \"c\"")] + [InlineData(" message {{{one}}} {two} {three}", new object[] { "a", "b", "c" }, " message {a} b c")] [InlineData(" message {{{1} {0} {0}}}", new object[] { "a", "b" }, " message {b a a}")] [InlineData(" completed in {time} sec", new object[] { 10 }, " completed in 10 sec")] - [InlineData(" completed task {name} in {time} sec", new object[] { "test", 10 }, " completed task \"test\" in 10 sec")] + [InlineData(" completed task {name} in {time} sec", new object[] { "test", 10 }, " completed task test in 10 sec")] [InlineData(" completed task {name:l} in {time} sec", new object[] { "test", 10 }, " completed task test in 10 sec")] [InlineData(" completed task {0} in {1} sec", new object[] { "test", 10 }, " completed task test in 10 sec")] [InlineData(" completed task {0} in {1:000} sec", new object[] { "test", 10 }, " completed task test in 010 sec")] - [InlineData(" completed task {name} in {time:000} sec", new object[] { "test", 10 }, " completed task \"test\" in 010 sec")] - [InlineData(" completed tasks {tasks} in {time:000} sec", new object[] { new[] { "parsing", "testing", "fixing" }, 10 }, " completed tasks \"parsing\", \"testing\", \"fixing\" in 010 sec")] + [InlineData(" completed task {name} in {time:000} sec", new object[] { "test", 10 }, " completed task test in 010 sec")] + [InlineData(" completed tasks {tasks} in {time:000} sec", new object[] { new[] { "parsing", "testing", "fixing" }, 10 }, " completed tasks parsing, testing, fixing in 010 sec")] [InlineData(" completed tasks {tasks:l} in {time:000} sec", new object[] { new[] { "parsing", "testing", "fixing" }, 10 }, " completed tasks parsing, testing, fixing in 010 sec")] #if !MONO [InlineData(" completed tasks {$tasks} in {time:000} sec", new object[] { new[] { "parsing", "testing", "fixing" }, 10 }, " completed tasks \"System.String[]\" in 010 sec")] diff --git a/tests/NLog.UnitTests/MessageTemplates/ValueFormatterTest.cs b/tests/NLog.UnitTests/MessageTemplates/ValueFormatterTest.cs index 4bf47e405c..47e71c14bf 100644 --- a/tests/NLog.UnitTests/MessageTemplates/ValueFormatterTest.cs +++ b/tests/NLog.UnitTests/MessageTemplates/ValueFormatterTest.cs @@ -196,7 +196,7 @@ public RecursiveTest(int integer) private static ValueFormatter CreateValueFormatter() { - return new ValueFormatter(LogManager.LogFactory.ServiceRepository); + return new ValueFormatter(LogManager.LogFactory.ServiceRepository, legacyStringQuotes: false); } [Fact] @@ -335,7 +335,7 @@ public void TestSerializationOfStringIsSuccessful() StringBuilder builder = new StringBuilder(); var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); - Assert.Equal("\"str\"", builder.ToString()); + Assert.Equal("str", builder.ToString()); } [Fact] @@ -355,7 +355,7 @@ public void TestSerialisationOfIConvertibleStringObjectIsSuccessful() StringBuilder builder = new StringBuilder(); var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); - var expectedValue = $"\"{typeof(Test).FullName}\""; + var expectedValue = $"{typeof(Test).FullName}"; Assert.Equal(expectedValue, builder.ToString()); } @@ -376,7 +376,7 @@ public void TestSerialisationOfIConvertibleCharObjectIsSuccessful() StringBuilder builder = new StringBuilder(); var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); - Assert.Equal("\"t\"", builder.ToString()); + Assert.Equal("t", builder.ToString()); } [Theory] From c1a085f4eebd442a2b25b902f1f5c4fa2f442c9a Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 23 Mar 2025 22:17:05 +0100 Subject: [PATCH 065/224] ExceptionLayoutRenderer - Handle Exception properties like StackTrace can throw (#5741) --- .../ExceptionLayoutRenderer.cs | 77 ++++++++++++++----- 1 file changed, 59 insertions(+), 18 deletions(-) diff --git a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs index 1f3f02778d..0b1ec9a917 100644 --- a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs @@ -73,8 +73,8 @@ public class ExceptionLayoutRenderer : LayoutRenderer, IRawValue private static readonly Dictionary> _renderingfunctions = new Dictionary>() { - {ExceptionRenderingFormat.Message, (layout, sb, ex, aggex) => layout.AppendMessage(sb, ex)}, - {ExceptionRenderingFormat.Type, (layout, sb, ex, aggex) => layout.AppendType(sb, ex)}, + { ExceptionRenderingFormat.Message, (layout, sb, ex, aggex) => layout.AppendMessage(sb, ex)}, + { ExceptionRenderingFormat.Type, (layout, sb, ex, aggex) => layout.AppendType(sb, ex)}, { ExceptionRenderingFormat.ShortType, (layout, sb, ex, aggex) => layout.AppendShortType(sb, ex)}, { ExceptionRenderingFormat.ToString, (layout, sb, ex, aggex) => layout.AppendToString(sb, ex)}, { ExceptionRenderingFormat.Method, (layout, sb, ex, aggex) => layout.AppendMethod(sb, ex)}, @@ -355,11 +355,10 @@ protected virtual void AppendMessage(StringBuilder sb, Exception ex) } catch (Exception exception) { - var message = - $"Exception in {typeof(ExceptionLayoutRenderer).FullName}.AppendMessage(): {exception.GetType().FullName}."; - sb.Append("NLog message: "); - sb.Append(message); - InternalLogger.Warn(exception, message); + InternalLogger.Warn(exception, "Exception-LayoutRenderer Could not output Message for Exception: {0}", ex.GetType()); + sb.Append(ex.GetType().ToString()); + sb.Append(" Message-property threw "); + sb.Append(exception.GetType().ToString()); } } @@ -371,7 +370,17 @@ protected virtual void AppendMessage(StringBuilder sb, Exception ex) [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow callsite logic", "IL2026")] protected virtual void AppendMethod(StringBuilder sb, Exception ex) { - sb.Append(ex.TargetSite?.ToString()); + try + { + sb.Append(ex.TargetSite?.ToString()); + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "Exception-LayoutRenderer Could not output TargetSite for Exception: {0}", ex.GetType()); + sb.Append(ex.GetType().ToString()); + sb.Append(" TargetSite-property threw "); + sb.Append(exception.GetType().ToString()); + } } /// @@ -381,7 +390,17 @@ protected virtual void AppendMethod(StringBuilder sb, Exception ex) /// The Exception whose stack trace should be appended. protected virtual void AppendStackTrace(StringBuilder sb, Exception ex) { - sb.Append(ex.StackTrace); + try + { + sb.Append(ex.StackTrace); + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "Exception-LayoutRenderer Could not output StackTrace for Exception: {0}", ex.GetType()); + sb.Append(ex.GetType().ToString()); + sb.Append(" StackTrace-property threw "); + sb.Append(exception.GetType().ToString()); + } } /// @@ -391,20 +410,25 @@ protected virtual void AppendStackTrace(StringBuilder sb, Exception ex) /// The Exception whose call to ToString() should be appended. protected virtual void AppendToString(StringBuilder sb, Exception ex) { + string exceptionMessage = string.Empty; + Exception innerException = null; try { + exceptionMessage = ex.Message; + innerException = ex.InnerException; sb.Append(ex.ToString()); } catch (Exception exception) { - var message = - $"Exception in {typeof(ExceptionLayoutRenderer).FullName}.AppendToString(): {exception.GetType().FullName}."; - sb.Append("NLog message: "); - sb.Append(message); - InternalLogger.Warn(exception, message); + InternalLogger.Warn(exception, "Exception-LayoutRenderer Could not output ToString for Exception: {0}", ex.GetType()); + sb.Append($"{ex.GetType()}: {exceptionMessage}"); + if (innerException != null) + { + sb.Append(Environment.NewLine); + AppendToString(sb, innerException); + } } - } /// @@ -414,7 +438,7 @@ protected virtual void AppendToString(StringBuilder sb, Exception ex) /// The Exception whose type should be appended. protected virtual void AppendType(StringBuilder sb, Exception ex) { - sb.Append(ex.GetType().FullName); + sb.Append(ex.GetType().ToString()); } /// @@ -434,7 +458,17 @@ protected virtual void AppendShortType(StringBuilder sb, Exception ex) /// The Exception whose source should be appended. protected virtual void AppendSource(StringBuilder sb, Exception ex) { - sb.Append(ex.Source); + try + { + sb.Append(ex.Source); + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "Exception-LayoutRenderer Could not output Source for Exception: {0}", ex.GetType()); + sb.Append(ex.GetType().ToString()); + sb.Append(" Source-property threw "); + sb.Append(exception.GetType().ToString()); + } } /// @@ -477,7 +511,14 @@ protected virtual void AppendData(StringBuilder sb, Exception ex) foreach (var key in ex.Data.Keys) { sb.Append(separator); - sb.AppendFormat("{0}: {1}", key, ex.Data[key]); + try + { + sb.AppendFormat("{0}: {1}", key, ex.Data[key]); + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "Exception-LayoutRenderer Could not output Data-collection for Exception: {0}", ex.GetType()); + } separator = ExceptionDataSeparator; } } From ca4747ed09565fcbeb6faa0406af70c7b7f0dbf5 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 24 Mar 2025 20:23:36 +0100 Subject: [PATCH 066/224] ExceptionLayoutRenderer - Handle if Data-collection-item throws on ToString (#5744) --- src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs index 0b1ec9a917..a8c8402b4f 100644 --- a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs @@ -513,7 +513,8 @@ protected virtual void AppendData(StringBuilder sb, Exception ex) sb.Append(separator); try { - sb.AppendFormat("{0}: {1}", key, ex.Data[key]); + sb.AppendFormat("{0}: ", key); + sb.AppendFormat("{0}", ex.Data[key]); } catch (Exception exception) { From 5456e5002d9472b424f70ab9ddaeaa97579b249d Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 25 Mar 2025 20:17:21 +0100 Subject: [PATCH 067/224] ReplaceLayoutRendererWrapper - SearchFor and ReplaceWith with basic Layout support (#5748) --- src/NLog/Config/XmlLoggingConfiguration.cs | 2 +- .../AllEventPropertiesLayoutRenderer.cs | 23 ++++++- .../ExceptionLayoutRenderer.cs | 62 +++++++++++++++---- .../ScopeContextNestedStatesLayoutRenderer.cs | 26 ++++++-- .../StackTraceLayoutRenderer.cs | 46 ++++++++------ .../Wrappers/ReplaceLayoutRendererWrapper.cs | 38 ++++++++++-- src/NLog/Layouts/SimpleLayout.cs | 35 ++++++++++- .../LayoutRenderers/Wrappers/ReplaceTests.cs | 12 ++++ 8 files changed, 197 insertions(+), 47 deletions(-) diff --git a/src/NLog/Config/XmlLoggingConfiguration.cs b/src/NLog/Config/XmlLoggingConfiguration.cs index accdf83849..409fb5ab4e 100644 --- a/src/NLog/Config/XmlLoggingConfiguration.cs +++ b/src/NLog/Config/XmlLoggingConfiguration.cs @@ -417,7 +417,7 @@ private void ParseIncludeElement(ILoggingConfigurationElement includeElement, st try { newFileName = ExpandSimpleVariables(newFileName); - newFileName = SimpleLayout.Evaluate(newFileName); + newFileName = SimpleLayout.Evaluate(newFileName, this); var fullNewFileName = newFileName; if (baseDirectory != null) { diff --git a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs index e2170c1ffb..2aafa7b9bf 100644 --- a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs @@ -62,7 +62,6 @@ public class AllEventPropertiesLayoutRenderer : LayoutRenderer /// public AllEventPropertiesLayoutRenderer() { - Separator = ", "; Format = "[key]=[value]"; Exclude = new HashSet(StringComparer.OrdinalIgnoreCase); } @@ -71,7 +70,17 @@ public AllEventPropertiesLayoutRenderer() /// Gets or sets string that will be used to separate key/value pairs. /// /// - public string Separator { get; set; } + public string Separator + { + get => _separatorOriginal ?? _separator; + set + { + _separatorOriginal = value; + _separator = Layouts.SimpleLayout.Evaluate(value, LoggingConfiguration, throwConfigExceptions: false); + } + } + private string _separator = ", "; + private string _separatorOriginal; /// /// Get or set if empty values should be included. @@ -142,6 +151,14 @@ public string Format /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + /// + protected override void InitializeLayoutRenderer() + { + base.InitializeLayoutRenderer(); + if (_separatorOriginal != null) + _separator = Layouts.SimpleLayout.Evaluate(_separatorOriginal, LoggingConfiguration); + } + /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { @@ -194,7 +211,7 @@ private bool AppendProperty(StringBuilder builder, object propertyKey, object pr if (includeSeparator) { - builder.Append(Separator); + builder.Append(_separator); } if (nonStandardFormat) diff --git a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs index a8c8402b4f..02f966b76d 100644 --- a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs @@ -138,7 +138,6 @@ public string Format public string InnerFormat { get => _innerFormat; - set { _innerFormat = value; @@ -150,15 +149,33 @@ public string InnerFormat /// Gets or sets the separator used to concatenate parts specified in the Format. /// /// - public string Separator { get => _seperator; set => _seperator = new NLog.Layouts.SimpleLayout(value).Render(LogEventInfo.CreateNullEvent()); } - private string _seperator = " "; + public string Separator + { + get => _separatorOriginal ?? _separator; + set + { + _separatorOriginal = value; + _separator = Layouts.SimpleLayout.Evaluate(value, LoggingConfiguration, throwConfigExceptions: false); + } + } + private string _separator = " "; + private string _separatorOriginal; /// /// Gets or sets the separator used to concatenate exception data specified in the Format. /// /// - public string ExceptionDataSeparator { get => _exceptionDataSeparator; set => _exceptionDataSeparator = new NLog.Layouts.SimpleLayout(value).Render(LogEventInfo.CreateNullEvent()); } + public string ExceptionDataSeparator + { + get => _exceptionDataSeparatorOriginal ?? _exceptionDataSeparator; + set + { + _exceptionDataSeparatorOriginal = value; + _exceptionDataSeparator = Layouts.SimpleLayout.Evaluate(value, LoggingConfiguration, throwConfigExceptions: false); + } + } private string _exceptionDataSeparator = ";"; + private string _exceptionDataSeparatorOriginal; /// /// Gets or sets the maximum number of inner exceptions to include in the output. @@ -223,6 +240,16 @@ private Exception GetTopException(LogEventInfo logEvent) return BaseException ? logEvent.Exception?.GetBaseException() : logEvent.Exception; } + /// + protected override void InitializeLayoutRenderer() + { + base.InitializeLayoutRenderer(); + if (_separatorOriginal != null) + _separator = Layouts.SimpleLayout.Evaluate(_separatorOriginal, LoggingConfiguration); + if (_exceptionDataSeparatorOriginal != null) + _exceptionDataSeparator = Layouts.SimpleLayout.Evaluate(_exceptionDataSeparatorOriginal, LoggingConfiguration); + } + /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { @@ -335,7 +362,7 @@ private void AppendException(Exception currentException, List 0 && !ReferenceEquals(ex, aggregateException)) { AppendData(builder, aggregateException); - builder.Append(Separator); + builder.Append(_separator); } AppendData(builder, ex); } @@ -514,13 +541,13 @@ protected virtual void AppendData(StringBuilder sb, Exception ex) try { sb.AppendFormat("{0}: ", key); + separator = _exceptionDataSeparator; sb.AppendFormat("{0}", ex.Data[key]); } catch (Exception exception) { InternalLogger.Warn(exception, "Exception-LayoutRenderer Could not output Data-collection for Exception: {0}", ex.GetType()); } - separator = ExceptionDataSeparator; } } } @@ -549,13 +576,22 @@ protected virtual void AppendProperties(StringBuilder sb, Exception ex) if (ExcludeDefaultProperties.Contains(property.Name)) continue; - var propertyValue = property.Value?.ToString(); - if (string.IsNullOrEmpty(propertyValue)) - continue; + try + { + var propertyValue = property.Value?.ToString(); + if (string.IsNullOrEmpty(propertyValue)) + continue; - sb.Append(separator); - sb.AppendFormat("{0}: {1}", property.Name, propertyValue); - separator = ExceptionDataSeparator; + sb.Append(separator); + sb.Append(property.Name); + separator = _exceptionDataSeparator; + sb.Append(": "); + sb.AppendFormat("{0}", propertyValue); + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "Exception-LayoutRenderer Could not output Property-collection for Exception: {0}", ex.GetType()); + } } } diff --git a/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs index 3798f063c1..4ed47cff94 100644 --- a/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs @@ -38,7 +38,6 @@ namespace NLog.LayoutRenderers using System.Globalization; using System.Text; using NLog.Internal; - using NLog.Layouts; /// /// Renders the nested states from like a callstack @@ -68,8 +67,17 @@ public sealed class ScopeContextNestedStatesLayoutRenderer : LayoutRenderer /// Gets or sets the separator to be used for concatenating nested logical context output. /// /// - public string Separator { get => _separator?.OriginalText; set => _separator = new SimpleLayout(value ?? string.Empty); } - private SimpleLayout _separator = new SimpleLayout(" "); + public string Separator + { + get => _separatorOriginal ?? _separator; + set + { + _separatorOriginal = value; + _separator = Layouts.SimpleLayout.Evaluate(value, LoggingConfiguration, throwConfigExceptions: false); + } + } + private string _separator = " "; + private string _separatorOriginal; /// /// Gets or sets how to format each nested state. Ex. like JSON = @ @@ -83,6 +91,14 @@ public sealed class ScopeContextNestedStatesLayoutRenderer : LayoutRenderer /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + /// + protected override void InitializeLayoutRenderer() + { + base.InitializeLayoutRenderer(); + if (_separatorOriginal != null) + _separator = Layouts.SimpleLayout.Evaluate(_separatorOriginal, LoggingConfiguration); + } + /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { @@ -122,7 +138,7 @@ private void AppendNestedStates(StringBuilder builder, IList messages, i string itemSeparator = null; if (formatAsJson) { - separator = _separator?.Render(logEvent) ?? string.Empty; + separator = _separator ?? string.Empty; builder.Append('['); builder.Append(separator); itemSeparator = "," + separator; @@ -140,7 +156,7 @@ private void AppendNestedStates(StringBuilder builder, IList messages, i builder.Append(Convert.ToString(messages[i])); // Special support for Microsoft Extension Logging ILogger.BeginScope else AppendFormattedValue(builder, logEvent, messages[i], Format, Culture); - currentSeparator = itemSeparator ?? _separator?.Render(logEvent) ?? string.Empty; + currentSeparator = itemSeparator ?? _separator ?? string.Empty; } } finally diff --git a/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs b/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs index 7771e5a9cb..2fec0d112f 100644 --- a/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs @@ -37,7 +37,6 @@ namespace NLog.LayoutRenderers using System.Text; using NLog.Config; using NLog.Internal; - using NLog.Layouts; /// /// Stack trace renderer. @@ -72,8 +71,17 @@ public class StackTraceLayoutRenderer : LayoutRenderer, IUsesStackTrace /// Gets or sets the stack frame separator string. /// /// - public string Separator { get => _separator?.OriginalText; set => _separator = new SimpleLayout(value ?? string.Empty); } - private SimpleLayout _separator = new SimpleLayout(" => "); + public string Separator + { + get => _separatorOriginal ?? _separator; + set + { + _separatorOriginal = value; + _separator = Layouts.SimpleLayout.Evaluate(value, LoggingConfiguration, throwConfigExceptions: false); + } + } + private string _separator = " => "; + private string _separatorOriginal; /// /// Logger should capture StackTrace, if it was not provided manually @@ -102,6 +110,14 @@ StackTraceUsage IUsesStackTrace.StackTraceUsage } } + /// + protected override void InitializeLayoutRenderer() + { + base.InitializeLayoutRenderer(); + if (_separatorOriginal != null) + _separator = Layouts.SimpleLayout.Evaluate(_separatorOriginal, LoggingConfiguration); + } + /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { @@ -122,15 +138,15 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) switch (Format) { case StackTraceFormat.Raw: - AppendRaw(builder, stackFrameList, logEvent); + AppendRaw(builder, stackFrameList); break; case StackTraceFormat.Flat: - AppendFlat(builder, stackFrameList, logEvent); + AppendFlat(builder, stackFrameList); break; case StackTraceFormat.DetailedFlat: - AppendDetailedFlat(builder, stackFrameList, logEvent); + AppendDetailedFlat(builder, stackFrameList); break; } } @@ -162,7 +178,7 @@ public StackFrameList(StackTrace stackTrace, int startingFrame, int endingFrame, } } - private void AppendRaw(StringBuilder builder, StackFrameList stackFrameList, LogEventInfo logEvent) + private void AppendRaw(StringBuilder builder, StackFrameList stackFrameList) { string separator = null; for (int i = 0; i < stackFrameList.Count; ++i) @@ -170,14 +186,12 @@ private void AppendRaw(StringBuilder builder, StackFrameList stackFrameList, Log builder.Append(separator); StackFrame f = stackFrameList[i]; builder.Append(f.ToString()); - separator = separator ?? _separator?.Render(logEvent) ?? string.Empty; + separator = separator ?? _separator ?? string.Empty; } } - private void AppendFlat(StringBuilder builder, StackFrameList stackFrameList, LogEventInfo logEvent) + private void AppendFlat(StringBuilder builder, StackFrameList stackFrameList) { - string separator = null; - bool first = true; for (int i = 0; i < stackFrameList.Count; ++i) { @@ -189,8 +203,7 @@ private void AppendFlat(StringBuilder builder, StackFrameList stackFrameList, Lo if (!first) { - separator = separator ?? _separator?.Render(logEvent) ?? string.Empty; - builder.Append(separator); + builder.Append(_separator); } var type = method.DeclaringType; @@ -209,10 +222,8 @@ private void AppendFlat(StringBuilder builder, StackFrameList stackFrameList, Lo } } - private void AppendDetailedFlat(StringBuilder builder, StackFrameList stackFrameList, LogEventInfo logEvent) + private void AppendDetailedFlat(StringBuilder builder, StackFrameList stackFrameList) { - string separator = null; - bool first = true; for (int i = 0; i < stackFrameList.Count; ++i) { @@ -224,8 +235,7 @@ private void AppendDetailedFlat(StringBuilder builder, StackFrameList stackFrame if (!first) { - separator = separator ?? _separator?.Render(logEvent) ?? string.Empty; - builder.Append(separator); + builder.Append(_separator); } builder.Append('['); builder.Append(method); diff --git a/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs index 1c3acdf82e..c06b97af9e 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs @@ -58,14 +58,34 @@ public sealed class ReplaceLayoutRendererWrapper : WrapperLayoutRendererBase /// The text search for. /// [RequiredParameter] - public string SearchFor { get; set; } + public string SearchFor + { + get => _searchForOriginal ?? _searchFor; + set + { + _searchForOriginal = value; + _searchFor = Layouts.SimpleLayout.Evaluate(value, LoggingConfiguration, throwConfigExceptions: false); + } + } + private string _searchFor; + private string _searchForOriginal; /// /// Gets or sets the replacement string. /// /// The replacement string. /// - public string ReplaceWith { get; set; } = string.Empty; + public string ReplaceWith + { + get => _replaceWithOriginal ?? _replaceWith; + set + { + _replaceWithOriginal = value; + _replaceWith = Layouts.SimpleLayout.Evaluate(value, LoggingConfiguration, throwConfigExceptions: false); + } + } + private string _replaceWith = string.Empty; + private string _replaceWithOriginal; /// /// Gets or sets a value indicating whether to ignore case. @@ -81,17 +101,27 @@ public sealed class ReplaceLayoutRendererWrapper : WrapperLayoutRendererBase /// public bool WholeWords { get; set; } + /// + protected override void InitializeLayoutRenderer() + { + base.InitializeLayoutRenderer(); + if (_searchForOriginal != null) + _searchFor = Layouts.SimpleLayout.Evaluate(_searchForOriginal, LoggingConfiguration); + if (_replaceWithOriginal != null) + _replaceWith = Layouts.SimpleLayout.Evaluate(_replaceWithOriginal, LoggingConfiguration); + } + /// protected override string Transform(string text) { if (IgnoreCase || WholeWords) { var stringComparer = IgnoreCase ? StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture; - return StringHelpers.Replace(text, SearchFor, ReplaceWith, stringComparer, WholeWords); + return StringHelpers.Replace(text, _searchFor, _replaceWith, stringComparer, WholeWords); } else { - return text.Replace(SearchFor, ReplaceWith); + return text.Replace(_searchFor, _replaceWith); } } } diff --git a/src/NLog/Layouts/SimpleLayout.cs b/src/NLog/Layouts/SimpleLayout.cs index 73a0633cfc..aab798cccf 100644 --- a/src/NLog/Layouts/SimpleLayout.cs +++ b/src/NLog/Layouts/SimpleLayout.cs @@ -218,8 +218,7 @@ public static string Escape([Localizable(false)] string text) /// values provided by the appropriate layout renderers. public static string Evaluate([Localizable(false)] string text, LogEventInfo logEvent) { - var layout = new SimpleLayout(text); - return layout.Render(logEvent); + return Evaluate(text, null, logEvent); } /// @@ -231,7 +230,37 @@ public static string Evaluate([Localizable(false)] string text, LogEventInfo log /// values provided by the appropriate layout renderers. public static string Evaluate([Localizable(false)] string text) { - return Evaluate(text, LogEventInfo.CreateNullEvent()); + return Evaluate(text, null); + } + + internal static string Evaluate(string text, LoggingConfiguration loggingConfiguration, LogEventInfo logEventInfo = null, bool? throwConfigExceptions = null) + { + try + { + if (string.IsNullOrEmpty(text)) + return string.Empty; + + if (text.IndexOf('$') < 0 || text.IndexOf('{') < 0 || text.IndexOf('}') < 0) + return text; + + throwConfigExceptions = throwConfigExceptions ?? loggingConfiguration?.LogFactory.ThrowConfigExceptions; + var layout = Layout.FromString(text, throwConfigExceptions ?? LogManager.ThrowConfigExceptions ?? LogManager.ThrowExceptions); + layout.Initialize(loggingConfiguration); + return layout.Render(logEventInfo ?? LogEventInfo.CreateNullEvent()); + } + catch (NLogConfigurationException ex) + { + if (throwConfigExceptions ?? LogManager.ThrowConfigExceptions ?? LogManager.ThrowExceptions) + throw; + + InternalLogger.Warn(ex, "Failed to Evaluate SimpleLayout: {0}", text); + return text; + } + catch (Exception ex) + { + InternalLogger.Warn(ex, "Failed to Evaluate SimpleLayout: {0}", text); + return text; + } } /// diff --git a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/ReplaceTests.cs b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/ReplaceTests.cs index d41f05179a..62fe5b1255 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/ReplaceTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/ReplaceTests.cs @@ -77,5 +77,17 @@ public void ReplaceTestWholeWordsWithoutRegEx() // Assert Assert.Equal("BAR bar bar foobar barfoo bar BAR", result); } + + [Fact] + public void ReplaceTestWithSecretVariable() + { + var memoryTarget = new NLog.Targets.MemoryTarget() { Layout = "${replace:inner=${message}:searchFor=${var:secret}:replaceWith=******" }; + var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(""). + LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memoryTarget)).LogFactory; + + logFactory.GetLogger(nameof(ReplaceTestWithSecretVariable)).Info("My name is secret"); + Assert.Single(memoryTarget.Logs); + Assert.Equal("My name is ******", memoryTarget.Logs[0]); + } } } From 9ec2f322ed68582b7e93a352af255c2a6d40a3c2 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 27 Mar 2025 20:59:43 +0100 Subject: [PATCH 068/224] ValueFormatter - Skip string-quotes by default (unit-test) (#5750) --- src/NLog/Common/InternalLogger.cs | 4 +--- src/NLog/LogFactory.cs | 2 ++ tests/NLog.UnitTests/MessageTemplates/RendererTests.cs | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/NLog/Common/InternalLogger.cs b/src/NLog/Common/InternalLogger.cs index 5c35af7790..0fa78c82bf 100644 --- a/src/NLog/Common/InternalLogger.cs +++ b/src/NLog/Common/InternalLogger.cs @@ -47,9 +47,7 @@ namespace NLog.Common /// /// Writes to file, console or custom text writer (see ) /// - /// - /// Don't use as that can lead to recursive calls - stackoverflow - /// + /// Documentation on NLog Wiki public static partial class InternalLogger { private static readonly object LockObject = new object(); diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 12ef1f9070..e2b952ffcf 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -709,6 +709,8 @@ public System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken var timeout = cancellationToken.CanBeCanceled ? -1 : (int)DefaultFlushTimeout.TotalMilliseconds; var flushTimeout = System.Threading.Tasks.Task.Delay(timeout, cancellationToken).ContinueWith(prevTask => throw new TimeoutException("LogFactory Flush Timeout")); flushTimeout.ContinueWith(prevTask => { flushTimeoutHandler.Invoke(null); flushCompleted.TrySetCanceled(); }); + if (cancellationToken.IsCancellationRequested) + throw new TimeoutException("LogFactory Flush Timeout"); return System.Threading.Tasks.TaskExtensions.Unwrap(System.Threading.Tasks.Task.WhenAny(flushCompleted.Task, flushTimeout)); } #endif diff --git a/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs b/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs index 2aad7fb23f..2c039f93f9 100644 --- a/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs +++ b/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs @@ -50,6 +50,7 @@ public class RendererTests [InlineData(" message {1} {0} {0}", new object[] { 'a', 'b' }, " message b a a")] [InlineData("char {one}", new object[] { 'X' }, "char X")] [InlineData("char {one:l}", new object[] { 'X' }, "char X")] + [InlineData("char {@one}", new object[] { 'X' }, "char \"X\"")] [InlineData(" message {{{1}}} {0} {0}", new object[] { "a", "b" }, " message {b} a a")] [InlineData(" message {{{one}}} {two} {three}", new object[] { "a", "b", "c" }, " message {a} b c")] [InlineData(" message {{{1} {0} {0}}}", new object[] { "a", "b" }, " message {b a a}")] From 85c195f606e5f274ad50bd325a53017e4559a828 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 30 Mar 2025 14:16:56 +0200 Subject: [PATCH 069/224] NetworkTarget - Added support for loading SSL certificate from file (#5752) --- .../Internal/PlatformDetector.cs | 2 + .../NetworkSenders/HttpNetworkSender.cs | 29 +++++ .../NetworkSenders/INetworkSenderFactory.cs | 4 +- .../NetworkSenders/NetworkSenderFactory.cs | 8 +- .../NetworkSenders/SslSocketProxy.cs | 34 +++-- .../NetworkSenders/TcpNetworkSender.cs | 5 +- src/NLog.Targets.Network/NetworkTarget.cs | 119 +++++++++++++++++- .../NetworkSenders/HttpNetworkSenderTests.cs | 7 +- .../NetworkTargetTests.cs | 49 +++++++- 9 files changed, 231 insertions(+), 26 deletions(-) diff --git a/src/NLog.Targets.Network/Internal/PlatformDetector.cs b/src/NLog.Targets.Network/Internal/PlatformDetector.cs index 20b7de4c53..14f314873b 100644 --- a/src/NLog.Targets.Network/Internal/PlatformDetector.cs +++ b/src/NLog.Targets.Network/Internal/PlatformDetector.cs @@ -62,6 +62,8 @@ private static PlatformOS GetCurrentPlatformOS() return PlatformOS.MacOSX; if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) return PlatformOS.Windows; + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Create("ANDROID"))) + return PlatformOS.Linux; #endif return PlatformOS.Unknown; } diff --git a/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs index ea1d616a8a..d61aff27f4 100644 --- a/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs @@ -34,6 +34,9 @@ namespace NLog.Internal.NetworkSenders { using System; + using System.Net; + using System.Net.Security; + using System.Security.Cryptography.X509Certificates; /// /// Network sender which uses HTTP or HTTPS POST. @@ -46,6 +49,8 @@ internal sealed class HttpNetworkSender : QueuedNetworkSender internal TimeSpan SendTimeout { get; set; } + internal X509Certificate2Collection SslCertificateOverride { get; set; } + /// /// Initializes a new instance of the class. /// @@ -70,6 +75,18 @@ protected override void BeginRequest(NetworkRequestArgs eventArgs) webRequest.Timeout = (int)SendTimeout.TotalMilliseconds; } + if (SslCertificateOverride != null) + { + if (webRequest is HttpWebRequest httpWebRequest) + { + if (SslCertificateOverride.Count > 0) + httpWebRequest.ClientCertificates = SslCertificateOverride; +#if NET45_OR_GREATER || NETSTANDARD + httpWebRequest.ServerCertificateValidationCallback = UserCertificateValidationCallback; +#endif + } + } + AsyncCallback onResponse = r => { @@ -132,5 +149,17 @@ private void CompleteRequest(Common.AsyncContinuation asyncContinuation) BeginRequest(nextRequest.Value); } } + + private static bool UserCertificateValidationCallback(object sender, object certificate, object chain, SslPolicyErrors sslPolicyErrors) + { + if (sslPolicyErrors == SslPolicyErrors.None) + return true; + + Common.InternalLogger.Warn("SSL certificate errors were encountered when establishing connection to the server: {0}, Certificate: {1}", sslPolicyErrors, certificate); + if (certificate is null) + return false; + + return true; + } } } diff --git a/src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs b/src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs index 7827ae2d5a..82da268fa9 100644 --- a/src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs +++ b/src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs @@ -34,6 +34,7 @@ namespace NLog.Internal.NetworkSenders { using System; + using System.Security.Cryptography.X509Certificates; using NLog.Targets; /// @@ -49,11 +50,12 @@ internal interface INetworkSenderFactory /// The overflow action when reaching maximum queue size. /// The maximum message size. /// SSL protocols for TCP + /// SSL Certificate override /// KeepAliveTime for TCP /// SendTimeout for TCP /// /// A newly created network sender. /// - QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQueueOverflowAction onQueueOverflow, int maxMessageSize, System.Security.Authentication.SslProtocols sslProtocols, TimeSpan keepAliveTime, TimeSpan sendTimeout); + QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQueueOverflowAction onQueueOverflow, int maxMessageSize, System.Security.Authentication.SslProtocols sslProtocols, X509Certificate2Collection sslCertificateOverride, TimeSpan keepAliveTime, TimeSpan sendTimeout); } } diff --git a/src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs b/src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs index 8840b8783d..7f1e2ed456 100644 --- a/src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs +++ b/src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs @@ -35,6 +35,7 @@ namespace NLog.Internal.NetworkSenders { using System; using System.Net.Sockets; + using System.Security.Cryptography.X509Certificates; using NLog.Targets; /// @@ -45,7 +46,7 @@ internal sealed class NetworkSenderFactory : INetworkSenderFactory public static readonly INetworkSenderFactory Default = new NetworkSenderFactory(); /// - public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQueueOverflowAction onQueueOverflow, int maxMessageSize, System.Security.Authentication.SslProtocols sslProtocols, TimeSpan keepAliveTime, TimeSpan sendTimeout) + public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQueueOverflowAction onQueueOverflow, int maxMessageSize, System.Security.Authentication.SslProtocols sslProtocols, X509Certificate2Collection sslCertificateOverride, TimeSpan keepAliveTime, TimeSpan sendTimeout) { if (url.StartsWith("tcp://", StringComparison.OrdinalIgnoreCase)) { @@ -56,6 +57,7 @@ public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQue SslProtocols = sslProtocols, KeepAliveTime = keepAliveTime, SendTimeout = sendTimeout, + SslCertificateOverride = sslCertificateOverride, }; } @@ -68,6 +70,7 @@ public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQue SslProtocols = sslProtocols, KeepAliveTime = keepAliveTime, SendTimeout = sendTimeout, + SslCertificateOverride = sslCertificateOverride, }; } @@ -80,6 +83,7 @@ public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQue SslProtocols = sslProtocols, KeepAliveTime = keepAliveTime, SendTimeout = sendTimeout, + SslCertificateOverride = sslCertificateOverride, }; } @@ -120,6 +124,7 @@ public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQue MaxQueueSize = maxQueueSize, OnQueueOverflow = onQueueOverflow, SendTimeout = sendTimeout, + SslCertificateOverride = sslCertificateOverride, }; } @@ -130,6 +135,7 @@ public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQue MaxQueueSize = maxQueueSize, OnQueueOverflow = onQueueOverflow, SendTimeout = sendTimeout, + SslCertificateOverride = sslCertificateOverride, }; } diff --git a/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs b/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs index 40493b1063..c29ccf0eb7 100644 --- a/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs +++ b/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs @@ -37,20 +37,23 @@ namespace NLog.Internal.NetworkSenders using System.Net.Security; using System.Net.Sockets; using System.Security.Authentication; + using System.Security.Cryptography.X509Certificates; internal sealed class SslSocketProxy : ISocket, IDisposable { - readonly AsyncCallback _sendCompleted; - readonly SocketProxy _socketProxy; - readonly string _host; - readonly SslProtocols _sslProtocol; + private readonly AsyncCallback _sendCompleted; + private readonly SocketProxy _socketProxy; + private readonly string _host; + private readonly SslProtocols _sslProtocol; + private readonly X509Certificate2Collection _sslCertificateOverride; SslStream _sslStream; - public SslSocketProxy(string host, SslProtocols sslProtocol, SocketProxy socketProxy) + public SslSocketProxy(string host, SslProtocols sslProtocol, SocketProxy socketProxy, X509Certificate2Collection sslCertificateOverride) { _socketProxy = socketProxy; _host = host; _sslProtocol = sslProtocol; + _sslCertificateOverride = sslCertificateOverride; _sendCompleted = (ar) => SocketProxySendCompleted(ar); } @@ -125,7 +128,7 @@ private void SocketProxyConnectCompleted(object sender, SocketAsyncEventArgs e) ReadTimeout = 20000 }; - AuthenticateAsClient(_sslStream, _host, _sslProtocol); + AuthenticateAsClient(_sslStream, _host, _sslProtocol, _sslCertificateOverride); } catch (SocketException ex) { @@ -161,21 +164,28 @@ private static SocketError GetSocketError(Exception ex) return socketError; } - private static void AuthenticateAsClient(SslStream sslStream, string targetHost, SslProtocols enabledSslProtocols) + private static void AuthenticateAsClient(SslStream sslStream, string targetHost, SslProtocols enabledSslProtocols, X509Certificate2Collection sslCertificateOverride) { - if (enabledSslProtocols != SslProtocols.Default) - sslStream.AuthenticateAsClient(targetHost, null, enabledSslProtocols, false); + if (enabledSslProtocols != SslProtocols.Default || sslCertificateOverride != null) + { + sslStream.AuthenticateAsClient(targetHost, sslCertificateOverride?.Count > 0 ? sslCertificateOverride : null, enabledSslProtocols, false); + } else + { sslStream.AuthenticateAsClient(targetHost); + } } - private static bool UserCertificateValidationCallback(object sender, object certificate, object chain, SslPolicyErrors sslPolicyErrors) + private bool UserCertificateValidationCallback(object sender, object certificate, object chain, SslPolicyErrors sslPolicyErrors) { if (sslPolicyErrors == SslPolicyErrors.None) return true; - Common.InternalLogger.Debug("SSL certificate errors were encountered when establishing connection to the server: {0}, Certificate: {1}", sslPolicyErrors, certificate); - return false; + Common.InternalLogger.Warn("SSL certificate errors were encountered when establishing connection to the server: {0}, Certificate: {1}", sslPolicyErrors, certificate); + if (certificate is null) + return false; + + return _sslCertificateOverride != null; } public bool SendAsync(SocketAsyncEventArgs args) diff --git a/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs index ba1fb83e6c..1272c47481 100644 --- a/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs @@ -36,6 +36,7 @@ namespace NLog.Internal.NetworkSenders using System; using System.IO; using System.Net.Sockets; + using System.Security.Cryptography.X509Certificates; using NLog.Common; using NLog.Targets.Internal; @@ -65,6 +66,8 @@ public TcpNetworkSender(string url, AddressFamily addressFamily) internal System.Security.Authentication.SslProtocols SslProtocols { get; set; } + internal X509Certificate2Collection SslCertificateOverride { get; set; } + internal TimeSpan KeepAliveTime { get; set; } internal TimeSpan SendTimeout { get; set; } @@ -94,7 +97,7 @@ protected internal virtual ISocket CreateSocket(string host, AddressFamily addre if (SslProtocols != System.Security.Authentication.SslProtocols.None) { - return new SslSocketProxy(host, SslProtocols, socketProxy); + return new SslSocketProxy(host, SslProtocols, socketProxy, SslCertificateOverride); } return socketProxy; diff --git a/src/NLog.Targets.Network/NetworkTarget.cs b/src/NLog.Targets.Network/NetworkTarget.cs index 38fa51261b..d36a30b1e3 100644 --- a/src/NLog.Targets.Network/NetworkTarget.cs +++ b/src/NLog.Targets.Network/NetworkTarget.cs @@ -40,6 +40,7 @@ namespace NLog.Targets using NLog.Common; using NLog.Layouts; using NLog.Internal.NetworkSenders; + using System.Security.Cryptography.X509Certificates; /// /// NetworkTarget for sending messages over the network using TCP / UDP sockets @@ -80,6 +81,9 @@ public class NetworkTarget : TargetWithLayout private readonly char[] _reusableEncodingBuffer = new char[32 * 1024]; private readonly StringBuilder _reusableStringBuilder = new StringBuilder(); + private readonly object _certificateCacheLock = new object(); + private Dictionary _certificateCache; + /// /// Initializes a new instance of the class. /// @@ -228,6 +232,18 @@ public LineEndingMode LineEnding /// public System.Security.Authentication.SslProtocols SslProtocols { get; set; } = System.Security.Authentication.SslProtocols.None; + /// + /// Gets or sets the file path to custom SSL certificate for TCP Socket SSL connections + /// + /// + public Layout SslCertificateFile { get; set; } + + /// + /// Gets or sets the password for the custom SSL certificate specified by + /// + /// + public Layout SslCertificatePassword { get; set; } + /// /// The number of seconds a connection will remain idle before the first keep-alive probe is sent /// @@ -308,6 +324,12 @@ protected override void CloseTarget() _currentSenderCache.Clear(); } + + if (_certificateCache?.Count > 0) + { + // Safe to reset without lock, since immutable collection + _certificateCache = null; + } } /// @@ -363,7 +385,7 @@ private void WriteBytesToCachedNetworkSender(string address, byte[] payload, Asy LinkedListNode senderNode; try { - senderNode = GetCachedNetworkSender(address); + senderNode = GetCachedNetworkSender(address, logEvent.LogEvent); } catch (Exception ex) { @@ -428,7 +450,7 @@ private void WriteBytesToNewNetworkSender(string address, byte[] payload, AsyncL try { - sender = CreateNetworkSender(address); + sender = CreateNetworkSender(address, logEvent.LogEvent); } catch (Exception ex) { @@ -557,7 +579,7 @@ private byte[] CompressBytesToWrite(byte[] payload) return payload; } - private LinkedListNode GetCachedNetworkSender(string address) + private LinkedListNode GetCachedNetworkSender(string address, LogEventInfo logEventInfo) { lock (_currentSenderCache) { @@ -590,7 +612,7 @@ private LinkedListNode GetCachedNetworkSender(string address) } } - NetworkSender sender = CreateNetworkSender(address); + NetworkSender sender = CreateNetworkSender(address, logEventInfo); lock (_openNetworkSenders) { senderNode = _openNetworkSenders.AddLast(sender); @@ -601,9 +623,13 @@ private LinkedListNode GetCachedNetworkSender(string address) } } - private NetworkSender CreateNetworkSender(string address) + private NetworkSender CreateNetworkSender(string address, LogEventInfo logEventInfo) { - var sender = SenderFactory.Create(address, MaxQueueSize, OnQueueOverflow, MaxMessageSize, SslProtocols, TimeSpan.FromSeconds(KeepAliveTimeSeconds), TimeSpan.FromSeconds(SendTimeoutSeconds)); + var sslCertificateFile = SslCertificateFile?.Render(logEventInfo); + var sslCertificatePassword = SslCertificatePassword?.Render(logEventInfo); + var sslCertificateOverride = LoadSslCertificateFromFile(sslCertificateFile, sslCertificatePassword); + + var sender = SenderFactory.Create(address, MaxQueueSize, OnQueueOverflow, MaxMessageSize, SslProtocols, sslCertificateOverride, TimeSpan.FromSeconds(KeepAliveTimeSeconds), TimeSpan.FromSeconds(SendTimeoutSeconds)); sender.Initialize(); if (KeepConnection || LogEventDropped != null) { @@ -638,5 +664,86 @@ private static void WriteBytesToNetworkSender(NetworkSender sender, byte[] paylo { sender.Send(payload, 0, payload.Length, continuation); } + + internal X509Certificate2Collection LoadSslCertificateFromFile(string sslCertificateFile, string sslCertificatePassword) + { + if (sslCertificateFile is null) + return null; // NOSONAR + + if (_certificateCache != null && _certificateCache.TryGetValue(sslCertificateFile, out var clientCertificates)) + return clientCertificates; // Safe to lookup without lock, since immutable collection + + try + { + lock (_certificateCacheLock) + { + if (_certificateCache?.TryGetValue(sslCertificateFile, out clientCertificates) == true) + return clientCertificates; + + if (string.IsNullOrEmpty(sslCertificateFile)) + { + clientCertificates = new X509Certificate2Collection(); + } + else if (sslCertificateFile.EndsWith(".pem", StringComparison.OrdinalIgnoreCase)) + { + InternalLogger.Debug("{0}: Loading SSL certificate from PEM-file: {1}", this, sslCertificateFile); + var clientCertificate = LoadCertificateFromPem(sslCertificateFile); + clientCertificates = new X509Certificate2Collection(clientCertificate); + } + else + { + InternalLogger.Debug("{0}: Loading SSL certificate from file: {1}", this, sslCertificateFile); + clientCertificates = new X509Certificate2Collection(new X509Certificate2(sslCertificateFile, string.IsNullOrEmpty(sslCertificatePassword) ? null : sslCertificatePassword)); + } + + var certificateCache = new Dictionary((_certificateCache?.Count ?? 0) + 1); + if (_certificateCache != null) + { + foreach (var existingCertificate in _certificateCache) + certificateCache.Add(existingCertificate.Key, existingCertificate.Value); + } + certificateCache[sslCertificateFile] = clientCertificates; + _certificateCache = certificateCache; + return clientCertificates; + } + } + catch (Exception ex) + { + InternalLogger.Error(ex, "{0}: Failed loading SSL certificate from file: {1}", this, sslCertificateFile); + throw new NLogRuntimeException($"NetworkTarget: Failed loading SSL certificate from file: {sslCertificateFile}", ex); + } + } + + private static X509Certificate2 LoadCertificateFromPem(string fileName) + { + using (var reader = new System.IO.StreamReader(new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read), System.Text.Encoding.UTF8)) + { + var pem = reader.ReadToEnd(); + string base64 = GetBase64FromPem(pem); + byte[] certBytes = Convert.FromBase64String(base64); + return new X509Certificate2(certBytes); + } + } + + private static string GetBase64FromPem(string pem) + { + const string header = "-----BEGIN CERTIFICATE-----"; + const string footer = "-----END CERTIFICATE-----"; + + int start = pem.IndexOf(header, StringComparison.Ordinal) + header.Length; + if (start <= header.Length) + { + throw new NLogRuntimeException("Invalid PEM format: Missing BEGIN CERTIFICATE header"); + } + + int end = pem.IndexOf(footer, start, StringComparison.Ordinal); + if (end <= start) + { + throw new NLogRuntimeException("Invalid PEM format: Missing END CERTIFICATE footer"); + } + string base64 = pem.Substring(start, end - start); + base64 = base64.Replace("\r", "").Replace("\n", "").Trim(); + return base64; + } } } diff --git a/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs b/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs index 55b390804c..2f23142569 100644 --- a/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs @@ -35,6 +35,7 @@ namespace NLog.Targets.Network { using System; using System.Security.Authentication; + using System.Security.Cryptography.X509Certificates; using NLog.Config; using NLog.Internal.NetworkSenders; using NSubstitute; @@ -88,7 +89,7 @@ public void HttpNetworkSenderViaNetworkTargetTest() Assert.Equal("HttpHappyPathTestLogger|test message1|", requestedString); Assert.Equal("POST", mock.Method); - networkSenderFactoryMock.Received(1).Create("http://test.with.mock", 1234, NetworkTargetQueueOverflowAction.Block, 0, SslProtocols.None, TimeSpan.Zero, TimeSpan.Zero); + networkSenderFactoryMock.Received(1).Create("http://test.with.mock", 1234, NetworkTargetQueueOverflowAction.Block, 0, SslProtocols.None, null, TimeSpan.Zero, TimeSpan.Zero); // Cleanup mock.Dispose(); @@ -134,7 +135,7 @@ public void HttpNetworkSenderViaNetworkTargetRecoveryTest() Assert.Equal("HttpHappyPathTestLogger|test message2|", requestedString); Assert.Equal("POST", mock.Method); - networkSenderFactoryMock.Received(1).Create("http://test.with.mock", 1234, NetworkTargetQueueOverflowAction.Block, 0, SslProtocols.None, TimeSpan.Zero, TimeSpan.Zero); // Only created one HttpNetworkSender + networkSenderFactoryMock.Received(1).Create("http://test.with.mock", 1234, NetworkTargetQueueOverflowAction.Block, 0, SslProtocols.None, null, TimeSpan.Zero, TimeSpan.Zero); // Only created one HttpNetworkSender // Cleanup mock.Dispose(); @@ -145,7 +146,7 @@ private static INetworkSenderFactory CreateNetworkSenderFactoryMock(WebRequestMo { var networkSenderFactoryMock = Substitute.For(); - networkSenderFactoryMock.Create(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + networkSenderFactoryMock.Create(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(url => new HttpNetworkSender(url.Arg()) { HttpRequestFactory = new WebRequestFactoryMock(webRequestMock) diff --git a/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs index 1264cb62aa..0bc20cdbb0 100644 --- a/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs @@ -1180,6 +1180,9 @@ public void AsyncConnectionFailureOnCloseShouldNotDeadlock() [InlineData("tls", SslProtocols.Tls)] [InlineData("tls11", SslProtocols.Tls11)] [InlineData("tls,tls11", SslProtocols.Tls11 | SslProtocols.Tls)] +#if NET6_0_OR_GREATER + [InlineData("Tls13", SslProtocols.Tls13)] +#endif public void SslProtocolsConfigTest(string sslOptions, SslProtocols expected) { var logFactory = new LogFactory().Setup().SetupExtensions(ext => ext.RegisterTarget()) @@ -1195,6 +1198,48 @@ public void SslProtocolsConfigTest(string sslOptions, SslProtocols expected) logFactory.Shutdown(); } + [Fact] + public void InvalidSslCertificateFilePath() + { + var senderFactory = new MyQueudSenderFactory(); + + var target = new NetworkTarget(); + target.Address = "tcp://${logger}.company.lan/"; + target.SenderFactory = senderFactory; + target.Layout = "${message}"; + target.KeepConnection = true; + target.SslProtocols = SslProtocols.Tls12; + target.SslCertificateFile = "{Invalid-Certificate-Path}"; + target.SslCertificatePassword = ""; + + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory) + { + var exception = Assert.Throws(() => logFactory.GetCurrentClassLogger().Info("Fails because invalid SSL certificate path")); + Assert.Contains("SSL", exception.Message); + } + } + + [Fact] + public void EmptySslCertificateFilePath() + { + var senderFactory = new MyQueudSenderFactory(); + senderFactory.FailCounter = 1; + + var target = new NetworkTarget(); + target.Address = "tcp://[Invalid-SSL-Host]"; + target.SenderFactory = senderFactory; + target.Layout = "${message}"; + target.KeepConnection = true; + target.SslProtocols = SslProtocols.Tls12; + target.SslCertificateFile = ""; + target.SslCertificatePassword = ""; + + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory) + { + Assert.Throws(() => logFactory.GetCurrentClassLogger().Info("Fails because invalid SSL Host Address")); + } + } + [Theory] [InlineData("0", 0)] [InlineData("30", 30)] @@ -1307,7 +1352,7 @@ internal sealed class MyQueudSenderFactory : INetworkSenderFactory internal StringWriter Log = new StringWriter(); private int _idCounter; - public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQueueOverflowAction onQueueOverflow, int maxMessageSize, SslProtocols sslProtocols, TimeSpan keepAliveTime, TimeSpan sendTimeout) + public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQueueOverflowAction onQueueOverflow, int maxMessageSize, SslProtocols sslProtocols, System.Security.Cryptography.X509Certificates.X509Certificate2Collection sslCertificateOverride, TimeSpan keepAliveTime, TimeSpan sendTimeout) { var sender = new MyQueudNetworkSender(url, ++_idCounter, Log, this) { MaxQueueSize = maxQueueSize, OnQueueOverflow = onQueueOverflow }; Senders.Add(sender); @@ -1331,7 +1376,7 @@ internal sealed class MyQueudNetworkSender : QueuedNetworkSender public MemoryStream MemoryStream { get; } = new MemoryStream(); public MyQueudNetworkSender(string url, int id, TextWriter log, MyQueudSenderFactory senderFactory) - : base(url) + : base(new Uri(url).ToString()) { _id = id; _log = log; From 382949a62a58beff077729a378fc287aa44c1f50 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 30 Mar 2025 15:07:55 +0200 Subject: [PATCH 070/224] LogFactory FlushAsync without direct throw (#5753) --- src/NLog/LogFactory.cs | 6 ++---- tests/NLog.UnitTests/LogFactoryTests.cs | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index e2b952ffcf..55229df489 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -391,7 +391,7 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() try { - var disposeTimeout = System.Threading.Tasks.Task.Delay(DefaultFlushTimeout).ContinueWith(prevTask => throw new TimeoutException("LogFactory Dispose Timeout")); + var disposeTimeout = System.Threading.Tasks.Task.Delay(DefaultFlushTimeout).ContinueWith(prevTask => throw new OperationCanceledException("LogFactory Dispose Timeout")); await System.Threading.Tasks.TaskExtensions.Unwrap(System.Threading.Tasks.Task.WhenAny(System.Threading.Tasks.Task.Run(() => DisposeInternal()), disposeTimeout)).ConfigureAwait(false); GC.SuppressFinalize(this); } @@ -707,10 +707,8 @@ public System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken } var timeout = cancellationToken.CanBeCanceled ? -1 : (int)DefaultFlushTimeout.TotalMilliseconds; - var flushTimeout = System.Threading.Tasks.Task.Delay(timeout, cancellationToken).ContinueWith(prevTask => throw new TimeoutException("LogFactory Flush Timeout")); + var flushTimeout = System.Threading.Tasks.Task.Delay(timeout, cancellationToken).ContinueWith(prevTask => throw new OperationCanceledException("LogFactory Flush Timeout")); flushTimeout.ContinueWith(prevTask => { flushTimeoutHandler.Invoke(null); flushCompleted.TrySetCanceled(); }); - if (cancellationToken.IsCancellationRequested) - throw new TimeoutException("LogFactory Flush Timeout"); return System.Threading.Tasks.TaskExtensions.Unwrap(System.Threading.Tasks.Task.WhenAny(flushCompleted.Task, flushTimeout)); } #endif diff --git a/tests/NLog.UnitTests/LogFactoryTests.cs b/tests/NLog.UnitTests/LogFactoryTests.cs index 139df670c8..06e0dc66f8 100644 --- a/tests/NLog.UnitTests/LogFactoryTests.cs +++ b/tests/NLog.UnitTests/LogFactoryTests.cs @@ -110,7 +110,7 @@ public void FlushAsync_TargetTimeout_Async() logFactory.Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteToMethodCall((evt, parms) => { System.Threading.Thread.Sleep(5000); })); System.Threading.Tasks.Task.Run(() => logFactory.GetCurrentClassLogger().Info("Sleep")).Wait(50); var task = logFactory.FlushAsync(new CancellationTokenSource(100).Token); - Assert.Throws(() => task.GetAwaiter().GetResult()); + Assert.Throws(() => task.GetAwaiter().GetResult()); } #endif From 8c4b2660e407450a42077c8ca5fbb3c3db642604 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 2 Apr 2025 21:12:56 +0200 Subject: [PATCH 071/224] NLog.Targets.Network - Introduced SyslogTarget and SyslogLayout (#5754) --- .../{ => Layouts}/Log4JXmlEventLayout.cs | 1 - .../Log4JXmlEventLayoutRenderer.cs | 2 +- .../{ => Layouts}/Log4JXmlEventParameter.cs | 3 +- .../Layouts/SyslogFacility.cs | 111 +++++ .../Layouts/SyslogLayout.cs | 467 ++++++++++++++++++ .../Layouts/SyslogSeverity.cs | 63 +++ .../NetworkSenders/SslSocketProxy.cs | 4 +- .../{ => Targets}/ChainsawTarget.cs | 0 .../NetworkLogEventDroppedEventArgs.cs | 0 .../NetworkLogEventDroppedReason.cs | 0 .../{ => Targets}/NetworkTarget.cs | 31 +- .../NetworkTargetCompressionType.cs | 0 .../NetworkTargetConnectionsOverflowAction.cs | 0 .../NetworkTargetOverflowAction.cs | 0 .../NetworkTargetQueueOverflowAction.cs | 0 .../Targets/SyslogTarget.cs | 98 ++++ src/NLog/LogFactory.cs | 4 +- .../SyslogLayoutTests.cs | 232 +++++++++ tests/NLog.UnitTests/ApiTests.cs | 35 +- 19 files changed, 1017 insertions(+), 34 deletions(-) rename src/NLog.Targets.Network/{ => Layouts}/Log4JXmlEventLayout.cs (99%) rename src/NLog.Targets.Network/{ => Layouts}/Log4JXmlEventLayoutRenderer.cs (99%) rename src/NLog.Targets.Network/{ => Layouts}/Log4JXmlEventParameter.cs (98%) create mode 100644 src/NLog.Targets.Network/Layouts/SyslogFacility.cs create mode 100644 src/NLog.Targets.Network/Layouts/SyslogLayout.cs create mode 100644 src/NLog.Targets.Network/Layouts/SyslogSeverity.cs rename src/NLog.Targets.Network/{ => Targets}/ChainsawTarget.cs (100%) rename src/NLog.Targets.Network/{ => Targets}/NetworkLogEventDroppedEventArgs.cs (100%) rename src/NLog.Targets.Network/{ => Targets}/NetworkLogEventDroppedReason.cs (100%) rename src/NLog.Targets.Network/{ => Targets}/NetworkTarget.cs (96%) rename src/NLog.Targets.Network/{ => Targets}/NetworkTargetCompressionType.cs (100%) rename src/NLog.Targets.Network/{ => Targets}/NetworkTargetConnectionsOverflowAction.cs (100%) rename src/NLog.Targets.Network/{ => Targets}/NetworkTargetOverflowAction.cs (100%) rename src/NLog.Targets.Network/{ => Targets}/NetworkTargetQueueOverflowAction.cs (100%) create mode 100644 src/NLog.Targets.Network/Targets/SyslogTarget.cs create mode 100644 tests/NLog.Targets.Network.Tests/SyslogLayoutTests.cs diff --git a/src/NLog.Targets.Network/Log4JXmlEventLayout.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs similarity index 99% rename from src/NLog.Targets.Network/Log4JXmlEventLayout.cs rename to src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs index 7e5a27c45b..d5c4fb9a0a 100644 --- a/src/NLog.Targets.Network/Log4JXmlEventLayout.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs @@ -39,7 +39,6 @@ namespace NLog.Layouts using System.Text; using NLog.Config; using NLog.LayoutRenderers; - using NLog.Targets; /// /// A specialized layout that renders Log4j-compatible XML events. diff --git a/src/NLog.Targets.Network/Log4JXmlEventLayoutRenderer.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayoutRenderer.cs similarity index 99% rename from src/NLog.Targets.Network/Log4JXmlEventLayoutRenderer.cs rename to src/NLog.Targets.Network/Layouts/Log4JXmlEventLayoutRenderer.cs index 31fd78a854..ade9eaa12d 100644 --- a/src/NLog.Targets.Network/Log4JXmlEventLayoutRenderer.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayoutRenderer.cs @@ -57,7 +57,7 @@ public class Log4JXmlEventLayoutRenderer : LayoutRenderer, IUsesStackTrace { private static readonly DateTime log4jDateBase = new DateTime(1970, 1, 1); - private static readonly string dummyNamespace = "http://nlog-project.org/dummynamespace/" + Guid.NewGuid(); + private static readonly string dummyNamespace = "https://nlog-project.org/dummynamespace/" + Guid.NewGuid(); private static readonly string dummyNamespaceRemover = " xmlns:log4j=\"" + dummyNamespace + "\""; private readonly ScopeContextNestedStatesLayoutRenderer _scopeNestedLayoutRenderer = new ScopeContextNestedStatesLayoutRenderer(); diff --git a/src/NLog.Targets.Network/Log4JXmlEventParameter.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs similarity index 98% rename from src/NLog.Targets.Network/Log4JXmlEventParameter.cs rename to src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs index 7c902e47da..c8f7ed9ee0 100644 --- a/src/NLog.Targets.Network/Log4JXmlEventParameter.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs @@ -31,10 +31,9 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.Targets +namespace NLog.Layouts { using NLog.Config; - using NLog.Layouts; using NLog.Targets.Internal; /// diff --git a/src/NLog.Targets.Network/Layouts/SyslogFacility.cs b/src/NLog.Targets.Network/Layouts/SyslogFacility.cs new file mode 100644 index 0000000000..00afe91aeb --- /dev/null +++ b/src/NLog.Targets.Network/Layouts/SyslogFacility.cs @@ -0,0 +1,111 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Layouts +{ + /// Syslog facilities + public enum SyslogFacility + { + /// Kernel messages + Kernel = 0, + + /// Random user-level messages + User = 1, + + /// Mail system + Mail = 2, + + /// System daemons + Daemons = 3, + + /// Security/authorization messages + Authorization = 4, + + /// Messages generated internally by syslogd + Syslog = 5, + + /// Line printer subsystem + Printer = 6, + + /// Network news subsystem + News = 7, + + /// UUCP subsystem + Uucp = 8, + + /// Clock (cron/at) daemon + Clock = 9, + + /// Security/authorization messages (private) + Authorization2 = 10, + + /// FTP daemon + Ftp = 11, + + /// NTP subsystem + Ntp = 12, + + /// Log audit + Audit = 13, + + /// Log alert + Alert = 14, + + /// Clock daemon + Clock2 = 15, + + /// Reserved for local use + Local0 = 16, + + /// Reserved for local use + Local1 = 17, + + /// Reserved for local use + Local2 = 18, + + /// Reserved for local use + Local3 = 19, + + /// Reserved for local use + Local4 = 20, + + /// Reserved for local use + Local5 = 21, + + /// Reserved for local use + Local6 = 22, + + /// Reserved for local use + Local7 = 23 + } +} diff --git a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs new file mode 100644 index 0000000000..5cdad9ad43 --- /dev/null +++ b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs @@ -0,0 +1,467 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NLog.Config; +using NLog.Targets; + +namespace NLog.Layouts +{ + /// + /// A specialized layout that renders Syslog-formatted events in format Rfc3164 / Rfc5424 + /// + public class SyslogLayout : Layout + { + private const string Rfc5424DefaultVersion = "1"; + private const string Rfc3164TimestampFormat = "{0:MMM} {0,11:d HH:mm:ss}"; + private const string NilValue = "-"; + private const int HostNameMaxLength = 255; + private const int AppNameMaxLength = 48; + private const int ProcessIdMaxLength = 128; + + private IValueFormatter ValueFormatter + { + get => _valueFormatter ?? (_valueFormatter = ResolveService()); + set => _valueFormatter = value; + } + private IValueFormatter _valueFormatter; + + /// + /// Gets or sets a value indicating whether to use RFC 3164 for Syslog Format + /// + /// + public bool Rfc3164 { get; set; } + + /// + /// Gets or sets a value indicating whether to use RFC 5424 for Syslog Format + /// + /// + public bool Rfc5424 { get; set; } + + /// + /// Gets or sets a value indicating what DateTime format should be used when = true + /// + public Layout SyslogTimestamp { get; set; } = "${date:format=o}"; + + /// + /// The FQDN or IPv4 address or IPv6 address or hostname of the sender machine (Optional) + /// + /// + /// RFC 5424 - NILVALUE or 1 to 255 PRINTUSASCII + /// + public Layout SyslogHostName + { + get => _hostName; + set + { + _hostName = value; + _hostNameString = _hostName is SimpleLayout simpleLayout && simpleLayout.IsFixedText ? EscapePropertyName(simpleLayout.FixedText, HostNameMaxLength) : null; + } + } + private Layout _hostName; + private string _hostNameString; + + /// + /// Name of the device / application / process sending the Syslog-message (Optional) + /// + /// + /// RFC 3164 - Tag-Name - Alphanumeric string not exceeding 32 characters + /// RFC 5424 - NILVALUE or 1 to 48 PRINTUSASCII + /// + public Layout SyslogAppName + { + get => _appName; + set + { + _appName = value; + _appNameString = _appName is SimpleLayout simpleLayout && simpleLayout.IsFixedText ? EscapePropertyName(simpleLayout.FixedText, AppNameMaxLength) : null; + } + } + private Layout _appName; + private string _appNameString; + + /// + /// Process Id or Process Name or Logger Name (Optional) + /// + /// + /// RFC 5424 - NILVALUE or 1 to 128 PRINTUSASCII + /// + public Layout SyslogProcessId + { + get => _processId; + set + { + _processId = value; + _processIdString = _processId is SimpleLayout simpleLayout && simpleLayout.IsFixedText ? EscapePropertyName(simpleLayout.FixedText, ProcessIdMaxLength) : null; + } + } + private Layout _processId; + private string _processIdString; + + /// + /// The type of message that should be the same for events with the same semantics. Ex ${event-properties:EventId} (Optional) + /// + /// + /// RFC 5424 - NILVALUE or 1 to 32 PRINTUSASCII + /// + public Layout SyslogMessageId { get; set; } + + /// + /// Mesage Payload + /// + public Layout SyslogMessage { get; set; } = "${message}${onexception:|}${exception:format=shortType,message}"; + + /// + /// Message Severity + /// + public Layout SyslogSeverity { get; set; } = Layout.FromMethod(l => ResolveSyslogSeverity(l.Level), LayoutRenderOptions.ThreadAgnostic); + + /// + /// Device Facility + /// + public SyslogFacility SyslogFacility { get; set; } = SyslogFacility.Local0; + + /// + /// Gets or sets the prefix for StructuredData when = true + /// + public Layout StructuredDataId { get; set; } = "meta"; + + /// + /// Gets or sets a value indicating whether LogEvent Properties should be included for StructuredData when = true + /// + public bool IncludeEventProperties { get; set; } + + /// + /// List of StructuredData Parameters to include when = true + /// + [ArrayParameter(typeof(TargetPropertyWithContext), "StructuredDataParam")] + public List StructuredDataParams { get; } = new List(); + + private KeyValuePair> _priValueMapping; + + /// + /// Initializes a new instance of the class. + /// + public SyslogLayout() + { + SyslogHostName = "${hostname}"; + SyslogAppName = "${processname}"; + SyslogProcessId = "${processid}"; + } + + /// + protected override string GetFormattedMessage(LogEventInfo logEvent) + { + var stringBuilder = new StringBuilder(128); + RenderFormattedMessage(logEvent, stringBuilder); + return stringBuilder.ToString(); + } + + /// + protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) + { + var severity = SyslogSeverity.RenderValue(logEvent); + var facility = SyslogFacility; + + var priValueMapper = _priValueMapping; + if (priValueMapper.Key != facility || priValueMapper.Value is null) + { + priValueMapper = new KeyValuePair>(facility, ResolveFacilityMapper(facility)); + _priValueMapping = priValueMapper; + } + + var priValue = priValueMapper.Value[severity]; + var hostName = _hostNameString ?? EscapePropertyName(SyslogHostName?.Render(logEvent) ?? string.Empty, HostNameMaxLength); + var appName = _appNameString ?? EscapePropertyName(SyslogAppName?.Render(logEvent) ?? string.Empty, AppNameMaxLength); + var processId = _processIdString ?? EscapePropertyName(SyslogProcessId?.Render(logEvent) ?? string.Empty, ProcessIdMaxLength); + + if (Rfc3164 || !Rfc5424) + { + Render_Rfc3164(logEvent, target, priValue, hostName, appName, processId); + } + else + { + Render_Rfc5424(logEvent, target, priValue, hostName, appName, processId); + } + } + + private void Render_Rfc3164(LogEventInfo logEvent, StringBuilder target, string priValue, string hostName, string appName, string processId) + { + target.Append(priValue); + target.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, Rfc3164TimestampFormat, logEvent.TimeStamp); + target.Append(' '); + target.Append(string.IsNullOrEmpty(hostName) ? NilValue : hostName); + target.Append(' '); + if (appName.Length > 32) + target.Append(appName, 0, 32); // Rfc3164 TAG + else + target.Append(string.IsNullOrEmpty(appName) ? NilValue : appName); // Rfc3164 TAG + + if (!string.IsNullOrEmpty(processId)) + { + target.Append('['); + target.Append(processId); + target.Append(']'); + } + target.Append(':'); + target.Append(' '); + + int startIndex = target.Length; + SyslogMessage.Render(logEvent, target); + + bool removeDuplicates = false; + + for (int i = startIndex; i < target.Length; ++i) + { + var chr = target[i]; + if (ToAscii(chr) != chr) + { + target[i] = ToAscii(chr); + removeDuplicates = true; + } + } + + if (removeDuplicates) + { + var fixedString = target.ToString(startIndex, target.Length - startIndex); + fixedString = fixedString.Replace("??", "?").Replace(" ", " "); + if (fixedString.Length >= 1000) + fixedString = fixedString.Trim(' ', '?', '_'); + target.Length = startIndex; + target.Append(fixedString); + } + } + + private void Render_Rfc5424(LogEventInfo logEvent, StringBuilder target, string priValue, string hostName, string appName, string processId) + { + var msgId = EscapePropertyName(SyslogMessageId?.Render(logEvent) ?? string.Empty, 32); + if (msgId?.IndexOf(' ') >= 0) + msgId = msgId.Trim().Replace(' ', '_'); + + target.Append(priValue); + target.Append(Rfc5424DefaultVersion); + target.Append(' '); + SyslogTimestamp.Render(logEvent, target); + target.Append(' '); + target.Append(string.IsNullOrEmpty(hostName) ? NilValue : hostName); + target.Append(' '); + target.Append(string.IsNullOrEmpty(appName) ? NilValue : appName); + target.Append(' '); + target.Append(string.IsNullOrEmpty(processId) ? NilValue : processId); + target.Append(' '); + target.Append(string.IsNullOrEmpty(msgId) ? NilValue : msgId); + + var structuredDataId = EscapePropertyName(StructuredDataId?.Render(logEvent) ?? string.Empty); + if (!string.IsNullOrEmpty(structuredDataId)) + { + if (IncludeEventProperties && logEvent.HasProperties) + { + foreach (var eventProperty in logEvent.Properties) + { + var propertyName = EscapePropertyName(eventProperty.Key?.ToString() ?? string.Empty, 32); + if (string.IsNullOrEmpty(propertyName)) + continue; + + structuredDataId = AppendPropertyName(target, structuredDataId, propertyName); + + var propertyValue = eventProperty.Value; + AppendPropertyValue(target, propertyValue); + } + } + + foreach (var sdParam in StructuredDataParams) + { + var sdParamName = EscapePropertyName(sdParam.Name ?? string.Empty, 32); + if (string.IsNullOrEmpty(sdParamName)) + continue; + + var sdParamValue = sdParam.RenderValue(logEvent); + if (!sdParam.IncludeEmptyValue && (sdParamValue is null || string.Empty.Equals(sdParamValue))) + continue; + + structuredDataId = AppendPropertyName(target, structuredDataId, sdParamName); + AppendPropertyValue(target, sdParamValue); + } + + if (string.IsNullOrEmpty(structuredDataId)) + { + target.Append(']'); + } + } + + target.Append(' '); + SyslogMessage.Render(logEvent, target); + } + + private static string AppendPropertyName(StringBuilder target, string structuredDataId, string propertyName) + { + if (!string.IsNullOrEmpty(structuredDataId)) + { + target.Append(' '); + target.Append('['); + target.Append(structuredDataId); + structuredDataId = string.Empty; + } + target.Append(' '); + target.Append(propertyName); + target.Append('='); + return structuredDataId; + } + + private void AppendPropertyValue(StringBuilder target, object propertyValue) + { + target.Append('"'); + + if (propertyValue is IConvertible convertPropertyValue) + { + propertyValue = EscapePropertyValue(convertPropertyValue); + ValueFormatter.FormatValue(propertyValue, null, MessageTemplates.CaptureType.Unknown, System.Globalization.CultureInfo.InvariantCulture, target); + } + else + { + var propertyStartIndex = target.Length; + ValueFormatter.FormatValue(propertyValue, null, MessageTemplates.CaptureType.Unknown, System.Globalization.CultureInfo.InvariantCulture, target); + for (int i = propertyStartIndex; i < target.Length; ++i) + { + var chr = target[i]; + if (IsSpecialChar(chr)) + { + var stringValue = target.ToString(propertyStartIndex, target.Length - propertyStartIndex); + target.Length = propertyStartIndex; + target.Append(EscapePropertyValue(stringValue).ToString()); + break; + } + } + } + + target.Append('"'); + } + + private static IConvertible EscapePropertyValue(IConvertible propertyValue) + { + var typeCode = propertyValue.GetTypeCode(); + switch (typeCode) + { + case TypeCode.Char: + { + var charValue = propertyValue.ToChar(System.Globalization.CultureInfo.CurrentCulture); + if (IsSpecialChar(charValue)) + return char.IsWhiteSpace(charValue) ? " " : $"\\{charValue}"; + else + return propertyValue; + } + case TypeCode.String: + { + var stringValue = propertyValue.ToString(); + if (stringValue.IndexOfAny(SpecialChars) >= 0) + return stringValue.Replace("\\", "\\\\").Replace("[", "\\[").Replace("]", "\\]").Replace("=", "\\=").Replace("\"", "\\\"").Replace("\n", " ").Replace("\r", ""); + else + return stringValue; + } + case TypeCode.DBNull: return string.Empty; + case TypeCode.Empty: return string.Empty; + default: + return propertyValue; + } + } + + private static string EscapePropertyName(string unicodeValue, int maxLength = 0) + { + if (string.IsNullOrEmpty(unicodeValue)) + return string.Empty; + + foreach (var chr in unicodeValue) + { + if (ToAscii(chr) == chr && !IsSpecialChar(chr) && chr != ' ') + continue; + + unicodeValue = new string(unicodeValue.Select(c => ToAsciiFixWhiteSpace(c)).Where(c => !IsSpecialChar(c)).ToArray()).Replace("??", "?").Trim('_').Replace("__", "_"); + break; + } + + if (maxLength > 0 && unicodeValue.Length > maxLength) + { + unicodeValue = unicodeValue.Substring(0, maxLength); + } + + return unicodeValue; + } + + private static readonly char[] SpecialChars = new[] { '[', ']', '"', '\\', '=', '\r', '\n' }; + + private static bool IsSpecialChar(char c) + { + switch (c) + { + case '[': + case ']': + case '"': + case '\'': + case '=': + case '\r': + case '\n': + return true; + default: + return false; + } + } + + private static char ToAsciiFixWhiteSpace(char c) + { + c = ToAscii(c); + return c == ' ' ? '_' : c; + } + + private static char ToAscii(char c) + { + if (char.IsWhiteSpace(c)) + return ' '; // newlines are also whitespace + if (!IsAscii(c)) + return '?'; + if (c <= 31) + return '?'; // control characters + return c; + } + + /// + /// Per http://www.unicode.org/glossary/#ASCII, ASCII is only U+0000..U+007F. + /// + private static bool IsAscii(char c) => (uint)c <= '\x007f'; + + private static readonly Dictionary _severityMapping = new Dictionary + { + { LogLevel.Fatal, Layouts.SyslogSeverity.Emergency }, + { LogLevel.Error, Layouts.SyslogSeverity.Error }, + { LogLevel.Warn, Layouts.SyslogSeverity.Warning }, + { LogLevel.Info, Layouts.SyslogSeverity.Informational }, + { LogLevel.Debug, Layouts.SyslogSeverity.Debug }, + { LogLevel.Trace, Layouts.SyslogSeverity.Debug } + }; + + private static SyslogSeverity ResolveSyslogSeverity(LogLevel logLevel) + { + return _severityMapping[logLevel]; + } + + private static Dictionary ResolveFacilityMapper(SyslogFacility facility) + { + return (new SyslogSeverity[] + { + Layouts.SyslogSeverity.Emergency, + Layouts.SyslogSeverity.Alert, + Layouts.SyslogSeverity.Critical, + Layouts.SyslogSeverity.Error, + Layouts.SyslogSeverity.Warning, + Layouts.SyslogSeverity.Notice, + Layouts.SyslogSeverity.Informational, + Layouts.SyslogSeverity.Debug + }).ToDictionary(s => s, s => ResolvePriHeader(facility, s)); + } + + private static string ResolvePriHeader(SyslogFacility facility, SyslogSeverity severity) + { + var priVal = (int)facility * 8 + (int)severity; + return $"<{priVal}>"; + } + } +} diff --git a/src/NLog.Targets.Network/Layouts/SyslogSeverity.cs b/src/NLog.Targets.Network/Layouts/SyslogSeverity.cs new file mode 100644 index 0000000000..06f2261afe --- /dev/null +++ b/src/NLog.Targets.Network/Layouts/SyslogSeverity.cs @@ -0,0 +1,63 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Layouts +{ + /// Syslog severities + public enum SyslogSeverity + { + /// Emergency severity + Emergency = 0, + + /// Alert severity + Alert = 1, + + /// Critical severity + Critical = 2, + + /// Error severity + Error = 3, + + /// Warning severity + Warning = 4, + + /// Notice severity + Notice = 5, + + /// Informational severity + Informational = 6, + + /// Debug severity + Debug = 7 + } +} diff --git a/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs b/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs index c29ccf0eb7..3658d12969 100644 --- a/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs +++ b/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs @@ -134,13 +134,13 @@ private void SocketProxyConnectCompleted(object sender, SocketAsyncEventArgs e) { if (proxyArgs != null) proxyArgs.SocketError = ex.SocketErrorCode; + NLog.Common.InternalLogger.Error(ex, "NetworkTarget: Failed to complete SSL handshake"); } catch (Exception ex) { if (proxyArgs != null) - { proxyArgs.SocketError = GetSocketError(ex); - } + NLog.Common.InternalLogger.Error(ex, "NetworkTarget: Failed to complete SSL handshake"); } finally { diff --git a/src/NLog.Targets.Network/ChainsawTarget.cs b/src/NLog.Targets.Network/Targets/ChainsawTarget.cs similarity index 100% rename from src/NLog.Targets.Network/ChainsawTarget.cs rename to src/NLog.Targets.Network/Targets/ChainsawTarget.cs diff --git a/src/NLog.Targets.Network/NetworkLogEventDroppedEventArgs.cs b/src/NLog.Targets.Network/Targets/NetworkLogEventDroppedEventArgs.cs similarity index 100% rename from src/NLog.Targets.Network/NetworkLogEventDroppedEventArgs.cs rename to src/NLog.Targets.Network/Targets/NetworkLogEventDroppedEventArgs.cs diff --git a/src/NLog.Targets.Network/NetworkLogEventDroppedReason.cs b/src/NLog.Targets.Network/Targets/NetworkLogEventDroppedReason.cs similarity index 100% rename from src/NLog.Targets.Network/NetworkLogEventDroppedReason.cs rename to src/NLog.Targets.Network/Targets/NetworkLogEventDroppedReason.cs diff --git a/src/NLog.Targets.Network/NetworkTarget.cs b/src/NLog.Targets.Network/Targets/NetworkTarget.cs similarity index 96% rename from src/NLog.Targets.Network/NetworkTarget.cs rename to src/NLog.Targets.Network/Targets/NetworkTarget.cs index d36a30b1e3..73298fe0b3 100644 --- a/src/NLog.Targets.Network/NetworkTarget.cs +++ b/src/NLog.Targets.Network/Targets/NetworkTarget.cs @@ -341,6 +341,7 @@ protected override void Write(AsyncLogEventInfo logEvent) { string address = RenderLogEvent(Address, logEvent.LogEvent); byte[] payload = GetBytesToWrite(logEvent.LogEvent); + byte[] header = GetHeaderToWrite(logEvent.LogEvent, address, payload); int messageSize = payload.Length; InternalLogger.Trace("{0}: Sending {1} bytes to address: '{2}'", this, messageSize, address); @@ -372,15 +373,15 @@ protected override void Write(AsyncLogEventInfo logEvent) if (KeepConnection) { - WriteBytesToCachedNetworkSender(address, payload, logEvent); + WriteBytesToCachedNetworkSender(address, header, payload, logEvent); } else { - WriteBytesToNewNetworkSender(address, payload, logEvent); + WriteBytesToNewNetworkSender(address, header, payload, logEvent); } } - private void WriteBytesToCachedNetworkSender(string address, byte[] payload, AsyncLogEventInfo logEvent) + private void WriteBytesToCachedNetworkSender(string address, byte[] header, byte[] payload, AsyncLogEventInfo logEvent) { LinkedListNode senderNode; try @@ -397,6 +398,7 @@ private void WriteBytesToCachedNetworkSender(string address, byte[] payload, Asy WriteBytesToNetworkSender( senderNode.Value, payload, + header, ex => { if (ex != null) @@ -410,7 +412,7 @@ private void WriteBytesToCachedNetworkSender(string address, byte[] payload, Asy }); } - private void WriteBytesToNewNetworkSender(string address, byte[] payload, AsyncLogEventInfo logEvent) + private void WriteBytesToNewNetworkSender(string address, byte[] header, byte[] payload, AsyncLogEventInfo logEvent) { NetworkSender sender; LinkedListNode linkedListNode; @@ -465,6 +467,7 @@ private void WriteBytesToNewNetworkSender(string address, byte[] payload, AsyncL WriteBytesToNetworkSender( sender, payload, + header, ex => { lock (_openNetworkSenders) @@ -510,7 +513,7 @@ private static bool TryRemove(LinkedList list, LinkedListNode node) } /// - /// Gets the bytes to be written. + /// Gets the payload bytes to be written. /// /// Log event. /// Byte array. @@ -526,6 +529,18 @@ protected virtual byte[] GetBytesToWrite(LogEventInfo logEvent) return payload; } + /// + /// Gets the header bytes to be written. + /// + /// Log event. + /// network address. + /// Payload buffer. + /// Byte array. + protected virtual byte[] GetHeaderToWrite(LogEventInfo logEvent, string address, byte[] payload) + { + return null; + } + private byte[] RenderBytesToWrite(LogEventInfo logEvent) { lock (_reusableEncodingBuffer) @@ -660,8 +675,10 @@ private void ReleaseCachedConnection(LinkedListNode senderNode) } } - private static void WriteBytesToNetworkSender(NetworkSender sender, byte[] payload, AsyncContinuation continuation) + private static void WriteBytesToNetworkSender(NetworkSender sender, byte[] payload, byte[] header, AsyncContinuation continuation) { + if (header?.Length > 0) + sender.Send(header, 0, header.Length, continuation); sender.Send(payload, 0, payload.Length, continuation); } @@ -716,7 +733,7 @@ internal X509Certificate2Collection LoadSslCertificateFromFile(string sslCertifi private static X509Certificate2 LoadCertificateFromPem(string fileName) { - using (var reader = new System.IO.StreamReader(new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read), System.Text.Encoding.UTF8)) + using (var reader = new System.IO.StreamReader(new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read), Encoding.UTF8)) { var pem = reader.ReadToEnd(); string base64 = GetBase64FromPem(pem); diff --git a/src/NLog.Targets.Network/NetworkTargetCompressionType.cs b/src/NLog.Targets.Network/Targets/NetworkTargetCompressionType.cs similarity index 100% rename from src/NLog.Targets.Network/NetworkTargetCompressionType.cs rename to src/NLog.Targets.Network/Targets/NetworkTargetCompressionType.cs diff --git a/src/NLog.Targets.Network/NetworkTargetConnectionsOverflowAction.cs b/src/NLog.Targets.Network/Targets/NetworkTargetConnectionsOverflowAction.cs similarity index 100% rename from src/NLog.Targets.Network/NetworkTargetConnectionsOverflowAction.cs rename to src/NLog.Targets.Network/Targets/NetworkTargetConnectionsOverflowAction.cs diff --git a/src/NLog.Targets.Network/NetworkTargetOverflowAction.cs b/src/NLog.Targets.Network/Targets/NetworkTargetOverflowAction.cs similarity index 100% rename from src/NLog.Targets.Network/NetworkTargetOverflowAction.cs rename to src/NLog.Targets.Network/Targets/NetworkTargetOverflowAction.cs diff --git a/src/NLog.Targets.Network/NetworkTargetQueueOverflowAction.cs b/src/NLog.Targets.Network/Targets/NetworkTargetQueueOverflowAction.cs similarity index 100% rename from src/NLog.Targets.Network/NetworkTargetQueueOverflowAction.cs rename to src/NLog.Targets.Network/Targets/NetworkTargetQueueOverflowAction.cs diff --git a/src/NLog.Targets.Network/Targets/SyslogTarget.cs b/src/NLog.Targets.Network/Targets/SyslogTarget.cs new file mode 100644 index 0000000000..498fec5e0b --- /dev/null +++ b/src/NLog.Targets.Network/Targets/SyslogTarget.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using System.Text; +using NLog.Config; +using NLog.Layouts; + +namespace NLog.Targets +{ + /// + /// Sends log messages to Syslog server using either TCP or UDP with format Rfc3164 or Rfc5424 + /// + /// + /// When using TCP then the default message-delimeter is octet-byte-count prefix, but it can be changed + /// by setting to or + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki + [Target("Syslog")] + public class SyslogTarget : NetworkTarget + { + private readonly SyslogLayout _syslogLayout = new SyslogLayout(); + + /// + public bool Rfc3164 { get => _syslogLayout.Rfc3164; set => _syslogLayout.Rfc3164 = value; } + + /// + public bool Rfc5424 { get => _syslogLayout.Rfc5424; set => _syslogLayout.Rfc5424 = value; } + + /// + public Layout SyslogTimestamp { get => _syslogLayout.SyslogTimestamp; set => _syslogLayout.SyslogTimestamp = value; } + + /// + public Layout SyslogHostName { get => _syslogLayout.SyslogHostName; set => _syslogLayout.SyslogHostName = value; } + + /// + public Layout SyslogAppName { get => _syslogLayout.SyslogAppName; set => _syslogLayout.SyslogAppName = value; } + + /// + public Layout SyslogProcessId { get => _syslogLayout.SyslogProcessId; set => _syslogLayout.SyslogProcessId = value; } + + /// + public Layout SyslogMessageId { get => _syslogLayout.SyslogMessageId; set => _syslogLayout.SyslogMessageId = value; } + + /// + public Layout SyslogMessage { get => _syslogLayout.SyslogMessage; set => _syslogLayout.SyslogMessage = value; } + + /// + public Layout SyslogSeverity { get => _syslogLayout.SyslogSeverity; set => _syslogLayout.SyslogSeverity = value; } + + /// + public SyslogFacility SyslogFacility { get => _syslogLayout.SyslogFacility; set => _syslogLayout.SyslogFacility = value; } + + /// + public Layout StructuredDataId { get => _syslogLayout.StructuredDataId; set => _syslogLayout.StructuredDataId = value; } + + /// + public bool IncludeEventProperties { get => _syslogLayout.IncludeEventProperties; set => _syslogLayout.IncludeEventProperties = value; } + + /// + [ArrayParameter(typeof(TargetPropertyWithContext), "StructuredDataParam")] + public List StructuredDataParams => _syslogLayout.StructuredDataParams; + + /// + /// Initializes a new instance of the class. + /// + public SyslogTarget() + { + LineEnding = LineEndingMode.None; + Layout = _syslogLayout; + } + + /// + protected override byte[] GetHeaderToWrite(LogEventInfo logEvent, string address, byte[] payload) + { + if (LineEnding?.NewLineCharacters?.Length > 0) + return null; + + if (address?.StartsWith("udp", System.StringComparison.OrdinalIgnoreCase) == true) + return null; + + var octetCount = payload.Length; + return Encoding.ASCII.GetBytes($"{octetCount} "); + } + + /// + public override Layout Layout + { + get + { + return _syslogLayout; + } + set + { + // Fixed SyslogLayout + } + } + } +} diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 55229df489..f3eac51ca4 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -391,7 +391,7 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() try { - var disposeTimeout = System.Threading.Tasks.Task.Delay(DefaultFlushTimeout).ContinueWith(prevTask => throw new OperationCanceledException("LogFactory Dispose Timeout")); + var disposeTimeout = System.Threading.Tasks.Task.Delay(DefaultFlushTimeout).ContinueWith(prevTask => throw new OperationCanceledException("NLog LogFactory Dispose Timeout")); await System.Threading.Tasks.TaskExtensions.Unwrap(System.Threading.Tasks.Task.WhenAny(System.Threading.Tasks.Task.Run(() => DisposeInternal()), disposeTimeout)).ConfigureAwait(false); GC.SuppressFinalize(this); } @@ -707,7 +707,7 @@ public System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken } var timeout = cancellationToken.CanBeCanceled ? -1 : (int)DefaultFlushTimeout.TotalMilliseconds; - var flushTimeout = System.Threading.Tasks.Task.Delay(timeout, cancellationToken).ContinueWith(prevTask => throw new OperationCanceledException("LogFactory Flush Timeout")); + var flushTimeout = System.Threading.Tasks.Task.Delay(timeout, cancellationToken).ContinueWith(prevTask => throw new OperationCanceledException("NLog LogFactory Flush Timeout")); flushTimeout.ContinueWith(prevTask => { flushTimeoutHandler.Invoke(null); flushCompleted.TrySetCanceled(); }); return System.Threading.Tasks.TaskExtensions.Unwrap(System.Threading.Tasks.Task.WhenAny(flushCompleted.Task, flushTimeout)); } diff --git a/tests/NLog.Targets.Network.Tests/SyslogLayoutTests.cs b/tests/NLog.Targets.Network.Tests/SyslogLayoutTests.cs new file mode 100644 index 0000000000..99273279f6 --- /dev/null +++ b/tests/NLog.Targets.Network.Tests/SyslogLayoutTests.cs @@ -0,0 +1,232 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets.Network +{ + using System; + using NLog.Layouts; + using Xunit; + + public class SyslogLayoutTests + { + private static string HostName = ResolveHostname(); + private static string ProcessName = ResolveProcessName(); + private static int ProcessId = ResolveProcessId(); + + public SyslogLayoutTests() + { + LogManager.ThrowExceptions = true; + LogManager.Setup().SetupExtensions(ext => + { + ext.RegisterLayout(); + }); + } + + [Fact] + public void SyslogLayout_Rfc3164() + { + var memTarget = new NLog.Targets.MemoryTarget() { Layout = new SyslogLayout() }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + { + var logger = logFactory.GetCurrentClassLogger(); + var logEvent = new LogEventInfo(LogLevel.Info, null, "Hello World"); + logger.Log(logEvent); + + var dateFormat = logEvent.TimeStamp.Day < 10 ? "{0:MMM d HH:mm:ss}" : "{0:MMM dd HH:mm:ss}"; + var timestamp = string.Format(System.Globalization.CultureInfo.InvariantCulture, dateFormat, logEvent.TimeStamp); + + Assert.Single(memTarget.Logs); + Assert.Equal($"<134>{timestamp} {HostName} {ProcessName}[{ProcessId}]: {logEvent.Message}", memTarget.Logs[0]); + } + } + + [Fact] + public void SyslogLayout_Rfc3164_Newline() + { + var memTarget = new NLog.Targets.MemoryTarget() { Layout = new SyslogLayout() }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + { + var logger = logFactory.GetCurrentClassLogger(); + var logEvent = new LogEventInfo(LogLevel.Info, null, "Hello World\r\nGoodbye World"); + logger.Log(logEvent); + + var dateFormat = logEvent.TimeStamp.Day < 10 ? "{0:MMM d HH:mm:ss}" : "{0:MMM dd HH:mm:ss}"; + var timestamp = string.Format(System.Globalization.CultureInfo.InvariantCulture, dateFormat, logEvent.TimeStamp); + + Assert.Single(memTarget.Logs); + Assert.Equal($"<134>{timestamp} {HostName} {ProcessName}[{ProcessId}]: Hello World Goodbye World", memTarget.Logs[0]); + } + } + + [Theory] + [InlineData("Trace", "<143>")] + [InlineData("Debug", "<143>")] + [InlineData("Info", "<142>")] + [InlineData("Warn", "<140>")] + [InlineData("Error", "<139>")] + [InlineData("Fatal", "<136>")] + public void SyslogLayout_Rfc3164_LogLevels(string logLevel, string priValue) + { + var memTarget = new NLog.Targets.MemoryTarget() { Layout = new SyslogLayout() { SyslogFacility = SyslogFacility.Local1 } }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + { + var logger = logFactory.GetCurrentClassLogger(); + var logEvent = new LogEventInfo(LogLevel.FromString(logLevel), null, "Hello World"); + logger.Log(logEvent); + + var dateFormat = logEvent.TimeStamp.Day < 10 ? "{0:MMM d HH:mm:ss}" : "{0:MMM dd HH:mm:ss}"; + var timestamp = string.Format(System.Globalization.CultureInfo.InvariantCulture, dateFormat, logEvent.TimeStamp); + + Assert.Single(memTarget.Logs); + Assert.Equal($"{priValue}{timestamp} {HostName} {ProcessName}[{ProcessId}]: {logEvent.Message}", memTarget.Logs[0]); + } + } + + [Fact] + public void SyslogLayout_Rfc3164_EscapeNames() + { + var memTarget = new NLog.Targets.MemoryTarget() { Layout = new SyslogLayout() { SyslogHostName = " Hello World ", SyslogAppName = " Destroy World ", SyslogProcessId = " Goodbye World " } }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + { + var logger = logFactory.GetCurrentClassLogger(); + var logEvent = new LogEventInfo(LogLevel.Info, null, "Hello World"); + logger.Log(logEvent); + + var dateFormat = logEvent.TimeStamp.Day < 10 ? "{0:MMM d HH:mm:ss}" : "{0:MMM dd HH:mm:ss}"; + var timestamp = string.Format(System.Globalization.CultureInfo.InvariantCulture, dateFormat, logEvent.TimeStamp); + + Assert.Single(memTarget.Logs); + Assert.Equal($"<134>{timestamp} Hello_World Destroy_World[Goodbye_World]: {logEvent.Message}", memTarget.Logs[0]); + } + } + + [Fact] + public void SyslogLayout_Rfc5424() + { + var memTarget = new NLog.Targets.MemoryTarget() { Layout = new SyslogLayout() { Rfc5424 = true } }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + { + var logger = logFactory.GetCurrentClassLogger(); + var logEvent = LogEventInfo.Create(LogLevel.Info, null, "Hello World"); + logger.Log(logEvent); + + Assert.Single(memTarget.Logs); + Assert.Equal($"<134>1 {logEvent.TimeStamp:o} {HostName} {ProcessName} {ProcessId} - {logEvent.Message}", memTarget.Logs[0]); + } + } + + [Fact] + public void SyslogLayout_Rfc5424_Newline() + { + var memTarget = new NLog.Targets.MemoryTarget() { Layout = new SyslogLayout() { Rfc5424 = true } }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + { + var logger = logFactory.GetCurrentClassLogger(); + var logEvent = LogEventInfo.Create(LogLevel.Info, null, "Hello World\r\nGoodbye World"); + logger.Log(logEvent); + + Assert.Single(memTarget.Logs); + Assert.Equal($"<134>1 {logEvent.TimeStamp:o} {HostName} {ProcessName} {ProcessId} - Hello World\r\nGoodbye World", memTarget.Logs[0]); + } + } + + [Theory] + [InlineData("Trace", "<143>")] + [InlineData("Debug", "<143>")] + [InlineData("Info", "<142>")] + [InlineData("Warn", "<140>")] + [InlineData("Error", "<139>")] + [InlineData("Fatal", "<136>")] + public void SyslogLayout_Rfc5424_LogLevels(string logLevel, string priValue) + { + var memTarget = new NLog.Targets.MemoryTarget() { Layout = new SyslogLayout() { Rfc5424 = true, SyslogFacility = SyslogFacility.Local1 } }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + { + var logger = logFactory.GetCurrentClassLogger(); + var logEvent = LogEventInfo.Create(LogLevel.FromString(logLevel), null, "Hello World"); + logger.Log(logEvent); + + Assert.Single(memTarget.Logs); + Assert.Equal($"{priValue}1 {logEvent.TimeStamp:o} {HostName} {ProcessName} {ProcessId} - {logEvent.Message}", memTarget.Logs[0]); + } + } + + [Fact] + public void SyslogLayout_Rfc5424_EscapeNames() + { + var memTarget = new NLog.Targets.MemoryTarget() { Layout = new SyslogLayout() { Rfc5424 = true } }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + { + var logger = logFactory.GetCurrentClassLogger(); + var logEvent = LogEventInfo.Create(LogLevel.Info, null, "Hello World"); + logger.Log(logEvent); + + Assert.Single(memTarget.Logs); + Assert.Equal($"<134>1 {logEvent.TimeStamp:o} {HostName} {ProcessName} {ProcessId} - {logEvent.Message}", memTarget.Logs[0]); + } + } + + [Fact] + public void SyslogLayout_Rfc5424_StructuredData() + { + var syslogLayout = new SyslogLayout() { Rfc5424 = true }; + syslogLayout.StructuredDataParams.Add(new TargetPropertyWithContext(" Hello World ", " Goodbye World ")); + syslogLayout.IncludeEventProperties = true; + + var memTarget = new NLog.Targets.MemoryTarget() { Layout = syslogLayout }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + { + var guid = Guid.NewGuid(); + + var logger = logFactory.GetCurrentClassLogger(); + var logEvent = LogEventInfo.Create(LogLevel.Info, null, null, "Hello {World} with {CorrelationKey}", new object[] { "\nEarth\n", guid }); + logger.Log(logEvent); + + Assert.Single(memTarget.Logs); + Assert.Equal($"<134>1 {logEvent.TimeStamp:o} {HostName} {ProcessName} {ProcessId} - [meta World=\" Earth \" CorrelationKey=\"{guid}\" Hello_World=\" Goodbye World \"] {logEvent.FormattedMessage}", memTarget.Logs[0]); + } + } + + static string ResolveHostname() + { + return Environment.GetEnvironmentVariable("HOSTNAME") + ?? Environment.GetEnvironmentVariable("COMPUTERNAME") + ?? Environment.GetEnvironmentVariable("MACHINENAME") + ?? Environment.MachineName; + } + + static string ResolveProcessName() => System.Diagnostics.Process.GetCurrentProcess().ProcessName; + + static int ResolveProcessId() => System.Diagnostics.Process.GetCurrentProcess().Id; + } +} diff --git a/tests/NLog.UnitTests/ApiTests.cs b/tests/NLog.UnitTests/ApiTests.cs index 2e1ac16471..c3f73c591d 100644 --- a/tests/NLog.UnitTests/ApiTests.cs +++ b/tests/NLog.UnitTests/ApiTests.cs @@ -31,12 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System.Linq; - namespace NLog.UnitTests { using System; using System.Collections.Generic; + using System.Linq; using System.Reflection; using System.Text; using NLog.Config; @@ -47,8 +46,8 @@ namespace NLog.UnitTests /// public class ApiTests : NLogTestBase { - private Type[] allTypes; - private Assembly nlogAssembly = typeof(LogManager).Assembly; + private readonly Type[] allTypes; + private readonly Assembly nlogAssembly = typeof(LogManager).Assembly; private readonly Dictionary typeUsageCount = new Dictionary(); public ApiTests() @@ -274,21 +273,19 @@ public void ValidateLayoutRendererTypeAlias() // Do NOT add more incorrect class-names to this exlusion-list HashSet oldFaultyClassNames = new HashSet() { - "GarbageCollectorInfoLayoutRenderer", - "ScopeContextNestedStatesLayoutRenderer", - "ScopeContextPropertyLayoutRenderer", - "ScopeContextTimingLayoutRenderer", - "ScopeContextIndentLayoutRenderer", - "TraceActivityIdLayoutRenderer", - "SpecialFolderApplicationDataLayoutRenderer", - "SpecialFolderCommonApplicationDataLayoutRenderer", - "SpecialFolderLocalApplicationDataLayoutRenderer", - "DirectorySeparatorLayoutRenderer", - "LiteralWithRawValueLayoutRenderer", - "LocalIpAddressLayoutRenderer", - "VariableLayoutRenderer", - "ObjectPathRendererWrapper", - "PaddingLayoutRendererWrapper", + nameof(NLog.LayoutRenderers.GarbageCollectorInfoLayoutRenderer), + nameof(NLog.LayoutRenderers.ScopeContextNestedStatesLayoutRenderer), + nameof(NLog.LayoutRenderers.ScopeContextPropertyLayoutRenderer), + nameof(NLog.LayoutRenderers.ScopeContextTimingLayoutRenderer), + nameof(NLog.LayoutRenderers.ScopeContextIndentLayoutRenderer), + nameof(NLog.LayoutRenderers.SpecialFolderApplicationDataLayoutRenderer), + nameof(NLog.LayoutRenderers.SpecialFolderCommonApplicationDataLayoutRenderer), + nameof(NLog.LayoutRenderers.SpecialFolderLocalApplicationDataLayoutRenderer), + nameof(NLog.LayoutRenderers.DirectorySeparatorLayoutRenderer), + nameof(NLog.LayoutRenderers.LiteralWithRawValueLayoutRenderer), + nameof(NLog.LayoutRenderers.VariableLayoutRenderer), + nameof(NLog.LayoutRenderers.Wrappers.ObjectPathRendererWrapper), + nameof(NLog.LayoutRenderers.Wrappers.PaddingLayoutRendererWrapper), }; foreach (Type type in allTypes) From f198d805e790fb8ddf75c4ece59095ad2bce918f Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 2 Apr 2025 22:36:44 +0200 Subject: [PATCH 072/224] ServiceRepository - Failing to resolve service-type should report service-type in Exception message (#5757) --- src/NLog/Config/ServiceRepositoryExtensions.cs | 14 +++++++------- src/NLog/Config/ServiceRepositoryInternal.cs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/NLog/Config/ServiceRepositoryExtensions.cs b/src/NLog/Config/ServiceRepositoryExtensions.cs index 6040d074b1..da6f4cf6f7 100644 --- a/src/NLog/Config/ServiceRepositoryExtensions.cs +++ b/src/NLog/Config/ServiceRepositoryExtensions.cs @@ -74,15 +74,15 @@ internal static T ResolveService(this ServiceRepository serviceProvider, bool } catch (Exception ex) { - if (ex.MustBeRethrown()) + if (ex.MustBeRethrownImmediately()) throw; - throw new NLogDependencyResolveException(ex.Message, ex, typeof(T)); + throw new NLogDependencyResolveException($"Service Provider cannot resolve type {typeof(T)} - {ex.Message}", ex, typeof(T)); } if (ReferenceEquals(externalServiceProvider, serviceProvider)) { - throw new NLogDependencyResolveException("Instance of class must be registered", typeof(T)); + throw new NLogDependencyResolveException($"Service Provider cannot resolve type {typeof(T)}", typeof(T)); } // External IServiceProvider can be dangerous to use from Logging-library and can lead to deadlock or stackoverflow @@ -100,7 +100,7 @@ internal static T GetService(this IServiceProvider serviceProvider) where T : { var service = (serviceProvider ?? LogManager.LogFactory.ServiceRepository).GetService(typeof(T)) as T; if (service is null) - throw new NLogDependencyResolveException("Instance of class is unavailable", typeof(T)); + throw new NLogDependencyResolveException($"Service Provider cannot resolve type {typeof(T)}", typeof(T)); return service; } @@ -109,14 +109,14 @@ internal static T GetService(this IServiceProvider serviceProvider) where T : if (ex.ServiceType == typeof(T)) throw; - throw new NLogDependencyResolveException(ex.Message, ex, typeof(T)); + throw new NLogDependencyResolveException($"Service Provider cannot resolve type {typeof(T)} - {ex.Message}", ex, typeof(T)); } catch (Exception ex) { - if (ex.MustBeRethrown()) + if (ex.MustBeRethrownImmediately()) throw; - throw new NLogDependencyResolveException(ex.Message, ex, typeof(T)); + throw new NLogDependencyResolveException($"Service Provider cannot resolve type {typeof(T)} - {ex.Message}", ex, typeof(T)); } } diff --git a/src/NLog/Config/ServiceRepositoryInternal.cs b/src/NLog/Config/ServiceRepositoryInternal.cs index d3c188de43..e0cd492a26 100644 --- a/src/NLog/Config/ServiceRepositoryInternal.cs +++ b/src/NLog/Config/ServiceRepositoryInternal.cs @@ -75,7 +75,7 @@ public override object GetService(Type serviceType) { object serviceInstance = TryGetService(serviceType); if (serviceInstance is null) - throw new NLogDependencyResolveException("Instance of class must be registered", serviceType); + throw new NLogDependencyResolveException($"Service Provider cannot resolve type {serviceType}", serviceType); return serviceInstance; } From 4fa9e9aefcf7afb4984061eb8be4606cdbae285c Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 3 Apr 2025 09:22:28 +0200 Subject: [PATCH 073/224] ServiceRepository - Failing to resolve service-type should report service-type in Exception message (#5760) --- src/NLog/Config/ServiceRepositoryExtensions.cs | 10 +++++----- src/NLog/Config/ServiceRepositoryInternal.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/NLog/Config/ServiceRepositoryExtensions.cs b/src/NLog/Config/ServiceRepositoryExtensions.cs index da6f4cf6f7..4093c223e6 100644 --- a/src/NLog/Config/ServiceRepositoryExtensions.cs +++ b/src/NLog/Config/ServiceRepositoryExtensions.cs @@ -77,12 +77,12 @@ internal static T ResolveService(this ServiceRepository serviceProvider, bool if (ex.MustBeRethrownImmediately()) throw; - throw new NLogDependencyResolveException($"Service Provider cannot resolve type {typeof(T)} - {ex.Message}", ex, typeof(T)); + throw new NLogDependencyResolveException($"Service Provider failed with exception - {ex.Message}", ex, typeof(T)); } if (ReferenceEquals(externalServiceProvider, serviceProvider)) { - throw new NLogDependencyResolveException($"Service Provider cannot resolve type {typeof(T)}", typeof(T)); + throw new NLogDependencyResolveException("Type not registered in Service Provider", typeof(T)); } // External IServiceProvider can be dangerous to use from Logging-library and can lead to deadlock or stackoverflow @@ -100,7 +100,7 @@ internal static T GetService(this IServiceProvider serviceProvider) where T : { var service = (serviceProvider ?? LogManager.LogFactory.ServiceRepository).GetService(typeof(T)) as T; if (service is null) - throw new NLogDependencyResolveException($"Service Provider cannot resolve type {typeof(T)}", typeof(T)); + throw new NLogDependencyResolveException("Type not registered in Service Provider", typeof(T)); return service; } @@ -109,14 +109,14 @@ internal static T GetService(this IServiceProvider serviceProvider) where T : if (ex.ServiceType == typeof(T)) throw; - throw new NLogDependencyResolveException($"Service Provider cannot resolve type {typeof(T)} - {ex.Message}", ex, typeof(T)); + throw new NLogDependencyResolveException(ex.Message, ex, typeof(T)); } catch (Exception ex) { if (ex.MustBeRethrownImmediately()) throw; - throw new NLogDependencyResolveException($"Service Provider cannot resolve type {typeof(T)} - {ex.Message}", ex, typeof(T)); + throw new NLogDependencyResolveException($"Service Provider failed with exception - {ex.Message}", ex, typeof(T)); } } diff --git a/src/NLog/Config/ServiceRepositoryInternal.cs b/src/NLog/Config/ServiceRepositoryInternal.cs index e0cd492a26..07204a4fa9 100644 --- a/src/NLog/Config/ServiceRepositoryInternal.cs +++ b/src/NLog/Config/ServiceRepositoryInternal.cs @@ -75,7 +75,7 @@ public override object GetService(Type serviceType) { object serviceInstance = TryGetService(serviceType); if (serviceInstance is null) - throw new NLogDependencyResolveException($"Service Provider cannot resolve type {serviceType}", serviceType); + throw new NLogDependencyResolveException("Type not registered in Service Provider", serviceType); return serviceInstance; } From a066d076cd5650c6744177dfd86b55f2a26e954e Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 3 Apr 2025 18:13:24 +0200 Subject: [PATCH 074/224] LayoutRenderer - Skip caching result of Inner Layout Render (#5758) --- src/NLog/Filters/WhenContainsFilter.cs | 4 ++-- src/NLog/Filters/WhenEqualFilter.cs | 4 ++-- src/NLog/Filters/WhenNotContainsFilter.cs | 3 +-- src/NLog/Filters/WhenNotEqualFilter.cs | 4 ++-- src/NLog/Filters/WhenRepeatedFilter.cs | 2 +- src/NLog/LayoutRenderers/CounterLayoutRenderer.cs | 2 +- src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs | 2 +- .../LayoutRenderers/ScopeContextIndentLayoutRenderer.cs | 2 +- .../LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs | 2 +- .../Wrappers/WhenEmptyLayoutRendererWrapper.cs | 6 +++--- .../LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs | 2 +- 11 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/NLog/Filters/WhenContainsFilter.cs b/src/NLog/Filters/WhenContainsFilter.cs index 0f6fb69bdc..d3374ef355 100644 --- a/src/NLog/Filters/WhenContainsFilter.cs +++ b/src/NLog/Filters/WhenContainsFilter.cs @@ -62,8 +62,8 @@ protected override FilterResult Check(LogEventInfo logEvent) StringComparison comparisonType = IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - - if (Layout.Render(logEvent).IndexOf(Substring, comparisonType) >= 0) + string result = Layout.Render(logEvent, cacheLayoutResult: false); + if (result.IndexOf(Substring, comparisonType) >= 0) { return Action; } diff --git a/src/NLog/Filters/WhenEqualFilter.cs b/src/NLog/Filters/WhenEqualFilter.cs index e78eeabfbf..c06d4a3d4f 100644 --- a/src/NLog/Filters/WhenEqualFilter.cs +++ b/src/NLog/Filters/WhenEqualFilter.cs @@ -62,8 +62,8 @@ protected override FilterResult Check(LogEventInfo logEvent) StringComparison comparisonType = IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - - if (Layout.Render(logEvent).Equals(CompareTo, comparisonType)) + string result = Layout.Render(logEvent, cacheLayoutResult: false); + if (result.Equals(CompareTo, comparisonType)) { return Action; } diff --git a/src/NLog/Filters/WhenNotContainsFilter.cs b/src/NLog/Filters/WhenNotContainsFilter.cs index ac60f613b0..ffa98acab7 100644 --- a/src/NLog/Filters/WhenNotContainsFilter.cs +++ b/src/NLog/Filters/WhenNotContainsFilter.cs @@ -62,8 +62,7 @@ protected override FilterResult Check(LogEventInfo logEvent) StringComparison comparison = IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - string result = Layout.Render(logEvent); - + string result = Layout.Render(logEvent, cacheLayoutResult: false); if (result.IndexOf(Substring, comparison) < 0) { return Action; diff --git a/src/NLog/Filters/WhenNotEqualFilter.cs b/src/NLog/Filters/WhenNotEqualFilter.cs index c237f16c65..8270223ae2 100644 --- a/src/NLog/Filters/WhenNotEqualFilter.cs +++ b/src/NLog/Filters/WhenNotEqualFilter.cs @@ -69,8 +69,8 @@ protected override FilterResult Check(LogEventInfo logEvent) StringComparison comparisonType = IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - - if (!Layout.Render(logEvent).Equals(CompareTo, comparisonType)) + string result = Layout.Render(logEvent, cacheLayoutResult: false); + if (!result.Equals(CompareTo, comparisonType)) { return Action; } diff --git a/src/NLog/Filters/WhenRepeatedFilter.cs b/src/NLog/Filters/WhenRepeatedFilter.cs index ffc25cef4f..0d0f4dd9cc 100644 --- a/src/NLog/Filters/WhenRepeatedFilter.cs +++ b/src/NLog/Filters/WhenRepeatedFilter.cs @@ -255,7 +255,7 @@ private FilterInfoKey RenderFilterInfoKey(LogEventInfo logEvent, StringBuilder t targetBuilder.Length = MaxLength; return new FilterInfoKey(targetBuilder, null); } - string value = Layout.Render(logEvent); + string value = Layout.Render(logEvent, cacheLayoutResult: false); if (value.Length > MaxLength) value = value.Substring(0, MaxLength); return new FilterInfoKey(null, value); diff --git a/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs b/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs index 28d3be6bf9..3d1f3b0d60 100644 --- a/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs @@ -90,7 +90,7 @@ private long GetNextValue(LogEventInfo logEvent) return currentValue; } - var sequenceName = Sequence.Render(logEvent); + var sequenceName = Sequence.Render(logEvent, cacheLayoutResult: false); lock (Sequences) { if (!Sequences.TryGetValue(sequenceName, out var nextValue)) diff --git a/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs b/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs index ca9c54b639..5bb20008a9 100644 --- a/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs @@ -77,7 +77,7 @@ string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) if (simpleLayout is null) return string.Empty; if (simpleLayout.IsFixedText || simpleLayout.IsSimpleStringText) - return simpleLayout.Render(logEvent); + return simpleLayout.Render(logEvent, cacheLayoutResult: false); return null; } diff --git a/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs index a1098657b8..b025bb5ffd 100644 --- a/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs @@ -62,7 +62,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) var messages = ScopeContext.GetAllNestedStateList(); for (int i = 0; i < messages.Count; ++i) { - indent = indent ?? Indent?.Render(logEvent) ?? string.Empty; + indent = indent ?? Indent?.Render(logEvent, cacheLayoutResult: false) ?? string.Empty; builder.Append(indent); } } diff --git a/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs index 60b063bd50..5d86021558 100644 --- a/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs @@ -134,7 +134,7 @@ protected override string RenderInner(LogEventInfo logEvent) { if (Cached) { - var newCacheKey = CacheKey?.Render(logEvent) ?? string.Empty; + var newCacheKey = CacheKey?.Render(logEvent, cacheLayoutResult: false) ?? string.Empty; var cachedValue = LookupValidCachedValue(logEvent, newCacheKey); if (cachedValue is null) diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs index 6bd594ce2a..85e3978bb1 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs @@ -90,14 +90,14 @@ string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) if (TryGetStringValue(out var innerLayout, out var whenEmptyLayout)) { - var innerValue = innerLayout.Render(logEvent); + var innerValue = innerLayout.Render(logEvent, cacheLayoutResult: false); if (!string.IsNullOrEmpty(innerValue)) { return innerValue; } // render WhenEmpty when the inner layout was empty - return whenEmptyLayout.Render(logEvent); + return whenEmptyLayout.Render(logEvent, cacheLayoutResult: false); } _skipStringValueRenderer = true; @@ -129,7 +129,7 @@ bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) } else { - var innerResult = Inner?.Render(logEvent); // Beware this can be very expensive call + var innerResult = Inner?.Render(logEvent, cacheLayoutResult: false); // Beware this can be very expensive call if (!string.IsNullOrEmpty(innerResult)) { value = null; diff --git a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs index f61eb0d6b2..7c7d8d1bfe 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs @@ -123,7 +123,7 @@ protected virtual string Transform(LogEventInfo logEvent, string text) /// Contents of inner layout. protected virtual string RenderInner(LogEventInfo logEvent) { - return Inner?.Render(logEvent) ?? string.Empty; + return Inner?.Render(logEvent, cacheLayoutResult: false) ?? string.Empty; } } } From d796c42271573aa424a24ae3132c73dbdfc92f58 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 5 Apr 2025 22:05:28 +0200 Subject: [PATCH 075/224] NLog.Targets.Network - Introduced GelfTarget and GelfLayout (#5765) --- .../Layouts/GelfLayout.cs | 517 ++++++++++++++++++ .../Layouts/SyslogLayout.cs | 90 ++- .../Targets/GelfTarget.cs | 102 ++++ .../Targets/NetworkTarget.cs | 2 +- .../Targets/SyslogTarget.cs | 54 +- .../AllEventPropertiesLayoutRenderer.cs | 6 +- .../JsonEncodeLayoutRendererWrapper.cs | 3 +- src/NLog/Layouts/JSON/JsonArrayLayout.cs | 2 +- src/NLog/Layouts/JSON/JsonAttribute.cs | 8 +- src/NLog/Layouts/JSON/JsonLayout.cs | 49 +- src/NLog/LogFactory.cs | 4 +- src/NLog/Targets/DefaultJsonSerializer.cs | 39 +- src/NLog/Targets/JsonSerializeOptions.cs | 7 +- .../GelfLayoutTests.cs | 315 +++++++++++ .../SyslogLayoutTests.cs | 26 +- .../Wrappers/JsonEncodeTests.cs | 4 +- .../NLog.UnitTests/Layouts/JsonLayoutTests.cs | 8 +- tests/NLog.UnitTests/LogFactoryTests.cs | 2 +- 18 files changed, 1117 insertions(+), 121 deletions(-) create mode 100644 src/NLog.Targets.Network/Layouts/GelfLayout.cs create mode 100644 src/NLog.Targets.Network/Targets/GelfTarget.cs create mode 100644 tests/NLog.Targets.Network.Tests/GelfLayoutTests.cs diff --git a/src/NLog.Targets.Network/Layouts/GelfLayout.cs b/src/NLog.Targets.Network/Layouts/GelfLayout.cs new file mode 100644 index 0000000000..26d1e193ed --- /dev/null +++ b/src/NLog.Targets.Network/Layouts/GelfLayout.cs @@ -0,0 +1,517 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NLog.Config; +using NLog.LayoutRenderers; +using NLog.Targets; + +namespace NLog.Layouts +{ + /// + /// GELF (Graylog Extended Log Format) is a JSON-based, structured log format for Graylog Log Management. + /// + /// + /// { + /// "version": "1.1", + /// "host": "example.org", + /// "short_message": "A short message that helps you identify what is going on", + /// "full_message": "Backtrace here\n\nmore stuff", + /// "timestamp": 1385053862.3072, + /// "level": 1, + /// "_user_id": 9001, + /// "_some_info": "foo", + /// "_some_env_var": "bar" + /// } + /// + [Layout("GelfLayout")] + [ThreadAgnostic] + [AppDomainFixedOutput] + public class GelfLayout : CompoundLayout + { + private IJsonConverter JsonConverter + { + get => _jsonConverter ?? (_jsonConverter = ResolveService()); + set => _jsonConverter = value; + } + private IJsonConverter _jsonConverter; + + /// + /// Graylog Message Host-field + /// + public Layout GelfHostName + { + get => _gelfHostName; + set + { + _gelfHostName = value; + _gelfHostNameString = null; + } + } + private Layout _gelfHostName; + private string _gelfHostNameString; + + /// + /// Graylog Message Short-Message-field + /// + /// Will truncate when longer than 250 chars + public Layout GelfShortMessage { get; set; } + + /// + /// Graylog Message Full-Message-field + /// + /// Will truncate when longer than 250 chars + public Layout GelfFullMessage { get; set; } + + /// + /// Graylog Message Facility-field + /// + /// + /// Activated legacy GELF v1.0 format + /// + public Layout GelfFacility + { + get => _gelfFacility; + set + { + _gelfFacility = value; + _gelfFacilityString = null; + } + } + private Layout _gelfFacility; + private string _gelfFacilityString; + + /// + /// Gets or sets GELF additional fields + /// + [ArrayParameter(typeof(TargetPropertyWithContext), "GelfField")] + public List GelfFields { get; } = new List(); + + /// + /// Gets or sets the option to include all properties from the log events + /// + public bool IncludeEventProperties { get; set; } + + /// + /// Gets or sets whether to include the contents of the properties-dictionary. + /// + public bool IncludeScopeProperties { get; set; } + + /// + /// Gets or sets the option to exclude null/empty properties from the log event (as JSON) + /// + public bool ExcludeEmptyProperties { get; set; } + + /// + /// List of property names to exclude when is true + /// + public ISet ExcludeProperties { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); + + /// + /// List of property names to include when is true + /// + public ISet IncludeProperties { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); + + /// + /// Disables to capture ScopeContext-properties from active thread context + /// + public LayoutRenderer DisableThreadAgnostic => IncludeScopeProperties ? _disableThreadAgnostic : (IncludeEventProperties ? _enableThreadAgnosticImmutable : null); + private static readonly LayoutRenderer _disableThreadAgnostic = new ScopeContextPropertyLayoutRenderer() { Item = string.Empty }; + private static readonly LayoutRenderer _enableThreadAgnosticImmutable = new ExceptionDataLayoutRenderer() { Item = string.Empty }; + + /// + /// Initializes a new instance of the class. + /// + public GelfLayout() + { + GelfHostName = "${hostname}"; + GelfShortMessage = GelfFullMessage = "${message}"; + } + + /// + protected override void InitializeLayout() + { + if (GelfFields.Count == 0) + { + GelfFields.Add(new TargetPropertyWithContext("_logLevel", "${level}")); + GelfFields.Add(new TargetPropertyWithContext("_logger", "${logger}")); + GelfFields.Add(new TargetPropertyWithContext("_exceptionType", "${exception:Format=Type}") { IncludeEmptyValue = false }); + GelfFields.Add(new TargetPropertyWithContext("_exceptionMessage", "${exception:Format=Message}") { IncludeEmptyValue = false }); + GelfFields.Add(new TargetPropertyWithContext("_stackTrace", "${exception:Format=ToString}") { IncludeEmptyValue = false }); + } + + // CompoundLayout includes optimization, so only doing precalculate/caching of relevant Layouts (instead of the entire GELF-message) + Layouts.Clear(); + if (!IncludeEventProperties && !IncludeScopeProperties) + { + if (GelfFacility != null) + Layouts.Add(GelfFacility); + if (GelfHostName != null) + Layouts.Add(GelfHostName); + if (GelfShortMessage != null) + Layouts.Add(GelfShortMessage); + if (GelfFullMessage != null) + Layouts.Add(GelfFullMessage); + for (int i = 0; i < GelfFields.Count; ++i) + Layouts.Add(GelfFields[i].Layout); + } + + base.InitializeLayout(); + + _gelfHostNameString = ResolveJsonFixedString(_gelfHostName); + _gelfFacilityString = ResolveJsonFixedString(_gelfFacility); + if (_gelfFacilityString != null && string.IsNullOrWhiteSpace(_gelfFacilityString)) + _gelfFacilityString = "GELF"; + } + + /// + protected override void CloseLayout() + { + base.CloseLayout(); + JsonConverter = null; + } + + private string ResolveJsonFixedString(Layout layout) + { + if (layout is SimpleLayout simpleLayout && simpleLayout.IsFixedText) + { + var stringBuilder = new StringBuilder(); + JsonConverter.SerializeObject(simpleLayout.FixedText, stringBuilder); + return stringBuilder.ToString(); + } + return null; + } + + /// + protected override string GetFormattedMessage(LogEventInfo logEvent) + { + var stringBuilder = new StringBuilder(); + RenderFormattedMessage(logEvent, stringBuilder); + return stringBuilder.ToString(); + } + + /// + protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) + { + // GELF ver. 1.1 + target.Append(_beginJsonMessage); + target.Append("version"); + target.Append(_completeJsonPropertyName); + target.Append('"'); + target.Append(GelfVersion11); + target.Append('"'); + + target.Append(_beginJsonPropertyName); + target.Append("host"); + target.Append(_completeJsonPropertyName); + if (_gelfHostNameString is null) + JsonConverter.SerializeObject(GelfHostName?.Render(logEvent) ?? string.Empty, target); + else + target.Append(_gelfHostNameString); + + var shortMessage = GelfShortMessage?.Render(logEvent) ?? string.Empty; + target.Append(_beginJsonPropertyName); + target.Append("short_message"); + target.Append(_completeJsonPropertyName); + JsonConverter.SerializeObject(shortMessage.Length > ShortMessageMaxLength ? shortMessage.Substring(0, ShortMessageMaxLength) : shortMessage, target); + + var fullMessage = GelfFullMessage?.Render(logEvent) ?? string.Empty; + if (string.IsNullOrEmpty(fullMessage) && shortMessage.Length > ShortMessageMaxLength) + fullMessage = shortMessage; + else if (fullMessage?.Length < ShortMessageMaxLength && string.Equals(fullMessage, shortMessage, StringComparison.Ordinal)) + fullMessage = string.Empty; + if (!string.IsNullOrEmpty(fullMessage)) + { + target.Append(_beginJsonPropertyName); + target.Append("full_message"); + target.Append(_completeJsonPropertyName); + JsonConverter.SerializeObject(fullMessage.Length > FullMessageMaxLength ? fullMessage.Substring(0, FullMessageMaxLength) : fullMessage, target); + } + + var unixTimestamp = ToUnixTimeStamp(logEvent.TimeStamp); + target.Append(_beginJsonPropertyName); + target.Append("timestamp"); + target.Append(_completeJsonPropertyName); + target.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0}", unixTimestamp); + + var gelfSeverity = ToSyslogSeverity(logEvent.Level); + target.Append(_beginJsonPropertyName); + target.Append("level"); + target.Append(_completeJsonPropertyName); + target.Append((int)gelfSeverity); + + if (_gelfFacility != null) + { + target.Append(_beginJsonPropertyName); + target.Append("facility"); + target.Append(_completeJsonPropertyName); + if (_gelfFacilityString is null) + JsonConverter.SerializeObject(_gelfFacility?.Render(logEvent) ?? string.Empty, target); + else + target.Append(_gelfFacilityString); + + var callerLineNumber = logEvent.CallerLineNumber; + if (callerLineNumber > 0) + { + target.Append(_beginJsonPropertyName); + target.Append("line"); + target.Append(_completeJsonPropertyName); + target.Append(callerLineNumber); + } + + var callerFileName = logEvent.CallerFilePath ?? logEvent.CallerClassName ?? logEvent.Exception?.TargetSite?.ToString() ?? logEvent.LoggerName; + if (!string.IsNullOrEmpty(callerFileName)) + { + target.Append(_beginJsonPropertyName); + target.Append("file"); + target.Append(_completeJsonPropertyName); + JsonConverter.SerializeObject(callerFileName, target); + } + } + + var eventProperties = IncludeEventProperties ? ResolveEventProperties(logEvent) : null; + var scopePropertyList = IncludeScopeProperties ? ResolveScopePropertyList() : null; + bool filterGelfFields = eventProperties?.Count > 0 || scopePropertyList?.Count > 0; + + foreach (var field in GelfFields) + { + var fieldName = field.Name; + if (string.IsNullOrEmpty(fieldName)) + continue; + + if (filterGelfFields && ExcludeGelfField(fieldName, eventProperties, scopePropertyList)) + continue; + + var fieldValue = field.RenderValue(logEvent); + if (!field.IncludeEmptyValue && (fieldValue is null || ReferenceEquals(string.Empty, fieldValue))) + continue; + + BeginJsonProperty(target, fieldName, fieldValue); + } + + if (scopePropertyList?.Count > 0) + { + var filterScopeProperties = eventProperties?.Count > 0 || ExcludeProperties?.Count > 0; + for (int i = 0; i < scopePropertyList.Count; ++i) + { + var scopeProperty = scopePropertyList[i]; + if (ExcludeEmptyProperties && (scopeProperty.Value is null || ReferenceEquals(scopeProperty.Value, string.Empty))) + continue; + + if (filterScopeProperties && ExcludeScopeProperty(scopeProperty.Key, eventProperties, ExcludeProperties)) + continue; + + BeginJsonProperty(target, scopeProperty.Key, scopeProperty.Value); + } + } + + if (eventProperties?.Count > 0) + { + var filterEventProperties = ExcludeProperties?.Count > 0; + foreach (var eventProperty in eventProperties) + { + if (ExcludeEmptyProperties && (eventProperty.Value is null || ReferenceEquals(eventProperty.Value, string.Empty))) + continue; + + var eventPropertyName = eventProperty.Key?.ToString() ?? string.Empty; + if (filterEventProperties && ExcludeProperties.Contains(eventPropertyName)) + continue; + + BeginJsonProperty(target, eventPropertyName, eventProperty.Value); + } + } + + target.Append(_completeJsonMessage); + } + + private static bool ExcludeScopeProperty(string propertyName, IDictionary eventProperties, ISet excludeProperties) + { + if (excludeProperties?.Contains(propertyName) == true) + return true; + if (eventProperties?.ContainsKey(propertyName) == true) + return true; + return false; + } + + private static bool ExcludeGelfField(string gelfFieldName, IDictionary eventProperties, IList> scopeProperties) + { + if (string.IsNullOrEmpty(gelfFieldName)) + return true; + + if (eventProperties?.ContainsKey(gelfFieldName) == true) + return true; + + var normalPropertyName = gelfFieldName[0] == '_' ? gelfFieldName.Substring(1) : null; + if (normalPropertyName != null && eventProperties?.ContainsKey(normalPropertyName) == true) + return true; + + if (scopeProperties?.Count > 0) + { + for (int i = 0; i < scopeProperties.Count; ++i) + { + if (gelfFieldName.Equals(scopeProperties[i].Key, StringComparison.OrdinalIgnoreCase)) + return true; + if (normalPropertyName?.Equals(scopeProperties[i].Key, StringComparison.OrdinalIgnoreCase) == true) + return true; + } + } + + return false; + } + + private IDictionary ResolveEventProperties(LogEventInfo logEvent) + { + if (!logEvent.HasProperties) + return null; + + var eventProperties = logEvent.Properties; + if (IncludeProperties?.Count > 0) + { + bool filterProperty = false; + bool foundProperty = false; + foreach (var eventProperty in logEvent.Properties) + { + if (IncludeProperties.Contains(eventProperty.Key?.ToString() ?? string.Empty)) + foundProperty = true; + else + filterProperty = true; + + if (filterProperty && foundProperty) + break; + } + + if (filterProperty) + return foundProperty ? eventProperties.Where(p => IncludeProperties.Contains(p.Key)).ToDictionary(p => p.Key, p => p.Value) : null; + } + + return eventProperties; + } + + private IList> ResolveScopePropertyList() + { + var scopeProperties = ScopeContext.GetAllProperties(); + if (scopeProperties is IList> scopePropertyList) + { + if (scopePropertyList.Count == 0) + return null; + + if (IncludeProperties?.Count > 0) + { + bool filterProperty = false; + bool foundProperty = false; + for (int i = 0; i < scopePropertyList.Count; ++i) + { + if (!IncludeProperties.Contains(scopePropertyList[i].Key)) + filterProperty = true; + else + foundProperty = true; + + if (filterProperty && foundProperty) + break; + } + + if (filterProperty) + return foundProperty ? scopePropertyList.Where(p => IncludeProperties.Contains(p.Key)).ToList() : null; + } + + return scopePropertyList; + } + + if (IncludeProperties?.Count > 0) + scopePropertyList = scopeProperties?.Where(p => IncludeProperties.Contains(p.Key)).ToList(); + else + scopePropertyList = scopeProperties?.ToList(); + return scopePropertyList?.Count > 0 ? scopePropertyList : null; + } + + private void BeginJsonProperty(StringBuilder sb, string propName, object propertyValue) + { + if (ExcludeEmptyProperties && (propertyValue is null || ReferenceEquals(propertyValue, string.Empty))) + return; + + var initialLength = sb.Length; + + sb.Append(_beginJsonPropertyName); + + sb.Append(EscapePropertyName(propName)); + + sb.Append(_completeJsonPropertyName); + + if (!JsonConverter.SerializeObject(propertyValue, sb)) + { + sb.Length = initialLength; + } + + if (ExcludeEmptyProperties && sb[sb.Length - 1] == '"' && sb[sb.Length - 2] == '"' && sb[sb.Length - 3] != '\\') + { + sb.Length = initialLength; + } + } + + internal static decimal ToUnixTimeStamp(DateTime timeStamp) + { + return Convert.ToDecimal(timeStamp.ToUniversalTime().Subtract(UnixDateStart).TotalSeconds); + } + + internal static SyslogSeverity ToSyslogSeverity(LogLevel logLevel) + { + try + { + return _logLevelMapping[logLevel.Ordinal]; + } + catch (IndexOutOfRangeException) + { + return SyslogSeverity.Emergency; + } + } + + private static readonly SyslogSeverity[] _logLevelMapping = new [] + { + NLog.Layouts.SyslogSeverity.Debug, + NLog.Layouts.SyslogSeverity.Debug, + NLog.Layouts.SyslogSeverity.Informational, + NLog.Layouts.SyslogSeverity.Warning, + NLog.Layouts.SyslogSeverity.Error, + NLog.Layouts.SyslogSeverity.Emergency, + NLog.Layouts.SyslogSeverity.Emergency, + }; + + private readonly string _beginJsonMessage = "{\""; + private readonly string _completeJsonMessage = "}"; + private readonly string _beginJsonPropertyName = ",\""; + private readonly string _completeJsonPropertyName = "\":"; + private const string GelfVersion11 = "1.1"; + private static DateTime UnixDateStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + private const int ShortMessageMaxLength = 250; + private const int FullMessageMaxLength = 16383; // Truncate due to: https://github.com/Graylog2/graylog2-server/issues/873 + + private static string EscapePropertyName(string propertyName) + { + if (string.IsNullOrWhiteSpace(propertyName)) + return string.Empty; + + foreach (var chr in propertyName) + { + if (RequiresJsonEscape(chr, true) || char.IsWhiteSpace(chr)) + { + propertyName = new string(propertyName.Select(c => char.IsWhiteSpace(c) || RequiresJsonEscape(c, true) ? '_' : c).ToArray()).Replace("__", "_").Trim('_'); + return string.IsNullOrEmpty(propertyName) ? string.Empty : $"_{propertyName}"; + } + } + + if (propertyName[0] == '_') + return propertyName; + else + return $"_{propertyName}"; + } + + private static bool RequiresJsonEscape(char ch, bool escapeUnicode) + { + if (ch < 32) + return true; + if (ch > 127) + return escapeUnicode; + return ch == '"' || ch == '\\'; + } + } +} diff --git a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs index 5cdad9ad43..9a149abd0a 100644 --- a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs +++ b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using NLog.Config; +using NLog.LayoutRenderers; using NLog.Targets; namespace NLog.Layouts @@ -10,7 +11,10 @@ namespace NLog.Layouts /// /// A specialized layout that renders Syslog-formatted events in format Rfc3164 / Rfc5424 /// - public class SyslogLayout : Layout + [Layout("SyslogLayout")] + [ThreadAgnostic] + [AppDomainFixedOutput] + public class SyslogLayout : CompoundLayout { private const string Rfc5424DefaultVersion = "1"; private const string Rfc3164TimestampFormat = "{0:MMM} {0,11:d HH:mm:ss}"; @@ -119,7 +123,7 @@ public Layout SyslogProcessId /// /// Device Facility /// - public SyslogFacility SyslogFacility { get; set; } = SyslogFacility.Local0; + public SyslogFacility SyslogFacility { get; set; } = SyslogFacility.User; /// /// Gets or sets the prefix for StructuredData when = true @@ -139,6 +143,12 @@ public Layout SyslogProcessId private KeyValuePair> _priValueMapping; + /// + /// Disables to capture volatile LogEvent-properties from active thread context + /// + public LayoutRenderer DisableThreadAgnostic => IncludeEventProperties ? _enableThreadAgnosticImmutable : null; + private static readonly LayoutRenderer _enableThreadAgnosticImmutable = new ExceptionDataLayoutRenderer() { Item = string.Empty }; + /// /// Initializes a new instance of the class. /// @@ -149,6 +159,42 @@ public SyslogLayout() SyslogProcessId = "${processid}"; } + /// + protected override void InitializeLayout() + { + // CompoundLayout includes optimization, so only doing precalculate/caching of relevant Layouts (instead of the entire SysLog-message) + Layouts.Clear(); + if (!IncludeEventProperties) + { + if (SyslogTimestamp != null) + Layouts.Add(SyslogTimestamp); + if (SyslogHostName != null) + Layouts.Add(SyslogHostName); + if (SyslogAppName != null) + Layouts.Add(SyslogAppName); + if (SyslogProcessId != null) + Layouts.Add(SyslogProcessId); + if (SyslogMessageId != null) + Layouts.Add(SyslogMessageId); + if (SyslogMessage != null) + Layouts.Add(SyslogMessage); + if (SyslogSeverity != null) + Layouts.Add(SyslogSeverity); + if (StructuredDataId != null) + Layouts.Add(StructuredDataId); + for (int i = 0; i < StructuredDataParams.Count; ++i) + Layouts.Add(StructuredDataParams[i].Layout); + } + + base.InitializeLayout(); + } + + /// + protected override void CloseLayout() + { + ValueFormatter = null; + } + /// protected override string GetFormattedMessage(LogEventInfo logEvent) { @@ -428,33 +474,41 @@ private static char ToAscii(char c) /// private static bool IsAscii(char c) => (uint)c <= '\x007f'; - private static readonly Dictionary _severityMapping = new Dictionary + private static readonly SyslogSeverity[] _severityMapping = new [] { - { LogLevel.Fatal, Layouts.SyslogSeverity.Emergency }, - { LogLevel.Error, Layouts.SyslogSeverity.Error }, - { LogLevel.Warn, Layouts.SyslogSeverity.Warning }, - { LogLevel.Info, Layouts.SyslogSeverity.Informational }, - { LogLevel.Debug, Layouts.SyslogSeverity.Debug }, - { LogLevel.Trace, Layouts.SyslogSeverity.Debug } + NLog.Layouts.SyslogSeverity.Debug, + NLog.Layouts.SyslogSeverity.Debug, + NLog.Layouts.SyslogSeverity.Informational, + NLog.Layouts.SyslogSeverity.Warning, + NLog.Layouts.SyslogSeverity.Error, + NLog.Layouts.SyslogSeverity.Emergency, + NLog.Layouts.SyslogSeverity.Emergency, }; private static SyslogSeverity ResolveSyslogSeverity(LogLevel logLevel) { - return _severityMapping[logLevel]; + try + { + return _severityMapping[logLevel.Ordinal]; + } + catch (IndexOutOfRangeException) + { + return NLog.Layouts.SyslogSeverity.Emergency; + } } private static Dictionary ResolveFacilityMapper(SyslogFacility facility) { return (new SyslogSeverity[] { - Layouts.SyslogSeverity.Emergency, - Layouts.SyslogSeverity.Alert, - Layouts.SyslogSeverity.Critical, - Layouts.SyslogSeverity.Error, - Layouts.SyslogSeverity.Warning, - Layouts.SyslogSeverity.Notice, - Layouts.SyslogSeverity.Informational, - Layouts.SyslogSeverity.Debug + NLog.Layouts.SyslogSeverity.Emergency, + NLog.Layouts.SyslogSeverity.Alert, + NLog.Layouts.SyslogSeverity.Critical, + NLog.Layouts.SyslogSeverity.Error, + NLog.Layouts.SyslogSeverity.Warning, + NLog.Layouts.SyslogSeverity.Notice, + NLog.Layouts.SyslogSeverity.Informational, + NLog.Layouts.SyslogSeverity.Debug }).ToDictionary(s => s, s => ResolvePriHeader(facility, s)); } diff --git a/src/NLog.Targets.Network/Targets/GelfTarget.cs b/src/NLog.Targets.Network/Targets/GelfTarget.cs new file mode 100644 index 0000000000..04b28c392c --- /dev/null +++ b/src/NLog.Targets.Network/Targets/GelfTarget.cs @@ -0,0 +1,102 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets +{ + using System.Collections.Generic; + using NLog.Config; + using NLog.Layouts; + + /// + /// Sends log messages to GrayLog server using either TCP or UDP with GELF-format + /// + [Target("Gelf")] + public class GelfTarget : NetworkTarget + { + private readonly GelfLayout _gelfLayout = new GelfLayout(); + + /// + public Layout GelfHostName { get => _gelfLayout.GelfHostName; set => _gelfLayout.GelfHostName = value; } + + /// + public Layout GelfShortMessage { get => _gelfLayout.GelfShortMessage; set => _gelfLayout.GelfShortMessage = value; } + + /// + public Layout GelfFullMessage { get => _gelfLayout.GelfFullMessage; set => _gelfLayout.GelfFullMessage = value; } + + /// + public Layout GelfFacility { get => _gelfLayout.GelfFacility; set => _gelfLayout.GelfFacility = value; } + + /// + [ArrayParameter(typeof(TargetPropertyWithContext), "GelfField")] + public List GelfFields { get => _gelfLayout.GelfFields; } + + /// + public bool IncludeEventProperties { get => _gelfLayout.IncludeEventProperties; set => _gelfLayout.IncludeEventProperties = value; } + + /// + public bool IncludeScopeProperties { get => _gelfLayout.IncludeScopeProperties; set => _gelfLayout.IncludeScopeProperties = value; } + + /// + public bool ExcludeEmptyProperties { get => _gelfLayout.ExcludeEmptyProperties; set => _gelfLayout.ExcludeEmptyProperties = value; } + + /// + public ISet ExcludeProperties { get => _gelfLayout.ExcludeProperties; } + + /// + public ISet IncludeProperties { get => _gelfLayout.IncludeProperties; } + + /// + public override Layout Layout + { + get + { + return _gelfLayout; + } + set + { + // Fixed GelfLayout + } + } + + /// + /// Initializes a new instance of the class. + /// + public GelfTarget() + { + LineEnding = LineEndingMode.Null; // Graylog Server oftens uses NUL-byte as message-delimiter for TCP (but prevents using compression) + NewLine = false; // LineEnding must be explicit enabled when using TCP + Layout = _gelfLayout; + } + } +} diff --git a/src/NLog.Targets.Network/Targets/NetworkTarget.cs b/src/NLog.Targets.Network/Targets/NetworkTarget.cs index 73298fe0b3..135a308bce 100644 --- a/src/NLog.Targets.Network/Targets/NetworkTarget.cs +++ b/src/NLog.Targets.Network/Targets/NetworkTarget.cs @@ -149,7 +149,7 @@ public LineEndingMode LineEnding set { _lineEnding = value; - NewLine = value != null; + NewLine = value?.NewLineCharacters?.Length > 0; } } private LineEndingMode _lineEnding = LineEndingMode.CRLF; diff --git a/src/NLog.Targets.Network/Targets/SyslogTarget.cs b/src/NLog.Targets.Network/Targets/SyslogTarget.cs index 498fec5e0b..4b213978a3 100644 --- a/src/NLog.Targets.Network/Targets/SyslogTarget.cs +++ b/src/NLog.Targets.Network/Targets/SyslogTarget.cs @@ -1,10 +1,43 @@ -using System.Collections.Generic; -using System.Text; -using NLog.Config; -using NLog.Layouts; +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// namespace NLog.Targets { + using System.Collections.Generic; + using System.Text; + using NLog.Config; + using NLog.Layouts; + /// /// Sends log messages to Syslog server using either TCP or UDP with format Rfc3164 or Rfc5424 /// @@ -61,7 +94,7 @@ public class SyslogTarget : NetworkTarget public List StructuredDataParams => _syslogLayout.StructuredDataParams; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public SyslogTarget() { @@ -75,11 +108,14 @@ protected override byte[] GetHeaderToWrite(LogEventInfo logEvent, string address if (LineEnding?.NewLineCharacters?.Length > 0) return null; - if (address?.StartsWith("udp", System.StringComparison.OrdinalIgnoreCase) == true) - return null; + if (address?.StartsWith("tcp", System.StringComparison.OrdinalIgnoreCase) == true) + { + var octetCount = payload.Length; + return Encoding.ASCII.GetBytes($"{octetCount} "); + } - var octetCount = payload.Length; - return Encoding.ASCII.GetBytes($"{octetCount} "); + // Skip octet framing for UDP protocols by returning null + return null; } /// diff --git a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs index 2aafa7b9bf..7efd6c3750 100644 --- a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs @@ -107,10 +107,10 @@ public string Separator #endif /// - /// Enables capture of ScopeContext-properties from active thread context + /// Disables to capture ScopeContext-properties from active thread context /// - public LayoutRenderer FixScopeContext => IncludeScopeProperties ? _fixScopeContext : null; - private static readonly LayoutRenderer _fixScopeContext = new ScopeContextPropertyLayoutRenderer() { Item = string.Empty }; + public LayoutRenderer DisableThreadAgnostic => IncludeScopeProperties ? _disableThreadAgnostic : null; + private static readonly LayoutRenderer _disableThreadAgnostic = new ScopeContextPropertyLayoutRenderer() { Item = string.Empty }; /// /// Gets or sets how key/value pairs will be formatted. diff --git a/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs index fe2ea0b106..4325b4ac74 100644 --- a/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs @@ -69,6 +69,7 @@ public sealed class JsonEncodeLayoutRendererWrapper : WrapperLayoutRendererBase /// If not set explicitly then the value of the parent will be used as default. /// /// + [Obsolete("Marked obsolete with NLog 5.5. Should never escape forward slash")] public bool EscapeForwardSlash { get; set; } /// @@ -77,7 +78,7 @@ protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBui Inner.Render(logEvent, builder); if (JsonEncode && builder.Length > orgLength) { - Targets.DefaultJsonSerializer.PerformJsonEscapeWhenNeeded(builder, orgLength, EscapeUnicode, EscapeForwardSlash); + Targets.DefaultJsonSerializer.PerformJsonEscapeWhenNeeded(builder, orgLength, EscapeUnicode); } } diff --git a/src/NLog/Layouts/JSON/JsonArrayLayout.cs b/src/NLog/Layouts/JSON/JsonArrayLayout.cs index 761c1be66e..7e2bef9de7 100644 --- a/src/NLog/Layouts/JSON/JsonArrayLayout.cs +++ b/src/NLog/Layouts/JSON/JsonArrayLayout.cs @@ -162,7 +162,7 @@ private bool RenderLayoutJsonValue(LogEventInfo logEvent, Layout layout, StringB layout.Render(logEvent, sb); if (beforeValueLength != sb.Length) { - NLog.Targets.DefaultJsonSerializer.PerformJsonEscapeWhenNeeded(sb, beforeValueLength, true, false); + NLog.Targets.DefaultJsonSerializer.PerformJsonEscapeWhenNeeded(sb, beforeValueLength, true); sb.Append('"'); } } diff --git a/src/NLog/Layouts/JSON/JsonAttribute.cs b/src/NLog/Layouts/JSON/JsonAttribute.cs index 86f39ac453..80be45028d 100644 --- a/src/NLog/Layouts/JSON/JsonAttribute.cs +++ b/src/NLog/Layouts/JSON/JsonAttribute.cs @@ -88,7 +88,7 @@ public string Name else { var builder = new System.Text.StringBuilder(); - Targets.DefaultJsonSerializer.AppendStringEscape(builder, value, false, false); + Targets.DefaultJsonSerializer.AppendStringEscape(builder, value, false); _name = builder.ToString(); } } @@ -133,8 +133,8 @@ public string Name /// If not set explicitly then the value of the parent will be used as default. /// /// - public bool EscapeForwardSlash { get => EscapeForwardSlashInternal ?? false; set => EscapeForwardSlashInternal = value; } - internal bool? EscapeForwardSlashInternal { get; private set; } + [Obsolete("Marked obsolete with NLog 5.5. Should never escape forward slash")] + public bool EscapeForwardSlash { get; set; } /// /// Gets or sets whether an attribute with empty value should be included in the output @@ -170,7 +170,7 @@ internal bool RenderAppendJsonValue(LogEventInfo logEvent, IJsonConverter jsonCo if (Encode) { - Targets.DefaultJsonSerializer.PerformJsonEscapeWhenNeeded(builder, orgLength, EscapeUnicode, EscapeForwardSlash); + Targets.DefaultJsonSerializer.PerformJsonEscapeWhenNeeded(builder, orgLength, EscapeUnicode); builder.Append('"'); } } diff --git a/src/NLog/Layouts/JSON/JsonLayout.cs b/src/NLog/Layouts/JSON/JsonLayout.cs index f64ff3be8a..18da8fbb0c 100644 --- a/src/NLog/Layouts/JSON/JsonLayout.cs +++ b/src/NLog/Layouts/JSON/JsonLayout.cs @@ -56,7 +56,7 @@ public class JsonLayout : Layout private LimitRecursionJsonConvert JsonConverter { - get => _jsonConverter ?? (_jsonConverter = new LimitRecursionJsonConvert(MaxRecursionLimit, EscapeForwardSlash, ResolveService())); + get => _jsonConverter ?? (_jsonConverter = new LimitRecursionJsonConvert(MaxRecursionLimit, ResolveService())); set => _jsonConverter = value; } private LimitRecursionJsonConvert _jsonConverter; @@ -73,11 +73,11 @@ private sealed class LimitRecursionJsonConvert : IJsonConverter private readonly Targets.DefaultJsonSerializer _serializer; private readonly Targets.JsonSerializeOptions _serializerOptions; - public LimitRecursionJsonConvert(int maxRecursionLimit, bool escapeForwardSlash, IJsonConverter converter) + public LimitRecursionJsonConvert(int maxRecursionLimit, IJsonConverter converter) { _converter = converter; _serializer = converter as Targets.DefaultJsonSerializer; - _serializerOptions = new Targets.JsonSerializeOptions() { MaxRecursionLimit = Math.Max(0, maxRecursionLimit), EscapeForwardSlash = escapeForwardSlash }; + _serializerOptions = new Targets.JsonSerializeOptions() { MaxRecursionLimit = Math.Max(0, maxRecursionLimit) }; } public bool SerializeObject(object value, StringBuilder builder) @@ -107,7 +107,8 @@ public JsonLayout() /// /// [ArrayParameter(typeof(JsonAttribute), "attribute")] - public IList Attributes { get; } = new List(); + public IList Attributes => _attributes; + private readonly List _attributes = new List(); /// /// Gets or sets the option to suppress the extra spaces in the output json @@ -232,12 +233,8 @@ public bool IndentJson /// If not set explicitly then the value of the parent will be used as default. /// /// - public bool EscapeForwardSlash - { - get => _escapeForwardSlashInternal ?? false; - set => _escapeForwardSlashInternal = value; - } - private bool? _escapeForwardSlashInternal; + [Obsolete("Marked obsolete with NLog 5.5. Should never escape forward slash")] + public bool EscapeForwardSlash { get; set; } /// protected override void InitializeLayout() @@ -255,20 +252,9 @@ protected override void InitializeLayout() _precalculateLayouts = (IncludeScopeProperties || IncludeEventProperties) ? null : ResolveLayoutPrecalculation(Attributes.Select(atr => atr.Layout)); - if (_escapeForwardSlashInternal.HasValue && Attributes?.Count > 0) - { - foreach (var attribute in Attributes) - { - if (!attribute.EscapeForwardSlashInternal.HasValue) - { - attribute.EscapeForwardSlash = _escapeForwardSlashInternal.Value; - } - } - } - - if (Attributes?.Count > 0) + if (_attributes.Count > 0) { - foreach (var attribute in Attributes) + foreach (var attribute in _attributes) { if (!attribute.IncludeEmptyValue && !attribute.Encode && attribute.Layout is JsonLayout jsonLayout && !jsonLayout._renderEmptyObject.HasValue) { @@ -313,13 +299,10 @@ private void RenderJsonFormattedMessage(LogEventInfo logEvent, StringBuilder sb) { int orgLength = sb.Length; - //Memory profiling pointed out that using a foreach-loop was allocating - //an Enumerator. Switching to a for-loop avoids the memory allocation. - for (int i = 0; i < Attributes.Count; i++) + foreach (var attribute in _attributes) { - var attrib = Attributes[i]; int beforeAttribLength = sb.Length; - if (!RenderAppendJsonPropertyValue(attrib, logEvent, sb, beforeAttribLength == orgLength)) + if (!RenderAppendJsonPropertyValue(attribute, logEvent, sb, beforeAttribLength == orgLength)) { sb.Length = beforeAttribLength; } @@ -387,7 +370,7 @@ private void BeginJsonProperty(StringBuilder sb, string propName, bool beginJson sb.Append(beginJsonMessage ? _beginJsonMessage : _beginJsonPropertyName); if (ensureStringEscape) - Targets.DefaultJsonSerializer.AppendStringEscape(sb, propName, false, false); + Targets.DefaultJsonSerializer.AppendStringEscape(sb, propName, false); else sb.Append(propName); @@ -464,7 +447,7 @@ private void AppendJsonPropertyValue(string propName, object propertyValue, stri // Overrides MaxRecursionLimit as message-template tells us it is unsafe int originalStart = sb.Length; ValueFormatter.FormatValue(propertyValue, format, captureType, formatProvider, sb); - PerformJsonEscapeIfNeeded(sb, originalStart, EscapeForwardSlash); + PerformJsonEscapeIfNeeded(sb, originalStart); } else { @@ -472,7 +455,7 @@ private void AppendJsonPropertyValue(string propName, object propertyValue, stri } } - private static void PerformJsonEscapeIfNeeded(StringBuilder sb, int valueStart, bool escapeForwardSlash) + private static void PerformJsonEscapeIfNeeded(StringBuilder sb, int valueStart) { var builderLength = sb.Length; if (builderLength - valueStart <= 2) @@ -480,12 +463,12 @@ private static void PerformJsonEscapeIfNeeded(StringBuilder sb, int valueStart, for (int i = valueStart + 1; i < builderLength - 1; ++i) { - if (Targets.DefaultJsonSerializer.RequiresJsonEscape(sb[i], false, escapeForwardSlash)) + if (Targets.DefaultJsonSerializer.RequiresJsonEscape(sb[i], false)) { var jsonEscape = sb.ToString(valueStart + 1, sb.Length - valueStart - 2); sb.Length = valueStart; sb.Append('"'); - Targets.DefaultJsonSerializer.AppendStringEscape(sb, jsonEscape, false, escapeForwardSlash); + Targets.DefaultJsonSerializer.AppendStringEscape(sb, jsonEscape, false); sb.Append('"'); break; } diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index f3eac51ca4..01e07b2239 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -391,7 +391,7 @@ public async System.Threading.Tasks.ValueTask DisposeAsync() try { - var disposeTimeout = System.Threading.Tasks.Task.Delay(DefaultFlushTimeout).ContinueWith(prevTask => throw new OperationCanceledException("NLog LogFactory Dispose Timeout")); + var disposeTimeout = System.Threading.Tasks.Task.Delay(DefaultFlushTimeout).ContinueWith(prevTask => throw new System.Threading.Tasks.TaskCanceledException("NLog LogFactory Dispose Timeout")); await System.Threading.Tasks.TaskExtensions.Unwrap(System.Threading.Tasks.Task.WhenAny(System.Threading.Tasks.Task.Run(() => DisposeInternal()), disposeTimeout)).ConfigureAwait(false); GC.SuppressFinalize(this); } @@ -707,7 +707,7 @@ public System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken } var timeout = cancellationToken.CanBeCanceled ? -1 : (int)DefaultFlushTimeout.TotalMilliseconds; - var flushTimeout = System.Threading.Tasks.Task.Delay(timeout, cancellationToken).ContinueWith(prevTask => throw new OperationCanceledException("NLog LogFactory Flush Timeout")); + var flushTimeout = System.Threading.Tasks.Task.Delay(timeout, cancellationToken).ContinueWith(prevTask => throw new System.Threading.Tasks.TaskCanceledException("NLog LogFactory Flush Timeout")); flushTimeout.ContinueWith(prevTask => { flushTimeoutHandler.Invoke(null); flushCompleted.TrySetCanceled(); }); return System.Threading.Tasks.TaskExtensions.Unwrap(System.Threading.Tasks.Task.WhenAny(flushCompleted.Task, flushTimeout)); } diff --git a/src/NLog/Targets/DefaultJsonSerializer.cs b/src/NLog/Targets/DefaultJsonSerializer.cs index a52f5e0fc5..4b37b7d5d7 100644 --- a/src/NLog/Targets/DefaultJsonSerializer.cs +++ b/src/NLog/Targets/DefaultJsonSerializer.cs @@ -97,7 +97,7 @@ public string SerializeObject(object value, JsonSerializeOptions options) { foreach (var chr in str) { - if (RequiresJsonEscape(chr, options.EscapeUnicode, options.EscapeForwardSlash)) + if (RequiresJsonEscape(chr, options.EscapeUnicode)) { StringBuilder sb = new StringBuilder(str.Length + 4); sb.Append('"'); @@ -266,7 +266,7 @@ private bool SerializeSimpleObjectValue(object value, StringBuilder destination, #else int startPos = destination.Length; destination.AppendFormat(CultureInfo.InvariantCulture, "{0}", formattable); // Support ISpanFormattable - PerformJsonEscapeWhenNeeded(destination, startPos, options.EscapeUnicode, options.EscapeForwardSlash); + PerformJsonEscapeWhenNeeded(destination, startPos, options.EscapeUnicode); #endif destination.Append('"'); return true; @@ -575,7 +575,7 @@ private static bool IsNumericTypeCode(TypeCode objTypeCode, bool includeDecimals /// JSON escaped string private static void AppendStringEscape(StringBuilder destination, string text, JsonSerializeOptions options) { - AppendStringEscape(destination, text, options.EscapeUnicode, options.EscapeForwardSlash); + AppendStringEscape(destination, text, options.EscapeUnicode); } /// @@ -584,9 +584,9 @@ private static void AppendStringEscape(StringBuilder destination, string text, J /// Destination Builder /// Input string /// Should non-ASCII characters be encoded - /// + /// /// JSON escaped string - internal static void AppendStringEscape(StringBuilder destination, string text, bool escapeUnicode, bool escapeForwardSlash) + internal static void AppendStringEscape(StringBuilder destination, string text, bool escapeUnicode) { if (string.IsNullOrEmpty(text)) return; @@ -594,9 +594,9 @@ internal static void AppendStringEscape(StringBuilder destination, string text, int i = 0; foreach (var chr in text) { - if (RequiresJsonEscape(chr, escapeUnicode, escapeForwardSlash)) + if (RequiresJsonEscape(chr, escapeUnicode)) { - AppendStringEscape(destination, text, escapeUnicode, escapeForwardSlash, i); + AppendStringEscape(destination, text, escapeUnicode, i); return; } @@ -606,14 +606,14 @@ internal static void AppendStringEscape(StringBuilder destination, string text, destination.Append(text); } - private static void AppendStringEscape(StringBuilder destination, string text, bool escapeUnicode, bool escapeForwardSlash, int startIndex) + private static void AppendStringEscape(StringBuilder destination, string text, bool escapeUnicode, int startIndex) { destination.Append(text, 0, startIndex); for (int i = startIndex; i < text.Length; ++i) { char ch = text[i]; - if (!RequiresJsonEscape(ch, escapeUnicode, escapeForwardSlash)) + if (!RequiresJsonEscape(ch, escapeUnicode)) { destination.Append(ch); continue; @@ -633,17 +633,6 @@ private static void AppendStringEscape(StringBuilder destination, string text, b destination.Append("\\b"); break; - case '/': - if (escapeForwardSlash) - { - destination.Append("\\/"); - } - else - { - destination.Append(ch); - } - break; - case '\r': destination.Append("\\r"); break; @@ -667,29 +656,27 @@ private static void AppendStringEscape(StringBuilder destination, string text, b } } - internal static void PerformJsonEscapeWhenNeeded(StringBuilder builder, int startPos, bool escapeUnicode, bool escapeForwardSlash) + internal static void PerformJsonEscapeWhenNeeded(StringBuilder builder, int startPos, bool escapeUnicode) { var builderLength = builder.Length; for (int i = startPos; i < builderLength; ++i) { - if (RequiresJsonEscape(builder[i], escapeUnicode, escapeForwardSlash)) + if (RequiresJsonEscape(builder[i], escapeUnicode)) { var str = builder.ToString(startPos, builder.Length - startPos); builder.Length = startPos; - Targets.DefaultJsonSerializer.AppendStringEscape(builder, str, escapeUnicode, escapeForwardSlash); + Targets.DefaultJsonSerializer.AppendStringEscape(builder, str, escapeUnicode); break; } } } - internal static bool RequiresJsonEscape(char ch, bool escapeUnicode, bool escapeForwardSlash) + internal static bool RequiresJsonEscape(char ch, bool escapeUnicode) { if (ch < 32) return true; if (ch > 127) return escapeUnicode; - if (ch == '/') - return escapeForwardSlash; return ch == '"' || ch == '\\'; } diff --git a/src/NLog/Targets/JsonSerializeOptions.cs b/src/NLog/Targets/JsonSerializeOptions.cs index 882f13b7e7..f5c3d46775 100644 --- a/src/NLog/Targets/JsonSerializeOptions.cs +++ b/src/NLog/Targets/JsonSerializeOptions.cs @@ -44,21 +44,21 @@ public class JsonSerializeOptions /// /// Add quotes around object keys? /// - [Obsolete("Marked obsolete on NLog 5.0.")] + [Obsolete("Marked obsolete with NLog 5.0.")] [EditorBrowsable(EditorBrowsableState.Never)] public bool QuoteKeys { get; set; } = true; /// /// Format provider for value /// - [Obsolete("Marked obsolete on NLog 5.0. Should always be InvariantCulture.")] + [Obsolete("Marked obsolete with NLog 5.0. Should always be InvariantCulture.")] [EditorBrowsable(EditorBrowsableState.Never)] public IFormatProvider FormatProvider { get; set; } /// /// Format string for value /// - [Obsolete("Marked obsolete on NLog 5.0. Should always be InvariantCulture.")] + [Obsolete("Marked obsolete with NLog 5.0. Should always be InvariantCulture.")] [EditorBrowsable(EditorBrowsableState.Never)] public string Format { get; set; } @@ -70,6 +70,7 @@ public class JsonSerializeOptions /// /// Should forward slashes be escaped? If true, / will be converted to \/ /// + [Obsolete("Marked obsolete with NLog 5.5. Should never escape forward slash")] public bool EscapeForwardSlash { get; set; } /// diff --git a/tests/NLog.Targets.Network.Tests/GelfLayoutTests.cs b/tests/NLog.Targets.Network.Tests/GelfLayoutTests.cs new file mode 100644 index 0000000000..6c3916c6ef --- /dev/null +++ b/tests/NLog.Targets.Network.Tests/GelfLayoutTests.cs @@ -0,0 +1,315 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets.Network +{ + using System; + using NLog.Layouts; + using Xunit; + + public class GelfLayoutTests + { + private static readonly string HostName = ResolveHostname(); + + public GelfLayoutTests() + { + LogManager.ThrowExceptions = true; + LogManager.Setup().SetupExtensions(ext => + { + ext.RegisterLayout(); + }); + } + + [Fact] + public void CanRenderGelf() + { + var gelfLayout = new GelfLayout(); + + var memTarget = new NLog.Targets.MemoryTarget() { Layout = gelfLayout }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + { + var loggerName = "TestLogger"; + var dateTime = DateTime.Now; + var message = "hello, gelf :)"; + var logLevel = LogLevel.Info; + + var logEvent = new LogEventInfo + { + LoggerName = loggerName, + Level = logLevel, + Message = message, + TimeStamp = dateTime, + }; + + logFactory.GetLogger(loggerName).Log(logEvent); + Assert.Single(memTarget.Logs); + var renderedGelf = memTarget.Logs[0]; + + var expectedDateTime = GelfLayout.ToUnixTimeStamp(dateTime); + var expectedGelf = string.Format(System.Globalization.CultureInfo.InvariantCulture, + "{{"+ "\"version\":\"1.1\"," + + "\"host\":\"{0}\"," + + "\"short_message\":\"{1}\"," + + "\"timestamp\":{2}," + + "\"level\":{3}," + + "\"_logLevel\":\"{4}\"," + + "\"_logger\":\"{5}\"" + + "}}", + HostName, + message, + expectedDateTime, + (int)GelfLayout.ToSyslogSeverity(logLevel), + logLevel, + loggerName); + + Assert.Equal(expectedGelf, renderedGelf); + } + } + + [Fact] + public void CanRenderGelfFacility() + { + var gelfLayout = new GelfLayout(); + gelfLayout.GelfFields.Add(new TargetPropertyWithContext("_LoggerName", "${logger}")); + + var memTarget = new NLog.Targets.MemoryTarget() { Layout = gelfLayout }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + { + var loggerName = "TestLogger"; + var facility = "TestFacility"; + var dateTime = DateTime.Now; + var message = "hello, gelf :)"; + var logLevel = LogLevel.Info; + + gelfLayout.GelfFacility = facility; + + var logEvent = new LogEventInfo + { + LoggerName = loggerName, + Level = logLevel, + Message = message, + TimeStamp = dateTime, + }; + + logFactory.GetLogger(loggerName).Log(logEvent); + Assert.Single(memTarget.Logs); + var renderedGelf = memTarget.Logs[0]; + + var expectedDateTime = GelfLayout.ToUnixTimeStamp(dateTime); + var expectedGelf = string.Format(System.Globalization.CultureInfo.InvariantCulture, + "{{"+ "\"version\":\"1.1\"," + + "\"host\":\"{0}\"," + + "\"short_message\":\"{1}\"," + + "\"timestamp\":{2}," + + "\"level\":{3}," + + "\"facility\":\"{4}\"," + + "\"file\":\"TestLogger\"," + + "\"_LoggerName\":\"{5}\"" + + "}}", + HostName, + message, + expectedDateTime, + (int)GelfLayout.ToSyslogSeverity(logLevel), + facility, + loggerName); + + Assert.Equal(expectedGelf, renderedGelf); + } + } + + [Fact] + public void CanRenderGelfAdditionalFieldsCustomMessage() + { + var gelfLayout = new GelfLayout(); + gelfLayout.GelfFields.Add(new TargetPropertyWithContext("ThreadId", "${threadid}") { PropertyType = typeof(int) }); + gelfLayout.GelfFullMessage = "${hostname}|${message}"; + gelfLayout.GelfShortMessage = "short|${message}"; + + var memTarget = new NLog.Targets.MemoryTarget() { Layout = gelfLayout }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget).WithAsync()).LogFactory) + { + var loggerName = "TestLogger"; + var dateTime = DateTime.Now; + var message = "hello, gelf :)"; + var logLevel = LogLevel.Info; + + var logEvent = new LogEventInfo + { + LoggerName = loggerName, + Level = logLevel, + Message = message, + TimeStamp = dateTime, + }; + + logFactory.GetLogger(loggerName).Log(logEvent); + logFactory.Flush(); + Assert.Single(memTarget.Logs); + var renderedGelf = memTarget.Logs[0]; + + int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId; + var expectedDateTime = GelfLayout.ToUnixTimeStamp(dateTime); + var expectedGelf = string.Format(System.Globalization.CultureInfo.InvariantCulture, + "{{" + "\"version\":\"1.1\"," + + "\"host\":\"{0}\"," + + "\"short_message\":\"short|{1}\"," + + "\"full_message\":\"{0}|{1}\"," + + "\"timestamp\":{2}," + + "\"level\":{3}," + + "\"_ThreadId\":{4}" + + "}}", + HostName, + message, + expectedDateTime, + (int)GelfLayout.ToSyslogSeverity(logLevel), + threadId); + Assert.Equal(expectedGelf, renderedGelf); + } + } + + [Fact] + public void CanRenderEventProperties() + { + var gelfLayout = new GelfLayout(); + gelfLayout.GelfFields.Add(new TargetPropertyWithContext(" ThreadId ", "${threadid}") { PropertyType = typeof(int) }); + gelfLayout.IncludeEventProperties = true; + gelfLayout.IncludeProperties.Add("RequestId"); + + var memTarget = new NLog.Targets.MemoryTarget() { Layout = gelfLayout }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget).WithAsync()).LogFactory) + { + var loggerName = "TestLogger"; + var dateTime = DateTime.Now; + var message = "hello, {world} from {RequestId} :)"; + var logLevel = LogLevel.Info; + var requestId = Guid.NewGuid(); + + var logEvent = new LogEventInfo + { + TimeStamp = dateTime, + LoggerName = loggerName, + Level = logLevel, + Message = message, + Parameters = new object[] { "Gelf", requestId }, + }; + + logFactory.GetLogger(loggerName).Log(logEvent); + logFactory.Flush(); + Assert.Single(memTarget.Logs); + var renderedGelf = memTarget.Logs[0]; + + int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId; + var expectedDateTime = GelfLayout.ToUnixTimeStamp(dateTime); + var expectedGelf = string.Format(System.Globalization.CultureInfo.InvariantCulture, + "{{" + "\"version\":\"1.1\"," + + "\"host\":\"{0}\"," + + "\"short_message\":\"hello, Gelf from {5} :)\"," + + "\"timestamp\":{2}," + + "\"level\":{3}," + + "\"_ThreadId\":{4}," + + "\"_RequestId\":\"{5}\"" + + "}}", + HostName, + message, + expectedDateTime, + (int)GelfLayout.ToSyslogSeverity(logLevel), + threadId, + requestId); + Assert.Equal(expectedGelf, renderedGelf); + } + } + + [Fact] + public void CanRenderScopeContext() + { + var gelfLayout = new GelfLayout(); + gelfLayout.GelfFields.Add(new TargetPropertyWithContext(" ThreadId ", "${threadid}") { PropertyType = typeof(int) }); + gelfLayout.IncludeEventProperties = true; + gelfLayout.IncludeScopeProperties = true; + gelfLayout.ExcludeProperties.Add("World"); + + var memTarget = new NLog.Targets.MemoryTarget() { Layout = gelfLayout }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget).WithAsync()).LogFactory) + { + var loggerName = "TestLogger"; + var dateTime = DateTime.Now; + var message = "hello, {world} :)"; + var logLevel = LogLevel.Info; + + var logEvent = new LogEventInfo + { + TimeStamp = dateTime, + LoggerName = loggerName, + Level = logLevel, + Message = message, + Parameters = new object[] { "Gelf" }, + }; + + var requestId = Guid.NewGuid(); + using (logFactory.GetLogger(loggerName).PushScopeProperty("RequestId", requestId)) + { + logFactory.GetLogger(loggerName).Log(logEvent); + } + logFactory.Flush(); + Assert.Single(memTarget.Logs); + var renderedGelf = memTarget.Logs[0]; + + int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId; + var expectedDateTime = GelfLayout.ToUnixTimeStamp(dateTime); + var expectedGelf = string.Format(System.Globalization.CultureInfo.InvariantCulture, + "{{" + "\"version\":\"1.1\"," + + "\"host\":\"{0}\"," + + "\"short_message\":\"hello, Gelf :)\"," + + "\"timestamp\":{2}," + + "\"level\":{3}," + + "\"_ThreadId\":{4}," + + "\"_RequestId\":\"{5}\"" + + "}}", + HostName, + message, + expectedDateTime, + (int)GelfLayout.ToSyslogSeverity(logLevel), + threadId, + requestId); + Assert.Equal(expectedGelf, renderedGelf); + } + } + + static string ResolveHostname() + { + return Environment.GetEnvironmentVariable("HOSTNAME") + ?? Environment.GetEnvironmentVariable("COMPUTERNAME") + ?? Environment.GetEnvironmentVariable("MACHINENAME") + ?? Environment.MachineName; + } + } +} diff --git a/tests/NLog.Targets.Network.Tests/SyslogLayoutTests.cs b/tests/NLog.Targets.Network.Tests/SyslogLayoutTests.cs index 99273279f6..cf5abf94a6 100644 --- a/tests/NLog.Targets.Network.Tests/SyslogLayoutTests.cs +++ b/tests/NLog.Targets.Network.Tests/SyslogLayoutTests.cs @@ -39,9 +39,9 @@ namespace NLog.Targets.Network public class SyslogLayoutTests { - private static string HostName = ResolveHostname(); - private static string ProcessName = ResolveProcessName(); - private static int ProcessId = ResolveProcessId(); + private static readonly string HostName = ResolveHostname(); + private static readonly string ProcessName = ResolveProcessName(); + private static readonly int ProcessId = ResolveProcessId(); public SyslogLayoutTests() { @@ -66,7 +66,7 @@ public void SyslogLayout_Rfc3164() var timestamp = string.Format(System.Globalization.CultureInfo.InvariantCulture, dateFormat, logEvent.TimeStamp); Assert.Single(memTarget.Logs); - Assert.Equal($"<134>{timestamp} {HostName} {ProcessName}[{ProcessId}]: {logEvent.Message}", memTarget.Logs[0]); + Assert.Equal($"<14>{timestamp} {HostName} {ProcessName}[{ProcessId}]: {logEvent.Message}", memTarget.Logs[0]); } } @@ -84,7 +84,7 @@ public void SyslogLayout_Rfc3164_Newline() var timestamp = string.Format(System.Globalization.CultureInfo.InvariantCulture, dateFormat, logEvent.TimeStamp); Assert.Single(memTarget.Logs); - Assert.Equal($"<134>{timestamp} {HostName} {ProcessName}[{ProcessId}]: Hello World Goodbye World", memTarget.Logs[0]); + Assert.Equal($"<14>{timestamp} {HostName} {ProcessName}[{ProcessId}]: Hello World Goodbye World", memTarget.Logs[0]); } } @@ -126,7 +126,7 @@ public void SyslogLayout_Rfc3164_EscapeNames() var timestamp = string.Format(System.Globalization.CultureInfo.InvariantCulture, dateFormat, logEvent.TimeStamp); Assert.Single(memTarget.Logs); - Assert.Equal($"<134>{timestamp} Hello_World Destroy_World[Goodbye_World]: {logEvent.Message}", memTarget.Logs[0]); + Assert.Equal($"<14>{timestamp} Hello_World Destroy_World[Goodbye_World]: {logEvent.Message}", memTarget.Logs[0]); } } @@ -141,7 +141,7 @@ public void SyslogLayout_Rfc5424() logger.Log(logEvent); Assert.Single(memTarget.Logs); - Assert.Equal($"<134>1 {logEvent.TimeStamp:o} {HostName} {ProcessName} {ProcessId} - {logEvent.Message}", memTarget.Logs[0]); + Assert.Equal($"<14>1 {logEvent.TimeStamp:o} {HostName} {ProcessName} {ProcessId} - {logEvent.Message}", memTarget.Logs[0]); } } @@ -156,7 +156,7 @@ public void SyslogLayout_Rfc5424_Newline() logger.Log(logEvent); Assert.Single(memTarget.Logs); - Assert.Equal($"<134>1 {logEvent.TimeStamp:o} {HostName} {ProcessName} {ProcessId} - Hello World\r\nGoodbye World", memTarget.Logs[0]); + Assert.Equal($"<14>1 {logEvent.TimeStamp:o} {HostName} {ProcessName} {ProcessId} - Hello World\r\nGoodbye World", memTarget.Logs[0]); } } @@ -192,7 +192,7 @@ public void SyslogLayout_Rfc5424_EscapeNames() logger.Log(logEvent); Assert.Single(memTarget.Logs); - Assert.Equal($"<134>1 {logEvent.TimeStamp:o} {HostName} {ProcessName} {ProcessId} - {logEvent.Message}", memTarget.Logs[0]); + Assert.Equal($"<14>1 {logEvent.TimeStamp:o} {HostName} {ProcessName} {ProcessId} - {logEvent.Message}", memTarget.Logs[0]); } } @@ -200,20 +200,22 @@ public void SyslogLayout_Rfc5424_EscapeNames() public void SyslogLayout_Rfc5424_StructuredData() { var syslogLayout = new SyslogLayout() { Rfc5424 = true }; - syslogLayout.StructuredDataParams.Add(new TargetPropertyWithContext(" Hello World ", " Goodbye World ")); + syslogLayout.StructuredDataParams.Add(new TargetPropertyWithContext(" ThreadId ", " ${threadid} ")); syslogLayout.IncludeEventProperties = true; var memTarget = new NLog.Targets.MemoryTarget() { Layout = syslogLayout }; - using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget).WithAsync()).LogFactory) { var guid = Guid.NewGuid(); var logger = logFactory.GetCurrentClassLogger(); var logEvent = LogEventInfo.Create(LogLevel.Info, null, null, "Hello {World} with {CorrelationKey}", new object[] { "\nEarth\n", guid }); logger.Log(logEvent); + logFactory.Flush(); + int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId; Assert.Single(memTarget.Logs); - Assert.Equal($"<134>1 {logEvent.TimeStamp:o} {HostName} {ProcessName} {ProcessId} - [meta World=\" Earth \" CorrelationKey=\"{guid}\" Hello_World=\" Goodbye World \"] {logEvent.FormattedMessage}", memTarget.Logs[0]); + Assert.Equal($"<14>1 {logEvent.TimeStamp:o} {HostName} {ProcessName} {ProcessId} - [meta World=\" Earth \" CorrelationKey=\"{guid}\" ThreadId=\" {threadId} \"] {logEvent.FormattedMessage}", memTarget.Logs[0]); } } diff --git a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/JsonEncodeTests.cs b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/JsonEncodeTests.cs index e95f3217ad..48d5ced4bf 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/JsonEncodeTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/JsonEncodeTests.cs @@ -43,9 +43,9 @@ public class JsonEncodeTests : NLogTestBase public void JsonEncodeTest1() { ScopeContext.PushProperty("foo", " abc\"\n\b\r\f\t/\u1234\u5432\\xyz "); - SimpleLayout l = "${json-encode:${scopeproperty:foo}:escapeForwardSlash=true}"; + SimpleLayout l = "${json-encode:${scopeproperty:foo}}"; - Assert.Equal(@" abc\""\n\b\r\f\t\/\u1234\u5432\\xyz ", l.Render(LogEventInfo.CreateNullEvent())); + Assert.Equal(@" abc\""\n\b\r\f\t/\u1234\u5432\\xyz ", l.Render(LogEventInfo.CreateNullEvent())); } [Fact] diff --git a/tests/NLog.UnitTests/Layouts/JsonLayoutTests.cs b/tests/NLog.UnitTests/Layouts/JsonLayoutTests.cs index 7b2c7f1711..48f99b662f 100644 --- a/tests/NLog.UnitTests/Layouts/JsonLayoutTests.cs +++ b/tests/NLog.UnitTests/Layouts/JsonLayoutTests.cs @@ -1043,7 +1043,7 @@ public void EncodesInvalidCharacters() - + @@ -1052,7 +1052,6 @@ public void EncodesInvalidCharacters() ").LogFactory; - var logger = logFactory.GetLogger("A"); var logEventInfo1 = new LogEventInfo(); @@ -1061,7 +1060,7 @@ public void EncodesInvalidCharacters() logger.Debug(logEventInfo1); - logFactory.AssertDebugLastMessage("{ \"InvalidCharacters\": [\"|\",\"#\",\"{\",\"}\",\"%\",\"&\",\"\\\"\",\"~\",\"+\",\"\\\\\",\"\\/\",\":\",\"*\",\"?\",\"<\",\">\"] }"); + logFactory.AssertDebugLastMessage("{ \"InvalidCharacters\": [\"|\",\"#\",\"{\",\"}\",\"%\",\"&\",\"\\\"\",\"~\",\"+\",\"\\\\\",\"/\",\":\",\"*\",\"?\",\"<\",\">\"] }"); } [Fact] @@ -1106,7 +1105,6 @@ public void EscapeForwardSlashDefaultTest() - @@ -1121,7 +1119,7 @@ public void EscapeForwardSlashDefaultTest() logEventInfo1.Properties.Add("myurl", "http://hello.world.com/"); logger.Debug(logEventInfo1); - logFactory.AssertDebugLastMessage("{ \"myurl1\": \"http://hello.world.com/\", \"myurl2\": \"http:\\/\\/hello.world.com\\/\", \"myurl\": \"http://hello.world.com/\" }"); + logFactory.AssertDebugLastMessage("{ \"myurl1\": \"http://hello.world.com/\", \"myurl\": \"http://hello.world.com/\" }"); } [Fact] diff --git a/tests/NLog.UnitTests/LogFactoryTests.cs b/tests/NLog.UnitTests/LogFactoryTests.cs index 06e0dc66f8..6d7f088f6d 100644 --- a/tests/NLog.UnitTests/LogFactoryTests.cs +++ b/tests/NLog.UnitTests/LogFactoryTests.cs @@ -110,7 +110,7 @@ public void FlushAsync_TargetTimeout_Async() logFactory.Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteToMethodCall((evt, parms) => { System.Threading.Thread.Sleep(5000); })); System.Threading.Tasks.Task.Run(() => logFactory.GetCurrentClassLogger().Info("Sleep")).Wait(50); var task = logFactory.FlushAsync(new CancellationTokenSource(100).Token); - Assert.Throws(() => task.GetAwaiter().GetResult()); + Assert.Throws(() => task.GetAwaiter().GetResult()); } #endif From 53da53a8a4fdb93fec5005071e5bd7200df92ecf Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 5 Apr 2025 23:24:11 +0200 Subject: [PATCH 076/224] Marked JsonLayout EscapeForwardSlash as obsolete and without any effect (#5767) --- .../Wrappers/JsonEncodeLayoutRendererWrapper.cs | 2 ++ src/NLog/Layouts/JSON/JsonAttribute.cs | 4 +++- src/NLog/Layouts/JSON/JsonLayout.cs | 1 + src/NLog/Targets/DefaultJsonSerializer.cs | 3 ++- src/NLog/Targets/JsonSerializeOptions.cs | 1 + 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs index 4325b4ac74..7aa9a757fb 100644 --- a/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs @@ -34,6 +34,7 @@ namespace NLog.LayoutRenderers.Wrappers { using System; + using System.ComponentModel; using System.Text; using NLog.Config; @@ -70,6 +71,7 @@ public sealed class JsonEncodeLayoutRendererWrapper : WrapperLayoutRendererBase /// /// [Obsolete("Marked obsolete with NLog 5.5. Should never escape forward slash")] + [EditorBrowsable(EditorBrowsableState.Never)] public bool EscapeForwardSlash { get; set; } /// diff --git a/src/NLog/Layouts/JSON/JsonAttribute.cs b/src/NLog/Layouts/JSON/JsonAttribute.cs index 80be45028d..c333c3c339 100644 --- a/src/NLog/Layouts/JSON/JsonAttribute.cs +++ b/src/NLog/Layouts/JSON/JsonAttribute.cs @@ -34,6 +34,7 @@ namespace NLog.Layouts { using System; + using System.ComponentModel; using System.Text; using NLog.Config; @@ -133,7 +134,8 @@ public string Name /// If not set explicitly then the value of the parent will be used as default. /// /// - [Obsolete("Marked obsolete with NLog 5.5. Should never escape forward slash")] + [Obsolete("Marked obsolete since forward slash are valid JSON. Marked obsolete with NLog v5.4")] + [EditorBrowsable(EditorBrowsableState.Never)] public bool EscapeForwardSlash { get; set; } /// diff --git a/src/NLog/Layouts/JSON/JsonLayout.cs b/src/NLog/Layouts/JSON/JsonLayout.cs index 18da8fbb0c..29e31d77a9 100644 --- a/src/NLog/Layouts/JSON/JsonLayout.cs +++ b/src/NLog/Layouts/JSON/JsonLayout.cs @@ -234,6 +234,7 @@ public bool IndentJson /// /// [Obsolete("Marked obsolete with NLog 5.5. Should never escape forward slash")] + [EditorBrowsable(EditorBrowsableState.Never)] public bool EscapeForwardSlash { get; set; } /// diff --git a/src/NLog/Targets/DefaultJsonSerializer.cs b/src/NLog/Targets/DefaultJsonSerializer.cs index 4b37b7d5d7..ed15057c28 100644 --- a/src/NLog/Targets/DefaultJsonSerializer.cs +++ b/src/NLog/Targets/DefaultJsonSerializer.cs @@ -95,9 +95,10 @@ public string SerializeObject(object value, JsonSerializeOptions options) } else if (value is string str) { + var escapeUnicode = options.EscapeUnicode; foreach (var chr in str) { - if (RequiresJsonEscape(chr, options.EscapeUnicode)) + if (RequiresJsonEscape(chr, escapeUnicode)) { StringBuilder sb = new StringBuilder(str.Length + 4); sb.Append('"'); diff --git a/src/NLog/Targets/JsonSerializeOptions.cs b/src/NLog/Targets/JsonSerializeOptions.cs index f5c3d46775..4a1b72e59c 100644 --- a/src/NLog/Targets/JsonSerializeOptions.cs +++ b/src/NLog/Targets/JsonSerializeOptions.cs @@ -71,6 +71,7 @@ public class JsonSerializeOptions /// Should forward slashes be escaped? If true, / will be converted to \/ /// [Obsolete("Marked obsolete with NLog 5.5. Should never escape forward slash")] + [EditorBrowsable(EditorBrowsableState.Never)] public bool EscapeForwardSlash { get; set; } /// From 9459c4630266919e5e9d3b38d8cedade84b74c72 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 6 Apr 2025 10:13:29 +0200 Subject: [PATCH 077/224] Updated API examples from SimpleConfigurator to LoggingConfiguration (#5768) --- .../ASPNetBufferingWrapper/Global.asax.cs | 4 +++- .../Configuration API/ASPNetTrace/Global.asax.cs | 5 ++++- .../AsyncWrapper/Wrapping File/Example.cs | 10 ++++++---- .../AutoFlushWrapper/Simple/Example.cs | 9 +++++---- .../BufferingWrapper/Simple/Example.cs | 10 ++++++---- .../Configuration API/Chainsaw/Simple/Example.cs | 7 ++++++- .../ColoredConsole/Row Highlighting/Example.cs | 8 ++++++-- .../ColoredConsole/Simple/Example.cs | 7 +++++-- .../ColoredConsole/Word Highlighting/Example.cs | 8 +++++--- .../Configuration API/Console/Simple/Example.cs | 5 ++++- .../Configuration API/Database/MSSQL/Example.cs | 5 ++++- .../Database/Oracle.Native/Example.cs | 5 ++++- .../Database/Oracle.OleDb/Example.cs | 5 ++++- .../Configuration API/Debug/Simple/Example.cs | 7 ++++--- .../Configuration API/Debugger/Simple/Example.cs | 7 ++++--- .../Configuration API/EventLog/Simple/Example.cs | 6 ++++-- .../FallbackGroup/Simple/Example.cs | 11 +++++------ .../Configuration API/File/Archive1/Example.cs | 9 ++++----- .../Configuration API/File/Archive2/Example.cs | 12 +++++------- .../Configuration API/File/Archive3/Example.cs | 12 +++++------- .../Configuration API/File/Archive4/Example.cs | 12 +++++------- .../Configuration API/File/Asynchronous/Example.cs | 10 +++++++--- .../targets/Configuration API/File/CSV/Example.cs | 8 ++++---- .../Configuration API/File/Multiple/Example.cs | 8 +++++--- .../Configuration API/File/Multiple2/Example.cs | 10 ++++++---- .../Configuration API/File/Simple/Example.cs | 8 +++++--- .../FilteringWrapper/Simple/Example.cs | 8 ++++---- .../MSMQ/Multiple Queues/Example.cs | 8 ++++---- .../Configuration API/MSMQ/Simple/Example.cs | 8 ++++---- .../Configuration API/Mail/Buffered/Example.cs | 8 +++++--- .../Configuration API/Mail/Simple/Example.cs | 6 ++++-- .../Configuration API/Memory/Simple/Example.cs | 7 ++++--- .../Configuration API/MethodCall/Simple/Example.cs | 7 +++++-- .../Configuration API/NLogViewer/Simple/Example.cs | 7 ++++++- .../Configuration API/Network/Simple/Example.cs | 7 ++++++- .../Configuration API/Null/Simple/Example.cs | 7 ++++--- .../OutputDebugString/Simple/Example.cs | 8 ++++---- .../PerfCounter/Simple/Example.cs | 11 +++++------ .../PostFilteringWrapper/Simple/Example.cs | 12 +++++------- .../RandomizeGroup/Simple/Example.cs | 14 ++++++++------ .../RepeatingWrapper/Simple/Example.cs | 8 ++++---- .../RetryingWrapper/Simple/Example.cs | 8 ++++---- .../RoundRobinGroup/Simple/Example.cs | 14 ++++++++------ .../Configuration API/SplitGroup/Simple/Example.cs | 8 ++++---- .../Configuration API/Trace/Simple/Example.cs | 5 ++++- .../Configuration API/WebService/Simple/Example.cs | 7 ++++++- 46 files changed, 223 insertions(+), 153 deletions(-) diff --git a/examples/targets/Configuration API/ASPNetBufferingWrapper/Global.asax.cs b/examples/targets/Configuration API/ASPNetBufferingWrapper/Global.asax.cs index bf4461fe6a..201889f18a 100644 --- a/examples/targets/Configuration API/ASPNetBufferingWrapper/Global.asax.cs +++ b/examples/targets/Configuration API/ASPNetBufferingWrapper/Global.asax.cs @@ -30,7 +30,9 @@ protected void Application_Start(object sender, EventArgs e) rule.Filter = "level >= LogLevel.Debug"; postfilteringTarget.Rules.Add(rule); - SimpleConfigurator.ConfigureForTargetLogging(aspnetBufferingTarget, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(aspnetBufferingTarget); + LogManager.Configuration = nlogConfig; } protected void Application_End(object sender, EventArgs e) diff --git a/examples/targets/Configuration API/ASPNetTrace/Global.asax.cs b/examples/targets/Configuration API/ASPNetTrace/Global.asax.cs index 9432ae14b1..1e54d20ecc 100644 --- a/examples/targets/Configuration API/ASPNetTrace/Global.asax.cs +++ b/examples/targets/Configuration API/ASPNetTrace/Global.asax.cs @@ -2,6 +2,7 @@ using System.Web; using NLog; +using NLog.Config; using NLog.Targets; namespace SomeWebApplication @@ -19,7 +20,9 @@ protected void Application_Start(Object sender, EventArgs e) ASPNetTraceTarget target = new ASPNetTraceTarget(); target.Layout = "${logger} ${message}"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; } } } diff --git a/examples/targets/Configuration API/AsyncWrapper/Wrapping File/Example.cs b/examples/targets/Configuration API/AsyncWrapper/Wrapping File/Example.cs index 678c2a2cad..f3f18ae5c6 100644 --- a/examples/targets/Configuration API/AsyncWrapper/Wrapping File/Example.cs +++ b/examples/targets/Configuration API/AsyncWrapper/Wrapping File/Example.cs @@ -1,7 +1,7 @@ using NLog; +using NLog.Config; using NLog.Targets; using NLog.Targets.Wrappers; -using System.Text; class Example { @@ -11,18 +11,20 @@ static void Main(string[] args) target.Layout = "${longdate} ${logger} ${message}"; target.FileName = "${basedir}/logs/logfile.txt"; target.KeepFileOpen = false; - target.Encoding = Encoding.UTF8; + target.Encoding = System.Text.Encoding.UTF8; AsyncTargetWrapper wrapper = new AsyncTargetWrapper(); wrapper.WrappedTarget = target; wrapper.QueueLimit = 5000; wrapper.OverflowAction = AsyncTargetWrapperOverflowAction.Discard; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(wrapper, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(wrapper); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); - wrapper.Flush(); + LogManager.Flush(); } } diff --git a/examples/targets/Configuration API/AutoFlushWrapper/Simple/Example.cs b/examples/targets/Configuration API/AutoFlushWrapper/Simple/Example.cs index 506736cc53..40c26d7c5b 100644 --- a/examples/targets/Configuration API/AutoFlushWrapper/Simple/Example.cs +++ b/examples/targets/Configuration API/AutoFlushWrapper/Simple/Example.cs @@ -1,9 +1,7 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; using NLog.Targets.Wrappers; -using System.Diagnostics; class Example { @@ -15,7 +13,10 @@ static void Main(string[] args) AutoFlushTargetWrapper target = new AutoFlushTargetWrapper(); target.WrappedTarget = wrappedTarget; target.Condition = "level >= LogLevel.Debug"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/BufferingWrapper/Simple/Example.cs b/examples/targets/Configuration API/BufferingWrapper/Simple/Example.cs index 795c51bc00..2b45989767 100644 --- a/examples/targets/Configuration API/BufferingWrapper/Simple/Example.cs +++ b/examples/targets/Configuration API/BufferingWrapper/Simple/Example.cs @@ -1,9 +1,7 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; using NLog.Targets.Wrappers; -using System.Diagnostics; class Example { @@ -16,9 +14,13 @@ static void Main(string[] args) target.BufferSize = 100; target.WrappedTarget = wrappedTarget; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); + + LogManager.Flush(); } } diff --git a/examples/targets/Configuration API/Chainsaw/Simple/Example.cs b/examples/targets/Configuration API/Chainsaw/Simple/Example.cs index 4bc224534b..4a81ce6f02 100644 --- a/examples/targets/Configuration API/Chainsaw/Simple/Example.cs +++ b/examples/targets/Configuration API/Chainsaw/Simple/Example.cs @@ -1,4 +1,5 @@ using NLog; +using NLog.Config; using NLog.Targets; class Example @@ -8,7 +9,9 @@ static void Main(string[] args) ChainsawTarget target = new ChainsawTarget(); target.Address = "udp://localhost:4000"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Trace("log message 1"); @@ -17,5 +20,7 @@ static void Main(string[] args) logger.Warn("log message 4"); logger.Error("log message 5"); logger.Fatal("log message 6"); + + LogManager.Flush(); } } diff --git a/examples/targets/Configuration API/ColoredConsole/Row Highlighting/Example.cs b/examples/targets/Configuration API/ColoredConsole/Row Highlighting/Example.cs index 944a23a9bf..e43cdc830d 100644 --- a/examples/targets/Configuration API/ColoredConsole/Row Highlighting/Example.cs +++ b/examples/targets/Configuration API/ColoredConsole/Row Highlighting/Example.cs @@ -1,5 +1,6 @@ using NLog; -using NLog.Win32.Targets; +using NLog.Config; +using NLog.Targets; class Example { @@ -21,7 +22,10 @@ static void Main(string[] args) ConsoleOutputColor.Yellow, // foreground color ConsoleOutputColor.DarkBlue) // background color ); - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Trace); + + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; // LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration("ColoredConsoleTargetRowHighlighting.nlog"); diff --git a/examples/targets/Configuration API/ColoredConsole/Simple/Example.cs b/examples/targets/Configuration API/ColoredConsole/Simple/Example.cs index 700cd1233f..a32de11345 100644 --- a/examples/targets/Configuration API/ColoredConsole/Simple/Example.cs +++ b/examples/targets/Configuration API/ColoredConsole/Simple/Example.cs @@ -1,5 +1,6 @@ using NLog; -using NLog.Win32.Targets; +using NLog.Config; +using NLog.Targets; class Example { @@ -8,7 +9,9 @@ static void Main(string[] args) ColoredConsoleTarget target = new ColoredConsoleTarget(); target.Layout = "${date:format=HH\\:MM\\:ss} ${logger} ${message}"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Trace); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Trace("trace log message"); diff --git a/examples/targets/Configuration API/ColoredConsole/Word Highlighting/Example.cs b/examples/targets/Configuration API/ColoredConsole/Word Highlighting/Example.cs index 104136e9b2..b051fe81fa 100644 --- a/examples/targets/Configuration API/ColoredConsole/Word Highlighting/Example.cs +++ b/examples/targets/Configuration API/ColoredConsole/Word Highlighting/Example.cs @@ -1,5 +1,6 @@ using NLog; -using NLog.Win32.Targets; +using NLog.Config; +using NLog.Targets; class Example { @@ -16,8 +17,9 @@ static void Main(string[] args) ConsoleOutputColor.Cyan, ConsoleOutputColor.NoChange)); - - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Trace); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Trace("trace log message abcdefghijklmnopq"); diff --git a/examples/targets/Configuration API/Console/Simple/Example.cs b/examples/targets/Configuration API/Console/Simple/Example.cs index 17e17f867f..94432abe4c 100644 --- a/examples/targets/Configuration API/Console/Simple/Example.cs +++ b/examples/targets/Configuration API/Console/Simple/Example.cs @@ -1,4 +1,5 @@ using NLog; +using NLog.Config; using NLog.Targets; class Example @@ -8,7 +9,9 @@ static void Main(string[] args) ConsoleTarget target = new ConsoleTarget(); target.Layout = "${date:format=HH\\:MM\\:ss} ${logger} ${message}"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/Database/MSSQL/Example.cs b/examples/targets/Configuration API/Database/MSSQL/Example.cs index b208c86890..535168d2d3 100644 --- a/examples/targets/Configuration API/Database/MSSQL/Example.cs +++ b/examples/targets/Configuration API/Database/MSSQL/Example.cs @@ -1,4 +1,5 @@ using NLog; +using NLog.Config; using NLog.Targets; class Example @@ -35,7 +36,9 @@ static void Main(string[] args) param.Layout = "${message}"; target.Parameters.Add(param); - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/Database/Oracle.Native/Example.cs b/examples/targets/Configuration API/Database/Oracle.Native/Example.cs index 005e2d456d..b8f68e4582 100644 --- a/examples/targets/Configuration API/Database/Oracle.Native/Example.cs +++ b/examples/targets/Configuration API/Database/Oracle.Native/Example.cs @@ -1,4 +1,5 @@ using NLog; +using NLog.Config; using NLog.Targets; class Example @@ -20,7 +21,9 @@ static void Main(string[] args) target.Parameters.Add(new DatabaseParameterInfo("CALLSITE", "${callsite:filename=true}")); target.Parameters.Add(new DatabaseParameterInfo("MESSAGE", "${message}")); - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/Database/Oracle.OleDb/Example.cs b/examples/targets/Configuration API/Database/Oracle.OleDb/Example.cs index bbb0c4b301..1de597b641 100644 --- a/examples/targets/Configuration API/Database/Oracle.OleDb/Example.cs +++ b/examples/targets/Configuration API/Database/Oracle.OleDb/Example.cs @@ -1,4 +1,5 @@ using NLog; +using NLog.Config; using NLog.Targets; class Example @@ -18,7 +19,9 @@ static void Main(string[] args) target.Parameters.Add(new DatabaseParameterInfo("CALLSITE", "${callsite:filename=true}")); target.Parameters.Add(new DatabaseParameterInfo("MESSAGE", "${message}")); - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/Debug/Simple/Example.cs b/examples/targets/Configuration API/Debug/Simple/Example.cs index cf7408229a..7eb2771069 100644 --- a/examples/targets/Configuration API/Debug/Simple/Example.cs +++ b/examples/targets/Configuration API/Debug/Simple/Example.cs @@ -1,6 +1,5 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; class Example @@ -10,7 +9,9 @@ static void Main(string[] args) DebugTarget target = new DebugTarget(); target.Layout = "${message}"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/Debugger/Simple/Example.cs b/examples/targets/Configuration API/Debugger/Simple/Example.cs index 3141094cf7..365b16c1ac 100644 --- a/examples/targets/Configuration API/Debugger/Simple/Example.cs +++ b/examples/targets/Configuration API/Debugger/Simple/Example.cs @@ -1,6 +1,5 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; class Example @@ -10,7 +9,9 @@ static void Main(string[] args) DebuggerTarget target = new DebuggerTarget(); target.Layout = "${message}"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/EventLog/Simple/Example.cs b/examples/targets/Configuration API/EventLog/Simple/Example.cs index 9eef6484bd..88c987c400 100644 --- a/examples/targets/Configuration API/EventLog/Simple/Example.cs +++ b/examples/targets/Configuration API/EventLog/Simple/Example.cs @@ -1,6 +1,6 @@ using NLog; +using NLog.Config; using NLog.Targets; -using NLog.Win32.Targets; class Example { @@ -11,7 +11,9 @@ static void Main(string[] args) target.Log = "Application"; target.Layout = "${logger}: ${message} ${exception}"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/FallbackGroup/Simple/Example.cs b/examples/targets/Configuration API/FallbackGroup/Simple/Example.cs index 7b3dd3f513..08c4412202 100644 --- a/examples/targets/Configuration API/FallbackGroup/Simple/Example.cs +++ b/examples/targets/Configuration API/FallbackGroup/Simple/Example.cs @@ -1,9 +1,7 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; -using NLog.Targets.Compound; -using System.Diagnostics; +using NLog.Targets.Wrappers; class Example { @@ -15,7 +13,6 @@ static void Main(string[] args) FileTarget file2 = new FileTarget(); file2.FileName = "\\\\server2\\share\\file1.txt"; - // write to server1, if it fails switch to server2 FallbackTarget target = new FallbackTarget(); @@ -23,7 +20,9 @@ static void Main(string[] args) target.Targets.Add(file1); target.Targets.Add(file2); - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/File/Archive1/Example.cs b/examples/targets/Configuration API/File/Archive1/Example.cs index 61b0664a22..c319fe38cb 100644 --- a/examples/targets/Configuration API/File/Archive1/Example.cs +++ b/examples/targets/Configuration API/File/Archive1/Example.cs @@ -1,6 +1,6 @@ using NLog; +using NLog.Config; using NLog.Targets; -using NLog.Targets.Wrappers; class Example { @@ -13,10 +13,9 @@ static void Main(string[] args) target.ArchiveAboveSize = 10 * 1024; // archive files greater than 10 KB target.ArchiveNumbering = FileTarget.ArchiveNumberingMode.Sequence; - // this speeds up things when no other processes are writing to the file - target.ConcurrentWrites = true; - - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); diff --git a/examples/targets/Configuration API/File/Archive2/Example.cs b/examples/targets/Configuration API/File/Archive2/Example.cs index 7d37f9d13c..b571c5277d 100644 --- a/examples/targets/Configuration API/File/Archive2/Example.cs +++ b/examples/targets/Configuration API/File/Archive2/Example.cs @@ -1,7 +1,6 @@ using NLog; +using NLog.Config; using NLog.Targets; -using NLog.Targets.Wrappers; -using System.Threading; class Example { @@ -15,10 +14,9 @@ static void Main(string[] args) target.ArchiveNumbering = FileTarget.ArchiveNumberingMode.Rolling; target.MaxArchiveFiles = 3; - // this speeds up things when no other processes are writing to the file - target.ConcurrentWrites = true; - - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); @@ -40,7 +38,7 @@ static void Main(string[] args) for (int i = 0; i < 250; ++i) { logger.Debug("log message {i}", i); - Thread.Sleep(1000); + System.Threading.Thread.Sleep(1000); } } } diff --git a/examples/targets/Configuration API/File/Archive3/Example.cs b/examples/targets/Configuration API/File/Archive3/Example.cs index 376bf0ea62..f89c5966cd 100644 --- a/examples/targets/Configuration API/File/Archive3/Example.cs +++ b/examples/targets/Configuration API/File/Archive3/Example.cs @@ -1,7 +1,6 @@ using NLog; +using NLog.Config; using NLog.Targets; -using NLog.Targets.Wrappers; -using System.Threading; class Example { @@ -17,10 +16,9 @@ static void Main(string[] args) target.MaxArchiveFiles = 3; target.ArchiveAboveSize = 10000; - // this speeds up things when no other processes are writing to the file - target.ConcurrentWrites = true; - - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); @@ -44,7 +42,7 @@ static void Main(string[] args) for (int i = 0; i < 2500; ++i) { logger.Debug("log message {i}", i); - Thread.Sleep(100); + System.Threading.Thread.Sleep(100); } } } diff --git a/examples/targets/Configuration API/File/Archive4/Example.cs b/examples/targets/Configuration API/File/Archive4/Example.cs index a3a9fab2e6..cf1c114a33 100644 --- a/examples/targets/Configuration API/File/Archive4/Example.cs +++ b/examples/targets/Configuration API/File/Archive4/Example.cs @@ -1,7 +1,6 @@ using NLog; +using NLog.Config; using NLog.Targets; -using NLog.Targets.Wrappers; -using System.Threading; class Example { @@ -17,10 +16,9 @@ static void Main(string[] args) target.MaxArchiveFiles = 3; target.ArchiveAboveSize = 10000; - // this speeds up things when no other processes are writing to the file - target.ConcurrentWrites = true; - - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); @@ -57,7 +55,7 @@ static void Main(string[] args) logger.Debug("log message {i}", i); logger.Error("log message {i}", i); logger.Fatal("log message {i}", i); - Thread.Sleep(100); + System.Threading.Thread.Sleep(100); } } } diff --git a/examples/targets/Configuration API/File/Asynchronous/Example.cs b/examples/targets/Configuration API/File/Asynchronous/Example.cs index 53a7449241..f3f18ae5c6 100644 --- a/examples/targets/Configuration API/File/Asynchronous/Example.cs +++ b/examples/targets/Configuration API/File/Asynchronous/Example.cs @@ -1,7 +1,7 @@ using NLog; +using NLog.Config; using NLog.Targets; using NLog.Targets.Wrappers; -using System.Text; class Example { @@ -11,16 +11,20 @@ static void Main(string[] args) target.Layout = "${longdate} ${logger} ${message}"; target.FileName = "${basedir}/logs/logfile.txt"; target.KeepFileOpen = false; - target.Encoding = Encoding.UTF8; + target.Encoding = System.Text.Encoding.UTF8; AsyncTargetWrapper wrapper = new AsyncTargetWrapper(); wrapper.WrappedTarget = target; wrapper.QueueLimit = 5000; wrapper.OverflowAction = AsyncTargetWrapperOverflowAction.Discard; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(wrapper, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(wrapper); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); + + LogManager.Flush(); } } diff --git a/examples/targets/Configuration API/File/CSV/Example.cs b/examples/targets/Configuration API/File/CSV/Example.cs index 23cd05644d..3661346237 100644 --- a/examples/targets/Configuration API/File/CSV/Example.cs +++ b/examples/targets/Configuration API/File/CSV/Example.cs @@ -1,6 +1,5 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; using NLog.Layouts; @@ -12,7 +11,6 @@ static void Main(string[] args) target.FileName = "${basedir}/file.csv"; CsvLayout layout = new CsvLayout(); - layout.Columns.Add(new CsvColumn("time", "${longdate}")); layout.Columns.Add(new CsvColumn("message", "${message}")); layout.Columns.Add(new CsvColumn("logger", "${logger}")); @@ -20,7 +18,9 @@ static void Main(string[] args) target.Layout = layout; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/File/Multiple/Example.cs b/examples/targets/Configuration API/File/Multiple/Example.cs index b6420514a3..55742bbbb8 100644 --- a/examples/targets/Configuration API/File/Multiple/Example.cs +++ b/examples/targets/Configuration API/File/Multiple/Example.cs @@ -1,6 +1,6 @@ using NLog; +using NLog.Config; using NLog.Targets; -using System.Text; class Example { @@ -10,9 +10,11 @@ static void Main(string[] args) target.Layout = "${longdate} ${logger} ${message}"; target.FileName = "${basedir}/${level}.log"; target.KeepFileOpen = false; - target.Encoding = Encoding.UTF8; + target.Encoding = System.Text.Encoding.UTF8; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/File/Multiple2/Example.cs b/examples/targets/Configuration API/File/Multiple2/Example.cs index a00ecebfbd..6b58c4d250 100644 --- a/examples/targets/Configuration API/File/Multiple2/Example.cs +++ b/examples/targets/Configuration API/File/Multiple2/Example.cs @@ -1,6 +1,6 @@ using NLog; +using NLog.Config; using NLog.Targets; -using System.Text; class Example { @@ -8,11 +8,13 @@ static void Main(string[] args) { FileTarget target = new FileTarget(); target.Layout = "${longdate} ${logger} ${message}"; - target.FileName = "${basedir}/${shortdate}/${windows-identity:domain=false}.${level}.log"; + target.FileName = "${basedir}/${shortdate}/${environment-user}.${level}.log"; target.KeepFileOpen = false; - target.Encoding = Encoding.UTF8; + target.Encoding = System.Text.Encoding.UTF8; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/File/Simple/Example.cs b/examples/targets/Configuration API/File/Simple/Example.cs index cc2da018f4..ea25304c78 100644 --- a/examples/targets/Configuration API/File/Simple/Example.cs +++ b/examples/targets/Configuration API/File/Simple/Example.cs @@ -1,6 +1,6 @@ using NLog; +using NLog.Config; using NLog.Targets; -using System.Text; class Example { @@ -10,9 +10,11 @@ static void Main(string[] args) target.Layout = "${longdate} ${logger} ${message}"; target.FileName = "${basedir}/logs/logfile.txt"; target.KeepFileOpen = false; - target.Encoding = Encoding.UTF8; + target.Encoding = System.Text.Encoding.UTF8; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/FilteringWrapper/Simple/Example.cs b/examples/targets/Configuration API/FilteringWrapper/Simple/Example.cs index 5f163416d6..2c17ad65e7 100644 --- a/examples/targets/Configuration API/FilteringWrapper/Simple/Example.cs +++ b/examples/targets/Configuration API/FilteringWrapper/Simple/Example.cs @@ -1,9 +1,7 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; using NLog.Targets.Wrappers; -using System.Diagnostics; class Example { @@ -17,7 +15,9 @@ static void Main(string[] args) filteringTarget.Condition = "contains('${message}','1')"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(filteringTarget, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(filteringTarget); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message 0"); diff --git a/examples/targets/Configuration API/MSMQ/Multiple Queues/Example.cs b/examples/targets/Configuration API/MSMQ/Multiple Queues/Example.cs index a8692fce24..b7bfa90e87 100644 --- a/examples/targets/Configuration API/MSMQ/Multiple Queues/Example.cs +++ b/examples/targets/Configuration API/MSMQ/Multiple Queues/Example.cs @@ -1,13 +1,11 @@ using NLog; using NLog.Config; -using NLog.Win32.Targets; +using NLog.Targets; class Example { static void Main(string[] args) { - NLog.Internal.InternalLogger.LogToConsole = true; - MSMQTarget target = new MSMQTarget(); target.Queue = ".\\private$\\nlog.${level}"; target.Label = "${message}"; @@ -15,7 +13,9 @@ static void Main(string[] args) target.CreateQueueIfNotExists = true; target.Recoverable = true; - SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Trace); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger l = LogManager.GetLogger("AAA"); l.Error("This is an error. It goes to .\\private$\\nlog.Error queue."); diff --git a/examples/targets/Configuration API/MSMQ/Simple/Example.cs b/examples/targets/Configuration API/MSMQ/Simple/Example.cs index 955b71d563..aa4c0b8c1a 100644 --- a/examples/targets/Configuration API/MSMQ/Simple/Example.cs +++ b/examples/targets/Configuration API/MSMQ/Simple/Example.cs @@ -1,13 +1,11 @@ using NLog; using NLog.Config; -using NLog.Win32.Targets; +using NLog.Targets; class Example { static void Main(string[] args) { - NLog.Internal.InternalLogger.LogToConsole = true; - MSMQTarget target = new MSMQTarget(); target.Queue = ".\\private$\\nlog"; target.Label = "${message}"; @@ -15,7 +13,9 @@ static void Main(string[] args) target.CreateQueueIfNotExists = true; target.Recoverable = true; - SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Trace); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger l = LogManager.GetLogger("AAA"); l.Error("This is an error. It goes to .\\private$\\nlog queue."); diff --git a/examples/targets/Configuration API/Mail/Buffered/Example.cs b/examples/targets/Configuration API/Mail/Buffered/Example.cs index 3c191e6e58..22083ec5c9 100644 --- a/examples/targets/Configuration API/Mail/Buffered/Example.cs +++ b/examples/targets/Configuration API/Mail/Buffered/Example.cs @@ -1,6 +1,7 @@ using System; using NLog; +using NLog.Config; using NLog.Targets; using NLog.Targets.Wrappers; @@ -21,9 +22,11 @@ static void Main(string[] args) target.Subject = "sample subject"; target.Body = "${message}${newline}"; - BufferingTargetWrapper buffer = new BufferingTargetWrapper(target, 5); + BufferingTargetWrapper bufferTarget = new BufferingTargetWrapper(target, 5); - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(buffer, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(bufferTarget); + LogManager.Configuration = nlogConfig; Console.WriteLine("Sending..."); Logger logger = LogManager.GetLogger("Example"); @@ -42,7 +45,6 @@ static void Main(string[] args) catch (Exception ex) { Console.WriteLine("EX: {0}", ex); - } } } diff --git a/examples/targets/Configuration API/Mail/Simple/Example.cs b/examples/targets/Configuration API/Mail/Simple/Example.cs index 4a9ccbee70..253ca28ef6 100644 --- a/examples/targets/Configuration API/Mail/Simple/Example.cs +++ b/examples/targets/Configuration API/Mail/Simple/Example.cs @@ -1,6 +1,7 @@ using System; using NLog; +using NLog.Config; using NLog.Targets; class Example @@ -17,7 +18,9 @@ static void Main(string[] args) target.To = "jaak@jkowalski.net"; target.Subject = "sample subject"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Console.WriteLine("Sending..."); Logger logger = LogManager.GetLogger("Example"); @@ -27,7 +30,6 @@ static void Main(string[] args) catch (Exception ex) { Console.WriteLine("EX: {0}", ex); - } } } diff --git a/examples/targets/Configuration API/Memory/Simple/Example.cs b/examples/targets/Configuration API/Memory/Simple/Example.cs index b4b45b5576..c508e0e1ac 100644 --- a/examples/targets/Configuration API/Memory/Simple/Example.cs +++ b/examples/targets/Configuration API/Memory/Simple/Example.cs @@ -1,6 +1,5 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; class Example @@ -10,7 +9,9 @@ static void Main(string[] args) MemoryTarget target = new MemoryTarget(); target.Layout = "${message}"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/MethodCall/Simple/Example.cs b/examples/targets/Configuration API/MethodCall/Simple/Example.cs index b11f24f9f3..1a3ec7f2ad 100644 --- a/examples/targets/Configuration API/MethodCall/Simple/Example.cs +++ b/examples/targets/Configuration API/MethodCall/Simple/Example.cs @@ -1,8 +1,8 @@ using System; using NLog; +using NLog.Config; using NLog.Targets; -using System.Diagnostics; public class Example { @@ -10,6 +10,7 @@ public static void LogMethod(string level, string message) { Console.WriteLine("l: {0} m: {1}", level, message); } + static void Main(string[] args) { MethodCallTarget target = new MethodCallTarget(); @@ -18,7 +19,9 @@ static void Main(string[] args) target.Parameters.Add(new MethodCallParameter("${level}")); target.Parameters.Add(new MethodCallParameter("${message}")); - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/NLogViewer/Simple/Example.cs b/examples/targets/Configuration API/NLogViewer/Simple/Example.cs index 49509962bb..96d4d16e67 100644 --- a/examples/targets/Configuration API/NLogViewer/Simple/Example.cs +++ b/examples/targets/Configuration API/NLogViewer/Simple/Example.cs @@ -1,4 +1,5 @@ using NLog; +using NLog.Config; using NLog.Targets; class Example @@ -8,7 +9,9 @@ static void Main(string[] args) NLogViewerTarget target = new NLogViewerTarget(); target.Address = "udp://localhost:4000"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Trace("log message 1"); @@ -17,5 +20,7 @@ static void Main(string[] args) logger.Warn("log message 4"); logger.Error("log message 5"); logger.Fatal("log message 6"); + + LogManager.Flush(); } } diff --git a/examples/targets/Configuration API/Network/Simple/Example.cs b/examples/targets/Configuration API/Network/Simple/Example.cs index ce1fa978e1..6f2f61e0bf 100644 --- a/examples/targets/Configuration API/Network/Simple/Example.cs +++ b/examples/targets/Configuration API/Network/Simple/Example.cs @@ -1,4 +1,5 @@ using NLog; +using NLog.Config; using NLog.Targets; class Example @@ -9,7 +10,9 @@ static void Main(string[] args) target.Layout = "${level} ${logger} ${message}${newline}"; target.Address = "tcp://localhost:5555"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Trace("log message 1"); @@ -18,5 +21,7 @@ static void Main(string[] args) logger.Warn("log message 4"); logger.Error("log message 5"); logger.Fatal("log message 6"); + + LogManager.Flush(); } } diff --git a/examples/targets/Configuration API/Null/Simple/Example.cs b/examples/targets/Configuration API/Null/Simple/Example.cs index d67ceddd16..dfc3b6a6ea 100644 --- a/examples/targets/Configuration API/Null/Simple/Example.cs +++ b/examples/targets/Configuration API/Null/Simple/Example.cs @@ -1,6 +1,5 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; class Example @@ -11,7 +10,9 @@ static void Main(string[] args) target.Layout = "${message}"; target.FormatMessage = true; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/OutputDebugString/Simple/Example.cs b/examples/targets/Configuration API/OutputDebugString/Simple/Example.cs index a6b83efafd..b747033718 100644 --- a/examples/targets/Configuration API/OutputDebugString/Simple/Example.cs +++ b/examples/targets/Configuration API/OutputDebugString/Simple/Example.cs @@ -1,8 +1,6 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; -using NLog.Win32.Targets; class Example { @@ -11,7 +9,9 @@ static void Main(string[] args) OutputDebugStringTarget target = new OutputDebugStringTarget(); target.Layout = "${message}"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/PerfCounter/Simple/Example.cs b/examples/targets/Configuration API/PerfCounter/Simple/Example.cs index 3dc4040ce1..d81c715653 100644 --- a/examples/targets/Configuration API/PerfCounter/Simple/Example.cs +++ b/examples/targets/Configuration API/PerfCounter/Simple/Example.cs @@ -1,9 +1,6 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; -using NLog.Win32.Targets; -using System.Diagnostics; class Example { @@ -13,10 +10,12 @@ static void Main(string[] args) target.AutoCreate = true; target.CategoryName = "My category"; target.CounterName = "My counter"; - target.CounterType = PerformanceCounterType.NumberOfItems32; + target.CounterType = System.Diagnostics.PerformanceCounterType.NumberOfItems32; target.InstanceName = "My instance"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/PostFilteringWrapper/Simple/Example.cs b/examples/targets/Configuration API/PostFilteringWrapper/Simple/Example.cs index de47474c31..706a006e17 100644 --- a/examples/targets/Configuration API/PostFilteringWrapper/Simple/Example.cs +++ b/examples/targets/Configuration API/PostFilteringWrapper/Simple/Example.cs @@ -1,9 +1,7 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; using NLog.Targets.Wrappers; -using System.Diagnostics; class Example { @@ -18,12 +16,10 @@ static void Main(string[] args) // set up default filter postFilteringTarget.DefaultFilter = "level >= LogLevel.Info"; - FilteringRule rule; - // if there are any warnings in the buffer // dump the messages whose level is Debug or higher - rule = new FilteringRule(); + FilteringRule rule = new FilteringRule(); rule.Exists = "level >= LogLevel.Warn"; rule.Filter = "level >= LogLevel.Debug"; @@ -33,7 +29,9 @@ static void Main(string[] args) target.BufferSize = 100; target.WrappedTarget = postFilteringTarget; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/RandomizeGroup/Simple/Example.cs b/examples/targets/Configuration API/RandomizeGroup/Simple/Example.cs index 5aab6bd82f..25351d6bc5 100644 --- a/examples/targets/Configuration API/RandomizeGroup/Simple/Example.cs +++ b/examples/targets/Configuration API/RandomizeGroup/Simple/Example.cs @@ -1,9 +1,7 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; -using NLog.Targets.Compound; -using System.Diagnostics; +using NLog.Targets.Wrappers; class Example { @@ -19,9 +17,13 @@ static void Main(string[] args) target.Targets.Add(file1); target.Targets.Add(file2); - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); - logger.Debug("log message"); + logger.Debug("log message 1"); + logger.Debug("log message 2"); + logger.Debug("log message 3"); } } diff --git a/examples/targets/Configuration API/RepeatingWrapper/Simple/Example.cs b/examples/targets/Configuration API/RepeatingWrapper/Simple/Example.cs index e37a6becfd..cf5f5a44f9 100644 --- a/examples/targets/Configuration API/RepeatingWrapper/Simple/Example.cs +++ b/examples/targets/Configuration API/RepeatingWrapper/Simple/Example.cs @@ -1,9 +1,7 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; using NLog.Targets.Wrappers; -using System.Diagnostics; class Example { @@ -16,7 +14,9 @@ static void Main(string[] args) target.WrappedTarget = wrappedTarget; target.RepeatCount = 3; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/RetryingWrapper/Simple/Example.cs b/examples/targets/Configuration API/RetryingWrapper/Simple/Example.cs index df19314aea..7d55c24022 100644 --- a/examples/targets/Configuration API/RetryingWrapper/Simple/Example.cs +++ b/examples/targets/Configuration API/RetryingWrapper/Simple/Example.cs @@ -1,9 +1,7 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; using NLog.Targets.Wrappers; -using System.Diagnostics; class Example { @@ -17,7 +15,9 @@ static void Main(string[] args) target.RetryCount = 3; target.RetryDelayMilliseconds = 1000; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/RoundRobinGroup/Simple/Example.cs b/examples/targets/Configuration API/RoundRobinGroup/Simple/Example.cs index e1cc24e3f2..256be49e1d 100644 --- a/examples/targets/Configuration API/RoundRobinGroup/Simple/Example.cs +++ b/examples/targets/Configuration API/RoundRobinGroup/Simple/Example.cs @@ -1,9 +1,7 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; -using NLog.Targets.Compound; -using System.Diagnostics; +using NLog.Targets.Wrapper; class Example { @@ -19,9 +17,13 @@ static void Main(string[] args) target.Targets.Add(file1); target.Targets.Add(file2); - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); - logger.Debug("log message"); + logger.Debug("log message 1"); + logger.Debug("log message 2"); + logger.Debug("log message 3"); } } diff --git a/examples/targets/Configuration API/SplitGroup/Simple/Example.cs b/examples/targets/Configuration API/SplitGroup/Simple/Example.cs index f7766baeda..fe23b57cdc 100644 --- a/examples/targets/Configuration API/SplitGroup/Simple/Example.cs +++ b/examples/targets/Configuration API/SplitGroup/Simple/Example.cs @@ -1,9 +1,7 @@ -using System; - using NLog; +using NLog.Config; using NLog.Targets; using NLog.Targets.Wrapper; -using System.Diagnostics; class Example { @@ -19,7 +17,9 @@ static void Main(string[] args) target.Targets.Add(file1); target.Targets.Add(file2); - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/Trace/Simple/Example.cs b/examples/targets/Configuration API/Trace/Simple/Example.cs index 45f1b44dee..508fdce2c0 100644 --- a/examples/targets/Configuration API/Trace/Simple/Example.cs +++ b/examples/targets/Configuration API/Trace/Simple/Example.cs @@ -1,6 +1,7 @@ using System; using NLog; +using NLog.Config; using NLog.Targets; using System.Diagnostics; @@ -13,7 +14,9 @@ static void Main(string[] args) TraceTarget target = new TraceTarget(); target.Layout = "${message}"; - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Debug("log message"); diff --git a/examples/targets/Configuration API/WebService/Simple/Example.cs b/examples/targets/Configuration API/WebService/Simple/Example.cs index e55ac1db76..0634d78889 100644 --- a/examples/targets/Configuration API/WebService/Simple/Example.cs +++ b/examples/targets/Configuration API/WebService/Simple/Example.cs @@ -1,4 +1,5 @@ using NLog; +using NLog.Config; using NLog.Targets; class Example @@ -15,7 +16,9 @@ static void Main(string[] args) target.Parameters.Add(new MethodCallParameter("n2", "${logger}")); target.Parameters.Add(new MethodCallParameter("n3", "${level}")); - NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; Logger logger = LogManager.GetLogger("Example"); logger.Trace("log message 1"); @@ -24,5 +27,7 @@ static void Main(string[] args) logger.Warn("log message 4"); logger.Error("log message 5"); logger.Fatal("log message 6"); + + LogManager.Flush(); } } From e5475c60e8c6fa277aeeee144df28f8cc13292c8 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 6 Apr 2025 21:31:08 +0200 Subject: [PATCH 078/224] NLog.Targets.AtomicFile for atomic file append (#5770) --- build.ps1 | 11 +- run-tests.ps1 | 8 + .../AtomicFileTarget.cs | 156 +++++++ .../NLog.Targets.AtomicFile.csproj | 94 ++++ .../Layouts/GelfLayout.cs | 22 +- .../Layouts/Log4JXmlEventLayout.cs | 1 + .../Layouts/SyslogFacility.cs | 40 +- .../Layouts/SyslogLayout.cs | 58 +-- .../{SyslogSeverity.cs => SyslogLevel.cs} | 20 +- .../Targets/SyslogTarget.cs | 4 +- src/NLog.sln | 14 + src/NLog/Config/ConfigurationItemFactory.cs | 116 +++-- .../EventPropertiesLayoutRenderer.cs | 1 + .../LoggerNameLayoutRenderer.cs | 1 + .../ReplaceNewLinesLayoutRendererWrapper.cs | 1 + src/NLog/Targets/ColoredConsoleTarget.cs | 1 + src/NLog/Targets/ConsoleTargetHelper.cs | 2 +- .../ExclusiveFileLockingAppender.cs | 2 +- .../ConcurrentWritesMultiProcessTests.cs | 407 ++++++++++++++++++ .../NLog.Targets.AtomicFile.Tests.csproj | 36 ++ .../NLogTests.snk | Bin 0 -> 596 bytes .../ProcessRunner.cs | 164 +++++++ .../Properties/AssemblyInfo.cs | 34 ++ .../ConcurrentFileTargetTests.cs | 2 +- .../ConcurrentWritesMultiProcessTests.cs | 8 +- .../FileAppenderCacheTests.cs | 2 +- .../FilePathLayoutTests.cs | 2 +- .../ProcessRunner.cs | 3 +- .../GelfLayoutTests.cs | 10 +- 29 files changed, 1059 insertions(+), 161 deletions(-) create mode 100644 src/NLog.Targets.AtomicFile/AtomicFileTarget.cs create mode 100644 src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj rename src/NLog.Targets.Network/Layouts/{SyslogSeverity.cs => SyslogLevel.cs} (75%) create mode 100644 tests/NLog.Targets.AtomicFile.Tests/ConcurrentWritesMultiProcessTests.cs create mode 100644 tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj create mode 100644 tests/NLog.Targets.AtomicFile.Tests/NLogTests.snk create mode 100644 tests/NLog.Targets.AtomicFile.Tests/ProcessRunner.cs create mode 100644 tests/NLog.Targets.AtomicFile.Tests/Properties/AssemblyInfo.cs diff --git a/build.ps1 b/build.ps1 index 13ad8f5aec..4946576e7c 100644 --- a/build.ps1 +++ b/build.ps1 @@ -15,7 +15,7 @@ if ($env:APPVEYOR_PULL_REQUEST_NUMBER) $versionSuffix = "PR" + $env:APPVEYOR_PULL_REQUEST_NUMBER } -$targetNugetExe = "tools\nuget.exe" +$targetNugetExe = "tools/nuget.exe" if (-Not (test-path $targetNugetExe)) { # download nuget.exe @@ -23,13 +23,13 @@ if (-Not (test-path $targetNugetExe)) Invoke-WebRequest $sourceNugetExe -OutFile $targetNugetExe } -msbuild /t:Restore,Pack .\src\NLog\ /p:targetFrameworks='"net46;net45;net35;netstandard2.0;netstandard2.1"' /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount +msbuild /t:Restore,Pack ./src/NLog/ /p:targetFrameworks='"net46;net45;net35;netstandard2.0;netstandard2.1"' /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount if (-Not $LastExitCode -eq 0) { exit $LastExitCode } function create-package($packageName, $targetFrameworks) { - $path = ".\src\$packageName\" + $path = "./src/$packageName/" msbuild /t:Restore,Pack $path /p:targetFrameworks=$targetFrameworks /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount if (-Not $LastExitCode -eq 0) { exit $LastExitCode } @@ -37,7 +37,6 @@ function create-package($packageName, $targetFrameworks) create-package 'NLog.AutoReloadConfig' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Database' '"net35;net45;net46;netstandard2.0"' -create-package 'NLog.Targets.ConcurrentFile' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.Mail' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.Network' '"net45;net46;netstandard2.0"' create-package 'NLog.Targets.Trace' '"net35;net45;net46;netstandard2.0"' @@ -45,8 +44,10 @@ create-package 'NLog.Targets.WebService' '"net45;net46;netstandard2.0"' create-package 'NLog.OutputDebugString' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.RegEx' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.WindowsRegistry' '"net35;net45;net46;netstandard2.0"' +create-package 'NLog.Targets.ConcurrentFile' '"net35;net45;net46;netstandard2.0"' +msbuild /t:Restore,Pack ./src/NLog.Targets.AtomicFile/ /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount create-package 'NLog.WindowsEventLog' '"netstandard2.0"' -msbuild /t:xsd /t:NuGetSchemaPackage .\src\NLog.proj /p:Configuration=Release /p:BuildNetFX45=true /p:BuildVersion=$versionProduct /p:Configuration=Release /p:BuildLabelOverride=NONE /verbosity:minimal +msbuild /t:xsd /t:NuGetSchemaPackage ./src/NLog.proj /p:Configuration=Release /p:BuildNetFX45=true /p:BuildVersion=$versionProduct /p:Configuration=Release /p:BuildLabelOverride=NONE /verbosity:minimal exit $LastExitCode diff --git a/run-tests.ps1 b/run-tests.ps1 index ee963a07b9..0c798a06c8 100644 --- a/run-tests.ps1 +++ b/run-tests.ps1 @@ -24,6 +24,10 @@ if ($isWindows -or $Env:WinDir) if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.AtomicFile.Tests/ --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + dotnet test ./tests/NLog.AutoReloadConfig.Tests/ --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } @@ -79,6 +83,10 @@ else if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj --framework net8.0 --configuration release --runtime linux-x64 + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + dotnet test ./tests/NLog.AutoReloadConfig.Tests/ --framework net6.0 --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } diff --git a/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs b/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs new file mode 100644 index 0000000000..be01af39fa --- /dev/null +++ b/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs @@ -0,0 +1,156 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets +{ + using System; + using System.IO; + using NLog.Common; + + /// + /// Extended standard FileTarget with atomic file append for multi-process logging to the same file + /// + [Target("AtomFile")] + [Target("AtomicFile")] + public class AtomicFileTarget : FileTarget + { + /// + /// Gets or sets a value indicating whether concurrent writes to the log file by multiple processes on the same host. + /// + public bool ConcurrentWrites { get; set; } = true; + + /// + protected override Stream CreateFileStream(string filePath, int bufferSize) + { + if (!ConcurrentWrites || !KeepFileOpen || ReplaceFileContentsOnEachWrite) + return base.CreateFileStream(filePath, bufferSize); + + const int maxRetryCount = 5; + for (int i = 1; i <= maxRetryCount; ++i) + { + try + { + return CreateAtomicFileStream(filePath); + } + catch (DirectoryNotFoundException) + { + throw; + } + catch (IOException ex) + { + InternalLogger.Debug(ex, "{0}: Failed opening file: {1}", this, filePath); + if (i == maxRetryCount) + throw; + } + catch (Exception ex) + { + InternalLogger.Debug(ex, "{0}: Failed opening file: {1}", this, filePath); + if (i > 1) + throw; + } + } + + throw new InvalidOperationException("Should not be reached."); + } + + private Stream CreateAtomicFileStream(string filePath) + { + var fileShare = FileShare.ReadWrite; + if (EnableFileDelete) + fileShare |= FileShare.Delete; + +#if NETFRAMEWORK + // https://blogs.msdn.microsoft.com/oldnewthing/20151127-00/?p=92211/ + // https://msdn.microsoft.com/en-us/library/ff548289.aspx + // If only the FILE_APPEND_DATA and SYNCHRONIZE flags are set, the caller can write only to the end of the file, + // and any offset information about writes to the file is ignored. + // However, the file will automatically be extended as necessary for this type of write operation. + return new FileStream( + filePath, + FileMode.Append, + System.Security.AccessControl.FileSystemRights.AppendData | System.Security.AccessControl.FileSystemRights.Synchronize, // <- Atomic append + fileShare, + bufferSize: 1, // No internal buffer, write directly from user-buffer + FileOptions.None); +#else + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) + { + var systemRights = System.Security.AccessControl.FileSystemRights.AppendData | System.Security.AccessControl.FileSystemRights.Synchronize; + return FileSystemAclExtensions.Create( + new FileInfo(filePath), + FileMode.Append, + systemRights, + fileShare, + bufferSize: 1, // No internal buffer, write directly from user-buffer + FileOptions.None, + fileSecurity: null); + } + else + { + return CreateUnixStream(filePath); + } +#endif + } + +#if !NETFRAMEWORK + private Stream CreateUnixStream(string filePath) + { + // Use 0666 (read/write for all) + var permissions = (Mono.Unix.Native.FilePermissions)(6 | (6 << 3) | (6 << 6)); + var openFlags = Mono.Unix.Native.OpenFlags.O_CREAT | Mono.Unix.Native.OpenFlags.O_WRONLY | Mono.Unix.Native.OpenFlags.O_APPEND; + + int fd = Mono.Unix.Native.Syscall.open(filePath, openFlags, permissions); + if (fd == -1 && Mono.Unix.Native.Stdlib.GetLastError() == Mono.Unix.Native.Errno.ENOENT && CreateDirs) + { + string dirName = Path.GetDirectoryName(filePath); + if (!Directory.Exists(dirName)) + Directory.CreateDirectory(dirName); + + fd = Mono.Unix.Native.Syscall.open(filePath, openFlags, permissions); + } + if (fd == -1) + Mono.Unix.UnixMarshal.ThrowExceptionForLastError(); + + try + { + return new Mono.Unix.UnixStream(fd, true); + } + catch + { + Mono.Unix.Native.Syscall.close(fd); + throw; + } + } +#endif + } +} diff --git a/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj new file mode 100644 index 0000000000..730df44510 --- /dev/null +++ b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj @@ -0,0 +1,94 @@ + + + + net35;net46;net8.0 + + NLog.Targets.AtomicFile + NLog + FileTarget with support for atomic append where multiple processes can write to the same file + NLog.Targets.AtomicFile v$(ProductVersion) + $(ProductVersion) + Jarek Kowalski,Kim Christensen,Julian Verdurmen + $([System.DateTime]::Now.ToString(yyyy)) + Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ + + + FileTarget Docs: + https://github.com/NLog/NLog/wiki/File-target + + NLog;File;Archive;NTFS;logging;log + N.png + https://nlog-project.org/ + BSD-3-Clause + git + https://github.com/NLog/NLog.git + + true + 5.0.0.0 + ..\NLog.snk + true + + true + true + true + true + true + copyused + true + + + + NLog.Targets.AtomicFile for .NET Framework 4.6 + true + + + + NLog.Targets.AtomicFile for .NET Framework 4.5 + true + + + + NLog.Targets.AtomicFile for .NET Framework 3.5 + true + + + + NLog.Targets.AtomicFile for NetStandard 2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NLog.Targets.Network/Layouts/GelfLayout.cs b/src/NLog.Targets.Network/Layouts/GelfLayout.cs index 26d1e193ed..899dd6a234 100644 --- a/src/NLog.Targets.Network/Layouts/GelfLayout.cs +++ b/src/NLog.Targets.Network/Layouts/GelfLayout.cs @@ -234,7 +234,7 @@ protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuil target.Append(_completeJsonPropertyName); target.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0}", unixTimestamp); - var gelfSeverity = ToSyslogSeverity(logEvent.Level); + var gelfSeverity = ToSyslogLevel(logEvent.Level); target.Append(_beginJsonPropertyName); target.Append("level"); target.Append(_completeJsonPropertyName); @@ -453,7 +453,7 @@ internal static decimal ToUnixTimeStamp(DateTime timeStamp) return Convert.ToDecimal(timeStamp.ToUniversalTime().Subtract(UnixDateStart).TotalSeconds); } - internal static SyslogSeverity ToSyslogSeverity(LogLevel logLevel) + internal static SyslogLevel ToSyslogLevel(LogLevel logLevel) { try { @@ -461,19 +461,19 @@ internal static SyslogSeverity ToSyslogSeverity(LogLevel logLevel) } catch (IndexOutOfRangeException) { - return SyslogSeverity.Emergency; + return SyslogLevel.Emergency; } } - private static readonly SyslogSeverity[] _logLevelMapping = new [] + private static readonly SyslogLevel[] _logLevelMapping = new [] { - NLog.Layouts.SyslogSeverity.Debug, - NLog.Layouts.SyslogSeverity.Debug, - NLog.Layouts.SyslogSeverity.Informational, - NLog.Layouts.SyslogSeverity.Warning, - NLog.Layouts.SyslogSeverity.Error, - NLog.Layouts.SyslogSeverity.Emergency, - NLog.Layouts.SyslogSeverity.Emergency, + NLog.Layouts.SyslogLevel.Debug, + NLog.Layouts.SyslogLevel.Debug, + NLog.Layouts.SyslogLevel.Informational, + NLog.Layouts.SyslogLevel.Warning, + NLog.Layouts.SyslogLevel.Error, + NLog.Layouts.SyslogLevel.Emergency, + NLog.Layouts.SyslogLevel.Emergency, }; private readonly string _beginJsonMessage = "{\""; diff --git a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs index d5c4fb9a0a..909ca5b447 100644 --- a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs @@ -51,6 +51,7 @@ namespace NLog.Layouts /// /// Documentation on NLog Wiki [Layout("Log4JXmlEventLayout")] + [Layout("Log4JXmlLayout")] [ThreadAgnostic] public class Log4JXmlEventLayout : Layout { diff --git a/src/NLog.Targets.Network/Layouts/SyslogFacility.cs b/src/NLog.Targets.Network/Layouts/SyslogFacility.cs index 00afe91aeb..923be196bb 100644 --- a/src/NLog.Targets.Network/Layouts/SyslogFacility.cs +++ b/src/NLog.Targets.Network/Layouts/SyslogFacility.cs @@ -36,40 +36,40 @@ namespace NLog.Layouts /// Syslog facilities public enum SyslogFacility { - /// Kernel messages + /// Kernel messages - LOG_KERN Kernel = 0, - /// Random user-level messages + /// Random user-level messages - LOG_USER User = 1, - /// Mail system + /// Mail system - LOG_MAIL Mail = 2, - /// System daemons + /// System daemons - LOG_DAEMON Daemons = 3, - /// Security/authorization messages + /// Security/authorization messages - LOG_AUTH Authorization = 4, - /// Messages generated internally by syslogd + /// Messages generated internally by syslogd - LOG_SYSLOG Syslog = 5, - /// Line printer subsystem + /// Line printer subsystem - LOG_LPR Printer = 6, - /// Network news subsystem + /// Network news subsystem - LOG_NEWS News = 7, - /// UUCP subsystem + /// UUCP subsystem - LOG_UUCP Uucp = 8, - /// Clock (cron/at) daemon + /// Clock (cron/at) daemon - LOG_CRON Clock = 9, - /// Security/authorization messages (private) + /// Security/authorization messages (private) - LOG_AUTHPRIV Authorization2 = 10, - /// FTP daemon + /// FTP daemon - LOG_FTP Ftp = 11, /// NTP subsystem @@ -84,28 +84,28 @@ public enum SyslogFacility /// Clock daemon Clock2 = 15, - /// Reserved for local use + /// Reserved for local use - LOG_LOCAL0 Local0 = 16, - /// Reserved for local use + /// Reserved for local use - LOG_LOCAL1 Local1 = 17, - /// Reserved for local use + /// Reserved for local use - LOG_LOCAL2 Local2 = 18, - /// Reserved for local use + /// Reserved for local use - LOG_LOCAL3 Local3 = 19, - /// Reserved for local use + /// Reserved for local use - LOG_LOCAL4 Local4 = 20, - /// Reserved for local use + /// Reserved for local use - LOG_LOCAL5 Local5 = 21, - /// Reserved for local use + /// Reserved for local use - LOG_LOCAL6 Local6 = 22, - /// Reserved for local use + /// Reserved for local use - LOG_LOCAL7 Local7 = 23 } } diff --git a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs index 9a149abd0a..2e52213e34 100644 --- a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs +++ b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs @@ -118,7 +118,7 @@ public Layout SyslogProcessId /// /// Message Severity /// - public Layout SyslogSeverity { get; set; } = Layout.FromMethod(l => ResolveSyslogSeverity(l.Level), LayoutRenderOptions.ThreadAgnostic); + public Layout SyslogLevel { get; set; } = Layout.FromMethod(l => ToSyslogLevel(l.Level), LayoutRenderOptions.ThreadAgnostic); /// /// Device Facility @@ -141,7 +141,7 @@ public Layout SyslogProcessId [ArrayParameter(typeof(TargetPropertyWithContext), "StructuredDataParam")] public List StructuredDataParams { get; } = new List(); - private KeyValuePair> _priValueMapping; + private KeyValuePair> _priValueMapping; /// /// Disables to capture volatile LogEvent-properties from active thread context @@ -178,8 +178,8 @@ protected override void InitializeLayout() Layouts.Add(SyslogMessageId); if (SyslogMessage != null) Layouts.Add(SyslogMessage); - if (SyslogSeverity != null) - Layouts.Add(SyslogSeverity); + if (SyslogLevel != null) + Layouts.Add(SyslogLevel); if (StructuredDataId != null) Layouts.Add(StructuredDataId); for (int i = 0; i < StructuredDataParams.Count; ++i) @@ -206,17 +206,17 @@ protected override string GetFormattedMessage(LogEventInfo logEvent) /// protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) { - var severity = SyslogSeverity.RenderValue(logEvent); + var syslogLevel = SyslogLevel.RenderValue(logEvent); var facility = SyslogFacility; var priValueMapper = _priValueMapping; if (priValueMapper.Key != facility || priValueMapper.Value is null) { - priValueMapper = new KeyValuePair>(facility, ResolveFacilityMapper(facility)); + priValueMapper = new KeyValuePair>(facility, ResolveFacilityMapper(facility)); _priValueMapping = priValueMapper; } - var priValue = priValueMapper.Value[severity]; + var priValue = priValueMapper.Value[syslogLevel]; var hostName = _hostNameString ?? EscapePropertyName(SyslogHostName?.Render(logEvent) ?? string.Empty, HostNameMaxLength); var appName = _appNameString ?? EscapePropertyName(SyslogAppName?.Render(logEvent) ?? string.Empty, AppNameMaxLength); var processId = _processIdString ?? EscapePropertyName(SyslogProcessId?.Render(logEvent) ?? string.Empty, ProcessIdMaxLength); @@ -474,45 +474,45 @@ private static char ToAscii(char c) /// private static bool IsAscii(char c) => (uint)c <= '\x007f'; - private static readonly SyslogSeverity[] _severityMapping = new [] + private static readonly SyslogLevel[] _loglevelMappings = new [] { - NLog.Layouts.SyslogSeverity.Debug, - NLog.Layouts.SyslogSeverity.Debug, - NLog.Layouts.SyslogSeverity.Informational, - NLog.Layouts.SyslogSeverity.Warning, - NLog.Layouts.SyslogSeverity.Error, - NLog.Layouts.SyslogSeverity.Emergency, - NLog.Layouts.SyslogSeverity.Emergency, + NLog.Layouts.SyslogLevel.Debug, + NLog.Layouts.SyslogLevel.Debug, + NLog.Layouts.SyslogLevel.Informational, + NLog.Layouts.SyslogLevel.Warning, + NLog.Layouts.SyslogLevel.Error, + NLog.Layouts.SyslogLevel.Emergency, + NLog.Layouts.SyslogLevel.Emergency, }; - private static SyslogSeverity ResolveSyslogSeverity(LogLevel logLevel) + private static SyslogLevel ToSyslogLevel(LogLevel logLevel) { try { - return _severityMapping[logLevel.Ordinal]; + return _loglevelMappings[logLevel.Ordinal]; } catch (IndexOutOfRangeException) { - return NLog.Layouts.SyslogSeverity.Emergency; + return NLog.Layouts.SyslogLevel.Emergency; } } - private static Dictionary ResolveFacilityMapper(SyslogFacility facility) + private static Dictionary ResolveFacilityMapper(SyslogFacility facility) { - return (new SyslogSeverity[] + return (new SyslogLevel[] { - NLog.Layouts.SyslogSeverity.Emergency, - NLog.Layouts.SyslogSeverity.Alert, - NLog.Layouts.SyslogSeverity.Critical, - NLog.Layouts.SyslogSeverity.Error, - NLog.Layouts.SyslogSeverity.Warning, - NLog.Layouts.SyslogSeverity.Notice, - NLog.Layouts.SyslogSeverity.Informational, - NLog.Layouts.SyslogSeverity.Debug + NLog.Layouts.SyslogLevel.Emergency, + NLog.Layouts.SyslogLevel.Alert, + NLog.Layouts.SyslogLevel.Critical, + NLog.Layouts.SyslogLevel.Error, + NLog.Layouts.SyslogLevel.Warning, + NLog.Layouts.SyslogLevel.Notice, + NLog.Layouts.SyslogLevel.Informational, + NLog.Layouts.SyslogLevel.Debug }).ToDictionary(s => s, s => ResolvePriHeader(facility, s)); } - private static string ResolvePriHeader(SyslogFacility facility, SyslogSeverity severity) + private static string ResolvePriHeader(SyslogFacility facility, SyslogLevel severity) { var priVal = (int)facility * 8 + (int)severity; return $"<{priVal}>"; diff --git a/src/NLog.Targets.Network/Layouts/SyslogSeverity.cs b/src/NLog.Targets.Network/Layouts/SyslogLevel.cs similarity index 75% rename from src/NLog.Targets.Network/Layouts/SyslogSeverity.cs rename to src/NLog.Targets.Network/Layouts/SyslogLevel.cs index 06f2261afe..1cba78d2b7 100644 --- a/src/NLog.Targets.Network/Layouts/SyslogSeverity.cs +++ b/src/NLog.Targets.Network/Layouts/SyslogLevel.cs @@ -33,31 +33,31 @@ namespace NLog.Layouts { - /// Syslog severities - public enum SyslogSeverity + /// Syslog Log Levels for severity + public enum SyslogLevel { - /// Emergency severity + /// System is unusable - LOG_EMERG Emergency = 0, - /// Alert severity + /// Action must be taken immediately - LOG_ALERT Alert = 1, - /// Critical severity + /// Critical conditions - LOG_CRIT Critical = 2, - /// Error severity + /// Error conditions - LOG_ERR Error = 3, - /// Warning severity + /// Warning conditions - LOG_WARNING Warning = 4, - /// Notice severity + /// Normal but significant condition - LOG_NOTICE Notice = 5, - /// Informational severity + /// Informational severity - LOG_INFO Informational = 6, - /// Debug severity + /// Debug messages - LOG_DEBUG Debug = 7 } } diff --git a/src/NLog.Targets.Network/Targets/SyslogTarget.cs b/src/NLog.Targets.Network/Targets/SyslogTarget.cs index 4b213978a3..75fb1bd55a 100644 --- a/src/NLog.Targets.Network/Targets/SyslogTarget.cs +++ b/src/NLog.Targets.Network/Targets/SyslogTarget.cs @@ -77,8 +77,8 @@ public class SyslogTarget : NetworkTarget /// public Layout SyslogMessage { get => _syslogLayout.SyslogMessage; set => _syslogLayout.SyslogMessage = value; } - /// - public Layout SyslogSeverity { get => _syslogLayout.SyslogSeverity; set => _syslogLayout.SyslogSeverity = value; } + /// + public Layout SyslogLevel { get => _syslogLayout.SyslogLevel; set => _syslogLayout.SyslogLevel = value; } /// public SyslogFacility SyslogFacility { get => _syslogLayout.SyslogFacility; set => _syslogLayout.SyslogFacility = value; } diff --git a/src/NLog.sln b/src/NLog.sln index ec9422a53f..214283edea 100644 --- a/src/NLog.sln +++ b/src/NLog.sln @@ -77,6 +77,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.Trace", "NLog. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.Trace.Tests", "..\tests\NLog.Targets.Trace.Tests\NLog.Targets.Trace.Tests.csproj", "{877F835E-4D1E-45D8-95AD-AC2F0E3764C7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.AtomicFile", "NLog.Targets.AtomicFile\NLog.Targets.AtomicFile.csproj", "{C767E563-82AB-4545-8904-B31E4CF86C2E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.AtomicFile.Tests", "..\tests\NLog.Targets.AtomicFile.Tests\NLog.Targets.AtomicFile.Tests.csproj", "{2365D168-476A-4239-9086-71DC8F5A2B75}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -187,6 +191,14 @@ Global {877F835E-4D1E-45D8-95AD-AC2F0E3764C7}.Debug|Any CPU.Build.0 = Debug|Any CPU {877F835E-4D1E-45D8-95AD-AC2F0E3764C7}.Release|Any CPU.ActiveCfg = Release|Any CPU {877F835E-4D1E-45D8-95AD-AC2F0E3764C7}.Release|Any CPU.Build.0 = Release|Any CPU + {C767E563-82AB-4545-8904-B31E4CF86C2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C767E563-82AB-4545-8904-B31E4CF86C2E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C767E563-82AB-4545-8904-B31E4CF86C2E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C767E563-82AB-4545-8904-B31E4CF86C2E}.Release|Any CPU.Build.0 = Release|Any CPU + {2365D168-476A-4239-9086-71DC8F5A2B75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2365D168-476A-4239-9086-71DC8F5A2B75}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2365D168-476A-4239-9086-71DC8F5A2B75}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2365D168-476A-4239-9086-71DC8F5A2B75}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -216,6 +228,8 @@ Global {A869B720-AB81-4AA9-94B4-12C5CF490FFE} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {68CE5BB3-DF9D-4774-9B66-6F7378BC04F5} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {877F835E-4D1E-45D8-95AD-AC2F0E3764C7} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {C767E563-82AB-4545-8904-B31E4CF86C2E} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {2365D168-476A-4239-9086-71DC8F5A2B75} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6B4C8E9F-1E2F-4AB3-993A-8776CFA08DC0} diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index e282a00311..d373f5f5b2 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -426,93 +426,73 @@ private object ResolveCreateInstanceLegacy(Type itemType) return itemFactory.ItemCreator.Invoke(); } + private static void SafeRegisterNamedType(Factory factory, string typeAlias, string fullTypeName, bool skipCheckExists) + where TBaseType : class + where TAttributeType : NameBaseAttribute + { +#pragma warning disable CS0618 // Type or member is obsolete + if (skipCheckExists || !factory.CheckTypeAliasExists(typeAlias)) + factory.RegisterNamedType(typeAlias, fullTypeName); +#pragma warning restore CS0618 // Type or member is obsolete + } + private void RegisterAllTargets(bool skipCheckExists) { AssemblyExtensionTypes.RegisterTargetTypes(this, skipCheckExists); -#pragma warning disable CS0618 // Type or member is obsolete - if (skipCheckExists || !_targets.CheckTypeAliasExists("diagnosticlistener")) - _targets.RegisterNamedType("diagnosticlistener", "NLog.Targets.DiagnosticListenerTarget, NLog.DiagnosticSource"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("database")) - _targets.RegisterNamedType("database", "NLog.Targets.DatabaseTarget, NLog.Database"); + SafeRegisterNamedType(_targets, "diagnosticlistener", "NLog.Targets.DiagnosticListenerTarget, NLog.DiagnosticSource", skipCheckExists); + SafeRegisterNamedType(_targets, "database", "NLog.Targets.DatabaseTarget, NLog.Database", skipCheckExists); + SafeRegisterNamedType(_targets, "atomfile", "NLog.Targets.AtomicFileTarget, NLog.Targets.AtomicFile", skipCheckExists); + SafeRegisterNamedType(_targets, "atomicfile", "NLog.Targets.AtomicFileTarget, NLog.Targets.AtomicFile", skipCheckExists); #if NETSTANDARD - if (skipCheckExists || !_targets.CheckTypeAliasExists("eventlog")) - _targets.RegisterNamedType("eventlog", "NLog.Targets.EventLogTarget, NLog.WindowsEventLog"); + SafeRegisterNamedType(_targets, "eventlog", "NLog.Targets.EventLogTarget, NLog.WindowsEventLog", skipCheckExists); #endif - if (skipCheckExists || !_targets.CheckTypeAliasExists("impersonatingwrapper")) - _targets.RegisterNamedType("impersonatingwrapper", "NLog.Targets.Wrappers.ImpersonatingTargetWrapper, NLog.WindowsIdentity"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("logreceiverservice")) - _targets.RegisterNamedType("logreceiverservice", "NLog.Targets.LogReceiverWebServiceTarget, NLog.Wcf"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("outputdebugstring")) - _targets.RegisterNamedType("outputdebugstring", "NLog.Targets.OutputDebugStringTarget, NLog.OutputDebugString"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("network")) - _targets.RegisterNamedType("network", "NLog.Targets.NetworkTarget, NLog.Targets.Network"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("chainsaw")) - _targets.RegisterNamedType("chainsaw", "NLog.Targets.ChainsawTarget, NLog.Targets.Network"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("nlogviewer")) - _targets.RegisterNamedType("nlogviewer", "NLog.Targets.ChainsawTarget, NLog.Targets.Network"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("mail")) - _targets.RegisterNamedType("mail", "NLog.Targets.MailTarget, NLog.Targets.Mail"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("email")) - _targets.RegisterNamedType("email", "NLog.Targets.MailTarget, NLog.Targets.Mail"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("smtp")) - _targets.RegisterNamedType("smtp", "NLog.Targets.MailTarget, NLog.Targets.Mail"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("performancecounter")) - _targets.RegisterNamedType("performancecounter", "NLog.Targets.PerformanceCounterTarget, NLog.PerformanceCounter"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("richtextbox")) - _targets.RegisterNamedType("richtextbox", "NLog.Windows.Forms.RichTextBoxTarget, NLog.Windows.Forms"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("messagebox")) - _targets.RegisterNamedType("messagebox", "NLog.Windows.Forms.MessageBoxTarget, NLog.Windows.Forms"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("formcontrol")) - _targets.RegisterNamedType("formcontrol", "NLog.Windows.Forms.FormControlTarget, NLog.Windows.Forms"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("toolstripitem")) - _targets.RegisterNamedType("toolstripitem", "NLog.Windows.Forms.ToolStripItemTarget, NLog.Windows.Forms"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("trace")) - _targets.RegisterNamedType("trace", "NLog.Targets.TraceTarget, NLog.Targets.Trace"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("tracesystem")) - _targets.RegisterNamedType("tracesystem", "NLog.Targets.TraceTarget, NLog.Targets.Trace"); - if (skipCheckExists || !_targets.CheckTypeAliasExists("webservice")) - _targets.RegisterNamedType("webservice", "NLog.Targets.WebServiceTarget, NLog.Targets.WebService"); -#pragma warning restore CS0618 // Type or member is obsolete + SafeRegisterNamedType(_targets, "impersonatingwrapper", "NLog.Targets.Wrappers.ImpersonatingTargetWrapper, NLog.WindowsIdentity", skipCheckExists); + SafeRegisterNamedType(_targets, "logreceiverservice", "NLog.Targets.LogReceiverWebServiceTarget, NLog.Wcf", skipCheckExists); + SafeRegisterNamedType(_targets, "outputdebugstring", "NLog.Targets.OutputDebugStringTarget, NLog.OutputDebugString", skipCheckExists); + SafeRegisterNamedType(_targets, "network", "NLog.Targets.NetworkTarget, NLog.Targets.Network", skipCheckExists); + SafeRegisterNamedType(_targets, "chainsaw", "NLog.Targets.ChainsawTarget, NLog.Targets.Network", skipCheckExists); + SafeRegisterNamedType(_targets, "nlogviewer", "NLog.Targets.ChainsawTarget, NLog.Targets.Network", skipCheckExists); + SafeRegisterNamedType(_targets, "syslog", "NLog.Targets.SyslogTarget, NLog.Targets.Network", skipCheckExists); + SafeRegisterNamedType(_targets, "gelf", "NLog.Targets.GelfTarget, NLog.Targets.Network", skipCheckExists); + SafeRegisterNamedType(_targets, "mail", "NLog.Targets.MailTarget, NLog.Targets.Mail", skipCheckExists); + SafeRegisterNamedType(_targets, "email", "NLog.Targets.MailTarget, NLog.Targets.Mail", skipCheckExists); + SafeRegisterNamedType(_targets, "smtp", "NLog.Targets.MailTarget, NLog.Targets.Mail", skipCheckExists); + SafeRegisterNamedType(_targets, "performancecounter", "NLog.Targets.PerformanceCounterTarget, NLog.PerformanceCounter", skipCheckExists); + SafeRegisterNamedType(_targets, "richtextbox", "NLog.Windows.Forms.RichTextBoxTarget, NLog.Windows.Forms", skipCheckExists); + SafeRegisterNamedType(_targets, "messagebox", "NLog.Windows.Forms.MessageBoxTarget, NLog.Windows.Forms", skipCheckExists); + SafeRegisterNamedType(_targets, "formcontrol", "NLog.Windows.Forms.FormControlTarget, NLog.Windows.Forms", skipCheckExists); + SafeRegisterNamedType(_targets, "toolstripitem", "NLog.Windows.Forms.ToolStripItemTarget, NLog.Windows.Forms", skipCheckExists); + SafeRegisterNamedType(_targets, "trace", "NLog.Targets.TraceTarget, NLog.Targets.Trace", skipCheckExists); + SafeRegisterNamedType(_targets, "tracesystem", "NLog.Targets.TraceTarget, NLog.Targets.Trace", skipCheckExists); + SafeRegisterNamedType(_targets, "webservice", "NLog.Targets.WebServiceTarget, NLog.Targets.WebService", skipCheckExists); } private void RegisterAllLayouts(bool skipCheckExists) { AssemblyExtensionTypes.RegisterLayoutTypes(this, skipCheckExists); -#pragma warning disable CS0618 // Type or member is obsolete - if (skipCheckExists || !_layouts.CheckTypeAliasExists("log4jxmleventlayout")) - _layouts.RegisterNamedType("log4jxmleventlayout", "NLog.Layouts.Log4JXmlEventLayout, NLog.Targets.Network"); #if !NET35 && !NET40 - if (skipCheckExists || !_layouts.CheckTypeAliasExists("microsoftconsolejsonlayout")) - _layouts.RegisterNamedType("microsoftconsolejsonlayout", "NLog.Extensions.Logging.MicrosoftConsoleJsonLayout, NLog.Extensions.Logging"); + SafeRegisterNamedType(_layouts, "microsoftconsolejsonlayout", "NLog.Extensions.Logging.MicrosoftConsoleJsonLayout, NLog.Extensions.Logging", skipCheckExists); #endif -#pragma warning restore CS0618 // Type or member is obsolete + SafeRegisterNamedType(_layouts, "sysloglayout", "NLog.Targets.SyslogLayout, NLog.Targets.Network", skipCheckExists); + SafeRegisterNamedType(_layouts, "log4jxmllayout", "NLog.Targets.Log4JXmlEventLayout, NLog.Targets.Network", skipCheckExists); + SafeRegisterNamedType(_layouts, "log4jxmleventlayout", "NLog.Targets.Log4JXmlEventLayout, NLog.Targets.Network", skipCheckExists); + SafeRegisterNamedType(_layouts, "gelflayout", "NLog.Targets.GelfLayout, NLog.Targets.Network", skipCheckExists); } private void RegisterAllLayoutRenderers(bool skipCheckExists) { AssemblyExtensionTypes.RegisterLayoutRendererTypes(this, skipCheckExists); -#pragma warning disable CS0618 // Type or member is obsolete #if !NET35 && !NET40 - if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("configsetting")) - _layoutRenderers.RegisterNamedType("configsetting", "NLog.Extensions.Logging.ConfigSettingLayoutRenderer, NLog.Extensions.Logging"); - if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("microsoftconsolelayout")) - _layoutRenderers.RegisterNamedType("microsoftconsolelayout", "NLog.Extensions.Logging.MicrosoftConsoleLayoutRenderer, NLog.Extensions.Logging"); + SafeRegisterNamedType(_layoutRenderers, "configsetting", "NLog.Extensions.Logging.ConfigSettingLayoutRenderer, NLog.Extensions.Logging", skipCheckExists); + SafeRegisterNamedType(_layoutRenderers, "microsoftconsolelayout", "NLog.Extensions.Logging.MicrosoftConsoleLayoutRenderer, NLog.Extensions.Logging", skipCheckExists); #endif - if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("log4jxmlevent")) - _layoutRenderers.RegisterNamedType("log4jxmlevent", "NLog.LayoutRenderers.Log4JXmlEventLayoutRenderer, NLog.Targets.Network"); - if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("performancecounter")) - _layoutRenderers.RegisterNamedType("performancecounter", "NLog.LayoutRenderers.PerformanceCounterLayoutRenderer, NLog.PerformanceCounter"); - if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("registry")) - _layoutRenderers.RegisterNamedType("registry", "NLog.LayoutRenderers.RegistryLayoutRenderer, NLog.WindowsRegistry"); - if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("windowsidentity")) - _layoutRenderers.RegisterNamedType("windowsidentity", "NLog.LayoutRenderers.WindowsIdentityLayoutRenderer, NLog.WindowsIdentity"); - if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("rtblink")) - _layoutRenderers.RegisterNamedType("rtblink", "NLog.Windows.Forms.RichTextBoxLinkLayoutRenderer, NLog.Windows.Forms"); - if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("activity")) - _layoutRenderers.RegisterNamedType("activity", "NLog.LayoutRenderers.ActivityTraceLayoutRenderer, NLog.DiagnosticSource"); - if (skipCheckExists || !_layoutRenderers.CheckTypeAliasExists("activityid")) - _layoutRenderers.RegisterNamedType("activityid", "NLog.LayoutRenderers.TraceActivityIdLayoutRenderer, NLog.Targets.Trace"); -#pragma warning restore CS0618 // Type or member is obsolete + SafeRegisterNamedType(_layoutRenderers, "log4jxmlevent", "NLog.LayoutRenderers.Log4JXmlEventLayoutRenderer, NLog.Targets.Network", skipCheckExists); + SafeRegisterNamedType(_layoutRenderers, "performancecounter", "NLog.LayoutRenderers.PerformanceCounterLayoutRenderer, NLog.PerformanceCounter", skipCheckExists); + SafeRegisterNamedType(_layoutRenderers, "registry", "NLog.LayoutRenderers.RegistryLayoutRenderer, NLog.WindowsRegistry", skipCheckExists); + SafeRegisterNamedType(_layoutRenderers, "windowsidentity", "NLog.LayoutRenderers.WindowsIdentityLayoutRenderer, NLog.WindowsIdentity", skipCheckExists); + SafeRegisterNamedType(_layoutRenderers, "rtblink", "NLog.Windows.Forms.RichTextBoxLinkLayoutRenderer, NLog.Windows.Forms", skipCheckExists); + SafeRegisterNamedType(_layoutRenderers, "activity", "NLog.LayoutRenderers.ActivityTraceLayoutRenderer, NLog.DiagnosticSource", skipCheckExists); + SafeRegisterNamedType(_layoutRenderers, "activityid", "NLog.LayoutRenderers.TraceActivityIdLayoutRenderer, NLog.Targets.Trace", skipCheckExists); } private void RegisterAllFilters(bool skipCheckExists) diff --git a/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs b/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs index 8a7a15d362..8e4a5d3f4d 100644 --- a/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs @@ -90,6 +90,7 @@ public string ObjectPath /// /// Gets or sets whether to perform case-sensitive property-name lookup /// + /// public bool IgnoreCase { get => _ignoreCase; diff --git a/src/NLog/LayoutRenderers/LoggerNameLayoutRenderer.cs b/src/NLog/LayoutRenderers/LoggerNameLayoutRenderer.cs index 0043b38d9c..3d83967825 100644 --- a/src/NLog/LayoutRenderers/LoggerNameLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LoggerNameLayoutRenderer.cs @@ -58,6 +58,7 @@ public class LoggerNameLayoutRenderer : LayoutRenderer, IStringValueRenderer /// /// Gets or sets a value indicating whether to render prefix of logger name (the part before the trailing dot character). /// + /// public bool PrefixName { get; set; } /// diff --git a/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs index 35a7db1f56..81de2043a5 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs @@ -73,6 +73,7 @@ public string Replacement /// /// Gets or sets a value indicating the string that should be used to replace newlines. /// + /// public string ReplaceNewLines { get => Replacement; set => Replacement = value; } /// diff --git a/src/NLog/Targets/ColoredConsoleTarget.cs b/src/NLog/Targets/ColoredConsoleTarget.cs index 8d1a4c3eee..1d43c96c7f 100644 --- a/src/NLog/Targets/ColoredConsoleTarget.cs +++ b/src/NLog/Targets/ColoredConsoleTarget.cs @@ -218,6 +218,7 @@ public Encoding Encoding /// /// Support NO_COLOR=1 environment variable. See also /// + /// public Layout NoColor { get; set; } = Layout.FromMethod((evt) => new string[] { "1", "TRUE" }.Contains(NLog.Internal.EnvironmentHelper.GetSafeEnvironmentVariable("NO_COLOR")?.Trim().ToUpper())); /// diff --git a/src/NLog/Targets/ConsoleTargetHelper.cs b/src/NLog/Targets/ConsoleTargetHelper.cs index 0ca0727c9d..ce677d6ee7 100644 --- a/src/NLog/Targets/ConsoleTargetHelper.cs +++ b/src/NLog/Targets/ConsoleTargetHelper.cs @@ -50,7 +50,7 @@ public static bool IsConsoleAvailable(out string reason) { if (!Environment.UserInteractive) { -#if !NETSTANDARD +#if NETFRAMEWORK if (Internal.PlatformDetector.IsMono && Console.In is StreamReader) return true; // Extra bonus check for Mono, that doesn't support Environment.UserInteractive #endif diff --git a/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs index 5561c1e471..fa4a3178d1 100644 --- a/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs +++ b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs @@ -169,7 +169,7 @@ private void MonitorFileHasBeenDeleted() private long? RefreshCountedFileSize() { - return (_fileTarget.ArchiveAboveSize > 0 && _fileStream is FileStream) ? _fileStream.Length : default(long?); + return (_fileTarget.ArchiveAboveSize > 0 && _fileTarget.GetType().Equals(typeof(FileTarget))) ? _fileStream.Length : default(long?); } private void SafeCloseFile(string filepath, ref Stream fileStream) diff --git a/tests/NLog.Targets.AtomicFile.Tests/ConcurrentWritesMultiProcessTests.cs b/tests/NLog.Targets.AtomicFile.Tests/ConcurrentWritesMultiProcessTests.cs new file mode 100644 index 0000000000..318acf8fde --- /dev/null +++ b/tests/NLog.Targets.AtomicFile.Tests/ConcurrentWritesMultiProcessTests.cs @@ -0,0 +1,407 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#define DISABLE_FILE_INTERNAL_LOGGING + +namespace NLog.Targets.AtomicFile.Tests +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Text; + using System.Threading; + using NLog.Common; + using NLog.Targets; + using NLog.Targets.Wrappers; + using Xunit; + + public class ConcurrentWritesMultiProcessTests + { + private static readonly Logger _logger = LogManager.GetLogger(nameof(ConcurrentWritesMultiProcessTests)); + + public ConcurrentWritesMultiProcessTests() + { + InternalLogger.Reset(); + LogManager.Configuration = null; + LogManager.ThrowExceptions = true; + } + + private static void ConfigureSharedFile(string mode, string fileName) + { + var modes = mode.Split('|'); + + AtomicFileTarget ft = new AtomicFileTarget(); + ft.FileName = fileName; + ft.Layout = "${message}"; + ft.ConcurrentWrites = true; + ft.OpenFileCacheTimeout = 10; + ft.OpenFileCacheSize = 1; + ft.LineEnding = LineEndingMode.LF; + ft.KeepFileOpen = Array.IndexOf(modes, "retry") >= 0 ? false : true; + ft.ArchiveAboveSize = Array.IndexOf(modes, "archive") >= 0 ? 50 : -1; + if (ft.ArchiveAboveSize > 0) + { + string archivePath = Path.Combine(Path.GetDirectoryName(fileName), "Archive"); + ft.ArchiveFileName = Path.Combine(archivePath, Path.GetFileName(fileName)); + ft.ArchiveSuffixFormat = "_{0:0000}"; + ft.MaxArchiveFiles = 10000; + } + + var name = "ConfigureSharedFile_" + mode.Replace('|', '_') + "-wrapper"; + + LogManager.Setup().SetupExtensions(ext => ext.RegisterTarget("file")); + + switch (modes[0]) + { + case "async": + var asyncTarget = new AsyncTargetWrapper(ft, 100, AsyncTargetWrapperOverflowAction.Grow) { Name = name, TimeToSleepBetweenBatches = 10 }; + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(asyncTarget)); + break; + + case "buffered": + var bufferTarget = new BufferingTargetWrapper(ft, 100) { Name = name }; + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(bufferTarget)); + break; + + case "buffered_timed_flush": + var bufferFlushTarget = new BufferingTargetWrapper(ft, 100, 10) { Name = name }; + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(bufferFlushTarget)); + break; + + default: + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(ft)); + break; + } + } + +#pragma warning disable xUnit1013 // Needed for test + public static void MultiProcessExecutor(string processIndex, string fileName, string numLogsString, string mode) +#pragma warning restore xUnit1013 + { + Thread.CurrentThread.Name = processIndex; + + int numLogs = Convert.ToInt32(numLogsString); + int idxProcess = Convert.ToInt32(processIndex); + + ConfigureSharedFile(mode, fileName); + + string format = processIndex + " {0}"; + + // Having the internal logger enabled would just slow things down, reducing the + // likelyhood for uncovering racing conditions. Uncomment #define DISABLE_FILE_INTERNAL_LOGGING + +#if !DISABLE_FILE_INTERNAL_LOGGING + var logWriter = new StringWriter { NewLine = Environment.NewLine }; + NLog.Common.InternalLogger.LogLevel = LogLevel.Warn; + NLog.Common.InternalLogger.LogFile = Path.Combine(Path.GetDirectoryName(fileName), string.Format("Internal_{0}.txt", processIndex)); + NLog.Common.InternalLogger.LogWriter = logWriter; + NLog.Common.InternalLogger.LogToConsole = true; + + try +#endif + { + LogManager.ThrowExceptions = false; + + Thread.Sleep(Math.Max((10 - idxProcess), 1) * 5); // Delay to wait for the other processes + + for (int i = 0; i < numLogs; ++i) + { + _logger.Debug(format, i); + } + + LogManager.Configuration = null; // Flush + Close + LogManager.ThrowExceptions = true; + } +#if !DISABLE_FILE_INTERNAL_LOGGING + catch (Exception ex) + { + using (var textWriter = File.AppendText(Path.Combine(Path.GetDirectoryName(fileName), string.Format("Internal_{0}.txt", processIndex)))) + { + textWriter.WriteLine(ex.ToString()); + textWriter.WriteLine(logWriter.GetStringBuilder().ToString()); + } + throw; + } + finally + { + LogManager.ThrowExceptions = true; + } + + using (var textWriter = File.AppendText(Path.Combine(Path.GetDirectoryName(fileName), string.Format("Internal_{0}.txt", processIndex)))) + { + textWriter.WriteLine(logWriter.GetStringBuilder().ToString()); + } +#endif + } + + private string MakeFileName(int numProcesses, int numLogs, string mode) + { + // Having separate file names for the various tests makes debugging easier. + return $"test_{numProcesses}_{numLogs}_{mode.Replace('|', '_')}.txt"; + } + + private void DoConcurrentTest(int numProcesses, int numLogs, string mode) + { + string tempPath = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + string archivePath = Path.Combine(tempPath, "Archive"); + + try + { + Directory.CreateDirectory(tempPath); + Directory.CreateDirectory(archivePath); + + string logFile = Path.Combine(tempPath, MakeFileName(numProcesses, numLogs, mode)); + if (File.Exists(logFile)) + { + throw new Exception($"file '{logFile}' already exists"); + } + + Process[] processes = new Process[numProcesses]; + + for (int i = 0; i < numProcesses; ++i) + { + processes[i] = ProcessRunner.SpawnMethod( + GetType(), + nameof(MultiProcessExecutor), + i.ToString(), + logFile, + numLogs.ToString(), + mode); + } + + // In case we'd like to capture stdout, we would need to drain it continuously. + // StandardOutput.ReadToEnd() wont work, since the other processes console only has limited buffer. + for (int i = 0; i < numProcesses; ++i) + { + processes[i].WaitForExit(); + Assert.Equal(0, processes[i].ExitCode); + processes[i].Dispose(); + processes[i] = null; + } + + var files = new System.Collections.Generic.List(Directory.GetFiles(archivePath)); + files.Add(logFile); + + bool verifyFileSize = files.Count > 1; + + var receivedNumbersSet = new List[numProcesses]; + for (int i = 0; i < numProcesses; i++) + { + var receivedNumbers = new List(numLogs); + receivedNumbersSet[i] = receivedNumbers; + } + + //Console.WriteLine("Verifying output file {0}", logFile); + foreach (var file in files) + { + using (StreamReader sr = File.OpenText(file)) + { + string line; + while ((line = sr.ReadLine()) != null) + { + string[] tokens = line.Split(' '); + Assert.Equal(2, tokens.Length); + + int thread = Convert.ToInt32(tokens[0]); + int number = Convert.ToInt32(tokens[1]); + Assert.True(thread >= 0); + Assert.True(thread < numProcesses); + receivedNumbersSet[thread].Add(number); + } + + if (verifyFileSize) + { + if (sr.BaseStream.Length > 150) + throw new InvalidOperationException( + $"Error when reading file {file}, size {sr.BaseStream.Length} is too large"); + else if (sr.BaseStream.Length < 35 && files[files.Count - 1] != file) + throw new InvalidOperationException( + $"Error when reading file {file}, size {sr.BaseStream.Length} is too small"); + } + } + } + + var expected = Enumerable.Range(0, numLogs).ToList(); + + int currentProcess = 0; + bool? equalsWhenReorderd = null; + try + { + for (; currentProcess < numProcesses; currentProcess++) + { + var receivedNumbers = receivedNumbersSet[currentProcess]; + + var equalLength = expected.Count == receivedNumbers.Count; + + var fastCheck = equalLength && expected.SequenceEqual(receivedNumbers); + + if (!fastCheck) + //assert equals on two long lists in xUnit is lame. Not showing the difference. + { + if (equalLength) + { + var reordered = receivedNumbers.OrderBy(i => i); + + equalsWhenReorderd = expected.SequenceEqual(reordered); + } + + Assert.Equal(string.Join(",", expected), string.Join(",", receivedNumbers)); + } + } + } + catch (Exception ex) + { + var reoderProblem = equalsWhenReorderd is null ? "Dunno" : (equalsWhenReorderd == true ? "Yes" : "No"); + throw new InvalidOperationException($"Error when comparing path {tempPath} for process {currentProcess}. Is this a reordering problem? {reoderProblem}", ex); + } + } +#if !DISABLE_FILE_INTERNAL_LOGGING + catch (Exception ex) + { + Console.WriteLine(ex); + Console.WriteLine(); + + StringBuilder sb = new StringBuilder(); + sb.Append(ex.Message); + + for (int i = 0; i < numProcesses; i++) + { + var internalLogFile = Path.Combine(tempPath, string.Format("Internal_{0}.txt", i)); + if (File.Exists(internalLogFile)) + { + sb.AppendFormat(" MultiProcessId={0}", numProcesses); + sb.AppendLine(); + foreach (var line in File.ReadAllLines(internalLogFile)) + sb.AppendLine(line); + sb.AppendLine(); + } + } + + if (sb.Length > ex.Message.Length) + throw new InvalidOperationException(sb.ToString(), ex); + else + throw; + } +#endif + finally + { + try + { + if (Directory.Exists(archivePath)) + Directory.Delete(archivePath, true); + if (Directory.Exists(tempPath)) + Directory.Delete(tempPath, true); + } + catch + { + } + } + } + + [Theory] + [InlineData(2, 10000, "none")] + [InlineData(5, 4000, "none")] + [InlineData(10, 2000, "none")] +#if !MONO + // Linux doesn't work well with concurrent archive operations + [InlineData(2, 500, "none|archive")] +#endif + public void SimpleConcurrentTest(int numProcesses, int numLogs, string mode) + { + if (mode.Contains("archive") && IsLinux()) + { + Console.WriteLine("[SKIP] ConcurrentWritesMultiProcessTests.SimpleConcurrentTest Concurrent archive not supported on Linux"); + return; + } + + for (int i = 1; i <= 3; ++i) + { + try + { + DoConcurrentTest(numProcesses, numLogs, mode); + return; + } + catch + { + if (i == 3) + throw; + } + } + } + + [Theory] + [InlineData("async")] + public void AsyncConcurrentTest(string mode) + { + // Before 2 processes are running into concurrent writes, + // the first process typically already has written couple thousand events. + // Thus to have a meaningful test, at least 10K events are required. + // Due to the buffering it makes no big difference in runtime, whether we + // have 2 process writing 10K events each or couple more processes with even more events. + // Runtime is mostly defined by Runner.exe compilation and JITing the first. + DoConcurrentTest(5, 1000, mode); + } + + [Theory] + [InlineData("buffered")] + public void BufferedConcurrentTest(string mode) + { + DoConcurrentTest(5, 1000, mode); + } + + [Theory] + [InlineData("buffered_timed_flush")] + public void BufferedTimedFlushConcurrentTest(string mode) + { + DoConcurrentTest(5, 1000, mode); + } + + private static void ChildLoggerProcess(string[] args) + { + string processIndex = args[0]; + string fileName = args[1]; + string numLogsString = args[2]; + string mode = args[3]; + + MultiProcessExecutor(processIndex, fileName, numLogsString, mode); + } + + protected static bool IsLinux() + { + var val = Environment.GetEnvironmentVariable("WINDIR"); + return string.IsNullOrEmpty(val); + } + } +} diff --git a/tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj b/tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj new file mode 100644 index 0000000000..242a5847cb --- /dev/null +++ b/tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj @@ -0,0 +1,36 @@ + + + + 17.0 + net462 + net462;net8.0 + + false + + NLogTests.snk + false + true + true + + Full + true + true + en + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/tests/NLog.Targets.AtomicFile.Tests/NLogTests.snk b/tests/NLog.Targets.AtomicFile.Tests/NLogTests.snk new file mode 100644 index 0000000000000000000000000000000000000000..f168e4dbecfd20aae45a58ff7c8146fd9437ca8c GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098+j;l|->ro!-M_v|L{!{P{!ND{K0P(6c zd-FqtRo5AlvWp)3?L^4gdYGOJw(uLvUU=}ZRnrkvZ)4sHmYxL91Vs-+gH5^l3FT%~ zT4&@aA_Hh(2U-<&=?)x2)II__B*H2}e}!2p6a#Iu;d48bO^8HhTl;j8?*d<1a_R|q8E50kv?eP0U3&ZlGn@^pP7iWL$P<_mA z`9zel$Knfm4TsO>!T$Xq=dg4ToMpb)#Ve)# literal 0 HcmV?d00001 diff --git a/tests/NLog.Targets.AtomicFile.Tests/ProcessRunner.cs b/tests/NLog.Targets.AtomicFile.Tests/ProcessRunner.cs new file mode 100644 index 0000000000..797aa43e0e --- /dev/null +++ b/tests/NLog.Targets.AtomicFile.Tests/ProcessRunner.cs @@ -0,0 +1,164 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets.AtomicFile.Tests +{ + using System; + using System.CodeDom.Compiler; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Text; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CSharp; + using Xunit; + + static class ProcessRunner + { + static ProcessRunner() + { + string sourceCode = @" +using System; +using System.Reflection; + +public static class C1 +{ + public static int Main(string[] args) + { + try + { + Console.WriteLine(""Starting...""); + if (args.Length < 3) + throw new Exception(""Usage: Runner.exe \""AssemblyName\"" \""ClassName\"" \""MethodName\"" \""Parameter1...N\""""); + + string assemblyName = args[0]; + string className = args[1]; + string methodName = args[2]; + object[] arguments = new object[args.Length - 3]; + for (int i = 0; i < arguments.Length; ++i) + arguments[i] = args[3 + i]; + + Assembly assembly = Assembly.Load(assemblyName); + Type type = assembly.GetType(className); + if (type == null) + throw new Exception(className + "" not found in "" + assemblyName); + MethodInfo method = type.GetMethod(methodName); + if (method == null) + throw new Exception(methodName + "" not found in "" + type); + object targetObject = null; + if (!method.IsStatic) + targetObject = Activator.CreateInstance(type); + method.Invoke(targetObject, arguments); + return 0; + } + catch (Exception ex) + { + Console.WriteLine(ex); + return 1; + } + } +}"; + +#if NETFRAMEWORK + CSharpCodeProvider provider = new CSharpCodeProvider(); + var options = new CompilerParameters(); + options.OutputAssembly = "Runner.exe"; + options.GenerateExecutable = true; + options.IncludeDebugInformation = true; + // To allow debugging the generated Runner.exe we need to keep files. + // See https://stackoverflow.com/questions/875723/how-to-debug-break-in-codedom-compiled-code + options.TempFiles = new TempFileCollection(Environment.GetEnvironmentVariable("TEMP"), true); + var results = provider.CompileAssemblyFromSource(options, sourceCode); +#else + var compilation = CSharpCompilation + .Create( + "Runner.dll", + new[] { CSharpSyntaxTree.ParseText(sourceCode) }, + references: Basic.Reference.Assemblies.ReferenceAssemblies.NetStandard20); + + var outputAssembly = Path.Combine(Path.GetDirectoryName(typeof(ProcessRunner).Assembly.Location), "Runner.dll"); + if (System.IO.File.Exists(outputAssembly)) + System.IO.File.Delete(outputAssembly); + using (var stream = new FileStream(outputAssembly, FileMode.CreateNew)) + //using (var stream = new MemoryStream()) + { + var result = compilation.Emit(stream); + var failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error).ToList(); + Assert.True(failures.Count == 0, string.Join(Environment.NewLine, failures)); + Assert.True(result.Success); + } + var outputRuntimeConfig = Path.Combine(Path.GetDirectoryName(typeof(ProcessRunner).Assembly.Location), "Runner.runtimeconfig.json"); + if (System.IO.File.Exists(outputRuntimeConfig)) + System.IO.File.Delete(outputRuntimeConfig); + using (var streamReader = new StreamReader(Path.Combine(Path.GetDirectoryName(typeof(ProcessRunner).Assembly.Location), typeof(ProcessRunner).Assembly.GetName().Name) + ".runtimeconfig.json")) + using (var streamWriter = new StreamWriter(outputRuntimeConfig)) + { + streamWriter.Write(streamReader.ReadToEnd()); + } +#endif + } + + public static Process SpawnMethod(Type type, string methodName, params string[] p) + { + string assemblyName = type.Assembly.FullName; + string typename = type.FullName; + StringBuilder sb = new StringBuilder(); +#if !NETFRAMEWORK + sb.AppendFormat("exec \"{0}\" ", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Runner.dll")); +#endif + sb.AppendFormat("\"{0}\" \"{1}\" \"{2}\"", assemblyName, typename, methodName); + foreach (string s in p) + { + sb.Append(" "); + sb.Append("\""); + sb.Append(s); + sb.Append("\""); + } + + Process proc = new Process(); + proc.StartInfo.Arguments = sb.ToString(); +#if NETFRAMEWORK + proc.StartInfo.FileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Runner.exe"); +#else + proc.StartInfo.FileName = "dotnet"; +#endif + proc.StartInfo.UseShellExecute = false; + proc.StartInfo.CreateNoWindow = true; + // Hint: + // In case we wanna redirect stdout we should drain the redirected pipe continuously. + // Otherwise Runner.exe's console buffer is full rather fast, leading to a lock within Console.Write(Line). + proc.Start(); + return proc; + } + } +} diff --git a/tests/NLog.Targets.AtomicFile.Tests/Properties/AssemblyInfo.cs b/tests/NLog.Targets.AtomicFile.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..23035655fa --- /dev/null +++ b/tests/NLog.Targets.AtomicFile.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +[assembly: Xunit.CollectionBehavior(DisableTestParallelization = true)] diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs index 256140004b..6c033f48ec 100644 --- a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.UnitTests.Targets +namespace NLog.Targets.ConcurrentFile.Tests { using System; using System.Collections.Generic; diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentWritesMultiProcessTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentWritesMultiProcessTests.cs index 56ffee0cc4..4e6a58b057 100644 --- a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentWritesMultiProcessTests.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentWritesMultiProcessTests.cs @@ -35,7 +35,7 @@ #define DISABLE_FILE_INTERNAL_LOGGING -namespace NLog.UnitTests.Targets +namespace NLog.Targets.ConcurrentFile.Tests { using System; using System.Collections.Generic; @@ -52,7 +52,7 @@ namespace NLog.UnitTests.Targets public class ConcurrentWritesMultiProcessTests { - private readonly Logger _logger = LogManager.GetLogger("NLog.UnitTests.Targets.ConcurrentWritesMultiProcessTests"); + private readonly Logger _logger = LogManager.GetLogger(nameof(ConcurrentWritesMultiProcessTests)); public ConcurrentWritesMultiProcessTests() { @@ -275,9 +275,9 @@ private void DoConcurrentTest(int numProcesses, int numLogs, string mode) { if (equalLength) { - var reodered = receivedNumbers.OrderBy(i => i); + var reordered = receivedNumbers.OrderBy(i => i); - equalsWhenReorderd = expected.SequenceEqual(reodered); + equalsWhenReorderd = expected.SequenceEqual(reordered); } Assert.Equal(string.Join(",", expected), string.Join(",", receivedNumbers)); diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/FileAppenderCacheTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/FileAppenderCacheTests.cs index 2c330c9d84..734a7e341c 100644 --- a/tests/NLog.Targets.ConcurrentFile.Tests/FileAppenderCacheTests.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/FileAppenderCacheTests.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.UnitTests.Internal.FileAppenders +namespace NLog.Targets.ConcurrentFile.Tests { using System; using System.IO; diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/FilePathLayoutTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/FilePathLayoutTests.cs index a3a615459d..d49d41880f 100644 --- a/tests/NLog.Targets.ConcurrentFile.Tests/FilePathLayoutTests.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/FilePathLayoutTests.cs @@ -31,7 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -namespace NLog.UnitTests.Internal +namespace NLog.Targets.ConcurrentFile.Tests { using NLog.Internal; using NLog.Layouts; diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/ProcessRunner.cs b/tests/NLog.Targets.ConcurrentFile.Tests/ProcessRunner.cs index 3b0d2f03a0..2f834a9bbc 100644 --- a/tests/NLog.Targets.ConcurrentFile.Tests/ProcessRunner.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/ProcessRunner.cs @@ -33,9 +33,8 @@ #if NETFRAMEWORK -namespace NLog.UnitTests +namespace NLog.Targets.ConcurrentFile.Tests { - using System; using System.CodeDom.Compiler; using System.Diagnostics; diff --git a/tests/NLog.Targets.Network.Tests/GelfLayoutTests.cs b/tests/NLog.Targets.Network.Tests/GelfLayoutTests.cs index 6c3916c6ef..efa3f941ec 100644 --- a/tests/NLog.Targets.Network.Tests/GelfLayoutTests.cs +++ b/tests/NLog.Targets.Network.Tests/GelfLayoutTests.cs @@ -88,7 +88,7 @@ public void CanRenderGelf() HostName, message, expectedDateTime, - (int)GelfLayout.ToSyslogSeverity(logLevel), + (int)GelfLayout.ToSyslogLevel(logLevel), logLevel, loggerName); @@ -139,7 +139,7 @@ public void CanRenderGelfFacility() HostName, message, expectedDateTime, - (int)GelfLayout.ToSyslogSeverity(logLevel), + (int)GelfLayout.ToSyslogLevel(logLevel), facility, loggerName); @@ -190,7 +190,7 @@ public void CanRenderGelfAdditionalFieldsCustomMessage() HostName, message, expectedDateTime, - (int)GelfLayout.ToSyslogSeverity(logLevel), + (int)GelfLayout.ToSyslogLevel(logLevel), threadId); Assert.Equal(expectedGelf, renderedGelf); } @@ -241,7 +241,7 @@ public void CanRenderEventProperties() HostName, message, expectedDateTime, - (int)GelfLayout.ToSyslogSeverity(logLevel), + (int)GelfLayout.ToSyslogLevel(logLevel), threadId, requestId); Assert.Equal(expectedGelf, renderedGelf); @@ -297,7 +297,7 @@ public void CanRenderScopeContext() HostName, message, expectedDateTime, - (int)GelfLayout.ToSyslogSeverity(logLevel), + (int)GelfLayout.ToSyslogLevel(logLevel), threadId, requestId); Assert.Equal(expectedGelf, renderedGelf); From e8fa0ef755c3c58a1449e001d3e6ea134c1275ad Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 8 Apr 2025 20:50:44 +0200 Subject: [PATCH 079/224] NLog.Targets.AtomicFile.Tests - Changed to dynamic sequence archive (#5771) --- .../FileArchiveHandlers/BaseFileArchiveHandler.cs | 10 +++++----- .../ConcurrentWritesMultiProcessTests.cs | 11 +++++------ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs b/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs index affb479729..99d0691d7c 100644 --- a/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs +++ b/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs @@ -307,12 +307,12 @@ private static string GetFileNameWildcard(string filepath) if (lastLength > 0) { - return filename.Substring(0, lastStart) + "*" + filename.Substring(lastStart + lastLength, filename.Length - lastStart - lastLength) + fileext; - } - else - { - return filename + "*" + fileext; + var prefix = filename.Substring(0, lastStart); + var suffix = filename.Substring(lastStart + lastLength, filename.Length - lastStart - lastLength); + return string.IsNullOrEmpty(suffix) ? string.Concat(prefix, "*", fileext) : string.Concat(prefix, "*", suffix, "*", fileext); } + + return string.Concat(filename, "*", fileext); } protected bool DeleteOldArchiveFile(string filepath) diff --git a/tests/NLog.Targets.AtomicFile.Tests/ConcurrentWritesMultiProcessTests.cs b/tests/NLog.Targets.AtomicFile.Tests/ConcurrentWritesMultiProcessTests.cs index 318acf8fde..77a5f43940 100644 --- a/tests/NLog.Targets.AtomicFile.Tests/ConcurrentWritesMultiProcessTests.cs +++ b/tests/NLog.Targets.AtomicFile.Tests/ConcurrentWritesMultiProcessTests.cs @@ -73,8 +73,6 @@ private static void ConfigureSharedFile(string mode, string fileName) ft.ArchiveAboveSize = Array.IndexOf(modes, "archive") >= 0 ? 50 : -1; if (ft.ArchiveAboveSize > 0) { - string archivePath = Path.Combine(Path.GetDirectoryName(fileName), "Archive"); - ft.ArchiveFileName = Path.Combine(archivePath, Path.GetFileName(fileName)); ft.ArchiveSuffixFormat = "_{0:0000}"; ft.MaxArchiveFiles = 10000; } @@ -124,7 +122,7 @@ public static void MultiProcessExecutor(string processIndex, string fileName, st #if !DISABLE_FILE_INTERNAL_LOGGING var logWriter = new StringWriter { NewLine = Environment.NewLine }; - NLog.Common.InternalLogger.LogLevel = LogLevel.Warn; + NLog.Common.InternalLogger.LogLevel = LogLevel.Debug; NLog.Common.InternalLogger.LogFile = Path.Combine(Path.GetDirectoryName(fileName), string.Format("Internal_{0}.txt", processIndex)); NLog.Common.InternalLogger.LogWriter = logWriter; NLog.Common.InternalLogger.LogToConsole = true; @@ -211,8 +209,9 @@ private void DoConcurrentTest(int numProcesses, int numLogs, string mode) processes[i] = null; } - var files = new System.Collections.Generic.List(Directory.GetFiles(archivePath)); - files.Add(logFile); + var files = new System.Collections.Generic.List(new DirectoryInfo(tempPath).GetFiles("test_*.txt").Select(f => f.FullName)); + if (files.Count == 0) + files.Add(logFile); bool verifyFileSize = files.Count > 1; @@ -243,7 +242,7 @@ private void DoConcurrentTest(int numProcesses, int numLogs, string mode) if (verifyFileSize) { - if (sr.BaseStream.Length > 150) + if (sr.BaseStream.Length > 120) throw new InvalidOperationException( $"Error when reading file {file}, size {sr.BaseStream.Length} is too large"); else if (sr.BaseStream.Length < 35 && files[files.Count - 1] != file) From c0bbe4b79d8d375b54fd409bb30750bb17106a58 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 12 Apr 2025 11:10:07 +0200 Subject: [PATCH 080/224] ObjectReflectionCache - PropertyValue with exception handler for AOT (#5775) --- src/NLog/Internal/ObjectReflectionCache.cs | 19 ++++++++++++++++--- src/NLog/Layouts/XML/XmlElementBase.cs | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/NLog/Internal/ObjectReflectionCache.cs b/src/NLog/Internal/ObjectReflectionCache.cs index 3020aefcf9..418454da8c 100644 --- a/src/NLog/Internal/ObjectReflectionCache.cs +++ b/src/NLog/Internal/ObjectReflectionCache.cs @@ -286,7 +286,6 @@ public struct PropertyValue public TypeCode TypeCode => Value is null ? TypeCode.Empty : _typecode; private readonly TypeCode _typecode; public bool HasNameAndValue => Name != null && Value != null; - public PropertyValue(string name, object value, TypeCode typeCode) { Name = name; @@ -297,15 +296,29 @@ public PropertyValue(string name, object value, TypeCode typeCode) public PropertyValue(object owner, PropertyInfo propertyInfo) { Name = propertyInfo.Name; - Value = propertyInfo.GetValue(owner, null); _typecode = TypeCode.Object; + try + { + Value = propertyInfo.GetValue(owner, null); + } + catch + { + Value = null; + } } public PropertyValue(object owner, FastPropertyLookup fastProperty) { Name = fastProperty.Name; - Value = fastProperty.ValueLookup(owner, null); _typecode = fastProperty.TypeCode; + try + { + Value = fastProperty.ValueLookup(owner, null); + } + catch + { + Value = null; + } } } diff --git a/src/NLog/Layouts/XML/XmlElementBase.cs b/src/NLog/Layouts/XML/XmlElementBase.cs index 1b60a69e97..4c51f2903c 100644 --- a/src/NLog/Layouts/XML/XmlElementBase.cs +++ b/src/NLog/Layouts/XML/XmlElementBase.cs @@ -559,7 +559,7 @@ private void AppendXmlObjectPropertyValues(string propName, ref ObjectReflection if (beforeValueLength > MaxXmlLength) break; - if (!property.HasNameAndValue) + if (string.IsNullOrEmpty(property.Name) || (!IncludeEmptyValue && StringHelpers.IsNullOrEmptyString(property.Value))) continue; var propertyTypeCode = property.TypeCode; From b6c5c0c6664dd3c93d22831dfb7349f635bda95c Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 12 Apr 2025 12:10:32 +0200 Subject: [PATCH 081/224] Layout - GetFormattedMessage with InvariantCulture (#5777) --- src/NLog/Internal/FormatHelper.cs | 6 +++++- src/NLog/LayoutRenderers/FuncLayoutRenderer.cs | 2 +- src/NLog/Layouts/Typed/Layout.cs | 3 +-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/NLog/Internal/FormatHelper.cs b/src/NLog/Internal/FormatHelper.cs index 0e5846eab6..5c71ecb343 100644 --- a/src/NLog/Internal/FormatHelper.cs +++ b/src/NLog/Internal/FormatHelper.cs @@ -71,13 +71,17 @@ internal static string TryFormatToString(object value, string format, IFormatPro { return formattable.ToString(format, formatProvider); } + else if (value is IConvertible convertible) + { + return convertible.ToString(formatProvider); + } else if (value is System.Collections.IEnumerable) { return null; } else { - return value?.ToString() ?? string.Empty; + return value?.ToString(); } } } diff --git a/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs b/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs index 0309d6ed63..4ccad30454 100644 --- a/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs @@ -104,7 +104,7 @@ private string GetStringValue(LogEventInfo logEvent) if (Format != MessageTemplates.ValueFormatter.FormatAsJson) { object value = RenderValue(logEvent); - string stringValue = FormatHelper.TryFormatToString(value, Format, GetFormatProvider(logEvent, null)); + string stringValue = FormatHelper.TryFormatToString(value, Format, GetFormatProvider(logEvent, Culture)); return stringValue; } return null; diff --git a/src/NLog/Layouts/Typed/Layout.cs b/src/NLog/Layouts/Typed/Layout.cs index 9f073f7b0e..3ce61f3aef 100644 --- a/src/NLog/Layouts/Typed/Layout.cs +++ b/src/NLog/Layouts/Typed/Layout.cs @@ -191,8 +191,7 @@ private object RenderObjectValue([CanBeNull] LogEventInfo logEvent, [CanBeNull] protected override string GetFormattedMessage(LogEventInfo logEvent) { var objectValue = IsFixed ? FixedObjectValue : RenderObjectValue(logEvent, null); - var formatProvider = logEvent.FormatProvider ?? LoggingConfiguration?.DefaultCultureInfo; - return Convert.ToString(objectValue, formatProvider); + return FormatHelper.TryFormatToString(objectValue, null, CultureInfo.InvariantCulture) ?? string.Empty; } /// From f92ae05c085a8c878c6b3250a60afa5bd97fe5ae Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 12 Apr 2025 16:21:06 +0200 Subject: [PATCH 082/224] NetworkTarget - Introduced option NoDelay to disable delayed ACK (#5779) --- .../NetworkSenders/INetworkSenderFactory.cs | 9 +-- .../NetworkSenders/NetworkSenderFactory.cs | 65 ++++++++++--------- .../NetworkSenders/TcpNetworkSender.cs | 17 +++-- .../Targets/NetworkTarget.cs | 11 +++- .../NetworkSenders/HttpNetworkSenderTests.cs | 6 +- .../NetworkSenders/TcpNetworkSenderTests.cs | 4 +- .../NetworkTargetTests.cs | 23 ++++++- 7 files changed, 82 insertions(+), 53 deletions(-) diff --git a/src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs b/src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs index 82da268fa9..3edbc6c782 100644 --- a/src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs +++ b/src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs @@ -46,16 +46,11 @@ internal interface INetworkSenderFactory /// Creates a new instance of the network sender based on a network URL. /// /// URL that determines the network sender to be created. - /// The maximum queue size. - /// The overflow action when reaching maximum queue size. - /// The maximum message size. - /// SSL protocols for TCP /// SSL Certificate override - /// KeepAliveTime for TCP - /// SendTimeout for TCP + /// NetworkTarget that requests the network connection. /// /// A newly created network sender. /// - QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQueueOverflowAction onQueueOverflow, int maxMessageSize, System.Security.Authentication.SslProtocols sslProtocols, X509Certificate2Collection sslCertificateOverride, TimeSpan keepAliveTime, TimeSpan sendTimeout); + QueuedNetworkSender Create(string url, X509Certificate2Collection sslCertificateOverride, NetworkTarget networkTarget); } } diff --git a/src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs b/src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs index 7f1e2ed456..6a7699cdb4 100644 --- a/src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs +++ b/src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs @@ -46,17 +46,18 @@ internal sealed class NetworkSenderFactory : INetworkSenderFactory public static readonly INetworkSenderFactory Default = new NetworkSenderFactory(); /// - public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQueueOverflowAction onQueueOverflow, int maxMessageSize, System.Security.Authentication.SslProtocols sslProtocols, X509Certificate2Collection sslCertificateOverride, TimeSpan keepAliveTime, TimeSpan sendTimeout) + public QueuedNetworkSender Create(string url, X509Certificate2Collection sslCertificateOverride, NetworkTarget networkTarget) { if (url.StartsWith("tcp://", StringComparison.OrdinalIgnoreCase)) { return new TcpNetworkSender(url, AddressFamily.Unspecified) { - MaxQueueSize = maxQueueSize, - OnQueueOverflow = onQueueOverflow, - SslProtocols = sslProtocols, - KeepAliveTime = keepAliveTime, - SendTimeout = sendTimeout, + MaxQueueSize = networkTarget.MaxQueueSize, + OnQueueOverflow = networkTarget.OnQueueOverflow, + NoDelay = networkTarget.NoDelay, + KeepAliveTime = TimeSpan.FromSeconds(networkTarget.KeepAliveTimeSeconds), + SendTimeout = TimeSpan.FromSeconds(networkTarget.SendTimeoutSeconds), + SslProtocols = networkTarget.SslProtocols, SslCertificateOverride = sslCertificateOverride, }; } @@ -65,11 +66,12 @@ public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQue { return new TcpNetworkSender(url, AddressFamily.InterNetwork) { - MaxQueueSize = maxQueueSize, - OnQueueOverflow = onQueueOverflow, - SslProtocols = sslProtocols, - KeepAliveTime = keepAliveTime, - SendTimeout = sendTimeout, + MaxQueueSize = networkTarget.MaxQueueSize, + OnQueueOverflow = networkTarget.OnQueueOverflow, + NoDelay = networkTarget.NoDelay, + KeepAliveTime = TimeSpan.FromSeconds(networkTarget.KeepAliveTimeSeconds), + SendTimeout = TimeSpan.FromSeconds(networkTarget.SendTimeoutSeconds), + SslProtocols = networkTarget.SslProtocols, SslCertificateOverride = sslCertificateOverride, }; } @@ -78,11 +80,12 @@ public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQue { return new TcpNetworkSender(url, AddressFamily.InterNetworkV6) { - MaxQueueSize = maxQueueSize, - OnQueueOverflow = onQueueOverflow, - SslProtocols = sslProtocols, - KeepAliveTime = keepAliveTime, - SendTimeout = sendTimeout, + MaxQueueSize = networkTarget.MaxQueueSize, + OnQueueOverflow = networkTarget.OnQueueOverflow, + NoDelay = networkTarget.NoDelay, + KeepAliveTime = TimeSpan.FromSeconds(networkTarget.KeepAliveTimeSeconds), + SendTimeout = TimeSpan.FromSeconds(networkTarget.SendTimeoutSeconds), + SslProtocols = networkTarget.SslProtocols, SslCertificateOverride = sslCertificateOverride, }; } @@ -91,9 +94,9 @@ public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQue { return new UdpNetworkSender(url, AddressFamily.Unspecified) { - MaxQueueSize = maxQueueSize, - OnQueueOverflow = onQueueOverflow, - MaxMessageSize = maxMessageSize, + MaxQueueSize = networkTarget.MaxQueueSize, + OnQueueOverflow = networkTarget.OnQueueOverflow, + MaxMessageSize = networkTarget.MaxMessageSize, }; } @@ -101,9 +104,9 @@ public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQue { return new UdpNetworkSender(url, AddressFamily.InterNetwork) { - MaxQueueSize = maxQueueSize, - OnQueueOverflow = onQueueOverflow, - MaxMessageSize = maxMessageSize, + MaxQueueSize = networkTarget.MaxQueueSize, + OnQueueOverflow = networkTarget.OnQueueOverflow, + MaxMessageSize = networkTarget.MaxMessageSize, }; } @@ -111,9 +114,9 @@ public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQue { return new UdpNetworkSender(url, AddressFamily.InterNetworkV6) { - MaxQueueSize = maxQueueSize, - OnQueueOverflow = onQueueOverflow, - MaxMessageSize = maxMessageSize, + MaxQueueSize = networkTarget.MaxQueueSize, + OnQueueOverflow = networkTarget.OnQueueOverflow, + MaxMessageSize = networkTarget.MaxMessageSize, }; } @@ -121,9 +124,9 @@ public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQue { return new HttpNetworkSender(url) { - MaxQueueSize = maxQueueSize, - OnQueueOverflow = onQueueOverflow, - SendTimeout = sendTimeout, + MaxQueueSize = networkTarget.MaxQueueSize, + OnQueueOverflow = networkTarget.OnQueueOverflow, + SendTimeout = TimeSpan.FromSeconds(networkTarget.SendTimeoutSeconds), SslCertificateOverride = sslCertificateOverride, }; } @@ -132,9 +135,9 @@ public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQue { return new HttpNetworkSender(url) { - MaxQueueSize = maxQueueSize, - OnQueueOverflow = onQueueOverflow, - SendTimeout = sendTimeout, + MaxQueueSize = networkTarget.MaxQueueSize, + OnQueueOverflow = networkTarget.OnQueueOverflow, + SendTimeout = TimeSpan.FromSeconds(networkTarget.SendTimeoutSeconds), SslCertificateOverride = sslCertificateOverride, }; } diff --git a/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs index 1272c47481..dfbca0a53a 100644 --- a/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs @@ -72,6 +72,8 @@ public TcpNetworkSender(string url, AddressFamily addressFamily) internal TimeSpan SendTimeout { get; set; } + internal bool NoDelay { get; set; } + /// /// Creates the socket with given parameters. /// @@ -79,20 +81,25 @@ public TcpNetworkSender(string url, AddressFamily addressFamily) /// The address family. /// Type of the socket. /// Type of the protocol. - /// Socket SendTimeout. + /// /// Instance of which represents the socket. - protected internal virtual ISocket CreateSocket(string host, AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, TimeSpan sendTimeout) + protected internal virtual ISocket CreateSocket(string host, AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType) { var socketProxy = new SocketProxy(addressFamily, socketType, protocolType); + if (NoDelay) + { + socketProxy.UnderlyingSocket.NoDelay = true; + } + if (KeepAliveTime.TotalSeconds >= 1.0 && EnableKeepAliveSuccessful != false) { EnableKeepAliveSuccessful = TryEnableKeepAlive(socketProxy.UnderlyingSocket, (int)KeepAliveTime.TotalSeconds); } - if (sendTimeout > TimeSpan.Zero) + if (SendTimeout > TimeSpan.Zero) { - socketProxy.UnderlyingSocket.SendTimeout = (int)sendTimeout.TotalMilliseconds; + socketProxy.UnderlyingSocket.SendTimeout = (int)SendTimeout.TotalMilliseconds; } if (SslProtocols != System.Security.Authentication.SslProtocols.None) @@ -179,7 +186,7 @@ protected override void DoInitialize() args.Completed += _socketOperationCompletedAsync; args.UserToken = null; - _socket = CreateSocket(uri.Host, args.RemoteEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp, SendTimeout); + _socket = CreateSocket(uri.Host, args.RemoteEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); base.BeginInitialize(); bool asyncOperation = false; diff --git a/src/NLog.Targets.Network/Targets/NetworkTarget.cs b/src/NLog.Targets.Network/Targets/NetworkTarget.cs index 135a308bce..eb493115c4 100644 --- a/src/NLog.Targets.Network/Targets/NetworkTarget.cs +++ b/src/NLog.Targets.Network/Targets/NetworkTarget.cs @@ -251,10 +251,15 @@ public LineEndingMode LineEnding public int KeepAliveTimeSeconds { get; set; } /// - /// The number of seconds a TCP socket send-operation will block before timeout error. Default wait forever when network cable unplugged and tcp-buffer becomes full. + /// The number of seconds a TCP socket send-operation will block before timeout error. Default = 100 secs (0 = wait forever when network cable unplugged and tcp-buffer becomes full). /// /// - public int SendTimeoutSeconds { get; set; } + public int SendTimeoutSeconds { get; set; } = 100; + + /// + /// Gets or sets whether to disable the delayed ACK timer, and avoid delay of 200 ms. Default = true. + /// + public bool NoDelay { get; set; } = true; /// /// Type of compression for protocol payload. Useful for UDP where datagram max-size is 8192 bytes. @@ -644,7 +649,7 @@ private NetworkSender CreateNetworkSender(string address, LogEventInfo logEventI var sslCertificatePassword = SslCertificatePassword?.Render(logEventInfo); var sslCertificateOverride = LoadSslCertificateFromFile(sslCertificateFile, sslCertificatePassword); - var sender = SenderFactory.Create(address, MaxQueueSize, OnQueueOverflow, MaxMessageSize, SslProtocols, sslCertificateOverride, TimeSpan.FromSeconds(KeepAliveTimeSeconds), TimeSpan.FromSeconds(SendTimeoutSeconds)); + var sender = SenderFactory.Create(address, sslCertificateOverride, this); sender.Initialize(); if (KeepConnection || LogEventDropped != null) { diff --git a/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs b/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs index 2f23142569..c1c139d796 100644 --- a/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkSenders/HttpNetworkSenderTests.cs @@ -89,7 +89,7 @@ public void HttpNetworkSenderViaNetworkTargetTest() Assert.Equal("HttpHappyPathTestLogger|test message1|", requestedString); Assert.Equal("POST", mock.Method); - networkSenderFactoryMock.Received(1).Create("http://test.with.mock", 1234, NetworkTargetQueueOverflowAction.Block, 0, SslProtocols.None, null, TimeSpan.Zero, TimeSpan.Zero); + networkSenderFactoryMock.Received(1).Create("http://test.with.mock", null, networkTarget); // Cleanup mock.Dispose(); @@ -135,7 +135,7 @@ public void HttpNetworkSenderViaNetworkTargetRecoveryTest() Assert.Equal("HttpHappyPathTestLogger|test message2|", requestedString); Assert.Equal("POST", mock.Method); - networkSenderFactoryMock.Received(1).Create("http://test.with.mock", 1234, NetworkTargetQueueOverflowAction.Block, 0, SslProtocols.None, null, TimeSpan.Zero, TimeSpan.Zero); // Only created one HttpNetworkSender + networkSenderFactoryMock.Received(1).Create("http://test.with.mock", null, networkTarget); // Only created one HttpNetworkSender // Cleanup mock.Dispose(); @@ -146,7 +146,7 @@ private static INetworkSenderFactory CreateNetworkSenderFactoryMock(WebRequestMo { var networkSenderFactoryMock = Substitute.For(); - networkSenderFactoryMock.Create(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) + networkSenderFactoryMock.Create(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(url => new HttpNetworkSender(url.Arg()) { HttpRequestFactory = new WebRequestFactoryMock(webRequestMock) diff --git a/tests/NLog.Targets.Network.Tests/NetworkSenders/TcpNetworkSenderTests.cs b/tests/NLog.Targets.Network.Tests/NetworkSenders/TcpNetworkSenderTests.cs index d098078213..fcf97e5b03 100644 --- a/tests/NLog.Targets.Network.Tests/NetworkSenders/TcpNetworkSenderTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkSenders/TcpNetworkSenderTests.cs @@ -142,7 +142,7 @@ public void TcpHappyPathTest() public void TcpProxyTest() { var sender = new TcpNetworkSender("tcp://foo:1234", AddressFamily.Unspecified); - var socket = sender.CreateSocket("foo", AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp, TimeSpan.Zero); + var socket = sender.CreateSocket("foo", AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); Assert.IsType(socket); } @@ -270,7 +270,7 @@ public MyTcpNetworkSender(string url, AddressFamily addressFamily) Log = new StringWriter(); } - protected internal override ISocket CreateSocket(string host, AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, TimeSpan sendTimeout) + protected internal override ISocket CreateSocket(string host, AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType) { return new MockSocket(addressFamily, socketType, protocolType, this); } diff --git a/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs index 0bc20cdbb0..399dac72fc 100644 --- a/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs @@ -1278,6 +1278,25 @@ public void SendTimeoutConfigTest(string sendTimeoutSeconds, int expected) logger.Info("Hello"); } + [Theory] + [InlineData("false", false)] + [InlineData("true", true)] + public void NoDelayConfigTest(string noDelay, bool expected) + { + var logFactory = new LogFactory().Setup().SetupExtensions(ext => ext.RegisterTarget()) + .LoadConfigurationFromXml($@" + + + + ").LogFactory; + + var target = logFactory.Configuration.FindTargetByName("target1"); + Assert.Equal(expected, target.NoDelay); + + var logger = logFactory.GetLogger("SendTimeoutSeconds"); + logger.Info("Hello"); + } + [Fact] public void Bug3990StackOverflowWhenUsingNLogViewerTarget() { @@ -1352,9 +1371,9 @@ internal sealed class MyQueudSenderFactory : INetworkSenderFactory internal StringWriter Log = new StringWriter(); private int _idCounter; - public QueuedNetworkSender Create(string url, int maxQueueSize, NetworkTargetQueueOverflowAction onQueueOverflow, int maxMessageSize, SslProtocols sslProtocols, System.Security.Cryptography.X509Certificates.X509Certificate2Collection sslCertificateOverride, TimeSpan keepAliveTime, TimeSpan sendTimeout) + public QueuedNetworkSender Create(string url, System.Security.Cryptography.X509Certificates.X509Certificate2Collection sslCertificateOverride, NetworkTarget networkTarget) { - var sender = new MyQueudNetworkSender(url, ++_idCounter, Log, this) { MaxQueueSize = maxQueueSize, OnQueueOverflow = onQueueOverflow }; + var sender = new MyQueudNetworkSender(url, ++_idCounter, Log, this) { MaxQueueSize = networkTarget.MaxQueueSize, OnQueueOverflow = networkTarget.OnQueueOverflow }; Senders.Add(sender); return sender; } From 64bad890aa999ed5d3fa5ad8536524bbc38b78d0 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 13 Apr 2025 12:56:30 +0200 Subject: [PATCH 083/224] JsonLayout - Changed SuppressSpaces to default true (#5781) --- src/NLog/Layouts/JSON/JsonArrayLayout.cs | 4 +- src/NLog/Layouts/JSON/JsonLayout.cs | 31 ++++++----- src/NLog/MessageTemplates/ValueFormatter.cs | 35 +++++++++++- src/NLog/Targets/DefaultJsonSerializer.cs | 17 +++--- src/NLog/Targets/JsonSerializeOptions.cs | 9 +++- .../LayoutRenderers/ExceptionTests.cs | 2 +- .../VariableLayoutRendererTests.cs | 2 +- .../Layouts/CompoundLayoutTests.cs | 3 +- .../Layouts/JsonArrayLayoutTests.cs | 8 +-- .../NLog.UnitTests/Layouts/JsonLayoutTests.cs | 54 +++++++++---------- .../LogMessageFormatterTests.cs | 16 +++--- tests/NLog.UnitTests/LoggerTests.cs | 6 +-- .../DefaultJsonSerializerClassTests.cs | 4 +- .../Targets/DefaultJsonSerializerTestsBase.cs | 14 ++--- 14 files changed, 122 insertions(+), 83 deletions(-) diff --git a/src/NLog/Layouts/JSON/JsonArrayLayout.cs b/src/NLog/Layouts/JSON/JsonArrayLayout.cs index 7e2bef9de7..b2efca31cc 100644 --- a/src/NLog/Layouts/JSON/JsonArrayLayout.cs +++ b/src/NLog/Layouts/JSON/JsonArrayLayout.cs @@ -65,10 +65,10 @@ private IJsonConverter JsonConverter public IList Items { get; } = new List(); /// - /// Gets or sets the option to suppress the extra spaces in the output json + /// Gets or sets the option to suppress the extra spaces in the output json. Default: true /// /// - public bool SuppressSpaces { get; set; } + public bool SuppressSpaces { get; set; } = true; /// /// Gets or sets the option to render the empty object value {} diff --git a/src/NLog/Layouts/JSON/JsonLayout.cs b/src/NLog/Layouts/JSON/JsonLayout.cs index 29e31d77a9..bb20aaf3ac 100644 --- a/src/NLog/Layouts/JSON/JsonLayout.cs +++ b/src/NLog/Layouts/JSON/JsonLayout.cs @@ -56,7 +56,7 @@ public class JsonLayout : Layout private LimitRecursionJsonConvert JsonConverter { - get => _jsonConverter ?? (_jsonConverter = new LimitRecursionJsonConvert(MaxRecursionLimit, ResolveService())); + get => _jsonConverter ?? (_jsonConverter = new LimitRecursionJsonConvert(MaxRecursionLimit, SuppressSpaces, ResolveService())); set => _jsonConverter = value; } private LimitRecursionJsonConvert _jsonConverter; @@ -73,11 +73,11 @@ private sealed class LimitRecursionJsonConvert : IJsonConverter private readonly Targets.DefaultJsonSerializer _serializer; private readonly Targets.JsonSerializeOptions _serializerOptions; - public LimitRecursionJsonConvert(int maxRecursionLimit, IJsonConverter converter) + public LimitRecursionJsonConvert(int maxRecursionLimit, bool suppressSpaces, IJsonConverter converter) { _converter = converter; _serializer = converter as Targets.DefaultJsonSerializer; - _serializerOptions = new Targets.JsonSerializeOptions() { MaxRecursionLimit = Math.Max(0, maxRecursionLimit) }; + _serializerOptions = new Targets.JsonSerializeOptions() { MaxRecursionLimit = Math.Max(0, maxRecursionLimit), SuppressSpaces = suppressSpaces, SanitizeDictionaryKeys = true }; } public bool SerializeObject(object value, StringBuilder builder) @@ -111,7 +111,7 @@ public JsonLayout() private readonly List _attributes = new List(); /// - /// Gets or sets the option to suppress the extra spaces in the output json + /// Gets or sets the option to suppress the extra spaces in the output json. Default: true /// /// public bool SuppressSpaces @@ -126,7 +126,7 @@ public bool SuppressSpaces } } } - private bool _suppressSpaces; + private bool _suppressSpaces = true; /// /// Gets or sets the option to render the empty object value {} @@ -147,6 +147,8 @@ public bool IndentJson if (_indentJson != value) { _indentJson = value; + if (_indentJson) + _suppressSpaces = false; RefreshJsonDelimiters(); } } @@ -253,14 +255,15 @@ protected override void InitializeLayout() _precalculateLayouts = (IncludeScopeProperties || IncludeEventProperties) ? null : ResolveLayoutPrecalculation(Attributes.Select(atr => atr.Layout)); - if (_attributes.Count > 0) + foreach (var attribute in _attributes) { - foreach (var attribute in _attributes) + if (!attribute.Encode && attribute.Layout is JsonLayout jsonLayout) { - if (!attribute.IncludeEmptyValue && !attribute.Encode && attribute.Layout is JsonLayout jsonLayout && !jsonLayout._renderEmptyObject.HasValue) - { + if (!attribute.IncludeEmptyValue && !jsonLayout._renderEmptyObject.HasValue) jsonLayout.RenderEmptyObject = false; - } + + if (!SuppressSpaces || IndentJson) + jsonLayout.SuppressSpaces = false; } } } @@ -378,10 +381,10 @@ private void BeginJsonProperty(StringBuilder sb, string propName, bool beginJson sb.Append(_completeJsonPropertyName); } - private string _beginJsonMessage = "{ \""; - private string _completeJsonMessage = " }"; - private string _beginJsonPropertyName = ", \""; - private string _completeJsonPropertyName = "\": "; + private string _beginJsonMessage = "{\""; + private string _completeJsonMessage = "}"; + private string _beginJsonPropertyName = ",\""; + private string _completeJsonPropertyName = "\":"; private void RefreshJsonDelimiters() { diff --git a/src/NLog/MessageTemplates/ValueFormatter.cs b/src/NLog/MessageTemplates/ValueFormatter.cs index 6a7c5996bb..bffde0509f 100644 --- a/src/NLog/MessageTemplates/ValueFormatter.cs +++ b/src/NLog/MessageTemplates/ValueFormatter.cs @@ -52,9 +52,42 @@ internal sealed class ValueFormatter : IValueFormatter private readonly IServiceProvider _serviceProvider; private readonly bool _legacyStringQuotes; - private IJsonConverter JsonConverter => _jsonConverter ?? (_jsonConverter = _serviceProvider.GetService()); + private IJsonConverter JsonConverter + { + get => _jsonConverter ?? (_jsonConverter = JsonConverterWithSpaces.CreateJsonConverter(_serviceProvider.GetService())); + } private IJsonConverter _jsonConverter; + private sealed class JsonConverterWithSpaces : IJsonConverter + { + private readonly Targets.DefaultJsonSerializer _serializer; + private readonly Targets.JsonSerializeOptions _serializerOptions; + private readonly Targets.JsonSerializeOptions _exceptionSerializerOptions; + + public static IJsonConverter CreateJsonConverter(IJsonConverter jsonConverter) + { + if (jsonConverter is Targets.DefaultJsonSerializer defaultJsonConverter) + return new JsonConverterWithSpaces(defaultJsonConverter); + else + return jsonConverter; + } + + private JsonConverterWithSpaces(Targets.DefaultJsonSerializer jsonConverter) + { + _serializer = jsonConverter; + _serializerOptions = new Targets.JsonSerializeOptions() { SuppressSpaces = false }; + _exceptionSerializerOptions = new Targets.JsonSerializeOptions() { SuppressSpaces = false, SanitizeDictionaryKeys = true }; + } + + public bool SerializeObject(object value, StringBuilder builder) + { + if (value is Exception) + return _serializer.SerializeObject(value, builder, _exceptionSerializerOptions); + else + return _serializer.SerializeObject(value, builder, _serializerOptions); + } + } + public ValueFormatter([NotNull] IServiceProvider serviceProvider, bool legacyStringQuotes) { _serviceProvider = serviceProvider; diff --git a/src/NLog/Targets/DefaultJsonSerializer.cs b/src/NLog/Targets/DefaultJsonSerializer.cs index ed15057c28..b780d794df 100644 --- a/src/NLog/Targets/DefaultJsonSerializer.cs +++ b/src/NLog/Targets/DefaultJsonSerializer.cs @@ -686,7 +686,8 @@ private bool SerializeObjectProperties(ObjectReflectionCache.ObjectPropertyList { destination.Append('{'); - bool first = true; + string jsonPropertyDelimeter = null; + foreach (var propertyValue in objectPropertyList) { var originalLength = destination.Length; @@ -696,10 +697,7 @@ private bool SerializeObjectProperties(ObjectReflectionCache.ObjectPropertyList if (!propertyValue.HasNameAndValue) continue; - if (!first) - { - destination.Append(", "); - } + destination.Append(jsonPropertyDelimeter); QuoteValue(destination, propertyValue.Name); destination.Append(':'); @@ -708,19 +706,18 @@ private bool SerializeObjectProperties(ObjectReflectionCache.ObjectPropertyList if (objTypeCode != TypeCode.Object) { SerializeSimpleTypeCodeValue((IConvertible)propertyValue.Value, objTypeCode, destination, options); - first = false; } else { if (!SerializeObject(propertyValue.Value, destination, options, objectsInPath, depth + 1)) { destination.Length = originalLength; - } - else - { - first = false; + continue; } } + + if (jsonPropertyDelimeter is null) + jsonPropertyDelimeter = options.SuppressSpaces ? "," : ", "; } catch { diff --git a/src/NLog/Targets/JsonSerializeOptions.cs b/src/NLog/Targets/JsonSerializeOptions.cs index 4a1b72e59c..b6a7593f96 100644 --- a/src/NLog/Targets/JsonSerializeOptions.cs +++ b/src/NLog/Targets/JsonSerializeOptions.cs @@ -63,7 +63,7 @@ public class JsonSerializeOptions public string Format { get; set; } /// - /// Should non-ascii characters be encoded + /// Should non-ascii characters be encoded. Default: false /// public bool EscapeUnicode { get; set; } @@ -75,10 +75,15 @@ public class JsonSerializeOptions public bool EscapeForwardSlash { get; set; } /// - /// Serialize enum as string value + /// Serialize enum as string value. Default: false /// public bool EnumAsInteger { get; set; } + /// + /// Gets or sets the option to suppress the extra spaces in the output json. Default: true + /// + public bool SuppressSpaces { get; set; } = true; + /// /// Should dictionary keys be sanitized. All characters must either be letters, numbers or underscore character (_). /// diff --git a/tests/NLog.UnitTests/LayoutRenderers/ExceptionTests.cs b/tests/NLog.UnitTests/LayoutRenderers/ExceptionTests.cs index 1e3bc0f69e..1b316e6a8a 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/ExceptionTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/ExceptionTests.cs @@ -352,7 +352,7 @@ public void InnerExceptionTest() } [Fact] - public void InnerExceptionTest_Serialize() + public void InnerExceptionTest_JsonSerialize() { var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@" diff --git a/tests/NLog.UnitTests/LayoutRenderers/VariableLayoutRendererTests.cs b/tests/NLog.UnitTests/LayoutRenderers/VariableLayoutRendererTests.cs index 4f80fff2fb..0f3be19574 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/VariableLayoutRendererTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/VariableLayoutRendererTests.cs @@ -137,7 +137,7 @@ public void Var_with_layout(string variableName, string layoutStyle) logger.Debug("msg"); var lastMessage = GetDebugLastMessage("debug", logFactory); - Assert.Equal("{ \"short date\": \"Debug\", \"message\": \"msg\" }", lastMessage); + Assert.Equal("{\"short date\":\"Debug\",\"message\":\"msg\"}", lastMessage); } [Fact] diff --git a/tests/NLog.UnitTests/Layouts/CompoundLayoutTests.cs b/tests/NLog.UnitTests/Layouts/CompoundLayoutTests.cs index 441c24c440..6f9a161be8 100644 --- a/tests/NLog.UnitTests/Layouts/CompoundLayoutTests.cs +++ b/tests/NLog.UnitTests/Layouts/CompoundLayoutTests.cs @@ -52,6 +52,7 @@ public void CodeCompoundLayoutIsRenderedCorrectly() new SimpleLayout("|Before| "), new JsonLayout { + SuppressSpaces = false, Attributes = { new JsonAttribute("short_date", "${shortdate}"), @@ -153,7 +154,7 @@ public void XmlCompoundLayoutIsRenderedCorrectly() - + diff --git a/tests/NLog.UnitTests/Layouts/JsonArrayLayoutTests.cs b/tests/NLog.UnitTests/Layouts/JsonArrayLayoutTests.cs index 45b8b441d8..1d3a70c461 100644 --- a/tests/NLog.UnitTests/Layouts/JsonArrayLayoutTests.cs +++ b/tests/NLog.UnitTests/Layouts/JsonArrayLayoutTests.cs @@ -59,7 +59,7 @@ public void JsonArrayLayoutRendering() Message = "hello\n world", }; - Assert.Equal("[ \"2010-01-01T12:34:56Z\", \"Info\", \"hello\\n world\" ]", jsonLayout.Render(logEventInfo)); + Assert.Equal("[\"2010-01-01T12:34:56Z\",\"Info\",\"hello\\n world\"]", jsonLayout.Render(logEventInfo)); } [Fact] @@ -90,7 +90,7 @@ public void JsonArrayLayoutRenderingFromXml() }; logFactory.GetCurrentClassLogger().Log(logEventInfo); - logFactory.AssertDebugLastMessage("[ \"2010-01-01T12:34:56Z\", \"Info\", \"hello\\n world\" ]"); + logFactory.AssertDebugLastMessage("[\"2010-01-01T12:34:56Z\",\"Info\",\"hello\\n world\"]"); } [Fact] @@ -135,7 +135,7 @@ public void JsonArrayLayoutRenderingNotEmpty() Message = "hello\n world", }; - Assert.Equal("[ ]", jsonLayout.Render(logEventInfo)); + Assert.Equal("[]", jsonLayout.Render(logEventInfo)); } [Fact] @@ -180,7 +180,7 @@ public void JsonArrayLayoutObjectRendering() Message = "hello\n world", }; - Assert.Equal("[ { \"date\": \"2010-01-01 12:34:56.0000\" }, { \"level\": \"Info\" }, { \"message\": \"hello\\n world\" } ]", jsonLayout.Render(logEventInfo)); + Assert.Equal("[{\"date\":\"2010-01-01 12:34:56.0000\"},{\"level\":\"Info\"},{\"message\":\"hello\\n world\"}]", jsonLayout.Render(logEventInfo)); } } } diff --git a/tests/NLog.UnitTests/Layouts/JsonLayoutTests.cs b/tests/NLog.UnitTests/Layouts/JsonLayoutTests.cs index 48f99b662f..539ab75b23 100644 --- a/tests/NLog.UnitTests/Layouts/JsonLayoutTests.cs +++ b/tests/NLog.UnitTests/Layouts/JsonLayoutTests.cs @@ -43,8 +43,8 @@ namespace NLog.UnitTests.Layouts public class JsonLayoutTests : NLogTestBase { - private const string ExpectedIncludeAllPropertiesWithExcludes = "{ \"StringProp\": \"ValueA\", \"IntProp\": 123, \"DoubleProp\": 123.123, \"DecimalProp\": 123.123, \"BoolProp\": true, \"NullProp\": null, \"DateTimeProp\": \"2345-01-23T12:34:56Z\" }"; - private const string ExpectedExcludeEmptyPropertiesWithExcludes = "{ \"StringProp\": \"ValueA\", \"IntProp\": 123, \"DoubleProp\": 123.123, \"DecimalProp\": 123.123, \"BoolProp\": true, \"DateTimeProp\": \"2345-01-23T12:34:56Z\", \"NoEmptyProp4\": \"hello\\\"\" }"; + private const string ExpectedIncludeAllPropertiesWithExcludes = "{\"StringProp\":\"ValueA\",\"IntProp\":123,\"DoubleProp\":123.123,\"DecimalProp\":123.123,\"BoolProp\":true,\"NullProp\":null,\"DateTimeProp\":\"2345-01-23T12:34:56Z\"}"; + private const string ExpectedExcludeEmptyPropertiesWithExcludes = "{\"StringProp\":\"ValueA\",\"IntProp\":123,\"DoubleProp\":123.123,\"DecimalProp\":123.123,\"BoolProp\":true,\"DateTimeProp\":\"2345-01-23T12:34:56Z\",\"NoEmptyProp4\":\"hello\\\"\"}"; [Fact] public void JsonLayoutRendering() @@ -66,7 +66,7 @@ public void JsonLayoutRendering() Message = "hello, world" }; - Assert.Equal("{ \"date\": \"2010-01-01 12:34:56.0000\", \"level\": \"Info\", \"message\": \"hello, world\" }", jsonLayout.Render(logEventInfo)); + Assert.Equal("{\"date\":\"2010-01-01 12:34:56.0000\",\"level\":\"Info\",\"message\":\"hello, world\"}", jsonLayout.Render(logEventInfo)); } [Fact] @@ -157,7 +157,7 @@ public void JsonLayoutRenderingAndEncodingSpecialCharacters() Message = "\"hello, world\"" }; - Assert.Equal("{ \"date\": \"2010-01-01 12:34:56.0000\", \"level\": \"Info\", \"message\": \"\\\"hello, world\\\"\" }", jsonLayout.Render(logEventInfo)); + Assert.Equal("{\"date\":\"2010-01-01 12:34:56.0000\",\"level\":\"Info\",\"message\":\"\\\"hello, world\\\"\"}", jsonLayout.Render(logEventInfo)); } [Fact] @@ -180,7 +180,7 @@ public void JsonLayoutRenderingAndEncodingLineBreaks() Message = "hello,\n\r world" }; - Assert.Equal("{ \"date\": \"2010-01-01 12:34:56.0000\", \"level\": \"Info\", \"message\": \"hello,\\n\\r world\" }", jsonLayout.Render(logEventInfo)); + Assert.Equal("{\"date\":\"2010-01-01 12:34:56.0000\",\"level\":\"Info\",\"message\":\"hello,\\n\\r world\"}", jsonLayout.Render(logEventInfo)); } [Fact] @@ -203,7 +203,7 @@ public void JsonLayoutRenderingAndNotEncodingMessageAttribute() Message = "{ \"hello\" : \"world\" }" }; - Assert.Equal("{ \"date\": \"2010-01-01 12:34:56.0000\", \"level\": \"Info\", \"message\": { \"hello\" : \"world\" } }", jsonLayout.Render(logEventInfo)); + Assert.Equal("{\"date\":\"2010-01-01 12:34:56.0000\",\"level\":\"Info\",\"message\":{ \"hello\" : \"world\" }}", jsonLayout.Render(logEventInfo)); } [Fact] @@ -226,7 +226,7 @@ public void JsonLayoutRenderingAndEncodingMessageAttribute() Message = "{ \"hello\" : \"world\" }" }; - Assert.Equal("{ \"date\": \"2010-01-01 12:34:56.0000\", \"level\": \"Info\", \"message\": \"{ \\\"hello\\\" : \\\"world\\\" }\" }", jsonLayout.Render(logEventInfo)); + Assert.Equal("{\"date\":\"2010-01-01 12:34:56.0000\",\"level\":\"Info\",\"message\":\"{ \\\"hello\\\" : \\\"world\\\" }\"}", jsonLayout.Render(logEventInfo)); } [Fact] @@ -249,7 +249,7 @@ public void JsonLayoutValueTypeAttribute() Message = "{ \"hello\" : \"world\" }" }; - Assert.Equal("{ \"date\": \"2010-01-01T12:34:56Z\", \"level\": \"Info\", \"message\": \"{ \\\"hello\\\" : \\\"world\\\" }\" }", jsonLayout.Render(logEventInfo)); + Assert.Equal("{\"date\":\"2010-01-01T12:34:56Z\",\"level\":\"Info\",\"message\":\"{ \\\"hello\\\" : \\\"world\\\" }\"}", jsonLayout.Render(logEventInfo)); } [Fact] @@ -345,7 +345,7 @@ public void NestedJsonAttrTest() }; var json = jsonLayout.Render(logEventInfo); - Assert.Equal("{ \"type\": \"NLog.NLogRuntimeException\", \"message\": \"test\", \"innerException\": { \"type\": \"System.NullReferenceException\", \"message\": \"null is bad!\" } }", json); + Assert.Equal("{\"type\":\"NLog.NLogRuntimeException\",\"message\":\"test\",\"innerException\":{\"type\":\"System.NullReferenceException\",\"message\":\"null is bad!\"}}", json); } [Fact] @@ -378,7 +378,7 @@ public void NestedJsonAttrDoesNotRenderEmptyLiteralIfRenderEmptyObjectIsFalseTes }; var json = jsonLayout.Render(logEventInfo); - Assert.Equal("{ \"type\": \"NLog.NLogRuntimeException\", \"message\": \"test\" }", json); + Assert.Equal("{\"type\":\"NLog.NLogRuntimeException\",\"message\":\"test\"}", json); } @@ -412,7 +412,7 @@ public void NestedJsonAttrRendersEmptyLiteralIfRenderEmptyObjectIsTrueTest() }; var json = jsonLayout.Render(logEventInfo); - Assert.Equal("{ \"type\": \"NLog.NLogRuntimeException\", \"message\": \"test\", \"innerException\": { } }", json); + Assert.Equal("{\"type\":\"NLog.NLogRuntimeException\",\"message\":\"test\",\"innerException\":{}}", json); } @@ -467,7 +467,7 @@ public void NestedJsonAttrTestFromXML() }; var json = jsonLayout.Render(logEventInfo); - Assert.Equal("{ \"time\": \"2016-10-30 13:30:55.0000\", \"level\": \"INFO\", \"nested\": { \"message\": \"this is message\", \"exception\": \"test\" } }", json); + Assert.Equal("{\"time\":\"2016-10-30 13:30:55.0000\",\"level\":\"INFO\",\"nested\":{\"message\":\"this is message\",\"exception\":\"test\"}}", json); } [Fact] @@ -496,7 +496,7 @@ public void PropertyKeyWithQuote() var logEventInfo = new LogEventInfo(); logEventInfo.Properties.Add(@"fo""o", "bar"); - Assert.Equal(@"{ ""fo\""o"": ""bar"" }", jsonLayout.Render(logEventInfo)); + Assert.Equal(@"{""fo\""o"":""bar""}", jsonLayout.Render(logEventInfo)); } [Fact] @@ -505,7 +505,7 @@ public void AttributerKeyWithQuote() var jsonLayout = new JsonLayout(); jsonLayout.Attributes.Add(new JsonAttribute(@"fo""o", "bar")); - Assert.Equal(@"{ ""fo\""o"": ""bar"" }", jsonLayout.Render(LogEventInfo.CreateNullEvent())); + Assert.Equal(@"{""fo\""o"":""bar""}", jsonLayout.Render(LogEventInfo.CreateNullEvent())); } [Fact] @@ -575,7 +575,7 @@ public void IncludeAllJsonPropertiesMaxRecursionLimit() data = new Dictionary() { { 42, "Hello" } } }; - Assert.Equal(@"{ ""Message"": {""data"":{}} }", jsonLayout.Render(logEventInfo)); + Assert.Equal(@"{""Message"":{""data"":{}}}", jsonLayout.Render(logEventInfo)); } [Fact] @@ -843,7 +843,7 @@ public void IncludeMdlcJsonNestedProperties() logFactory.Flush(); - logFactory.AssertDebugLastMessage(@"{ ""scope"": " + ExpectedIncludeAllPropertiesWithExcludes + " }"); + logFactory.AssertDebugLastMessage(@"{""scope"":" + ExpectedIncludeAllPropertiesWithExcludes + "}"); } /// @@ -942,7 +942,7 @@ public void IncludeAllJsonPropertiesMutableNestedXml() logFactory.Flush(); // Assert - logFactory.AssertDebugLastMessage(@"{ ""properties"": " + ExpectedIncludeAllPropertiesWithExcludes + " }"); + logFactory.AssertDebugLastMessage(@"{""properties"":" + ExpectedIncludeAllPropertiesWithExcludes + "}"); } /// @@ -973,7 +973,7 @@ public void SerializeObjectRecursionSingle() logger.Debug(logEventInfo1); - logFactory.AssertDebugLastMessage("{ \"nestedObject\": [{\"val\":1, \"val2\":\"value2\"},{\"val3\":3, \"val4\":\"value4\"}] }"); + logFactory.AssertDebugLastMessage("{\"nestedObject\":[{\"val\":1,\"val2\":\"value2\"},{\"val3\":3,\"val4\":\"value4\"}]}"); var logEventInfo2 = new LogEventInfo(); @@ -981,7 +981,7 @@ public void SerializeObjectRecursionSingle() logger.Debug(logEventInfo2); - logFactory.AssertDebugLastMessage("{ \"nestedObject\": {\"val\":1, \"val2\":\"value2\"} }"); + logFactory.AssertDebugLastMessage("{\"nestedObject\":{\"val\":1,\"val2\":\"value2\"}}"); var logEventInfo3 = new LogEventInfo(); @@ -989,7 +989,7 @@ public void SerializeObjectRecursionSingle() logger.Debug(logEventInfo3); - logFactory.AssertDebugLastMessage("{ \"nestedObject\": [[\"{ val = 1, val2 = value2 }\"]] }"); // Allows nested collection, but then only ToString + logFactory.AssertDebugLastMessage("{\"nestedObject\":[[\"{ val = 1, val2 = value2 }\"]]}"); // Allows nested collection, but then only ToString } [Fact] @@ -1017,7 +1017,7 @@ public void SerializeObjectRecursionZero() logger.Debug(logEventInfo1); - logFactory.AssertDebugLastMessage("{ \"nestedObject\": [\"{ val = 1, val2 = value2 }\",\"{ val3 = 3, val4 = value5 }\"] }"); // Allows single collection recursion + logFactory.AssertDebugLastMessage("{\"nestedObject\":[\"{ val = 1, val2 = value2 }\",\"{ val3 = 3, val4 = value5 }\"]}"); // Allows single collection recursion var logEventInfo2 = new LogEventInfo(); @@ -1025,7 +1025,7 @@ public void SerializeObjectRecursionZero() logger.Debug(logEventInfo2); - logFactory.AssertDebugLastMessage("{ \"nestedObject\": \"{ val = 1, val2 = value2 }\" }"); // Never object recursion, only ToString + logFactory.AssertDebugLastMessage("{\"nestedObject\":\"{ val = 1, val2 = value2 }\"}"); // Never object recursion, only ToString var logEventInfo3 = new LogEventInfo(); @@ -1033,7 +1033,7 @@ public void SerializeObjectRecursionZero() logger.Debug(logEventInfo3); - logFactory.AssertDebugLastMessage("{ \"nestedObject\": [[]] }"); // No support for nested collections + logFactory.AssertDebugLastMessage("{\"nestedObject\":[[]]}"); // No support for nested collections } [Fact] @@ -1060,7 +1060,7 @@ public void EncodesInvalidCharacters() logger.Debug(logEventInfo1); - logFactory.AssertDebugLastMessage("{ \"InvalidCharacters\": [\"|\",\"#\",\"{\",\"}\",\"%\",\"&\",\"\\\"\",\"~\",\"+\",\"\\\\\",\"/\",\":\",\"*\",\"?\",\"<\",\">\"] }"); + logFactory.AssertDebugLastMessage("{\"InvalidCharacters\":[\"|\",\"#\",\"{\",\"}\",\"%\",\"&\",\"\\\"\",\"~\",\"+\",\"\\\\\",\"/\",\":\",\"*\",\"?\",\"<\",\">\"]}"); } [Fact] @@ -1093,7 +1093,7 @@ public void EncodesInvalidDoubles() logger.Debug(logEventInfo1); - logFactory.AssertDebugLastMessage("{ \"DoubleNaN\": \"NaN\", \"DoubleInfPositive\": \"Infinity\", \"DoubleInfNegative\": \"-Infinity\", \"FloatNaN\": \"NaN\", \"FloatInfPositive\": \"Infinity\", \"FloatInfNegative\": \"-Infinity\" }"); + logFactory.AssertDebugLastMessage("{\"DoubleNaN\":\"NaN\",\"DoubleInfPositive\":\"Infinity\",\"DoubleInfNegative\":\"-Infinity\",\"FloatNaN\":\"NaN\",\"FloatInfPositive\":\"Infinity\",\"FloatInfNegative\":\"-Infinity\"}"); } [Fact] @@ -1119,7 +1119,7 @@ public void EscapeForwardSlashDefaultTest() logEventInfo1.Properties.Add("myurl", "http://hello.world.com/"); logger.Debug(logEventInfo1); - logFactory.AssertDebugLastMessage("{ \"myurl1\": \"http://hello.world.com/\", \"myurl\": \"http://hello.world.com/\" }"); + logFactory.AssertDebugLastMessage("{\"myurl1\":\"http://hello.world.com/\",\"myurl\":\"http://hello.world.com/\"}"); } [Fact] @@ -1140,7 +1140,7 @@ public void SkipInvalidJsonPropertyValues() logEventInfo.Properties["RequestId"] = expectedValue; var actualValue = jsonLayout.Render(logEventInfo); - Assert.Equal($"{{ \"BadObject\": {{\"Recursive\":[\"Hello\"], \"WeirdProperty\":\"System.Action\"}}, \"RequestId\": \"{expectedValue}\" }}", actualValue); + Assert.Equal($"{{\"BadObject\":{{\"Recursive\":[\"Hello\"],\"WeirdProperty\":\"System.Action\"}},\"RequestId\":\"{expectedValue}\"}}", actualValue); } class BadObject diff --git a/tests/NLog.UnitTests/LogMessageFormatterTests.cs b/tests/NLog.UnitTests/LogMessageFormatterTests.cs index 15a5ba98e6..3116d2caa8 100644 --- a/tests/NLog.UnitTests/LogMessageFormatterTests.cs +++ b/tests/NLog.UnitTests/LogMessageFormatterTests.cs @@ -41,7 +41,7 @@ namespace NLog.UnitTests public class LogMessageFormatterTests : NLogTestBase { [Fact] - public void ExtensionsLoggingFormatTest() + public void ExtensionsLoggingFormatJsonTest() { LogEventInfo logEventInfo = new LogEventInfo(LogLevel.Info, "MyLogger", "Login request from {Username} for {Application}", new[] { @@ -74,7 +74,7 @@ public void ExtensionsLoggingFormatTest() var logger = logFactory.GetLogger("A"); logger.Log(logEventInfo); - logFactory.AssertDebugLastMessage("{ \"LogMessage\": \"Login request from {Username} for {Application}\", \"Username\": \"John\", \"Application\": \"BestApplicationEver\" }"); + logFactory.AssertDebugLastMessage("{\"LogMessage\":\"Login request from {Username} for {Application}\",\"Username\":\"John\",\"Application\":\"BestApplicationEver\"}"); Assert.Equal("Login request from John for BestApplicationEver", logEventInfo.FormattedMessage); @@ -85,7 +85,7 @@ public void ExtensionsLoggingFormatTest() } [Fact] - public void ExtensionsLoggingPreFormatTest() + public void ExtensionsLoggingPreFormatJsonTest() { LogEventInfo logEventInfo1 = new LogEventInfo(LogLevel.Info, "MyLogger", "Login request from John for BestApplicationEver", "Login request from {Username} for {Application}", new[] { @@ -128,7 +128,7 @@ public void ExtensionsLoggingPreFormatTest() var result1 = debugTarget.Layout.Render(logEventInfo1); Assert.NotSame(result1, debugTarget.LastMessage); - logFactory.AssertDebugLastMessage("{ \"LogMessage\": \"Login request from {Username} for {Application}\", \"Username\": \"John\", \"Application\": \"BestApplicationEver\" }"); + logFactory.AssertDebugLastMessage("{\"LogMessage\":\"Login request from {Username} for {Application}\",\"Username\":\"John\",\"Application\":\"BestApplicationEver\"}"); Assert.Equal("Login request from John for BestApplicationEver", logEventInfo1.FormattedMessage); @@ -139,7 +139,7 @@ public void ExtensionsLoggingPreFormatTest() } [Fact] - public void NormalStringFormatTest() + public void NormalStringFormatJsonTest() { LogEventInfo logEventInfo = new LogEventInfo(LogLevel.Info, "MyLogger", null, "{0:X} - Login request from {1} for {2} with userid {0}", new object[] { @@ -164,7 +164,7 @@ public void NormalStringFormatTest() var logger = logFactory.GetLogger("A"); logger.Log(logEventInfo); - logFactory.AssertDebugLastMessage("{ \"LogMessage\": \"{0:X} - Login request from {1} for {2} with userid {0}\" }"); + logFactory.AssertDebugLastMessage("{\"LogMessage\":\"{0:X} - Login request from {1} for {2} with userid {0}\"}"); Assert.Equal("2A - Login request from John for BestApplicationEver with userid 42", logEventInfo.FormattedMessage); @@ -175,7 +175,7 @@ public void NormalStringFormatTest() } [Fact] - public void MessageTemplateFormatTest() + public void MessageTemplateFormatJsonTest() { LogEventInfo logEventInfo = new LogEventInfo(LogLevel.Info, "MyLogger", null, "Login request from {@Username} for {Application:l}", new object[] { @@ -199,7 +199,7 @@ public void MessageTemplateFormatTest() var logger = logFactory.GetLogger("A"); logger.Log(logEventInfo); - logFactory.AssertDebugLastMessage("{ \"LogMessage\": \"Login request from {@Username} for {Application:l}\", \"Username\": \"John\", \"Application\": \"BestApplicationEver\" }"); + logFactory.AssertDebugLastMessage("{\"LogMessage\":\"Login request from {@Username} for {Application:l}\",\"Username\":\"John\",\"Application\":\"BestApplicationEver\"}"); Assert.Equal("Login request from \"John\" for BestApplicationEver", logEventInfo.FormattedMessage); diff --git a/tests/NLog.UnitTests/LoggerTests.cs b/tests/NLog.UnitTests/LoggerTests.cs index 8caf3b9eb1..46233ef8a4 100644 --- a/tests/NLog.UnitTests/LoggerTests.cs +++ b/tests/NLog.UnitTests/LoggerTests.cs @@ -2548,7 +2548,7 @@ public void TestStructuredProperties_json() logger.Error("Login request from {@Username} for {$Application}", new Person("John"), "BestApplicationEver"); - AssertDebugLastMessage("debug", "{ \"LogMessage\": \"Login request from {@Username} for {$Application}\", \"Username\": {\"Name\":\"John\"}, \"Application\": \"BestApplicationEver\" }"); + AssertDebugLastMessage("debug", "{\"LogMessage\":\"Login request from {@Username} for {$Application}\",\"Username\":{\"Name\":\"John\"},\"Application\":\"BestApplicationEver\"}"); } /// @@ -2579,7 +2579,7 @@ public void TestStructuredProperties_json_async() logger.Error("Login request from {@Username} for {$Application}", new Person("John"), sb); sb.Clear(); LogManager.Flush(); - AssertDebugLastMessage("debug", "{ \"LogMessage\": \"Login request from {@Username} for {$Application}\", \"Username\": {\"Name\":\"John\"}, \"Application\": \"BestApplicationEver\" }"); + AssertDebugLastMessage("debug", "{\"LogMessage\":\"Login request from {@Username} for {$Application}\",\"Username\":{\"Name\":\"John\"},\"Application\":\"BestApplicationEver\"}"); } /// @@ -2594,7 +2594,7 @@ public void TestStructuredProperties_json_compound() - + diff --git a/tests/NLog.UnitTests/Targets/DefaultJsonSerializerClassTests.cs b/tests/NLog.UnitTests/Targets/DefaultJsonSerializerClassTests.cs index 21c5cf98b3..ffb7b15869 100644 --- a/tests/NLog.UnitTests/Targets/DefaultJsonSerializerClassTests.cs +++ b/tests/NLog.UnitTests/Targets/DefaultJsonSerializerClassTests.cs @@ -110,7 +110,7 @@ public void IExcludedInterfaceSerializer_RegistersSerializeAsToString_InvokesToS var jsonSerializer = new DefaultJsonSerializer(logFactory.ServiceRepository); jsonSerializer.SerializeObject(testObject, sb, options); const string expectedValue = - @"{""S"":""sample"", ""Excluded"":""Skipped"", ""Included"":{""IncludedString"":""serialized""}}"; + @"{""S"":""sample"",""Excluded"":""Skipped"",""Included"":{""IncludedString"":""serialized""}}"; Assert.Equal(expectedValue, sb.ToString()); } @@ -128,7 +128,7 @@ public void ExcludedClassSerializer_RegistersSerializeAsToString_InvokesToString var jsonSerializer = new DefaultJsonSerializer(logFactory.ServiceRepository); jsonSerializer.SerializeObject(testObject, sb, options); const string expectedValue = - @"{""S"":""sample"", ""Excluded"":""Skipped"", ""Included"":{""IncludedString"":""serialized""}}"; + @"{""S"":""sample"",""Excluded"":""Skipped"",""Included"":{""IncludedString"":""serialized""}}"; Assert.Equal(expectedValue, sb.ToString()); } } diff --git a/tests/NLog.UnitTests/Targets/DefaultJsonSerializerTestsBase.cs b/tests/NLog.UnitTests/Targets/DefaultJsonSerializerTestsBase.cs index 743a32f4af..6d78f8800d 100644 --- a/tests/NLog.UnitTests/Targets/DefaultJsonSerializerTestsBase.cs +++ b/tests/NLog.UnitTests/Targets/DefaultJsonSerializerTestsBase.cs @@ -372,7 +372,7 @@ public void SerializeExpandoDict_Test() dictionary.Add("key 2", 1.3m); dictionary.Add("level", LogLevel.Info); var actual = SerializeObject(dictionary); - Assert.Equal("{\"key 2\":1.3, \"level\":\"Info\"}", actual); + Assert.Equal("{\"key 2\":1.3,\"level\":\"Info\"}", actual); } [Fact] @@ -393,7 +393,7 @@ public void SerializeReadOnlyExpandoDict_Test() var readonlyDictionary = new Internal.ReadOnlyExpandoTestDictionary(dictionary); var actual = SerializeObject(readonlyDictionary); - Assert.Equal("{\"key 2\":1.3, \"level\":\"Info\"}", actual); + Assert.Equal("{\"key 2\":1.3,\"level\":\"Info\"}", actual); } #endif @@ -476,7 +476,7 @@ public void SerializeObject_Test() object1.Linked = object2; var actual = SerializeObject(object1); - Assert.Equal("{\"Name\":\"object1\", \"Linked\":{\"Name\":\"object2\"}}", actual); + Assert.Equal("{\"Name\":\"object1\",\"Linked\":{\"Name\":\"object2\"}}", actual); } [Fact] @@ -499,7 +499,7 @@ public void SerializeListObject_Test() var list = new[] { object1, object2 }; var actual = SerializeObject(list); - Assert.Equal("[{\"Name\":\"object1\", \"Linked\":{\"Name\":\"object2\"}},{\"Name\":\"object2\"}]", actual); + Assert.Equal("[{\"Name\":\"object1\",\"Linked\":{\"Name\":\"object2\"}},{\"Name\":\"object2\"}]", actual); } [Fact] @@ -535,7 +535,7 @@ public void SerializeAnonymousObject_Test() { var object1 = new { Id = 123, Name = "test name" }; var actual = SerializeObject(object1); - Assert.Equal("{\"Id\":123, \"Name\":\"test name\"}", actual); + Assert.Equal("{\"Id\":123,\"Name\":\"test name\"}", actual); } /// @@ -572,7 +572,7 @@ public void SerializeExpandoObject_Test() object1.Id = 123; object1.Name = "test name"; var actual = SerializeObject(object1); - Assert.Equal("{\"Id\":123, \"Name\":\"test name\"}", actual); + Assert.Equal("{\"Id\":123,\"Name\":\"test name\"}", actual); } [Fact] @@ -580,7 +580,7 @@ public void SerializeDynamicObject_Test() { var object1 = new MyDynamicClass(); var actual = SerializeObject(object1); - Assert.Equal("{\"Id\":123, \"Name\":\"test name\"}", actual); + Assert.Equal("{\"Id\":123,\"Name\":\"test name\"}", actual); } private class MyDynamicClass : DynamicObject From a1251166508616615eec815a403355548acefff6 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 24 Apr 2025 11:33:19 +0200 Subject: [PATCH 084/224] XmlLoggingConfiguration - AutoReload available by default as before (#5782) --- build.ps1 | 1 - run-tests.ps1 | 8 - .../NLog.AutoReloadConfig.csproj | 80 ------ src/NLog.AutoReloadConfig/README.md | 15 - .../SetupBuilderAutoReloadExtensions.cs | 267 ------------------ src/NLog.sln | 14 - src/NLog/Config/LoggingConfiguration.cs | 3 + src/NLog/Config/XmlLoggingConfiguration.cs | 210 +++++++++++++- .../Internal}/MultiFileWatcher.cs | 0 .../NLog.AutoReloadConfig.Tests.csproj | 26 -- .../Config}/AutoReloadTests.cs | 18 +- tests/NLog.UnitTests/Config/ReloadTests.cs | 84 +++--- .../NLog.UnitTests/ConfigFileLocatorTests.cs | 2 + 13 files changed, 258 insertions(+), 470 deletions(-) delete mode 100644 src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj delete mode 100644 src/NLog.AutoReloadConfig/README.md delete mode 100644 src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs rename src/{NLog.AutoReloadConfig => NLog/Internal}/MultiFileWatcher.cs (100%) delete mode 100644 tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj rename tests/{NLog.AutoReloadConfig.Tests => NLog.UnitTests/Config}/AutoReloadTests.cs (94%) diff --git a/build.ps1 b/build.ps1 index 4946576e7c..a544742e35 100644 --- a/build.ps1 +++ b/build.ps1 @@ -35,7 +35,6 @@ function create-package($packageName, $targetFrameworks) { exit $LastExitCode } } -create-package 'NLog.AutoReloadConfig' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Database' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.Mail' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.Network' '"net45;net46;netstandard2.0"' diff --git a/run-tests.ps1 b/run-tests.ps1 index 0c798a06c8..668c24bd70 100644 --- a/run-tests.ps1 +++ b/run-tests.ps1 @@ -28,10 +28,6 @@ if ($isWindows -or $Env:WinDir) if (-Not $LastExitCode -eq 0) { exit $LastExitCode } - dotnet test ./tests/NLog.AutoReloadConfig.Tests/ --configuration release - if (-Not $LastExitCode -eq 0) - { exit $LastExitCode } - dotnet test ./tests/NLog.RegEx.Tests/ --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } @@ -87,10 +83,6 @@ else if (-Not $LastExitCode -eq 0) { exit $LastExitCode } - dotnet test ./tests/NLog.AutoReloadConfig.Tests/ --framework net6.0 --configuration release - if (-Not $LastExitCode -eq 0) - { exit $LastExitCode } - dotnet test ./tests/NLog.RegEx.Tests/ --framework net6.0 --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } diff --git a/src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj b/src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj deleted file mode 100644 index afb7fe325f..0000000000 --- a/src/NLog.AutoReloadConfig/NLog.AutoReloadConfig.csproj +++ /dev/null @@ -1,80 +0,0 @@ - - - - net35;net46;netstandard2.0 - - NLog.AutoReloadConfig - NLog - NLog.AutoReloadConfig enables AutoReload support for NLog.config XML-files - NLog.AutoReloadConfig v$(ProductVersion) - $(ProductVersion) - Jarek Kowalski,Kim Christensen,Julian Verdurmen - $([System.DateTime]::Now.ToString(yyyy)) - Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ - - - AutoReload Docs: - https://github.com/NLog/NLog/wiki/Configuration-file#automatic-reconfiguration - - README.md - NLog;AutoReload;logging;log - N.png - https://nlog-project.org/ - BSD-3-Clause - git - https://github.com/NLog/NLog.git - - true - 5.0.0.0 - ..\NLog.snk - true - - true - true - true - true - true - copyused - - - - NLog.AutoReloadConfig for .NET Framework 4.6 - true - - - - NLog.AutoReloadConfig for .NET Framework 4.5 - true - - - - NLog.AutoReloadConfig for .NET Framework 3.5 - true - - - - NLog.AutoReloadConfig for NetStandard 2.0 - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/NLog.AutoReloadConfig/README.md b/src/NLog.AutoReloadConfig/README.md deleted file mode 100644 index 25c9a4be1f..0000000000 --- a/src/NLog.AutoReloadConfig/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# NLog AutoReload Config - -NLog AutoReload Config Monitor for activating AutoReload support for `NLog.config` XML-files. - -If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) - -## Register Extension - -AutoReload Config Monitor must be enabled from code using [fluent configuration API](https://github.com/NLog/NLog/wiki/Fluent-Configuration-API): - -```csharp -LogManager.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(); -``` - -Notice if using `appsettings.json` for NLog configuration, then this extension is not required for supporting `AutoReload`. \ No newline at end of file diff --git a/src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs b/src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs deleted file mode 100644 index d390a74687..0000000000 --- a/src/NLog.AutoReloadConfig/SetupBuilderAutoReloadExtensions.cs +++ /dev/null @@ -1,267 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// -// - -namespace NLog -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Threading; - using NLog.Common; - using NLog.Config; - using NLog.Internal; - - /// - /// Setup()-extension to activate AutoReload support by monitoring - /// - public static class SetupBuilderAutoReloadExtensions - { - private static readonly Dictionary _watchers = new Dictionary(); - - /// - /// Activates AutoReload support by monitoring - /// - /// - /// Hooks into and setup file-monitoring for NLog.config file-changes. - /// - public static ISetupBuilder SetupMonitorForAutoReload(this ISetupBuilder setupBuilder) - { - AutoReloadConfigFileWatcher fileWatcher = null; - lock (_watchers) - { - _watchers.TryGetValue(setupBuilder.LogFactory, out fileWatcher); - } - - if (fileWatcher is null || fileWatcher.IsDisposed) - { - InternalLogger.Info("AutoReload Config File Monitor starting"); - fileWatcher = new AutoReloadConfigFileWatcher(setupBuilder.LogFactory); - lock (_watchers) - { - _watchers[setupBuilder.LogFactory] = fileWatcher; - } - } - - setupBuilder.LoadConfiguration(cfg => - { - var fileNamesToWatch = cfg.Configuration.FileNamesToWatch?.ToList(); - if (fileNamesToWatch?.Count > 0 || cfg.Configuration is XmlLoggingConfiguration) - { - fileWatcher.RefreshFileWatcher(fileNamesToWatch ?? System.Linq.Enumerable.Empty()); - } - else if (cfg.Configuration.LoggingRules.Count == 0 && cfg.Configuration.AllTargets.Count == 0) - { - cfg.Configuration = null; - } - }); - return setupBuilder; - } - - private sealed class AutoReloadConfigFileWatcher : IDisposable - { - private readonly LogFactory _logFactory; - private readonly MultiFileWatcher _fileWatcher = new MultiFileWatcher(); - private readonly object _lockObject = new object(); - private Timer _reloadTimer; - private bool _isDisposing; - - internal bool IsDisposed => _isDisposing; - - public AutoReloadConfigFileWatcher(LogFactory logFactory) - { - _logFactory = logFactory; - _logFactory.ConfigurationChanged += LogFactory_ConfigurationChanged; - _fileWatcher.FileChanged += FileWatcher_FileChanged; - } - - private void LogFactory_ConfigurationChanged(object sender, LoggingConfigurationChangedEventArgs e) - { - try - { - if (_isDisposing) - return; - - if (e.ActivatedConfiguration is null) - { - InternalLogger.Info("AutoReload Config File Monitor stopping, since no active configuration"); - Dispose(); - } - else - { - InternalLogger.Debug("AutoReload Config File Monitor refreshing after configuration changed"); - var fileNamesToWatch = e.ActivatedConfiguration?.FileNamesToWatch ?? Enumerable.Empty(); - _fileWatcher.Watch(fileNamesToWatch); - } - } - catch (Exception ex) - { - InternalLogger.Error(ex, "AutoReload Config File Monitor failed to refresh after configuration changed."); - - if (LogManager.ThrowExceptions || _logFactory.ThrowExceptions) - { - throw; - } - } - } - - private void FileWatcher_FileChanged(object sender, System.IO.FileSystemEventArgs e) - { - lock (_lockObject) - { - if (_isDisposing) - return; - - var reloadTimer = _reloadTimer; - if (reloadTimer is null) - { - var currentConfig = _logFactory.Configuration; - if (currentConfig is null) - return; - - _reloadTimer = new Timer((s) => ReloadTimer(s), currentConfig, 1000, Timeout.Infinite); - } - else - { - reloadTimer.Change(1000, Timeout.Infinite); - } - } - } - - private void ReloadTimer(object state) - { - if (_isDisposing) - { - return; //timer was disposed already. - } - - LoggingConfiguration oldConfig = (LoggingConfiguration)state; - - InternalLogger.Info("AutoReload Config File Monitor reloading configuration..."); - - lock (_lockObject) - { - if (_isDisposing) - { - return; //timer was disposed already. - } - - var currentTimer = _reloadTimer; - if (currentTimer != null) - { - _reloadTimer = null; - currentTimer.Dispose(); - } - } - - LoggingConfiguration newConfig = null; - - try - { - var currentConfig = _logFactory.Configuration; - if (!ReferenceEquals(currentConfig, oldConfig)) - { - InternalLogger.Debug("AutoReload Config File Monitor skipping reload, since existing NLog config has changed."); - return; - } - - newConfig = oldConfig.Reload(); - if (newConfig is null || ReferenceEquals(newConfig, oldConfig)) - { - InternalLogger.Debug("AutoReload Config File Monitor skipping reload, since new configuration has not changed."); - return; - } - - currentConfig = _logFactory.Configuration; - if (!ReferenceEquals(currentConfig, oldConfig)) - { - InternalLogger.Debug("AutoReload Config File Monitor skipping reload, since existing NLog config has changed."); - return; - } - } - catch (Exception exception) - { - InternalLogger.Warn(exception, "AutoReload Config File Monitor failed to reload NLog LoggingConfiguration."); - return; - } - - try - { - TryUnwatchConfigFile(); - _logFactory.Configuration = newConfig; // Will trigger LogFactory_ConfigurationChanged - } - catch (Exception exception) - { - InternalLogger.Warn(exception, "AutoReload Config File Monitor failed to activate new NLog LoggingConfiguration."); - _fileWatcher.Watch(oldConfig.FileNamesToWatch); - } - } - - public void RefreshFileWatcher(IEnumerable fileNamesToWatch) - { - _fileWatcher.Watch(fileNamesToWatch); - } - - public void Dispose() - { - _isDisposing = true; - _logFactory.ConfigurationChanged -= LogFactory_ConfigurationChanged; - _fileWatcher.FileChanged -= FileWatcher_FileChanged; - lock (_lockObject) - { - var reloadTimer = _reloadTimer; - _reloadTimer = null; - reloadTimer?.Dispose(); - } - _fileWatcher.Dispose(); - } - - private void TryUnwatchConfigFile() - { - try - { - _fileWatcher?.StopWatching(); - } - catch (Exception exception) - { - InternalLogger.Warn(exception, "AutoReload Config File Monitor failed to stop file watcher."); - - if (LogManager.ThrowExceptions || _logFactory.ThrowExceptions) - { - throw; - } - } - } - } - } -} diff --git a/src/NLog.sln b/src/NLog.sln index 214283edea..bf2cf113c9 100644 --- a/src/NLog.sln +++ b/src/NLog.sln @@ -65,10 +65,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.RegEx", "NLog.RegEx\NL EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.RegEx.Tests", "..\tests\NLog.RegEx.Tests\NLog.RegEx.Tests.csproj", "{7C77DA2A-DF9A-4F4C-91BF-A593B31D7619}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.AutoReloadConfig", "NLog.AutoReloadConfig\NLog.AutoReloadConfig.csproj", "{FB2DFD9F-5EB1-4BBF-99F3-38C2E997206E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.AutoReloadConfig.Tests", "..\tests\NLog.AutoReloadConfig.Tests\NLog.AutoReloadConfig.Tests.csproj", "{47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.ConcurrentFile", "NLog.Targets.ConcurrentFile\NLog.Targets.ConcurrentFile.csproj", "{C54C900A-2FAC-4482-BFFB-D4A9F6AB3619}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLog.Targets.ConcurrentFile.Tests", "..\tests\NLog.Targets.ConcurrentFile.Tests\NLog.Targets.ConcurrentFile.Tests.csproj", "{A869B720-AB81-4AA9-94B4-12C5CF490FFE}" @@ -167,14 +163,6 @@ Global {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619}.Release|Any CPU.Build.0 = Release|Any CPU - {FB2DFD9F-5EB1-4BBF-99F3-38C2E997206E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FB2DFD9F-5EB1-4BBF-99F3-38C2E997206E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FB2DFD9F-5EB1-4BBF-99F3-38C2E997206E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FB2DFD9F-5EB1-4BBF-99F3-38C2E997206E}.Release|Any CPU.Build.0 = Release|Any CPU - {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A}.Release|Any CPU.Build.0 = Release|Any CPU {C54C900A-2FAC-4482-BFFB-D4A9F6AB3619}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C54C900A-2FAC-4482-BFFB-D4A9F6AB3619}.Debug|Any CPU.Build.0 = Debug|Any CPU {C54C900A-2FAC-4482-BFFB-D4A9F6AB3619}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -222,8 +210,6 @@ Global {38828090-4953-4EF5-929C-8063FD4BCCC9} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {A87BFA04-DBFD-44F4-8223-A02AC0E85C7F} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {7C77DA2A-DF9A-4F4C-91BF-A593B31D7619} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} - {FB2DFD9F-5EB1-4BBF-99F3-38C2E997206E} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} - {47F3A0BB-E7FB-41B5-8172-B8F2DC5C2E7A} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {C54C900A-2FAC-4482-BFFB-D4A9F6AB3619} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {A869B720-AB81-4AA9-94B4-12C5CF490FFE} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {68CE5BB3-DF9D-4774-9B66-6F7378BC04F5} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} diff --git a/src/NLog/Config/LoggingConfiguration.cs b/src/NLog/Config/LoggingConfiguration.cs index d16b433021..a817992fa5 100644 --- a/src/NLog/Config/LoggingConfiguration.cs +++ b/src/NLog/Config/LoggingConfiguration.cs @@ -36,6 +36,7 @@ namespace NLog.Config using System; using System.Collections.Generic; using System.Collections.ObjectModel; + using System.ComponentModel; using System.Globalization; using System.Linq; using System.Threading; @@ -103,6 +104,8 @@ public LoggingConfiguration(LogFactory logFactory) /// /// Gets the collection of file names which should be watched for changes by NLog. /// + [Obsolete("NLog LogFactory no longer supports FileWatcher. Marked obsolete with NLog v6")] + [EditorBrowsable(EditorBrowsableState.Never)] public virtual IEnumerable FileNamesToWatch => ArrayHelper.Empty(); /// diff --git a/src/NLog/Config/XmlLoggingConfiguration.cs b/src/NLog/Config/XmlLoggingConfiguration.cs index 409fb5ab4e..1cc3a50b91 100644 --- a/src/NLog/Config/XmlLoggingConfiguration.cs +++ b/src/NLog/Config/XmlLoggingConfiguration.cs @@ -38,6 +38,7 @@ namespace NLog.Config using System.ComponentModel; using System.IO; using System.Linq; + using System.Threading; using System.Xml; using JetBrains.Annotations; using NLog.Common; @@ -52,6 +53,7 @@ namespace NLog.Config /// public class XmlLoggingConfiguration : LoggingConfigurationParser { + private static readonly Dictionary _watchers = new Dictionary(); private readonly Dictionary _fileMustAutoReloadLookup = new Dictionary(StringComparer.OrdinalIgnoreCase); private string _originalFileName; @@ -147,13 +149,7 @@ public static XmlLoggingConfiguration CreateFromXmlString(string xml, LogFactory /// public bool AutoReload { - get - { - if (_fileMustAutoReloadLookup.Count == 0) - return false; - else - return _fileMustAutoReloadLookup.Values.Any(mustAutoReload => mustAutoReload); - } + get => AutoReloadFileNames.Any(); set { var autoReloadFiles = _fileMustAutoReloadLookup.Keys.ToList(); @@ -167,17 +163,22 @@ public bool AutoReload /// This is the list of configuration files processed. /// If the autoReload attribute is not set it returns empty collection. /// - public override IEnumerable FileNamesToWatch + public IEnumerable AutoReloadFileNames { get { if (_fileMustAutoReloadLookup.Count == 0) return ArrayHelper.Empty(); - else + else return _fileMustAutoReloadLookup.Where(entry => entry.Value).Select(entry => entry.Key); } } + /// + [Obsolete("Replaced by AutoReloadFileNames. Marked obsolete with NLog v6")] + [EditorBrowsable(EditorBrowsableState.Never)] + public override IEnumerable FileNamesToWatch => AutoReloadFileNames; + /// /// Loads the NLog LoggingConfiguration from its original configuration file and returns the new object. /// @@ -196,6 +197,51 @@ public override LoggingConfiguration Reload() return base.Reload(); } + /// + protected internal override void OnConfigurationAssigned(LogFactory logFactory) + { + base.OnConfigurationAssigned(logFactory); + + try + { + var configFactory = logFactory ?? LogFactory ?? NLog.LogManager.LogFactory; + + AutoReloadConfigFileWatcher fileWatcher = null; + lock (_watchers) + { + _watchers.TryGetValue(configFactory, out fileWatcher); + } + + if (logFactory is null || !AutoReload) + { + if (fileWatcher != null) + { + InternalLogger.Info("AutoReload Config File Monitor stopping, since no active configuration"); + fileWatcher.Dispose(); + } + } + else + { + InternalLogger.Debug("AutoReload Config File Monitor refreshing after configuration changed"); + if (fileWatcher is null || fileWatcher.IsDisposed) + { + InternalLogger.Info("AutoReload Config File Monitor starting"); + fileWatcher = new AutoReloadConfigFileWatcher(configFactory); + lock (_watchers) + { + _watchers[configFactory] = fileWatcher; + } + } + + fileWatcher.RefreshFileWatcher(AutoReloadFileNames); + } + } + catch (Exception ex) + { + InternalLogger.Error(ex, "AutoReload Config File Monitor failed to refresh after configuration changed."); + } + } + /// /// Obsolete and replaced by and with NLog v5.2. /// @@ -512,5 +558,151 @@ public override string ToString() { return $"{base.ToString()}, FilePath={_originalFileName}"; } + + + private sealed class AutoReloadConfigFileWatcher : IDisposable + { + private readonly LogFactory _logFactory; + private readonly MultiFileWatcher _fileWatcher = new MultiFileWatcher(); + private readonly object _lockObject = new object(); + private Timer _reloadTimer; + private bool _isDisposing; + + internal bool IsDisposed => _isDisposing; + + public AutoReloadConfigFileWatcher(LogFactory logFactory) + { + _logFactory = logFactory; + _fileWatcher.FileChanged += FileWatcher_FileChanged; + } + + private void FileWatcher_FileChanged(object sender, System.IO.FileSystemEventArgs e) + { + lock (_lockObject) + { + if (_isDisposing) + return; + + var reloadTimer = _reloadTimer; + if (reloadTimer is null) + { + var currentConfig = _logFactory.Configuration; + if (currentConfig is null) + return; + + _reloadTimer = new Timer((s) => ReloadTimer(s), currentConfig, 1000, Timeout.Infinite); + } + else + { + reloadTimer.Change(1000, Timeout.Infinite); + } + } + } + + private void ReloadTimer(object state) + { + if (_isDisposing) + { + return; //timer was disposed already. + } + + LoggingConfiguration oldConfig = (LoggingConfiguration)state; + + InternalLogger.Info("AutoReload Config File Monitor reloading configuration..."); + + lock (_lockObject) + { + if (_isDisposing) + { + return; //timer was disposed already. + } + + var currentTimer = _reloadTimer; + if (currentTimer != null) + { + _reloadTimer = null; + currentTimer.Dispose(); + } + } + + LoggingConfiguration newConfig = null; + + try + { + var currentConfig = _logFactory.Configuration; + if (!ReferenceEquals(currentConfig, oldConfig)) + { + InternalLogger.Debug("AutoReload Config File Monitor skipping reload, since existing NLog config has changed."); + return; + } + + newConfig = oldConfig.Reload(); + if (newConfig is null || ReferenceEquals(newConfig, oldConfig)) + { + InternalLogger.Debug("AutoReload Config File Monitor skipping reload, since new configuration has not changed."); + return; + } + + currentConfig = _logFactory.Configuration; + if (!ReferenceEquals(currentConfig, oldConfig)) + { + InternalLogger.Debug("AutoReload Config File Monitor skipping reload, since existing NLog config has changed."); + return; + } + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "AutoReload Config File Monitor failed to reload NLog LoggingConfiguration."); + return; + } + + try + { + TryUnwatchConfigFile(); + _logFactory.Configuration = newConfig; + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "AutoReload Config File Monitor failed to activate new NLog LoggingConfiguration."); + _fileWatcher.Watch((oldConfig as XmlLoggingConfiguration)?.AutoReloadFileNames ?? ArrayHelper.Empty()); + + } + } + + public void RefreshFileWatcher(IEnumerable fileNamesToWatch) + { + _fileWatcher.Watch(fileNamesToWatch); + } + + public void Dispose() + { + _isDisposing = true; + _fileWatcher.FileChanged -= FileWatcher_FileChanged; + lock (_lockObject) + { + var reloadTimer = _reloadTimer; + _reloadTimer = null; + reloadTimer?.Dispose(); + } + _fileWatcher.Dispose(); + } + + private void TryUnwatchConfigFile() + { + try + { + _fileWatcher?.StopWatching(); + } + catch (Exception exception) + { + InternalLogger.Warn(exception, "AutoReload Config File Monitor failed to stop file watcher."); + + if (LogManager.ThrowExceptions || _logFactory.ThrowExceptions) + { + throw; + } + } + } + } } } diff --git a/src/NLog.AutoReloadConfig/MultiFileWatcher.cs b/src/NLog/Internal/MultiFileWatcher.cs similarity index 100% rename from src/NLog.AutoReloadConfig/MultiFileWatcher.cs rename to src/NLog/Internal/MultiFileWatcher.cs diff --git a/tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj b/tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj deleted file mode 100644 index 07399530c0..0000000000 --- a/tests/NLog.AutoReloadConfig.Tests/NLog.AutoReloadConfig.Tests.csproj +++ /dev/null @@ -1,26 +0,0 @@ - - - - 17.0 - net462 - net462;net6.0 - - false - - Full - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - diff --git a/tests/NLog.AutoReloadConfig.Tests/AutoReloadTests.cs b/tests/NLog.UnitTests/Config/AutoReloadTests.cs similarity index 94% rename from tests/NLog.AutoReloadConfig.Tests/AutoReloadTests.cs rename to tests/NLog.UnitTests/Config/AutoReloadTests.cs index ee4299e4af..31ede65d13 100644 --- a/tests/NLog.AutoReloadConfig.Tests/AutoReloadTests.cs +++ b/tests/NLog.UnitTests/Config/AutoReloadTests.cs @@ -32,7 +32,7 @@ // -namespace NLog.AutoReloadConfig.Tests +namespace NLog.UnitTests.Config { using System; using System.IO; @@ -71,7 +71,7 @@ public void TestNoAutoReload() string configFilePath = Path.Combine(tempDir, nameof(TestNoAutoReload) + ".nlog"); WriteConfigFile(configFilePath, config1); - var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(configFilePath).GetCurrentClassLogger(); + var logger = logFactory.Setup().LoadConfigurationFromFile(configFilePath).GetCurrentClassLogger(); Assert.False(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); @@ -119,7 +119,7 @@ public void TestAutoReloadOnFileChange() string configFilePath = Path.Combine(tempDir, nameof(TestAutoReloadOnFileChange) + ".nlog"); WriteConfigFile(configFilePath, config1); - var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(configFilePath).GetCurrentClassLogger(); + var logger = logFactory.Setup().LoadConfigurationFromFile(configFilePath).GetCurrentClassLogger(); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); @@ -179,7 +179,7 @@ public void TestAutoReloadOnFileMove() WriteConfigFile(configFilePath, config1); string otherFilePath = Path.Combine(tempDir, "other.nlog"); - var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(configFilePath).GetCurrentClassLogger(); + var logger = logFactory.Setup().LoadConfigurationFromFile(configFilePath).GetCurrentClassLogger(); logger.Debug("aaa"); AssertDebugLastMessage("aaa", logFactory); @@ -246,7 +246,7 @@ public void TestAutoReloadOnFileCopy() WriteConfigFile(configFilePath, config1); string otherFilePath = Path.Combine(tempPath, "other.nlog"); - var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(configFilePath).GetCurrentClassLogger(); + var logger = logFactory.Setup().LoadConfigurationFromFile(configFilePath).GetCurrentClassLogger(); logger.Debug("aaa"); AssertDebugLastMessage("aaa", logFactory); @@ -316,7 +316,7 @@ public void TestIncludedConfigNoReload() string includedConfigFilePath = Path.Combine(tempDir, "included.nlog"); WriteConfigFile(includedConfigFilePath, includedConfig1); - var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(mainConfigFilePath).GetCurrentClassLogger(); + var logger = logFactory.Setup().LoadConfigurationFromFile(mainConfigFilePath).GetCurrentClassLogger(); logger.Debug("aaa"); AssertDebugLastMessage("aaa", logFactory); @@ -375,7 +375,7 @@ public void TestIncludedConfigReload() string includedConfigFilePath = Path.Combine(tempDir, "included.nlog"); WriteConfigFile(includedConfigFilePath, includedConfig1); - var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(mainConfigFilePath).GetCurrentClassLogger(); + var logger = logFactory.Setup().LoadConfigurationFromFile(mainConfigFilePath).GetCurrentClassLogger(); logger.Debug("aaa"); AssertDebugLastMessage("aaa", logFactory); @@ -440,7 +440,7 @@ public void TestMainConfigReload() string included2ConfigFilePath = Path.Combine(tempDir, "included2.nlog"); WriteConfigFile(included2ConfigFilePath, included2Config1); - var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(mainConfigFilePath).GetCurrentClassLogger(); + var logger = logFactory.Setup().LoadConfigurationFromFile(mainConfigFilePath).GetCurrentClassLogger(); logger.Debug("aaa"); AssertDebugLastMessage("aaa", logFactory); @@ -504,7 +504,7 @@ public void TestMainConfigReloadIncludedConfigNoReload() string included2ConfigFilePath = Path.Combine(tempDir, "included2.nlog"); WriteConfigFile(included2ConfigFilePath, included2Config1); - var logger = logFactory.Setup().SetupMonitorForAutoReload().LoadConfigurationFromFile(mainConfigFilePath).GetCurrentClassLogger(); + var logger = logFactory.Setup().LoadConfigurationFromFile(mainConfigFilePath).GetCurrentClassLogger(); logger.Debug("aaa"); AssertDebugLastMessage("aaa", logFactory); diff --git a/tests/NLog.UnitTests/Config/ReloadTests.cs b/tests/NLog.UnitTests/Config/ReloadTests.cs index 19499dd76c..6980068a30 100644 --- a/tests/NLog.UnitTests/Config/ReloadTests.cs +++ b/tests/NLog.UnitTests/Config/ReloadTests.cs @@ -95,8 +95,8 @@ public void TestAutoReloadOnFileChange(bool useExplicitFileLoading) logger.Debug("aaa"); AssertDebugLastMessage("debug", "aaa", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(configFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault()); WriteConfigFileAndReload(logFactory, configFilePath, badConfig); @@ -104,8 +104,8 @@ public void TestAutoReloadOnFileChange(bool useExplicitFileLoading) // Assert that config1 is still loaded. AssertDebugLastMessage("debug", "bbb", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(configFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault()); WriteConfigFileAndReload(logFactory, configFilePath, config2); @@ -113,8 +113,8 @@ public void TestAutoReloadOnFileChange(bool useExplicitFileLoading) // Assert that config2 is loaded. AssertDebugLastMessage("debug", "[ccc]", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(configFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault()); } finally { @@ -158,8 +158,8 @@ public void TestAutoReloadOnFileMove(bool useExplicitFileLoading) logger.Debug("aaa"); AssertDebugLastMessage("debug", "aaa", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(configFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault()); File.Move(configFilePath, otherFilePath); logFactory.Setup().ReloadConfiguration(); @@ -167,8 +167,8 @@ public void TestAutoReloadOnFileMove(bool useExplicitFileLoading) // Assert that config1 is still loaded. AssertDebugLastMessage("debug", "bbb", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(configFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault()); WriteConfigFile(otherFilePath, config2); File.Move(otherFilePath, configFilePath); @@ -178,8 +178,8 @@ public void TestAutoReloadOnFileMove(bool useExplicitFileLoading) // Assert that config2 is loaded. AssertDebugLastMessage("debug", "[ccc]", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(configFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault()); } finally { @@ -222,8 +222,8 @@ public void TestAutoReloadOnFileCopy(bool useExplicitFileLoading) logger.Debug("aaa"); AssertDebugLastMessage("debug", "aaa", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(configFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault()); File.Delete(configFilePath); logFactory.Setup().ReloadConfiguration(); @@ -232,8 +232,8 @@ public void TestAutoReloadOnFileCopy(bool useExplicitFileLoading) // Assert that config1 is still loaded. AssertDebugLastMessage("debug", "bbb", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(configFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault()); WriteConfigFile(otherFilePath, config2); File.Copy(otherFilePath, configFilePath); @@ -244,8 +244,8 @@ public void TestAutoReloadOnFileCopy(bool useExplicitFileLoading) // Assert that config2 is loaded. AssertDebugLastMessage("debug", "[ccc]", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(configFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(configFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault()); } finally { @@ -296,7 +296,7 @@ public void TestIncludedConfigNoReload(bool useExplicitFileLoading) logger.Debug("aaa"); AssertDebugLastMessage("debug", "aaa", logFactory); Assert.False(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Empty(logFactory.Configuration.FileNamesToWatch); + Assert.Empty(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); WriteConfigFileAndReload(logFactory, mainConfigFilePath, mainConfig2); @@ -304,7 +304,7 @@ public void TestIncludedConfigNoReload(bool useExplicitFileLoading) // Assert that mainConfig2 has been loaded. AssertDebugLastMessage("debug", "", logFactory); Assert.False(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Empty(logFactory.Configuration.FileNamesToWatch); + Assert.Empty(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); logger.Info("bbb"); AssertDebugLastMessage("debug", "bbb", logFactory); @@ -315,7 +315,7 @@ public void TestIncludedConfigNoReload(bool useExplicitFileLoading) // Assert that includedConfig2 has been loaded. AssertDebugLastMessage("debug", "[ccc]", logFactory); Assert.False(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Empty(logFactory.Configuration.FileNamesToWatch); + Assert.Empty(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); } finally { @@ -362,8 +362,8 @@ public void TestIncludedConfigReload(bool useExplicitFileLoading) logger.Debug("aaa"); AssertDebugLastMessage("debug", "aaa", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(includedConfigFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(includedConfigFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault()); WriteConfigFileAndReload(logFactory, includedConfigFilePath, includedConfig2); @@ -371,8 +371,8 @@ public void TestIncludedConfigReload(bool useExplicitFileLoading) // Assert that includedConfig2 is loaded. AssertDebugLastMessage("debug", "[ccc]", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(includedConfigFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(includedConfigFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault()); } finally { @@ -429,9 +429,9 @@ public void TestMainConfigReload(bool useExplicitFileLoading) logger.Debug("aaa"); AssertDebugLastMessage("debug", "aaa", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.NotEmpty(logFactory.Configuration.FileNamesToWatch); - Assert.Contains(mainConfigFilePath, logFactory.Configuration.FileNamesToWatch); - Assert.Contains(included1ConfigFilePath, logFactory.Configuration.FileNamesToWatch); + Assert.NotEmpty(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Contains(mainConfigFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Contains(included1ConfigFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); WriteConfigFileAndReload(logFactory, mainConfigFilePath, mainConfig2); @@ -439,9 +439,9 @@ public void TestMainConfigReload(bool useExplicitFileLoading) // Assert that mainConfig2 is loaded (which refers to included2.nlog). AssertDebugLastMessage("debug", "[bbb]", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.NotEmpty(logFactory.Configuration.FileNamesToWatch); - Assert.Contains(mainConfigFilePath, logFactory.Configuration.FileNamesToWatch); - Assert.Contains(included2ConfigFilePath, logFactory.Configuration.FileNamesToWatch); + Assert.NotEmpty(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Contains(mainConfigFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Contains(included2ConfigFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); WriteConfigFileAndReload(logFactory, included2ConfigFilePath, included2Config2); @@ -449,9 +449,9 @@ public void TestMainConfigReload(bool useExplicitFileLoading) // Assert that included2Config2 is loaded. AssertDebugLastMessage("debug", "(ccc)", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.NotEmpty(logFactory.Configuration.FileNamesToWatch); - Assert.Contains(mainConfigFilePath, logFactory.Configuration.FileNamesToWatch); - Assert.Contains(included2ConfigFilePath, logFactory.Configuration.FileNamesToWatch); + Assert.NotEmpty(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Contains(mainConfigFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Contains(included2ConfigFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); } finally { @@ -508,9 +508,9 @@ public void TestMainConfigReloadIncludedConfigNoReload(bool useExplicitFileLoadi logger.Debug("aaa"); AssertDebugLastMessage("debug", "aaa", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.NotEmpty(logFactory.Configuration.FileNamesToWatch); - Assert.Contains(mainConfigFilePath, logFactory.Configuration.FileNamesToWatch); - Assert.Contains(included1ConfigFilePath, logFactory.Configuration.FileNamesToWatch); + Assert.NotEmpty(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Contains(mainConfigFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Contains(included1ConfigFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); WriteConfigFileAndReload(logFactory, mainConfigFilePath, mainConfig2); @@ -518,8 +518,8 @@ public void TestMainConfigReloadIncludedConfigNoReload(bool useExplicitFileLoadi // Assert that mainConfig2 is loaded (which refers to included2.nlog). AssertDebugLastMessage("debug", "[bbb]", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(mainConfigFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(mainConfigFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault()); WriteConfigFileAndReload(logFactory, included2ConfigFilePath, included2Config2); @@ -527,8 +527,8 @@ public void TestMainConfigReloadIncludedConfigNoReload(bool useExplicitFileLoadi // Assert that included2Config2 has been loaded. AssertDebugLastMessage("debug", "(ccc)", logFactory); Assert.True(((XmlLoggingConfiguration)logFactory.Configuration).AutoReload); - Assert.Single(logFactory.Configuration.FileNamesToWatch); - Assert.Equal(mainConfigFilePath, logFactory.Configuration.FileNamesToWatch.FirstOrDefault()); + Assert.Single(((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames); + Assert.Equal(mainConfigFilePath, ((XmlLoggingConfiguration)logFactory.Configuration).AutoReloadFileNames.FirstOrDefault()); } finally { @@ -700,7 +700,9 @@ public void TestReloadingInvalidConfiguration() config = logFactory.Configuration; Assert.NotNull(config); Assert.Empty(config.AllTargets); // Failed to load +#pragma warning disable CS0618 // Type or member is obsolete Assert.Single(config.FileNamesToWatch); // But file-watcher is active +#pragma warning restore CS0618 // Type or member is obsolete WriteConfigFile(nlogConfigFile, validXmlConfig); config = logFactory.Configuration.Reload(); diff --git a/tests/NLog.UnitTests/ConfigFileLocatorTests.cs b/tests/NLog.UnitTests/ConfigFileLocatorTests.cs index 8a225f0c04..1ff4e2dbfa 100644 --- a/tests/NLog.UnitTests/ConfigFileLocatorTests.cs +++ b/tests/NLog.UnitTests/ConfigFileLocatorTests.cs @@ -121,7 +121,9 @@ public void GetConfigFile_absolutePath_loads(string filename, string accepts, st var result = fileLoader.Load(logFactory, filename); // Assert +#pragma warning disable CS0618 // Type or member is obsolete Assert.Equal(expected, result?.FileNamesToWatch.First()); +#pragma warning restore CS0618 // Type or member is obsolete } public static IEnumerable GetConfigFile_absolutePath_loads_testData() From be1ef34198e89defdd8b1de9b77f889099855ea5 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 24 Apr 2025 15:13:36 +0200 Subject: [PATCH 085/224] NLog.Targets.GZipFile for writing GZip Log Files (#5783) --- build.ps1 | 1 + run-tests.ps1 | 8 + .../NLog.Targets.AtomicFile.csproj | 2 +- src/NLog.Targets.GZipFile/GZipFileTarget.cs | 98 ++++++++++++ .../NLog.Targets.GZipFile.csproj | 80 ++++++++++ src/NLog.sln | 14 ++ src/NLog/Targets/FileTarget.cs | 2 +- .../GZipFileTests.cs | 148 ++++++++++++++++++ .../NLog.Targets.GZipFile.Tests.csproj | 31 ++++ 9 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 src/NLog.Targets.GZipFile/GZipFileTarget.cs create mode 100644 src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj create mode 100644 tests/NLog.Targets.GZipFile.Tests/GZipFileTests.cs create mode 100644 tests/NLog.Targets.GZipFile.Tests/NLog.Targets.GZipFile.Tests.csproj diff --git a/build.ps1 b/build.ps1 index a544742e35..6a3231e1d8 100644 --- a/build.ps1 +++ b/build.ps1 @@ -40,6 +40,7 @@ create-package 'NLog.Targets.Mail' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.Network' '"net45;net46;netstandard2.0"' create-package 'NLog.Targets.Trace' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.Targets.WebService' '"net45;net46;netstandard2.0"' +create-package 'NLog.Targets.GZipFile' '"net45;net46;netstandard2.0"' create-package 'NLog.OutputDebugString' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.RegEx' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.WindowsRegistry' '"net35;net45;net46;netstandard2.0"' diff --git a/run-tests.ps1 b/run-tests.ps1 index 668c24bd70..5424209f61 100644 --- a/run-tests.ps1 +++ b/run-tests.ps1 @@ -48,6 +48,10 @@ if ($isWindows -or $Env:WinDir) if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.GZipFile.Tests/ --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + dotnet test ./tests/NLog.WindowsRegistry.Tests/ --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } @@ -103,6 +107,10 @@ else if (-Not $LastExitCode -eq 0) { exit $LastExitCode } + dotnet test ./tests/NLog.Targets.GZipFile.Tests/ --framework net6.0 --configuration release + if (-Not $LastExitCode -eq 0) + { exit $LastExitCode } + # Need help from MONO to run normal .NetFramework tests dotnet msbuild /t:restore ./tests/NLog.UnitTests/ /p:RestoreForce=true /p:monobuild=1 if (-Not $LastExitCode -eq 0) diff --git a/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj index 730df44510..10fa9efb29 100644 --- a/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj +++ b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj @@ -53,7 +53,7 @@ - NLog.Targets.AtomicFile for NetStandard 2.0 + NLog.Targets.AtomicFile for NET8 diff --git a/src/NLog.Targets.GZipFile/GZipFileTarget.cs b/src/NLog.Targets.GZipFile/GZipFileTarget.cs new file mode 100644 index 0000000000..a40cba051f --- /dev/null +++ b/src/NLog.Targets.GZipFile/GZipFileTarget.cs @@ -0,0 +1,98 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets +{ + using System.IO; + using System.IO.Compression; + + /// + /// Extended standard FileTarget with GZip compression as part of file-logging + /// + [Target("GZipFile")] + public class GZipFileTarget : FileTarget + { + /// + /// Initializes a new instance of the class. + /// + public GZipFileTarget() + { + ArchiveOldFileOnStartup = true; // Not possible to append to existing file using GZip, must read the entire file into memory and rewrite everything again + } + + /// + /// Initializes a new instance of the class. + /// + public GZipFileTarget(string name) : this() + { + Name = name; + } + + /// + /// Gets or sets whether to enable file-compression using + /// + public bool EnableArchiveFileCompression { get; set; } = true; + + /// + /// Gets or sets whether to emphasize Fastest-speed or Optimal-compression + /// + public CompressionLevel CompressionLevel { get; set; } = CompressionLevel.Fastest; + + /// + protected override void InitializeTarget() + { + base.InitializeTarget(); + + if (!KeepFileOpen) + throw new NLogConfigurationException("GZipFileTarget requires KeepFileOpen = true"); + + if (!ArchiveOldFileOnStartup) + throw new NLogConfigurationException("GZipFileTarget requires ArchiveOldFileOnStartup = true"); + } + + /// + protected override Stream CreateFileStream(string filePath, int bufferSize) + { + if (!EnableArchiveFileCompression || CompressionLevel == CompressionLevel.NoCompression || !ArchiveOldFileOnStartup || !KeepFileOpen) + return base.CreateFileStream(filePath, bufferSize); + + var underlyingStream = base.CreateFileStream(filePath, bufferSize); + var compressStream = new GZipStream(underlyingStream, CompressionLevel); + + if (!AutoFlush && BufferSize > 0) + return new BufferedStream(compressStream, BufferSize); + else + return compressStream; + } + } +} diff --git a/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj b/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj new file mode 100644 index 0000000000..ff2dcadbc1 --- /dev/null +++ b/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj @@ -0,0 +1,80 @@ + + + + netstandard2.0;net46 + + NLog.Targets.GZipFile + NLog + FileTarget with support for writing GZip FileStream + NLog.Targets.GZipFile v$(ProductVersion) + $(ProductVersion) + Jarek Kowalski,Kim Christensen,Julian Verdurmen + $([System.DateTime]::Now.ToString(yyyy)) + Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ + + + FileTarget Docs: + https://github.com/NLog/NLog/wiki/File-target + + NLog;File;Archive;GZip;logging;log + N.png + https://nlog-project.org/ + BSD-3-Clause + git + https://github.com/NLog/NLog.git + + true + 5.0.0.0 + ..\NLog.snk + true + + true + true + true + true + true + copyused + true + + + + NLog.Targets.GZipFile for .NET Framework 4.6 + true + + + + NLog.Targets.GZipFile for .NET Framework 4.5 + true + + + + NLog.Targets.GZipFile for NetStandard 2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NLog.sln b/src/NLog.sln index bf2cf113c9..15391f8a6e 100644 --- a/src/NLog.sln +++ b/src/NLog.sln @@ -77,6 +77,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.AtomicFile", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.AtomicFile.Tests", "..\tests\NLog.Targets.AtomicFile.Tests\NLog.Targets.AtomicFile.Tests.csproj", "{2365D168-476A-4239-9086-71DC8F5A2B75}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.GZipFile", "NLog.Targets.GZipFile\NLog.Targets.GZipFile.csproj", "{D21F3DA2-B45C-4CBC-A392-03ACA2021CC1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLog.Targets.GZipFile.Tests", "..\tests\NLog.Targets.GZipFile.Tests\NLog.Targets.GZipFile.Tests.csproj", "{DED8D8EC-B0E6-4F2C-9BB8-744FA34230B5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -187,6 +191,14 @@ Global {2365D168-476A-4239-9086-71DC8F5A2B75}.Debug|Any CPU.Build.0 = Debug|Any CPU {2365D168-476A-4239-9086-71DC8F5A2B75}.Release|Any CPU.ActiveCfg = Release|Any CPU {2365D168-476A-4239-9086-71DC8F5A2B75}.Release|Any CPU.Build.0 = Release|Any CPU + {D21F3DA2-B45C-4CBC-A392-03ACA2021CC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D21F3DA2-B45C-4CBC-A392-03ACA2021CC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D21F3DA2-B45C-4CBC-A392-03ACA2021CC1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D21F3DA2-B45C-4CBC-A392-03ACA2021CC1}.Release|Any CPU.Build.0 = Release|Any CPU + {DED8D8EC-B0E6-4F2C-9BB8-744FA34230B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DED8D8EC-B0E6-4F2C-9BB8-744FA34230B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DED8D8EC-B0E6-4F2C-9BB8-744FA34230B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DED8D8EC-B0E6-4F2C-9BB8-744FA34230B5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -216,6 +228,8 @@ Global {877F835E-4D1E-45D8-95AD-AC2F0E3764C7} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {C767E563-82AB-4545-8904-B31E4CF86C2E} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} {2365D168-476A-4239-9086-71DC8F5A2B75} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {D21F3DA2-B45C-4CBC-A392-03ACA2021CC1} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} + {DED8D8EC-B0E6-4F2C-9BB8-744FA34230B5} = {194AB4BD-C817-4C59-A86C-49D89B8C3A64} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6B4C8E9F-1E2F-4AB3-993A-8776CFA08DC0} diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index bef6d53c27..538a07217f 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -48,7 +48,7 @@ namespace NLog.Targets using NLog.Targets.FileArchiveHandlers; /// - /// FileTarget for writing formatted messages to one or more text files. + /// FileTarget for writing formatted messages to one or more log-files. /// /// /// See NLog Wiki diff --git a/tests/NLog.Targets.GZipFile.Tests/GZipFileTests.cs b/tests/NLog.Targets.GZipFile.Tests/GZipFileTests.cs new file mode 100644 index 0000000000..9ce16a3be2 --- /dev/null +++ b/tests/NLog.Targets.GZipFile.Tests/GZipFileTests.cs @@ -0,0 +1,148 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +using System; +using System.IO; +using System.IO.Compression; +using Xunit; + +namespace NLog.Targets.GZipFile.Tests +{ + public class GZipFileTests + { + [Fact] + public void SimpleFileGZipStream() + { + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFileName = Path.Combine(tempDir, "log.gzip"); + + try + { + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => + { + cfg.ForLogger().WriteTo(new GZipFileTarget() { FileName = logFileName, Layout = "${message}", LineEnding = LineEndingMode.LF }); + }).LogFactory; + + logFactory.GetCurrentClassLogger().Info("Hello"); + logFactory.GetCurrentClassLogger().Info("World"); + logFactory.Shutdown(); + + using (var logFile = new StreamReader(new GZipStream(new FileStream(logFileName, FileMode.Open), CompressionMode.Decompress))) + { + Assert.Equal("Hello", logFile.ReadLine()); + Assert.Equal("World", logFile.ReadLine()); + Assert.Null(logFile.ReadLine()); + } + } + finally + { + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void SimpleFileGZipStream_AutoFlush_False() + { + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFileName = Path.Combine(tempDir, "log.gzip"); + + try + { + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => + { + cfg.ForLogger().WriteTo(new GZipFileTarget() { FileName = logFileName, Layout = "${message}", LineEnding = LineEndingMode.LF, AutoFlush = false, CompressionLevel = CompressionLevel.Optimal }); + }).LogFactory; + + logFactory.GetCurrentClassLogger().Info("Hello"); + logFactory.GetCurrentClassLogger().Info("World"); + logFactory.Shutdown(); + + using (var logFile = new StreamReader(new GZipStream(new FileStream(logFileName, FileMode.Open), CompressionMode.Decompress))) + { + Assert.Equal("Hello", logFile.ReadLine()); + Assert.Equal("World", logFile.ReadLine()); + Assert.Null(logFile.ReadLine()); + } + } + finally + { + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void SimpleFileGZipStream_ArchiveOldFileOnStartup() + { + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFileName = Path.Combine(tempDir, "log.gzip"); + + try + { + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => + { + cfg.ForLogger().WriteTo(new GZipFileTarget() { FileName = logFileName, Layout = "${message}", LineEnding = LineEndingMode.LF }); + }).LogFactory; + + logFactory.GetCurrentClassLogger().Info("Hello"); + logFactory.Shutdown(); + + var logFactory2 = new LogFactory().Setup().LoadConfiguration(cfg => + { + cfg.ForLogger().WriteTo(new GZipFileTarget() { FileName = logFileName, Layout = "${message}", LineEnding = LineEndingMode.LF }); + }).LogFactory; + + logFactory2.GetCurrentClassLogger().Info("World"); + logFactory2.Shutdown(); + + using (var logFile = new StreamReader(new GZipStream(new FileStream(logFileName, FileMode.Open), CompressionMode.Decompress))) + { + Assert.Equal("Hello", logFile.ReadLine()); + Assert.Null(logFile.ReadLine()); + } + + using (var logFile = new StreamReader(new GZipStream(new FileStream(Path.Combine(tempDir, "log_01.gzip"), FileMode.Open), CompressionMode.Decompress))) + { + Assert.Equal("World", logFile.ReadLine()); + Assert.Null(logFile.ReadLine()); + } + } + finally + { + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + } +} diff --git a/tests/NLog.Targets.GZipFile.Tests/NLog.Targets.GZipFile.Tests.csproj b/tests/NLog.Targets.GZipFile.Tests/NLog.Targets.GZipFile.Tests.csproj new file mode 100644 index 0000000000..f64a14ecb3 --- /dev/null +++ b/tests/NLog.Targets.GZipFile.Tests/NLog.Targets.GZipFile.Tests.csproj @@ -0,0 +1,31 @@ + + + + 17.0 + net462 + net462;net6.0 + + false + + Full + true + true + en + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + From ed8329a2c22fb9dd8c4af6724e8778276a28527b Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 24 Apr 2025 18:41:55 +0200 Subject: [PATCH 086/224] SyslogTarget - Reduce allocations for writing Octet header (#5784) --- .../Targets/NetworkTarget.cs | 1 + src/NLog.Targets.Network/Targets/SyslogTarget.cs | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/NLog.Targets.Network/Targets/NetworkTarget.cs b/src/NLog.Targets.Network/Targets/NetworkTarget.cs index eb493115c4..8f6e9213aa 100644 --- a/src/NLog.Targets.Network/Targets/NetworkTarget.cs +++ b/src/NLog.Targets.Network/Targets/NetworkTarget.cs @@ -632,6 +632,7 @@ private LinkedListNode GetCachedNetworkSender(string address, Log } } + InternalLogger.Debug("{0}: Creating NetworkServer to address: {1}", this, address); NetworkSender sender = CreateNetworkSender(address, logEventInfo); lock (_openNetworkSenders) { diff --git a/src/NLog.Targets.Network/Targets/SyslogTarget.cs b/src/NLog.Targets.Network/Targets/SyslogTarget.cs index 75fb1bd55a..3341da7096 100644 --- a/src/NLog.Targets.Network/Targets/SyslogTarget.cs +++ b/src/NLog.Targets.Network/Targets/SyslogTarget.cs @@ -111,13 +111,27 @@ protected override byte[] GetHeaderToWrite(LogEventInfo logEvent, string address if (address?.StartsWith("tcp", System.StringComparison.OrdinalIgnoreCase) == true) { var octetCount = payload.Length; - return Encoding.ASCII.GetBytes($"{octetCount} "); + return GenerateOctetHeader(octetCount); } // Skip octet framing for UDP protocols by returning null return null; } + private static byte[] GenerateOctetHeader(int octetCount) + { + if (octetCount < OctetHeaders.Length) + { + var headerBytes = OctetHeaders[octetCount]; + if (headerBytes is null) + OctetHeaders[octetCount] = headerBytes = Encoding.ASCII.GetBytes($"{octetCount} "); + return headerBytes; + } + + return Encoding.ASCII.GetBytes($"{octetCount} "); + } + private static readonly byte[][] OctetHeaders = new byte[4046][]; + /// public override Layout Layout { From 09cb6ad206c6aa2d674cb3d31c10305850f5f1e6 Mon Sep 17 00:00:00 2001 From: Julian Verdurmen <5808377+304NotModified@users.noreply.github.com> Date: Thu, 24 Apr 2025 23:45:29 +0200 Subject: [PATCH 087/224] Remove NLog.Contrib.ActiveMQ (#5785) --- packages-and-status.md | 1 - 1 file changed, 1 deletion(-) diff --git a/packages-and-status.md b/packages-and-status.md index 4a38e210dc..39b91e9c9f 100644 --- a/packages-and-status.md +++ b/packages-and-status.md @@ -3,7 +3,6 @@ Package | Build status | NuGet NLog | [![AppVeyor](https://img.shields.io/appveyor/ci/nlog/nlog/master.svg)](https://ci.appveyor.com/project/nlog/nlog/branch/master) | [![NuGet](https://img.shields.io/nuget/vpre/nlog.svg)](https://www.nuget.org/packages/NLog) [NLog.Extensions.Logging](https://github.com/NLog/NLog.Extensions.Logging) | [![Build status](https://img.shields.io/appveyor/ci/nlog/nlog-framework-logging/master.svg)](https://ci.appveyor.com/project/nlog/nlog-framework-logging/branch/master) | [![NuGet Pre Release](https://img.shields.io/nuget/vpre/NLog.Extensions.Logging.svg)](https://www.nuget.org/packages/NLog.Extensions.Logging) NLog.Config | [![AppVeyor](https://img.shields.io/appveyor/ci/nlog/nlog/master.svg)](https://ci.appveyor.com/project/nlog/nlog/branch/master) | [![NuGet package](https://img.shields.io/nuget/v/NLog.Config.svg)](https://www.nuget.org/packages/NLog.Config) -[NLog.Contrib.ActiveMQ](https://github.com/NLog/NLog.Contrib.ActiveMQ) | [![AppVeyor](https://img.shields.io/appveyor/ci/nlog/nlog-contrib-activemq/master.svg)](https://ci.appveyor.com/project/nlog/nlog-contrib-activemq/branch/master) | [![NuGet package](https://img.shields.io/nuget/v/NLog.Contrib.ActiveMQ.svg)](https://www.nuget.org/packages/NLog.Contrib.ActiveMQ) NLog.Database | [![AppVeyor](https://img.shields.io/appveyor/ci/nlog/nlog/master.svg)](https://ci.appveyor.com/project/nlog/nlog/branch/master) | [![NuGet](https://img.shields.io/nuget/vpre/NLog.Database.svg)](https://www.nuget.org/packages/NLog.Database) [NLog.Elmah](https://github.com/NLog/NLog.Elmah) | [![AppVeyor](https://img.shields.io/appveyor/ci/nlog/nlog-Elmah/master.svg)](https://ci.appveyor.com/project/nlog/nlog-Elmah/branch/master) | [![NuGet package](https://img.shields.io/nuget/v/NLog.Elmah.svg)](https://www.nuget.org/packages/NLog.Elmah) [NLog.Etw](https://github.com/NLog/NLog.Etw) | [![AppVeyor](https://img.shields.io/appveyor/ci/nlog/nlog-etw/master.svg)](https://ci.appveyor.com/project/nlog/nlog-etw/branch/master) | [![NuGet package](https://img.shields.io/nuget/v/NLog.Etw.svg)](https://www.nuget.org/packages/NLog.Etw) From 379afc4c78301327889cc5734209ca81f8b61a40 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 25 Apr 2025 12:47:57 +0200 Subject: [PATCH 088/224] LiteralWithRawValueLayoutRenderer without reflection (#5786) --- src/NLog/Config/AssemblyExtensionLoader.cs | 36 +++++++++------------ src/NLog/Config/AssemblyExtensionTypes.cs | 1 + src/NLog/Config/AssemblyExtensionTypes.tt | 1 + src/NLog/Config/ConfigurationItemFactory.cs | 30 +++++++++++++---- src/NLog/Layouts/Layout.cs | 1 + 5 files changed, 41 insertions(+), 28 deletions(-) diff --git a/src/NLog/Config/AssemblyExtensionLoader.cs b/src/NLog/Config/AssemblyExtensionLoader.cs index c761ceb09f..c08fa1bd46 100644 --- a/src/NLog/Config/AssemblyExtensionLoader.cs +++ b/src/NLog/Config/AssemblyExtensionLoader.cs @@ -215,10 +215,8 @@ private static bool IsNLogConfigurationItemType(Type itemType) var nameAttribute = itemType.GetFirstCustomAttribute(); return !string.IsNullOrEmpty(nameAttribute?.Name); } - else - { - return false; - } + + return false; } private static bool IsNLogItemTypeAlreadyRegistered(ConfigurationItemFactory factory, Type itemType, string itemNamePrefix) @@ -227,39 +225,35 @@ private static bool IsNLogItemTypeAlreadyRegistered(ConfigurationItemFactory fac { return false; } - else if (IsNLogItemTypeAlreadyRegistered, Layouts.Layout, Layouts.LayoutAttribute>(factory.LayoutFactory, itemType, itemNamePrefix)) + else if (typeof(Layouts.Layout).IsAssignableFrom(itemType)) { - return true; + return IsNLogItemTypeAlreadyRegistered(factory.LayoutFactory, itemType, itemNamePrefix); } - else if (IsNLogItemTypeAlreadyRegistered, LayoutRenderers.LayoutRenderer, LayoutRenderers.LayoutRendererAttribute>(factory.LayoutRendererFactory, itemType, itemNamePrefix)) + else if (typeof(LayoutRenderers.LayoutRenderer).IsAssignableFrom(itemType)) { - return true; + return IsNLogItemTypeAlreadyRegistered(factory.LayoutRendererFactory, itemType, itemNamePrefix); } - else if (IsNLogItemTypeAlreadyRegistered, Targets.Target, Targets.TargetAttribute>(factory.TargetFactory, itemType, itemNamePrefix)) + else if (typeof(Targets.Target).IsAssignableFrom(itemType)) { - return true; + return IsNLogItemTypeAlreadyRegistered(factory.TargetFactory, itemType, itemNamePrefix); } - else if (IsNLogItemTypeAlreadyRegistered, Filters.Filter, Filters.FilterAttribute>(factory.FilterFactory, itemType, itemNamePrefix)) + else if (typeof(Filters.Filter).IsAssignableFrom(itemType)) { - return true; + return IsNLogItemTypeAlreadyRegistered(factory.FilterFactory, itemType, itemNamePrefix); } return false; } - private static bool IsNLogItemTypeAlreadyRegistered(TFactory factory, Type itemType, string itemNamePrefix) + private static bool IsNLogItemTypeAlreadyRegistered(IFactory factory, Type itemType, string itemNamePrefix) where TAttribute : NameBaseAttribute - where TFactory : IFactory where TBaseType : class { - if (typeof(TBaseType).IsAssignableFrom(itemType)) + var nameAttribute = itemType.GetFirstCustomAttribute(); + if (!string.IsNullOrEmpty(nameAttribute?.Name)) { - var nameAttribute = itemType.GetFirstCustomAttribute(); - if (!string.IsNullOrEmpty(nameAttribute?.Name)) - { - var typeAlias = string.IsNullOrEmpty(itemNamePrefix) ? nameAttribute.Name : itemNamePrefix + nameAttribute.Name; - return factory.TryCreateInstance(typeAlias, out var _); - } + var typeAlias = string.IsNullOrEmpty(itemNamePrefix) ? nameAttribute.Name : itemNamePrefix + nameAttribute.Name; + return factory.TryCreateInstance(typeAlias, out var _); } return false; } diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index 627f2d42bd..39ab7a7229 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -124,6 +124,7 @@ public static void RegisterLayoutRendererTypes(ConfigurationItemFactory factory, #if NETFRAMEWORK factory.GetLayoutRendererFactory().RegisterType("appsetting"); #endif + factory.RegisterTypeProperties(() => null); if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("all-event-properties")) factory.GetLayoutRendererFactory().RegisterType("all-event-properties"); if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("appdomain")) diff --git a/src/NLog/Config/AssemblyExtensionTypes.tt b/src/NLog/Config/AssemblyExtensionTypes.tt index 31225c2ff0..e035491711 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.tt +++ b/src/NLog/Config/AssemblyExtensionTypes.tt @@ -128,6 +128,7 @@ namespace NLog.Config #if NETFRAMEWORK factory.GetLayoutRendererFactory().RegisterType("appsetting"); #endif + factory.RegisterTypeProperties(() => null); <# foreach(var type in AllTypes) { diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index d373f5f5b2..18047b23c5 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -112,7 +112,7 @@ internal ConfigurationItemFactory(ServiceRepository serviceRepository) _ambientProperties, _timeSources, }; - RegisterType(); + RegisterType(); } /// @@ -136,13 +136,21 @@ public IFactory TargetFactory get { if (!_targets.Initialized) + { _targets.Initialize(skipCheckExists => RegisterAllTargets(skipCheckExists)); - // Targets can depend on filters - if (!_filters.Initialized) - _filters.Initialize(skipCheckExists => RegisterAllFilters(skipCheckExists)); - // Targets can depend on conditions - if (!_conditionMethods.Initialized) - _conditionMethods.Initialize(skipCheckExists => RegisterAllConditionMethods(skipCheckExists)); + // Targets can depend on filters + if (!_filters.Initialized) + _filters.Initialize(skipCheckExists => RegisterAllFilters(skipCheckExists)); + // Targets can depend on conditions + if (!_conditionMethods.Initialized) + _conditionMethods.Initialize(skipCheckExists => RegisterAllConditionMethods(skipCheckExists)); + // Targets can depend on layouts + if (!_layouts.Initialized) + _layouts.Initialize(skipCheckExists => RegisterAllLayouts(skipCheckExists)); + // Targets can depend on layoutrenderers + if (!_layoutRenderers.Initialized) + _layoutRenderers.Initialize(skipCheckExists => RegisterAllLayoutRenderers(skipCheckExists)); + } return _targets; } } @@ -155,7 +163,15 @@ public IFactory LayoutFactory get { if (!_layouts.Initialized) + { _layouts.Initialize(skipCheckExists => RegisterAllLayouts(skipCheckExists)); + // Layout can depend on layoutrenderers + if (!_layoutRenderers.Initialized) + _layoutRenderers.Initialize(skipCheckExists => RegisterAllLayoutRenderers(skipCheckExists)); + // When-LayoutRenderers depends on conditions + if (!_conditionMethods.Initialized) + _conditionMethods.Initialize(skipCheckExists => RegisterAllConditionMethods(skipCheckExists)); + } return _layouts; } } diff --git a/src/NLog/Layouts/Layout.cs b/src/NLog/Layouts/Layout.cs index 6e578309c0..2a11a56ede 100644 --- a/src/NLog/Layouts/Layout.cs +++ b/src/NLog/Layouts/Layout.cs @@ -371,6 +371,7 @@ internal void PerformObjectScanning() var objectGraphTypes = new HashSet(objectGraphScannerList.Select(o => o.GetType())); objectGraphTypes.Remove(typeof(SimpleLayout)); objectGraphTypes.Remove(typeof(NLog.LayoutRenderers.LiteralLayoutRenderer)); + objectGraphTypes.Remove(typeof(NLog.LayoutRenderers.LiteralWithRawValueLayoutRenderer)); // determine whether the layout is thread-agnostic // layout is thread agnostic if it is thread-agnostic and From 760853f7419bb80e3d6059238983944f2c253347 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 25 Apr 2025 15:17:41 +0200 Subject: [PATCH 089/224] FuncLayoutRenderer without reflection (#5787) --- src/NLog/Config/AssemblyExtensionTypes.cs | 2 ++ src/NLog/Config/AssemblyExtensionTypes.tt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index 39ab7a7229..5fb32260c3 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -125,6 +125,8 @@ public static void RegisterLayoutRendererTypes(ConfigurationItemFactory factory, factory.GetLayoutRendererFactory().RegisterType("appsetting"); #endif factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("all-event-properties")) factory.GetLayoutRendererFactory().RegisterType("all-event-properties"); if (skipCheckExists || !factory.GetLayoutRendererFactory().CheckTypeAliasExists("appdomain")) diff --git a/src/NLog/Config/AssemblyExtensionTypes.tt b/src/NLog/Config/AssemblyExtensionTypes.tt index e035491711..6a1d70d35a 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.tt +++ b/src/NLog/Config/AssemblyExtensionTypes.tt @@ -129,6 +129,8 @@ namespace NLog.Config factory.GetLayoutRendererFactory().RegisterType("appsetting"); #endif factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); <# foreach(var type in AllTypes) { From 5eadf0d949f22aaeba40eb37716e733246a75edd Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 25 Apr 2025 16:44:35 +0200 Subject: [PATCH 090/224] ConcurrentFileTarget - Removed explicit static constructor (#5789) --- .../ConcurrentFileTarget.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs b/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs index 04bcc18a55..9b51038beb 100644 --- a/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs +++ b/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs @@ -156,13 +156,6 @@ internal ConcurrentFileTarget(IFileAppenderCache fileAppenderCache) _fileAppenderCache = fileAppenderCache; } -#if !NET35 && !NET40 - static ConcurrentFileTarget() - { - FileCompressor = new ZipArchiveFileCompressor(); - } -#endif - /// /// Initializes a new instance of the class. /// @@ -675,6 +668,9 @@ public ArchiveNumberingMode ArchiveNumbering /// /// public static IFileCompressor FileCompressor { get; set; } +#if !NET35 && !NET40 + = new ZipArchiveFileCompressor(); +#endif /// /// Gets or sets a value indicating whether to compress archive files into the zip archive format. From 8c181375c9434c4f840be64c8aafecc9e43f0884 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 25 Apr 2025 17:18:28 +0200 Subject: [PATCH 091/224] LogManager - Lazy initialization of LogFactory to avoid static constructor (#5790) --- src/NLog/LogManager.cs | 84 ++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/src/NLog/LogManager.cs b/src/NLog/LogManager.cs index 0f5804031a..1af43ebb75 100644 --- a/src/NLog/LogManager.cs +++ b/src/NLog/LogManager.cs @@ -38,7 +38,6 @@ namespace NLog using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; - using NLog.Common; using NLog.Config; using NLog.Internal; @@ -51,13 +50,19 @@ namespace NLog /// public static class LogManager { - private static readonly LogFactory factory = new LogFactory(); - /// /// Gets the instance used in the . /// - /// Could be used to pass the to other methods - public static LogFactory LogFactory => factory; + public static LogFactory LogFactory => _logFactory ?? CreateLogFactorySingleton(); + private static LogFactory _logFactory; + + private static LogFactory CreateLogFactorySingleton() + { + var logFactory = new LogFactory(); + if (!(System.Threading.Interlocked.CompareExchange(ref _logFactory, logFactory, null) is null)) + logFactory.Dispose(); // Raced by other thread, so dispose instance not needed + return _logFactory; + } /// /// Occurs when logging changes. Both when assigned to new config or config unloaded. @@ -67,8 +72,8 @@ public static class LogManager /// public static event EventHandler ConfigurationChanged { - add => factory.ConfigurationChanged += value; - remove => factory.ConfigurationChanged -= value; + add => LogFactory.ConfigurationChanged += value; + remove => LogFactory.ConfigurationChanged -= value; } /// @@ -77,8 +82,8 @@ public static event EventHandler Configura /// public static bool ThrowExceptions { - get => factory.ThrowExceptions; - set => factory.ThrowExceptions = value; + get => LogFactory.ThrowExceptions; + set => LogFactory.ThrowExceptions = value; } /// @@ -92,8 +97,8 @@ public static bool ThrowExceptions /// public static bool? ThrowConfigExceptions { - get => factory.ThrowConfigExceptions; - set => factory.ThrowConfigExceptions = value; + get => LogFactory.ThrowConfigExceptions; + set => LogFactory.ThrowConfigExceptions = value; } /// @@ -101,8 +106,8 @@ public static bool? ThrowConfigExceptions /// public static bool KeepVariablesOnReload { - get => factory.KeepVariablesOnReload; - set => factory.KeepVariablesOnReload = value; + get => LogFactory.KeepVariablesOnReload; + set => LogFactory.KeepVariablesOnReload = value; } /// @@ -111,8 +116,8 @@ public static bool KeepVariablesOnReload /// public static bool AutoShutdown { - get => factory.AutoShutdown; - set => factory.AutoShutdown = value; + get => LogFactory.AutoShutdown; + set => LogFactory.AutoShutdown = value; } /// @@ -123,8 +128,8 @@ public static bool AutoShutdown /// public static LoggingConfiguration Configuration { - get => factory.Configuration; - set => factory.Configuration = value; + get => LogFactory.Configuration; + set => LogFactory.Configuration = value; } /// @@ -132,8 +137,8 @@ public static LoggingConfiguration Configuration /// public static LogLevel GlobalThreshold { - get => factory.GlobalThreshold; - set => factory.GlobalThreshold = value; + get => LogFactory.GlobalThreshold; + set => LogFactory.GlobalThreshold = value; } /// @@ -162,8 +167,7 @@ public static LogFactory Setup(Action setupBuilder) [EditorBrowsable(EditorBrowsableState.Never)] public static LogFactory LoadConfiguration(string configFile) { - factory.LoadConfiguration(configFile); - return factory; + return LogFactory.LoadConfiguration(configFile); } /// @@ -188,7 +192,7 @@ public static void AddHiddenAssembly(Assembly assembly) public static Logger GetCurrentClassLogger() { var className = StackTraceUsageUtils.GetClassFullName(new System.Diagnostics.StackFrame(1, false)); - return factory.GetLogger(className); + return LogFactory.GetLogger(className); } /// @@ -208,7 +212,7 @@ public static Logger GetCurrentClassLogger() public static Logger GetCurrentClassLogger([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type loggerType) { var className = StackTraceUsageUtils.GetClassFullName(new System.Diagnostics.StackFrame(1, false)); - return factory.GetLogger(className, loggerType); + return LogFactory.GetLogger(className, loggerType); } /// @@ -217,7 +221,7 @@ public static Logger GetCurrentClassLogger([DynamicallyAccessedMembers(Dynamical /// Null logger which discards all log messages. public static Logger CreateNullLogger() { - return factory.CreateNullLogger(); + return LogFactory.CreateNullLogger(); } /// @@ -227,7 +231,7 @@ public static Logger CreateNullLogger() /// The logger reference. Multiple calls to GetLogger with the same argument aren't guaranteed to return the same logger reference. public static Logger GetLogger(string name) { - return factory.GetLogger(name); + return LogFactory.GetLogger(name); } /// @@ -243,7 +247,7 @@ public static Logger GetLogger(string name) [EditorBrowsable(EditorBrowsableState.Never)] public static Logger GetLogger(string name, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type loggerType) { - return factory.GetLogger(name, loggerType); + return LogFactory.GetLogger(name, loggerType); } /// @@ -253,7 +257,7 @@ public static Logger GetLogger(string name, [DynamicallyAccessedMembers(Dynamica /// public static void ReconfigExistingLoggers() { - factory.ReconfigExistingLoggers(); + LogFactory.ReconfigExistingLoggers(); } /// @@ -264,7 +268,7 @@ public static void ReconfigExistingLoggers() /// Purge garbage collected logger-items from the cache public static void ReconfigExistingLoggers(bool purgeObsoleteLoggers) { - factory.ReconfigExistingLoggers(purgeObsoleteLoggers); + LogFactory.ReconfigExistingLoggers(purgeObsoleteLoggers); } /// @@ -272,7 +276,7 @@ public static void ReconfigExistingLoggers(bool purgeObsoleteLoggers) /// public static void Flush() { - factory.Flush(); + LogFactory.Flush(); } /// @@ -281,7 +285,7 @@ public static void Flush() /// Maximum time to allow for the flush. Any messages after that time will be discarded. public static void Flush(TimeSpan timeout) { - factory.Flush(timeout); + LogFactory.Flush(timeout); } /// @@ -290,7 +294,7 @@ public static void Flush(TimeSpan timeout) /// Maximum time to allow for the flush. Any messages after that time will be discarded. public static void Flush(int timeoutMilliseconds) { - factory.Flush(timeoutMilliseconds); + LogFactory.Flush(timeoutMilliseconds); } /// @@ -299,7 +303,7 @@ public static void Flush(int timeoutMilliseconds) /// The asynchronous continuation. public static void Flush(AsyncContinuation asyncContinuation) { - factory.Flush(asyncContinuation); + LogFactory.Flush(asyncContinuation); } /// @@ -309,7 +313,7 @@ public static void Flush(AsyncContinuation asyncContinuation) /// Maximum time to allow for the flush. Any messages after that time will be discarded. public static void Flush(AsyncContinuation asyncContinuation, TimeSpan timeout) { - factory.Flush(asyncContinuation, timeout); + LogFactory.Flush(asyncContinuation, timeout); } /// @@ -319,7 +323,7 @@ public static void Flush(AsyncContinuation asyncContinuation, TimeSpan timeout) /// Maximum time to allow for the flush. Any messages after that time will be discarded. public static void Flush(AsyncContinuation asyncContinuation, int timeoutMilliseconds) { - factory.Flush(asyncContinuation, timeoutMilliseconds); + LogFactory.Flush(asyncContinuation, timeoutMilliseconds); } /// @@ -336,7 +340,7 @@ public static void Flush(AsyncContinuation asyncContinuation, int timeoutMillise [EditorBrowsable(EditorBrowsableState.Never)] public static IDisposable DisableLogging() { - return factory.SuspendLogging(); + return LogFactory.SuspendLogging(); } /// @@ -351,7 +355,7 @@ public static IDisposable DisableLogging() [EditorBrowsable(EditorBrowsableState.Never)] public static void EnableLogging() { - factory.ResumeLogging(); + LogFactory.ResumeLogging(); } /// @@ -365,7 +369,7 @@ public static void EnableLogging() /// To be used with C# using () statement. public static IDisposable SuspendLogging() { - return factory.SuspendLogging(); + return LogFactory.SuspendLogging(); } /// @@ -377,7 +381,7 @@ public static IDisposable SuspendLogging() /// public static void ResumeLogging() { - factory.ResumeLogging(); + LogFactory.ResumeLogging(); } /// @@ -391,7 +395,7 @@ public static void ResumeLogging() /// otherwise. public static bool IsLoggingEnabled() { - return factory.IsLoggingEnabled(); + return LogFactory.IsLoggingEnabled(); } /// @@ -399,7 +403,7 @@ public static bool IsLoggingEnabled() /// public static void Shutdown() { - factory.Shutdown(); + LogFactory.Shutdown(); } } } From bcd75c533f645cdbe050e72fce9963436be6a515 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 25 Apr 2025 19:07:24 +0200 Subject: [PATCH 092/224] Removed obsolete FactoryHelper since not referenced (#5791) --- src/NLog/Internal/FactoryHelper.cs | 60 ------------------------------ 1 file changed, 60 deletions(-) delete mode 100644 src/NLog/Internal/FactoryHelper.cs diff --git a/src/NLog/Internal/FactoryHelper.cs b/src/NLog/Internal/FactoryHelper.cs deleted file mode 100644 index 16be081d1c..0000000000 --- a/src/NLog/Internal/FactoryHelper.cs +++ /dev/null @@ -1,60 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.Internal -{ - using System; - using NLog.Config; - - /// - /// Object construction helper. - /// - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - internal static class FactoryHelper - { - internal static readonly ConfigurationItemCreator CreateInstance = DefaultCreateInstance; - - [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Ignore since obsolete", "IL2067")] - private static object DefaultCreateInstance(Type type) - { - try - { - return Activator.CreateInstance(type); - } - catch (MissingMethodException exception) - { - throw new NLogConfigurationException($"Cannot access the constructor of type: {type.FullName}. Is the required permission granted?", exception); - } - } - } -} From 980b7e0a9d720d919e9b83c30a234ffad6d3b8a3 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 25 Apr 2025 20:31:30 +0200 Subject: [PATCH 093/224] TargetWithContext - Reduce allocation for RenderLogEvent when SimpleLayout (#5793) --- .../LayoutRenderers/MessageLayoutRenderer.cs | 2 +- src/NLog/Layouts/SimpleLayout.cs | 11 ++++++++++- src/NLog/Layouts/Typed/Layout.cs | 6 ++++-- src/NLog/Targets/Target.cs | 6 ++++-- src/NLog/Targets/TargetWithContext.cs | 18 ++++++++++++++++-- tests/NLog.UnitTests/ApiTests.cs | 2 +- .../Targets/TargetWithContextTest.cs | 16 ++++++++++++++++ 7 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs b/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs index 04281f9463..ccca9130a6 100644 --- a/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs @@ -131,7 +131,7 @@ private static Exception GetPrimaryException(Exception exception) string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) { - if (WithException) + if (WithException && logEvent.Exception != null) return null; else return (Raw ? logEvent.Message : logEvent.FormattedMessage) ?? string.Empty; diff --git a/src/NLog/Layouts/SimpleLayout.cs b/src/NLog/Layouts/SimpleLayout.cs index aab798cccf..f2d1ec5e08 100644 --- a/src/NLog/Layouts/SimpleLayout.cs +++ b/src/NLog/Layouts/SimpleLayout.cs @@ -57,7 +57,7 @@ namespace NLog.Layouts [Layout("SimpleLayout")] [ThreadAgnostic] [AppDomainFixedOutput] - public class SimpleLayout : Layout, IUsesStackTrace + public class SimpleLayout : Layout, IUsesStackTrace, IStringValueRenderer { private readonly IRawValue _rawValueRenderer; private IStringValueRenderer _stringValueRenderer; @@ -494,5 +494,14 @@ protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuil target.Append(FixedText); } } + + string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) + { + if (IsFixedText) + return FixedText; + if (IsSimpleStringText) + return Render(logEvent, false); + return null; + } } } diff --git a/src/NLog/Layouts/Typed/Layout.cs b/src/NLog/Layouts/Typed/Layout.cs index 3ce61f3aef..dbac617cde 100644 --- a/src/NLog/Layouts/Typed/Layout.cs +++ b/src/NLog/Layouts/Typed/Layout.cs @@ -572,9 +572,11 @@ public object RenderObjectValue(LogEventInfo logEvent, StringBuilder stringBuild private string RenderStringValue(LogEventInfo logEvent, StringBuilder stringBuilder, string previousStringValue) { - if (_innerLayout is SimpleLayout simpleLayout && simpleLayout.IsSimpleStringText) + if (_innerLayout is IStringValueRenderer stringLayout) { - return simpleLayout.Render(logEvent, false); + var result = stringLayout.GetFormattedString(logEvent); + if (result != null) + return result; } if (stringBuilder?.Length == 0) diff --git a/src/NLog/Targets/Target.cs b/src/NLog/Targets/Target.cs index fc6bf2c5b9..d2cf44d57a 100644 --- a/src/NLog/Targets/Target.cs +++ b/src/NLog/Targets/Target.cs @@ -705,9 +705,11 @@ protected string RenderLogEvent([CanBeNull] Layout layout, [CanBeNull] LogEventI if (layout is null || logEvent is null) return null; // Signal that input was wrong - if (layout is SimpleLayout simpleLayout && (simpleLayout.IsFixedText || simpleLayout.IsSimpleStringText)) + if (layout is IStringValueRenderer stringLayout) { - return simpleLayout.Render(logEvent, false); + var result = stringLayout.GetFormattedString(logEvent); + if (result != null) + return result; } if (TryGetCachedValue(layout, logEvent, out var value)) diff --git a/src/NLog/Targets/TargetWithContext.cs b/src/NLog/Targets/TargetWithContext.cs index 7f5c06f48c..c45de2360f 100644 --- a/src/NLog/Targets/TargetWithContext.cs +++ b/src/NLog/Targets/TargetWithContext.cs @@ -563,10 +563,19 @@ protected virtual bool SerializeItemValue(LogEventInfo logEvent, string name, ob } [ThreadAgnostic] - internal sealed class TargetWithContextLayout : Layout, IIncludeContext, IUsesStackTrace + internal sealed class TargetWithContextLayout : Layout, IIncludeContext, IUsesStackTrace, IStringValueRenderer { - public Layout TargetLayout { get => _targetLayout; set => _targetLayout = ReferenceEquals(this, value) ? _targetLayout : value; } + public Layout TargetLayout + { + get => _targetLayout; + set + { + _targetLayout = ReferenceEquals(this, value) ? _targetLayout : value; + _targetStringLayout = _targetLayout as IStringValueRenderer; + } + } private Layout _targetLayout; + private IStringValueRenderer _targetStringLayout; /// Internal Layout that allows capture of properties-dictionary internal LayoutScopeContextProperties ScopeContextPropertiesLayout { get; } @@ -726,6 +735,11 @@ protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuil TargetLayout?.Render(logEvent, target); } + string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) + { + return _targetStringLayout?.GetFormattedString(logEvent); + } + public class LayoutScopeContextProperties : Layout { private readonly TargetWithContext _owner; diff --git a/tests/NLog.UnitTests/ApiTests.cs b/tests/NLog.UnitTests/ApiTests.cs index c3f73c591d..7f24f3c7ff 100644 --- a/tests/NLog.UnitTests/ApiTests.cs +++ b/tests/NLog.UnitTests/ApiTests.cs @@ -189,7 +189,7 @@ public void IStringValueRenderer_AppDomainFixedOutput_Attribute_NotRequired() { foreach (Type type in allTypes) { - if (typeof(NLog.Internal.IStringValueRenderer).IsAssignableFrom(type) && !type.IsInterface) + if (typeof(NLog.Internal.IStringValueRenderer).IsAssignableFrom(type) && !type.IsInterface && !typeof(NLog.Layouts.SimpleLayout).Equals(type)) { var appDomainFixedOutputAttribute = type.GetCustomAttribute(); Assert.True(appDomainFixedOutputAttribute is null, $"{type.ToString()} should not implement IStringValueRenderer"); diff --git a/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs b/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs index c17be6bf27..971f1d4e98 100644 --- a/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs +++ b/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs @@ -201,6 +201,22 @@ private static void WriteAndAssertSingleKey(CustomTargetWithContext target) Assert.Equal("{ a = b }", target.LastCombinedProperties["TestKey"]); } + [Fact] + public void TargetWithContextStringLayoutTest() + { + var target = new CustomTargetWithContext() { Layout = "${message}", SkipAssert = true }; + var logFactory = new LogFactory().Setup() + .SetupExtensions(ext => ext.RegisterTarget("contexttarget")) + .LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; + + var logEventInfo = LogEventInfo.Create(LogLevel.Info, null, null, "Hello {World}", new[] { "Earth" }); + var expectedMessage = logEventInfo.FormattedMessage; + + logFactory.GetCurrentClassLogger().Info(logEventInfo); + + Assert.Same(expectedMessage, target.LastMessage); + } + [Fact] public void TargetWithContextConfigTest() { From 2fda713e943a948326c346a4f753ba8321306b92 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 26 Apr 2025 13:45:58 +0200 Subject: [PATCH 094/224] Layout Render without caching, as handled by Precalculate (#5794) --- src/NLog/Filters/WhenContainsFilter.cs | 2 +- src/NLog/Filters/WhenEqualFilter.cs | 2 +- src/NLog/Filters/WhenNotContainsFilter.cs | 2 +- src/NLog/Filters/WhenNotEqualFilter.cs | 2 +- src/NLog/Filters/WhenRepeatedFilter.cs | 2 +- src/NLog/Internal/AppendBuilderCreator.cs | 4 +- .../LayoutRenderers/CounterLayoutRenderer.cs | 2 +- .../EnvironmentLayoutRenderer.cs | 2 +- src/NLog/LayoutRenderers/LayoutRenderer.cs | 12 +- .../LayoutRenderers/MessageLayoutRenderer.cs | 2 +- .../ScopeContextIndentLayoutRenderer.cs | 2 +- .../Wrappers/CachedLayoutRendererWrapper.cs | 2 +- .../Wrappers/ObjectPathRendererWrapper.cs | 6 +- .../WhenEmptyLayoutRendererWrapper.cs | 6 +- .../Wrappers/WrapperLayoutRendererBase.cs | 2 +- src/NLog/Layouts/Layout.cs | 80 +++---- src/NLog/Layouts/SimpleLayout.cs | 209 ++++++++---------- src/NLog/Layouts/Typed/Layout.cs | 2 +- src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs | 2 +- .../NLog.UnitTests/Layouts/CsvLayoutTests.cs | 3 + .../Layouts/SimpleLayoutOutputTests.cs | 4 +- 21 files changed, 147 insertions(+), 203 deletions(-) diff --git a/src/NLog/Filters/WhenContainsFilter.cs b/src/NLog/Filters/WhenContainsFilter.cs index d3374ef355..9e646ee1ef 100644 --- a/src/NLog/Filters/WhenContainsFilter.cs +++ b/src/NLog/Filters/WhenContainsFilter.cs @@ -62,7 +62,7 @@ protected override FilterResult Check(LogEventInfo logEvent) StringComparison comparisonType = IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - string result = Layout.Render(logEvent, cacheLayoutResult: false); + string result = Layout.Render(logEvent); if (result.IndexOf(Substring, comparisonType) >= 0) { return Action; diff --git a/src/NLog/Filters/WhenEqualFilter.cs b/src/NLog/Filters/WhenEqualFilter.cs index c06d4a3d4f..77e863e96e 100644 --- a/src/NLog/Filters/WhenEqualFilter.cs +++ b/src/NLog/Filters/WhenEqualFilter.cs @@ -62,7 +62,7 @@ protected override FilterResult Check(LogEventInfo logEvent) StringComparison comparisonType = IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - string result = Layout.Render(logEvent, cacheLayoutResult: false); + string result = Layout.Render(logEvent); if (result.Equals(CompareTo, comparisonType)) { return Action; diff --git a/src/NLog/Filters/WhenNotContainsFilter.cs b/src/NLog/Filters/WhenNotContainsFilter.cs index ffa98acab7..86ded5613b 100644 --- a/src/NLog/Filters/WhenNotContainsFilter.cs +++ b/src/NLog/Filters/WhenNotContainsFilter.cs @@ -62,7 +62,7 @@ protected override FilterResult Check(LogEventInfo logEvent) StringComparison comparison = IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - string result = Layout.Render(logEvent, cacheLayoutResult: false); + string result = Layout.Render(logEvent); if (result.IndexOf(Substring, comparison) < 0) { return Action; diff --git a/src/NLog/Filters/WhenNotEqualFilter.cs b/src/NLog/Filters/WhenNotEqualFilter.cs index 8270223ae2..325f1665f1 100644 --- a/src/NLog/Filters/WhenNotEqualFilter.cs +++ b/src/NLog/Filters/WhenNotEqualFilter.cs @@ -69,7 +69,7 @@ protected override FilterResult Check(LogEventInfo logEvent) StringComparison comparisonType = IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; - string result = Layout.Render(logEvent, cacheLayoutResult: false); + string result = Layout.Render(logEvent); if (!result.Equals(CompareTo, comparisonType)) { return Action; diff --git a/src/NLog/Filters/WhenRepeatedFilter.cs b/src/NLog/Filters/WhenRepeatedFilter.cs index 0d0f4dd9cc..ffc25cef4f 100644 --- a/src/NLog/Filters/WhenRepeatedFilter.cs +++ b/src/NLog/Filters/WhenRepeatedFilter.cs @@ -255,7 +255,7 @@ private FilterInfoKey RenderFilterInfoKey(LogEventInfo logEvent, StringBuilder t targetBuilder.Length = MaxLength; return new FilterInfoKey(targetBuilder, null); } - string value = Layout.Render(logEvent, cacheLayoutResult: false); + string value = Layout.Render(logEvent); if (value.Length > MaxLength) value = value.Substring(0, MaxLength); return new FilterInfoKey(null, value); diff --git a/src/NLog/Internal/AppendBuilderCreator.cs b/src/NLog/Internal/AppendBuilderCreator.cs index 2efcfb5893..ee9324c7fd 100644 --- a/src/NLog/Internal/AppendBuilderCreator.cs +++ b/src/NLog/Internal/AppendBuilderCreator.cs @@ -73,10 +73,8 @@ public AppendBuilderCreator(StringBuilder appendTarget, bool mustBeEmpty) public void Dispose() { if (!ReferenceEquals(_builder.Item, _appendTarget)) - { _builder.Item.CopyTo(_appendTarget); - _builder.Dispose(); - } + _builder.Dispose(); } } } diff --git a/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs b/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs index 3d1f3b0d60..28d3be6bf9 100644 --- a/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs @@ -90,7 +90,7 @@ private long GetNextValue(LogEventInfo logEvent) return currentValue; } - var sequenceName = Sequence.Render(logEvent, cacheLayoutResult: false); + var sequenceName = Sequence.Render(logEvent); lock (Sequences) { if (!Sequences.TryGetValue(sequenceName, out var nextValue)) diff --git a/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs b/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs index 5bb20008a9..ca9c54b639 100644 --- a/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs @@ -77,7 +77,7 @@ string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) if (simpleLayout is null) return string.Empty; if (simpleLayout.IsFixedText || simpleLayout.IsSimpleStringText) - return simpleLayout.Render(logEvent, cacheLayoutResult: false); + return simpleLayout.Render(logEvent); return null; } diff --git a/src/NLog/LayoutRenderers/LayoutRenderer.cs b/src/NLog/LayoutRenderers/LayoutRenderer.cs index 2d8b84f274..a243cb8900 100644 --- a/src/NLog/LayoutRenderers/LayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LayoutRenderer.cs @@ -156,18 +156,18 @@ internal void Close() /// The layout render output is appended to builder internal void RenderAppendBuilder(LogEventInfo logEvent, StringBuilder builder) { - if (!_isInitialized) - { - Initialize(); - } - try { + if (!_isInitialized) + { + Initialize(); + } + Append(builder, logEvent); } catch (Exception exception) { - InternalLogger.Warn(exception, "Exception in layout renderer."); + InternalLogger.Warn(exception, "Exception in '{0}.Append()'", GetType()); if (exception.MustBeRethrown()) { diff --git a/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs b/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs index ccca9130a6..04281f9463 100644 --- a/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs @@ -131,7 +131,7 @@ private static Exception GetPrimaryException(Exception exception) string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) { - if (WithException && logEvent.Exception != null) + if (WithException) return null; else return (Raw ? logEvent.Message : logEvent.FormattedMessage) ?? string.Empty; diff --git a/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs index b025bb5ffd..a1098657b8 100644 --- a/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs @@ -62,7 +62,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) var messages = ScopeContext.GetAllNestedStateList(); for (int i = 0; i < messages.Count; ++i) { - indent = indent ?? Indent?.Render(logEvent, cacheLayoutResult: false) ?? string.Empty; + indent = indent ?? Indent?.Render(logEvent) ?? string.Empty; builder.Append(indent); } } diff --git a/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs index 5d86021558..60b063bd50 100644 --- a/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs @@ -134,7 +134,7 @@ protected override string RenderInner(LogEventInfo logEvent) { if (Cached) { - var newCacheKey = CacheKey?.Render(logEvent, cacheLayoutResult: false) ?? string.Empty; + var newCacheKey = CacheKey?.Render(logEvent) ?? string.Empty; var cachedValue = LookupValidCachedValue(logEvent, newCacheKey); if (cachedValue is null) diff --git a/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs index a3b1ceaf1e..6221a8e67c 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs @@ -106,12 +106,8 @@ protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBui private bool TryGetRawPropertyValue(LogEventInfo logEvent, out object propertyValue) { - if (Inner != null && - Inner.TryGetRawValue(logEvent, out var rawValue) && - TryGetPropertyValue(rawValue, out propertyValue)) - { + if (Inner?.TryGetRawValue(logEvent, out var rawValue) == true && TryGetPropertyValue(rawValue, out propertyValue)) return true; - } propertyValue = null; return false; diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs index 85e3978bb1..6bd594ce2a 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs @@ -90,14 +90,14 @@ string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) if (TryGetStringValue(out var innerLayout, out var whenEmptyLayout)) { - var innerValue = innerLayout.Render(logEvent, cacheLayoutResult: false); + var innerValue = innerLayout.Render(logEvent); if (!string.IsNullOrEmpty(innerValue)) { return innerValue; } // render WhenEmpty when the inner layout was empty - return whenEmptyLayout.Render(logEvent, cacheLayoutResult: false); + return whenEmptyLayout.Render(logEvent); } _skipStringValueRenderer = true; @@ -129,7 +129,7 @@ bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) } else { - var innerResult = Inner?.Render(logEvent, cacheLayoutResult: false); // Beware this can be very expensive call + var innerResult = Inner?.Render(logEvent); // Beware this can be very expensive call if (!string.IsNullOrEmpty(innerResult)) { value = null; diff --git a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs index 7c7d8d1bfe..f61eb0d6b2 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs @@ -123,7 +123,7 @@ protected virtual string Transform(LogEventInfo logEvent, string text) /// Contents of inner layout. protected virtual string RenderInner(LogEventInfo logEvent) { - return Inner?.Render(logEvent, cacheLayoutResult: false) ?? string.Empty; + return Inner?.Render(logEvent) ?? string.Empty; } } } diff --git a/src/NLog/Layouts/Layout.cs b/src/NLog/Layouts/Layout.cs index 2a11a56ede..b3bc535351 100644 --- a/src/NLog/Layouts/Layout.cs +++ b/src/NLog/Layouts/Layout.cs @@ -175,13 +175,11 @@ internal static LayoutRenderers.FuncLayoutRenderer CreateFuncLayoutRenderer(Func /// Precalculates the layout for the specified log event and stores the result /// in per-log event cache. /// - /// Only if the layout doesn't have [ThreadAgnostic] and doesn't contain layouts with [ThreadAgnostic]. + /// Skips context capture when Layout have [ThreadAgnostic], and only contains layouts with [ThreadAgnostic]. /// /// The log event. /// - /// Calling this method enables you to store the log event in a buffer - /// and/or potentially evaluate it in another thread even though the - /// layout may contain thread-dependent renderer. + /// Override this method to make it conditional whether to capture Layout output-value for /// public virtual void Precalculate(LogEventInfo logEvent) { @@ -189,7 +187,7 @@ public virtual void Precalculate(LogEventInfo logEvent) { using (var localTarget = new AppendBuilderCreator(true)) { - RenderAppendBuilder(logEvent, localTarget.Builder, true); + PrecalculateCachedLayoutValue(logEvent, localTarget.Builder); } } } @@ -201,35 +199,21 @@ public virtual void Precalculate(LogEventInfo logEvent) /// The logging event. /// The formatted output as string. public string Render(LogEventInfo logEvent) - { - return Render(logEvent, true); - } - - internal string Render(LogEventInfo logEvent, bool cacheLayoutResult) { if (!IsInitialized) { Initialize(LoggingConfiguration); } - bool lookupCacheLayout = !ThreadAgnostic || ThreadAgnosticImmutable; - if (lookupCacheLayout) + if (!ThreadAgnostic || ThreadAgnosticImmutable) { - object cachedValue; - if (logEvent.TryGetCachedLayoutValue(this, out cachedValue)) + if (logEvent.TryGetCachedLayoutValue(this, out var cachedValue)) { return cachedValue?.ToString() ?? string.Empty; } } - string layoutValue = GetFormattedMessage(logEvent) ?? string.Empty; - if (lookupCacheLayout && cacheLayoutResult) - { - // Would be nice to only do this in Precalculate(), but we need to ensure internal cache - // is updated for custom Layouts that overrides Precalculate (without calling base.Precalculate) - logEvent.AddCachedLayoutValue(this, layoutValue); - } - return layoutValue; + return GetFormattedMessage(logEvent) ?? string.Empty; } /// @@ -239,23 +223,6 @@ internal string Render(LogEventInfo logEvent, bool cacheLayoutResult) /// The logging event. /// Appends the formatted output to target public void Render(LogEventInfo logEvent, StringBuilder target) - { - RenderAppendBuilder(logEvent, target, false); - } - - internal virtual void PrecalculateBuilder(LogEventInfo logEvent, StringBuilder target) - { - Precalculate(logEvent); // Allow custom Layouts to also work - } - - /// - /// Optimized version of that works best when - /// override of is available. - /// - /// The logging event. - /// Appends the string representing log event to target - /// Should rendering result be cached on LogEventInfo - private void RenderAppendBuilder(LogEventInfo logEvent, StringBuilder target, bool cacheLayoutResult) { if (!IsInitialized) { @@ -264,26 +231,35 @@ private void RenderAppendBuilder(LogEventInfo logEvent, StringBuilder target, bo if (!ThreadAgnostic || ThreadAgnosticImmutable) { - object cachedValue; - if (logEvent.TryGetCachedLayoutValue(this, out cachedValue)) + if (logEvent.TryGetCachedLayoutValue(this, out var cachedValue)) { target.Append(cachedValue?.ToString()); return; } } - else + + RenderFormattedMessage(logEvent, target); + } + + internal virtual void PrecalculateBuilder(LogEventInfo logEvent, StringBuilder target) + { + Precalculate(logEvent); // Allow custom Layouts to also work + } + + private void PrecalculateCachedLayoutValue(LogEventInfo logEvent, StringBuilder target) + { + if (!IsInitialized) { - cacheLayoutResult = false; + Initialize(LoggingConfiguration); } - using (var localTarget = new AppendBuilderCreator(target, cacheLayoutResult)) + if (!ThreadAgnostic || ThreadAgnosticImmutable) { - RenderFormattedMessage(logEvent, localTarget.Builder); - if (cacheLayoutResult) - { - // when needed as it generates garbage - logEvent.AddCachedLayoutValue(this, localTarget.Builder.ToString()); - } + if (logEvent.TryGetCachedLayoutValue(this, out var _)) + return; // Precalculate already captured + + RenderFormattedMessage(logEvent, target); + logEvent.AddCachedLayoutValue(this, target.ToString()); } } @@ -508,14 +484,14 @@ internal void PrecalculateBuilderInternal(LogEventInfo logEvent, StringBuilder t { if (precalculateLayout is null) { - RenderAppendBuilder(logEvent, target, true); + PrecalculateCachedLayoutValue(logEvent, target); } else { foreach (var layout in precalculateLayout) { + target.ClearBuilder(); layout.PrecalculateBuilder(logEvent, target); - target.Length = 0; } } } diff --git a/src/NLog/Layouts/SimpleLayout.cs b/src/NLog/Layouts/SimpleLayout.cs index f2d1ec5e08..400c5135b2 100644 --- a/src/NLog/Layouts/SimpleLayout.cs +++ b/src/NLog/Layouts/SimpleLayout.cs @@ -57,7 +57,7 @@ namespace NLog.Layouts [Layout("SimpleLayout")] [ThreadAgnostic] [AppDomainFixedOutput] - public class SimpleLayout : Layout, IUsesStackTrace, IStringValueRenderer + public sealed class SimpleLayout : Layout, IUsesStackTrace, IStringValueRenderer { private readonly IRawValue _rawValueRenderer; private IStringValueRenderer _stringValueRenderer; @@ -286,11 +286,9 @@ protected override void InitializeLayout() catch (Exception exception) { //also check IsErrorEnabled, otherwise 'MustBeRethrown' writes it to Error - - //check for performance if (InternalLogger.IsWarnEnabled || InternalLogger.IsErrorEnabled) { - InternalLogger.Warn(exception, "Exception in '{0}.InitializeLayout()'", renderer.GetType().FullName); + InternalLogger.Warn(exception, "Exception in '{0}.Initialize()'", renderer.GetType()); } if (exception.MustBeRethrown()) @@ -306,70 +304,49 @@ protected override void InitializeLayout() /// public override void Precalculate(LogEventInfo logEvent) { - if (MustPrecalculateLayoutValue(logEvent)) + if (PrecalculateMustRenderLayoutValue(logEvent)) { - Render(logEvent); + using (var localTarget = new AppendBuilderCreator(true)) + { + RenderFormattedMessage(logEvent, localTarget.Builder); + logEvent.AddCachedLayoutValue(this, localTarget.Builder.ToString()); + } } } internal override void PrecalculateBuilder(LogEventInfo logEvent, StringBuilder target) { - if (MustPrecalculateLayoutValue(logEvent)) + if (PrecalculateMustRenderLayoutValue(logEvent)) { - PrecalculateBuilderInternal(logEvent, target, null); + RenderFormattedMessage(logEvent, target); + logEvent.AddCachedLayoutValue(this, target.ToString()); } } - private bool MustPrecalculateLayoutValue(LogEventInfo logEvent) - { - return _rawValueRenderer is null - ? (!ThreadAgnostic || ThreadAgnosticImmutable) - : !IsRawValueImmutable(logEvent); - } - - private bool IsRawValueImmutable(LogEventInfo logEvent) + private bool PrecalculateMustRenderLayoutValue(LogEventInfo logEvent) { - try + if (!IsInitialized) { - if (!IsInitialized) - { - Initialize(LoggingConfiguration); - } - - if (ThreadAgnostic) - { - if (ThreadAgnosticImmutable) - { - // If raw value doesn't have the ability to mutate, then we can skip precalculate - var success = _rawValueRenderer.TryGetRawValue(logEvent, out var value); - if (success && IsRawValueImmutable(value)) - return true; - } - else - { - return true; - } - } + Initialize(LoggingConfiguration); + } + if (ThreadAgnostic && !ThreadAgnosticImmutable) return false; - } - catch (Exception exception) - { - //also check IsErrorEnabled, otherwise 'MustBeRethrown' writes it to Error - //check for performance - if (InternalLogger.IsWarnEnabled || InternalLogger.IsErrorEnabled) - { - InternalLogger.Warn(exception, "Exception in precalculate using '{0}.TryGetRawValue()'", _rawValueRenderer?.GetType()); - } + if (_rawValueRenderer != null && TryGetRawValue(logEvent, out var rawValue) && IsRawValueImmutable(rawValue)) + return false; // If raw value is immutable, then we can skip precalculate-caching - if (exception.MustBeRethrown()) - { - throw; - } + if (logEvent.TryGetCachedLayoutValue(this, out var _)) + return false; + if (IsSimpleStringText) + { + var cachedLayout = GetFormattedMessage(logEvent); + logEvent.AddCachedLayoutValue(this, cachedLayout); return false; } + + return true; } private static bool IsRawValueImmutable(object value) @@ -380,38 +357,42 @@ private static bool IsRawValueImmutable(object value) /// internal override bool TryGetRawValue(LogEventInfo logEvent, out object rawValue) { - if (_rawValueRenderer != null) + if (_rawValueRenderer is null) { - try - { - if (!IsInitialized) - { - Initialize(LoggingConfiguration); - } - - if ((!ThreadAgnostic || ThreadAgnosticImmutable) && logEvent.TryGetCachedLayoutValue(this, out _)) - { - rawValue = null; - return false; // Raw-Value has been precalculated, so not available - } + rawValue = null; + return false; + } + return TryGetSafeRawValue(logEvent, out rawValue); + } - var success = _rawValueRenderer.TryGetRawValue(logEvent, out rawValue); - return success; + private bool TryGetSafeRawValue(LogEventInfo logEvent, out object rawValue) + { + try + { + if (!IsInitialized) + { + Initialize(LoggingConfiguration); } - catch (Exception exception) + + if ((!ThreadAgnostic || ThreadAgnosticImmutable) && logEvent.TryGetCachedLayoutValue(this, out _)) { - //also check IsErrorEnabled, otherwise 'MustBeRethrown' writes it to Error + rawValue = null; + return false; // Raw-Value has been precalculated, so not available + } - //check for performance - if (InternalLogger.IsWarnEnabled || InternalLogger.IsErrorEnabled) - { - InternalLogger.Warn(exception, "Exception in TryGetRawValue using '{0}.TryGetRawValue()'", _rawValueRenderer?.GetType()); - } + return _rawValueRenderer.TryGetRawValue(logEvent, out rawValue); + } + catch (Exception exception) + { + //also check IsErrorEnabled, otherwise 'MustBeRethrown' writes it to Error + if (InternalLogger.IsWarnEnabled || InternalLogger.IsErrorEnabled) + { + InternalLogger.Warn(exception, "Exception in TryGetRawValue using '{0}.TryGetRawValue()'", _rawValueRenderer?.GetType()); + } - if (exception.MustBeRethrown()) - { - throw; - } + if (exception.MustBeRethrown()) + { + throw; } } @@ -423,63 +404,48 @@ internal override bool TryGetRawValue(LogEventInfo logEvent, out object rawValue protected override string GetFormattedMessage(LogEventInfo logEvent) { if (IsFixedText) - { return FixedText; - } - if (_stringValueRenderer != null) + string stringValue = string.Empty; + if (_stringValueRenderer is null || !TryGetSafeStringValue(logEvent, out stringValue)) + return RenderAllocateBuilder(logEvent); + else + return stringValue; + } + + private bool TryGetSafeStringValue(LogEventInfo logEvent, out string stringValue) + { + try { - try + if (!IsInitialized) { - string stringValue = _stringValueRenderer.GetFormattedString(logEvent); - if (stringValue != null) - return stringValue; - - _stringValueRenderer = null; // Optimization is not possible + Initialize(LoggingConfiguration); } - catch (Exception exception) - { - //also check IsErrorEnabled, otherwise 'MustBeRethrown' writes it to Error - //check for performance - if (InternalLogger.IsWarnEnabled || InternalLogger.IsErrorEnabled) - { - InternalLogger.Warn(exception, "Exception in '{0}.GetFormattedString()'", _stringValueRenderer.GetType().FullName); - } - if (exception.MustBeRethrown()) - { - throw; - } + stringValue = _stringValueRenderer.GetFormattedString(logEvent); + if (stringValue is null) + { + _stringValueRenderer = null; // Optimization is not possible + return false; } + return true; } - - return RenderAllocateBuilder(logEvent); - } - - private void RenderAllRenderers(LogEventInfo logEvent, StringBuilder target) - { - foreach (var renderer in _layoutRenderers) + catch (Exception exception) { - try + //also check IsErrorEnabled, otherwise 'MustBeRethrown' writes it to Error + if (InternalLogger.IsWarnEnabled || InternalLogger.IsErrorEnabled) { - renderer.RenderAppendBuilder(logEvent, target); + InternalLogger.Warn(exception, "Exception in '{0}.GetFormattedString()'", _stringValueRenderer?.GetType()); } - catch (Exception exception) - { - //also check IsErrorEnabled, otherwise 'MustBeRethrown' writes it to Error - //check for performance - if (InternalLogger.IsWarnEnabled || InternalLogger.IsErrorEnabled) - { - InternalLogger.Warn(exception, "Exception in '{0}.Append()'", renderer.GetType().FullName); - } - - if (exception.MustBeRethrown()) - { - throw; - } + if (exception.MustBeRethrown()) + { + throw; } } + + stringValue = null; + return false; } /// @@ -487,7 +453,10 @@ protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuil { if (FixedText is null) { - RenderAllRenderers(logEvent, target); + foreach (var renderer in _layoutRenderers) + { + renderer.RenderAppendBuilder(logEvent, target); + } } else { @@ -500,7 +469,7 @@ string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) if (IsFixedText) return FixedText; if (IsSimpleStringText) - return Render(logEvent, false); + return Render(logEvent); return null; } } diff --git a/src/NLog/Layouts/Typed/Layout.cs b/src/NLog/Layouts/Typed/Layout.cs index dbac617cde..aad8b3640c 100644 --- a/src/NLog/Layouts/Typed/Layout.cs +++ b/src/NLog/Layouts/Typed/Layout.cs @@ -238,7 +238,7 @@ public static Layout FromMethod(Func layoutMethod, LayoutRen return new Layout(layoutMethod, options); } - private void PrecalculateInnerLayout(LogEventInfo logEvent, StringBuilder target) + private void PrecalculateInnerLayout(LogEventInfo logEvent, [CanBeNull] StringBuilder target) { if (IsFixed || (_layoutValue.ThreadAgnostic && !_layoutValue.ThreadAgnosticImmutable)) return; diff --git a/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs b/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs index 6ed93bf295..14dbaf73a3 100644 --- a/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs +++ b/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs @@ -267,7 +267,7 @@ public StringLayoutTypeValue(Layout layout) public object RenderObjectValue(LogEventInfo logEvent, StringBuilder stringBuilder) { - return _innerLayout.Render(logEvent, false); + return _innerLayout.Render(logEvent); } public override string ToString() diff --git a/tests/NLog.UnitTests/Layouts/CsvLayoutTests.cs b/tests/NLog.UnitTests/Layouts/CsvLayoutTests.cs index 5c16f1e356..66b7ea5c7f 100644 --- a/tests/NLog.UnitTests/Layouts/CsvLayoutTests.cs +++ b/tests/NLog.UnitTests/Layouts/CsvLayoutTests.cs @@ -319,6 +319,9 @@ public void CsvLayoutCachingTest() Message = "hello, world" }; + csvLayout.Precalculate(e1); + csvLayout.Precalculate(e2); + var r11 = csvLayout.Render(e1); var r12 = csvLayout.Render(e1); var r21 = csvLayout.Render(e2); diff --git a/tests/NLog.UnitTests/Layouts/SimpleLayoutOutputTests.cs b/tests/NLog.UnitTests/Layouts/SimpleLayoutOutputTests.cs index f247bf635e..6e8dc233cc 100644 --- a/tests/NLog.UnitTests/Layouts/SimpleLayoutOutputTests.cs +++ b/tests/NLog.UnitTests/Layouts/SimpleLayoutOutputTests.cs @@ -75,6 +75,7 @@ public void SimpleLayoutCachingTest() { var l = new SimpleLayout("xx${threadid}yy"); var ev = LogEventInfo.CreateNullEvent(); + l.Precalculate(ev); string output1 = l.Render(ev); string output2 = l.Render(ev); Assert.Same(output1, output2); @@ -182,8 +183,9 @@ public void TryGetRawValue_SingleLayoutRender_ShouldGiveRawValue() public void TryGetRawValue_MultipleLayoutRender_ShouldGiveNullRawValue() { // Arrange - SimpleLayout l = "${sequenceid} "; + SimpleLayout l = "${sequenceid}${threadid}"; var logEventInfo = LogEventInfo.CreateNullEvent(); + l.Precalculate(logEventInfo); // Act var success = l.TryGetRawValue(logEventInfo, out var value); From 2b7834801cd13e55faf42e2d2111d1aa076122f1 Mon Sep 17 00:00:00 2001 From: Pavan Kumar <97884852+Pavan8374@users.noreply.github.com> Date: Sat, 26 Apr 2025 21:56:40 +0530 Subject: [PATCH 095/224] Removed static constructor from LogFactory (#5762) --- src/NLog/LogFactory.cs | 49 ++++++++-------- tests/NLog.UnitTests/ApiTests.cs | 97 +++++++++++++++++++++++++++++++- 2 files changed, 119 insertions(+), 27 deletions(-) diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 01e07b2239..5d6be8f295 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -90,15 +90,31 @@ public class LogFactory : IDisposable /// public event EventHandler ConfigurationChanged; - private static event EventHandler LoggerShutdown; - /// - /// Initializes static members of the LogManager class. + /// Event that is raised when the current Process / AppDomain terminates. /// - static LogFactory() + private static event EventHandler LoggerShutdown { - RegisterEvents(DefaultAppEnvironment); + add + { + if (_loggerShutdown is null) + { + InternalLogger.Debug("Registered shutdown event handler for ProcessExit."); + DefaultAppEnvironment.ProcessExit += OnLoggerShutdown; + } + _loggerShutdown += value; + } + remove + { + _loggerShutdown -= value; + if (_loggerShutdown is null && defaultAppEnvironment != null) + { + InternalLogger.Debug("Unregistered shutdown event handler for ProcessExit."); + defaultAppEnvironment.ProcessExit -= OnLoggerShutdown; + } + } } + private static event EventHandler _loggerShutdown; /// /// Initializes a new instance of the class. @@ -1225,30 +1241,11 @@ void IDisposable.Dispose() } } - private static void RegisterEvents(IAppEnvironment appEnvironment) - { - if (appEnvironment is null) return; - - try - { - appEnvironment.ProcessExit += OnLoggerShutdown; - } - catch (Exception exception) - { - InternalLogger.Warn(exception, "Error setting up termination events."); - - if (exception.MustBeRethrown()) - { - throw; - } - } - } - private static void OnLoggerShutdown(object sender, EventArgs args) { try { - LoggerShutdown?.Invoke(sender, args); + _loggerShutdown?.Invoke(null, args); } catch (Exception ex) { @@ -1258,7 +1255,7 @@ private static void OnLoggerShutdown(object sender, EventArgs args) } finally { - LoggerShutdown = null; + _loggerShutdown = null; if (defaultAppEnvironment != null) { defaultAppEnvironment.ProcessExit -= OnLoggerShutdown; // Unregister from AppDomain diff --git a/tests/NLog.UnitTests/ApiTests.cs b/tests/NLog.UnitTests/ApiTests.cs index 7f24f3c7ff..445da11ad5 100644 --- a/tests/NLog.UnitTests/ApiTests.cs +++ b/tests/NLog.UnitTests/ApiTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen // // All rights reserved. @@ -37,6 +37,7 @@ namespace NLog.UnitTests using System.Collections.Generic; using System.Linq; using System.Reflection; + using System.Runtime.CompilerServices; using System.Text; using NLog.Config; using Xunit; @@ -466,5 +467,99 @@ public void ValidateConfigurationItemFactory() Assert.Empty(missingTypes); } + + [Fact] + public void ShouldNotHaveExplicitStaticConstructors() + { + // Known allowed explicit static constructors (full type names) + var knownStaticConstructors = new HashSet + { + // Explicit static constructors + "NLog.LogFactory", + + // Likely compiler generated or false-positives. + "NLog.GlobalDiagnosticsContext", + "NLog.LogEventInfo", + "NLog.Logger", + "NLog.LogLevel", + "NLog.LogManager", + "NLog.ScopeContext", + "NLog.Time.TimeSource", + "NLog.Targets.ConsoleRowHighlightingRule", + "NLog.Targets.ConsoleTargetHelper", + "NLog.Targets.DefaultJsonSerializer", + "NLog.Targets.FileTarget", + "NLog.Targets.LineEndingMode", + "NLog.Targets.FileArchiveHandlers.DisabledFileArchiveHandler", + "NLog.MessageTemplates.MessageTemplateParameters", + "NLog.MessageTemplates.TemplateEnumerator", + "NLog.MessageTemplates.ValueFormatter", + "NLog.Layouts.LayoutParser", + "NLog.Layouts.ValueTypeLayoutInfo", + "NLog.Layouts.XmlElementBase", + "NLog.LayoutRenderers.AllEventPropertiesLayoutRenderer", + "NLog.LayoutRenderers.CounterLayoutRenderer", + "NLog.LayoutRenderers.ExceptionLayoutRenderer", + "NLog.LayoutRenderers.LevelLayoutRenderer", + "NLog.Internal.AppendBuilderCreator", + "NLog.Internal.CallSiteInformation", + "NLog.Internal.ExceptionMessageFormatProvider", + "NLog.Internal.FactoryHelper", + "NLog.Internal.LogMessageStringFormatter", + "NLog.Internal.LogMessageTemplateFormatter", + "NLog.Internal.PathHelpers", + "NLog.Internal.PropertiesDictionary", + "NLog.Internal.PropertyHelper", + "NLog.Internal.SingleCallContinuation", + "NLog.Internal.StackTraceUsageUtils", + "NLog.Internal.StringBuilderExt", + "NLog.Internal.TargetWithFilterChain", + "NLog.Internal.UrlHelper", + "NLog.Internal.XmlHelper", + "NLog.Config.ConfigurationItemFactory", + "NLog.Config.InstallationContext", + "NLog.Config.LoggingRuleLevelFilter", + "NLog.Config.PropertyTypeConverter", + "NLog.Config.XmlLoggingConfiguration", + "NLog.Conditions.ConditionExpression", + "NLog.Conditions.ConditionRelationalExpression", + "NLog.Conditions.ConditionTokenizer", + "NLog.Common.InternalLogger", + "NLog.Internal.ObjectReflectionCache+ObjectPropertyList", + "NLog.Internal.ObjectReflectionCache+ObjectPropertyInfos", + "NLog.Internal.ObjectReflectionCache+EmptyDictionaryEnumerator", + "NLog.Internal.PropertiesDictionary+PropertyKeyComparer", + "NLog.Internal.SingleItemOptimizedHashSet`1+ReferenceEqualityComparer", + "NLog.Internal.ArrayHelper+EmptyArray`1", + "NLog.Config.LoggerNameMatcher+NoneLoggerNameMatcher", + "NLog.Config.LoggerNameMatcher+AllLoggerNameMatcher", + "NLog.Config.LoggingConfigurationParser+ValidatedConfigurationElement" + }; + + var typesWithStaticConstructors = allTypes + // Exclude compiler-generated types (e.g., lambdas, nested <>c classes) + .Where(type => !type.IsDefined(typeof(CompilerGeneratedAttribute), inherit: false)) + .Select(type => new + { + TypeName = type.FullName, + StaticConstructor = type.TypeInitializer + }) + + // Check for non-compiler-generated static constructors + .Where(t => t.StaticConstructor != null + && !t.StaticConstructor.IsDefined(typeof(CompilerGeneratedAttribute), inherit: false)) + .Select(t => t.TypeName) + .Distinct() + .ToList(); + + var newConstructors = typesWithStaticConstructors + .Where(type => !knownStaticConstructors.Contains(type)) + .ToList(); + + if (newConstructors.Count > 0) + { + Assert.Fail($"Found new explicit static constructors in:\n{string.Join("\n", newConstructors)}"); + } + } } } From 95c9e94f7cff7e33c0171a4b270cb35029855b4f Mon Sep 17 00:00:00 2001 From: Anant Asati <85818017+ana1250@users.noreply.github.com> Date: Sat, 26 Apr 2025 22:24:24 +0530 Subject: [PATCH 096/224] Log4JXmlEventLayout using Nlog XmlLayout (#5774) --- .../Layouts/Log4JXmlEventLayout.cs | 216 ++++++--- .../Layouts/Log4JXmlEventLayoutRenderer.cs | 446 ------------------ .../Layouts/Log4JXmlEventParameter.cs | 2 +- .../Targets/ChainsawTarget.cs | 56 +-- src/NLog/Config/ConfigurationItemFactory.cs | 1 - src/NLog/Internal/XmlHelper.cs | 55 +++ .../XmlEncodeLayoutRendererWrapper.cs | 28 +- src/NLog/Layouts/XML/XmlElement.cs | 9 + .../Log4JXmlTests.cs | 175 ++++++- 9 files changed, 424 insertions(+), 564 deletions(-) delete mode 100644 src/NLog.Targets.Network/Layouts/Log4JXmlEventLayoutRenderer.cs diff --git a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs index 909ca5b447..b6284ee263 100644 --- a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs @@ -44,9 +44,6 @@ namespace NLog.Layouts /// A specialized layout that renders Log4j-compatible XML events. /// /// - /// - /// This layout is not meant to be used explicitly. Instead you can use ${log4jxmlevent} layout renderer. - /// /// See NLog Wiki /// /// Documentation on NLog Wiki @@ -55,58 +52,159 @@ namespace NLog.Layouts [ThreadAgnostic] public class Log4JXmlEventLayout : Layout { + + private static readonly DateTime log4jDateBase = new DateTime(1970, 1, 1); + private IList _parameters = new List(); + + /// - /// Initializes a new instance of the class. + /// Gets inner XML layout. /// - public Log4JXmlEventLayout() + public XmlLayout InnerXml { get; } = new XmlLayout(); + + /// + protected override void InitializeLayout() { - Renderer = new Log4JXmlEventLayoutRenderer(); - Parameters = new List(); - Renderer.Parameters = Parameters; - } + InnerXml.Attributes.Clear(); + InnerXml.Elements.Clear(); - /// - /// Gets the instance that renders log events. - /// - public Log4JXmlEventLayoutRenderer Renderer { get; } + InnerXml.ElementName = "log4j:event"; + + InnerXml.Attributes.Add(new XmlAttribute("logger", LoggerName)); + InnerXml.Attributes.Add(new XmlAttribute("level", "${level:uppercase=true}") { IncludeEmptyValue = true }); + InnerXml.Attributes.Add(new XmlAttribute("timestamp", Layout.FromMethod(evt => (long)(evt.TimeStamp.ToUniversalTime() - log4jDateBase).TotalMilliseconds, LayoutRenderOptions.ThreadAgnostic))); + InnerXml.Attributes.Add(new XmlAttribute("thread", "${threadid}") { IncludeEmptyValue = true }); + + InnerXml.Elements.Add(new XmlElement("log4j:message", FormattedMessage ?? "${message}")); + InnerXml.Elements.Add(new XmlElement("log4j:throwable", "${exception:format=ToString}") + { + CDataEncode = WriteThrowableCData, + }); + + if (IncludeCallSite || IncludeSourceInfo) + { + var locationInfo = new XmlElement("log4j:locationInfo", null) + { + Attributes = + { + new XmlAttribute("class", "${callsite:methodName=false}"), + new XmlAttribute("method", "${callsite:className=false}") + } + }; + + if (IncludeSourceInfo) + { + locationInfo.Attributes.Add(new XmlAttribute("file", "${callsite-filename}")); + locationInfo.Attributes.Add(new XmlAttribute("line", "${callsite-linenumber}")); + } + + InnerXml.Elements.Add(locationInfo); + } + + if (IncludeScopeNested) + { + var separator = ScopeNestedSeparator; + Layout scopeNested = string.IsNullOrEmpty(separator) ? "${scopenested}" : ("${scopenested:separator=" + separator + "}"); + InnerXml.Elements.Add(new XmlElement("log4j:NDC", scopeNested)); + } + + var dataProperties = new XmlElement("log4j:properties", null) + { + PropertiesElementName = "log4j:data", + PropertiesElementKeyAttribute = "name", + PropertiesElementValueAttribute = "value", + IncludeEventProperties = IncludeEventProperties, + IncludeScopeProperties = IncludeScopeProperties + }; + + foreach (var parameter in Parameters) + { + var propertyElement = new XmlElement("log4j:data", null) + { + IncludeEmptyValue = parameter.IncludeEmptyValue + }; + + propertyElement.Attributes.Add(new XmlAttribute("name", parameter.Name)); + propertyElement.Attributes.Add(new XmlAttribute("value", parameter.Layout)); + dataProperties.Elements.Add(propertyElement); + } + + + var appProperty = new XmlElement("log4j:data", null); + appProperty.Attributes.Add(new XmlAttribute("name", "log4japp")); + appProperty.Attributes.Add(new XmlAttribute("value", AppInfo)); + dataProperties.Elements.Add(appProperty); + + var machineProperty = new XmlElement("log4j:data", null); + machineProperty.Attributes.Add(new XmlAttribute("name", "log4jmachinename")); + machineProperty.Attributes.Add(new XmlAttribute("value", "${machinename}")); + dataProperties.Elements.Add(machineProperty); + + InnerXml.Elements.Add(dataProperties); + + base.InitializeLayout(); + } /// /// Gets the collection of parameters. Each parameter contains a mapping /// between NLog layout and a named parameter. /// - /// [ArrayParameter(typeof(Log4JXmlEventParameter), "parameter")] - public IList Parameters { get => Renderer.Parameters; set => Renderer.Parameters = value; } + public IList Parameters + { + get => _parameters; + set => _parameters = value ?? new List(); + } /// /// Gets or sets the option to include all properties from the log events /// /// - public bool IncludeEventProperties - { - get => Renderer.IncludeEventProperties; - set => Renderer.IncludeEventProperties = value; - } + public bool IncludeEventProperties { get; set; } /// /// Gets or sets whether to include the contents of the properties-dictionary. /// /// - public bool IncludeScopeProperties - { - get => Renderer.IncludeScopeProperties; - set => Renderer.IncludeScopeProperties = value; - } + public bool IncludeScopeProperties { get => _includeScopeProperties ?? (_includeMdlc == true || _includeMdc == true); set => _includeScopeProperties = value; } + private bool? _includeScopeProperties; /// /// Gets or sets whether to include log4j:NDC in output from nested context. /// /// - public bool IncludeScopeNested - { - get => Renderer.IncludeScopeNested; - set => Renderer.IncludeScopeNested = value; - } + public bool IncludeScopeNested { get => _includeScopeNested ?? (_includeNdlc == true || _includeNdc == true); set => _includeScopeNested = value; } + private bool? _includeScopeNested; + + /// + /// Gets or sets a value indicating whether to include NLog-specific extensions to log4j schema. + /// + /// + [Obsolete("Non standard extension to the Log4j-XML format. Marked obsolete with NLog 6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + public bool IncludeNLogData { get; set; } + + /// + /// Obsolete and replaced by with NLog v5. + /// + /// Gets or sets the stack separator for log4j:NDC in output from nested context. + /// + /// + [Obsolete("Replaced by NdcItemSeparator. Marked obsolete on NLog 5.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + public string NdlcItemSeparator { get => ScopeNestedSeparator; set => ScopeNestedSeparator = value; } + + /// + /// Gets or sets the stack separator for log4j:NDC in output from nested context. + /// + /// + public string ScopeNestedSeparator { get; set; } + + /// + /// Gets or sets the stack separator for log4j:NDC in output from nested context. + /// + /// + public string NdcItemSeparator { get => ScopeNestedSeparator; set => ScopeNestedSeparator = value; } /// /// Obsolete and replaced by with NLog v5. @@ -126,13 +224,15 @@ public bool IncludeScopeNested /// [Obsolete("Replaced by IncludeScopeProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] - public bool IncludeMdc { get => Renderer.IncludeMdc; set => Renderer.IncludeMdc = value; } + public bool IncludeMdc { get => _includeMdc ?? false; set => _includeMdc = value; } + private bool? _includeMdc; /// /// Gets or sets whether to include log4j:NDC in output from nested context. /// /// - public bool IncludeNdc { get => Renderer.IncludeNdc; set => Renderer.IncludeNdc = value; } + public bool IncludeNdc { get => _includeNdc ?? false; set => _includeNdc = value; } + private bool? _includeNdc; /// /// Obsolete and replaced by with NLog v5. @@ -142,7 +242,8 @@ public bool IncludeScopeNested /// [Obsolete("Replaced by IncludeScopeProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] - public bool IncludeMdlc { get => Renderer.IncludeMdlc; set => Renderer.IncludeMdlc = value; } + public bool IncludeMdlc { get => _includeMdlc ?? false; set => _includeMdlc = value; } + private bool? _includeMdlc; /// /// Obsolete and replaced by with NLog v5. @@ -150,80 +251,59 @@ public bool IncludeScopeNested /// Gets or sets a value indicating whether to include contents of the stack. /// /// - [Obsolete("Replaced by IncludeScopeNested. Marked obsolete on NLog 5.0")] + [Obsolete("Replaced by IncludeNdc. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] - public bool IncludeNdlc { get => Renderer.IncludeNdlc; set => Renderer.IncludeNdlc = value; } + public bool IncludeNdlc { get => _includeNdlc ?? false; set => _includeNdlc = value; } + private bool? _includeNdlc; /// /// Gets or sets the log4j:event logger-xml-attribute. Default: ${logger} /// /// - public Layout LoggerName - { - get => Renderer.LoggerName; - set => Renderer.LoggerName = value; - } + public Layout LoggerName { get; set; } = "${logger}"; /// /// Gets or sets the log4j:event message-xml-element. Default: ${message} /// /// - public Layout FormattedMessage - { - get => Renderer.FormattedMessage; - set => Renderer.FormattedMessage = value; - } + public Layout FormattedMessage { get; set; } /// /// Gets or sets the log4j:event log4japp-xml-element. By default it's the friendly name of the current AppDomain. /// /// - public Layout AppInfo - { - get => Renderer.AppInfo; - set => Renderer.AppInfo = value; - } + public Layout AppInfo { get; set; } /// /// Gets or sets whether the log4j:throwable xml-element should be written as CDATA /// - /// - public bool WriteThrowableCData - { - get => Renderer.WriteThrowableCData; - set => Renderer.WriteThrowableCData = value; - } + /// + public bool WriteThrowableCData { get; set; } /// /// Gets or sets a value indicating whether to include call site (class and method name) in the information sent over the network. /// /// - public bool IncludeCallSite - { - get => Renderer.IncludeCallSite; - set => Renderer.IncludeCallSite = value; - } + public bool IncludeCallSite { get; set; } /// /// Gets or sets a value indicating whether to include source info (file name and line number) in the information sent over the network. /// /// - public bool IncludeSourceInfo - { - get => Renderer.IncludeSourceInfo; - set => Renderer.IncludeSourceInfo = value; - } + public bool IncludeSourceInfo { get; set; } /// protected override string GetFormattedMessage(LogEventInfo logEvent) { - return Renderer.Render(logEvent); + var sb = new StringBuilder(1024); + RenderFormattedMessage(logEvent, sb); + return sb.ToString(); } /// protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) { - Renderer.AppendBuilder(logEvent, target); + InnerXml.Render(logEvent, target); } } } diff --git a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayoutRenderer.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayoutRenderer.cs deleted file mode 100644 index ade9eaa12d..0000000000 --- a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayoutRenderer.cs +++ /dev/null @@ -1,446 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.LayoutRenderers -{ - using System; - using System.Collections.Generic; - using System.ComponentModel; - using System.Globalization; - using System.Text; - using System.Xml; - using NLog.Common; - using NLog.Config; - using NLog.Layouts; - using NLog.Targets; - using NLog.Targets.Internal; - - /// - /// XML event description compatible with log4j, Chainsaw and NLogViewer. - /// - /// - /// See NLog Wiki - /// - /// Documentation on NLog Wiki - [LayoutRenderer("log4jxmlevent")] - public class Log4JXmlEventLayoutRenderer : LayoutRenderer, IUsesStackTrace - { - private static readonly DateTime log4jDateBase = new DateTime(1970, 1, 1); - - private static readonly string dummyNamespace = "https://nlog-project.org/dummynamespace/" + Guid.NewGuid(); - private static readonly string dummyNamespaceRemover = " xmlns:log4j=\"" + dummyNamespace + "\""; - - private readonly ScopeContextNestedStatesLayoutRenderer _scopeNestedLayoutRenderer = new ScopeContextNestedStatesLayoutRenderer(); - - /// - /// Initializes a new instance of the class. - /// - public Log4JXmlEventLayoutRenderer() - { - AppInfo = string.Format( - CultureInfo.InvariantCulture, - "{0}({1})", - AppDomain.CurrentDomain.FriendlyName, - System.Diagnostics.Process.GetCurrentProcess().Id); - - Parameters = new List(); - - try - { - _machineName = Environment.MachineName; - if (string.IsNullOrEmpty(_machineName)) - { - InternalLogger.Info("MachineName is not available."); - } - } - catch (Exception exception) - { - InternalLogger.Error(exception, "Error getting machine name."); - if (LogManager.ThrowExceptions) - { - throw; - } - - _machineName = string.Empty; - } - } - - /// - protected override void InitializeLayoutRenderer() - { - base.InitializeLayoutRenderer(); - - _xmlWriterSettings = new XmlWriterSettings - { - Indent = IndentXml, - ConformanceLevel = ConformanceLevel.Fragment, -#if !NET35 - NamespaceHandling = NamespaceHandling.OmitDuplicates, -#endif - IndentChars = " ", - }; - } - - /// - /// Gets or sets a value indicating whether to include NLog-specific extensions to log4j schema. - /// - /// - [Obsolete("Non standard extension to the Log4j-XML format. Marked obsolete with NLog 6.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public bool IncludeNLogData { get; set; } - - /// - /// Gets or sets a value indicating whether the XML should use spaces for indentation. - /// - /// - public bool IndentXml { get; set; } - - /// - /// Gets or sets the log4j:event logger-xml-attribute. Default: ${logger} - /// - /// - public Layout LoggerName { get; set; } - - /// - /// Gets or sets the log4j:event message-xml-element. Default: ${message} - /// - /// - public Layout FormattedMessage { get; set; } - - /// - /// Gets or sets the log4j:event log4japp-xml-element. By default it's the friendly name of the current AppDomain. - /// - /// - public Layout AppInfo { get; set; } - - /// - /// Gets or sets a value indicating whether to include call site (class and method name) in the information sent over the network. - /// - /// - public bool IncludeCallSite { get; set; } - - /// - /// Gets or sets a value indicating whether to include source info (file name and line number) in the information sent over the network. - /// - /// - public bool IncludeSourceInfo { get; set; } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Gets or sets a value indicating whether to include contents of the dictionary. - /// - /// - [Obsolete("Replaced by IncludeScopeProperties. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public bool IncludeMdc { get => _includeMdc ?? false; set => _includeMdc = value; } - private bool? _includeMdc; - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Gets or sets a value indicating whether to include contents of the dictionary. - /// - /// - [Obsolete("Replaced by IncludeScopeProperties. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public bool IncludeMdlc { get => _includeMdlc ?? false; set => _includeMdlc = value; } - private bool? _includeMdlc; - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Gets or sets a value indicating whether to include contents of the stack. - /// - /// - [Obsolete("Replaced by IncludeNdc. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public bool IncludeNdlc { get => _includeNdlc ?? false; set => _includeNdlc = value; } - private bool? _includeNdlc; - - /// - /// Gets or sets whether to include log4j:NDC in output from nested context. - /// - /// - public bool IncludeNdc { get => _includeNdc ?? false; set => _includeNdc = value; } - private bool? _includeNdc; - - /// - /// Gets or sets whether to include the contents of the properties-dictionary. - /// - /// - public bool IncludeScopeProperties { get => _includeScopeProperties ?? (_includeMdlc == true || _includeMdc == true); set => _includeScopeProperties = value; } - private bool? _includeScopeProperties; - - /// - /// Gets or sets whether to include log4j:NDC in output from nested context. - /// - /// - public bool IncludeScopeNested { get => _includeScopeNested ?? (_includeNdlc == true || _includeNdc == true); set => _includeScopeNested = value; } - private bool? _includeScopeNested; - - /// - /// Gets or sets the stack separator for log4j:NDC in output from nested context. - /// - /// - public string ScopeNestedSeparator - { - get => _scopeNestedLayoutRenderer.Separator; - set => _scopeNestedLayoutRenderer.Separator = value; - } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Gets or sets the stack separator for log4j:NDC in output from nested context. - /// - /// - [Obsolete("Replaced by NdcItemSeparator. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public string NdlcItemSeparator { get => ScopeNestedSeparator; set => ScopeNestedSeparator = value; } - - /// - /// Obsolete and replaced by with NLog v5. - /// - /// Gets or sets the option to include all properties from the log events - /// - /// - [Obsolete("Replaced by IncludeEventProperties. Marked obsolete on NLog 5.0")] - [EditorBrowsable(EditorBrowsableState.Never)] - public bool IncludeAllProperties { get => IncludeEventProperties; set => IncludeEventProperties = value; } - - /// - /// Gets or sets the option to include all properties from the log events - /// - /// - public bool IncludeEventProperties { get; set; } = true; - - /// - /// Gets or sets the stack separator for log4j:NDC in output from nested context. - /// - /// - public string NdcItemSeparator { get => ScopeNestedSeparator; set => ScopeNestedSeparator = value; } - - /// - /// Gets or sets whether the log4j:throwable xml-element should be written as CDATA - /// - /// - public bool WriteThrowableCData { get; set; } - - private readonly string _machineName; - - private XmlWriterSettings _xmlWriterSettings; - - /// - StackTraceUsage IUsesStackTrace.StackTraceUsage - { - get - { - if (IncludeSourceInfo) - return StackTraceUsage.WithCallSite | StackTraceUsage.WithCallSiteClassName | StackTraceUsage.WithSource; - else if (IncludeCallSite) - return StackTraceUsage.WithCallSite | StackTraceUsage.WithCallSiteClassName; - else - return StackTraceUsage.None; - } - } - - internal IList Parameters { get; set; } - - internal void AppendBuilder(LogEventInfo logEvent, StringBuilder builder) - { - Append(builder, logEvent); - } - - /// - protected override void Append(StringBuilder builder, LogEventInfo logEvent) - { - StringBuilder sb = new StringBuilder(); - using (XmlWriter xtw = XmlWriter.Create(sb, _xmlWriterSettings)) - { - xtw.WriteStartElement("log4j", "event", dummyNamespace); - xtw.WriteAttributeSafeString("logger", LoggerName?.Render(logEvent) ?? logEvent.LoggerName); - xtw.WriteAttributeString("level", logEvent.Level.Name.ToUpperInvariant()); - xtw.WriteAttributeString("timestamp", Convert.ToString((long)(logEvent.TimeStamp.ToUniversalTime() - log4jDateBase).TotalMilliseconds, CultureInfo.InvariantCulture)); - xtw.WriteAttributeString("thread", System.Threading.Thread.CurrentThread.ManagedThreadId.ToString(CultureInfo.InvariantCulture)); - - xtw.WriteElementSafeString("log4j", "message", dummyNamespace, FormattedMessage?.Render(logEvent) ?? logEvent.FormattedMessage); - if (logEvent.Exception != null) - { - if (WriteThrowableCData) - { - // CDATA correctly preserves newlines and indention, but not all viewers support this - xtw.WriteStartElement("log4j", "throwable", dummyNamespace); - xtw.WriteSafeCData(logEvent.Exception.ToString()); - xtw.WriteEndElement(); - } - else - { - xtw.WriteElementSafeString("log4j", "throwable", dummyNamespace, logEvent.Exception.ToString()); - } - } - - AppendScopeContextNestedStates(xtw, logEvent); - - if (IncludeCallSite || IncludeSourceInfo) - { - AppendCallSite(logEvent, xtw); - } - - xtw.WriteStartElement("log4j", "properties", dummyNamespace); - - AppendScopeContextProperties(xtw); - - if (IncludeEventProperties) - { - AppendDataProperties("log4j", dummyNamespace, xtw, logEvent); - } - - AppendParameters(logEvent, xtw); - - var appInfo = AppInfo?.Render(logEvent); - AppendDataProperty(xtw, "log4japp", appInfo, dummyNamespace); - - AppendDataProperty(xtw, "log4jmachinename", _machineName, dummyNamespace); - - xtw.WriteEndElement(); // properties - - xtw.WriteEndElement(); // event - xtw.Flush(); - - // get rid of 'nlog' and 'log4j' namespace declarations - sb.Replace(dummyNamespaceRemover, string.Empty); - builder.Append(sb.ToString()); // StringBuilder.Replace is not good when reusing the StringBuilder - } - } - - private void AppendScopeContextProperties(XmlWriter xtw) - { - if (IncludeScopeProperties) - { - foreach (var scopeProperty in ScopeContext.GetAllProperties()) - { - string propertyKey = XmlHelpers.RemoveInvalidXmlChars(scopeProperty.Key); - if (string.IsNullOrEmpty(propertyKey)) - continue; - - string propertyValue = XmlHelpers.XmlConvertToStringSafe(scopeProperty.Value); - if (propertyValue is null) - continue; - - AppendDataProperty(xtw, propertyKey, propertyValue, dummyNamespace); - } - } - } - - private void AppendScopeContextNestedStates(XmlWriter xtw, LogEventInfo logEvent) - { - if (IncludeScopeNested) - { - var nestedStates = _scopeNestedLayoutRenderer.Render(logEvent); - //NDLC and NDC should be in the same element - xtw.WriteElementSafeString("log4j", "NDC", dummyNamespace, nestedStates); - } - } - - private void AppendParameters(LogEventInfo logEvent, XmlWriter xtw) - { - for (int i = 0; i < Parameters?.Count; ++i) - { - var parameter = Parameters[i]; - - string parameterName = parameter?.Name; // property-setter has ensured safe xml-string - if (string.IsNullOrEmpty(parameterName)) - continue; - - var parameterValue = parameter.Layout?.Render(logEvent); - if (!parameter.IncludeEmptyValue && string.IsNullOrEmpty(parameterValue)) - continue; - - AppendDataProperty(xtw, parameterName, parameterValue, dummyNamespace); - } - } - - private void AppendCallSite(LogEventInfo logEvent, XmlWriter xtw) - { - string callerMemberName = logEvent.CallerMemberName; - if (string.IsNullOrEmpty(callerMemberName)) - return; - - string callerClassName = logEvent.CallerClassName; - - xtw.WriteStartElement("log4j", "locationInfo", dummyNamespace); - if (!string.IsNullOrEmpty(callerClassName)) - { - xtw.WriteAttributeSafeString("class", callerClassName); - } - - xtw.WriteAttributeSafeString("method", callerMemberName); - - if (IncludeSourceInfo) - { - xtw.WriteAttributeSafeString("file", logEvent.CallerFilePath); - xtw.WriteAttributeString("line", logEvent.CallerLineNumber.ToString(CultureInfo.InvariantCulture)); - } - - xtw.WriteEndElement(); - } - - private static void AppendDataProperties(string prefix, string propertiesNamespace, XmlWriter xtw, LogEventInfo logEvent) - { - if (logEvent.HasProperties) - { - foreach (var contextProperty in logEvent.Properties) - { - string propertyKey = XmlHelpers.XmlConvertToStringSafe(contextProperty.Key); - if (string.IsNullOrEmpty(propertyKey)) - continue; - - string propertyValue = XmlHelpers.XmlConvertToStringSafe(contextProperty.Value); - if (propertyValue is null) - continue; - - AppendDataProperty(xtw, propertyKey, propertyValue, propertiesNamespace, prefix); - } - } - } - - private static void AppendDataProperty(XmlWriter xtw, string propertyKey, string propertyValue, string propertiesNamespace, string prefix = "log4j") - { - xtw.WriteStartElement(prefix, "data", propertiesNamespace); - xtw.WriteAttributeString("name", propertyKey); - xtw.WriteAttributeSafeString("value", propertyValue); - xtw.WriteEndElement(); - } - } -} diff --git a/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs index c8f7ed9ee0..8c612c44a7 100644 --- a/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs @@ -54,7 +54,7 @@ public Log4JXmlEventParameter() /// /// [RequiredParameter] - public string Name { get => _name; set => _name = XmlHelpers.RemoveInvalidXmlChars(value); } + public string Name { get => _name; set => _name = value; } private string _name; /// diff --git a/src/NLog.Targets.Network/Targets/ChainsawTarget.cs b/src/NLog.Targets.Network/Targets/ChainsawTarget.cs index 9d5d393c4b..64d730dfa4 100644 --- a/src/NLog.Targets.Network/Targets/ChainsawTarget.cs +++ b/src/NLog.Targets.Network/Targets/ChainsawTarget.cs @@ -37,7 +37,6 @@ namespace NLog.Targets using System.Collections.Generic; using System.ComponentModel; using NLog.Config; - using NLog.LayoutRenderers; using NLog.Layouts; /// @@ -94,8 +93,8 @@ public ChainsawTarget(string name) : this() [EditorBrowsable(EditorBrowsableState.Never)] public bool IncludeNLogData { - get => Renderer.IncludeNLogData; - set => Renderer.IncludeNLogData = value; + get => _log4JLayout.IncludeNLogData; + set => _log4JLayout.IncludeNLogData = value; } /// @@ -104,8 +103,8 @@ public bool IncludeNLogData /// public Layout LoggerName { - get => Renderer.LoggerName; - set => Renderer.LoggerName = value; + get => _log4JLayout.LoggerName; + set => _log4JLayout.LoggerName = value; } /// @@ -114,8 +113,8 @@ public Layout LoggerName /// public Layout FormattedMessage { - get => Renderer.FormattedMessage; - set => Renderer.FormattedMessage = value; + get => _log4JLayout.FormattedMessage; + set => _log4JLayout.FormattedMessage = value; } /// @@ -124,8 +123,8 @@ public Layout FormattedMessage /// public Layout AppInfo { - get => Renderer.AppInfo; - set => Renderer.AppInfo = value; + get => _log4JLayout.AppInfo; + set => _log4JLayout.AppInfo = value; } /// @@ -134,8 +133,8 @@ public Layout AppInfo /// public bool IncludeCallSite { - get => Renderer.IncludeCallSite; - set => Renderer.IncludeCallSite = value; + get => _log4JLayout.IncludeCallSite; + set => _log4JLayout.IncludeCallSite = value; } /// @@ -144,8 +143,8 @@ public bool IncludeCallSite /// public bool IncludeSourceInfo { - get => Renderer.IncludeSourceInfo; - set => Renderer.IncludeSourceInfo = value; + get => _log4JLayout.IncludeSourceInfo; + set => _log4JLayout.IncludeSourceInfo = value; } /// @@ -157,8 +156,8 @@ public bool IncludeSourceInfo [EditorBrowsable(EditorBrowsableState.Never)] public bool IncludeMdc { - get => Renderer.IncludeMdc; - set => Renderer.IncludeMdc = value; + get => _log4JLayout.IncludeMdc; + set => _log4JLayout.IncludeMdc = value; } /// @@ -167,33 +166,33 @@ public bool IncludeMdc /// public bool IncludeNdc { - get => Renderer.IncludeNdc; - set => Renderer.IncludeNdc = value; + get => _log4JLayout.IncludeNdc; + set => _log4JLayout.IncludeNdc = value; } /// /// Gets or sets the option to include all properties from the log events /// /// - public bool IncludeEventProperties { get => Renderer.IncludeEventProperties; set => Renderer.IncludeEventProperties = value; } + public bool IncludeEventProperties { get => _log4JLayout.IncludeEventProperties; set => _log4JLayout.IncludeEventProperties = value; } /// /// Gets or sets whether to include the contents of the properties-dictionary. /// /// - public bool IncludeScopeProperties { get => Renderer.IncludeScopeProperties; set => Renderer.IncludeScopeProperties = value; } + public bool IncludeScopeProperties { get => _log4JLayout.IncludeScopeProperties; set => _log4JLayout.IncludeScopeProperties = value; } /// /// Gets or sets whether to include log4j:NDC in output from nested context. /// /// - public bool IncludeScopeNested { get => Renderer.IncludeScopeNested; set => Renderer.IncludeScopeNested = value; } + public bool IncludeScopeNested { get => _log4JLayout.IncludeScopeNested; set => _log4JLayout.IncludeScopeNested = value; } /// /// Gets or sets the separator for operation-states-stack. /// /// - public string ScopeNestedSeparator { get => Renderer.ScopeNestedSeparator; set => Renderer.ScopeNestedSeparator = value; } + public string ScopeNestedSeparator { get => _log4JLayout.ScopeNestedSeparator; set => _log4JLayout.ScopeNestedSeparator = value; } /// /// Obsolete and replaced by with NLog v5. @@ -211,7 +210,7 @@ public bool IncludeNdc /// [Obsolete("Replaced by IncludeScopeProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] - public bool IncludeMdlc { get => Renderer.IncludeMdlc; set => Renderer.IncludeMdlc = value; } + public bool IncludeMdlc { get => _log4JLayout.IncludeMdlc; set => _log4JLayout.IncludeMdlc = value; } /// /// Obsolete and replaced by with NLog v5. @@ -220,7 +219,7 @@ public bool IncludeNdc /// [Obsolete("Replaced by IncludeNdc. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] - public bool IncludeNdlc { get => Renderer.IncludeNdlc; set => Renderer.IncludeNdlc = value; } + public bool IncludeNdlc { get => _log4JLayout.IncludeNdlc; set => _log4JLayout.IncludeNdlc = value; } /// /// Obsolete and replaced by with NLog v5. @@ -229,7 +228,7 @@ public bool IncludeNdc /// [Obsolete("Replaced by NdcItemSeparator. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] - public string NdlcItemSeparator { get => Renderer.NdlcItemSeparator; set => Renderer.NdlcItemSeparator = value; } + public string NdlcItemSeparator { get => _log4JLayout.NdlcItemSeparator; set => _log4JLayout.NdlcItemSeparator = value; } /// /// Gets or sets the stack separator for log4j:NDC in output from nested context. @@ -237,8 +236,8 @@ public bool IncludeNdc /// public string NdcItemSeparator { - get => Renderer.NdcItemSeparator; - set => Renderer.NdcItemSeparator = value; + get => _log4JLayout.NdcItemSeparator; + set => _log4JLayout.NdcItemSeparator = value; } /// @@ -249,11 +248,6 @@ public string NdcItemSeparator [ArrayParameter(typeof(Log4JXmlEventParameter), "parameter")] public IList Parameters => _log4JLayout.Parameters; - /// - /// Gets the layout renderer which produces Log4j-compatible XML events. - /// - public Log4JXmlEventLayoutRenderer Renderer => _log4JLayout.Renderer; - /// /// Gets or sets the instance of that is used to format log messages. /// diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index 18047b23c5..648b5cf9c1 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -502,7 +502,6 @@ private void RegisterAllLayoutRenderers(bool skipCheckExists) SafeRegisterNamedType(_layoutRenderers, "configsetting", "NLog.Extensions.Logging.ConfigSettingLayoutRenderer, NLog.Extensions.Logging", skipCheckExists); SafeRegisterNamedType(_layoutRenderers, "microsoftconsolelayout", "NLog.Extensions.Logging.MicrosoftConsoleLayoutRenderer, NLog.Extensions.Logging", skipCheckExists); #endif - SafeRegisterNamedType(_layoutRenderers, "log4jxmlevent", "NLog.LayoutRenderers.Log4JXmlEventLayoutRenderer, NLog.Targets.Network", skipCheckExists); SafeRegisterNamedType(_layoutRenderers, "performancecounter", "NLog.LayoutRenderers.PerformanceCounterLayoutRenderer, NLog.PerformanceCounter", skipCheckExists); SafeRegisterNamedType(_layoutRenderers, "registry", "NLog.LayoutRenderers.RegistryLayoutRenderer, NLog.WindowsRegistry", skipCheckExists); SafeRegisterNamedType(_layoutRenderers, "windowsidentity", "NLog.LayoutRenderers.WindowsIdentityLayoutRenderer, NLog.WindowsIdentity", skipCheckExists); diff --git a/src/NLog/Internal/XmlHelper.cs b/src/NLog/Internal/XmlHelper.cs index 7e9701ce2a..64826464b0 100644 --- a/src/NLog/Internal/XmlHelper.cs +++ b/src/NLog/Internal/XmlHelper.cs @@ -445,5 +445,60 @@ private static string EnsureDecimalPlace(string text) } private static readonly char[] DecimalScientificExponent = new[] { 'e', 'E' }; + + public static void RemoveInvalidXmlIfNeeded(StringBuilder builder, int orgLength) + { +#if !NET35 + bool containsInvalid = false; + for (int i = orgLength; i < builder.Length; ++i) + { + if (!XmlConvert.IsXmlChar(builder[i])) + { + containsInvalid = true; + break; + } + } + + if (containsInvalid) + { + var text = builder.ToString(orgLength, builder.Length - orgLength); + var cleanedText = RemoveInvalidXmlChars(text); + builder.Length = orgLength; + builder.Append(cleanedText); + } +#else + var text = builder.ToString(orgLength, builder.Length - orgLength); + var cleanedText = RemoveInvalidXmlChars(text); + builder.Length = orgLength; + builder.Append(cleanedText); +#endif + } + + public static void EscapeCDataIfNeeded(StringBuilder builder, int orgLength) + { + for (int i = orgLength; i + 2 < builder.Length; ++i) + { + if (builder[i] == ']' && builder[i + 1] == ']' && builder[i + 2] == '>') + { + var escapedCData = builder.ToString(i, builder.Length - i) + .Replace("]]>", "]]]]>"); + builder.Length = i; + builder.Append(escapedCData); + break; + } + } + } + + + public static string WrapInCData(string text) + { + if (string.IsNullOrEmpty(text)) + return ""; + + if (text.Contains("]]>")) + text = text.Replace("]]>", "]]]]>"); + + return $""; + } } } diff --git a/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs index 192acd40d7..eb6c7a768b 100644 --- a/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs @@ -57,6 +57,11 @@ public sealed class XmlEncodeLayoutRendererWrapper : WrapperLayoutRendererBase /// public bool XmlEncode { get; set; } = true; + /// + /// Indicates whether the rendered value should be wrapped in section. + /// + public bool CDataEncode { get; set; } = false; + /// /// Gets or sets a value indicating whether to transform newlines (\r\n) into ( ) /// @@ -66,16 +71,37 @@ public sealed class XmlEncodeLayoutRendererWrapper : WrapperLayoutRendererBase /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) { + if (CDataEncode) + { + builder.Append(""); + } + + else if (XmlEncode) { XmlHelper.PerformXmlEscapeWhenNeeded(builder, orgLength, XmlEncodeNewlines); } } + /// protected override string Transform(string text) { + if (CDataEncode) + { + return XmlHelper.WrapInCData(text); + } + if (XmlEncode) { return XmlHelper.EscapeXmlString(text, XmlEncodeNewlines); diff --git a/src/NLog/Layouts/XML/XmlElement.cs b/src/NLog/Layouts/XML/XmlElement.cs index 55457023d0..31c399202c 100644 --- a/src/NLog/Layouts/XML/XmlElement.cs +++ b/src/NLog/Layouts/XML/XmlElement.cs @@ -96,5 +96,14 @@ public bool Encode get => base.LayoutWrapper.XmlEncode; set => base.LayoutWrapper.XmlEncode = value; } + + /// + /// Wraps the element value in a CDATA section instead of escaping XML characters. + /// + public bool CDataEncode + { + get => LayoutWrapper.CDataEncode; + set => LayoutWrapper.CDataEncode = value; + } } } diff --git a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs index 3fcbb96d90..8a4db3ae30 100644 --- a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs +++ b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs @@ -35,8 +35,10 @@ namespace NLog.Targets.Network { using System; using System.Collections.Generic; + using System.Globalization; using System.IO; using System.Reflection; + using System.Text; using System.Xml; using NLog.LayoutRenderers; using NLog.Layouts; @@ -51,7 +53,6 @@ public Log4JXmlTests() LogManager.ThrowExceptions = true; LogManager.Setup().SetupExtensions(ext => { - ext.RegisterLayoutRenderer(); ext.RegisterLayout(); }); } @@ -61,14 +62,25 @@ public void Log4JXmlTest() { var logFactory = new LogFactory().Setup() .LoadConfigurationFromXml(@" - - - - - + + + + + + + - - ").LogFactory; + + + ").LogFactory; ScopeContext.Clear(); @@ -99,7 +111,7 @@ public void Log4JXmlTest() { "log4j.event", "log4j.message", - "log4j.NDC", + //"log4j.NDC", To-Do "log4j.locationInfo", "log4j.properties", "log4j.throwable", @@ -145,8 +157,8 @@ public void Log4JXmlTest() break; case "NDC": - reader.Read(); - Assert.Equal("baz1::baz2::baz3", reader.Value); + // reader.Read(); + // Assert.Equal("baz1::baz2::baz3", reader.Value); break; case "locationInfo": @@ -169,9 +181,10 @@ public void Log4JXmlTest() switch (name) { case "log4japp": - Assert.Equal(AppDomain.CurrentDomain.FriendlyName + "(" + System.Diagnostics.Process.GetCurrentProcess().Id + ")", value); + var expectedAppInfo = string.Format( CultureInfo.InvariantCulture, "{0}({1})", AppDomain.CurrentDomain.FriendlyName,System.Diagnostics.Process.GetCurrentProcess().Id); + var actualAppInfo = value.Substring(value.IndexOf(':') + 1); + Assert.Equal(expectedAppInfo, actualAppInfo); break; - case "log4jmachinename": Assert.Equal(Environment.MachineName, value); break; @@ -225,22 +238,102 @@ public void Log4JXmlEventLayoutParameterTest() } }, }; - log4jLayout.Renderer.AppInfo = "MyApp"; + log4jLayout.AppInfo = "MyApp"; var logEventInfo = new LogEventInfo { LoggerName = "MyLOgger", TimeStamp = new DateTime(2010, 01, 01, 12, 34, 56, DateTimeKind.Utc), Level = LogLevel.Info, Message = "hello, <{0}>", - Parameters = new[] { "world" }, + Parameters = new[] { "world" } }; var threadid = Environment.CurrentManagedThreadId; var machinename = Environment.MachineName; - Assert.Equal($"hello, <world>", log4jLayout.Render(logEventInfo)); + var test = log4jLayout.Render(logEventInfo); + Assert.Equal($"hello, <world>", log4jLayout.Render(logEventInfo)); } [Fact] + public void Log4JXmlEventLayout_ThrowableWrappedInCData_Test() + { + var log4jLayout = new Log4JXmlEventLayout + { + WriteThrowableCData = true, + AppInfo = "MyApp", + }; + + var logEventInfo = new LogEventInfo + { + LoggerName = "TestLogger", + TimeStamp = new DateTime(2020, 01, 01, 12, 00, 00, DateTimeKind.Utc), + Level = LogLevel.Error, + Message = "Test message", + Exception = new Exception("Something went wrong <>&") + }; + + var result = log4jLayout.Render(logEventInfo); + + Assert.Contains("", result); + Assert.Contains("", result); + } + + [Fact(Skip = "TO DO")] + public void Log4JXmlEventLayout_IncludeScopeNested_Test() + { + var log4jLayout = new Log4JXmlEventLayout + { + IncludeScopeNested = true, + ScopeNestedSeparator = "::", + AppInfo = "MyApp", + }; + + ScopeContext.Clear(); + ScopeContext.PushNestedState("One"); + ScopeContext.PushNestedState("Two"); + ScopeContext.PushNestedState("Three"); + + var logEventInfo = new LogEventInfo + { + LoggerName = "TestLogger", + TimeStamp = new DateTime(2025, 04, 11, 12, 00, 00, DateTimeKind.Utc), + Level = LogLevel.Info, + Message = "Nested scope log test" + }; + + var result = log4jLayout.Render(logEventInfo); + + Assert.Contains("One::Two::Three", result); + } + + + + [Fact] + public void Log4JXmlEventLayout_ThrowableWithoutCData_EncodesCorrectly() + { + var layout = new Log4JXmlEventLayout + { + WriteThrowableCData = false, + AppInfo = "TestApp", + }; + + var logEvent = new LogEventInfo(LogLevel.Error, "TestLogger", "Error occurred") + { + Exception = new Exception("Boom < & >") + }; + + string result = layout.Render(logEvent); + + Assert.Contains("", result); + Assert.DoesNotContain("", result); + } + + [Fact(Skip = "Deprecated with new XmlLayout logic. Use Log4JXmlEventLayout_CompliantXml_Test instead.")] + [Obsolete("This test uses old renderer layout, no longer relevant.")] public void BadXmlValueTest() { var sb = new System.Text.StringBuilder(); @@ -308,5 +401,55 @@ public void BadXmlValueTest() Assert.Contains("abc", badString); Assert.Contains("abc", goodString); } + + [Fact] + public void Log4JXmlEventLayout_Should_Remove_InvalidXmlCharacters() + { + // Arrange + var sb = new System.Text.StringBuilder(); + + // Build a string with all legal XML characters, plus some "known safe" + var validContent = new StringBuilder("abc"); // something we can track + int start = 0; int end = 0xFFFD; + for (int i = start; i <= end; i++) + { + char c = (char)i; + if (!char.IsSurrogate(c) && XmlConvert.IsXmlChar(c)) + { + validContent.Append(c); + } + } + + var badString = validContent.ToString(); + + var log4jLayout = new Log4JXmlEventLayout() + { + FormattedMessage = badString, // assigning directly + }; + + var logEventInfo = new LogEventInfo(LogLevel.Info, "loggerName", badString); + + // Act + string xmlOutput = log4jLayout.Render(logEventInfo); + + // Assert: Try reading the XML back and verify content + string wrappedXml = $"{xmlOutput}"; + string goodString = null; + + using (var reader = XmlReader.Create(new StringReader(wrappedXml))) + { + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Text) + { + if (reader.Value.Contains("abc")) + goodString = reader.Value; + } + } + } + + Assert.NotNull(goodString); + Assert.Contains("abc", goodString); + } } } From 110641061e6d85970fd09523fc0da6fd44143e53 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 26 Apr 2025 23:10:55 +0200 Subject: [PATCH 097/224] Log4JXmlEventLayout - Inherit from CompoundLayout to optimize for async (#5796) --- .../Internal/XmlHelpers.cs | 207 --------------- .../Layouts/Log4JXmlEventLayout.cs | 241 ++++++++++-------- .../Layouts/Log4JXmlEventParameter.cs | 3 +- .../NLog.Targets.Network.csproj | 1 - .../Targets/ChainsawTarget.cs | 2 +- .../Targets/NetworkTarget.cs | 2 +- .../XmlEncodeLayoutRendererWrapper.cs | 6 +- src/NLog/Layouts/XML/XmlElement.cs | 4 +- src/NLog/Layouts/XML/XmlElementBase.cs | 6 +- src/NLog/Targets/TargetWithContext.cs | 2 +- .../NLog.Database.Tests.csproj | 4 +- .../NLog.RegEx.Tests/NLog.RegEx.Tests.csproj | 4 +- .../NLog.Targets.AtomicFile.Tests.csproj | 4 +- .../NLog.Targets.ConcurrentFile.Tests.csproj | 4 +- .../NLog.Targets.GZipFile.Tests.csproj | 4 +- .../NLog.Targets.Mail.Tests.csproj | 4 +- .../Log4JXmlTests.cs | 144 +++-------- .../NLog.Targets.Network.Tests.csproj | 4 +- .../NLog.Targets.Trace.Tests.csproj | 4 +- .../NLog.Targets.WebService.Tests.csproj | 4 +- tests/NLog.UnitTests/NLog.UnitTests.csproj | 2 +- .../NLog.WindowsRegistry.Tests.csproj | 4 +- 22 files changed, 201 insertions(+), 459 deletions(-) delete mode 100644 src/NLog.Targets.Network/Internal/XmlHelpers.cs diff --git a/src/NLog.Targets.Network/Internal/XmlHelpers.cs b/src/NLog.Targets.Network/Internal/XmlHelpers.cs deleted file mode 100644 index 3457e4f1cc..0000000000 --- a/src/NLog.Targets.Network/Internal/XmlHelpers.cs +++ /dev/null @@ -1,207 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.Targets.Internal -{ - using System; - using System.Globalization; - using System.Text; - using System.Xml; - - internal static class XmlHelpers - { - /// - /// Safe version of WriteAttributeString - /// - /// - /// - /// - public static void WriteAttributeSafeString(this XmlWriter writer, string localName, string value) - { - writer.WriteAttributeString(localName, RemoveInvalidXmlChars(value)); - } - - /// - /// Safe version of WriteElementSafeString - /// - /// - /// - /// - /// - /// - public static void WriteElementSafeString(this XmlWriter writer, string prefix, string localName, string ns, string value) - { - writer.WriteElementString(prefix, localName, ns, RemoveInvalidXmlChars(value)); - } - - /// - /// Safe version of WriteCData - /// - /// - /// - public static void WriteSafeCData(this XmlWriter writer, string value) - { - writer.WriteCData(RemoveInvalidXmlChars(value)); - } - - public static string XmlConvertToStringSafe(object value) - { - try - { - var convertibleValue = value as IConvertible; - var objTypeCode = convertibleValue?.GetTypeCode() ?? (value is null ? TypeCode.Empty : TypeCode.Object); - if (objTypeCode != TypeCode.Object) - { - return XmlConvertToString(convertibleValue, objTypeCode); - } - - return XmlConvertToStringInvariant(value); - } - catch - { - return string.Empty; - } - } - - /// - /// Converts object value to invariant format (understood by JavaScript) - /// - /// Object value - /// Object TypeCode - /// Check and remove unusual unicode characters from the result string. - /// Object value converted to string - internal static string XmlConvertToString(IConvertible value, TypeCode objTypeCode, bool safeConversion = false) - { - if (objTypeCode == TypeCode.Empty || value is null) - { - return "null"; - } - - switch (objTypeCode) - { - case TypeCode.Boolean: - return XmlConvert.ToString(value.ToBoolean(CultureInfo.InvariantCulture)); // boolean as lowercase - case TypeCode.Byte: - return XmlConvert.ToString(value.ToByte(CultureInfo.InvariantCulture)); - case TypeCode.SByte: - return XmlConvert.ToString(value.ToSByte(CultureInfo.InvariantCulture)); - case TypeCode.Int16: - return XmlConvert.ToString(value.ToInt16(CultureInfo.InvariantCulture)); - case TypeCode.Int32: - return XmlConvert.ToString(value.ToInt32(CultureInfo.InvariantCulture)); - case TypeCode.Int64: - return XmlConvert.ToString(value.ToInt64(CultureInfo.InvariantCulture)); - case TypeCode.UInt16: - return XmlConvert.ToString(value.ToUInt16(CultureInfo.InvariantCulture)); - case TypeCode.UInt32: - return XmlConvert.ToString(value.ToUInt32(CultureInfo.InvariantCulture)); - case TypeCode.UInt64: - return XmlConvert.ToString(value.ToUInt64(CultureInfo.InvariantCulture)); - case TypeCode.Single: - return XmlConvert.ToString(value.ToSingle(CultureInfo.InvariantCulture)); - case TypeCode.Double: - return XmlConvert.ToString(value.ToDouble(CultureInfo.InvariantCulture)); - case TypeCode.Decimal: - return XmlConvert.ToString(value.ToDecimal(CultureInfo.InvariantCulture)); - case TypeCode.DateTime: - return XmlConvert.ToString(value.ToDateTime(CultureInfo.InvariantCulture), XmlDateTimeSerializationMode.Utc); - case TypeCode.Char: - return RemoveInvalidXmlChars(value.ToString(CultureInfo.InvariantCulture)); - case TypeCode.String: - return RemoveInvalidXmlChars(value.ToString(CultureInfo.InvariantCulture)); - default: - return XmlConvertToStringInvariant(value); - } - } - - private static string XmlConvertToStringInvariant(object value) - { - try - { - string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); - return RemoveInvalidXmlChars(valueString); - } - catch - { - return string.Empty; - } - } - - - /// - /// removes any unusual unicode characters that can't be encoded into XML - /// - public static string RemoveInvalidXmlChars(string text) - { - if (string.IsNullOrEmpty(text)) - return string.Empty; - - int length = text.Length; - for (int i = 0; i < length; ++i) - { - char ch = text[i]; - if (!XmlConvert.IsXmlChar(ch)) - { - if (i + 1 < text.Length && XmlConvert.IsXmlSurrogatePair(text[i + 1], ch)) - { - ++i; - } - else - { - return CreateValidXmlString(text); // rare expensive case - } - } - } - return text; - } - - /// - /// Cleans string of any invalid XML chars found - /// - /// unclean string - /// string with only valid XML chars - private static string CreateValidXmlString(string text) - { - var sb = new StringBuilder(text.Length); - for (int i = 0; i < text.Length; ++i) - { - char ch = text[i]; - if (XmlConvert.IsXmlChar(ch)) - { - sb.Append(ch); - } - } - return sb.ToString(); - } - } -} diff --git a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs index b6284ee263..671c74fce1 100644 --- a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs @@ -38,7 +38,6 @@ namespace NLog.Layouts using System.ComponentModel; using System.Text; using NLog.Config; - using NLog.LayoutRenderers; /// /// A specialized layout that renders Log4j-compatible XML events. @@ -50,111 +49,17 @@ namespace NLog.Layouts [Layout("Log4JXmlEventLayout")] [Layout("Log4JXmlLayout")] [ThreadAgnostic] - public class Log4JXmlEventLayout : Layout + public class Log4JXmlEventLayout : CompoundLayout { - - private static readonly DateTime log4jDateBase = new DateTime(1970, 1, 1); - private IList _parameters = new List(); - - - /// - /// Gets inner XML layout. - /// - public XmlLayout InnerXml { get; } = new XmlLayout(); - - /// - protected override void InitializeLayout() - { - InnerXml.Attributes.Clear(); - InnerXml.Elements.Clear(); - - InnerXml.ElementName = "log4j:event"; - - InnerXml.Attributes.Add(new XmlAttribute("logger", LoggerName)); - InnerXml.Attributes.Add(new XmlAttribute("level", "${level:uppercase=true}") { IncludeEmptyValue = true }); - InnerXml.Attributes.Add(new XmlAttribute("timestamp", Layout.FromMethod(evt => (long)(evt.TimeStamp.ToUniversalTime() - log4jDateBase).TotalMilliseconds, LayoutRenderOptions.ThreadAgnostic))); - InnerXml.Attributes.Add(new XmlAttribute("thread", "${threadid}") { IncludeEmptyValue = true }); - - InnerXml.Elements.Add(new XmlElement("log4j:message", FormattedMessage ?? "${message}")); - InnerXml.Elements.Add(new XmlElement("log4j:throwable", "${exception:format=ToString}") - { - CDataEncode = WriteThrowableCData, - }); - - if (IncludeCallSite || IncludeSourceInfo) - { - var locationInfo = new XmlElement("log4j:locationInfo", null) - { - Attributes = - { - new XmlAttribute("class", "${callsite:methodName=false}"), - new XmlAttribute("method", "${callsite:className=false}") - } - }; - - if (IncludeSourceInfo) - { - locationInfo.Attributes.Add(new XmlAttribute("file", "${callsite-filename}")); - locationInfo.Attributes.Add(new XmlAttribute("line", "${callsite-linenumber}")); - } - - InnerXml.Elements.Add(locationInfo); - } - - if (IncludeScopeNested) - { - var separator = ScopeNestedSeparator; - Layout scopeNested = string.IsNullOrEmpty(separator) ? "${scopenested}" : ("${scopenested:separator=" + separator + "}"); - InnerXml.Elements.Add(new XmlElement("log4j:NDC", scopeNested)); - } - - var dataProperties = new XmlElement("log4j:properties", null) - { - PropertiesElementName = "log4j:data", - PropertiesElementKeyAttribute = "name", - PropertiesElementValueAttribute = "value", - IncludeEventProperties = IncludeEventProperties, - IncludeScopeProperties = IncludeScopeProperties - }; - - foreach (var parameter in Parameters) - { - var propertyElement = new XmlElement("log4j:data", null) - { - IncludeEmptyValue = parameter.IncludeEmptyValue - }; - - propertyElement.Attributes.Add(new XmlAttribute("name", parameter.Name)); - propertyElement.Attributes.Add(new XmlAttribute("value", parameter.Layout)); - dataProperties.Elements.Add(propertyElement); - } - - - var appProperty = new XmlElement("log4j:data", null); - appProperty.Attributes.Add(new XmlAttribute("name", "log4japp")); - appProperty.Attributes.Add(new XmlAttribute("value", AppInfo)); - dataProperties.Elements.Add(appProperty); - - var machineProperty = new XmlElement("log4j:data", null); - machineProperty.Attributes.Add(new XmlAttribute("name", "log4jmachinename")); - machineProperty.Attributes.Add(new XmlAttribute("value", "${machinename}")); - dataProperties.Elements.Add(machineProperty); - - InnerXml.Elements.Add(dataProperties); - - base.InitializeLayout(); - } + private static readonly DateTime UnixDateStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + private readonly XmlLayout _innerXml = new XmlLayout() { ElementName = "log4j:event" }; /// /// Gets the collection of parameters. Each parameter contains a mapping /// between NLog layout and a named parameter. /// [ArrayParameter(typeof(Log4JXmlEventParameter), "parameter")] - public IList Parameters - { - get => _parameters; - set => _parameters = value ?? new List(); - } + public IList Parameters { get; } = new List(); /// /// Gets or sets the option to include all properties from the log events @@ -180,7 +85,7 @@ public IList Parameters /// Gets or sets a value indicating whether to include NLog-specific extensions to log4j schema. /// /// - [Obsolete("Non standard extension to the Log4j-XML format. Marked obsolete with NLog 6.0")] + [Obsolete("Non standard extension to the Log4j-XML format. Marked obsolete with NLog v5.4")] [EditorBrowsable(EditorBrowsableState.Never)] public bool IncludeNLogData { get; set; } @@ -260,25 +165,51 @@ public IList Parameters /// Gets or sets the log4j:event logger-xml-attribute. Default: ${logger} /// /// - public Layout LoggerName { get; set; } = "${logger}"; + public Layout LoggerName { get => _loggerName.Layout; set => _loggerName.Layout = value; } + private readonly XmlAttribute _loggerName = new XmlAttribute("logger", "${logger}"); + + private readonly XmlAttribute _levelValue = new XmlAttribute("level", "${level:uppercase=true}"); + + private readonly XmlAttribute _timestampValue = new XmlAttribute("timestamp", Layout.FromMethod(evt => (long)(evt.TimeStamp.ToUniversalTime() - UnixDateStart).TotalMilliseconds, LayoutRenderOptions.ThreadAgnostic)); + + private readonly XmlAttribute _threadIdValue = new XmlAttribute("thread", "${threadid}"); /// /// Gets or sets the log4j:event message-xml-element. Default: ${message} /// /// - public Layout FormattedMessage { get; set; } + public Layout FormattedMessage { get => _formattedMessage.Layout; set => _formattedMessage.Layout = value; } + private readonly XmlElement _formattedMessage = new XmlElement("log4j:message", "${message}"); /// /// Gets or sets the log4j:event log4japp-xml-element. By default it's the friendly name of the current AppDomain. /// /// - public Layout AppInfo { get; set; } + public Layout AppInfo { get => _log4jAppName.Attributes[1].Layout; set => _log4jAppName.Attributes[1].Layout = value; } + private readonly XmlElement _log4jAppName = new XmlElement("log4j:data", null) + { + Attributes = + { + new XmlAttribute("name", "log4japp"), + new XmlAttribute("value", "${appdomain:format=Friendly}(${processid})") + } + }; + + private readonly XmlElement _log4jMachineName = new XmlElement("log4j:data", null) + { + Attributes = + { + new XmlAttribute("name", "log4jmachinename"), + new XmlAttribute("value", "${hostname}") + } + }; /// /// Gets or sets whether the log4j:throwable xml-element should be written as CDATA /// /// - public bool WriteThrowableCData { get; set; } + public bool WriteThrowableCData { get => _exceptionThrowable.CDataEncode; set => _exceptionThrowable.CDataEncode = value; } + private readonly XmlElement _exceptionThrowable = new XmlElement("log4j:throwable", "${exception:format=ToString}"); /// /// Gets or sets a value indicating whether to include call site (class and method name) in the information sent over the network. @@ -292,6 +223,106 @@ public IList Parameters /// public bool IncludeSourceInfo { get; set; } + /// + protected override void InitializeLayout() + { + _innerXml.Attributes.Clear(); + _innerXml.Elements.Clear(); + + _innerXml.Attributes.Add(_loggerName); + _innerXml.Attributes.Add(_levelValue); + _innerXml.Attributes.Add(_timestampValue); + _innerXml.Attributes.Add(_threadIdValue); + + _innerXml.Elements.Add(_formattedMessage); + _innerXml.Elements.Add(_exceptionThrowable); + + if (IncludeCallSite || IncludeSourceInfo) + { + var locationInfo = new XmlElement("log4j:locationInfo", null) + { + Attributes = + { + new XmlAttribute("class", "${callsite:methodName=false}"), + new XmlAttribute("method", "${callsite:className=false}") + } + }; + + if (IncludeSourceInfo) + { + locationInfo.Attributes.Add(new XmlAttribute("file", "${callsite-filename}")); + locationInfo.Attributes.Add(new XmlAttribute("line", "${callsite-linenumber}")); + } + + _innerXml.Elements.Add(locationInfo); + } + +#pragma warning disable CS0618 // Type or member is obsolete + if (IncludeNLogData) + { + _innerXml.Elements.Add(new XmlElement("nlog:eventSequenceNumber", "${sequenceid}")); + _innerXml.Elements.Add(new XmlElement("nlog:locationInfo", null) + { + Attributes = + { + new XmlAttribute("assembly", "${callsite:fileName=true:className=false:methodName=false}") + } + }); + _innerXml.Elements.Add(new XmlElement("nlog:properties", null) + { + PropertiesElementName = "nlog:data", + PropertiesElementKeyAttribute = "name", + PropertiesElementValueAttribute = "value", + IncludeEventProperties = true, + }); + } +#pragma warning restore CS0618 // Type or member is obsolete + + if (IncludeScopeNested) + { + var separator = ScopeNestedSeparator; + Layout scopeNested = string.IsNullOrEmpty(separator) ? "${scopenested}" : ("${scopenested:separator=" + separator.Replace(":", "\\:") + "}"); + _innerXml.Elements.Add(new XmlElement("log4j:NDC", scopeNested)); + } + + var dataProperties = new XmlElement("log4j:properties", null) + { + PropertiesElementName = "log4j:data", + PropertiesElementKeyAttribute = "name", + PropertiesElementValueAttribute = "value", + IncludeEventProperties = IncludeEventProperties, + IncludeScopeProperties = IncludeScopeProperties + }; + + foreach (var parameter in Parameters) + { + var propertyElement = new XmlElement("log4j:data", null) + { + IncludeEmptyValue = parameter.IncludeEmptyValue, + Attributes = + { + new XmlAttribute("name", parameter.Name), + new XmlAttribute("value", parameter.Layout), + } + }; + dataProperties.Elements.Add(propertyElement); + } + + dataProperties.Elements.Add(_log4jAppName); + dataProperties.Elements.Add(_log4jMachineName); + + _innerXml.Elements.Add(dataProperties); + + // CompoundLayout includes optimization, so only doing precalculate/caching of relevant Layouts (instead of the entire LOG4J-message) + Layouts.Clear(); + foreach (var xmlAttribute in _innerXml.Attributes) + Layouts.Add(xmlAttribute.Layout); + foreach (var xmlElement in _innerXml.Elements) + Layouts.Add(xmlElement); + + base.InitializeLayout(); + } + /// protected override string GetFormattedMessage(LogEventInfo logEvent) { @@ -303,7 +334,7 @@ protected override string GetFormattedMessage(LogEventInfo logEvent) /// protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) { - InnerXml.Render(logEvent, target); + _innerXml.Render(logEvent, target); } } } diff --git a/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs index 8c612c44a7..ea18e64d73 100644 --- a/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs @@ -54,8 +54,7 @@ public Log4JXmlEventParameter() /// /// [RequiredParameter] - public string Name { get => _name; set => _name = value; } - private string _name; + public string Name { get; set; } /// /// Gets or sets the layout that should be use to calculate the value for the parameter. diff --git a/src/NLog.Targets.Network/NLog.Targets.Network.csproj b/src/NLog.Targets.Network/NLog.Targets.Network.csproj index 652c0b977e..766c5c85dd 100644 --- a/src/NLog.Targets.Network/NLog.Targets.Network.csproj +++ b/src/NLog.Targets.Network/NLog.Targets.Network.csproj @@ -54,7 +54,6 @@ - diff --git a/src/NLog.Targets.Network/Targets/ChainsawTarget.cs b/src/NLog.Targets.Network/Targets/ChainsawTarget.cs index 64d730dfa4..fa62ae2cbd 100644 --- a/src/NLog.Targets.Network/Targets/ChainsawTarget.cs +++ b/src/NLog.Targets.Network/Targets/ChainsawTarget.cs @@ -89,7 +89,7 @@ public ChainsawTarget(string name) : this() /// Gets or sets a value indicating whether to include NLog-specific extensions to log4j schema. /// /// - [Obsolete("Non standard extension to the Log4j-XML format. Marked obsolete with NLog 6.0")] + [Obsolete("Non standard extension to the Log4j-XML format. Marked obsolete with NLog v5.4")] [EditorBrowsable(EditorBrowsableState.Never)] public bool IncludeNLogData { diff --git a/src/NLog.Targets.Network/Targets/NetworkTarget.cs b/src/NLog.Targets.Network/Targets/NetworkTarget.cs index 8f6e9213aa..59a429d604 100644 --- a/src/NLog.Targets.Network/Targets/NetworkTarget.cs +++ b/src/NLog.Targets.Network/Targets/NetworkTarget.cs @@ -632,7 +632,7 @@ private LinkedListNode GetCachedNetworkSender(string address, Log } } - InternalLogger.Debug("{0}: Creating NetworkServer to address: {1}", this, address); + InternalLogger.Debug("{0}: Creating network sender to address: {1}", this, address); NetworkSender sender = CreateNetworkSender(address, logEventInfo); lock (_openNetworkSenders) { diff --git a/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs index eb6c7a768b..a422e71378 100644 --- a/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs @@ -51,14 +51,14 @@ namespace NLog.LayoutRenderers.Wrappers public sealed class XmlEncodeLayoutRendererWrapper : WrapperLayoutRendererBase { /// - /// Gets or sets whether output should be encoded with Xml-string escaping. + /// Gets or sets whether output should be encoded with XML-string escaping. /// /// Ensures always valid XML, but gives a performance hit /// public bool XmlEncode { get; set; } = true; /// - /// Indicates whether the rendered value should be wrapped in section. + /// Gets or sets whether output should be wrapped using CDATA section instead of XML-string escaping /// public bool CDataEncode { get; set; } = false; @@ -86,14 +86,12 @@ protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBui XmlHelper.EscapeCDataIfNeeded(builder, orgLength); builder.Append("]]>"); } - else if (XmlEncode) { XmlHelper.PerformXmlEscapeWhenNeeded(builder, orgLength, XmlEncodeNewlines); } } - /// protected override string Transform(string text) { diff --git a/src/NLog/Layouts/XML/XmlElement.cs b/src/NLog/Layouts/XML/XmlElement.cs index 31c399202c..7c178f50e4 100644 --- a/src/NLog/Layouts/XML/XmlElement.cs +++ b/src/NLog/Layouts/XML/XmlElement.cs @@ -88,7 +88,7 @@ public Layout Value public Layout Layout { get => Value; set => Value = value; } /// - /// Gets or sets whether output should be encoded with Xml-string escaping, or be treated as valid xml-element-value + /// Gets or sets whether output should be encoded with XML-string escaping, or be treated as valid xml-element-value /// /// public bool Encode @@ -98,7 +98,7 @@ public bool Encode } /// - /// Wraps the element value in a CDATA section instead of escaping XML characters. + /// Gets or sets whether output should be wrapped using CDATA section instead of XML-string escaping /// public bool CDataEncode { diff --git a/src/NLog/Layouts/XML/XmlElementBase.cs b/src/NLog/Layouts/XML/XmlElementBase.cs index 4c51f2903c..2584943e41 100644 --- a/src/NLog/Layouts/XML/XmlElementBase.cs +++ b/src/NLog/Layouts/XML/XmlElementBase.cs @@ -420,11 +420,9 @@ private void AppendLogEventProperties(LogEventInfo logEventInfo, StringBuilder s var propertyValue = prop.Value; if (!string.IsNullOrEmpty(prop.Format) && propertyValue is IFormattable formattedProperty) - propertyValue = formattedProperty.ToString(prop.Format, - logEventInfo.FormatProvider ?? LoggingConfiguration?.DefaultCultureInfo); + propertyValue = formattedProperty.ToString(prop.Format, System.Globalization.CultureInfo.InvariantCulture); else if (prop.CaptureType == MessageTemplates.CaptureType.Stringify) - propertyValue = Convert.ToString(prop.Value ?? string.Empty, - logEventInfo.FormatProvider ?? LoggingConfiguration?.DefaultCultureInfo); + propertyValue = Convert.ToString(prop.Value ?? string.Empty, System.Globalization.CultureInfo.InvariantCulture); AppendXmlPropertyObjectValue(prop.Name, propertyValue, sb, orgLength, default(SingleItemOptimizedHashSet), 0); } diff --git a/src/NLog/Targets/TargetWithContext.cs b/src/NLog/Targets/TargetWithContext.cs index c45de2360f..448e6d9cd3 100644 --- a/src/NLog/Targets/TargetWithContext.cs +++ b/src/NLog/Targets/TargetWithContext.cs @@ -558,7 +558,7 @@ protected virtual bool SerializeItemValue(LogEventInfo logEvent, string name, ob } // Make snapshot of the context value - serializedValue = Convert.ToString(value, logEvent.FormatProvider ?? LoggingConfiguration?.DefaultCultureInfo); + serializedValue = Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture); return true; } diff --git a/tests/NLog.Database.Tests/NLog.Database.Tests.csproj b/tests/NLog.Database.Tests/NLog.Database.Tests.csproj index e4ffe29be2..d9a4723461 100644 --- a/tests/NLog.Database.Tests/NLog.Database.Tests.csproj +++ b/tests/NLog.Database.Tests/NLog.Database.Tests.csproj @@ -24,8 +24,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/NLog.RegEx.Tests/NLog.RegEx.Tests.csproj b/tests/NLog.RegEx.Tests/NLog.RegEx.Tests.csproj index b61a92c4f1..d27defab47 100644 --- a/tests/NLog.RegEx.Tests/NLog.RegEx.Tests.csproj +++ b/tests/NLog.RegEx.Tests/NLog.RegEx.Tests.csproj @@ -11,8 +11,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj b/tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj index 242a5847cb..61f19c133d 100644 --- a/tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj +++ b/tests/NLog.Targets.AtomicFile.Tests/NLog.Targets.AtomicFile.Tests.csproj @@ -20,8 +20,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/NLog.Targets.ConcurrentFile.Tests.csproj b/tests/NLog.Targets.ConcurrentFile.Tests/NLog.Targets.ConcurrentFile.Tests.csproj index 3bf00b6c70..727db04cc1 100644 --- a/tests/NLog.Targets.ConcurrentFile.Tests/NLog.Targets.ConcurrentFile.Tests.csproj +++ b/tests/NLog.Targets.ConcurrentFile.Tests/NLog.Targets.ConcurrentFile.Tests.csproj @@ -17,9 +17,9 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/NLog.Targets.GZipFile.Tests/NLog.Targets.GZipFile.Tests.csproj b/tests/NLog.Targets.GZipFile.Tests/NLog.Targets.GZipFile.Tests.csproj index f64a14ecb3..a6b7fc7d28 100644 --- a/tests/NLog.Targets.GZipFile.Tests/NLog.Targets.GZipFile.Tests.csproj +++ b/tests/NLog.Targets.GZipFile.Tests/NLog.Targets.GZipFile.Tests.csproj @@ -15,8 +15,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj b/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj index f5213ce32f..d9cd805f6e 100644 --- a/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj +++ b/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj @@ -16,8 +16,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs index 8a4db3ae30..29306c512a 100644 --- a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs +++ b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs @@ -40,10 +40,8 @@ namespace NLog.Targets.Network using System.Reflection; using System.Text; using System.Xml; - using NLog.LayoutRenderers; using NLog.Layouts; using NLog.Targets; - using NLog.Targets.Internal; using Xunit; public class Log4JXmlTests @@ -63,17 +61,15 @@ public void Log4JXmlTest() var logFactory = new LogFactory().Setup() .LoadConfigurationFromXml(@" - + + includeNdc='true' + ndcItemSeparator='::' /> @@ -96,6 +92,8 @@ public void Log4JXmlTest() var logEventInfo = LogEventInfo.Create(LogLevel.Debug, "A", new Exception("Hello Exception", new Exception("Goodbye Exception")), null, "some message \u0014"); logEventInfo.Properties["nlogPropertyKey"] = "nlogPropertyValue"; logger.Log(logEventInfo); + logFactory.Flush(); + string result = logFactory.Configuration.FindTargetByName("debug").LastMessage; Assert.DoesNotContain("dummy", result); @@ -111,7 +109,7 @@ public void Log4JXmlTest() { "log4j.event", "log4j.message", - //"log4j.NDC", To-Do + "log4j.NDC", "log4j.locationInfo", "log4j.properties", "log4j.throwable", @@ -144,10 +142,8 @@ public void Log4JXmlTest() var epochStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); long timestamp = Convert.ToInt64(reader.GetAttribute("timestamp")); - var time = epochStart.AddMilliseconds(timestamp); - var now = DateTime.UtcNow; - Assert.True(now.Ticks - time.Ticks < TimeSpan.FromSeconds(3).Ticks); - + var eventTime = logEventInfo.TimeStamp.Date.AddHours(logEventInfo.TimeStamp.Hour).AddMinutes(logEventInfo.TimeStamp.Minute).AddSeconds(logEventInfo.TimeStamp.Second).AddMilliseconds(logEventInfo.TimeStamp.Millisecond).ToUniversalTime(); + Assert.Equal(eventTime, epochStart.AddMilliseconds(timestamp)); Assert.Equal(Environment.CurrentManagedThreadId.ToString(), reader.GetAttribute("thread")); break; @@ -157,8 +153,8 @@ public void Log4JXmlTest() break; case "NDC": - // reader.Read(); - // Assert.Equal("baz1::baz2::baz3", reader.Value); + reader.Read(); + Assert.Equal("baz1::baz2::baz3", reader.Value); break; case "locationInfo": @@ -181,10 +177,10 @@ public void Log4JXmlTest() switch (name) { case "log4japp": - var expectedAppInfo = string.Format( CultureInfo.InvariantCulture, "{0}({1})", AppDomain.CurrentDomain.FriendlyName,System.Diagnostics.Process.GetCurrentProcess().Id); - var actualAppInfo = value.Substring(value.IndexOf(':') + 1); - Assert.Equal(expectedAppInfo, actualAppInfo); + var expectedAppInfo = string.Format(CultureInfo.InvariantCulture, "{0}({1})", AppDomain.CurrentDomain.FriendlyName, System.Diagnostics.Process.GetCurrentProcess().Id); + Assert.Equal(expectedAppInfo, value); break; + case "log4jmachinename": Assert.Equal(Environment.MachineName, value); break; @@ -250,8 +246,8 @@ public void Log4JXmlEventLayoutParameterTest() var threadid = Environment.CurrentManagedThreadId; var machinename = Environment.MachineName; - var test = log4jLayout.Render(logEventInfo); - Assert.Equal($"hello, <world>", log4jLayout.Render(logEventInfo)); + var result = log4jLayout.Render(logEventInfo); + Assert.Equal($"hello, <world>", result); } [Fact] @@ -260,27 +256,25 @@ public void Log4JXmlEventLayout_ThrowableWrappedInCData_Test() var log4jLayout = new Log4JXmlEventLayout { WriteThrowableCData = true, - AppInfo = "MyApp", + AppInfo = "TestApp", }; var logEventInfo = new LogEventInfo { LoggerName = "TestLogger", - TimeStamp = new DateTime(2020, 01, 01, 12, 00, 00, DateTimeKind.Utc), + TimeStamp = new DateTime(2010, 01, 01, 12, 34, 56, DateTimeKind.Utc), Level = LogLevel.Error, - Message = "Test message", + Message = "Error occurred", Exception = new Exception("Something went wrong <>&") }; + var threadid = Environment.CurrentManagedThreadId; + var machinename = Environment.MachineName; var result = log4jLayout.Render(logEventInfo); - - Assert.Contains("", result); - Assert.Contains("", result); + Assert.Equal($"Error occurred&]]>", result); } - [Fact(Skip = "TO DO")] + [Fact] public void Log4JXmlEventLayout_IncludeScopeNested_Test() { var log4jLayout = new Log4JXmlEventLayout @@ -298,22 +292,21 @@ public void Log4JXmlEventLayout_IncludeScopeNested_Test() var logEventInfo = new LogEventInfo { LoggerName = "TestLogger", - TimeStamp = new DateTime(2025, 04, 11, 12, 00, 00, DateTimeKind.Utc), + TimeStamp = new DateTime(2010, 01, 01, 12, 34, 56, DateTimeKind.Utc), Level = LogLevel.Info, Message = "Nested scope log test" }; + var threadid = Environment.CurrentManagedThreadId; + var machinename = Environment.MachineName; var result = log4jLayout.Render(logEventInfo); - - Assert.Contains("One::Two::Three", result); + Assert.Equal($"Nested scope log testOne::Two::Three", result); } - - [Fact] public void Log4JXmlEventLayout_ThrowableWithoutCData_EncodesCorrectly() { - var layout = new Log4JXmlEventLayout + var log4jLayout = new Log4JXmlEventLayout { WriteThrowableCData = false, AppInfo = "TestApp", @@ -321,85 +314,16 @@ public void Log4JXmlEventLayout_ThrowableWithoutCData_EncodesCorrectly() var logEvent = new LogEventInfo(LogLevel.Error, "TestLogger", "Error occurred") { + LoggerName = "TestLogger", + TimeStamp = new DateTime(2010, 01, 01, 12, 34, 56, DateTimeKind.Utc), + Level = LogLevel.Error, Exception = new Exception("Boom < & >") }; - string result = layout.Render(logEvent); - - Assert.Contains("", result); - Assert.DoesNotContain("", result); - } - - [Fact(Skip = "Deprecated with new XmlLayout logic. Use Log4JXmlEventLayout_CompliantXml_Test instead.")] - [Obsolete("This test uses old renderer layout, no longer relevant.")] - public void BadXmlValueTest() - { - var sb = new System.Text.StringBuilder(); - - var forbidden = new HashSet(); - int start = 64976; int end = 65007; - - for (int i = start; i <= end; i++) - { - forbidden.Add(i); - } - - forbidden.Add(0xFFFE); - forbidden.Add(0xFFFF); - - for (int i = char.MinValue; i <= char.MaxValue; i++) - { - char c = Convert.ToChar(i); - if (char.IsSurrogate(c)) - { - continue; // skip surrogates - } - - if (forbidden.Contains(c)) - { - continue; - } - - sb.Append(c); - } - - var badString = sb.ToString(); - - var settings = new XmlWriterSettings - { - Indent = true, - ConformanceLevel = ConformanceLevel.Fragment, - IndentChars = " ", - }; - - sb.Length = 0; - using (XmlWriter xtw = XmlWriter.Create(sb, settings)) - { - xtw.WriteStartElement("log4j", "event", "http:://hello/"); - xtw.WriteElementSafeString("log4j", "message", "http:://hello/", badString); - xtw.WriteEndElement(); - xtw.Flush(); - } - - string goodString = null; - using (XmlReader reader = XmlReader.Create(new StringReader(sb.ToString()))) - { - while (reader.Read()) - { - if (reader.NodeType == XmlNodeType.Text) - { - if (reader.Value.Contains("abc")) - goodString = reader.Value; - } - } - } - - Assert.NotNull(goodString); - Assert.NotEqual(badString.Length, goodString.Length); - Assert.Contains("abc", badString); - Assert.Contains("abc", goodString); + var threadid = Environment.CurrentManagedThreadId; + var machinename = Environment.MachineName; + var result = log4jLayout.Render(logEvent); + Assert.Equal($"Error occurredSystem.Exception: Boom < & >", result); } [Fact] diff --git a/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj b/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj index fbb57dda5d..1f5b538d89 100644 --- a/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj +++ b/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj @@ -16,8 +16,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/NLog.Targets.Trace.Tests/NLog.Targets.Trace.Tests.csproj b/tests/NLog.Targets.Trace.Tests/NLog.Targets.Trace.Tests.csproj index fc11970e7a..0a8cbdf511 100644 --- a/tests/NLog.Targets.Trace.Tests/NLog.Targets.Trace.Tests.csproj +++ b/tests/NLog.Targets.Trace.Tests/NLog.Targets.Trace.Tests.csproj @@ -16,8 +16,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj b/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj index 48c91630ea..fb8df76dc9 100644 --- a/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj +++ b/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj @@ -16,8 +16,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/NLog.UnitTests/NLog.UnitTests.csproj b/tests/NLog.UnitTests/NLog.UnitTests.csproj index 91513b9247..e0b00b5e75 100644 --- a/tests/NLog.UnitTests/NLog.UnitTests.csproj +++ b/tests/NLog.UnitTests/NLog.UnitTests.csproj @@ -42,7 +42,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj b/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj index 977a22aa79..484938e3bd 100644 --- a/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj +++ b/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj @@ -16,8 +16,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive From 0c8604c774c2149281ac36d0620315c1598d61c2 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 27 Apr 2025 00:04:12 +0200 Subject: [PATCH 098/224] XmlParser to replace XmlReader to support AOT (#5712) --- src/NLog.Targets.Trace/NLogTraceListener.cs | 4 +- src/NLog/Config/ConfigSectionHandler.cs | 2 + .../LoggingConfigurationElementExtensions.cs | 18 + .../Config/LoggingConfigurationFileLoader.cs | 36 +- src/NLog/Config/XmlLoggingConfiguration.cs | 180 +++-- .../Config/XmlLoggingConfigurationElement.cs | 21 +- .../Config/XmlParserConfigurationElement.cs | 173 +++++ src/NLog/Config/XmlParserException.cs | 69 ++ src/NLog/Internal/AppEnvironmentWrapper.cs | 7 +- src/NLog/Internal/AssemblyHelpers.cs | 1 + src/NLog/Internal/IFileSystem.cs | 6 +- src/NLog/Internal/XmlHelper.cs | 148 ++-- src/NLog/Internal/XmlParser.cs | 722 ++++++++++++++++++ .../XmlEncodeLayoutRendererWrapper.cs | 2 +- src/NLog/SetupBuilderExtensions.cs | 13 +- tests/NLog.UnitTests/ApiTests.cs | 1 + .../Config/LogFactorySetupTests.cs | 10 +- tests/NLog.UnitTests/Config/ReloadTests.cs | 4 +- tests/NLog.UnitTests/Config/XmlConfigTests.cs | 2 +- .../NLog.UnitTests/ConfigFileLocatorTests.cs | 16 +- .../NLog.UnitTests/Internal/XmlParserTests.cs | 332 ++++++++ .../LayoutRenderers/BaseDirTests.cs | 6 +- .../MdlcLayoutRendererTests.cs | 3 +- .../Mocks/AppEnvironmentMock.cs | 13 +- 24 files changed, 1573 insertions(+), 216 deletions(-) create mode 100644 src/NLog/Config/XmlParserConfigurationElement.cs create mode 100644 src/NLog/Config/XmlParserException.cs create mode 100644 src/NLog/Internal/XmlParser.cs create mode 100644 tests/NLog.UnitTests/Internal/XmlParserTests.cs diff --git a/src/NLog.Targets.Trace/NLogTraceListener.cs b/src/NLog.Targets.Trace/NLogTraceListener.cs index d4e4420fff..7b4cfe2b80 100644 --- a/src/NLog.Targets.Trace/NLogTraceListener.cs +++ b/src/NLog.Targets.Trace/NLogTraceListener.cs @@ -572,11 +572,11 @@ private void InitAttributes() break; case "AUTOLOGGERNAME": - AutoLoggerName = value?.Length == 1 ? value[0] == '1' : bool.Parse(value); + AutoLoggerName = value.Length == 1 ? value[0] == '1' : bool.Parse(value); break; case "DISABLEFLUSH": - DisableFlush = value?.Length == 1 ? value[0] == '1' : bool.Parse(value); + DisableFlush = value.Length == 1 ? value[0] == '1' : bool.Parse(value); break; } } diff --git a/src/NLog/Config/ConfigSectionHandler.cs b/src/NLog/Config/ConfigSectionHandler.cs index bff2f1e631..12e1f31714 100644 --- a/src/NLog/Config/ConfigSectionHandler.cs +++ b/src/NLog/Config/ConfigSectionHandler.cs @@ -89,7 +89,9 @@ protected override void DeserializeElement(XmlReader reader, bool serializeColle try { string configFileName = LogFactory.DefaultAppEnvironment.AppDomainConfigurationFile; +#pragma warning disable CS0618 // Type or member is obsolete _config = new XmlLoggingConfiguration(reader, configFileName, LogManager.LogFactory); +#pragma warning restore CS0618 // Type or member is obsolete } catch (Exception exception) { diff --git a/src/NLog/Config/LoggingConfigurationElementExtensions.cs b/src/NLog/Config/LoggingConfigurationElementExtensions.cs index 2e2ca9564d..3ead46d7c9 100644 --- a/src/NLog/Config/LoggingConfigurationElementExtensions.cs +++ b/src/NLog/Config/LoggingConfigurationElementExtensions.cs @@ -32,6 +32,7 @@ // using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using NLog.Common; @@ -120,6 +121,23 @@ public static string GetConfigItemTypeAttribute(this ILoggingConfigurationElemen return StripOptionalNamespacePrefix(typeAttributeValue)?.Trim(); } + /// + /// Returns children elements with the specified element name. + /// + public static IEnumerable FilterChildren(this ILoggingConfigurationElement element, string elementName) + { + if (elementName is null || element?.Children is null) + yield break; + + foreach (var childElement in element.Children) + { + if (childElement.Name.Equals(elementName, StringComparison.OrdinalIgnoreCase)) + { + yield return childElement; + } + } + } + /// /// Remove the namespace (before :) /// diff --git a/src/NLog/Config/LoggingConfigurationFileLoader.cs b/src/NLog/Config/LoggingConfigurationFileLoader.cs index 04dad2f995..4371af8e1d 100644 --- a/src/NLog/Config/LoggingConfigurationFileLoader.cs +++ b/src/NLog/Config/LoggingConfigurationFileLoader.cs @@ -37,7 +37,6 @@ namespace NLog.Config using System.Collections.Generic; using System.IO; using System.Security; - using System.Xml; using NLog.Common; using NLog.Internal; using NLog.Internal.Fakeables; @@ -132,27 +131,22 @@ private LoggingConfiguration LoadXmlLoggingConfigurationFile(LogFactory logFacto { InternalLogger.Debug("Reading config from XML file: {0}", configFile); - using (var xmlReader = _appEnvironment.LoadXmlFile(configFile)) + using (var textReader = _appEnvironment.LoadTextFile(configFile)) { - return LoadXmlLoggingConfiguration(xmlReader, configFile, logFactory); - } - } - - private LoggingConfiguration LoadXmlLoggingConfiguration(XmlReader xmlReader, string configFile, LogFactory logFactory) - { - try - { - return new XmlLoggingConfiguration(xmlReader, configFile, logFactory); - } - catch (Exception ex) - { - if (ex.MustBeRethrown() || (logFactory.ThrowConfigExceptions ?? logFactory.ThrowExceptions)) - throw; + try + { + return new XmlLoggingConfiguration(textReader, configFile, logFactory); + } + catch (Exception ex) + { + if (ex.MustBeRethrown() || (logFactory.ThrowConfigExceptions ?? logFactory.ThrowExceptions)) + throw; - if (ThrowXmlConfigExceptions(configFile, xmlReader, logFactory, out var autoReload)) - throw; + if (ThrowXmlConfigExceptions(configFile, ex is XmlParserException, logFactory, out var autoReload)) + throw; - return CreateEmptyDefaultConfig(configFile, logFactory, autoReload); + return CreateEmptyDefaultConfig(configFile, logFactory, autoReload); + } } } @@ -161,7 +155,7 @@ private static LoggingConfiguration CreateEmptyDefaultConfig(string configFile, return new XmlLoggingConfiguration($"", configFile, logFactory); // Empty default config, but monitors file } - private bool ThrowXmlConfigExceptions(string configFile, XmlReader xmlReader, LogFactory logFactory, out bool autoReload) + private static bool ThrowXmlConfigExceptions(string configFile, bool invalidXml, LogFactory logFactory, out bool autoReload) { autoReload = false; @@ -172,7 +166,7 @@ private bool ThrowXmlConfigExceptions(string configFile, XmlReader xmlReader, Lo var fileContent = File.ReadAllText(configFile); - if (xmlReader.ReadState == ReadState.Error) + if (invalidXml) { // Avoid reacting to throwExceptions="true" that only exists in comments, only check when invalid xml if (ScanForBooleanParameter(fileContent, "throwExceptions", true)) diff --git a/src/NLog/Config/XmlLoggingConfiguration.cs b/src/NLog/Config/XmlLoggingConfiguration.cs index 1cc3a50b91..0106c99d84 100644 --- a/src/NLog/Config/XmlLoggingConfiguration.cs +++ b/src/NLog/Config/XmlLoggingConfiguration.cs @@ -39,14 +39,13 @@ namespace NLog.Config using System.IO; using System.Linq; using System.Threading; - using System.Xml; using JetBrains.Annotations; using NLog.Common; using NLog.Internal; using NLog.Layouts; /// - /// Loads NLog LoggingConfiguration from xml-file (like app.config) using + /// Loads NLog LoggingConfiguration from xml-file /// /// /// Make sure to update official NLog.xsd schema, when adding new config-options outside targets/layouts @@ -68,7 +67,7 @@ internal XmlLoggingConfiguration(LogFactory logFactory) /// /// Initializes a new instance of the class. /// - /// Configuration file to be read. + /// Path to the config-file to read. public XmlLoggingConfiguration([NotNull] string fileName) : this(fileName, LogManager.LogFactory) { } @@ -76,52 +75,92 @@ public XmlLoggingConfiguration([NotNull] string fileName) /// /// Initializes a new instance of the class. /// - /// Configuration file to be read. + /// Path to the config-file to read. /// The to which to apply any applicable configuration values. public XmlLoggingConfiguration([NotNull] string fileName, LogFactory logFactory) : base(logFactory) { + Guard.ThrowIfNullOrEmpty(fileName); LoadFromXmlFile(fileName); } + /// + /// Initializes a new instance of the class. + /// + /// Configuration file to be read. + public XmlLoggingConfiguration([NotNull] TextReader xmlSource) + : this(xmlSource, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Configuration file to be read. + /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. + public XmlLoggingConfiguration([NotNull] TextReader xmlSource, [CanBeNull] string filePath) + : this(xmlSource, filePath, LogManager.LogFactory) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Configuration file to be read. + /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. + /// The to which to apply any applicable configuration values. + public XmlLoggingConfiguration([NotNull] TextReader xmlSource, [CanBeNull] string filePath, LogFactory logFactory) + : base(logFactory) + { + Guard.ThrowIfNull(xmlSource); + ParseFromTextReader(xmlSource, filePath); + } + +#if NETFRAMEWORK /// /// Initializes a new instance of the class. /// /// XML reader to read from. - public XmlLoggingConfiguration([NotNull] XmlReader reader) + [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] + public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader) : this(reader, null) { } /// /// Initializes a new instance of the class. /// - /// containing the configuration section. - /// Name of the file that contains the element (to be used as a base for including other files). null is allowed. - public XmlLoggingConfiguration([NotNull] XmlReader reader, [CanBeNull] string fileName) + /// XmlReader containing the configuration section. + /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. + [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] + public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader, [CanBeNull] string fileName) : this(reader, fileName, LogManager.LogFactory) { } /// /// Initializes a new instance of the class. /// - /// containing the configuration section. - /// Name of the file that contains the element (to be used as a base for including other files). null is allowed. + /// XmlReader containing the configuration section. + /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The to which to apply any applicable configuration values. - public XmlLoggingConfiguration([NotNull] XmlReader reader, [CanBeNull] string fileName, LogFactory logFactory) + [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] + public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader, [CanBeNull] string fileName, LogFactory logFactory) : base(logFactory) { + Guard.ThrowIfNull(reader); ParseFromXmlReader(reader, fileName); } +#endif /// /// Initializes a new instance of the class. /// /// NLog configuration as XML string. - /// Name of the XML file. + /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The to which to apply any applicable configuration values. - internal XmlLoggingConfiguration([NotNull] string xmlContents, [CanBeNull] string fileName, LogFactory logFactory) + internal XmlLoggingConfiguration([NotNull] string xmlContents, [CanBeNull] string filePath, LogFactory logFactory) : base(logFactory) { - LoadFromXmlContent(xmlContents, fileName); + Guard.ThrowIfNullOrEmpty(xmlContents); + LoadFromXmlContent(xmlContents, filePath); } /// @@ -280,56 +319,60 @@ public static void ResetCandidateConfigFilePath() LogManager.LogFactory.ResetCandidateConfigFilePath(); } - private void LoadFromXmlFile(string fileName) + private void LoadFromXmlFile(string filePath) { - using (XmlReader reader = CreateFileReader(fileName)) + using (var textReader = LogFactory.CurrentAppEnvironment.LoadTextFile(filePath)) { - ParseFromXmlReader(reader, fileName); + ParseFromTextReader(textReader, filePath); } } - internal void LoadFromXmlContent(string xmlContent, string fileName) + internal void LoadFromXmlContent(string xmlContents, string filePath) { - using (var stringReader = new StringReader(xmlContent)) + using (var stringReader = new StringReader(xmlContents)) { - using (XmlReader reader = XmlReader.Create(stringReader)) - { - ParseFromXmlReader(reader, fileName); - } + ParseFromTextReader(stringReader, filePath); } } - /// - /// Create XML reader for (xml config) file. - /// - /// filepath - /// reader or null if filename is empty. - private XmlReader CreateFileReader(string fileName) +#if NETFRAMEWORK + [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] + private void ParseFromXmlReader([NotNull] System.Xml.XmlReader reader, [CanBeNull] string filePath) { - if (!string.IsNullOrEmpty(fileName)) + try { - fileName = fileName.Trim(); - return LogFactory.CurrentAppEnvironment.LoadXmlFile(fileName); + _originalFileName = string.IsNullOrEmpty(filePath) ? filePath : GetFileLookupKey(filePath); + reader.MoveToContent(); + var content = new XmlLoggingConfigurationElement(reader); + if (!string.IsNullOrEmpty(_originalFileName)) + { + InternalLogger.Info("Loading NLog config from XML file: {0}", _originalFileName); + ParseTopLevel(content, filePath, autoReloadDefault: false); + } + else + { + ParseTopLevel(content, null, autoReloadDefault: false); + } + } + catch (Exception exception) + { + var configurationException = new NLogConfigurationException($"Exception when loading configuration {filePath}", exception); + InternalLogger.Error(exception, configurationException.Message); + throw configurationException; } - return null; } +#endif - /// - /// Initializes the configuration. - /// - /// containing the configuration section. - /// Name of the file that contains the element (to be used as a base for including other files). null is allowed. - private void ParseFromXmlReader([NotNull] XmlReader reader, [CanBeNull] string fileName) + private void ParseFromTextReader(TextReader textReader, string filePath) { try { - _originalFileName = string.IsNullOrEmpty(fileName) ? fileName : GetFileLookupKey(fileName); - reader.MoveToContent(); - var content = new XmlLoggingConfigurationElement(reader); + _originalFileName = string.IsNullOrEmpty(filePath) ? filePath : GetFileLookupKey(filePath); + var content = new XmlParserConfigurationElement(new XmlParser(textReader).LoadDocument(out var _)); if (!string.IsNullOrEmpty(_originalFileName)) { InternalLogger.Info("Loading NLog config from XML file: {0}", _originalFileName); - ParseTopLevel(content, fileName, autoReloadDefault: false); + ParseTopLevel(content, filePath, autoReloadDefault: false); } else { @@ -338,25 +381,23 @@ private void ParseFromXmlReader([NotNull] XmlReader reader, [CanBeNull] string f } catch (Exception exception) { - var configurationException = new NLogConfigurationException($"Exception when loading configuration {fileName}", exception); + var configurationException = new NLogConfigurationException($"Exception when loading configuration {filePath}", exception); InternalLogger.Error(exception, configurationException.Message); throw configurationException; } } /// - /// Add a file with configuration. Check if not already included. + /// Include new file into the configuration. Check if not already included. /// - /// - /// - private void ConfigureFromFile([NotNull] string fileName, bool autoReloadDefault) + private void IncludeNewConfigFile([NotNull] string filePath, bool autoReloadDefault) { - if (!_fileMustAutoReloadLookup.ContainsKey(GetFileLookupKey(fileName))) + if (!_fileMustAutoReloadLookup.ContainsKey(GetFileLookupKey(filePath))) { - using (var reader = LogFactory.CurrentAppEnvironment.LoadXmlFile(fileName)) + using (var textReader = LogFactory.CurrentAppEnvironment.LoadTextFile(filePath)) { - reader.MoveToContent(); - ParseTopLevel(new XmlLoggingConfigurationElement(reader, false), fileName, autoReloadDefault); + var configElement = new XmlParserConfigurationElement(new XmlParser(textReader).LoadDocument(out var _), false); + ParseTopLevel(configElement, filePath, autoReloadDefault); } } } @@ -365,13 +406,13 @@ private void ConfigureFromFile([NotNull] string fileName, bool autoReloadDefault /// Parse the root /// /// - /// path to config file. + /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The default value for the autoReload option. - private void ParseTopLevel(XmlLoggingConfigurationElement content, [CanBeNull] string filePath, bool autoReloadDefault) + private void ParseTopLevel(ILoggingConfigurationElement content, [CanBeNull] string filePath, bool autoReloadDefault) { content.AssertName("nlog", "configuration"); - switch (content.LocalName.ToUpperInvariant()) + switch (content.Name.ToUpperInvariant()) { case "CONFIGURATION": ParseConfigurationElement(content, filePath, autoReloadDefault); @@ -387,9 +428,9 @@ private void ParseTopLevel(XmlLoggingConfigurationElement content, [CanBeNull] s /// Parse {configuration} xml element. /// /// - /// path to config file. + /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The default value for the autoReload option. - private void ParseConfigurationElement(XmlLoggingConfigurationElement configurationElement, [CanBeNull] string filePath, bool autoReloadDefault) + private void ParseConfigurationElement(ILoggingConfigurationElement configurationElement, [CanBeNull] string filePath, bool autoReloadDefault) { InternalLogger.Trace("ParseConfigurationElement"); configurationElement.AssertName("configuration"); @@ -405,7 +446,7 @@ private void ParseConfigurationElement(XmlLoggingConfigurationElement configurat /// Parse {NLog} xml element. /// /// - /// path to config file. + /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The default value for the autoReload option. private void ParseNLogElement(ILoggingConfigurationElement nlogElement, [CanBeNull] string filePath, bool autoReloadDefault) { @@ -464,16 +505,16 @@ private void ParseIncludeElement(ILoggingConfigurationElement includeElement, st { newFileName = ExpandSimpleVariables(newFileName); newFileName = SimpleLayout.Evaluate(newFileName, this); - var fullNewFileName = newFileName; + var filePath = newFileName; if (baseDirectory != null) { - fullNewFileName = Path.Combine(baseDirectory, newFileName); + filePath = Path.Combine(baseDirectory, newFileName); } - if (File.Exists(fullNewFileName)) + if (File.Exists(filePath)) { - InternalLogger.Debug("Including file '{0}'", fullNewFileName); - ConfigureFromFile(fullNewFileName, autoReloadDefault); + InternalLogger.Debug("Including file '{0}'", filePath); + IncludeNewConfigFile(filePath, autoReloadDefault); } else { @@ -481,18 +522,18 @@ private void ParseIncludeElement(ILoggingConfigurationElement includeElement, st if (newFileName.IndexOf('*') >= 0) { - ConfigureFromFilesByMask(baseDirectory, newFileName, autoReloadDefault); + IncludeConfigFilesByMask(baseDirectory, newFileName, autoReloadDefault); } else { if (ignoreErrors) { //quick stop for performances - InternalLogger.Debug("Skipping included file '{0}' as it can't be found", fullNewFileName); + InternalLogger.Debug("Skipping included file '{0}' as it can't be found", filePath); return; } - throw new FileNotFoundException("Included file not found: " + fullNewFileName); + throw new FileNotFoundException("Included file not found: " + filePath); } } } @@ -516,7 +557,7 @@ private void ParseIncludeElement(ILoggingConfigurationElement includeElement, st /// base directory in case if is relative /// relative or absolute fileMask /// - private void ConfigureFromFilesByMask(string baseDirectory, string fileMask, bool autoReloadDefault) + private void IncludeConfigFilesByMask(string baseDirectory, string fileMask, bool autoReloadDefault) { var directory = baseDirectory; @@ -541,10 +582,10 @@ private void ConfigureFromFilesByMask(string baseDirectory, string fileMask, boo } var files = Directory.GetFiles(directory, fileMask); - foreach (var file in files) + foreach (var filePath in files) { //note we exclude our self in ConfigureFromFile - ConfigureFromFile(file, autoReloadDefault); + IncludeNewConfigFile(filePath, autoReloadDefault); } } @@ -559,7 +600,6 @@ public override string ToString() return $"{base.ToString()}, FilePath={_originalFileName}"; } - private sealed class AutoReloadConfigFileWatcher : IDisposable { private readonly LogFactory _logFactory; diff --git a/src/NLog/Config/XmlLoggingConfigurationElement.cs b/src/NLog/Config/XmlLoggingConfigurationElement.cs index 7d3f267ef0..ebba26606c 100644 --- a/src/NLog/Config/XmlLoggingConfigurationElement.cs +++ b/src/NLog/Config/XmlLoggingConfigurationElement.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#if NETFRAMEWORK + namespace NLog.Config { using System; @@ -42,6 +44,7 @@ namespace NLog.Config /// /// Represents simple XML element with case-insensitive attribute semantics. /// + [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] internal sealed class XmlLoggingConfigurationElement : ILoggingConfigurationElement { /// @@ -120,22 +123,6 @@ IEnumerable ILoggingConfigurationElement.Children } } - /// - /// Returns children elements with the specified element name. - /// - /// Name of the element. - /// Children elements with the specified element name. - public IEnumerable FilterChildren(string elementName) - { - foreach (var childElement in Children) - { - if (childElement.LocalName.Equals(elementName, StringComparison.OrdinalIgnoreCase)) - { - yield return childElement; - } - } - } - /// /// Asserts that the name of the element is among specified element names. /// @@ -228,3 +215,5 @@ public override string ToString() } } } + +#endif diff --git a/src/NLog/Config/XmlParserConfigurationElement.cs b/src/NLog/Config/XmlParserConfigurationElement.cs new file mode 100644 index 0000000000..f499d54c2b --- /dev/null +++ b/src/NLog/Config/XmlParserConfigurationElement.cs @@ -0,0 +1,173 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Config +{ + using System; + using System.Collections.Generic; + using System.Linq; + using NLog.Internal; + + internal sealed class XmlParserConfigurationElement : ILoggingConfigurationElement + { + /// + /// Gets the element name. + /// + public string Name { get; private set; } + + /// + /// Gets the value of the element. + /// + public string Value { get; private set; } + + /// + /// Gets the dictionary of attribute values. + /// + public IList> AttributeValues { get; } + + /// + /// Gets the collection of child elements. + /// + public IList Children { get; } + + public IEnumerable> Values + { + get + { + for (int i = 0; i < Children.Count; ++i) + { + var child = Children[i]; + if (SingleValueElement(child)) + { + // Values assigned using nested node-elements. Maybe in combination with attributes + return AttributeValues.Concat(Children.Where(item => SingleValueElement(item)).Select(item => new KeyValuePair(item.Name, item.Value))); + } + } + return AttributeValues; + } + } + + IEnumerable ILoggingConfigurationElement.Children + { + get + { + for (int i = 0; i < Children.Count; ++i) + { + var child = Children[i]; + if (!SingleValueElement(child)) + return Children.Where(item => !SingleValueElement(item)).Cast(); + } + + return ArrayHelper.Empty(); + } + } + + public XmlParserConfigurationElement(XmlParser.XmlParserElement xmlElement) + : this(xmlElement, false) + { + } + + public XmlParserConfigurationElement(XmlParser.XmlParserElement xmlElement, bool nestedElement) + { + Parse(xmlElement, nestedElement, out var attributes, out var children); + AttributeValues = attributes ?? ArrayHelper.Empty>(); + Children = children ?? ArrayHelper.Empty(); + } + + private static bool SingleValueElement(XmlParserConfigurationElement child) + { + // Node-element that works like an attribute + return child.Children.Count == 0 && child.AttributeValues.Count == 0 && child.Value != null; + } + + private void Parse(XmlParser.XmlParserElement xmlElement, bool nestedElement, out IList> attributes, out IList children) + { + var namePrefixIndex = xmlElement.Name.IndexOf(':'); + Name = namePrefixIndex >= 0 ? xmlElement.Name.Substring(namePrefixIndex + 1) : xmlElement.Name; + Value = xmlElement.InnerText; + attributes = xmlElement.Attributes; + + if (attributes?.Count > 0) + { + if (!nestedElement) + { + for (int i = attributes.Count - 1; i >= 0; --i) + { + var attributeName = attributes[i].Key; + if (IsSpecialXmlRootAttribute(attributeName)) + { + attributes.RemoveAt(i); + } + } + } + + for (int j = 0; j < attributes.Count; ++j) + { + var attributePrefixIndex = attributes[j].Key.IndexOf(':'); + if (attributePrefixIndex >= 0) + attributes[j] = new KeyValuePair(attributes[j].Key.Substring(attributePrefixIndex + 1), attributes[j].Value); + } + } + + children = null; + + if (xmlElement.Children?.Count > 0) + { + foreach (var child in xmlElement.Children) + { + children = children ?? new List(); + var nestedChild = nestedElement || !string.Equals(child.Name, "nlog", StringComparison.OrdinalIgnoreCase); + children.Add(new XmlParserConfigurationElement(child, nestedChild)); + } + } + } + + /// + /// Special attribute we could ignore + /// + private static bool IsSpecialXmlRootAttribute(string attributeName) + { + if (attributeName?.StartsWith("xmlns", StringComparison.OrdinalIgnoreCase) == true) + return true; + if (attributeName?.IndexOf(":xmlns", StringComparison.OrdinalIgnoreCase) >= 0) + return true; + if (attributeName?.StartsWith("schemaLocation", StringComparison.OrdinalIgnoreCase) == true) + return true; + if (attributeName?.IndexOf(":schemaLocation", StringComparison.OrdinalIgnoreCase) >= 0) + return true; + if (attributeName?.StartsWith("xsi:", StringComparison.OrdinalIgnoreCase) == true) + return true; + return false; + } + } +} diff --git a/src/NLog/Config/XmlParserException.cs b/src/NLog/Config/XmlParserException.cs new file mode 100644 index 0000000000..aa3aa97f96 --- /dev/null +++ b/src/NLog/Config/XmlParserException.cs @@ -0,0 +1,69 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Config +{ + using System; + + /// + /// Exception thrown during XML parsing + /// + public sealed class XmlParserException : NLogConfigurationException + { + /// + /// Initializes a new instance of the class. + /// + public XmlParserException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public XmlParserException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public XmlParserException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/src/NLog/Internal/AppEnvironmentWrapper.cs b/src/NLog/Internal/AppEnvironmentWrapper.cs index ead79971b0..748e7e20d0 100644 --- a/src/NLog/Internal/AppEnvironmentWrapper.cs +++ b/src/NLog/Internal/AppEnvironmentWrapper.cs @@ -37,7 +37,6 @@ namespace NLog.Internal.Fakeables using System.Collections.Generic; using System.Diagnostics; using System.IO; - using System.Xml; using NLog.Common; internal sealed class AppEnvironmentWrapper : IAppEnvironment @@ -103,11 +102,9 @@ public bool FileExists(string path) return File.Exists(path); } - /// - public XmlReader LoadXmlFile(string path) + public TextReader LoadTextFile(string path) { - path = FixFilePathWithLongUNC(path); - return XmlReader.Create(path); + return new StreamReader(path); } /// diff --git a/src/NLog/Internal/AssemblyHelpers.cs b/src/NLog/Internal/AssemblyHelpers.cs index 3c1cbc79a9..ad3e848716 100644 --- a/src/NLog/Internal/AssemblyHelpers.cs +++ b/src/NLog/Internal/AssemblyHelpers.cs @@ -61,6 +61,7 @@ public static string GetAssemblyFileLocation(Assembly assembly) InternalLogger.Debug("Ignoring assembly location because code base is unknown: '{0}' ({1})", assembly.CodeBase, assemblyFullName); return string.Empty; } +#pragma warning restore SYSLIB0012 // Type or member is obsolete var assemblyLocation = System.IO.Path.GetDirectoryName(assemblyCodeBase.LocalPath); if (string.IsNullOrEmpty(assemblyLocation)) diff --git a/src/NLog/Internal/IFileSystem.cs b/src/NLog/Internal/IFileSystem.cs index f8e5578574..0f04118cce 100644 --- a/src/NLog/Internal/IFileSystem.cs +++ b/src/NLog/Internal/IFileSystem.cs @@ -33,7 +33,7 @@ namespace NLog.Internal.Fakeables { - using System.Xml; + using System.IO; /// /// Abstract calls to FileSystem @@ -43,8 +43,8 @@ internal interface IFileSystem /// Determines whether the specified file exists. /// The file to check. bool FileExists(string path); - /// Returns the content of the specified file + /// Returns the content of the specified text-file /// The file to load. - XmlReader LoadXmlFile(string path); + TextReader LoadTextFile(string path); } } diff --git a/src/NLog/Internal/XmlHelper.cs b/src/NLog/Internal/XmlHelper.cs index 64826464b0..c2a52635e3 100644 --- a/src/NLog/Internal/XmlHelper.cs +++ b/src/NLog/Internal/XmlHelper.cs @@ -36,20 +36,51 @@ namespace NLog.Internal using System; using System.Globalization; using System.Text; - using System.Xml; /// /// Helper class for XML /// internal static class XmlHelper { - // found on https://stackoverflow.com/questions/397250/unicode-regex-invalid-xml-characters/961504#961504 - // filters control characters but allows only properly-formed surrogate sequences -#if NET35 - private static readonly System.Text.RegularExpressions.Regex InvalidXmlChars = new System.Text.RegularExpressions.Regex( - @"(? '\u001f' && chr < HIGH_SURROGATE_START) || ExoticIsXmlChar(chr); + } + + private static bool ExoticIsXmlChar(char chr) + { + if (chr < '\u0020') + return chr == '\u0009' || chr == '\u000a' || chr == '\u000d'; + + if (XmlConvertIsHighSurrogate(chr) || XmlConvertIsLowSurrogate(chr)) + return false; + + if (chr == '\ufffe' || chr == '\uffff') + return false; + + return true; + } + + public static bool XmlConvertIsHighSurrogate(char chr) + { + return chr >= HIGH_SURROGATE_START && chr <= HIGH_SURROGATE_END; + } + + public static bool XmlConvertIsLowSurrogate(char chr) + { + return chr >= LOW_SURROGATE_START && chr <= LOW_SURROGATE_END; + } + + public static bool XmlConvertIsXmlSurrogatePair(char lowChar, char highChar) + { + return XmlConvertIsHighSurrogate(highChar) && XmlConvertIsLowSurrogate(lowChar); + } /// /// removes any unusual unicode characters that can't be encoded into XML @@ -59,14 +90,13 @@ private static string RemoveInvalidXmlChars(string text) if (string.IsNullOrEmpty(text)) return string.Empty; -#if !NET35 int length = text.Length; for (int i = 0; i < length; ++i) { char ch = text[i]; - if (!XmlConvert.IsXmlChar(ch)) + if (!XmlConvertIsXmlChar(ch)) { - if (i + 1 < text.Length && XmlConvert.IsXmlSurrogatePair(text[i + 1], ch)) + if (i + 1 < text.Length && XmlConvertIsXmlSurrogatePair(text[i + 1], ch)) { ++i; } @@ -76,13 +106,10 @@ private static string RemoveInvalidXmlChars(string text) } } } + return text; -#else - return InvalidXmlChars.Replace(text, string.Empty); -#endif } -#if !NET35 /// /// Cleans string of any invalid XML chars found /// @@ -94,14 +121,13 @@ private static string CreateValidXmlString(string text) for (int i = 0; i < text.Length; ++i) { char ch = text[i]; - if (XmlConvert.IsXmlChar(ch)) + if (XmlConvertIsXmlChar(ch)) { sb.Append(ch); } } return sb.ToString(); } -#endif internal static void PerformXmlEscapeWhenNeeded(StringBuilder builder, int startPos, bool xmlEncodeNewlines) { @@ -226,29 +252,35 @@ internal static string XmlConvertToString(object value) internal static string XmlConvertToString(float value) { - if (float.IsNaN(value)) - return XmlConvert.ToString(value); - - if (float.IsInfinity(value)) + if (float.IsInfinity(value) || float.IsNaN(value)) return Convert.ToString(value, CultureInfo.InvariantCulture); - - return EnsureDecimalPlace(XmlConvert.ToString(value)); + else + return EnsureDecimalPlace(value.ToString("R", NumberFormatInfo.InvariantInfo)); } internal static string XmlConvertToString(double value) { - if (double.IsNaN(value)) - return XmlConvert.ToString(value); - - if (double.IsInfinity(value)) + if (double.IsInfinity(value) || double.IsNaN(value)) return Convert.ToString(value, CultureInfo.InvariantCulture); - - return EnsureDecimalPlace(XmlConvert.ToString(value)); + else + return EnsureDecimalPlace(value.ToString("R", NumberFormatInfo.InvariantInfo)); } internal static string XmlConvertToString(decimal value) { - return EnsureDecimalPlace(XmlConvert.ToString(value)); + return EnsureDecimalPlace(value.ToString(null, NumberFormatInfo.InvariantInfo)); + } + + /// + /// Converts DateTime to ISO 8601 format in UTC timezone. + /// + internal static string XmlConvertToString(DateTime value) + { + if (value.Kind == DateTimeKind.Unspecified) + value = new DateTime(value.Ticks, DateTimeKind.Utc); + else + value = value.ToUniversalTime(); + return value.ToString("yyyy-MM-ddTHH:mm:ss.FFFFFFFK"); } /// @@ -385,23 +417,23 @@ internal static string XmlConvertToString(IConvertible value, TypeCode objTypeCo switch (objTypeCode) { case TypeCode.Boolean: - return XmlConvert.ToString(value.ToBoolean(CultureInfo.InvariantCulture)); // boolean as lowercase + return value.ToBoolean(CultureInfo.InvariantCulture) ? "true" : "false"; // boolean as lowercase case TypeCode.Byte: - return XmlConvert.ToString(value.ToByte(CultureInfo.InvariantCulture)); + return value.ToByte(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); case TypeCode.SByte: - return XmlConvert.ToString(value.ToSByte(CultureInfo.InvariantCulture)); + return value.ToSByte(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); case TypeCode.Int16: - return XmlConvert.ToString(value.ToInt16(CultureInfo.InvariantCulture)); + return value.ToInt16(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); case TypeCode.Int32: - return XmlConvert.ToString(value.ToInt32(CultureInfo.InvariantCulture)); + return value.ToInt32(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); case TypeCode.Int64: - return XmlConvert.ToString(value.ToInt64(CultureInfo.InvariantCulture)); + return value.ToInt64(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); case TypeCode.UInt16: - return XmlConvert.ToString(value.ToUInt16(CultureInfo.InvariantCulture)); + return value.ToUInt16(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); case TypeCode.UInt32: - return XmlConvert.ToString(value.ToUInt32(CultureInfo.InvariantCulture)); + return value.ToUInt32(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); case TypeCode.UInt64: - return XmlConvert.ToString(value.ToUInt64(CultureInfo.InvariantCulture)); + return value.ToUInt64(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); case TypeCode.Single: return XmlConvertToString(value.ToSingle(CultureInfo.InvariantCulture)); case TypeCode.Double: @@ -409,7 +441,7 @@ internal static string XmlConvertToString(IConvertible value, TypeCode objTypeCo case TypeCode.Decimal: return XmlConvertToString(value.ToDecimal(CultureInfo.InvariantCulture)); case TypeCode.DateTime: - return XmlConvert.ToString(value.ToDateTime(CultureInfo.InvariantCulture), XmlDateTimeSerializationMode.Utc); + return XmlConvertToString(value.ToDateTime(CultureInfo.InvariantCulture)); case TypeCode.Char: return safeConversion ? RemoveInvalidXmlChars(value.ToString(CultureInfo.InvariantCulture)) : value.ToString(CultureInfo.InvariantCulture); case TypeCode.String: @@ -448,49 +480,35 @@ private static string EnsureDecimalPlace(string text) public static void RemoveInvalidXmlIfNeeded(StringBuilder builder, int orgLength) { -#if !NET35 - bool containsInvalid = false; for (int i = orgLength; i < builder.Length; ++i) { - if (!XmlConvert.IsXmlChar(builder[i])) + if (!XmlConvertIsXmlChar(builder[i])) { - containsInvalid = true; + var text = builder.ToString(i, builder.Length - i); + builder.Length = i; + text = RemoveInvalidXmlChars(text); + builder.Append(text); break; } } - - if (containsInvalid) - { - var text = builder.ToString(orgLength, builder.Length - orgLength); - var cleanedText = RemoveInvalidXmlChars(text); - builder.Length = orgLength; - builder.Append(cleanedText); - } -#else - var text = builder.ToString(orgLength, builder.Length - orgLength); - var cleanedText = RemoveInvalidXmlChars(text); - builder.Length = orgLength; - builder.Append(cleanedText); -#endif } public static void EscapeCDataIfNeeded(StringBuilder builder, int orgLength) { - for (int i = orgLength; i + 2 < builder.Length; ++i) + for (int i = orgLength; i < builder.Length; ++i) { - if (builder[i] == ']' && builder[i + 1] == ']' && builder[i + 2] == '>') + if (builder[i] == ']' && i + 2 < builder.Length && builder[i + 1] == ']' && builder[i + 2] == '>') { - var escapedCData = builder.ToString(i, builder.Length - i) - .Replace("]]>", "]]]]>"); + var text = builder.ToString(i, builder.Length - i); builder.Length = i; - builder.Append(escapedCData); + text = text.Replace("]]>", "]]]]>"); + builder.Append(text); break; } } } - - public static string WrapInCData(string text) + public static string EscapeCData(string text) { if (string.IsNullOrEmpty(text)) return ""; diff --git a/src/NLog/Internal/XmlParser.cs b/src/NLog/Internal/XmlParser.cs new file mode 100644 index 0000000000..2143365d29 --- /dev/null +++ b/src/NLog/Internal/XmlParser.cs @@ -0,0 +1,722 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Internal +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using NLog.Config; + + /// + /// A minimal XML reader, because .NET System.Xml.XmlReader doesn't work with AOT + /// + internal sealed class XmlParser + { + private readonly CharEnumerator _xmlSource; + private readonly StringBuilder _stringBuilder = new StringBuilder(); + + public XmlParser(TextReader xmlSource) + { + _xmlSource = new CharEnumerator(xmlSource); + } + + public XmlParser(string xmlSource) + { + _xmlSource = new CharEnumerator(new StringReader(xmlSource)); + } + + public XmlParserElement LoadDocument(out IList processingInstructions) + { + try + { + TryReadProcessingInstructions(out processingInstructions); + + if (!TryReadStartElement(out var rootName, out var rootAttributes)) + throw new XmlParserException("Invalid XML document. Cannot parse root start-tag"); + + Stack stack = new Stack(); + + var currentRoot = new XmlParserElement(rootName, rootAttributes); + stack.Push(currentRoot); + + bool stillReading = true; + + while (stillReading) + { + stillReading = false; + + if (TryReadEndElement(currentRoot.Name)) + { + stillReading = true; + stack.Pop(); + if (stack.Count == 0) + break; + + currentRoot = stack.Peek(); + } + + try + { + if (TryReadInnerText(out var innerText)) + { + stillReading = true; + currentRoot.InnerText += innerText; + } + + if (TryReadStartElement(out var elementName, out var elementAttributes)) + { + stillReading = true; + currentRoot = new XmlParserElement(elementName, elementAttributes); + stack.Peek().AddChild(currentRoot); + stack.Push(currentRoot); + } + } + catch (XmlParserException ex) + { + throw new XmlParserException(ex.Message + $" - Start-tag: {currentRoot.Name}"); + } + } + + if (!stillReading) + throw new XmlParserException($"Invalid XML document. Cannot parse end-tag: {currentRoot.Name}"); + + SkipWhiteSpaces(); + if (_xmlSource.MoveNext()) + throw new XmlParserException($"Invalid XML document. Unexpected characters after end-tag: {currentRoot.Name}"); + + return currentRoot; + } + catch (XmlParserException ex) + { + throw new XmlParserException(ex.Message + $" - Line: {_xmlSource.LineNumber}"); + } + } + + public bool TryReadProcessingInstructions(out IList processingInstructions) + { + SkipWhiteSpaces(); + + processingInstructions = null; + + while (_xmlSource.Current == '<' && _xmlSource.Peek() == '?') + { + if (!TryBeginReadStartElement(out var instructionName, processingInstruction: true)) + throw new XmlParserException("Invalid XML document. Cannot parse XML processing instruction"); + + if (string.IsNullOrEmpty(instructionName) || instructionName.Length == 1 || instructionName[0] != '?') + throw new XmlParserException("Invalid XML document. Cannot parse XML processing instruction"); + + instructionName = instructionName.Substring(1); + + TryReadAttributes(out var instructionAttributes, expectsProcessingInstruction: true); + + if (!SkipChar('?')) + throw new XmlParserException($"Invalid XML document. Cannot parse XML processing instruction: {instructionName}"); + if (!SkipChar('>')) + throw new XmlParserException($"Invalid XML document. Cannot parse XML processing instruction: {instructionName}"); + + var xmlInstruction = new XmlParserElement(instructionName, instructionAttributes); + processingInstructions = processingInstructions ?? new List(); + processingInstructions.Add(xmlInstruction); + + SkipWhiteSpaces(); + } + + return processingInstructions != null; + } + + /// + /// Reads a start element. + /// + /// True if start element was found. + /// Something unexpected has failed. + public bool TryReadStartElement(out string name, out List> attributes) + { + SkipWhiteSpaces(); + + if (TryBeginReadStartElement(out name)) + { + try + { + TryReadAttributes(out attributes); + SkipChar('>'); + } + catch (XmlParserException ex) + { + throw new XmlParserException(ex.Message + $" - Cannot parse attributes for Start-tag: {name}"); + } + return true; + } + + name = default; + attributes = default; + return false; + } + + /// + /// Skips an end element. + /// + /// The name of the element to skip. + /// True if an end element was skipped; otherwise, false. + /// Something unexpected has failed. + public bool TryReadEndElement(string name) + { + _ = SkipWhiteSpaces(); + + if (_xmlSource.Current == '<' && _xmlSource.Peek() != '/') + return false; + + if (_xmlSource.Current == '/' && _xmlSource.Peek() == '>') + return SkipChar('/') && SkipChar('>'); // Self-closing element + + if (!SkipChar('<')) + return false; + + if (!SkipChar('/')) + throw new XmlParserException($"Invalid XML document. Cannot parse end-tag: {name}"); + + for (var i = 0; i < name.Length; i++) + { + var chr = _xmlSource.Current; + if (chr != name[i]) + throw new XmlParserException($"Invalid XML document. Cannot parse end-tag: {name}"); + + if (!_xmlSource.MoveNext()) + throw new XmlParserException($"Invalid XML document. Cannot parse end-tag: {name}"); + } + + if (!SkipChar('>')) + throw new XmlParserException($"Invalid XML document. Cannot parse end-tag: {name}"); + + return true; + } + + /// + /// Reads content of an element. + /// + /// The content of the element. + /// Something unexpected has failed. + public bool TryReadInnerText(out string innerText) + { + var currentChar = _xmlSource.Current; + + SkipWhiteSpaces(); + + innerText = ReadUntilChar('<', includeSpaces: true); + + while (_xmlSource.Current == '<' && _xmlSource.Peek() == '!') + { + _xmlSource.MoveNext(); + if (_xmlSource.Peek() == '-') + { + // + SkipXmlComment(); + } + else if (_xmlSource.Peek() == '[') + { + // + innerText += ReadCDATA(); + } + else + { + throw new XmlParserException($"Invalid XML document. Cannot parse XML comment"); + } + + innerText += ReadUntilChar('<', includeSpaces: true); + } + + SkipWhiteSpaces(); + if (string.IsNullOrEmpty(innerText) && _xmlSource.Current == '<') + return currentChar != '<'; + else + return true; + } + + private string ReadCDATA() + { + string contentValue; + if (!SkipChar('!') || !SkipChar('[') || !SkipChar('C') || !SkipChar('D') || !SkipChar('A') || !SkipChar('T') || !SkipChar('A') || !SkipChar('[')) + throw new XmlParserException("Invalid XML document. Cannot parse XML CDATA"); + + _stringBuilder.ClearBuilder(); + + do + { + if (_xmlSource.Current == ']' && _xmlSource.Peek() == ']') + { + _xmlSource.MoveNext(); + if (_xmlSource.Peek() == '>') + { + _xmlSource.MoveNext(); + _xmlSource.MoveNext(); + break; + } + + _stringBuilder.Append(']'); + } + + _stringBuilder.Append(_xmlSource.Current); + } while (_xmlSource.MoveNext()); + + contentValue = _stringBuilder.ToString(); + SkipWhiteSpaces(); + return contentValue; + } + + private void SkipXmlComment() + { + if (!SkipChar('!') || !SkipChar('-') || !SkipChar('-')) + throw new XmlParserException("Invalid XML document. Cannot parse XML comment"); + + while (_xmlSource.MoveNext()) + { + if (!SkipChar('-')) + continue; + + if (SkipChar('-') && SkipChar('>')) + break; + } + + SkipWhiteSpaces(); + } + + /// Something unexpected has failed. + private bool TryReadAttributes(out List> attributes, bool expectsProcessingInstruction = false) + { + SkipWhiteSpaces(); + + attributes = null; + + while (_xmlSource.Current != '>' && _xmlSource.Current != '/' && (!expectsProcessingInstruction || _xmlSource.Current != '?')) + { + var attName = ReadUntilChar('=').Trim(); + if (string.IsNullOrEmpty(attName)) + throw new XmlParserException("Invalid XML document. Cannot parse XML attribute"); + + if (!SkipChar('=')) + throw new XmlParserException("Invalid XML document. Cannot parse XML attribute"); + + var isApostrophe = false; + + SkipWhiteSpaces(); + + if (!SkipChar('"')) + { + if (SkipChar('\'')) + { + isApostrophe = true; + } + else + { + throw new XmlParserException($"Invalid XML document. Cannot parse XML attribute: {attName}"); + } + } + + try + { + var attValue = ReadUntilChar(isApostrophe ? '\'' : '"', includeSpaces: true); + _xmlSource.MoveNext(); + + attributes = attributes ?? new List>(); + attributes.Add(new KeyValuePair(attName, attValue)); + + SkipWhiteSpaces(); + } + catch (XmlParserException ex) + { + throw new XmlParserException(ex.Message + $" - XML attribute: {attName}"); + } + } + + return attributes != null; + } + + /// + /// Consumer of this method should handle safe position. + /// + /// Something unexpected has failed. + private bool TryBeginReadStartElement(out string name, bool processingInstruction = false) + { + if (_xmlSource.Current != '<' || _xmlSource.Peek() == '/' || _xmlSource.Peek() == '!') + { + name = default; + return false; + } + + _xmlSource.MoveNext(); + + SkipWhiteSpaces(); + + _stringBuilder.ClearBuilder(); + + do + { + var chr = _xmlSource.Current; + if (CharIsSpace(chr) || chr == '/' || chr == '>') + { + break; + } + + if (processingInstruction && chr == '?') + { + if (_stringBuilder.Length != 0) + throw new XmlParserException($"Invalid XML document. Cannot parse XML start-tag with character: {chr}"); + } + else if (!IsValidXmlNameChar(chr)) + { + throw new XmlParserException($"Invalid XML document. Cannot parse XML start-tag with character: {chr}"); + } + + _stringBuilder.Append(chr); + } while (_xmlSource.MoveNext()); + + name = _stringBuilder.ToString(); + if (string.IsNullOrEmpty(name)) + throw new XmlParserException($"Invalid XML document. Cannot parse XML start-tag"); + + return true; + } + + private bool SkipChar(char c) + { + if (_xmlSource.Current != c) + { + return false; + } + + _xmlSource.MoveNext(); + return true; + } + + private bool SkipWhiteSpaces() + { + var skipped = false; + while (!_xmlSource.EndOfFile && CharIsSpace(_xmlSource.Current) && _xmlSource.MoveNext()) + { + skipped = true; + } + return skipped; + } + + /// Something unexpected has failed. + private string ReadUntilChar(char expectedChar, bool includeSpaces = false) + { + _stringBuilder.ClearBuilder(); + + bool trimEnd = false; + + do + { + var chr = _xmlSource.Current; + if (chr == expectedChar) + { + break; + } + + if (!includeSpaces && CharIsSpace(chr)) + { + SkipWhiteSpaces(); + if (_xmlSource.Current == expectedChar) + break; + throw new XmlParserException($"Invalid XML document. Cannot parse attribute-name with white-space"); + } + else if (!includeSpaces && !IsValidXmlNameChar(chr)) + { + throw new XmlParserException($"Invalid XML document. Cannot parse attribute-name with character: {chr}"); + } + + if (chr == '<' && (!includeSpaces || expectedChar == '<')) + throw new XmlParserException($"Invalid XML document. Cannot parse value with '<'"); + + if (chr == '>' && (!includeSpaces || expectedChar == '<')) + throw new XmlParserException($"Invalid XML document. Cannot parse value with '>'"); + + if (includeSpaces && chr == '&') + { + _xmlSource.MoveNext(); + if (_xmlSource.Current == '#' && char.IsDigit(_xmlSource.Peek())) + { + int unicode = TryParseUnicodeValue(); + if (unicode != 0) + _stringBuilder.Append((char)unicode); + } + else if (_xmlSource.Current == '#' && (_xmlSource.Peek() == 'x' || _xmlSource.Peek() == 'X')) + { + _xmlSource.MoveNext(); + int unicode = TryParseUnicodeValueHex(); + if (unicode != 0) + _stringBuilder.Append((char)unicode); + } + else if (TryParseSpecialXmlToken(out var specialToken)) + { + _stringBuilder.Append(specialToken); + } + else + { + _stringBuilder.Append('&'); + if (_xmlSource.Current == expectedChar) + break; + _stringBuilder.Append(_xmlSource.Current); + } + } + else + { + if (includeSpaces && expectedChar == '<') + { + if (_stringBuilder.Length == 0 && CharIsSpace(chr)) + continue; + + trimEnd = !trimEnd && CharIsSpace(chr); + } + _stringBuilder.Append(chr); + } + } while (_xmlSource.MoveNext()); + + var value = _stringBuilder.ToString(); + return trimEnd ? value.TrimEnd(ArrayHelper.Empty()) : value; + } + + private static bool IsValidXmlNameChar(char chr) + { + if (char.IsLetter(chr) || char.IsDigit(chr)) + return true; + + switch (chr) + { + case '_': + case '-': + case '.': + case ':': + return true; + default: + return false; + } + } + + private int TryParseUnicodeValue() + { + int unicode = '\0'; + while (_xmlSource.MoveNext()) + { + if (_xmlSource.Current == ';') + break; + + if (_xmlSource.Current < '0' || _xmlSource.Current > '9') + throw new XmlParserException("Invalid XML document. Cannot parse unicode-char digit-value"); + + unicode *= 10; + unicode += _xmlSource.Current - '0'; + } + + if (unicode >= '\uffff') + throw new XmlParserException("Invalid XML document. Unicode value exceeds maximum allowed value"); + + return unicode; + } + + private int TryParseUnicodeValueHex() + { + int unicode = '\0'; + while (_xmlSource.MoveNext()) + { + if (_xmlSource.Current == ';') + break; + + unicode *= 16; + if ("abcdef".Contains(char.ToLower(_xmlSource.Current))) + unicode += int.Parse(_xmlSource.Current.ToString(), System.Globalization.NumberStyles.HexNumber); + else if (_xmlSource.Current < '0' || _xmlSource.Current > '9') + throw new XmlParserException("Invalid XML document. Cannot parse unicode-char hex-value"); + else + unicode += _xmlSource.Current - '0'; + } + + if (unicode >= '\uffff') + throw new XmlParserException("Invalid XML document. Unicode value exceeds maximum allowed value"); + + return unicode; + } + + private bool TryParseSpecialXmlToken(out string xmlToken) + { + foreach (var token in _specialTokens) + { + if (_xmlSource.Current == token.Key[0] && _xmlSource.Peek() == token.Key[1]) + { + foreach (var tokenChr in token.Key) + if (!SkipChar(tokenChr)) + throw new XmlParserException($"Invalid XML document. Cannot parse special token: {token.Key}"); + if (_xmlSource.Current != ';') + throw new XmlParserException($"Invalid XML document. Cannot parse special token: {token.Key}"); + xmlToken = token.Value; + return true; + } + } + + xmlToken = null; + return false; + } + + private static readonly Dictionary _specialTokens = new Dictionary() + { + { "amp", "&" }, + { "AMP", "&" }, + { "apos", "\'" }, + { "APOS", "\'" }, + { "quot", "\"" }, + { "QUOT", "\"" }, + { "lt", "<" }, + { "LT", "<" }, + { "gt", ">" }, + { "GT", ">" }, + }; + + private static bool CharIsSpace(char c) + { + switch (c) + { + case ' ': + case '\t': + case '\r': + case '\n': + return true; + default: + return char.IsWhiteSpace(c); + } + } + + public sealed class XmlParserElement + { + public string Name { get; set; } + public string InnerText { get; set; } + public IList Children { get; private set; } + public IList> Attributes { get; } + + public XmlParserElement(string name, IList> attributes) + { + Name = name; + Attributes = attributes; + } + + public void AddChild(XmlParserElement child) + { + if (Children is null) + Children = new List(); + Children.Add(child); + } + } + + private sealed class CharEnumerator : IEnumerator + { + private readonly TextReader _xmlSource; + private int _lineNumber; + private char _current; + private char? _peek; + private bool _endOfFile; + + public CharEnumerator(TextReader xmlSource) + { + _xmlSource = xmlSource; + var current = xmlSource.Read(); + _current = current < 0 ? '\0' : (char)current; + _lineNumber = current == '\n' ? 2 : 1; + } + + public char Current + { + get + { + if (_endOfFile) + throw new XmlParserException($"Invalid XML document. Unexpected end of document."); + return _current; + } + } + + public int LineNumber => _lineNumber; + + object IEnumerator.Current => Current; + + public bool EndOfFile => _endOfFile; + + public bool MoveNext() + { + if (_peek.HasValue) + { + _current = _peek.Value; + if (_current == '\n') + ++_lineNumber; + _peek = null; + return true; + } + + var current = _xmlSource.Read(); + if (current < 0) + { + _endOfFile = true; + return false; + } + + _current = (char)current; + if (_current == '\n') + ++_lineNumber; + return true; + } + + public char Peek() + { + if (_peek.HasValue) + return _peek.Value; + + var current = _xmlSource.Read(); + if (current < 0) + return '\0'; + _peek = (char)current; + return _peek.Value; + } + + void IEnumerator.Reset() + { + // NOSONAR: Nothing to reset + } + + void IDisposable.Dispose() + { + // NOSONAR: Nothing to dispose + } + } + } +} diff --git a/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs index a422e71378..5d6f81ac58 100644 --- a/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs @@ -97,7 +97,7 @@ protected override string Transform(string text) { if (CDataEncode) { - return XmlHelper.WrapInCData(text); + return XmlHelper.EscapeCData(text); } if (XmlEncode) diff --git a/src/NLog/SetupBuilderExtensions.cs b/src/NLog/SetupBuilderExtensions.cs index e288e52b49..6875025945 100644 --- a/src/NLog/SetupBuilderExtensions.cs +++ b/src/NLog/SetupBuilderExtensions.cs @@ -35,6 +35,7 @@ namespace NLog { using System; using System.Collections.Generic; + using System.IO; using System.Linq; using System.Runtime.CompilerServices; using NLog.Common; @@ -207,24 +208,24 @@ public static ISetupBuilder LoadConfigurationFromAssemblyResource(this ISetupBui var nlogConfigStream = applicationAssembly.GetManifestResourceStream(resourcePaths[0]); if (nlogConfigStream?.Length > 0) { - NLog.Common.InternalLogger.Info("Loading NLog XML config from assembly embedded resource '{0}'", resourceName); - using (var xmlReader = System.Xml.XmlReader.Create(nlogConfigStream)) + InternalLogger.Info("Loading NLog XML config from assembly embedded resource '{0}'", resourceName); + using (var streamReader = new StreamReader(nlogConfigStream)) { - setupBuilder.LoadConfiguration(new XmlLoggingConfiguration(xmlReader, null, setupBuilder.LogFactory)); + setupBuilder.LoadConfiguration(new XmlLoggingConfiguration(streamReader, null, setupBuilder.LogFactory)); } } else { - NLog.Common.InternalLogger.Debug("No NLog config loaded. Empty Embedded resource '{0}' found in assembly: {1}", resourceName, applicationAssembly.FullName); + InternalLogger.Debug("No NLog config loaded. Empty Embedded resource '{0}' found in assembly: {1}", resourceName, applicationAssembly.FullName); } } else if (resourcePaths.Count == 0) { - NLog.Common.InternalLogger.Debug("No NLog config loaded. No matching embedded resource '{0}' found in assembly: {1}", resourceName, applicationAssembly.FullName); + InternalLogger.Debug("No NLog config loaded. No matching embedded resource '{0}' found in assembly: {1}", resourceName, applicationAssembly.FullName); } else { - NLog.Common.InternalLogger.Error("No NLog config loaded. Multiple matching embedded resource '{0}' found in assembly: {1}", resourceName, applicationAssembly.FullName); + InternalLogger.Error("No NLog config loaded. Multiple matching embedded resource '{0}' found in assembly: {1}", resourceName, applicationAssembly.FullName); } return setupBuilder; } diff --git a/tests/NLog.UnitTests/ApiTests.cs b/tests/NLog.UnitTests/ApiTests.cs index 445da11ad5..6f197f1155 100644 --- a/tests/NLog.UnitTests/ApiTests.cs +++ b/tests/NLog.UnitTests/ApiTests.cs @@ -516,6 +516,7 @@ public void ShouldNotHaveExplicitStaticConstructors() "NLog.Internal.TargetWithFilterChain", "NLog.Internal.UrlHelper", "NLog.Internal.XmlHelper", + "NLog.Internal.XmlParser", "NLog.Config.ConfigurationItemFactory", "NLog.Config.InstallationContext", "NLog.Config.LoggingRuleLevelFilter", diff --git a/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs b/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs index 77d9142c4f..a047dcc980 100644 --- a/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs +++ b/tests/NLog.UnitTests/Config/LogFactorySetupTests.cs @@ -706,7 +706,7 @@ public void SetupBuilderLoadConfigurationFromFileTest() { // Arrange var xmlFile = new System.IO.StringReader(""); - var appEnv = new Mocks.AppEnvironmentMock(f => true, f => System.Xml.XmlReader.Create(xmlFile)); + var appEnv = new Mocks.AppEnvironmentMock(f => true, f => xmlFile); var configLoader = new LoggingConfigurationFileLoader(appEnv); var logFactory = new LogFactory(configLoader); @@ -736,7 +736,7 @@ public void SetupBuilderLoadConfigurationFromFileMissingButRequiredTest_Integrat public void SetupBuilderLoadConfigurationFromFileMissingTest() { // Arrange - var appEnv = new Mocks.AppEnvironmentMock(f => false, f => null); + var appEnv = new Mocks.AppEnvironmentMock(f => false); var configLoader = new LoggingConfigurationFileLoader(appEnv); var logFactory = new LogFactory(configLoader); @@ -752,7 +752,7 @@ public void SetupBuilderLoadNLogConfigFromFileNotExistsTest() { // Arrange var xmlFile = new System.IO.StringReader(""); - var appEnv = new Mocks.AppEnvironmentMock(f => false, f => System.Xml.XmlReader.Create(xmlFile)); + var appEnv = new Mocks.AppEnvironmentMock(f => false); var configLoader = new LoggingConfigurationFileLoader(appEnv); var logFactory = new LogFactory(configLoader); @@ -768,7 +768,7 @@ public void SetupBuilderLoadConfigurationFromFileOptionalFalseTest() { // Arrange var xmlFile = new System.IO.StringReader(""); - var appEnv = new Mocks.AppEnvironmentMock(f => false, f => System.Xml.XmlReader.Create(xmlFile)); + var appEnv = new Mocks.AppEnvironmentMock(f => false); var configLoader = new LoggingConfigurationFileLoader(appEnv); var logFactory = new LogFactory(configLoader); @@ -794,7 +794,7 @@ public void SetupBuilderLoadConfigurationFromXmlPatchTest() { // Arrange var xmlFile = new System.IO.StringReader(""); - var appEnv = new Mocks.AppEnvironmentMock(f => true, f => System.Xml.XmlReader.Create(xmlFile)); + var appEnv = new Mocks.AppEnvironmentMock(f => true); var configLoader = new LoggingConfigurationFileLoader(appEnv); var logFactory = new LogFactory(configLoader); diff --git a/tests/NLog.UnitTests/Config/ReloadTests.cs b/tests/NLog.UnitTests/Config/ReloadTests.cs index 6980068a30..afbb3175ad 100644 --- a/tests/NLog.UnitTests/Config/ReloadTests.cs +++ b/tests/NLog.UnitTests/Config/ReloadTests.cs @@ -679,8 +679,8 @@ public void TestReloadingInvalidConfiguration() "; - var invalidXmlConfig = @" - + var invalidXmlConfig = @" + "; string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); diff --git a/tests/NLog.UnitTests/Config/XmlConfigTests.cs b/tests/NLog.UnitTests/Config/XmlConfigTests.cs index bf281be813..681c682651 100644 --- a/tests/NLog.UnitTests/Config/XmlConfigTests.cs +++ b/tests/NLog.UnitTests/Config/XmlConfigTests.cs @@ -246,7 +246,7 @@ public void XmlConfig_ParseFilter_WithoutAttributes() // Arrange var xml = @" - + diff --git a/tests/NLog.UnitTests/ConfigFileLocatorTests.cs b/tests/NLog.UnitTests/ConfigFileLocatorTests.cs index 1ff4e2dbfa..ec82ba0201 100644 --- a/tests/NLog.UnitTests/ConfigFileLocatorTests.cs +++ b/tests/NLog.UnitTests/ConfigFileLocatorTests.cs @@ -113,7 +113,7 @@ public void ResetCandidateConfigTest() public void GetConfigFile_absolutePath_loads(string filename, string accepts, string expected, string baseDir) { // Arrange - var appEnvMock = new AppEnvironmentMock(f => f == accepts, f => System.Xml.XmlReader.Create(new StringReader(@""))) { AppDomainBaseDirectory = baseDir }; + var appEnvMock = new AppEnvironmentMock(f => f == accepts, f => new StringReader(@"")) { AppDomainBaseDirectory = baseDir }; var fileLoader = new LoggingConfigurationFileLoader(appEnvMock); var logFactory = new LogFactory(fileLoader); @@ -145,7 +145,7 @@ public static IEnumerable GetConfigFile_absolutePath_loads_testData() public void LoadConfigFile_EmptyEnvironment_UseCurrentDirectory() { // Arrange - var appEnvMock = new AppEnvironmentMock(f => true, f => null); + var appEnvMock = new AppEnvironmentMock(f => true); var fileLoader = new LoggingConfigurationFileLoader(appEnvMock); // Act @@ -163,7 +163,7 @@ public void LoadConfigFile_NetCoreUnpublished_UseEntryDirectory() { // Arrange var tmpDir = Path.GetTempPath(); - var appEnvMock = new AppEnvironmentMock(f => true, f => null) + var appEnvMock = new AppEnvironmentMock(f => true) { AppDomainBaseDirectory = Path.Combine(tmpDir, "BaseDir"), #if NETFRAMEWORK @@ -190,7 +190,7 @@ public void LoadConfigFile_NetCorePublished_UseBaseDirectory() { // Arrange var tmpDir = Path.GetTempPath(); - var appEnvMock = new AppEnvironmentMock(f => true, f => null) + var appEnvMock = new AppEnvironmentMock(f => true) { AppDomainBaseDirectory = Path.Combine(tmpDir, "BaseDir"), #if NETFRAMEWORK @@ -218,7 +218,7 @@ public void LoadConfigFile_NetCorePublished_UseProcessDirectory() { // Arrange var tmpDir = Path.GetTempPath(); - var appEnvMock = new AppEnvironmentMock(f => true, f => null) + var appEnvMock = new AppEnvironmentMock(f => true) { AppDomainBaseDirectory = Path.Combine(tmpDir, "BaseDir"), #if NETFRAMEWORK @@ -245,7 +245,7 @@ public void LoadConfigFile_NetCoreSingleFilePublish_IgnoreTempDirectory() { // Arrange var tmpDir = Path.GetTempPath(); - var appEnvMock = new AppEnvironmentMock(f => true, f => null) + var appEnvMock = new AppEnvironmentMock(f => true) { AppDomainBaseDirectory = Path.Combine(tmpDir, "BaseDir"), #if NETFRAMEWORK @@ -276,7 +276,7 @@ public void LoadConfigFile_NetCoreSingleFilePublish_IgnoreTmpDirectory() { // Arrange var tmpDir = "/var/tmp/"; - var appEnvMock = new AppEnvironmentMock(f => true, f => null) + var appEnvMock = new AppEnvironmentMock(f => true) { AppDomainBaseDirectory = Path.Combine(tmpDir, "BaseDir"), #if NETFRAMEWORK @@ -317,7 +317,7 @@ public void ValueWithVariableMustNotCauseInfiniteRecursion() "; // Arrange - var appEnvMock = new AppEnvironmentMock(f => true, f => throw new NLogConfigurationException("Never allow loading config")); + var appEnvMock = new AppEnvironmentMock(f => true, f => throw new NLogConfigurationException("Never allow loading config file")); var fileLoader = new LoggingConfigurationFileLoader(appEnvMock); var logFactory = new LogFactory(fileLoader).Setup().LoadConfigurationFromXml(nlogConfigXml).LogFactory; diff --git a/tests/NLog.UnitTests/Internal/XmlParserTests.cs b/tests/NLog.UnitTests/Internal/XmlParserTests.cs new file mode 100644 index 0000000000..d3086d17c8 --- /dev/null +++ b/tests/NLog.UnitTests/Internal/XmlParserTests.cs @@ -0,0 +1,332 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.UnitTests.Internal +{ + using System.Linq; + using NLog.Config; + using NLog.Internal; + using Xunit; + + public class XmlParserTests + { + [Fact] + public void XmlConvertIsXmlCharTest() + { + for (char ch = '\0'; ch < char.MaxValue; ++ch) + { + var expected = System.Xml.XmlConvert.IsXmlChar(ch); + var actual = XmlHelper.XmlConvertIsXmlChar(ch); + if (expected != actual) + Assert.True(expected == actual, $"{ch} ({(int)ch})"); + } + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + [InlineData("\n")] + [InlineData("\t")] + [InlineData(">")] + [InlineData("<")] + [InlineData(" >")] + [InlineData(" <")] + [InlineData(" > ")] + [InlineData(" < ")] + [InlineData(" <> ")] + [InlineData(" < > ")] + [InlineData(" ")] + [InlineData(" < /> ")] + [InlineData(" <\n> ")] + [InlineData(" ")] + [InlineData(" ")] + [InlineData(" ")] + [InlineData(" ")] + [InlineData(" ")] + [InlineData(" ")] + [InlineData(" ")] + [InlineData(" \n ")] + [InlineData(" ")] + [InlineData(" \n ")] + [InlineData(" ")] + [InlineData(" \n ")] + [InlineData("n")] + [InlineData(" n")] + [InlineData(" n ")] + [InlineData("nlog")] + [InlineData("")] + [InlineData("\n")] + [InlineData("")] + [InlineData("\n")] + [InlineData("")] + [InlineData("\n")] + [InlineData("")] + [InlineData("\n")] + [InlineData("")] + [InlineData("\nnl")] + [InlineData("\nnl>g\n")] + [InlineData("")] + [InlineData("\n")] + [InlineData("")] + [InlineData("\n")] + [InlineData("")] + [InlineData("\n\n")] + [InlineData("")] + [InlineData("\n\n")] + [InlineData("")] + [InlineData("\n\n")] + [InlineData("")] + [InlineData("\n\n")] + [InlineData(" nlog")] + [InlineData("\n\nnlog")] + [InlineData("nlog")] + [InlineData("")] + [InlineData(">")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("Exceptions='true' />")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData(" Z;")] + [InlineData("")] + [InlineData("Z;")] + [InlineData("")] + [InlineData("&quop;")] + [InlineData(""")] + public void XmlParse_InvalidDocument(string xmlSource) + { + Assert.Throws(() => new XmlParser(xmlSource).LoadDocument(out var _)); + } + + [Theory] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData("")] + [InlineData(" ")] + [InlineData(" ")] + [InlineData("\n\n\n\n")] + [InlineData("\n\n\n\n")] + [InlineData("")] + [InlineData("")] + [InlineData(" ")] + [InlineData(" ")] + [InlineData("\n\n\n\n")] + [InlineData("\n\n\n\n")] + [InlineData(" ")] + [InlineData(" ")] + [InlineData("\n\n")] + [InlineData("\n\n")] + [InlineData("")] + [InlineData("\n")] + public void XmlParse_EmptyDocument(string xmlSource) + { + var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); + Assert.NotNull(xmlDocument); + Assert.Equal("nlog", xmlDocument.Name.ToLower()); + Assert.Null(xmlDocument.Children); + Assert.Null(xmlDocument.Attributes); + } + + [Theory] + [InlineData(@"")] + [InlineData(@"")] + [InlineData("")] + [InlineData(@"")] + [InlineData(@"")] + [InlineData("")] + [InlineData(@"")] + [InlineData(@"")] + [InlineData(@"")] + [InlineData("")] + [InlineData(@"")] + [InlineData("")] + [InlineData("")] + public void XmlParse_Attributes(string xmlSource) + { + var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); + Assert.NotNull(xmlDocument); + Assert.Equal("nlog", xmlDocument.Name); + Assert.Null(xmlDocument.Children); + Assert.Single(xmlDocument.Attributes); + Assert.Equal("throwExceptions", xmlDocument.Attributes[0].Key); + Assert.Equal("false", xmlDocument.Attributes[0].Value); + } + + [Theory] + [InlineData(@"")] + [InlineData(@"")] + [InlineData(@"")] + [InlineData(@"")] + public void XmlParse_Attributes_Multiple(string xmlSource) + { + var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); + Assert.NotNull(xmlDocument); + Assert.Equal("nlog", xmlDocument.Name); + Assert.Null(xmlDocument.Children); + Assert.Equal(2, xmlDocument.Attributes.Count); + Assert.Equal("throwExceptions", xmlDocument.Attributes[0].Key); + Assert.Equal("false", xmlDocument.Attributes[0].Value); + Assert.Equal("internalLogLevel", xmlDocument.Attributes[1].Key); + Assert.Equal("Debug", xmlDocument.Attributes[1].Value); + } + + [Theory] + [InlineData("", "")] + [InlineData("", ".\\logfile.txt")] + [InlineData("", "C:\\logfile.txt")] + [InlineData("", "./logfile.txt")] + [InlineData("", "./%HOSTNAME%.txt")] + [InlineData("", "http://example.com")] + [InlineData("", " < > ? \" & ")] + [InlineData("", " < > ? \" &")] + [InlineData("", " < > ? \" & ")] + [InlineData("", " < > ? \" &")] + [InlineData("", " < > ? \" & ")] + [InlineData("", " < > ? \" &")] + [InlineData("", " < > ? \" & ")] + [InlineData("", " < > ? \" &")] + public void XmlParse_Attributes_Tokens(string xmlSource, string value) + { + var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); + Assert.NotNull(xmlDocument); + Assert.Equal("nlog", xmlDocument.Name); + Assert.Null(xmlDocument.Children); + Assert.Single(xmlDocument.Attributes); + Assert.Equal("internalLogFile", xmlDocument.Attributes[0].Key); + Assert.Equal(value, xmlDocument.Attributes[0].Value); + } + + [Theory] + [InlineData("", null)] + [InlineData(" ", null)] + [InlineData("\n", null)] + [InlineData("\n\n", null)] + [InlineData("< > ? " &", "< > ? \" &")] + [InlineData(" < > ? " & ", "< > ? \" &")] + [InlineData("\n< > ? " &\n", "< > ? \" &")] + [InlineData("< > ? " &", "< > ? \" &")] + [InlineData(" < > ? " & ", "< > ? \" &")] + [InlineData("\n< > ? " &\n", "< > ? \" &")] + [InlineData("< > ? " &", "< > ? \" &")] + [InlineData(" < > ? " & ", "< > ? \" &")] + [InlineData("\n< > ? " &\n", "< > ? \" &")] + [InlineData("", "\n\n")] + [InlineData(" ", "\n\n")] + [InlineData("\n\n", "\n\n")] + [InlineData("\n]]>\n", "")] + [InlineData("\n\n]]>\n", "")] + [InlineData("A]]>", "")] + [InlineData("]]]]>", "")] + public void XmlParse_InnerText_Tokens(string xmlSource, string value) + { + var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); + Assert.NotNull(xmlDocument); + Assert.Equal("nlog", xmlDocument.Name); + Assert.Null(xmlDocument.Children); + Assert.Null(xmlDocument.Attributes); + Assert.Equal(value, xmlDocument.InnerText); + } + + [Theory] + [InlineData("")] + [InlineData("\n\n")] + [InlineData("abc${message}")] + [InlineData("\n\nabc\n${message}\n\n")] + public void XmlParse_Children(string xmlSource) + { + var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); + Assert.NotNull(xmlDocument); + Assert.Equal("nlog", xmlDocument.Name); + Assert.Single(xmlDocument.Children); + Assert.Equal("variable", xmlDocument.Children[0].Name); + } + + [Theory] + [InlineData("")] + [InlineData("\n\n\n")] + [InlineData("${message}")] + [InlineData("\n\n\n${message}\n\n")] + [InlineData("abc${message}123${message}")] + [InlineData("\n\nabc\n${message}\n\n\n123\n${message}\n\n")] + public void XmlParse_Children_Multiple(string xmlSource) + { + var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); + Assert.NotNull(xmlDocument); + Assert.Equal("nlog", xmlDocument.Name); + Assert.NotNull(xmlDocument.Children); + Assert.Equal(2, xmlDocument.Children.Count); + Assert.Equal("variable", xmlDocument.Children[0].Name); + Assert.Equal("variable", xmlDocument.Children[1].Name); + } + + [Fact] + public void XmlParse_DeeplyNestedXml_DoesNotThrowStackOverflowException() + { + string deeplyNestedXml = "" + string.Join("", Enumerable.Repeat("", 10000).ToArray()) + string.Join("", Enumerable.Repeat("", 10000).ToArray()) + ""; + var xmlDocument = new XmlParser(deeplyNestedXml).LoadDocument(out var _); + Assert.NotNull(xmlDocument); + Assert.Equal("root", xmlDocument.Name); + Assert.Single(xmlDocument.Children); + } + } +} diff --git a/tests/NLog.UnitTests/LayoutRenderers/BaseDirTests.cs b/tests/NLog.UnitTests/LayoutRenderers/BaseDirTests.cs index edd10f76d5..2b09d74fb1 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/BaseDirTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/BaseDirTests.cs @@ -88,7 +88,7 @@ public void BaseDirDirFileCombineTest() public void InjectBaseDirAndCheckConfigPathsTest() { string fakeBaseDir = @"y:\root\"; - var appEnvironment = new Mocks.AppEnvironmentMock(null, null); + var appEnvironment = new Mocks.AppEnvironmentMock(null); appEnvironment.AppDomainBaseDirectory = fakeBaseDir; var baseLayoutRenderer = new NLog.LayoutRenderers.BaseDirLayoutRenderer(appEnvironment); @@ -102,7 +102,7 @@ public void BaseDir_FixTempDir_ChoosesProcessDir() var tempDir = System.IO.Path.GetTempPath(); var processPath = CurrentProcessPath; - var appEnvironment = new Mocks.AppEnvironmentMock(null, null); + var appEnvironment = new Mocks.AppEnvironmentMock(null); appEnvironment.AppDomainBaseDirectory = tempDir; appEnvironment.UserTempFilePath = tempDir; appEnvironment.CurrentProcessFilePath = processPath; @@ -120,7 +120,7 @@ public void BaseDir_FixTempDir_ChoosesProcessDir() [Fact] public void BaseDir_FixFilePathForNet9_WhenLongUNC() { - var appEnvironment = new Mocks.AppEnvironmentMock(null, null); + var appEnvironment = new Mocks.AppEnvironmentMock(null); appEnvironment.AppDomainBaseDirectory = @"\\?\UNC\major\tom\groundcontrol\bin\Development\net9.0\NLog.config"; var baseLayoutRenderer = new NLog.LayoutRenderers.BaseDirLayoutRenderer(appEnvironment); diff --git a/tests/NLog.UnitTests/LayoutRenderers/MdlcLayoutRendererTests.cs b/tests/NLog.UnitTests/LayoutRenderers/MdlcLayoutRendererTests.cs index 7a4426f481..6fd4079054 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/MdlcLayoutRendererTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/MdlcLayoutRendererTests.cs @@ -54,8 +54,7 @@ public MdlcLayoutRendererTests() "; - var element = XElement.Parse(configXml); - var config = new XmlLoggingConfiguration(element.CreateReader(), null); + var config = XmlLoggingConfiguration.CreateFromXmlString(configXml); LogManager.Configuration = config; _target = LogManager.Configuration.FindTargetByName("debug") as DebugTarget; diff --git a/tests/NLog.UnitTests/Mocks/AppEnvironmentMock.cs b/tests/NLog.UnitTests/Mocks/AppEnvironmentMock.cs index 755db94610..cc4a969fc0 100644 --- a/tests/NLog.UnitTests/Mocks/AppEnvironmentMock.cs +++ b/tests/NLog.UnitTests/Mocks/AppEnvironmentMock.cs @@ -33,6 +33,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Xml; using NLog.Internal.Fakeables; @@ -41,12 +42,12 @@ namespace NLog.UnitTests.Mocks internal sealed class AppEnvironmentMock : IAppEnvironment { private readonly Func _fileexists; - private readonly Func _fileload; + private readonly Func _loadTextFile; - public AppEnvironmentMock(Func fileExists, Func fileLoad) + public AppEnvironmentMock(Func fileExists = null, Func loadTextFile = null) { - _fileexists = fileExists; - _fileload = fileLoad; + _fileexists = fileExists != null ? fileExists : (f) => throw new NotSupportedException("FileSystem unavailable"); + _loadTextFile = loadTextFile != null ? loadTextFile : (f) => throw new NotSupportedException("FileSystem unavailable"); } public int AppDomainId { get; set; } @@ -81,9 +82,9 @@ public bool FileExists(string path) return _fileexists(path); } - public XmlReader LoadXmlFile(string path) + public TextReader LoadTextFile(string path) { - return _fileload(path); + return _loadTextFile(path); } public void SignalShutdown() From 80fddc80a64f08282192c35d2f545d2f13cb1e0e Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 27 Apr 2025 12:19:05 +0200 Subject: [PATCH 099/224] ConfigurationItemFactory - Restored RegisterItemsFromAssembly for NLog.Web (#5798) --- src/NLog/Config/AssemblyExtensionLoader.cs | 14 ++++++------ src/NLog/Config/ConfigurationItemFactory.cs | 25 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/NLog/Config/AssemblyExtensionLoader.cs b/src/NLog/Config/AssemblyExtensionLoader.cs index c08fa1bd46..f86e6301bc 100644 --- a/src/NLog/Config/AssemblyExtensionLoader.cs +++ b/src/NLog/Config/AssemblyExtensionLoader.cs @@ -395,20 +395,20 @@ private static bool IncludeAsHiddenAssembly(string assemblyFullName) internal static IEnumerable> GetAutoLoadingFileLocations() { - var nlogAssembly = typeof(LogFactory).Assembly; - var nlogAssemblyLocation = PathHelpers.TrimDirectorySeparators(System.IO.Path.GetDirectoryName(AssemblyHelpers.GetAssemblyFileLocation(nlogAssembly))); - InternalLogger.Debug("Auto loading based on NLog-Assembly found location: {0}", nlogAssemblyLocation); - if (!string.IsNullOrEmpty(nlogAssemblyLocation)) - yield return new KeyValuePair(nlogAssemblyLocation, nameof(nlogAssemblyLocation)); + var nlogAssemblyLocation = AssemblyHelpers.GetAssemblyFileLocation(typeof(LogFactory).Assembly); + var nlogAssemblyDirectory = string.IsNullOrEmpty(nlogAssemblyLocation) ? nlogAssemblyLocation : PathHelpers.TrimDirectorySeparators(System.IO.Path.GetDirectoryName(nlogAssemblyLocation)); + InternalLogger.Debug("Auto loading based on NLog-Assembly found location: {0}", nlogAssemblyDirectory); + if (!string.IsNullOrEmpty(nlogAssemblyDirectory)) + yield return new KeyValuePair(nlogAssemblyDirectory, nameof(nlogAssemblyLocation)); var entryAssemblyLocation = PathHelpers.TrimDirectorySeparators(LogFactory.DefaultAppEnvironment.EntryAssemblyLocation); InternalLogger.Debug("Auto loading based on GetEntryAssembly-Assembly found location: {0}", entryAssemblyLocation); - if (!string.IsNullOrEmpty(entryAssemblyLocation) && !string.Equals(entryAssemblyLocation, nlogAssemblyLocation, StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrEmpty(entryAssemblyLocation) && !string.Equals(entryAssemblyLocation, nlogAssemblyDirectory, StringComparison.OrdinalIgnoreCase)) yield return new KeyValuePair(entryAssemblyLocation, nameof(entryAssemblyLocation)); var baseDirectory = PathHelpers.TrimDirectorySeparators(LogFactory.DefaultAppEnvironment.AppDomainBaseDirectory); InternalLogger.Debug("Auto loading based on AppDomain-BaseDirectory found location: {0}", baseDirectory); - if (!string.IsNullOrEmpty(baseDirectory) && !string.Equals(baseDirectory, nlogAssemblyLocation, StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrEmpty(baseDirectory) && !string.Equals(baseDirectory, nlogAssemblyDirectory, StringComparison.OrdinalIgnoreCase) && !string.Equals(baseDirectory, entryAssemblyLocation, StringComparison.OrdinalIgnoreCase)) yield return new KeyValuePair(baseDirectory, nameof(baseDirectory)); } diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index 648b5cf9c1..86dbffd358 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -290,6 +290,31 @@ public bool? ParseMessageTemplates set => _serviceRepository.ParseMessageTemplates(value); } + /// + /// Obsolete since dynamic assembly loading is not compatible with publish as trimmed application. + /// Registers named items from the assembly. + /// + /// The assembly. + [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] + [EditorBrowsable(EditorBrowsableState.Never)] + public void RegisterItemsFromAssembly(Assembly assembly) + { + AssemblyLoader.LoadAssembly(this, assembly, string.Empty); + } + + /// + /// Obsolete since dynamic assembly loading is not compatible with publish as trimmed application. + /// Registers named items from the assembly. + /// + /// The assembly. + /// Item name prefix. + [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] + [EditorBrowsable(EditorBrowsableState.Never)] + public void RegisterItemsFromAssembly(Assembly assembly, string itemNamePrefix) + { + AssemblyLoader.LoadAssembly(this, assembly, itemNamePrefix); + } + /// /// Clears the contents of all factories. /// From 4629c7e88519a8c8d50c7ac4b155db56caa7fe61 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 28 Apr 2025 18:35:22 +0200 Subject: [PATCH 100/224] FileTarget - Fixed legacy ArchiveFileName to allow File.Move with dynamic-archive-logic (#5799) --- .../LegacyArchiveFileNameHandler.cs | 13 +++-- .../NLog.UnitTests/Targets/FileTargetTests.cs | 53 ++++++++++++++++++- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs b/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs index 08d93de1c1..af0e18dee4 100644 --- a/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs +++ b/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs @@ -166,7 +166,7 @@ private bool ArchiveOldFile(string archiveFileName, FileInfo newFileInfo, LogEve if (previousFileLastModified.HasValue && (previousFileLastModified > fileLastWriteTime || fileLastWriteTime >= firstLogEvent.TimeStamp)) fileLastWriteTime = previousFileLastModified.Value; - var archiveNextSequenceNo = ResolveNextArchiveSequenceNo(archiveFileName, fileLastWriteTime); + var archiveNextSequenceNo = ResolveNextArchiveSequenceNo(archiveFileName, newFileInfo, fileLastWriteTime); string archiveFullPath = BuildArchiveFilePath(archiveFileName, archiveNextSequenceNo, fileLastWriteTime); if (!File.Exists(archiveFullPath)) @@ -239,7 +239,7 @@ private void ArchiveFileAppendExisting(string newFilePath, string archiveFilePat } } - private int ResolveNextArchiveSequenceNo(string archiveFileName, DateTime fileLastWriteTime) + private int ResolveNextArchiveSequenceNo(string archiveFileName, FileInfo newFileInfo, DateTime fileLastWriteTime) { // Archive operation triggered, how to resolve the next archive-sequence-number ? // - Old version was able to "parse" the file-names of the archive-folder and "guess" the next sequence number @@ -251,7 +251,11 @@ private int ResolveNextArchiveSequenceNo(string archiveFileName, DateTime fileLa var directoryInfo = new DirectoryInfo(archiveDirectory); if (!directoryInfo.Exists) { - directoryInfo.Create(); + if (_fileTarget.CreateDirs) + { + InternalLogger.Debug("{0}: Creating archive directory: {1}", _fileTarget, archiveDirectory); + directoryInfo.Create(); + } } var archiveWildCardFileName = Path.GetFileName(archiveFilePath).Replace(int.MaxValue.ToString(), "*"); @@ -274,6 +278,9 @@ private int ResolveNextArchiveSequenceNo(string archiveFileName, DateTime fileLa if (sequenceNo.HasValue) return sequenceNo.Value + 1; + if (string.Equals(directoryInfo.FullName, newFileInfo.DirectoryName, StringComparison.OrdinalIgnoreCase) && string.Equals(Path.GetFileName(archiveFileName), newFileInfo.Name, StringComparison.OrdinalIgnoreCase)) + return 1; // Same folder archive + return 0; } } diff --git a/tests/NLog.UnitTests/Targets/FileTargetTests.cs b/tests/NLog.UnitTests/Targets/FileTargetTests.cs index 5991e86158..58f58005c1 100644 --- a/tests/NLog.UnitTests/Targets/FileTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/FileTargetTests.cs @@ -2692,7 +2692,7 @@ public void FileTarget_LogAndArchiveFilesWithSameName_ShouldArchive() } [Fact] - public void FileTarget_ArchiveAboveSize_RollWhenFull() + public void FileTarget_ArchiveAboveSize_NewStyle_RollWhenFull() { var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "Application"); @@ -2737,6 +2737,57 @@ public void FileTarget_ArchiveAboveSize_RollWhenFull() } } + [Fact] + public void FileTarget_ArchiveAboveSize_OldStyle_RollWhenFull() + { + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFile = Path.Combine(tempDir, "${shortdate}"); + var tempDirectory = new DirectoryInfo(tempDir); + var maxArchiveFiles = 2; + + try + { + var fileTarget = new FileTarget + { + FileName = logFile + ".log", + ArchiveFileName = logFile + ".log", + ArchiveSuffixFormat = "_{0:00}", + Layout = "${message}", + LineEnding = LineEndingMode.LF, + ArchiveAboveSize = 7, + MaxArchiveFiles = maxArchiveFiles, + }; + + LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); + + logger.Debug("aaaa"); + logger.Debug("bbbb"); // Not roll (new style so all agree when rolling) + logger.Debug("cccc"); // Roll + logger.Debug("dddd"); // Not roll (new style so all agree when rolling) + logger.Debug("eeee"); // Roll + logger.Debug("ffff"); // Not roll (new style so all agree when rolling) + logger.Debug("gggg"); // Roll + + LogManager.Configuration = null; // Flush + + var logFileName = SimpleLayout.Evaluate(logFile); + + Assert.True(File.Exists(logFileName + ".log")); + Assert.False(File.Exists(logFileName + "_01.log")); + AssertFileContents(logFileName + ".log", "gggg\n", Encoding.UTF8); + AssertFileContents(logFileName + "_02.log", "cccc\ndddd\n", Encoding.UTF8); + AssertFileContents(logFileName + "_03.log", "eeee\nffff\n", Encoding.UTF8); + Assert.Equal(maxArchiveFiles + 1, tempDirectory.GetFiles().Length); + } + finally + { + if (tempDirectory.Exists) + { + tempDirectory.Delete(true); + } + } + } + [Fact] public void FileTarget_Handle_Other_Files_That_Match_Archive_Format() { From 43e71edcb4f97a18923886d4d79e715f7a168eea Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 29 Apr 2025 20:20:56 +0200 Subject: [PATCH 101/224] NLog 6.0 preview 1 with strong-version 6.0.0.0 (#5797) --- CHANGELOG.md | 32 ++++++++++++++ appveyor.yml | 6 +-- build.ps1 | 4 +- src/NLog.Database/NLog.Database.csproj | 2 +- .../NLog.OutputDebugString.csproj | 2 +- src/NLog.RegEx/NLog.RegEx.csproj | 2 +- .../NLog.Targets.AtomicFile.csproj | 2 +- .../NLog.Targets.ConcurrentFile.csproj | 2 +- .../NLog.Targets.GZipFile.csproj | 2 +- .../NLog.Targets.Mail.csproj | 2 +- .../NLog.Targets.Network.csproj | 2 +- .../NLog.Targets.Trace.csproj | 2 +- .../NLog.Targets.WebService.csproj | 2 +- .../NLog.WindowsEventLog.csproj | 2 +- .../NLog.WindowsRegistry.csproj | 2 +- src/NLog/Config/Factory.cs | 2 +- src/NLog/NLog.csproj | 44 +++++++++---------- src/NLog/Targets/TargetWithContext.cs | 18 ++++++++ 18 files changed, 89 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc0502d8f5..c4ab487680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,38 @@ Date format: (year/month/day) ## Change Log +### Version 6.0 Preview 1 (2025/04/27) + +**Major Changes** + +- Support AOT builds without build warnings. +- New FileTarget without ConcurrentWrites support, but still support KeepFileOpen true / false. +- Moved old FileTarget into the new nuget-package NLog.Targets.ConcurrentFile. +- Created new nuget-package NLog.Targets.AtomicFile that supports ConcurrentWrites for NET8 on both Windows / Linux. +- Created new nuget-package NLog.Targets.GZipFile that uses GZipStream for writing directly to compressed files. +- Moved MailTarget into the new nuget-package NLog.Targets.Mail. +- Moved NetworkTarget into the new nuget-package NLog.Targets.Network. +- New GelfTarget introduced for the new nuget-package NLog.Targets.Network. +- New SyslogTarget introduced for the new nuget-package NLog.Targets.Network. +- Moved TraceTarget and NLogTraceListener into the new nuget-package NLog.Targets.Trace. +- Moved WebServiceTarget into the new nuget-package NLog.Targets.WebService +- Removed dependency on System.Text.RegularExpressions and introduced new nuget-package NLog.RegEx. +- Removed dependency on System.Xml.XmlReader by implementing own internal basic XML-Parser. + +NLog v6.0 release notes: https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html + +List of all [NLog 6.0 Pull Requests](https://github.com/NLog/NLog/pulls?q=is%3Apr+is%3Amerged+milestone:%226.0%22) + +- [Breaking Changes](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22breaking%20change%22+is%3Amerged+milestone:%226.0%22) + +- [Breaking Behavior Changes](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22breaking%20behavior%20change%22+is%3Amerged+milestone:%226.0%22) + +- [Features](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22Feature%22+is%3Amerged+milestone:%226.0%22) + +- [Improvements](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22Enhancement%22+is%3Amerged+milestone:%226.0%22) + +- [Performance](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22Performance%22+is%3Amerged+milestone:%226.0%22) + ### Version 5.3.4 (2024/09/12) **Improvements** diff --git a/appveyor.yml b/appveyor.yml index 38091fcec0..052c96509c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 5.0.0-{build} # Only change for mayor versions (e.g. 6.0) +version: 6.0.0-{build} # Only change for mayor versions (e.g. 7.0) image: - Visual Studio 2022 - Ubuntu2004 @@ -37,12 +37,12 @@ for: deploy: - provider: NuGet api_key: - secure: ACKSV1ixxNpO+2k8KvNDy6hd9QmR8lkQmKn773ZIIeVpG0ywYUhY4j8LcyykVR1a + secure: f6oWebyOFLpuuo2PMd6xgoxwMq+JvXVUmPyBme89zS7UF0zcvLYPSKN/p6B/KaMs on: branch: master - provider: NuGet api_key: - secure: ACKSV1ixxNpO+2k8KvNDy6hd9QmR8lkQmKn773ZIIeVpG0ywYUhY4j8LcyykVR1a + secure: f6oWebyOFLpuuo2PMd6xgoxwMq+JvXVUmPyBme89zS7UF0zcvLYPSKN/p6B/KaMs on: branch: dev artifacts: diff --git a/build.ps1 b/build.ps1 index 6a3231e1d8..b8102a30a1 100644 --- a/build.ps1 +++ b/build.ps1 @@ -2,8 +2,8 @@ # creates NuGet package at \artifacts dotnet --version -$versionPrefix = "5.3.4" -$versionSuffix = "" +$versionPrefix = "6.0.0" +$versionSuffix = "preview1" $versionFile = $versionPrefix + "." + ${env:APPVEYOR_BUILD_NUMBER} $versionProduct = $versionPrefix; if (-Not $versionSuffix.Equals("")) diff --git a/src/NLog.Database/NLog.Database.csproj b/src/NLog.Database/NLog.Database.csproj index 38ffa6a4eb..049eb100ef 100644 --- a/src/NLog.Database/NLog.Database.csproj +++ b/src/NLog.Database/NLog.Database.csproj @@ -27,7 +27,7 @@ https://github.com/NLog/NLog.git true - 5.0.0.0 + 6.0.0.0 ..\NLog.snk true diff --git a/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj b/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj index 65566d94c0..4fa364f993 100644 --- a/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj +++ b/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj @@ -25,7 +25,7 @@ https://github.com/NLog/NLog.git true - 5.0.0.0 + 6.0.0.0 ..\NLog.snk true diff --git a/src/NLog.RegEx/NLog.RegEx.csproj b/src/NLog.RegEx/NLog.RegEx.csproj index c107c0f951..67cf230beb 100644 --- a/src/NLog.RegEx/NLog.RegEx.csproj +++ b/src/NLog.RegEx/NLog.RegEx.csproj @@ -25,7 +25,7 @@ https://github.com/NLog/NLog.git true - 5.0.0.0 + 6.0.0.0 ..\NLog.snk true diff --git a/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj index 10fa9efb29..19d3bfc66a 100644 --- a/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj +++ b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj @@ -24,7 +24,7 @@ https://github.com/NLog/NLog.git true - 5.0.0.0 + 6.0.0.0 ..\NLog.snk true diff --git a/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj b/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj index 4004a33995..ac9189378e 100644 --- a/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj +++ b/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj @@ -25,7 +25,7 @@ https://github.com/NLog/NLog.git true - 5.0.0.0 + 6.0.0.0 ..\NLog.snk true diff --git a/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj b/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj index ff2dcadbc1..785f8515cb 100644 --- a/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj +++ b/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj @@ -24,7 +24,7 @@ https://github.com/NLog/NLog.git true - 5.0.0.0 + 6.0.0.0 ..\NLog.snk true diff --git a/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj b/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj index f377a522ea..c3a6dc75d5 100644 --- a/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj +++ b/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj @@ -25,7 +25,7 @@ https://github.com/NLog/NLog.git true - 5.0.0.0 + 6.0.0.0 ..\NLog.snk true diff --git a/src/NLog.Targets.Network/NLog.Targets.Network.csproj b/src/NLog.Targets.Network/NLog.Targets.Network.csproj index 766c5c85dd..2a959766be 100644 --- a/src/NLog.Targets.Network/NLog.Targets.Network.csproj +++ b/src/NLog.Targets.Network/NLog.Targets.Network.csproj @@ -25,7 +25,7 @@ https://github.com/NLog/NLog.git true - 5.0.0.0 + 6.0.0.0 ..\NLog.snk true diff --git a/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj b/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj index de49f6feb9..89ab57c41c 100644 --- a/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj +++ b/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj @@ -25,7 +25,7 @@ https://github.com/NLog/NLog.git true - 5.0.0.0 + 6.0.0.0 ..\NLog.snk true diff --git a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj index bcc81bf0ef..1a9b37d6f8 100644 --- a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj +++ b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj @@ -25,7 +25,7 @@ https://github.com/NLog/NLog.git true - 5.0.0.0 + 6.0.0.0 ..\NLog.snk true diff --git a/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj b/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj index 8e19d52644..a8ea939549 100644 --- a/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj +++ b/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj @@ -25,7 +25,7 @@ https://github.com/NLog/NLog.git true - 5.0.0.0 + 6.0.0.0 ..\NLog.snk true diff --git a/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj b/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj index 84e42040a7..154e8acd2b 100644 --- a/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj +++ b/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj @@ -25,7 +25,7 @@ https://github.com/NLog/NLog.git true - 5.0.0.0 + 6.0.0.0 ..\NLog.snk true diff --git a/src/NLog/Config/Factory.cs b/src/NLog/Config/Factory.cs index 6cd347d839..1cb13c05fd 100644 --- a/src/NLog/Config/Factory.cs +++ b/src/NLog/Config/Factory.cs @@ -82,7 +82,7 @@ public void Initialize(Action itemRegistration) } } - public bool CheckTypeAliasExists(string typeAlias) => _items.ContainsKey(typeAlias); + public bool CheckTypeAliasExists(string typeAlias) => _items.ContainsKey(FactoryExtensions.NormalizeName(typeAlias)); /// /// Registers the type. diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index 483c8051e0..d2f819716f 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -10,7 +10,7 @@ NLog supports traditional logging, structured logging and the combination of bot Supported platforms: -- .NET 5, 6, 7 and 8 +- .NET 5, 6, 7, 8 and 9 - .NET Core 1, 2 and 3 - .NET Standard 1.3+ and 2.0+ - .NET Framework 3.5 - 4.8 @@ -27,27 +27,25 @@ For ASP.NET Core, check: https://www.nuget.org/packages/NLog.Web.AspNetCore Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ -ChangeLog: - -- Layout.FromMethod that supports typed Layout (#5572) (@smnsht) -- Layout.FromMethod that supports typed Layout (without boxing) (#5580) (@snakefoot) -- ScopeContextPropertyEnumerator - Optimize HasUniqueCollectionKeys (#5570) (@snakefoot) -- XmlLayout - Fixed bug in handling unsafe xml property names (#5571) (@snakefoot) -- FuncThreadAgnosticLayoutRenderer - Implement IRawValue (#5573) (@snakefoot) -- Introduced OnConfigurationAssigned to signal activation of LoggingConfiguration (#5577) (@snakefoot) -- Update copyright to 2024, and removed trailing white spaces in source code (#5578) (@snakefoot) -- Fixed various issues reported by EnableNETAnalyzers (#5585) (@snakefoot) -- NetworkTarget - Added SendTimeoutSeconds to assign TCP Socket SendTimeout (#5587) (@snakefoot) -- DateLayoutRenderer - Optimize for Round Trip ISO 8601 Date Format = o (#5588) (@snakefoot) -- LayoutRenderer - Changed Render-method to use StringBuilderPool (#5589) (@snakefoot) -- JsonLayout - Precalculate Json-Document delimiters upfront (#5600) (@snakefoot) -- JsonLayout - Refactor code to simplify rendering of scope properties (#5599) (@snakefoot) - -NLog v5.2 changes how to load extensions: https://nlog-project.org/2023/05/30/nlog-5-2-trim-warnings.html - -List of major changes in NLog 5.0: https://nlog-project.org/2022/05/16/nlog-5-0-finally-ready.html - -Full changelog: https://github.com/NLog/NLog/blob/master/CHANGELOG.md +NLog v6.0 Preview1 with the following major changes: + +- Support AOT builds without build warnings. +- New FileTarget without ConcurrentWrites support, but still support KeepFileOpen true / false. +- Moved old FileTarget into the new nuget-package NLog.Targets.ConcurrentFile. +- Created new nuget-package NLog.Targets.AtomicFile that supports ConcurrentWrites for NET8 on both Windows / Linux. +- Created new nuget-package NLog.Targets.GZipFile that uses GZipStream for writing directly to compressed files. +- Moved MailTarget into the new nuget-package NLog.Targets.Mail. +- Moved NetworkTarget into the new nuget-package NLog.Targets.Network. +- New GelfTarget introduced for the new nuget-package NLog.Targets.Network. +- New SyslogTarget introduced for the new nuget-package NLog.Targets.Network. +- Moved TraceTarget and NLogTraceListener into the new nuget-package NLog.Targets.Trace. +- Moved WebServiceTarget into the new nuget-package NLog.Targets.WebService +- Removed dependency on System.Text.RegularExpressions and introduced new nuget-package NLog.RegEx. +- Removed dependency on System.Xml.XmlReader by implementing own internal basic XML-Parser. + +NLog v6.0 release notes: https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html + +Full changelog: https://github.com/NLog/NLog/releases For all config options and platform support, check https://nlog-project.org/config/ @@ -59,7 +57,7 @@ For all config options and platform support, check https://nlog-project.org/conf https://github.com/NLog/NLog.git true - 5.0.0.0 + 6.0.0.0 ..\NLog.snk true diff --git a/src/NLog/Targets/TargetWithContext.cs b/src/NLog/Targets/TargetWithContext.cs index 448e6d9cd3..e32259445a 100644 --- a/src/NLog/Targets/TargetWithContext.cs +++ b/src/NLog/Targets/TargetWithContext.cs @@ -562,6 +562,24 @@ protected virtual bool SerializeItemValue(LogEventInfo logEvent, string name, ob return true; } + /// + /// Returns the captured snapshot of for the + /// + /// + /// Collection with NDLC context if any, else null + [Obsolete("Replaced by GetScopeContextNested. Marked obsolete on NLog 5.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + protected IList GetContextNdlc(LogEventInfo logEvent) => GetScopeContextNested(logEvent); + + /// + /// Returns the captured snapshot of for the + /// + /// + /// Dictionary with MDLC context if any, else null + [Obsolete("Replaced by GetScopeContextProperties. Marked obsolete on NLog 5.0")] + [EditorBrowsable(EditorBrowsableState.Never)] + protected IDictionary GetContextMdlc(LogEventInfo logEvent) => GetScopeContextProperties(logEvent); + [ThreadAgnostic] internal sealed class TargetWithContextLayout : Layout, IIncludeContext, IUsesStackTrace, IStringValueRenderer { From d2951d2746bf794ebdba6adc462709c0257bc1d5 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 3 May 2025 12:07:34 +0200 Subject: [PATCH 102/224] Fixed XmlConvertToString to have InvariantCulture (#5801) --- src/NLog/Internal/XmlHelper.cs | 2 +- src/NLog/LogFactory.cs | 2 +- tests/NLog.UnitTests/Config/ExtensionTests.cs | 34 +++++- tests/NLog.UnitTests/GetLoggerTests.cs | 9 +- tests/NLog.UnitTests/LoggerTests.cs | 107 +++++++++++------- 5 files changed, 103 insertions(+), 51 deletions(-) diff --git a/src/NLog/Internal/XmlHelper.cs b/src/NLog/Internal/XmlHelper.cs index c2a52635e3..d3fbb061a9 100644 --- a/src/NLog/Internal/XmlHelper.cs +++ b/src/NLog/Internal/XmlHelper.cs @@ -280,7 +280,7 @@ internal static string XmlConvertToString(DateTime value) value = new DateTime(value.Ticks, DateTimeKind.Utc); else value = value.ToUniversalTime(); - return value.ToString("yyyy-MM-ddTHH:mm:ss.FFFFFFFK"); + return value.ToString("yyyy-MM-ddTHH:mm:ss.FFFFFFFK", CultureInfo.InvariantCulture); } /// diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 5d6be8f295..3250613ae3 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -1010,7 +1010,7 @@ internal Logger CreateNewLogger(Type loggerType, Func loggerCreato { throw new NLogRuntimeException($"GetLogger / GetCurrentClassLogger with type '{loggerType}' could not create instance of NLog Logger"); } - else if (ThrowExceptions) + else if (ThrowExceptions || LogManager.ThrowExceptions) { throw new NLogRuntimeException($"GetLogger / GetCurrentClassLogger with type '{loggerType}' does not inherit from NLog Logger"); } diff --git a/tests/NLog.UnitTests/Config/ExtensionTests.cs b/tests/NLog.UnitTests/Config/ExtensionTests.cs index cd2fdaa0fa..43222e165e 100644 --- a/tests/NLog.UnitTests/Config/ExtensionTests.cs +++ b/tests/NLog.UnitTests/Config/ExtensionTests.cs @@ -63,6 +63,8 @@ private static string GetExtensionAssemblyFullPath() [Fact] public void ExtensionTest1() { + ConfigurationItemFactory.Default = null; + Assert.NotNull(typeof(FooLayout)); var configuration = new LogFactory().Setup().LoadConfigurationFromXml(@" @@ -108,6 +110,8 @@ public void ExtensionTest1() [Fact] public void ExtensionTest2() { + ConfigurationItemFactory.Default = null; + var configuration = new LogFactory().Setup().LoadConfigurationFromXml(@" @@ -155,6 +159,8 @@ public void ExtensionTest2() [Fact] public void ExtensionWithPrefixLoadTwiceTest() { + ConfigurationItemFactory.Default = null; + var configuration = new LogFactory().Setup().SetupExtensions(ext => ext.RegisterAssembly(extensionAssemblyName1)) .LoadConfigurationFromXml(@" @@ -203,6 +209,8 @@ public void ExtensionWithPrefixLoadTwiceTest() [Fact] public void ExtensionWithPrefixTest() { + ConfigurationItemFactory.Default = null; + var configuration = new LogFactory().Setup().LoadConfigurationFromXml(@" @@ -246,6 +254,8 @@ public void ExtensionWithPrefixTest() [Fact] public void ExtensionTest4() { + ConfigurationItemFactory.Default = null; + Assert.NotNull(typeof(FooLayout)); var configuration = new LogFactory().Setup().LoadConfigurationFromXml(@" @@ -304,6 +314,8 @@ public void RegisterNamedTypeLessTest() [Fact] public void ExtensionTest_extensions_not_top_and_used() { + ConfigurationItemFactory.Default = null; + Assert.NotNull(typeof(FooLayout)); var configuration = new LogFactory().Setup().LoadConfigurationFromXml(@" @@ -351,6 +363,8 @@ public void ExtensionTest_extensions_not_top_and_used() [Fact] public void ExtensionShouldThrowNLogConfiguratonExceptionWhenRegisteringInvalidType() { + ConfigurationItemFactory.Default = null; + var configXml = @" @@ -375,6 +389,8 @@ public void ExtensionShouldThrowNLogConfiguratonExceptionWhenRegisteringInvalidA [Fact] public void ExtensionShouldThrowNLogConfiguratonExceptionWhenRegisteringInvalidAssemblyFile() { + ConfigurationItemFactory.Default = null; + var configXml = @" @@ -387,6 +403,8 @@ public void ExtensionShouldThrowNLogConfiguratonExceptionWhenRegisteringInvalidA [Fact] public void ExtensionShouldNotThrowWhenRegisteringInvalidTypeIfThrowConfigExceptionsFalse() { + ConfigurationItemFactory.Default = null; + var configXml = @" @@ -401,6 +419,8 @@ public void ExtensionShouldNotThrowWhenRegisteringInvalidTypeIfThrowConfigExcept [Fact] public void ExtensionShouldNotThrowWhenRegisteringInvalidAssemblyIfThrowConfigExceptionsFalse() { + ConfigurationItemFactory.Default = null; + var configXml = @" @@ -414,6 +434,8 @@ public void ExtensionShouldNotThrowWhenRegisteringInvalidAssemblyIfThrowConfigEx [Fact] public void ExtensionShouldNotThrowWhenRegisteringInvalidAssemblyFileIfThrowConfigExceptionsFalse() { + ConfigurationItemFactory.Default = null; + var configXml = @" @@ -427,6 +449,8 @@ public void ExtensionShouldNotThrowWhenRegisteringInvalidAssemblyFileIfThrowConf [Fact] public void CustomXmlNamespaceTest() { + ConfigurationItemFactory.Default = null; + var configuration = XmlLoggingConfiguration.CreateFromXmlString(@" @@ -442,6 +466,8 @@ public void CustomXmlNamespaceTest() [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] public void Extension_should_be_auto_loaded_when_following_NLog_dll_format() { + ConfigurationItemFactory.Default = null; + var fileLocations = AssemblyExtensionLoader.GetAutoLoadingFileLocations().ToArray(); Assert.NotEmpty(fileLocations); Assert.NotNull(fileLocations[0].Key); @@ -466,6 +492,8 @@ public void Extension_should_be_auto_loaded_when_following_NLog_dll_format() [Fact] public void ExtensionTypeWithAssemblyNameCanLoad() { + ConfigurationItemFactory.Default = null; + var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@" @@ -483,6 +511,8 @@ public void ExtensionTypeWithAssemblyNameCanLoad() [Fact] public void ImplicitConversionOperatorTest() { + ConfigurationItemFactory.Default = null; + var config = XmlLoggingConfiguration.CreateFromXmlString(@" @@ -507,6 +537,8 @@ public void LoadExtensionFromAppDomain() { try { + ConfigurationItemFactory.Default = null; + LoadManuallyLoadedExtensionDll(); InternalLogger.LogLevel = LogLevel.Trace; @@ -545,7 +577,7 @@ public void LoadExtensionFromAppDomain() public void FullyQualifiedExtensionTest() { // Arrange - + ConfigurationItemFactory.Default = null; LoadManuallyLoadedExtensionDll(); // Act diff --git a/tests/NLog.UnitTests/GetLoggerTests.cs b/tests/NLog.UnitTests/GetLoggerTests.cs index 1cbb2910c4..6131a8ba9e 100644 --- a/tests/NLog.UnitTests/GetLoggerTests.cs +++ b/tests/NLog.UnitTests/GetLoggerTests.cs @@ -41,14 +41,14 @@ public class GetLoggerTests : NLogTestBase [Fact] public void GetCurrentClassLoggerTest() { - var logger = LogManager.GetCurrentClassLogger(); + var logger = new LogFactory().GetCurrentClassLogger(); Assert.Equal("NLog.UnitTests.GetLoggerTests", logger.Name); } [Fact] public void GetCurrentClassLoggerLambdaTest() { - System.Linq.Expressions.Expression> sum = () => LogManager.GetCurrentClassLogger(); + System.Linq.Expressions.Expression> sum = () => new LogFactory().GetCurrentClassLogger(); var logger = sum.Compile().Invoke(); Assert.Equal("NLog.UnitTests.GetLoggerTests", logger.Name); } @@ -133,14 +133,13 @@ private InvalidLogger() } } - [Fact] [Obsolete("Replaced by GetCurrentClassLogger(). Marked obsolete on NLog 5.2")] public void InvalidLoggerConfiguration_NotThrowsThrowExceptions_NotThrows() { using (new NoThrowNLogExceptions()) { - var result = LogManager.GetCurrentClassLogger(typeof(InvalidLogger)); + var result = new LogFactory().GetCurrentClassLogger(typeof(InvalidLogger)); Assert.NotNull(result); } } @@ -152,7 +151,7 @@ public void InvalidLoggerConfiguration_ThrowsThrowExceptions_Throws() LogManager.ThrowExceptions = true; Assert.Throws(() => { - LogManager.GetCurrentClassLogger(typeof(InvalidLogger)); + new LogFactory().GetCurrentClassLogger(typeof(InvalidLogger)); }); } diff --git a/tests/NLog.UnitTests/LoggerTests.cs b/tests/NLog.UnitTests/LoggerTests.cs index 46233ef8a4..5f16a7713c 100644 --- a/tests/NLog.UnitTests/LoggerTests.cs +++ b/tests/NLog.UnitTests/LoggerTests.cs @@ -2225,31 +2225,38 @@ public void SingleTargetMessageFormatOptimizationTest() [InlineData(null, "OrderId", "@Client")] public void MixedStructuredEventsConfigTest(bool? parseMessageTemplates, string param1, string param2) { - LogManager.Configuration = CreateSimpleDebugConfig(parseMessageTemplates); - var logger = LogManager.GetLogger("A"); - logger.Debug("Process order {" + param1 + "} for {" + param2 + "}", 13424, new { ClientId = 3001, ClientName = "John Doe" }); - - string param1Value; - if (param1.StartsWith("$")) + try { - param1Value = "\"13424\""; - } - else - { - param1Value = "13424"; - } + LogManager.Configuration = CreateSimpleDebugConfig(parseMessageTemplates); + var logger = LogManager.GetLogger("A"); + logger.Debug("Process order {" + param1 + "} for {" + param2 + "}", 13424, new { ClientId = 3001, ClientName = "John Doe" }); - string param2Value; - if (param2.StartsWith("@")) - { - param2Value = "{\"ClientId\":3001, \"ClientName\":\"John Doe\"}"; + string param1Value; + if (param1.StartsWith("$")) + { + param1Value = "\"13424\""; + } + else + { + param1Value = "13424"; + } + + string param2Value; + if (param2.StartsWith("@")) + { + param2Value = "{\"ClientId\":3001, \"ClientName\":\"John Doe\"}"; + } + else + { + param2Value = "{ ClientId = 3001, ClientName = John Doe }"; + } + + AssertDebugLastMessage("debug", $"A|Process order {param1Value} for {param2Value}"); } - else + finally { - param2Value = "{ ClientId = 3001, ClientName = John Doe }"; + ConfigurationItemFactory.Default.ParseMessageTemplates = null; } - - AssertDebugLastMessage("debug", $"A|Process order {param1Value} for {param2Value}"); } [Fact] @@ -2285,17 +2292,24 @@ public void StructuredParametersShouldHandleDeferredCheck() [InlineData(null)] public void TooManyStructuredParametersShouldKeepBeInParamList(bool? parseMessageTemplates) { - LogManager.Configuration = CreateSimpleDebugConfig(parseMessageTemplates); - var target = new MyTarget(); - LogManager.Configuration.AddRuleForAllLevels(target); - LogManager.ReconfigExistingLoggers(); + try + { + LogManager.Configuration = CreateSimpleDebugConfig(parseMessageTemplates); + var target = new MyTarget(); + LogManager.Configuration.AddRuleForAllLevels(target); + LogManager.ReconfigExistingLoggers(); - var logger = LogManager.GetLogger("A"); - logger.Debug("Hello World {0}", "world", "universe"); + var logger = LogManager.GetLogger("A"); + logger.Debug("Hello World {0}", "world", "universe"); - Assert.Equal(2, target.LastEvent.Parameters.Length); - Assert.Equal("world", target.LastEvent.Parameters[0]); - Assert.Equal("universe", target.LastEvent.Parameters[1]); + Assert.Equal(2, target.LastEvent.Parameters.Length); + Assert.Equal("world", target.LastEvent.Parameters[0]); + Assert.Equal("universe", target.LastEvent.Parameters[1]); + } + finally + { + ConfigurationItemFactory.Default.ParseMessageTemplates = null; + } } [Theory] @@ -2306,24 +2320,31 @@ public void TooManyStructuredParametersShouldKeepBeInParamList(bool? parseMessag [InlineData(null, null)] public void StructuredEventsConfigTest(bool? parseMessageTemplates, bool? overrideParseMessageTemplates) { - LogManager.Configuration = CreateSimpleDebugConfig(parseMessageTemplates); - - if (parseMessageTemplates.HasValue) + try { - Assert.Equal(ConfigurationItemFactory.Default.ParseMessageTemplates, parseMessageTemplates.Value); - } + LogManager.Configuration = CreateSimpleDebugConfig(parseMessageTemplates); + + if (parseMessageTemplates.HasValue) + { + Assert.Equal(ConfigurationItemFactory.Default.ParseMessageTemplates, parseMessageTemplates.Value); + } + + if (overrideParseMessageTemplates.HasValue) + { + ConfigurationItemFactory.Default.ParseMessageTemplates = overrideParseMessageTemplates.Value; + } - if (overrideParseMessageTemplates.HasValue) + var logger = LogManager.GetLogger("A"); + logger.Debug("Hello World {0}", new object[] { null }); + if (parseMessageTemplates == true || overrideParseMessageTemplates == true) + AssertDebugLastMessage("debug", "A|Hello World NULL"); + else + AssertDebugLastMessage("debug", "A|Hello World "); + } + finally { - ConfigurationItemFactory.Default.ParseMessageTemplates = overrideParseMessageTemplates.Value; + ConfigurationItemFactory.Default.ParseMessageTemplates = null; } - - var logger = LogManager.GetLogger("A"); - logger.Debug("Hello World {0}", new object[] { null }); - if (parseMessageTemplates == true || overrideParseMessageTemplates == true) - AssertDebugLastMessage("debug", "A|Hello World NULL"); - else - AssertDebugLastMessage("debug", "A|Hello World "); } [Fact] From ec1b38fc3dd8fcc187bc1297ceaf9651496ead72 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 3 May 2025 12:50:28 +0200 Subject: [PATCH 103/224] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ad5221d7a..3165b548fc 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Having troubles? Check the [troubleshooting guide](https://github.com/NLog/NLog/ ℹ️ NLog 6.0 will support AOT -NLog 6.0 is now being prepared. See [List of goals for NLog 6.0](https://nlog-project.org/2024/10/01/nlog-6-0-goals.html) +[NLog 6.0 Preview1](https://www.nuget.org/packages/NLog/6.0.0-preview1#releasenotes-body-tab) now available. See also [List of major changes in NLog 6.0](https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html) NLog Packages --- From 87ed9710828b628b83027bf52b73b31527e38d63 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 3 May 2025 14:17:24 +0200 Subject: [PATCH 104/224] Logger with support for params ReadOnlySpan (#5800) --- appveyor.yml | 5 +- src/NLog/Common/InternalLogger-generated.cs | 165 ++++- src/NLog/Common/InternalLogger-generated.tt | 29 +- src/NLog/Common/InternalLogger.cs | 29 + src/NLog/ILogger-V1.cs | 541 +++++++++++++++ src/NLog/ILoggerBase-V1.cs | 91 +++ .../OverloadResolutionPriorityAttribute.cs | 60 ++ src/NLog/LogEventBuilder.cs | 50 ++ src/NLog/Logger-Conditional.cs | 346 ++++++++-- src/NLog/Logger-V1Compat.cs | 631 ++++++++++++++++++ src/NLog/Logger-generated.cs | 218 +++++- src/NLog/Logger-generated.tt | 42 +- src/NLog/Logger.cs | 38 ++ src/NLog/NLog.csproj | 6 +- .../Fluent/LogEventBuilderTests.cs | 2 +- 15 files changed, 2185 insertions(+), 68 deletions(-) create mode 100644 src/NLog/Internal/OverloadResolutionPriorityAttribute.cs diff --git a/appveyor.yml b/appveyor.yml index 052c96509c..89f27c3805 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,7 @@ version: 6.0.0-{build} # Only change for mayor versions (e.g. 7.0) image: - Visual Studio 2022 - - Ubuntu2004 + - Ubuntu2204 configuration: Release build: false test: false @@ -54,9 +54,10 @@ for: - matrix: only: - - image: Ubuntu2004 + - image: Ubuntu2204 environment: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + MSBUILDTERMINALLOGGER: off FrameworkPathOverride: /usr/lib/mono/4.6.1-api/ build_script: - ps: dotnet --version diff --git a/src/NLog/Common/InternalLogger-generated.cs b/src/NLog/Common/InternalLogger-generated.cs index e3ad568d8f..04b2a080ac 100644 --- a/src/NLog/Common/InternalLogger-generated.cs +++ b/src/NLog/Common/InternalLogger-generated.cs @@ -70,6 +70,33 @@ public static partial class InternalLogger public static bool IsFatalEnabled => IsLogLevelEnabled(LogLevel.Fatal); +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Logs the specified message without an at the Trace level. + /// + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Trace([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + if (IsTraceEnabled) + Write(null, LogLevel.Trace, message, args.IsEmpty ? null : args.ToArray()); + } + + /// + /// Logs the specified message with an at the Trace level. + /// + /// Exception to be logged. + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Trace(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + { + if (IsTraceEnabled) + Write(ex, LogLevel.Trace, message, args.IsEmpty ? null : args.ToArray()); + } +#endif + /// /// Logs the specified message without an at the Trace level. /// @@ -184,6 +211,33 @@ public static void Trace(Exception ex, Func messageFunc) Write(ex, LogLevel.Trace, messageFunc(), null); } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Logs the specified message without an at the Debug level. + /// + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Debug([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + if (IsDebugEnabled) + Write(null, LogLevel.Debug, message, args.IsEmpty ? null : args.ToArray()); + } + + /// + /// Logs the specified message with an at the Debug level. + /// + /// Exception to be logged. + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Debug(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + { + if (IsDebugEnabled) + Write(ex, LogLevel.Debug, message, args.IsEmpty ? null : args.ToArray()); + } +#endif + /// /// Logs the specified message without an at the Debug level. /// @@ -298,6 +352,33 @@ public static void Debug(Exception ex, Func messageFunc) Write(ex, LogLevel.Debug, messageFunc(), null); } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Logs the specified message without an at the Info level. + /// + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Info([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + if (IsInfoEnabled) + Write(null, LogLevel.Info, message, args.IsEmpty ? null : args.ToArray()); + } + + /// + /// Logs the specified message with an at the Info level. + /// + /// Exception to be logged. + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Info(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + { + if (IsInfoEnabled) + Write(ex, LogLevel.Info, message, args.IsEmpty ? null : args.ToArray()); + } +#endif + /// /// Logs the specified message without an at the Info level. /// @@ -412,6 +493,33 @@ public static void Info(Exception ex, Func messageFunc) Write(ex, LogLevel.Info, messageFunc(), null); } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Logs the specified message without an at the Warn level. + /// + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Warn([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + if (IsWarnEnabled) + Write(null, LogLevel.Warn, message, args.IsEmpty ? null : args.ToArray()); + } + + /// + /// Logs the specified message with an at the Warn level. + /// + /// Exception to be logged. + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Warn(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + { + if (IsWarnEnabled) + Write(ex, LogLevel.Warn, message, args.IsEmpty ? null : args.ToArray()); + } +#endif + /// /// Logs the specified message without an at the Warn level. /// @@ -526,6 +634,33 @@ public static void Warn(Exception ex, Func messageFunc) Write(ex, LogLevel.Warn, messageFunc(), null); } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Logs the specified message without an at the Error level. + /// + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Error([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + if (IsErrorEnabled) + Write(null, LogLevel.Error, message, args.IsEmpty ? null : args.ToArray()); + } + + /// + /// Logs the specified message with an at the Error level. + /// + /// Exception to be logged. + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Error(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + { + if (IsErrorEnabled) + Write(ex, LogLevel.Error, message, args.IsEmpty ? null : args.ToArray()); + } +#endif + /// /// Logs the specified message without an at the Error level. /// @@ -640,6 +775,33 @@ public static void Error(Exception ex, Func messageFunc) Write(ex, LogLevel.Error, messageFunc(), null); } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Logs the specified message without an at the Fatal level. + /// + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Fatal([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + if (IsFatalEnabled) + Write(null, LogLevel.Fatal, message, args.IsEmpty ? null : args.ToArray()); + } + + /// + /// Logs the specified message with an at the Fatal level. + /// + /// Exception to be logged. + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Fatal(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + { + if (IsFatalEnabled) + Write(ex, LogLevel.Fatal, message, args.IsEmpty ? null : args.ToArray()); + } +#endif + /// /// Logs the specified message without an at the Fatal level. /// @@ -753,6 +915,5 @@ public static void Fatal(Exception ex, Func messageFunc) if (IsFatalEnabled) Write(ex, LogLevel.Fatal, messageFunc(), null); } - } -} +} \ No newline at end of file diff --git a/src/NLog/Common/InternalLogger-generated.tt b/src/NLog/Common/InternalLogger-generated.tt index e12cb907b8..63c5c25b9d 100644 --- a/src/NLog/Common/InternalLogger-generated.tt +++ b/src/NLog/Common/InternalLogger-generated.tt @@ -33,9 +33,9 @@ namespace NLog.Common { - using JetBrains.Annotations; using System; using System.ComponentModel; + using JetBrains.Annotations; public static partial class InternalLogger { @@ -56,6 +56,33 @@ namespace NLog.Common { #> +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Logs the specified message without an at the <#=level#> level. + /// + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + if (Is<#=level#>Enabled) + Write(null, LogLevel.<#=level#>, message, args.IsEmpty ? null : args.ToArray()); + } + + /// + /// Logs the specified message with an at the <#=level#> level. + /// + /// Exception to be logged. + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void <#=level#>(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + { + if (Is<#=level#>Enabled) + Write(ex, LogLevel.<#=level#>, message, args.IsEmpty ? null : args.ToArray()); + } +#endif + /// /// Logs the specified message without an at the <#=level#> level. /// diff --git a/src/NLog/Common/InternalLogger.cs b/src/NLog/Common/InternalLogger.cs index 0fa78c82bf..803d76ba52 100644 --- a/src/NLog/Common/InternalLogger.cs +++ b/src/NLog/Common/InternalLogger.cs @@ -181,6 +181,35 @@ public static void Log(LogLevel level, [Localizable(false)] string message, para Write(null, level, message, args); } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Logs the specified message without an at the specified level. + /// + /// Log level. + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Log(LogLevel level, [Localizable(false)] string message, params ReadOnlySpan args) + { + if (IsLogLevelEnabled(level)) + Write(null, level, message, args.IsEmpty ? null : args.ToArray()); + } + + /// + /// Logs the specified message with an at the specified level. + /// + /// Exception to be logged. + /// Log level. + /// Message which may include positional parameters. + /// Arguments to the message. + [StringFormatMethod("message")] + public static void Log(Exception ex, LogLevel level, [Localizable(false)] string message, params ReadOnlySpan args) + { + if (IsLogLevelEnabled(level)) + Write(ex, level, message, args.IsEmpty ? null : args.ToArray()); + } +#endif + /// /// Logs the specified message without an at the specified level. /// diff --git a/src/NLog/ILogger-V1.cs b/src/NLog/ILogger-V1.cs index 111f99a0de..fb86392277 100644 --- a/src/NLog/ILogger-V1.cs +++ b/src/NLog/ILogger-V1.cs @@ -35,6 +35,7 @@ namespace NLog { using System; using System.ComponentModel; + using System.Runtime.CompilerServices; using JetBrains.Annotations; /// @@ -50,6 +51,9 @@ public partial interface ILogger /// /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(object value); /// @@ -58,6 +62,9 @@ public partial interface ILogger /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, object value); /// @@ -68,6 +75,9 @@ public partial interface ILogger /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); /// @@ -79,6 +89,9 @@ public partial interface ILogger /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); /// @@ -89,6 +102,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -98,6 +114,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -108,6 +127,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -117,6 +139,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -127,6 +152,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -136,6 +164,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -146,6 +177,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -155,6 +189,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -165,6 +202,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -174,6 +214,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -184,6 +227,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -193,6 +239,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -203,6 +252,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -212,6 +264,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -222,6 +277,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -231,6 +289,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -241,6 +302,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -250,6 +314,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -260,6 +327,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -269,6 +339,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -279,6 +352,9 @@ public partial interface ILogger /// The argument to format.s [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -288,6 +364,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -298,6 +377,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -307,6 +389,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -317,6 +402,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// @@ -326,6 +414,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Trace([Localizable(false)][StructuredMessageTemplate] string message, ulong argument); #endregion @@ -337,6 +428,9 @@ public partial interface ILogger /// /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(object value); /// @@ -345,6 +439,9 @@ public partial interface ILogger /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, object value); /// @@ -355,6 +452,9 @@ public partial interface ILogger /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); /// @@ -366,6 +466,9 @@ public partial interface ILogger /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); /// @@ -376,6 +479,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -385,6 +491,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -395,6 +504,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -404,6 +516,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -414,6 +529,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -423,6 +541,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -433,6 +554,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -442,6 +566,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -452,6 +579,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -461,6 +591,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -471,6 +604,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -480,6 +616,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -490,6 +629,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -499,6 +641,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -509,6 +654,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -518,6 +666,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -528,6 +679,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -537,6 +691,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -547,6 +704,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -556,6 +716,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -566,6 +729,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -575,6 +741,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -585,6 +754,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -594,6 +766,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -604,6 +779,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// @@ -613,6 +791,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Debug([Localizable(false)][StructuredMessageTemplate] string message, ulong argument); #endregion @@ -624,6 +805,9 @@ public partial interface ILogger /// /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(object value); /// @@ -632,6 +816,9 @@ public partial interface ILogger /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, object value); /// @@ -642,6 +829,9 @@ public partial interface ILogger /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); /// @@ -653,6 +843,9 @@ public partial interface ILogger /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); /// @@ -663,6 +856,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -672,6 +868,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -682,6 +881,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -691,6 +893,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -701,6 +906,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -710,6 +918,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -720,6 +931,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -729,6 +943,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -739,6 +956,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -748,6 +968,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -758,6 +981,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -767,6 +993,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -777,6 +1006,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -786,6 +1018,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -796,6 +1031,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -805,6 +1043,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -815,6 +1056,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -824,6 +1068,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -834,6 +1081,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -843,6 +1093,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -853,6 +1106,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -862,6 +1118,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -872,6 +1131,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -881,6 +1143,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -891,6 +1156,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// @@ -900,6 +1168,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Info([Localizable(false)][StructuredMessageTemplate] string message, ulong argument); #endregion @@ -911,6 +1182,9 @@ public partial interface ILogger /// /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(object value); /// @@ -919,6 +1193,9 @@ public partial interface ILogger /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, object value); /// @@ -929,6 +1206,9 @@ public partial interface ILogger /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); /// @@ -940,6 +1220,9 @@ public partial interface ILogger /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); /// @@ -950,6 +1233,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -959,6 +1245,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -969,6 +1258,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -978,6 +1270,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -988,6 +1283,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -997,6 +1295,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -1007,6 +1308,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -1016,6 +1320,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -1026,6 +1333,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -1035,6 +1345,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -1045,6 +1358,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -1054,6 +1370,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -1064,6 +1383,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -1073,6 +1395,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -1083,6 +1408,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -1092,6 +1420,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -1102,6 +1433,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -1111,6 +1445,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -1121,6 +1458,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -1130,6 +1470,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -1140,6 +1483,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -1149,6 +1495,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -1159,6 +1508,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -1168,6 +1520,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -1178,6 +1533,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// @@ -1187,6 +1545,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Warn([Localizable(false)][StructuredMessageTemplate] string message, ulong argument); #endregion @@ -1198,6 +1559,9 @@ public partial interface ILogger /// /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(object value); /// @@ -1206,6 +1570,9 @@ public partial interface ILogger /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, object value); /// @@ -1216,6 +1583,9 @@ public partial interface ILogger /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); /// @@ -1227,6 +1597,9 @@ public partial interface ILogger /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); /// @@ -1237,6 +1610,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -1245,6 +1621,9 @@ public partial interface ILogger /// A containing one format item. /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -1255,6 +1634,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -1264,6 +1646,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -1274,6 +1659,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -1282,6 +1670,9 @@ public partial interface ILogger /// A containing one format item. /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -1292,6 +1683,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -1301,6 +1695,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -1311,6 +1708,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -1320,6 +1720,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -1330,6 +1733,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -1339,6 +1745,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -1349,6 +1758,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -1357,6 +1769,9 @@ public partial interface ILogger /// A containing one format item. /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -1367,6 +1782,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -1376,6 +1794,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -1386,6 +1807,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -1395,6 +1819,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -1405,6 +1832,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -1414,6 +1844,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -1424,6 +1857,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -1433,6 +1869,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -1443,6 +1882,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -1452,6 +1894,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -1462,6 +1907,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// @@ -1471,6 +1919,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Error([Localizable(false)][StructuredMessageTemplate] string message, ulong argument); #endregion @@ -1482,6 +1933,9 @@ public partial interface ILogger /// /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(object value); /// @@ -1490,6 +1944,9 @@ public partial interface ILogger /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, object value); /// @@ -1500,6 +1957,9 @@ public partial interface ILogger /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); /// @@ -1511,6 +1971,9 @@ public partial interface ILogger /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); /// @@ -1521,6 +1984,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -1530,6 +1996,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -1540,6 +2009,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -1549,6 +2021,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -1559,6 +2034,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -1568,6 +2046,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -1578,6 +2059,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -1587,6 +2071,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -1597,6 +2084,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -1606,6 +2096,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -1616,6 +2109,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -1625,6 +2121,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -1635,6 +2134,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -1644,6 +2146,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -1654,6 +2159,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -1663,6 +2171,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -1673,6 +2184,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -1681,6 +2195,9 @@ public partial interface ILogger /// A containing one format item. /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -1691,6 +2208,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -1700,6 +2220,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -1710,6 +2233,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -1719,6 +2245,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -1729,6 +2258,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -1738,6 +2270,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -1748,6 +2283,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// @@ -1757,6 +2295,9 @@ public partial interface ILogger /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Fatal([Localizable(false)][StructuredMessageTemplate] string message, ulong argument); #endregion diff --git a/src/NLog/ILoggerBase-V1.cs b/src/NLog/ILoggerBase-V1.cs index 1903bb6ad7..127ad80344 100644 --- a/src/NLog/ILoggerBase-V1.cs +++ b/src/NLog/ILoggerBase-V1.cs @@ -35,6 +35,7 @@ namespace NLog { using System; using System.ComponentModel; + using System.Runtime.CompilerServices; using JetBrains.Annotations; /// @@ -51,6 +52,9 @@ public partial interface ILoggerBase /// The log level. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, object value); /// @@ -60,6 +64,9 @@ public partial interface ILoggerBase /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, object value); /// @@ -71,6 +78,9 @@ public partial interface ILoggerBase /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); /// @@ -83,6 +93,9 @@ public partial interface ILoggerBase /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); /// @@ -94,6 +107,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -104,6 +120,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// @@ -115,6 +134,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -125,6 +147,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// @@ -136,6 +161,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -146,6 +174,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// @@ -157,6 +188,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -167,6 +201,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, string argument); /// @@ -178,6 +215,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -188,6 +228,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// @@ -199,6 +242,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -209,6 +255,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// @@ -220,6 +269,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -230,6 +282,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// @@ -241,6 +296,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -251,6 +309,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// @@ -262,6 +323,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -272,6 +336,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// @@ -283,6 +350,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -293,6 +363,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object argument); /// @@ -304,6 +377,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -314,6 +390,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// @@ -325,6 +404,9 @@ public partial interface ILoggerBase /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -336,6 +418,9 @@ public partial interface ILoggerBase [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// @@ -348,6 +433,9 @@ public partial interface ILoggerBase [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// @@ -359,6 +447,9 @@ public partial interface ILoggerBase [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); #endregion diff --git a/src/NLog/Internal/OverloadResolutionPriorityAttribute.cs b/src/NLog/Internal/OverloadResolutionPriorityAttribute.cs new file mode 100644 index 0000000000..bea34420d8 --- /dev/null +++ b/src/NLog/Internal/OverloadResolutionPriorityAttribute.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#if NETSTANDARD2_1_OR_GREATER && !NET9_0_OR_GREATER + +namespace System.Runtime.CompilerServices +{ + /// + /// Specifies the priority of a member in overload resolution. When unspecified, the default priority is 0. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] + internal sealed class OverloadResolutionPriorityAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// The priority of the attributed member. Higher numbers are prioritized, lower numbers are deprioritized. 0 is the default if no attribute is present. + public OverloadResolutionPriorityAttribute(int priority) + { + Priority = priority; + } + + /// + /// The priority of the member. + /// + public int Priority { get; } + } +} + +#endif diff --git a/src/NLog/LogEventBuilder.cs b/src/NLog/LogEventBuilder.cs index e98b4541a1..dd92d2c4bd 100644 --- a/src/NLog/LogEventBuilder.cs +++ b/src/NLog/LogEventBuilder.cs @@ -124,6 +124,38 @@ public LogEventBuilder Properties([NotNull] IEnumerable + /// Sets multiple per-event context properties on the logging event. + /// + /// The properties to set. + public LogEventBuilder Properties(params ReadOnlySpan<(string, object)> properties) + { + if (_logEvent is null) + return this; + + if (_logEvent.Parameters is null) + { + var eventProperties = _logEvent.CreateOrUpdatePropertiesInternal(false, null); + if (eventProperties is null) + { + // Now allocate PropertiesDictionary and copy from properties + var messageProperties = new MessageTemplates.MessageTemplateParameter[properties.Length]; + for (int i = 0; i < properties.Length; ++i) + { + messageProperties[i] = new MessageTemplates.MessageTemplateParameter(properties[i].Item1, properties[i].Item2, null); + } + _logEvent.CreateOrUpdatePropertiesInternal(false, messageProperties); + return this; + } + } + + foreach (var property in properties) + _logEvent.Properties[property.Item1] = property.Item2; + return this; + } +#endif + /// /// Sets the information of the logging event. /// @@ -237,6 +269,24 @@ public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] s return this; } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Sets the log message and parameters for formatting on the logging event. + /// + /// A containing format items. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + if (_logEvent != null) + { + _logEvent.Message = message; + _logEvent.Parameters = args.IsEmpty ? null : args.ToArray(); + } + return this; + } +#endif + /// /// Sets the log message and parameters for formatting on the logging event. /// diff --git a/src/NLog/Logger-Conditional.cs b/src/NLog/Logger-Conditional.cs index c23b04c164..47996e3d44 100644 --- a/src/NLog/Logger-Conditional.cs +++ b/src/NLog/Logger-Conditional.cs @@ -36,6 +36,7 @@ namespace NLog using System; using System.ComponentModel; using System.Diagnostics; + using System.Runtime.CompilerServices; using JetBrains.Annotations; /// @@ -102,6 +103,21 @@ public void ConditionalDebug(Exception exception, [Localizable(false)][Structure Debug(exception, message, args); } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Writes the diagnostic message and exception at the Debug level. + /// Only executed when the DEBUG conditional compilation symbol is set. + /// A to be written. + /// An exception to be logged. + /// Arguments to format. + [Conditional("DEBUG")] + [MessageTemplateFormatMethod("message")] + public void ConditionalDebug(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + Debug(exception, message, args); + } +#endif + /// /// Writes the diagnostic message and exception at the Debug level. /// Only executed when the DEBUG conditional compilation symbol is set. @@ -151,6 +167,19 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str Debug(message, args); } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Writes the diagnostic message at the Debug level using the specified parameters. + /// Only executed when the DEBUG conditional compilation symbol is set. + /// A containing format items. + /// Arguments to format. + [Conditional("DEBUG")] + [MessageTemplateFormatMethod("message")] + public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + Debug(message, args); + } +#endif /// /// Writes the diagnostic message at the Debug level using the specified parameter and formatting it with the supplied format provider. @@ -250,9 +279,13 @@ public void ConditionalDebug([Localizable(fa /// Only executed when the DEBUG conditional compilation symbol is set. /// A to be written. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug(object value) { - Debug(value); + Debug(value); } /// @@ -261,9 +294,13 @@ public void ConditionalDebug(object value) /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug(IFormatProvider formatProvider, object value) { - Debug(formatProvider, value); + Debug(formatProvider, value); } /// @@ -273,10 +310,14 @@ public void ConditionalDebug(IFormatProvider formatProvider, object value) /// First argument to format. /// Second argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) { - Debug(message, arg1, arg2); + Debug(message, arg1, arg2); } /// @@ -287,10 +328,14 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// Second argument to format. /// Third argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) { - Debug(message, arg1, arg2, arg3); + Debug(message, arg1, arg2, arg3); } /// @@ -300,10 +345,14 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { - Debug(formatProvider, message, argument); + Debug(formatProvider, message, argument); } /// @@ -312,10 +361,14 @@ public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, bool argument) { - Debug(message, argument); + Debug(message, argument); } /// @@ -325,10 +378,14 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { - Debug(formatProvider, message, argument); + Debug(formatProvider, message, argument); } /// @@ -337,10 +394,14 @@ public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, char argument) { - Debug(message, argument); + Debug(message, argument); } /// @@ -350,10 +411,14 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { - Debug(formatProvider, message, argument); + Debug(formatProvider, message, argument); } /// @@ -362,10 +427,14 @@ public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, byte argument) { - Debug(message, argument); + Debug(message, argument); } /// @@ -375,10 +444,14 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) { - Debug(formatProvider, message, argument); + Debug(formatProvider, message, argument); } /// @@ -387,10 +460,14 @@ public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, string argument) { - Debug(message, argument); + Debug(message, argument); } /// @@ -400,10 +477,14 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { - Debug(formatProvider, message, argument); + Debug(formatProvider, message, argument); } /// @@ -412,10 +493,14 @@ public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, int argument) { - Debug(message, argument); + Debug(message, argument); } /// @@ -425,10 +510,14 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { - Debug(formatProvider, message, argument); + Debug(formatProvider, message, argument); } /// @@ -437,10 +526,14 @@ public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, long argument) { - Debug(message, argument); + Debug(message, argument); } /// @@ -450,10 +543,14 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { - Debug(formatProvider, message, argument); + Debug(formatProvider, message, argument); } /// @@ -462,10 +559,14 @@ public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, float argument) { - Debug(message, argument); + Debug(message, argument); } /// @@ -475,10 +576,14 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { - Debug(formatProvider, message, argument); + Debug(formatProvider, message, argument); } /// @@ -487,10 +592,14 @@ public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, double argument) { - Debug(message, argument); + Debug(message, argument); } /// @@ -500,10 +609,14 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { - Debug(formatProvider, message, argument); + Debug(formatProvider, message, argument); } /// @@ -512,10 +625,14 @@ public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { - Debug(message, argument); + Debug(message, argument); } /// @@ -525,10 +642,14 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) { - Debug(formatProvider, message, argument); + Debug(formatProvider, message, argument); } /// @@ -537,10 +658,14 @@ public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, object argument) { - Debug(message, argument); + Debug(message, argument); } #endregion @@ -596,6 +721,21 @@ public void ConditionalTrace(Exception exception, [Localizable(false)][Structure Trace(exception, message, args); } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Writes the diagnostic message and exception at the Trace level. + /// Only executed when the DEBUG conditional compilation symbol is set. + /// A to be written. + /// An exception to be logged. + /// Arguments to format. + [Conditional("DEBUG")] + [MessageTemplateFormatMethod("message")] + public void ConditionalTrace(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + Trace(exception, message, args); + } +#endif + /// /// Writes the diagnostic message and exception at the Trace level. /// Only executed when the DEBUG conditional compilation symbol is set. @@ -646,6 +786,20 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str Trace(message, args); } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Writes the diagnostic message at the Trace level using the specified parameters. + /// Only executed when the DEBUG conditional compilation symbol is set. + /// A containing format items. + /// Arguments to format. + [Conditional("DEBUG")] + [MessageTemplateFormatMethod("message")] + public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + Trace(message, args); + } +#endif + /// /// Writes the diagnostic message at the Trace level using the specified parameter and formatting it with the supplied format provider. /// Only executed when the DEBUG conditional compilation symbol is set. @@ -744,9 +898,13 @@ public void ConditionalTrace([Localizable(fa /// Only executed when the DEBUG conditional compilation symbol is set. /// A to be written. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace(object value) { - Trace(value); + Trace(value); } /// @@ -755,9 +913,13 @@ public void ConditionalTrace(object value) /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace(IFormatProvider formatProvider, object value) { - Trace(formatProvider, value); + Trace(formatProvider, value); } /// @@ -767,10 +929,14 @@ public void ConditionalTrace(IFormatProvider formatProvider, object value) /// First argument to format. /// Second argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) { - Trace(message, arg1, arg2); + Trace(message, arg1, arg2); } /// @@ -781,10 +947,14 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str /// Second argument to format. /// Third argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) { - Trace(message, arg1, arg2, arg3); + Trace(message, arg1, arg2, arg3); } /// @@ -794,10 +964,14 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { - Trace(formatProvider, message, argument); + Trace(formatProvider, message, argument); } /// @@ -806,10 +980,14 @@ public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, bool argument) { - Trace(message, argument); + Trace(message, argument); } /// @@ -819,10 +997,14 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { - Trace(formatProvider, message, argument); + Trace(formatProvider, message, argument); } /// @@ -831,10 +1013,14 @@ public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, char argument) { - Trace(message, argument); + Trace(message, argument); } /// @@ -844,10 +1030,14 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { - Trace(formatProvider, message, argument); + Trace(formatProvider, message, argument); } /// @@ -856,10 +1046,14 @@ public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, byte argument) { - Trace(message, argument); + Trace(message, argument); } /// @@ -869,10 +1063,14 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) { - Trace(formatProvider, message, argument); + Trace(formatProvider, message, argument); } /// @@ -881,10 +1079,14 @@ public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, string argument) { - Trace(message, argument); + Trace(message, argument); } /// @@ -894,10 +1096,14 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { - Trace(formatProvider, message, argument); + Trace(formatProvider, message, argument); } /// @@ -906,10 +1112,14 @@ public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, int argument) { - Trace(message, argument); + Trace(message, argument); } /// @@ -919,10 +1129,14 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { - Trace(formatProvider, message, argument); + Trace(formatProvider, message, argument); } /// @@ -931,10 +1145,14 @@ public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, long argument) { - Trace(message, argument); + Trace(message, argument); } /// @@ -944,10 +1162,14 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { - Trace(formatProvider, message, argument); + Trace(formatProvider, message, argument); } /// @@ -956,10 +1178,14 @@ public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, float argument) { - Trace(message, argument); + Trace(message, argument); } /// @@ -969,10 +1195,14 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { - Trace(formatProvider, message, argument); + Trace(formatProvider, message, argument); } /// @@ -981,10 +1211,14 @@ public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, double argument) { - Trace(message, argument); + Trace(message, argument); } /// @@ -994,10 +1228,14 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { - Trace(formatProvider, message, argument); + Trace(formatProvider, message, argument); } /// @@ -1006,10 +1244,14 @@ public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { - Trace(message, argument); + Trace(message, argument); } /// @@ -1019,10 +1261,14 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) { - Trace(formatProvider, message, argument); + Trace(formatProvider, message, argument); } /// @@ -1031,10 +1277,14 @@ public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [Conditional("DEBUG")] + [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, object argument) { - Trace(message, argument); + Trace(message, argument); } diff --git a/src/NLog/Logger-V1Compat.cs b/src/NLog/Logger-V1Compat.cs index 60c818ce7c..bbc347ca08 100644 --- a/src/NLog/Logger-V1Compat.cs +++ b/src/NLog/Logger-V1Compat.cs @@ -35,6 +35,7 @@ namespace NLog { using System; using System.ComponentModel; + using System.Runtime.CompilerServices; using JetBrains.Annotations; /// @@ -51,6 +52,9 @@ public partial class Logger : ILogger /// The log level. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, object value) { if (IsEnabled(level)) @@ -66,6 +70,9 @@ public void Log(LogLevel level, object value) /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, object value) { if (IsEnabled(level)) @@ -83,6 +90,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, object value) /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) { if (IsEnabled(level)) @@ -101,6 +111,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) { if (IsEnabled(level)) @@ -118,6 +131,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsEnabled(level)) @@ -134,6 +150,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsEnabled(level)) @@ -151,6 +170,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsEnabled(level)) @@ -167,6 +189,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsEnabled(level)) @@ -184,6 +209,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsEnabled(level)) @@ -200,6 +228,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsEnabled(level)) @@ -217,6 +248,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsEnabled(level)) @@ -233,6 +267,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsEnabled(level)) @@ -250,6 +287,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, string message, int argument) { if (IsEnabled(level)) @@ -266,6 +306,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsEnabled(level)) @@ -283,6 +326,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsEnabled(level)) @@ -299,6 +345,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsEnabled(level)) @@ -316,6 +365,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsEnabled(level)) @@ -332,6 +384,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsEnabled(level)) @@ -349,6 +404,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsEnabled(level)) @@ -365,6 +423,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsEnabled(level)) @@ -382,6 +443,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsEnabled(level)) @@ -398,6 +462,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsEnabled(level)) @@ -415,6 +482,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsEnabled(level)) @@ -431,6 +501,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsEnabled(level)) @@ -449,6 +522,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsEnabled(level)) @@ -466,6 +542,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsEnabled(level)) @@ -484,6 +563,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsEnabled(level)) @@ -501,6 +583,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsEnabled(level)) @@ -519,6 +604,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsEnabled(level)) @@ -536,6 +624,9 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsEnabled(level)) @@ -553,6 +644,9 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(object value) { if (IsTraceEnabled) @@ -567,6 +661,9 @@ public void Trace(object value) /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, object value) { if (IsTraceEnabled) @@ -583,6 +680,9 @@ public void Trace(IFormatProvider formatProvider, object value) /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) { if (IsTraceEnabled) @@ -600,6 +700,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) { if (IsTraceEnabled) @@ -616,6 +719,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsTraceEnabled) @@ -631,6 +737,9 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsTraceEnabled) @@ -647,6 +756,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsTraceEnabled) @@ -662,6 +774,9 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsTraceEnabled) @@ -678,6 +793,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsTraceEnabled) @@ -693,6 +811,9 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsTraceEnabled) @@ -709,6 +830,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsTraceEnabled) @@ -724,6 +848,9 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsTraceEnabled) @@ -740,6 +867,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, string message, int argument) { if (IsTraceEnabled) @@ -755,6 +885,9 @@ public void Trace(IFormatProvider formatProvider, string message, int argument) /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsTraceEnabled) @@ -771,6 +904,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsTraceEnabled) @@ -786,6 +922,9 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsTraceEnabled) @@ -802,6 +941,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsTraceEnabled) @@ -817,6 +959,9 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsTraceEnabled) @@ -833,6 +978,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsTraceEnabled) @@ -848,6 +996,9 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsTraceEnabled) @@ -864,6 +1015,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsTraceEnabled) @@ -879,6 +1033,9 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsTraceEnabled) @@ -895,6 +1052,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsTraceEnabled) @@ -910,6 +1070,9 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsTraceEnabled) @@ -927,6 +1090,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsTraceEnabled) @@ -943,6 +1109,9 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsTraceEnabled) @@ -960,6 +1129,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsTraceEnabled) @@ -976,6 +1148,9 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsTraceEnabled) @@ -993,6 +1168,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsTraceEnabled) @@ -1009,6 +1187,9 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Trace([Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsTraceEnabled) @@ -1026,6 +1207,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(object value) { if (IsDebugEnabled) @@ -1040,6 +1224,9 @@ public void Debug(object value) /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, object value) { if (IsDebugEnabled) @@ -1056,6 +1243,9 @@ public void Debug(IFormatProvider formatProvider, object value) /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) { if (IsDebugEnabled) @@ -1073,6 +1263,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) { if (IsDebugEnabled) @@ -1089,6 +1282,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsDebugEnabled) @@ -1104,6 +1300,9 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsDebugEnabled) @@ -1120,6 +1319,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsDebugEnabled) @@ -1135,6 +1337,9 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsDebugEnabled) @@ -1151,6 +1356,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsDebugEnabled) @@ -1166,6 +1374,9 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsDebugEnabled) @@ -1182,6 +1393,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsDebugEnabled) @@ -1197,6 +1411,9 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsDebugEnabled) @@ -1213,6 +1430,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsDebugEnabled) @@ -1228,6 +1448,9 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsDebugEnabled) @@ -1244,6 +1467,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsDebugEnabled) @@ -1259,6 +1485,9 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsDebugEnabled) @@ -1275,6 +1504,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsDebugEnabled) @@ -1290,6 +1522,9 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsDebugEnabled) @@ -1306,6 +1541,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsDebugEnabled) @@ -1321,6 +1559,9 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsDebugEnabled) @@ -1337,6 +1578,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsDebugEnabled) @@ -1352,6 +1596,9 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsDebugEnabled) @@ -1368,6 +1615,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsDebugEnabled) @@ -1383,6 +1633,9 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsDebugEnabled) @@ -1400,6 +1653,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsDebugEnabled) @@ -1416,6 +1672,9 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsDebugEnabled) @@ -1433,6 +1692,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsDebugEnabled) @@ -1449,6 +1711,9 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsDebugEnabled) @@ -1466,6 +1731,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsDebugEnabled) @@ -1482,6 +1750,9 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Debug([Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsDebugEnabled) @@ -1499,6 +1770,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(object value) { if (IsInfoEnabled) @@ -1513,6 +1787,9 @@ public void Info(object value) /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, object value) { if (IsInfoEnabled) @@ -1529,6 +1806,9 @@ public void Info(IFormatProvider formatProvider, object value) /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) { if (IsInfoEnabled) @@ -1546,6 +1826,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) { if (IsInfoEnabled) @@ -1562,6 +1845,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsInfoEnabled) @@ -1577,6 +1863,9 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsInfoEnabled) @@ -1593,6 +1882,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsInfoEnabled) @@ -1608,6 +1900,9 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsInfoEnabled) @@ -1624,6 +1919,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsInfoEnabled) @@ -1639,6 +1937,9 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsInfoEnabled) @@ -1655,6 +1956,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsInfoEnabled) @@ -1670,6 +1974,9 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsInfoEnabled) @@ -1686,6 +1993,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsInfoEnabled) @@ -1701,6 +2011,9 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsInfoEnabled) @@ -1717,6 +2030,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsInfoEnabled) @@ -1732,6 +2048,9 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsInfoEnabled) @@ -1748,6 +2067,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsInfoEnabled) @@ -1763,6 +2085,9 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsInfoEnabled) @@ -1779,6 +2104,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsInfoEnabled) @@ -1794,6 +2122,9 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsInfoEnabled) @@ -1810,6 +2141,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsInfoEnabled) @@ -1825,6 +2159,9 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsInfoEnabled) @@ -1841,6 +2178,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsInfoEnabled) @@ -1856,6 +2196,9 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsInfoEnabled) @@ -1873,6 +2216,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsInfoEnabled) @@ -1889,6 +2235,9 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsInfoEnabled) @@ -1906,6 +2255,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsInfoEnabled) @@ -1922,6 +2274,9 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsInfoEnabled) @@ -1939,6 +2294,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsInfoEnabled) @@ -1955,6 +2313,9 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Info([Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsInfoEnabled) @@ -1972,6 +2333,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(object value) { if (IsWarnEnabled) @@ -1986,6 +2350,9 @@ public void Warn(object value) /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, object value) { if (IsWarnEnabled) @@ -2002,6 +2369,9 @@ public void Warn(IFormatProvider formatProvider, object value) /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) { if (IsWarnEnabled) @@ -2019,6 +2389,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) { if (IsWarnEnabled) @@ -2035,6 +2408,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsWarnEnabled) @@ -2050,6 +2426,9 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsWarnEnabled) @@ -2066,6 +2445,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsWarnEnabled) @@ -2081,6 +2463,9 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsWarnEnabled) @@ -2097,6 +2482,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsWarnEnabled) @@ -2112,6 +2500,9 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsWarnEnabled) @@ -2128,6 +2519,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsWarnEnabled) @@ -2143,6 +2537,9 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsWarnEnabled) @@ -2159,6 +2556,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsWarnEnabled) @@ -2174,6 +2574,9 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsWarnEnabled) @@ -2190,6 +2593,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsWarnEnabled) @@ -2205,6 +2611,9 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsWarnEnabled) @@ -2221,6 +2630,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsWarnEnabled) @@ -2236,6 +2648,9 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsWarnEnabled) @@ -2252,6 +2667,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsWarnEnabled) @@ -2267,6 +2685,9 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsWarnEnabled) @@ -2283,6 +2704,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsWarnEnabled) @@ -2298,6 +2722,9 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsWarnEnabled) @@ -2314,6 +2741,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsWarnEnabled) @@ -2329,6 +2759,9 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsWarnEnabled) @@ -2346,6 +2779,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsWarnEnabled) @@ -2362,6 +2798,9 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsWarnEnabled) @@ -2379,6 +2818,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsWarnEnabled) @@ -2395,6 +2837,9 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsWarnEnabled) @@ -2412,6 +2857,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsWarnEnabled) @@ -2428,6 +2876,9 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Warn([Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsWarnEnabled) @@ -2445,6 +2896,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(object value) { if (IsErrorEnabled) @@ -2459,6 +2913,9 @@ public void Error(object value) /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, object value) { if (IsErrorEnabled) @@ -2475,6 +2932,9 @@ public void Error(IFormatProvider formatProvider, object value) /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) { if (IsErrorEnabled) @@ -2492,6 +2952,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) { if (IsErrorEnabled) @@ -2508,6 +2971,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsErrorEnabled) @@ -2523,6 +2989,9 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsErrorEnabled) @@ -2539,6 +3008,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsErrorEnabled) @@ -2554,6 +3026,9 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsErrorEnabled) @@ -2570,6 +3045,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsErrorEnabled) @@ -2585,6 +3063,9 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsErrorEnabled) @@ -2601,6 +3082,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsErrorEnabled) @@ -2616,6 +3100,9 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsErrorEnabled) @@ -2632,6 +3119,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsErrorEnabled) @@ -2647,6 +3137,9 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsErrorEnabled) @@ -2663,6 +3156,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsErrorEnabled) @@ -2678,6 +3174,9 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsErrorEnabled) @@ -2694,6 +3193,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsErrorEnabled) @@ -2709,6 +3211,9 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsErrorEnabled) @@ -2725,6 +3230,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsErrorEnabled) @@ -2740,6 +3248,9 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsErrorEnabled) @@ -2756,6 +3267,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsErrorEnabled) @@ -2771,6 +3285,9 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsErrorEnabled) @@ -2787,6 +3304,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsErrorEnabled) @@ -2802,6 +3322,9 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsErrorEnabled) @@ -2819,6 +3342,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsErrorEnabled) @@ -2835,6 +3361,9 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsErrorEnabled) @@ -2852,6 +3381,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsErrorEnabled) @@ -2868,6 +3400,9 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsErrorEnabled) @@ -2885,6 +3420,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsErrorEnabled) @@ -2901,6 +3439,9 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Error([Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsErrorEnabled) @@ -2918,6 +3459,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(object value) { if (IsFatalEnabled) @@ -2932,6 +3476,9 @@ public void Fatal(object value) /// An IFormatProvider that supplies culture-specific formatting information. /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, object value) { if (IsFatalEnabled) @@ -2948,6 +3495,9 @@ public void Fatal(IFormatProvider formatProvider, object value) /// Second argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) { if (IsFatalEnabled) @@ -2965,6 +3515,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message /// Third argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) { if (IsFatalEnabled) @@ -2981,6 +3534,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsFatalEnabled) @@ -2996,6 +3552,9 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsFatalEnabled) @@ -3012,6 +3571,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsFatalEnabled) @@ -3027,6 +3589,9 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsFatalEnabled) @@ -3043,6 +3608,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsFatalEnabled) @@ -3058,6 +3626,9 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsFatalEnabled) @@ -3074,6 +3645,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsFatalEnabled) @@ -3089,6 +3663,9 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, string argument) { if (IsFatalEnabled) @@ -3105,6 +3682,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsFatalEnabled) @@ -3120,6 +3700,9 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsFatalEnabled) @@ -3136,6 +3719,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsFatalEnabled) @@ -3151,6 +3737,9 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsFatalEnabled) @@ -3167,6 +3756,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsFatalEnabled) @@ -3182,6 +3774,9 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsFatalEnabled) @@ -3198,6 +3793,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsFatalEnabled) @@ -3213,6 +3811,9 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsFatalEnabled) @@ -3229,6 +3830,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsFatalEnabled) @@ -3244,6 +3848,9 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsFatalEnabled) @@ -3260,6 +3867,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsFatalEnabled) @@ -3275,6 +3885,9 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure /// The argument to format. [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object argument) { if (IsFatalEnabled) @@ -3292,6 +3905,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsFatalEnabled) @@ -3308,6 +3924,9 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsFatalEnabled) @@ -3325,6 +3944,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsFatalEnabled) @@ -3341,6 +3963,9 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsFatalEnabled) @@ -3358,6 +3983,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsFatalEnabled) @@ -3374,6 +4002,9 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] [MessageTemplateFormatMethod("message")] +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + [OverloadResolutionPriority(-1)] +#endif public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsFatalEnabled) diff --git a/src/NLog/Logger-generated.cs b/src/NLog/Logger-generated.cs index 1e3e735623..3e49b57c93 100644 --- a/src/NLog/Logger-generated.cs +++ b/src/NLog/Logger-generated.cs @@ -139,7 +139,6 @@ public void Trace(LogMessageGenerator messageFunc) if (IsTraceEnabled) { Guard.ThrowIfNull(messageFunc); - WriteToTargets(LogLevel.Trace, messageFunc()); } } @@ -185,6 +184,41 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message } } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Writes the diagnostic message at the Trace level using the specified parameters. + /// + /// A containing format items. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Trace([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.Trace); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.Trace, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } + + /// + /// Writes the diagnostic message and exception at the Trace level. + /// + /// A to be written. + /// An exception to be logged. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Trace(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.Trace); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.Trace, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } +#endif + /// /// Writes the diagnostic message and exception at the Trace level. /// @@ -377,7 +411,6 @@ public void Debug(LogMessageGenerator messageFunc) if (IsDebugEnabled) { Guard.ThrowIfNull(messageFunc); - WriteToTargets(LogLevel.Debug, messageFunc()); } } @@ -423,6 +456,41 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message } } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Writes the diagnostic message at the Debug level using the specified parameters. + /// + /// A containing format items. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Debug([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.Debug); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.Debug, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } + + /// + /// Writes the diagnostic message and exception at the Debug level. + /// + /// A to be written. + /// An exception to be logged. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Debug(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.Debug); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.Debug, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } +#endif + /// /// Writes the diagnostic message and exception at the Debug level. /// @@ -615,7 +683,6 @@ public void Info(LogMessageGenerator messageFunc) if (IsInfoEnabled) { Guard.ThrowIfNull(messageFunc); - WriteToTargets(LogLevel.Info, messageFunc()); } } @@ -661,6 +728,41 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, } } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Writes the diagnostic message at the Info level using the specified parameters. + /// + /// A containing format items. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Info([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.Info); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.Info, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } + + /// + /// Writes the diagnostic message and exception at the Info level. + /// + /// A to be written. + /// An exception to be logged. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Info(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.Info); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.Info, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } +#endif + /// /// Writes the diagnostic message and exception at the Info level. /// @@ -853,7 +955,6 @@ public void Warn(LogMessageGenerator messageFunc) if (IsWarnEnabled) { Guard.ThrowIfNull(messageFunc); - WriteToTargets(LogLevel.Warn, messageFunc()); } } @@ -899,6 +1000,41 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, } } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Writes the diagnostic message at the Warn level using the specified parameters. + /// + /// A containing format items. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Warn([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.Warn); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.Warn, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } + + /// + /// Writes the diagnostic message and exception at the Warn level. + /// + /// A to be written. + /// An exception to be logged. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Warn(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.Warn); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.Warn, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } +#endif + /// /// Writes the diagnostic message and exception at the Warn level. /// @@ -1091,7 +1227,6 @@ public void Error(LogMessageGenerator messageFunc) if (IsErrorEnabled) { Guard.ThrowIfNull(messageFunc); - WriteToTargets(LogLevel.Error, messageFunc()); } } @@ -1137,6 +1272,41 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message } } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Writes the diagnostic message at the Error level using the specified parameters. + /// + /// A containing format items. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Error([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.Error); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.Error, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } + + /// + /// Writes the diagnostic message and exception at the Error level. + /// + /// A to be written. + /// An exception to be logged. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Error(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.Error); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.Error, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } +#endif + /// /// Writes the diagnostic message and exception at the Error level. /// @@ -1329,7 +1499,6 @@ public void Fatal(LogMessageGenerator messageFunc) if (IsFatalEnabled) { Guard.ThrowIfNull(messageFunc); - WriteToTargets(LogLevel.Fatal, messageFunc()); } } @@ -1375,6 +1544,41 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message } } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Writes the diagnostic message at the Fatal level using the specified parameters. + /// + /// A containing format items. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.Fatal); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.Fatal, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } + + /// + /// Writes the diagnostic message and exception at the Fatal level. + /// + /// A to be written. + /// An exception to be logged. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Fatal(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.Fatal); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.Fatal, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } +#endif + /// /// Writes the diagnostic message and exception at the Fatal level. /// @@ -1526,4 +1730,4 @@ public void Fatal([Localizable(false)][Struc #endregion } -} +} \ No newline at end of file diff --git a/src/NLog/Logger-generated.tt b/src/NLog/Logger-generated.tt index 2744e1ec59..c679ee8507 100644 --- a/src/NLog/Logger-generated.tt +++ b/src/NLog/Logger-generated.tt @@ -45,6 +45,7 @@ namespace NLog using System; using System.ComponentModel; using JetBrains.Annotations; + using NLog.Internal; /// /// Provides logging interface and utility functions. @@ -113,11 +114,7 @@ namespace NLog { if (Is<#=level#>Enabled) { - if (messageFunc is null) - { - throw new ArgumentNullException(nameof(messageFunc)); - } - + Guard.ThrowIfNull(messageFunc); WriteToTargets(LogLevel.<#=level#>, messageFunc()); } } @@ -163,6 +160,41 @@ namespace NLog } } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Writes the diagnostic message at the <#=level#> level using the specified parameters. + /// + /// A containing format items. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.<#=level#>); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.<#=level#>, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } + + /// + /// Writes the diagnostic message and exception at the <#=level#> level. + /// + /// A to be written. + /// An exception to be logged. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void <#=level#>(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(LogLevel.<#=level#>); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(LogLevel.<#=level#>, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } +#endif + /// /// Writes the diagnostic message and exception at the <#=level#> level. /// diff --git a/src/NLog/Logger.cs b/src/NLog/Logger.cs index 7c2c1352c1..ebb495e81f 100644 --- a/src/NLog/Logger.cs +++ b/src/NLog/Logger.cs @@ -382,6 +382,44 @@ public void Log(LogLevel level, Exception exception, [Localizable(false)][Struct } } + +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Writes the diagnostic message at the specified level using the specified parameters. + /// + /// The log level. + /// A containing format items. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(level); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(level, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } + + /// + /// Writes the diagnostic message and exception at the specified level. + /// + /// The log level. + /// An exception to be logged. + /// A to be written. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Log(LogLevel level, Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(level); + if (targetsForLevel != null) + { + var logEvent = LogEventInfo.Create(level, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); + WriteToTargets(logEvent, targetsForLevel); + } + } +#endif + /// /// Writes the diagnostic message and exception at the specified level. /// diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index d2f819716f..daa6a3669d 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -1,6 +1,8 @@ + 17.0 + net35;net46;netstandard2.0 net35;net46;netstandard2.0;netstandard2.1 NLog for .NET Framework and .NET Standard @@ -11,8 +13,7 @@ NLog supports traditional logging, structured logging and the combination of bot Supported platforms: - .NET 5, 6, 7, 8 and 9 -- .NET Core 1, 2 and 3 -- .NET Standard 1.3+ and 2.0+ +- .NET Standard 2.0 and 2.1 - .NET Framework 3.5 - 4.8 - Xamarin Android + iOS (.NET Standard) - Mono 4 @@ -65,6 +66,7 @@ For all config options and platform support, check https://nlog-project.org/conf true true true + 13 diff --git a/tests/NLog.UnitTests/Fluent/LogEventBuilderTests.cs b/tests/NLog.UnitTests/Fluent/LogEventBuilderTests.cs index d08617e7ee..2efbfbf6a1 100644 --- a/tests/NLog.UnitTests/Fluent/LogEventBuilderTests.cs +++ b/tests/NLog.UnitTests/Fluent/LogEventBuilderTests.cs @@ -311,7 +311,7 @@ public void LogBuilder_null_lead_to_ArgumentNullException() Assert.Throws(() => new LogEventBuilder(logger, null)); var logBuilder = new LogEventBuilder(logger); - Assert.Throws(() => logBuilder.Properties(null)); + Assert.Throws(() => logBuilder.Properties((KeyValuePair[])null)); Assert.Throws(() => logBuilder.Property(null, "b")); } From 36943d33444140afb889a12c9aa25172f6ef4bb6 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 4 May 2025 10:12:36 +0200 Subject: [PATCH 105/224] MessageTemplate structs marked as readonly when not NetFramework (#5802) --- .../Internal/SortHelpers.cs | 16 +++++++--- .../NetworkSenders/HttpNetworkSender.cs | 2 +- src/NLog/Config/ConfigurationItemFactory.cs | 4 +-- .../Config/LoggingConfigurationFileLoader.cs | 2 +- src/NLog/Config/LoggingConfigurationParser.cs | 2 +- .../Config/ServiceRepositoryExtensions.cs | 11 ++++--- src/NLog/Config/ServiceRepositoryInternal.cs | 8 ++--- .../Internal/DictionaryEntryEnumerable.cs | 12 +++++-- .../Internal/LogMessageTemplateFormatter.cs | 17 +++++----- src/NLog/Internal/ObjectReflectionCache.cs | 18 +++++++++-- src/NLog/Internal/SortHelpers.cs | 16 +++++++--- src/NLog/LogFactory.cs | 31 +++++++------------ src/NLog/MessageTemplates/Hole.cs | 6 +++- src/NLog/MessageTemplates/Literal.cs | 6 +++- src/NLog/MessageTemplates/LiteralHole.cs | 18 +++++++++-- .../SetupSerializationBuilderExtensions.cs | 2 +- .../Targets/Wrappers/AsyncTargetWrapper.cs | 8 +++-- .../MessageTemplates/ParserTests.cs | 16 +++++----- .../MessageTemplates/RendererTests.cs | 2 +- 19 files changed, 123 insertions(+), 74 deletions(-) diff --git a/src/NLog.Targets.ConcurrentFile/Internal/SortHelpers.cs b/src/NLog.Targets.ConcurrentFile/Internal/SortHelpers.cs index 7c7d7a6c5c..46f45e278a 100644 --- a/src/NLog.Targets.ConcurrentFile/Internal/SortHelpers.cs +++ b/src/NLog.Targets.ConcurrentFile/Internal/SortHelpers.cs @@ -136,11 +136,19 @@ private static Dictionary> CreateBucketDictionaryWithValue /// The type of the key. /// The type of the value. - public struct ReadOnlySingleBucketDictionary : IDictionary + public +#if !NETFRAMEWORK + readonly +#endif + struct ReadOnlySingleBucketDictionary : IDictionary { - KeyValuePair? _singleBucket; // Not readonly to avoid struct-copy, and to avoid VerificationException when medium-trust AppDomain - readonly Dictionary _multiBucket; - readonly IEqualityComparer _comparer; + private +#if !NETFRAMEWORK + readonly +#endif + KeyValuePair? _singleBucket; // Not readonly to avoid struct-copy, and to avoid VerificationException when medium-trust AppDomain + private readonly Dictionary _multiBucket; + private readonly IEqualityComparer _comparer; public IEqualityComparer Comparer => _comparer; public ReadOnlySingleBucketDictionary(KeyValuePair singleBucket) diff --git a/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs index d61aff27f4..382832a0d8 100644 --- a/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs @@ -81,7 +81,7 @@ protected override void BeginRequest(NetworkRequestArgs eventArgs) { if (SslCertificateOverride.Count > 0) httpWebRequest.ClientCertificates = SslCertificateOverride; -#if NET45_OR_GREATER || NETSTANDARD +#if NET45_OR_GREATER || !NETFRAMEWORK httpWebRequest.ServerCertificateValidationCallback = UserCertificateValidationCallback; #endif } diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index 86dbffd358..5f33e713bd 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -287,7 +287,7 @@ public IJsonConverter JsonConverter public bool? ParseMessageTemplates { get => _serviceRepository.ResolveParseMessageTemplates(); - set => _serviceRepository.ParseMessageTemplates(value); + set => _serviceRepository.ParseMessageTemplates(LogManager.LogFactory, value); } /// @@ -484,7 +484,7 @@ private void RegisterAllTargets(bool skipCheckExists) SafeRegisterNamedType(_targets, "database", "NLog.Targets.DatabaseTarget, NLog.Database", skipCheckExists); SafeRegisterNamedType(_targets, "atomfile", "NLog.Targets.AtomicFileTarget, NLog.Targets.AtomicFile", skipCheckExists); SafeRegisterNamedType(_targets, "atomicfile", "NLog.Targets.AtomicFileTarget, NLog.Targets.AtomicFile", skipCheckExists); -#if NETSTANDARD +#if !NETFRAMEWORK SafeRegisterNamedType(_targets, "eventlog", "NLog.Targets.EventLogTarget, NLog.WindowsEventLog", skipCheckExists); #endif SafeRegisterNamedType(_targets, "impersonatingwrapper", "NLog.Targets.Wrappers.ImpersonatingTargetWrapper, NLog.WindowsIdentity", skipCheckExists); diff --git a/src/NLog/Config/LoggingConfigurationFileLoader.cs b/src/NLog/Config/LoggingConfigurationFileLoader.cs index 4371af8e1d..bece0d27cd 100644 --- a/src/NLog/Config/LoggingConfigurationFileLoader.cs +++ b/src/NLog/Config/LoggingConfigurationFileLoader.cs @@ -287,7 +287,7 @@ public IEnumerable GetAppSpecificNLogLocations(string baseDirectory, str yield return Path.ChangeExtension(configurationFile.Replace(vshostSubStr, "."), ".nlog"); } } -#if NETSTANDARD +#if !NETFRAMEWORK else { if (string.IsNullOrEmpty(entryAssemblyLocation)) diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index 1c4198aff2..b866e84f9f 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -197,7 +197,7 @@ private void SetNLogElementSettings(ILoggingConfigurationElement nlogConfig) ConfigurationItemFactory.Default.AssemblyLoader.ScanForAutoLoadExtensions(ConfigurationItemFactory.Default); } - _serviceRepository.ParseMessageTemplates(parseMessageTemplates); + _serviceRepository.ParseMessageTemplates(LogFactory, parseMessageTemplates); } /// diff --git a/src/NLog/Config/ServiceRepositoryExtensions.cs b/src/NLog/Config/ServiceRepositoryExtensions.cs index 4093c223e6..8560cc8a55 100644 --- a/src/NLog/Config/ServiceRepositoryExtensions.cs +++ b/src/NLog/Config/ServiceRepositoryExtensions.cs @@ -170,12 +170,12 @@ internal static ServiceRepository RegisterObjectTypeTransformer(this ServiceRepo return serviceRepository; } - internal static ServiceRepository ParseMessageTemplates(this ServiceRepository serviceRepository, bool? enable) + internal static ServiceRepository ParseMessageTemplates(this ServiceRepository serviceRepository, LogFactory logFactory, bool? enable) { if (enable == true) { NLog.Common.InternalLogger.Debug("Message Template Format always enabled"); - serviceRepository.RegisterSingleton(new LogMessageTemplateFormatter(serviceRepository, true, false)); + serviceRepository.RegisterSingleton(new LogMessageTemplateFormatter(logFactory, true, false)); } else if (enable == false) { @@ -186,7 +186,7 @@ internal static ServiceRepository ParseMessageTemplates(this ServiceRepository s { //null = auto NLog.Common.InternalLogger.Debug("Message Template Auto Format enabled"); - serviceRepository.RegisterSingleton(new LogMessageTemplateFormatter(serviceRepository, false, false)); + serviceRepository.RegisterSingleton(new LogMessageTemplateFormatter(logFactory, false, false)); } return serviceRepository; } @@ -197,10 +197,11 @@ internal static ServiceRepository ParseMessageTemplates(this ServiceRepository s return messageFormatter?.EnableMessageTemplateParser; } - internal static ServiceRepository RegisterDefaults(this ServiceRepository serviceRepository) + internal static ServiceRepository RegisterDefaults(this ServiceRepository serviceRepository, LogFactory logFactory) { + // Maybe also include active TimeSource ? Could also be done with LogFactory extension-methods serviceRepository.RegisterSingleton(serviceRepository); - serviceRepository.RegisterSingleton(new LogMessageTemplateFormatter(serviceRepository, false, false)); + serviceRepository.RegisterSingleton(new LogMessageTemplateFormatter(logFactory, false, false)); serviceRepository.RegisterJsonConverter(new DefaultJsonSerializer(serviceRepository)); serviceRepository.RegisterValueFormatter(new MessageTemplates.ValueFormatter(serviceRepository, legacyStringQuotes: false)); serviceRepository.RegisterPropertyTypeConverter(PropertyTypeConverter.Instance); diff --git a/src/NLog/Config/ServiceRepositoryInternal.cs b/src/NLog/Config/ServiceRepositoryInternal.cs index 07204a4fa9..1c2e47f056 100644 --- a/src/NLog/Config/ServiceRepositoryInternal.cs +++ b/src/NLog/Config/ServiceRepositoryInternal.cs @@ -49,13 +49,9 @@ internal sealed class ServiceRepositoryInternal : ServiceRepository /// /// Initializes a new instance of the class. /// - internal ServiceRepositoryInternal(bool resetGlobalCache = false) + internal ServiceRepositoryInternal(LogFactory logFactory) { - if (resetGlobalCache) - ConfigurationItemFactory.Default = null; //build new global factory - - this.RegisterDefaults(); - // Maybe also include active TimeSource ? Could also be done with LogFactory extension-methods + this.RegisterDefaults(logFactory); } public override void RegisterService(Type type, object instance) diff --git a/src/NLog/Internal/DictionaryEntryEnumerable.cs b/src/NLog/Internal/DictionaryEntryEnumerable.cs index 7cb32a79ca..76581b926d 100644 --- a/src/NLog/Internal/DictionaryEntryEnumerable.cs +++ b/src/NLog/Internal/DictionaryEntryEnumerable.cs @@ -40,7 +40,11 @@ namespace NLog.Internal /// /// Ensures that IDictionary.GetEnumerator returns DictionaryEntry values /// - internal struct DictionaryEntryEnumerable : IEnumerable + internal +#if !NETFRAMEWORK + readonly +#endif + struct DictionaryEntryEnumerable : IEnumerable { private readonly IDictionary _dictionary; @@ -64,7 +68,11 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } - internal struct DictionaryEntryEnumerator : IEnumerator + internal +#if !NETFRAMEWORK + readonly +#endif + struct DictionaryEntryEnumerator : IEnumerator { private readonly IDictionaryEnumerator _entryEnumerator; diff --git a/src/NLog/Internal/LogMessageTemplateFormatter.cs b/src/NLog/Internal/LogMessageTemplateFormatter.cs index ed810fca58..b3ab982bbe 100644 --- a/src/NLog/Internal/LogMessageTemplateFormatter.cs +++ b/src/NLog/Internal/LogMessageTemplateFormatter.cs @@ -44,9 +44,9 @@ namespace NLog.Internal internal sealed class LogMessageTemplateFormatter : ILogMessageFormatter { private static readonly StringBuilderPool _builderPool = new StringBuilderPool(Environment.ProcessorCount * 2); - private readonly IServiceProvider _serviceProvider; + private readonly LogFactory _logFactory; - private IValueFormatter ValueFormatter => _valueFormatter ?? (_valueFormatter = _serviceProvider.GetService()); + private IValueFormatter ValueFormatter => _valueFormatter ?? (_valueFormatter = _logFactory.ServiceRepository.GetService()); private IValueFormatter _valueFormatter; /// @@ -58,12 +58,13 @@ internal sealed class LogMessageTemplateFormatter : ILogMessageFormatter /// /// New formatter /// - /// + /// /// When true: Do not fallback to StringBuilder.Format for positional templates /// - public LogMessageTemplateFormatter([NotNull] IServiceProvider serviceProvider, bool forceTemplateRenderer, bool singleTargetOnly) + /// + public LogMessageTemplateFormatter([NotNull] LogFactory logFactory, bool forceTemplateRenderer, bool singleTargetOnly) { - _serviceProvider = serviceProvider; + _logFactory = logFactory; _forceTemplateRenderer = forceTemplateRenderer; _singleTargetOnly = singleTargetOnly; MessageFormatter = FormatMessage; @@ -86,7 +87,7 @@ public bool HasProperties(LogEventInfo logEvent) { // Perform quick check for valid message template parameter names (No support for rewind if mixed message-template) TemplateEnumerator holeEnumerator = new TemplateEnumerator(logEvent.Message); - if (holeEnumerator.MoveNext() && holeEnumerator.Current.MaybePositionalTemplate) + if (!holeEnumerator.MoveNext() || holeEnumerator.Current.MaybePositionalTemplate) { return false; // Skip allocation of PropertiesDictionary } @@ -99,7 +100,7 @@ public void AppendFormattedMessage(LogEventInfo logEvent, StringBuilder builder) { if (_singleTargetOnly) { - Render(logEvent.Message, logEvent.FormatProvider ?? CultureInfo.CurrentCulture, logEvent.Parameters, builder, out _); + Render(logEvent.Message, logEvent.FormatProvider ?? _logFactory.DefaultCultureInfo ?? CultureInfo.CurrentCulture, logEvent.Parameters, builder, out _); } else { @@ -123,7 +124,7 @@ public string FormatMessage(LogEventInfo logEvent) private void AppendToBuilder(LogEventInfo logEvent, StringBuilder builder) { - Render(logEvent.Message, logEvent.FormatProvider ?? CultureInfo.CurrentCulture, logEvent.Parameters, builder, out var messageTemplateParameterList); + Render(logEvent.Message, logEvent.FormatProvider ?? _logFactory.DefaultCultureInfo ?? CultureInfo.CurrentCulture, logEvent.Parameters, builder, out var messageTemplateParameterList); logEvent.CreateOrUpdatePropertiesInternal(false, messageTemplateParameterList ?? ArrayHelper.Empty()); } diff --git a/src/NLog/Internal/ObjectReflectionCache.cs b/src/NLog/Internal/ObjectReflectionCache.cs index 418454da8c..c3ae725b28 100644 --- a/src/NLog/Internal/ObjectReflectionCache.cs +++ b/src/NLog/Internal/ObjectReflectionCache.cs @@ -271,7 +271,11 @@ private static FastPropertyLookup[] BuildFastLookup(PropertyInfo[] properties, b return fastLookup; } - internal struct ObjectPropertyList : IEnumerable + internal +#if !NETFRAMEWORK + readonly +#endif + struct ObjectPropertyList : IEnumerable { internal static readonly StringComparer NameComparer = StringComparer.Ordinal; private static readonly FastPropertyLookup[] CreateIDictionaryEnumerator = new[] { new FastPropertyLookup(string.Empty, TypeCode.Object, (o, p) => ((IDictionary)o).GetEnumerator()) }; @@ -279,7 +283,11 @@ internal struct ObjectPropertyList : IEnumerable> CreateBucketDictionaryWithValue /// The type of the key. /// The type of the value. - public struct ReadOnlySingleBucketDictionary : IDictionary + public +#if !NETFRAMEWORK + readonly +#endif + struct ReadOnlySingleBucketDictionary : IDictionary { - KeyValuePair? _singleBucket; // Not readonly to avoid struct-copy, and to avoid VerificationException when medium-trust AppDomain - readonly Dictionary _multiBucket; - readonly IEqualityComparer _comparer; + private +#if !NETFRAMEWORK + readonly +#endif + KeyValuePair? _singleBucket; // Not readonly to avoid struct-copy, and to avoid VerificationException when medium-trust AppDomain + private readonly Dictionary _multiBucket; + private readonly IEqualityComparer _comparer; public IEqualityComparer Comparer => _comparer; public ReadOnlySingleBucketDictionary(KeyValuePair singleBucket) diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 3250613ae3..2eddd782f6 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -64,7 +64,7 @@ public class LogFactory : IDisposable /// internal readonly object _syncRoot = new object(); private readonly LoggerCache _loggerCache = new LoggerCache(); - [NotNull] private ServiceRepositoryInternal _serviceRepository = new ServiceRepositoryInternal(); + [NotNull] private ServiceRepositoryInternal _serviceRepository; private IAppEnvironment _currentAppEnvironment; internal LoggingConfiguration _config; internal LogMessageFormatter ActiveMessageFormatter; @@ -122,8 +122,7 @@ private static event EventHandler LoggerShutdown public LogFactory() : this(new LoggingConfigurationFileLoader(DefaultAppEnvironment)) { - _serviceRepository.TypeRegistered += ServiceRepository_TypeRegistered; - RefreshMessageFormatter(); + } /// @@ -150,6 +149,9 @@ internal LogFactory(ILoggingConfigurationLoader configLoader, IAppEnvironment ap _configLoader = configLoader; _currentAppEnvironment = appEnvironment; LoggerShutdown += OnStopLogging; + _serviceRepository = new ServiceRepositoryInternal(this); + _serviceRepository.TypeRegistered += ServiceRepository_TypeRegistered; + RefreshMessageFormatter(); } internal static IAppEnvironment DefaultAppEnvironment @@ -166,6 +168,12 @@ internal IAppEnvironment CurrentAppEnvironment set => _currentAppEnvironment = value; } + /// + /// Repository of interfaces used by NLog to allow override for dependency injection + /// + [NotNull] + public ServiceRepository ServiceRepository => _serviceRepository; + /// /// Gets or sets a value indicating whether exceptions should be thrown. See also . /// @@ -281,21 +289,6 @@ private void ActivateLoggingConfiguration(LoggingConfiguration config) InternalLogger.Info("Configuration initialized."); } - /// - /// Repository of interfaces used by NLog to allow override for dependency injection - /// - [NotNull] - public ServiceRepository ServiceRepository - { - get => _serviceRepository; - internal set - { - _serviceRepository.TypeRegistered -= ServiceRepository_TypeRegistered; - _serviceRepository = (value as ServiceRepositoryInternal) ?? new ServiceRepositoryInternal(true); - _serviceRepository.TypeRegistered += ServiceRepository_TypeRegistered; - } - } - private void ServiceRepository_TypeRegistered(object sender, ServiceRepositoryUpdateEventArgs e) { _config?.CheckForMissingServiceTypes(e.ServiceType); @@ -312,7 +305,7 @@ private void RefreshMessageFormatter() ActiveMessageFormatter = messageFormatter.FormatMessage; if (messageFormatter is LogMessageTemplateFormatter templateFormatter) { - SingleTargetMessageFormatter = new LogMessageTemplateFormatter(_serviceRepository, templateFormatter.EnableMessageTemplateParser == true, true).FormatMessage; + SingleTargetMessageFormatter = new LogMessageTemplateFormatter(this, templateFormatter.EnableMessageTemplateParser == true, true).FormatMessage; } else { diff --git a/src/NLog/MessageTemplates/Hole.cs b/src/NLog/MessageTemplates/Hole.cs index 64c2c123d8..12e606e9a9 100644 --- a/src/NLog/MessageTemplates/Hole.cs +++ b/src/NLog/MessageTemplates/Hole.cs @@ -36,7 +36,11 @@ namespace NLog.MessageTemplates /// /// A hole that will be replaced with a value /// - internal struct Hole + internal +#if !NETFRAMEWORK + readonly +#endif + struct Hole { /// /// Constructor diff --git a/src/NLog/MessageTemplates/Literal.cs b/src/NLog/MessageTemplates/Literal.cs index 84deda65e8..c14380df55 100644 --- a/src/NLog/MessageTemplates/Literal.cs +++ b/src/NLog/MessageTemplates/Literal.cs @@ -36,7 +36,11 @@ namespace NLog.MessageTemplates /// /// A fixed value /// - internal struct Literal + internal +#if !NETFRAMEWORK + readonly +#endif + struct Literal { /// Number of characters from the original template to copy at the current position. /// This can be 0 when the template starts with a hole or when there are multiple consecutive holes. diff --git a/src/NLog/MessageTemplates/LiteralHole.cs b/src/NLog/MessageTemplates/LiteralHole.cs index ccc72a2b46..ae396e99a5 100644 --- a/src/NLog/MessageTemplates/LiteralHole.cs +++ b/src/NLog/MessageTemplates/LiteralHole.cs @@ -36,13 +36,25 @@ namespace NLog.MessageTemplates /// /// Combines Literal and Hole /// - internal struct LiteralHole + internal +#if !NETFRAMEWORK + readonly +#endif + struct LiteralHole { /// Literal - public Literal Literal; // Not readonly to avoid struct-copy, and to avoid VerificationException when medium-trust AppDomain + public +#if !NETFRAMEWORK + readonly +#endif + Literal Literal; // Not readonly to avoid struct-copy, and to avoid VerificationException when medium-trust AppDomain /// Hole /// Uninitialized when = 0. - public Hole Hole; // Not readonly to avoid struct-copy, and to avoid VerificationException when medium-trust AppDomain + public +#if !NETFRAMEWORK + readonly +#endif + Hole Hole; // Not readonly to avoid struct-copy, and to avoid VerificationException when medium-trust AppDomain public LiteralHole(Literal literal, Hole hole) { diff --git a/src/NLog/SetupSerializationBuilderExtensions.cs b/src/NLog/SetupSerializationBuilderExtensions.cs index 8c06395fa3..9f15efbad7 100644 --- a/src/NLog/SetupSerializationBuilderExtensions.cs +++ b/src/NLog/SetupSerializationBuilderExtensions.cs @@ -50,7 +50,7 @@ public static class SetupSerializationBuilderExtensions /// public static ISetupSerializationBuilder ParseMessageTemplates(this ISetupSerializationBuilder setupBuilder, bool? enable) { - setupBuilder.LogFactory.ServiceRepository.ParseMessageTemplates(enable); + setupBuilder.LogFactory.ServiceRepository.ParseMessageTemplates(setupBuilder.LogFactory, enable); return setupBuilder; } diff --git a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs index 2f717fa9fa..74a5d4ba74 100644 --- a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs @@ -125,15 +125,17 @@ public AsyncTargetWrapper(Target wrappedTarget, int queueLimit, AsyncTargetWrapp { Name = string.IsNullOrEmpty(wrappedTarget?.Name) ? Name : (wrappedTarget.Name + "_wrapper"); WrappedTarget = wrappedTarget; -#if NETSTANDARD2_0 + +#if NETFRAMEWORK + _requestQueue = new AsyncRequestQueue(10000, AsyncTargetWrapperOverflowAction.Discard); +#else // NetStandard20 includes many optimizations for ConcurrentQueue: // - See: https://blogs.msdn.microsoft.com/dotnet/2017/06/07/performance-improvements-in-net-core/ // Net40 ConcurrencyQueue can seem to leak, because it doesn't clear properly on dequeue // - See: https://blogs.msdn.microsoft.com/pfxteam/2012/05/08/concurrentqueuet-holding-on-to-a-few-dequeued-elements/ _requestQueue = new ConcurrentRequestQueue(10000, AsyncTargetWrapperOverflowAction.Discard); -#else - _requestQueue = new AsyncRequestQueue(10000, AsyncTargetWrapperOverflowAction.Discard); #endif + QueueLimit = queueLimit; OverflowAction = overflowAction; } diff --git a/tests/NLog.UnitTests/MessageTemplates/ParserTests.cs b/tests/NLog.UnitTests/MessageTemplates/ParserTests.cs index be23dc9280..c55838f642 100644 --- a/tests/NLog.UnitTests/MessageTemplates/ParserTests.cs +++ b/tests/NLog.UnitTests/MessageTemplates/ParserTests.cs @@ -80,7 +80,7 @@ public class ParserTests public void ParseAndPrint(string input, int parameterCount) { var logEventInfo = new LogEventInfo(LogLevel.Info, "Logger", null, input, ManyParameters); - logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory.ServiceRepository, true, false).MessageFormatter, null); + logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory, true, false).MessageFormatter, null); var templateAuto = logEventInfo.MessageTemplateParameters; Assert.Equal(parameterCount, templateAuto.Count); } @@ -98,7 +98,7 @@ public void ParseAndPrint(string input, int parameterCount) public void ParsePositional(string input, int index, string format) { var logEventInfo = new LogEventInfo(LogLevel.Info, "Logger", null, input, ManyParameters); - logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory.ServiceRepository, true, false).MessageFormatter, null); + logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory, true, false).MessageFormatter, null); var template = logEventInfo.MessageTemplateParameters; Assert.True(template.IsPositional); @@ -117,7 +117,7 @@ public void ParsePositional(string input, int index, string format) public void ParseNominal(string input) { var logEventInfo = new LogEventInfo(LogLevel.Info, "Logger", null, input, ManyParameters); - logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory.ServiceRepository, true, false).MessageFormatter, null); + logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory, true, false).MessageFormatter, null); var template = logEventInfo.MessageTemplateParameters; Assert.False(template.IsPositional); @@ -137,7 +137,7 @@ public void ParseNominal(string input) public void ParseName(string input, string name) { var logEventInfo = new LogEventInfo(LogLevel.Info, "Logger", null, input, ManyParameters); - logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory.ServiceRepository, true, false).MessageFormatter, null); + logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory, true, false).MessageFormatter, null); var template = logEventInfo.MessageTemplateParameters; Assert.Equal(1, template.Count); @@ -157,7 +157,7 @@ public void ParseName(string input, string name) public void ParseHoleType(string input, string holeType) { var logEventInfo = new LogEventInfo(LogLevel.Info, "Logger", null, input, ManyParameters); - logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory.ServiceRepository, true, false).MessageFormatter, null); + logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory, true, false).MessageFormatter, null); var template = logEventInfo.MessageTemplateParameters; Assert.Equal(1, template.Count); @@ -179,7 +179,7 @@ public void ParseHoleType(string input, string holeType) public void ParseFormatAndAlignment_numeric(string input, int? alignment, string format) { var logEventInfo = new LogEventInfo(LogLevel.Info, "Logger", null, input, ManyParameters); - logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory.ServiceRepository, true, false).MessageFormatter, null); + logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory, true, false).MessageFormatter, null); var template = logEventInfo.MessageTemplateParameters; Assert.Equal(1, template.Count); @@ -198,7 +198,7 @@ public void ParseFormatAndAlignment_numeric(string input, int? alignment, string public void ParseFormatAndAlignment_text(string input, int? alignment, string format) { var logEventInfo = new LogEventInfo(LogLevel.Info, "Logger", null, input, ManyParameters); - logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory.ServiceRepository, true, false).MessageFormatter, null); + logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory, true, false).MessageFormatter, null); var template = logEventInfo.MessageTemplateParameters; Assert.Equal(1, template.Count); @@ -238,7 +238,7 @@ public void ThrowsTemplateParserException(string input) Assert.Throws(() => { var logEventInfo = new LogEventInfo(LogLevel.Info, "Logger", null, input, ManyParameters); - logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory.ServiceRepository, true, false).MessageFormatter, null); + logEventInfo.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory, true, false).MessageFormatter, null); var template = logEventInfo.MessageTemplateParameters; }); } diff --git a/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs b/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs index 2c039f93f9..ee89787d84 100644 --- a/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs +++ b/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs @@ -130,7 +130,7 @@ public void RenderDateTimeOffset(string input, string arg, string expected) private static void RenderAndTest(string input, CultureInfo culture, object[] args, string expected) { var logEventInfoAlways = new LogEventInfo(LogLevel.Info, "Logger", culture, input, args); - logEventInfoAlways.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory.ServiceRepository, true, false).MessageFormatter, null); + logEventInfoAlways.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory, true, false).MessageFormatter, null); var templateAlways = logEventInfoAlways.MessageTemplateParameters; Assert.Equal(expected, logEventInfoAlways.FormattedMessage); } From 336e58466e6854c385dc6bd8af3ef20400b54c75 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 4 May 2025 18:06:07 +0200 Subject: [PATCH 106/224] Logger skip parameters-array allocation when properties (#5804) --- src/NLog/ILoggerExtensions.cs | 12 +- .../Internal/LogMessageTemplateFormatter.cs | 135 ++++++++--- src/NLog/LogEventInfo.cs | 22 +- src/NLog/LogFactory.cs | 6 +- src/NLog/Logger-V1Compat.cs | 152 +++++++++++- src/NLog/Logger-generated.cs | 218 ++++++++++++++---- src/NLog/Logger-generated.tt | 36 ++- src/NLog/Logger.cs | 182 +++++++++++---- src/NLog/MessageTemplates/LiteralHole.cs | 2 +- .../MessageTemplates/TemplateEnumerator.cs | 2 + 10 files changed, 610 insertions(+), 157 deletions(-) diff --git a/src/NLog/ILoggerExtensions.cs b/src/NLog/ILoggerExtensions.cs index 927fe13e17..038878dc4b 100644 --- a/src/NLog/ILoggerExtensions.cs +++ b/src/NLog/ILoggerExtensions.cs @@ -252,7 +252,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, [L { if (logger.IsDebugEnabled) { - logger.Debug(message, new object[] { argument }); + logger.Debug(message, argument); } } @@ -271,7 +271,7 @@ public static void ConditionalDebug([NotNull] this ILogg { if (logger.IsDebugEnabled) { - logger.Debug(message, new object[] { argument1, argument2 }); + logger.Debug(message, argument1, argument2); } } @@ -292,7 +292,7 @@ public static void ConditionalDebug([NotNull { if (logger.IsDebugEnabled) { - logger.Debug(message, new object[] { argument1, argument2, argument3 }); + logger.Debug(message, argument1, argument2, argument3); } } @@ -419,7 +419,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, [L { if (logger.IsTraceEnabled) { - logger.Trace(message, new object[] { argument }); + logger.Trace(message, argument); } } @@ -438,7 +438,7 @@ public static void ConditionalTrace([NotNull] this ILogg { if (logger.IsTraceEnabled) { - logger.Trace(message, new object[] { argument1, argument2 }); + logger.Trace(message, argument1, argument2); } } @@ -459,7 +459,7 @@ public static void ConditionalTrace([NotNull { if (logger.IsTraceEnabled) { - logger.Trace(message, new object[] { argument1, argument2, argument3 }); + logger.Trace(message, argument1, argument2, argument3); } } diff --git a/src/NLog/Internal/LogMessageTemplateFormatter.cs b/src/NLog/Internal/LogMessageTemplateFormatter.cs index b3ab982bbe..5e280e0f96 100644 --- a/src/NLog/Internal/LogMessageTemplateFormatter.cs +++ b/src/NLog/Internal/LogMessageTemplateFormatter.cs @@ -52,20 +52,20 @@ internal sealed class LogMessageTemplateFormatter : ILogMessageFormatter /// /// When true: Do not fallback to StringBuilder.Format for positional templates /// - private readonly bool _forceTemplateRenderer; + private readonly bool _forceMessageTemplateRenderer; private readonly bool _singleTargetOnly; /// /// New formatter /// /// - /// When true: Do not fallback to StringBuilder.Format for positional templates + /// When true: Do not fallback to StringBuilder.Format for positional templates /// /// - public LogMessageTemplateFormatter([NotNull] LogFactory logFactory, bool forceTemplateRenderer, bool singleTargetOnly) + public LogMessageTemplateFormatter([NotNull] LogFactory logFactory, bool forceMessageTemplateRenderer, bool singleTargetOnly) { _logFactory = logFactory; - _forceTemplateRenderer = forceTemplateRenderer; + _forceMessageTemplateRenderer = forceMessageTemplateRenderer; _singleTargetOnly = singleTargetOnly; MessageFormatter = FormatMessage; } @@ -75,7 +75,7 @@ public LogMessageTemplateFormatter([NotNull] LogFactory logFactory, bool forceTe /// public LogMessageFormatter MessageFormatter { get; } - public bool? EnableMessageTemplateParser => _forceTemplateRenderer ? true : default(bool?); + public bool? EnableMessageTemplateParser => _forceMessageTemplateRenderer ? true : default(bool?); /// public bool HasProperties(LogEventInfo logEvent) @@ -138,39 +138,103 @@ private void AppendToBuilder(LogEventInfo logEvent, StringBuilder builder) /// Parameters for the holes. private void Render(string template, IFormatProvider formatProvider, object[] parameters, StringBuilder sb, out IList messageTemplateParameters) { + messageTemplateParameters = null; + + TemplateEnumerator templateEnumerator = new TemplateEnumerator(template); + if (!templateEnumerator.MoveNext() || (templateEnumerator.Current.MaybePositionalTemplate && !_forceMessageTemplateRenderer)) + { + // string.Format when not message-template for structured logging + sb.AppendFormat(formatProvider, template, parameters); + return; + } + + // Handle message-template-format or string-format or mixed-format int pos = 0; int holeIndex = 0; int holeStartPosition = 0; - messageTemplateParameters = null; int originalLength = sb.Length; - TemplateEnumerator templateEnumerator = new TemplateEnumerator(template); - while (templateEnumerator.MoveNext()) + do { - if (holeIndex == 0 && !_forceTemplateRenderer && templateEnumerator.Current.MaybePositionalTemplate && sb.Length == originalLength) - { - // Not a structured template - sb.AppendFormat(formatProvider, template, parameters); - return; - } - var literal = templateEnumerator.Current.Literal; sb.Append(template, pos, literal.Print); pos += literal.Print; if (literal.Skip == 0) { pos++; + continue; + } + + pos += literal.Skip; + var hole = templateEnumerator.Current.Hole; + if (hole.Alignment != 0) + holeStartPosition = sb.Length; + if (hole.Index != -1 && messageTemplateParameters is null) + { + holeIndex++; + RenderHolePositional(sb, hole, formatProvider, parameters[hole.Index]); } else { + var holeParameter = parameters[holeIndex]; + if (messageTemplateParameters is null) + { + messageTemplateParameters = new MessageTemplateParameter[parameters.Length]; + if (holeIndex != 0) + { + // rewind and try again + templateEnumerator = new TemplateEnumerator(template); + sb.Length = originalLength; + holeIndex = 0; + pos = 0; + continue; + } + } + messageTemplateParameters[holeIndex++] = new MessageTemplateParameter(hole.Name, holeParameter, hole.Format, hole.CaptureType); + RenderHole(sb, hole, formatProvider, holeParameter); + } + if (hole.Alignment != 0) + RenderPadding(sb, hole.Alignment, holeStartPosition); + } while (templateEnumerator.MoveNext()); + + messageTemplateParameters = VerifyMessageTemplateParameters(messageTemplateParameters, holeIndex); + } + +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + internal string Render(ref TemplateEnumerator templateEnumerator, IFormatProvider formatProvider, in ReadOnlySpan parameters, out IList messageTemplateParameters) + { + // Handle message-template-format or string-format or mixed-format + messageTemplateParameters = null; + + using (var builder = _builderPool.Acquire()) + { + var sb = builder.Item; + + string template = templateEnumerator.Template; + int pos = 0; + int holeStartPosition = 0; + int holeIndex = 0; + + do + { + var literal = templateEnumerator.Current.Literal; + sb.Append(template, pos, literal.Print); + pos += literal.Print; + if (literal.Skip == 0) + { + pos++; + continue; + } + pos += literal.Skip; var hole = templateEnumerator.Current.Hole; + if (hole.Alignment != 0) holeStartPosition = sb.Length; if (hole.Index != -1 && messageTemplateParameters is null) { holeIndex++; - RenderHole(sb, hole, formatProvider, parameters[hole.Index], true); + RenderHolePositional(sb, hole, formatProvider, parameters[hole.Index]); } else { @@ -182,7 +246,7 @@ private void Render(string template, IFormatProvider formatProvider, object[] pa { // rewind and try again templateEnumerator = new TemplateEnumerator(template); - sb.Length = originalLength; + sb.ClearBuilder(); holeIndex = 0; pos = 0; continue; @@ -193,9 +257,16 @@ private void Render(string template, IFormatProvider formatProvider, object[] pa } if (hole.Alignment != 0) RenderPadding(sb, hole.Alignment, holeStartPosition); - } + } while (templateEnumerator.MoveNext()); + + messageTemplateParameters = VerifyMessageTemplateParameters(messageTemplateParameters, holeIndex); + return sb.ToString(); } + } +#endif + private static IList VerifyMessageTemplateParameters(IList messageTemplateParameters, int holeIndex) + { if (messageTemplateParameters != null && holeIndex != messageTemplateParameters.Count) { var truncateParameters = new MessageTemplateParameter[holeIndex]; @@ -203,24 +274,36 @@ private void Render(string template, IFormatProvider formatProvider, object[] pa truncateParameters[i] = messageTemplateParameters[i]; messageTemplateParameters = truncateParameters; } - } - private void RenderHole(StringBuilder sb, Hole hole, IFormatProvider formatProvider, object value, bool legacy = false) - { - RenderHole(sb, hole.CaptureType, hole.Format, formatProvider, value, legacy); + return messageTemplateParameters; } - private void RenderHole(StringBuilder sb, CaptureType captureType, string holeFormat, IFormatProvider formatProvider, object value, bool legacy = false) + private void RenderHolePositional(StringBuilder sb, in Hole hole, IFormatProvider formatProvider, object value) { if (value is null) { sb.Append("NULL"); - return; } + else if (hole.CaptureType == CaptureType.Normal) + { + MessageTemplates.ValueFormatter.FormatToString(value, hole.Format, formatProvider, sb); + } + else + { + RenderHole(sb, hole.CaptureType, hole.Format, formatProvider, value); + } + } - if (captureType == CaptureType.Normal && legacy) + private void RenderHole(StringBuilder sb, in Hole hole, IFormatProvider formatProvider, object value) + { + RenderHole(sb, hole.CaptureType, hole.Format, formatProvider, value); + } + + private void RenderHole(StringBuilder sb, CaptureType captureType, string holeFormat, IFormatProvider formatProvider, object value) + { + if (value is null) { - MessageTemplates.ValueFormatter.FormatToString(value, holeFormat, formatProvider, sb); + sb.Append("NULL"); } else { diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index f9415b3d3b..4029a691b7 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -614,16 +614,29 @@ private static bool NeedToPreformatMessage(object[] parameters) // we need to preformat message if it contains any parameters which could possibly // do logging in their ToString() if (parameters is null || parameters.Length == 0) - { return false; - } if (parameters.Length > 5) + return true; // too many parameters, too costly to check + + foreach (var parameter in parameters) { - // too many parameters, too costly to check - return true; + if (!IsSafeToDeferFormatting(parameter)) + return true; } + return false; + } + +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + internal static bool NeedToPreformatMessage(in ReadOnlySpan parameters) + { + if (parameters.IsEmpty) + return false; + + if (parameters.Length > 5) + return true; // too many parameters, too costly to check + foreach (var parameter in parameters) { if (!IsSafeToDeferFormatting(parameter)) @@ -632,6 +645,7 @@ private static bool NeedToPreformatMessage(object[] parameters) return false; } +#endif private static bool IsSafeToDeferFormatting(object value) { diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 2eddd782f6..2da1ca3220 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -69,6 +69,7 @@ public class LogFactory : IDisposable internal LoggingConfiguration _config; internal LogMessageFormatter ActiveMessageFormatter; internal LogMessageFormatter SingleTargetMessageFormatter; + internal LogMessageTemplateFormatter AutoMessageTemplateFormatter; private LogLevel _globalThreshold = LogLevel.MinLevel; private bool _configLoaded; private int _supendLoggingCounter; @@ -305,11 +306,14 @@ private void RefreshMessageFormatter() ActiveMessageFormatter = messageFormatter.FormatMessage; if (messageFormatter is LogMessageTemplateFormatter templateFormatter) { - SingleTargetMessageFormatter = new LogMessageTemplateFormatter(this, templateFormatter.EnableMessageTemplateParser == true, true).FormatMessage; + var logMessageTemplateFormatter = new LogMessageTemplateFormatter(this, templateFormatter.EnableMessageTemplateParser == true, true); + SingleTargetMessageFormatter = logMessageTemplateFormatter.FormatMessage; + AutoMessageTemplateFormatter = templateFormatter.EnableMessageTemplateParser == true ? null : logMessageTemplateFormatter; } else { SingleTargetMessageFormatter = null; + AutoMessageTemplateFormatter = null; } } diff --git a/src/NLog/Logger-V1Compat.cs b/src/NLog/Logger-V1Compat.cs index bbc347ca08..278d6ca1a4 100644 --- a/src/NLog/Logger-V1Compat.cs +++ b/src/NLog/Logger-V1Compat.cs @@ -95,10 +95,18 @@ public void Log(LogLevel level, IFormatProvider formatProvider, object value) #endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, arg1, arg2); + } +#else if (IsEnabled(level)) { WriteToTargets(level, message, new[] { arg1, arg2 }); } +#endif } /// @@ -116,10 +124,18 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); + } +#else if (IsEnabled(level)) { WriteToTargets(level, message, new[] { arg1, arg2, arg3 }); } +#endif } /// @@ -487,10 +503,18 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #endif public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, null, formatProvider, message, argument); + } +#else if (IsEnabled(level)) { WriteToTargets(level, formatProvider, message, new object[] { argument }); } +#endif } /// @@ -506,10 +530,18 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal #endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object argument) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, argument); + } +#else if (IsEnabled(level)) { WriteToTargets(level, message, new object[] { argument }); } +#endif } /// @@ -635,7 +667,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] } } - #endregion +#endregion #region Trace() overloads @@ -687,7 +719,11 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, arg1, arg2); +#else WriteToTargets(LogLevel.Trace, message, new[] { arg1, arg2 }); +#endif } } @@ -707,7 +743,11 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); +#else WriteToTargets(LogLevel.Trace, message, new[] { arg1, arg2, arg3 }); +#endif } } @@ -1059,7 +1099,11 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure { if (IsTraceEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Trace, null, formatProvider, message, argument); +#else WriteToTargets(LogLevel.Trace, formatProvider, message, new object[] { argument }); +#endif } } @@ -1077,7 +1121,11 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, argument); +#else WriteToTargets(LogLevel.Trace, message, new object[] { argument }); +#endif } } @@ -1250,7 +1298,11 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, arg1, arg2); +#else WriteToTargets(LogLevel.Debug, message, new[] { arg1, arg2 }); +#endif } } @@ -1270,7 +1322,11 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); +#else WriteToTargets(LogLevel.Debug, message, new[] { arg1, arg2, arg3 }); +#endif } } @@ -1622,7 +1678,11 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, formatProvider, message, new object[] { argument }); +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Debug, null, formatProvider, message, argument); +#else + WriteToTargets(LogLevel.Debug, formatProvider, message, new[] { argument }); +#endif } } @@ -1640,7 +1700,11 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, argument); +#else + WriteToTargets(LogLevel.Debug, message, new[] { argument }); +#endif } } @@ -1761,7 +1825,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message } } - #endregion +#endregion #region Info() overloads @@ -1813,7 +1877,11 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, arg1, arg2); +#else WriteToTargets(LogLevel.Info, message, new[] { arg1, arg2 }); +#endif } } @@ -1833,7 +1901,11 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); +#else WriteToTargets(LogLevel.Info, message, new[] { arg1, arg2, arg3 }); +#endif } } @@ -2185,7 +2257,11 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, formatProvider, message, new object[] { argument }); +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Info, null, formatProvider, message, argument); +#else + WriteToTargets(LogLevel.Info, formatProvider, message, new[] { argument }); +#endif } } @@ -2203,7 +2279,11 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object[] { argument }); +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, argument); +#else + WriteToTargets(LogLevel.Info, message, new[] { argument }); +#endif } } @@ -2376,7 +2456,11 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, arg1, arg2); +#else WriteToTargets(LogLevel.Warn, message, new[] { arg1, arg2 }); +#endif } } @@ -2396,7 +2480,11 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); +#else WriteToTargets(LogLevel.Warn, message, new[] { arg1, arg2, arg3 }); +#endif } } @@ -2748,7 +2836,11 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, formatProvider, message, new object[] { argument }); +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Warn, null, formatProvider, message, argument); +#else + WriteToTargets(LogLevel.Warn, formatProvider, message, new[] { argument }); +#endif } } @@ -2766,7 +2858,11 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, argument); +#else + WriteToTargets(LogLevel.Warn, message, new[] { argument }); +#endif } } @@ -2939,7 +3035,11 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, arg1, arg2); +#else WriteToTargets(LogLevel.Error, message, new[] { arg1, arg2 }); +#endif } } @@ -2959,7 +3059,11 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); +#else WriteToTargets(LogLevel.Error, message, new[] { arg1, arg2, arg3 }); +#endif } } @@ -3311,7 +3415,11 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, formatProvider, message, new object[] { argument }); +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Error, null, formatProvider, message, argument); +#else + WriteToTargets(LogLevel.Error, formatProvider, message, new[] { argument }); +#endif } } @@ -3329,7 +3437,11 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object[] { argument }); +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, argument); +#else + WriteToTargets(LogLevel.Error, message, new[] { argument }); +#endif } } @@ -3502,7 +3614,11 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, arg1, arg2); +#else WriteToTargets(LogLevel.Fatal, message, new[] { arg1, arg2 }); +#endif } } @@ -3522,7 +3638,11 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); +#else WriteToTargets(LogLevel.Fatal, message, new[] { arg1, arg2, arg3 }); +#endif } } @@ -3874,7 +3994,11 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, formatProvider, message, new object[] { argument }); +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Fatal, null, formatProvider, message, argument); +#else + WriteToTargets(LogLevel.Fatal, formatProvider, message, new[] { argument }); +#endif } } @@ -3892,7 +4016,11 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, argument); +#else + WriteToTargets(LogLevel.Fatal, message, new[] { argument }); +#endif } } diff --git a/src/NLog/Logger-generated.cs b/src/NLog/Logger-generated.cs index 3e49b57c93..a7d28fb266 100644 --- a/src/NLog/Logger-generated.cs +++ b/src/NLog/Logger-generated.cs @@ -193,11 +193,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message [MessageTemplateFormatMethod("message")] public void Trace([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.Trace); - if (targetsForLevel != null) + if (IsTraceEnabled) { - var logEvent = LogEventInfo.Create(LogLevel.Trace, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, args); } } @@ -210,11 +208,9 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message [MessageTemplateFormatMethod("message")] public void Trace(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.Trace); - if (targetsForLevel != null) + if (IsTraceEnabled) { - var logEvent = LogEventInfo.Create(LogLevel.Trace, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargetsWithSpan(LogLevel.Trace, exception, Factory.DefaultCultureInfo, message, args); } } #endif @@ -275,7 +271,11 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false) { if (IsTraceEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Trace, null, formatProvider, message, argument); +#else WriteToTargets(LogLevel.Trace, formatProvider, message, new object[] { argument }); +#endif } } @@ -290,7 +290,11 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] str { if (IsTraceEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, argument); +#else WriteToTargets(LogLevel.Trace, message, new object[] { argument }); +#endif } } @@ -308,7 +312,11 @@ public void Trace(IFormatProvider formatProvider, [Local { if (IsTraceEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Trace, null, formatProvider, message, argument1, argument2); +#else WriteToTargets(LogLevel.Trace, formatProvider, message, new object[] { argument1, argument2 }); +#endif } } @@ -325,7 +333,11 @@ public void Trace([Localizable(false)][StructuredMessage { if (IsTraceEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, argument1, argument2); +#else WriteToTargets(LogLevel.Trace, message, new object[] { argument1, argument2 }); +#endif } } @@ -345,7 +357,11 @@ public void Trace(IFormatProvider formatProv { if (IsTraceEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Trace, null, formatProvider, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.Trace, formatProvider, message, new object[] { argument1, argument2, argument3 }); +#endif } } @@ -364,7 +380,11 @@ public void Trace([Localizable(false)][Struc { if (IsTraceEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.Trace, message, new object[] { argument1, argument2, argument3 }); +#endif } } @@ -465,11 +485,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message [MessageTemplateFormatMethod("message")] public void Debug([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.Debug); - if (targetsForLevel != null) + if (IsDebugEnabled) { - var logEvent = LogEventInfo.Create(LogLevel.Debug, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, args); } } @@ -482,11 +500,9 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message [MessageTemplateFormatMethod("message")] public void Debug(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.Debug); - if (targetsForLevel != null) + if (IsDebugEnabled) { - var logEvent = LogEventInfo.Create(LogLevel.Debug, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargetsWithSpan(LogLevel.Debug, exception, Factory.DefaultCultureInfo, message, args); } } #endif @@ -547,7 +563,11 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false) { if (IsDebugEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Debug, null, formatProvider, message, argument); +#else WriteToTargets(LogLevel.Debug, formatProvider, message, new object[] { argument }); +#endif } } @@ -562,7 +582,11 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] str { if (IsDebugEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, argument); +#else WriteToTargets(LogLevel.Debug, message, new object[] { argument }); +#endif } } @@ -580,7 +604,11 @@ public void Debug(IFormatProvider formatProvider, [Local { if (IsDebugEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Debug, null, formatProvider, message, argument1, argument2); +#else WriteToTargets(LogLevel.Debug, formatProvider, message, new object[] { argument1, argument2 }); +#endif } } @@ -597,7 +625,11 @@ public void Debug([Localizable(false)][StructuredMessage { if (IsDebugEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, argument1, argument2); +#else WriteToTargets(LogLevel.Debug, message, new object[] { argument1, argument2 }); +#endif } } @@ -617,7 +649,11 @@ public void Debug(IFormatProvider formatProv { if (IsDebugEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Debug, null, formatProvider, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.Debug, formatProvider, message, new object[] { argument1, argument2, argument3 }); +#endif } } @@ -636,7 +672,11 @@ public void Debug([Localizable(false)][Struc { if (IsDebugEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.Debug, message, new object[] { argument1, argument2, argument3 }); +#endif } } @@ -737,11 +777,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, [MessageTemplateFormatMethod("message")] public void Info([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.Info); - if (targetsForLevel != null) + if (IsInfoEnabled) { - var logEvent = LogEventInfo.Create(LogLevel.Info, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, args); } } @@ -754,11 +792,9 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, [MessageTemplateFormatMethod("message")] public void Info(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.Info); - if (targetsForLevel != null) + if (IsInfoEnabled) { - var logEvent = LogEventInfo.Create(LogLevel.Info, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargetsWithSpan(LogLevel.Info, exception, Factory.DefaultCultureInfo, message, args); } } #endif @@ -819,7 +855,11 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)] { if (IsInfoEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Info, null, formatProvider, message, argument); +#else WriteToTargets(LogLevel.Info, formatProvider, message, new object[] { argument }); +#endif } } @@ -834,7 +874,11 @@ public void Info([Localizable(false)][StructuredMessageTemplate] stri { if (IsInfoEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, argument); +#else WriteToTargets(LogLevel.Info, message, new object[] { argument }); +#endif } } @@ -852,7 +896,11 @@ public void Info(IFormatProvider formatProvider, [Locali { if (IsInfoEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Info, null, formatProvider, message, argument1, argument2); +#else WriteToTargets(LogLevel.Info, formatProvider, message, new object[] { argument1, argument2 }); +#endif } } @@ -869,7 +917,11 @@ public void Info([Localizable(false)][StructuredMessageT { if (IsInfoEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, argument1, argument2); +#else WriteToTargets(LogLevel.Info, message, new object[] { argument1, argument2 }); +#endif } } @@ -889,7 +941,11 @@ public void Info(IFormatProvider formatProvi { if (IsInfoEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Info, null, formatProvider, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.Info, formatProvider, message, new object[] { argument1, argument2, argument3 }); +#endif } } @@ -908,7 +964,11 @@ public void Info([Localizable(false)][Struct { if (IsInfoEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.Info, message, new object[] { argument1, argument2, argument3 }); +#endif } } @@ -1009,11 +1069,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, [MessageTemplateFormatMethod("message")] public void Warn([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.Warn); - if (targetsForLevel != null) + if (IsWarnEnabled) { - var logEvent = LogEventInfo.Create(LogLevel.Warn, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, args); } } @@ -1026,11 +1084,9 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, [MessageTemplateFormatMethod("message")] public void Warn(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.Warn); - if (targetsForLevel != null) + if (IsWarnEnabled) { - var logEvent = LogEventInfo.Create(LogLevel.Warn, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargetsWithSpan(LogLevel.Warn, exception, Factory.DefaultCultureInfo, message, args); } } #endif @@ -1091,7 +1147,11 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)] { if (IsWarnEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Warn, null, formatProvider, message, argument); +#else WriteToTargets(LogLevel.Warn, formatProvider, message, new object[] { argument }); +#endif } } @@ -1106,7 +1166,11 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] stri { if (IsWarnEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, argument); +#else WriteToTargets(LogLevel.Warn, message, new object[] { argument }); +#endif } } @@ -1124,7 +1188,11 @@ public void Warn(IFormatProvider formatProvider, [Locali { if (IsWarnEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Warn, null, formatProvider, message, argument1, argument2); +#else WriteToTargets(LogLevel.Warn, formatProvider, message, new object[] { argument1, argument2 }); +#endif } } @@ -1141,7 +1209,11 @@ public void Warn([Localizable(false)][StructuredMessageT { if (IsWarnEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, argument1, argument2); +#else WriteToTargets(LogLevel.Warn, message, new object[] { argument1, argument2 }); +#endif } } @@ -1161,7 +1233,11 @@ public void Warn(IFormatProvider formatProvi { if (IsWarnEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Warn, null, formatProvider, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.Warn, formatProvider, message, new object[] { argument1, argument2, argument3 }); +#endif } } @@ -1180,7 +1256,11 @@ public void Warn([Localizable(false)][Struct { if (IsWarnEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.Warn, message, new object[] { argument1, argument2, argument3 }); +#endif } } @@ -1281,11 +1361,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message [MessageTemplateFormatMethod("message")] public void Error([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.Error); - if (targetsForLevel != null) + if (IsErrorEnabled) { - var logEvent = LogEventInfo.Create(LogLevel.Error, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, args); } } @@ -1298,11 +1376,9 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message [MessageTemplateFormatMethod("message")] public void Error(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.Error); - if (targetsForLevel != null) + if (IsErrorEnabled) { - var logEvent = LogEventInfo.Create(LogLevel.Error, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargetsWithSpan(LogLevel.Error, exception, Factory.DefaultCultureInfo, message, args); } } #endif @@ -1363,7 +1439,11 @@ public void Error(IFormatProvider formatProvider, [Localizable(false) { if (IsErrorEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Error, null, formatProvider, message, argument); +#else WriteToTargets(LogLevel.Error, formatProvider, message, new object[] { argument }); +#endif } } @@ -1378,7 +1458,11 @@ public void Error([Localizable(false)][StructuredMessageTemplate] str { if (IsErrorEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, argument); +#else WriteToTargets(LogLevel.Error, message, new object[] { argument }); +#endif } } @@ -1396,7 +1480,11 @@ public void Error(IFormatProvider formatProvider, [Local { if (IsErrorEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Error, null, formatProvider, message, argument1, argument2); +#else WriteToTargets(LogLevel.Error, formatProvider, message, new object[] { argument1, argument2 }); +#endif } } @@ -1413,7 +1501,11 @@ public void Error([Localizable(false)][StructuredMessage { if (IsErrorEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, argument1, argument2); +#else WriteToTargets(LogLevel.Error, message, new object[] { argument1, argument2 }); +#endif } } @@ -1433,7 +1525,11 @@ public void Error(IFormatProvider formatProv { if (IsErrorEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Error, null, formatProvider, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.Error, formatProvider, message, new object[] { argument1, argument2, argument3 }); +#endif } } @@ -1452,7 +1548,11 @@ public void Error([Localizable(false)][Struc { if (IsErrorEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.Error, message, new object[] { argument1, argument2, argument3 }); +#endif } } @@ -1553,11 +1653,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message [MessageTemplateFormatMethod("message")] public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.Fatal); - if (targetsForLevel != null) + if (IsFatalEnabled) { - var logEvent = LogEventInfo.Create(LogLevel.Fatal, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, args); } } @@ -1570,11 +1668,9 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message [MessageTemplateFormatMethod("message")] public void Fatal(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.Fatal); - if (targetsForLevel != null) + if (IsFatalEnabled) { - var logEvent = LogEventInfo.Create(LogLevel.Fatal, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargetsWithSpan(LogLevel.Fatal, exception, Factory.DefaultCultureInfo, message, args); } } #endif @@ -1635,7 +1731,11 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false) { if (IsFatalEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Fatal, null, formatProvider, message, argument); +#else WriteToTargets(LogLevel.Fatal, formatProvider, message, new object[] { argument }); +#endif } } @@ -1650,7 +1750,11 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] str { if (IsFatalEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, argument); +#else WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); +#endif } } @@ -1668,7 +1772,11 @@ public void Fatal(IFormatProvider formatProvider, [Local { if (IsFatalEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Fatal, null, formatProvider, message, argument1, argument2); +#else WriteToTargets(LogLevel.Fatal, formatProvider, message, new object[] { argument1, argument2 }); +#endif } } @@ -1685,7 +1793,11 @@ public void Fatal([Localizable(false)][StructuredMessage { if (IsFatalEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, argument1, argument2); +#else WriteToTargets(LogLevel.Fatal, message, new object[] { argument1, argument2 }); +#endif } } @@ -1705,7 +1817,11 @@ public void Fatal(IFormatProvider formatProv { if (IsFatalEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Fatal, null, formatProvider, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.Fatal, formatProvider, message, new object[] { argument1, argument2, argument3 }); +#endif } } @@ -1724,10 +1840,14 @@ public void Fatal([Localizable(false)][Struc { if (IsFatalEnabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.Fatal, message, new object[] { argument1, argument2, argument3 }); +#endif } } #endregion } -} \ No newline at end of file +} diff --git a/src/NLog/Logger-generated.tt b/src/NLog/Logger-generated.tt index c679ee8507..aa69b206ba 100644 --- a/src/NLog/Logger-generated.tt +++ b/src/NLog/Logger-generated.tt @@ -169,11 +169,9 @@ namespace NLog [MessageTemplateFormatMethod("message")] public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.<#=level#>); - if (targetsForLevel != null) + if (Is<#=level#>Enabled) { - var logEvent = LogEventInfo.Create(LogLevel.<#=level#>, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargets(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, args); } } @@ -186,11 +184,9 @@ namespace NLog [MessageTemplateFormatMethod("message")] public void <#=level#>(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { - var targetsForLevel = GetTargetsForLevel(LogLevel.<#=level#>); - if (targetsForLevel != null) + if (Is<#=level#>Enabled) { - var logEvent = LogEventInfo.Create(LogLevel.<#=level#>, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); + WriteToTargets(LogLevel.<#=level#>, exception, Factory.DefaultCultureInfo, message, args); } } #endif @@ -251,7 +247,11 @@ namespace NLog { if (Is<#=level#>Enabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargets(LogLevel.<#=level#>, null, formatProvider, message, argument); +#else WriteToTargets(LogLevel.<#=level#>, formatProvider, message, new object[] { argument }); +#endif } } @@ -266,7 +266,11 @@ namespace NLog { if (Is<#=level#>Enabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargets(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, argument); +#else WriteToTargets(LogLevel.<#=level#>, message, new object[] { argument }); +#endif } } @@ -284,7 +288,11 @@ namespace NLog { if (Is<#=level#>Enabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargets(LogLevel.<#=level#>, null, formatProvider, message, argument1, argument2); +#else WriteToTargets(LogLevel.<#=level#>, formatProvider, message, new object[] { argument1, argument2 }); +#endif } } @@ -301,7 +309,11 @@ namespace NLog { if (Is<#=level#>Enabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargets(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, argument1, argument2); +#else WriteToTargets(LogLevel.<#=level#>, message, new object[] { argument1, argument2 }); +#endif } } @@ -321,7 +333,11 @@ namespace NLog { if (Is<#=level#>Enabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargets(LogLevel.<#=level#>, null, formatProvider, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.<#=level#>, formatProvider, message, new object[] { argument1, argument2, argument3 }); +#endif } } @@ -340,7 +356,11 @@ namespace NLog { if (Is<#=level#>Enabled) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + WriteToTargets(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); +#else WriteToTargets(LogLevel.<#=level#>, message, new object[] { argument1, argument2, argument3 }); +#endif } } diff --git a/src/NLog/Logger.cs b/src/NLog/Logger.cs index ebb495e81f..82aa6be296 100644 --- a/src/NLog/Logger.cs +++ b/src/NLog/Logger.cs @@ -251,7 +251,7 @@ public void Log(LogEventInfo logEvent) logEvent.LoggerName = Name; if (logEvent.FormatProvider is null) logEvent.FormatProvider = Factory.DefaultCultureInfo; - WriteToTargets(logEvent, targetsForLevel); + WriteLogEventToTargets(logEvent, targetsForLevel); } } @@ -269,7 +269,7 @@ public void Log(Type wrapperType, LogEventInfo logEvent) logEvent.LoggerName = Name; if (logEvent.FormatProvider is null) logEvent.FormatProvider = Factory.DefaultCultureInfo; - WriteToTargets(wrapperType, logEvent, targetsForLevel); + WriteLogEventToTargets(wrapperType, logEvent, targetsForLevel); } } @@ -382,44 +382,6 @@ public void Log(LogLevel level, Exception exception, [Localizable(false)][Struct } } - -#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER - /// - /// Writes the diagnostic message at the specified level using the specified parameters. - /// - /// The log level. - /// A containing format items. - /// Arguments to format. - [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) - { - var targetsForLevel = GetTargetsForLevel(level); - if (targetsForLevel != null) - { - var logEvent = LogEventInfo.Create(level, Name, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); - } - } - - /// - /// Writes the diagnostic message and exception at the specified level. - /// - /// The log level. - /// An exception to be logged. - /// A to be written. - /// Arguments to format. - [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) - { - var targetsForLevel = GetTargetsForLevel(level); - if (targetsForLevel != null) - { - var logEvent = LogEventInfo.Create(level, Name, exception, Factory.DefaultCultureInfo, message, args.IsEmpty ? null : args.ToArray()); - WriteToTargets(logEvent, targetsForLevel); - } - } -#endif - /// /// Writes the diagnostic message and exception at the specified level. /// @@ -448,10 +410,18 @@ public void Log(LogLevel level, Exception exception, IFormatProvider formatProvi [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, null, formatProvider, message, argument); + } +#else if (IsEnabled(level)) { WriteToTargets(level, formatProvider, message, new object[] { argument }); } +#endif } /// @@ -464,10 +434,18 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Loca [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, argument); + } +#else if (IsEnabled(level)) { WriteToTargets(level, message, new object[] { argument }); } +#endif } /// @@ -483,10 +461,18 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessag [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, null, formatProvider, message, argument1, argument2); + } +#else if (IsEnabled(level)) { WriteToTargets(level, formatProvider, message, new object[] { argument1, argument2 }); } +#endif } /// @@ -501,10 +487,18 @@ public void Log(LogLevel level, IFormatProvider formatPr [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, argument1, argument2); + } +#else if (IsEnabled(level)) { WriteToTargets(level, message, new object[] { argument1, argument2 }); } +#endif } /// @@ -522,10 +516,18 @@ public void Log(LogLevel level, [Localizable(false)][Str [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, null, formatProvider, message, argument1, argument2, argument3); + } +#else if (IsEnabled(level)) { WriteToTargets(level, formatProvider, message, new object[] { argument1, argument2, argument3 }); } +#endif } /// @@ -542,12 +544,95 @@ public void Log(LogLevel level, IFormatProvi [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); + } +#else if (IsEnabled(level)) { WriteToTargets(level, message, new object[] { argument1, argument2, argument3 }); } +#endif + } + +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Writes the diagnostic message at the specified level using the specified parameters. + /// + /// The log level. + /// A containing format items. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, args); + } } + /// + /// Writes the diagnostic message and exception at the specified level. + /// + /// The log level. + /// An exception to be logged. + /// A to be written. + /// Arguments to format. + [MessageTemplateFormatMethod("message")] + public void Log(LogLevel level, Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, exception, Factory.DefaultCultureInfo, message, args); + } + } + + private void WriteToTargetsWithSpan(LogLevel level, Exception exception, IFormatProvider formatProvider, string message, params ReadOnlySpan args) + { + var targetsForLevel = GetTargetsForLevel(level); + if (targetsForLevel != null) + { + WriteToTargetsWithSpan(targetsForLevel, level, exception, formatProvider, message, args); + } + } + + private void WriteToTargetsWithSpan(ITargetWithFilterChain targetsForLevel, LogLevel level, Exception exception, IFormatProvider formatProvider, string message, params ReadOnlySpan args) + { + if (Factory.AutoMessageTemplateFormatter is null || !LogEventInfo.NeedToPreformatMessage(args)) + { + // Deferred message formatting and capturing of Properties + var logEvent = LogEventInfo.Create(level, Name, exception, formatProvider, message, args.IsEmpty ? null : args.ToArray()); + WriteLogEventToTargets(logEvent, targetsForLevel); + } + else + { + // Pre-format upfront and skip parameter-object[]-array-allocation when possible + var templateEnumerator = new MessageTemplates.TemplateEnumerator(message); + if (templateEnumerator.MoveNext() && !templateEnumerator.Current.MaybePositionalTemplate) + { + // Convert parameters into Properties and skip Parameters-array-allocation (Like with Microsoft Extension Logging) + var formattedMessage = Factory.AutoMessageTemplateFormatter.Render(ref templateEnumerator, formatProvider, in args, out var messageTemplateParameters); + var logEvent = new LogEventInfo(level, Name, formattedMessage, message, messageTemplateParameters); + logEvent.Exception = exception; + WriteLogEventToTargets(logEvent, targetsForLevel); + } + else + { + // Pre-format using string.Format, and provide Parameters-array for Targets that fallback to Parameters-array when no Properties + var logEvent = LogEventInfo.Create(level, Name, exception, formatProvider, message, args.ToArray()); + logEvent.MessageFormatter = LogMessageStringFormatter.Default.MessageFormatter; // Force string.Format since confirmed + WriteLogEventToTargets(logEvent, targetsForLevel); + } + } + } +#endif + #endregion + private LogEventInfo PrepareLogEventInfo(LogEventInfo logEvent) { if (_contextProperties != null) @@ -563,9 +648,6 @@ private LogEventInfo PrepareLogEventInfo(LogEventInfo logEvent) return logEvent; } - #endregion - - /// /// Runs the provided action. If the action throws, the exception is logged at Error level. The exception is not propagated outside of this method. /// @@ -718,7 +800,7 @@ private void WriteToTargets(LogLevel level, IFormatProvider formatProvider, stri if (targetsForLevel != null) { var logEvent = LogEventInfo.Create(level, Name, formatProvider, message, args); - WriteToTargets(logEvent, targetsForLevel); + WriteLogEventToTargets(logEvent, targetsForLevel); } } @@ -730,7 +812,7 @@ private void WriteToTargets(LogLevel level, string message) // please note that this overload calls the overload of LogEventInfo.Create with object[] parameter on purpose - // to avoid unnecessary string.Format (in case of calling Create(LogLevel, string, IFormatProvider, object)) var logEvent = LogEventInfo.Create(level, Name, Factory.DefaultCultureInfo, message, (object[])null); - WriteToTargets(logEvent, targetsForLevel); + WriteLogEventToTargets(logEvent, targetsForLevel); } } @@ -740,7 +822,7 @@ private void WriteToTargets(LogLevel level, IFormatProvider formatProvider, T if (targetsForLevel != null) { var logEvent = LogEventInfo.Create(level, Name, formatProvider, value); - WriteToTargets(logEvent, targetsForLevel); + WriteLogEventToTargets(logEvent, targetsForLevel); } } @@ -753,7 +835,7 @@ private void WriteToTargets(LogLevel level, Exception ex, string message, object var logEvent = message is null && ex != null && !(args?.Length > 0) ? LogEventInfo.Create(level, Name, ExceptionMessageFormatProvider.Instance, ex) : LogEventInfo.Create(level, Name, ex, Factory.DefaultCultureInfo, message, args); - WriteToTargets(logEvent, targetsForLevel); + WriteLogEventToTargets(logEvent, targetsForLevel); } } @@ -763,11 +845,11 @@ private void WriteToTargets(LogLevel level, Exception ex, IFormatProvider format if (targetsForLevel != null) { var logEvent = LogEventInfo.Create(level, Name, ex, formatProvider, message, args); - WriteToTargets(logEvent, targetsForLevel); + WriteLogEventToTargets(logEvent, targetsForLevel); } } - private void WriteToTargets([NotNull] LogEventInfo logEvent, [NotNull] ITargetWithFilterChain targetsForLevel) + private void WriteLogEventToTargets([NotNull] LogEventInfo logEvent, [NotNull] ITargetWithFilterChain targetsForLevel) { try { @@ -787,7 +869,7 @@ private void WriteToTargets([NotNull] LogEventInfo logEvent, [NotNull] ITargetWi } } - private void WriteToTargets(Type wrapperType, [NotNull] LogEventInfo logEvent, [NotNull] ITargetWithFilterChain targetsForLevel) + private void WriteLogEventToTargets(Type wrapperType, [NotNull] LogEventInfo logEvent, [NotNull] ITargetWithFilterChain targetsForLevel) { try { diff --git a/src/NLog/MessageTemplates/LiteralHole.cs b/src/NLog/MessageTemplates/LiteralHole.cs index ae396e99a5..7a86bb5bc1 100644 --- a/src/NLog/MessageTemplates/LiteralHole.cs +++ b/src/NLog/MessageTemplates/LiteralHole.cs @@ -56,7 +56,7 @@ struct LiteralHole #endif Hole Hole; // Not readonly to avoid struct-copy, and to avoid VerificationException when medium-trust AppDomain - public LiteralHole(Literal literal, Hole hole) + public LiteralHole(in Literal literal, in Hole hole) { Literal = literal; Hole = hole; diff --git a/src/NLog/MessageTemplates/TemplateEnumerator.cs b/src/NLog/MessageTemplates/TemplateEnumerator.cs index 895185e2ac..82318d1f98 100644 --- a/src/NLog/MessageTemplates/TemplateEnumerator.cs +++ b/src/NLog/MessageTemplates/TemplateEnumerator.cs @@ -54,6 +54,8 @@ internal struct TemplateEnumerator : IEnumerator private LiteralHole _current; private const short Zero = 0; + public string Template => _template; + /// /// Parse a template. /// From 2ab5f7b2da0d02cd26d1cfa9c2d384eb0b4af9b5 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 8 May 2025 07:09:06 +0200 Subject: [PATCH 107/224] ILogger with nullable references (#5805) --- src/NLog/ILogger-V1.cs | 228 +++++------ src/NLog/ILogger.cs | 178 +++++---- src/NLog/ILoggerBase-V1.cs | 40 +- src/NLog/ILoggerBase.cs | 30 +- src/NLog/ILoggerExtensions.cs | 62 +-- src/NLog/ISuppress.cs | 10 +- .../Internal/LogMessageTemplateFormatter.cs | 2 +- src/NLog/Internal/MemberNotNullAttribute.cs | 171 ++++++++ .../AllEventPropertiesLayoutRenderer.cs | 2 +- src/NLog/Layouts/JSON/JsonLayout.cs | 2 +- src/NLog/Layouts/XML/XmlElementBase.cs | 2 +- src/NLog/LogEventBuilder.cs | 62 +-- src/NLog/LogEventInfo.cs | 149 +++---- src/NLog/Logger-Conditional.cs | 120 +++--- src/NLog/Logger-V1Compat.cs | 376 +++++++++--------- src/NLog/Logger-generated.cs | 256 ++++++------ src/NLog/Logger-generated.tt | 60 +-- src/NLog/Logger.cs | 107 ++--- src/NLog/NLog.csproj | 1 + src/NLog/Targets/TargetWithContext.cs | 2 +- tests/NLog.UnitTests/LoggerTests.cs | 13 +- 21 files changed, 1041 insertions(+), 832 deletions(-) create mode 100644 src/NLog/Internal/MemberNotNullAttribute.cs diff --git a/src/NLog/ILogger-V1.cs b/src/NLog/ILogger-V1.cs index fb86392277..17a7095514 100644 --- a/src/NLog/ILogger-V1.cs +++ b/src/NLog/ILogger-V1.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -54,7 +56,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(object value); + void Trace(object? value); /// /// Writes the diagnostic message at the Trace level. @@ -65,7 +67,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, object value); + void Trace(IFormatProvider? formatProvider, object? value); /// /// Writes the diagnostic message at the Trace level using the specified parameters. @@ -78,7 +80,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); + void Trace([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2); /// /// Writes the diagnostic message at the Trace level using the specified parameters. @@ -92,7 +94,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); + void Trace([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter and formatting it with the supplied format provider. @@ -105,7 +107,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter. @@ -130,7 +132,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter. @@ -155,7 +157,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter. @@ -180,7 +182,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter. @@ -192,7 +194,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace([Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Trace([Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter and formatting it with the supplied format provider. @@ -205,7 +207,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter. @@ -230,7 +232,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter. @@ -255,7 +257,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter. @@ -280,7 +282,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter. @@ -305,7 +307,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter. @@ -330,7 +332,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter. @@ -342,7 +344,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace([Localizable(false)][StructuredMessageTemplate] string message, object argument); + void Trace([Localizable(false)][StructuredMessageTemplate] string message, object? argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter and formatting it with the supplied format provider. @@ -355,7 +357,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter. @@ -380,7 +382,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter. @@ -405,7 +407,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// /// Writes the diagnostic message at the Trace level using the specified value as a parameter. @@ -431,7 +433,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(object value); + void Debug(object? value); /// /// Writes the diagnostic message at the Debug level. @@ -442,7 +444,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, object value); + void Debug(IFormatProvider? formatProvider, object? value); /// /// Writes the diagnostic message at the Debug level using the specified parameters. @@ -455,7 +457,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); + void Debug([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2); /// /// Writes the diagnostic message at the Debug level using the specified parameters. @@ -469,7 +471,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); + void Debug([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter and formatting it with the supplied format provider. @@ -482,7 +484,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter. @@ -507,7 +509,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter. @@ -532,7 +534,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter. @@ -557,7 +559,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter. @@ -569,7 +571,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug([Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Debug([Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter and formatting it with the supplied format provider. @@ -582,7 +584,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter. @@ -607,7 +609,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter. @@ -632,7 +634,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter. @@ -657,7 +659,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter. @@ -682,7 +684,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter. @@ -707,7 +709,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter. @@ -719,7 +721,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug([Localizable(false)][StructuredMessageTemplate] string message, object argument); + void Debug([Localizable(false)][StructuredMessageTemplate] string message, object? argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter and formatting it with the supplied format provider. @@ -732,7 +734,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter. @@ -757,7 +759,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter. @@ -782,7 +784,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// /// Writes the diagnostic message at the Debug level using the specified value as a parameter. @@ -808,7 +810,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(object value); + void Info(object? value); /// /// Writes the diagnostic message at the Info level. @@ -819,7 +821,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, object value); + void Info(IFormatProvider? formatProvider, object? value); /// /// Writes the diagnostic message at the Info level using the specified parameters. @@ -832,7 +834,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); + void Info([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2); /// /// Writes the diagnostic message at the Info level using the specified parameters. @@ -846,7 +848,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); + void Info([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter and formatting it with the supplied format provider. @@ -859,7 +861,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter. @@ -884,7 +886,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter. @@ -909,7 +911,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter. @@ -934,7 +936,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter. @@ -946,7 +948,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info([Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Info([Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter and formatting it with the supplied format provider. @@ -959,7 +961,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter. @@ -984,7 +986,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter. @@ -1009,7 +1011,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter. @@ -1034,7 +1036,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter. @@ -1059,7 +1061,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter. @@ -1084,7 +1086,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter. @@ -1096,7 +1098,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info([Localizable(false)][StructuredMessageTemplate] string message, object argument); + void Info([Localizable(false)][StructuredMessageTemplate] string message, object? argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter and formatting it with the supplied format provider. @@ -1109,7 +1111,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter. @@ -1134,7 +1136,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter. @@ -1159,7 +1161,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// /// Writes the diagnostic message at the Info level using the specified value as a parameter. @@ -1185,7 +1187,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(object value); + void Warn(object? value); /// /// Writes the diagnostic message at the Warn level. @@ -1196,7 +1198,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, object value); + void Warn(IFormatProvider? formatProvider, object? value); /// /// Writes the diagnostic message at the Warn level using the specified parameters. @@ -1209,7 +1211,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); + void Warn([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2); /// /// Writes the diagnostic message at the Warn level using the specified parameters. @@ -1223,7 +1225,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); + void Warn([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter and formatting it with the supplied format provider. @@ -1236,7 +1238,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter. @@ -1261,7 +1263,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter. @@ -1286,7 +1288,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter. @@ -1311,7 +1313,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter. @@ -1323,7 +1325,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn([Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Warn([Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter and formatting it with the supplied format provider. @@ -1336,7 +1338,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter. @@ -1361,7 +1363,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter. @@ -1386,7 +1388,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter. @@ -1411,7 +1413,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter. @@ -1436,7 +1438,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter. @@ -1461,7 +1463,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter. @@ -1473,7 +1475,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn([Localizable(false)][StructuredMessageTemplate] string message, object argument); + void Warn([Localizable(false)][StructuredMessageTemplate] string message, object? argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter and formatting it with the supplied format provider. @@ -1486,7 +1488,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter. @@ -1511,7 +1513,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter. @@ -1536,7 +1538,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// /// Writes the diagnostic message at the Warn level using the specified value as a parameter. @@ -1562,7 +1564,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(object value); + void Error(object? value); /// /// Writes the diagnostic message at the Error level. @@ -1573,7 +1575,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, object value); + void Error(IFormatProvider? formatProvider, object? value); /// /// Writes the diagnostic message at the Error level using the specified parameters. @@ -1586,7 +1588,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); + void Error([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2); /// /// Writes the diagnostic message at the Error level using the specified parameters. @@ -1600,7 +1602,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); + void Error([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter and formatting it with the supplied format provider. @@ -1613,7 +1615,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter. @@ -1637,7 +1639,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter. @@ -1662,7 +1664,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter. @@ -1686,7 +1688,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter. @@ -1698,7 +1700,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error([Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Error([Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter and formatting it with the supplied format provider. @@ -1711,7 +1713,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter. @@ -1736,7 +1738,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter. @@ -1761,7 +1763,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter. @@ -1785,7 +1787,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter. @@ -1810,7 +1812,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter. @@ -1835,7 +1837,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter. @@ -1860,7 +1862,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter. @@ -1885,7 +1887,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter. @@ -1910,7 +1912,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// /// Writes the diagnostic message at the Error level using the specified value as a parameter. @@ -1936,7 +1938,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(object value); + void Fatal(object? value); /// /// Writes the diagnostic message at the Fatal level. @@ -1947,7 +1949,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, object value); + void Fatal(IFormatProvider? formatProvider, object? value); /// /// Writes the diagnostic message at the Fatal level using the specified parameters. @@ -1960,7 +1962,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); + void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2); /// /// Writes the diagnostic message at the Fatal level using the specified parameters. @@ -1974,7 +1976,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); + void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter and formatting it with the supplied format provider. @@ -1987,7 +1989,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter. @@ -2012,7 +2014,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter. @@ -2037,7 +2039,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter. @@ -2062,7 +2064,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter. @@ -2074,7 +2076,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal([Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Fatal([Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter and formatting it with the supplied format provider. @@ -2087,7 +2089,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter. @@ -2112,7 +2114,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter. @@ -2137,7 +2139,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter. @@ -2162,7 +2164,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter. @@ -2187,7 +2189,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter. @@ -2211,7 +2213,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter. @@ -2223,7 +2225,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object argument); + void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object? argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter and formatting it with the supplied format provider. @@ -2236,7 +2238,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter. @@ -2261,7 +2263,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter. @@ -2286,7 +2288,7 @@ public partial interface ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// /// Writes the diagnostic message at the Fatal level using the specified value as a parameter. diff --git a/src/NLog/ILogger.cs b/src/NLog/ILogger.cs index 0b5f2bfd86..1f555bbbb1 100644 --- a/src/NLog/ILogger.cs +++ b/src/NLog/ILogger.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -110,7 +112,7 @@ bool IsFatalEnabled /// /// Type of the value. /// The value to be written. - void Trace(T value); + void Trace(T? value); /// /// Writes the diagnostic message at the Trace level. @@ -118,7 +120,7 @@ bool IsFatalEnabled /// Type of the value. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - void Trace(IFormatProvider formatProvider, T value); + void Trace(IFormatProvider? formatProvider, T? value); /// /// Writes the diagnostic message at the Trace level. @@ -134,14 +136,14 @@ bool IsFatalEnabled /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. [Obsolete("Use Trace(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] [EditorBrowsable(EditorBrowsableState.Never)] - void TraceException([Localizable(false)] string message, Exception exception); + void TraceException([Localizable(false)] string message, Exception? exception); /// /// Writes the diagnostic message and exception at the Trace level. /// /// A to be written. /// An exception to be logged. - void Trace(Exception exception, [Localizable(false)] string message); + void Trace(Exception? exception, [Localizable(false)] string message); /// /// Writes the diagnostic message and exception at the Trace level. @@ -150,7 +152,7 @@ bool IsFatalEnabled /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Trace(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Trace(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message and exception at the Trace level. @@ -160,7 +162,7 @@ bool IsFatalEnabled /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Trace(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Trace(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Trace level using the specified parameters and formatting them with the supplied format provider. @@ -169,7 +171,7 @@ bool IsFatalEnabled /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Trace level. @@ -183,7 +185,7 @@ bool IsFatalEnabled /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Trace([Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Trace([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Obsolete and replaced by - Writes the diagnostic message and exception at the Trace level. @@ -193,7 +195,7 @@ bool IsFatalEnabled /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. [Obsolete("Use Trace(Exception exception, string message) method instead. Marked obsolete with v4.3.11")] [EditorBrowsable(EditorBrowsableState.Never)] - void Trace([Localizable(false)] string message, Exception exception); + void Trace([Localizable(false)] string message, Exception? exception); /// /// Writes the diagnostic message at the Trace level using the specified parameter and formatting it with the supplied format provider. @@ -203,7 +205,7 @@ bool IsFatalEnabled /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument); /// /// Writes the diagnostic message at the Trace level using the specified parameter. @@ -212,7 +214,7 @@ bool IsFatalEnabled /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - void Trace([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument); + void Trace([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument); /// /// Writes the diagnostic message at the Trace level using the specified arguments formatting it with the supplied format provider. @@ -224,7 +226,7 @@ bool IsFatalEnabled /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the Trace level using the specified parameters. @@ -235,7 +237,7 @@ bool IsFatalEnabled /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Trace([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Trace([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the Trace level using the specified arguments formatting it with the supplied format provider. @@ -249,7 +251,7 @@ bool IsFatalEnabled /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); /// /// Writes the diagnostic message at the Trace level using the specified parameters. @@ -262,7 +264,7 @@ bool IsFatalEnabled /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Trace([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Trace([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); #endregion @@ -276,7 +278,7 @@ bool IsFatalEnabled /// /// Type of the value. /// The value to be written. - void Debug(T value); + void Debug(T? value); /// /// Writes the diagnostic message at the Debug level. @@ -284,7 +286,7 @@ bool IsFatalEnabled /// Type of the value. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - void Debug(IFormatProvider formatProvider, T value); + void Debug(IFormatProvider? formatProvider, T? value); /// /// Writes the diagnostic message at the Debug level. @@ -297,7 +299,7 @@ bool IsFatalEnabled /// /// A to be written. /// An exception to be logged. - void Debug(Exception exception, [Localizable(false)] string message); + void Debug(Exception? exception, [Localizable(false)] string message); /// /// Writes the diagnostic message and exception at the Debug level. @@ -306,7 +308,7 @@ bool IsFatalEnabled /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Debug(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Debug(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message and exception at the Debug level. @@ -316,7 +318,7 @@ bool IsFatalEnabled /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Debug(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Debug(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Debug level using the specified parameters and formatting them with the supplied format provider. @@ -325,7 +327,7 @@ bool IsFatalEnabled /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Debug level. @@ -339,7 +341,7 @@ bool IsFatalEnabled /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Debug([Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Debug([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Debug level using the specified parameter and formatting it with the supplied format provider. @@ -349,7 +351,7 @@ bool IsFatalEnabled /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument); /// /// Writes the diagnostic message at the Debug level using the specified parameter. @@ -358,7 +360,7 @@ bool IsFatalEnabled /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - void Debug([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument); + void Debug([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument); /// /// Writes the diagnostic message at the Debug level using the specified arguments formatting it with the supplied format provider. @@ -370,7 +372,7 @@ bool IsFatalEnabled /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the Debug level using the specified parameters. @@ -381,7 +383,7 @@ bool IsFatalEnabled /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Debug([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Debug([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the Debug level using the specified arguments formatting it with the supplied format provider. @@ -395,7 +397,7 @@ bool IsFatalEnabled /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); /// /// Writes the diagnostic message at the Debug level using the specified parameters. @@ -408,7 +410,7 @@ bool IsFatalEnabled /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Debug([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Debug([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); /// /// Obsolete and replaced by - Writes the diagnostic message and exception at the Debug level. @@ -428,7 +430,7 @@ bool IsFatalEnabled /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. [Obsolete("Use Debug(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] [EditorBrowsable(EditorBrowsableState.Never)] - void DebugException([Localizable(false)] string message, Exception exception); + void DebugException([Localizable(false)] string message, Exception? exception); #endregion @@ -442,7 +444,7 @@ bool IsFatalEnabled /// /// Type of the value. /// The value to be written. - void Info(T value); + void Info(T? value); /// /// Writes the diagnostic message at the Info level. @@ -450,7 +452,7 @@ bool IsFatalEnabled /// Type of the value. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - void Info(IFormatProvider formatProvider, T value); + void Info(IFormatProvider? formatProvider, T? value); /// /// Writes the diagnostic message at the Info level. @@ -463,7 +465,7 @@ bool IsFatalEnabled /// /// A to be written. /// An exception to be logged. - void Info(Exception exception, [Localizable(false)] string message); + void Info(Exception? exception, [Localizable(false)] string message); /// /// Writes the diagnostic message and exception at the Info level. @@ -472,7 +474,7 @@ bool IsFatalEnabled /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Info(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Info(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message and exception at the Info level. @@ -482,7 +484,7 @@ bool IsFatalEnabled /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Info(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Info(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Info level using the specified parameters and formatting them with the supplied format provider. @@ -491,7 +493,7 @@ bool IsFatalEnabled /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Info level. @@ -505,7 +507,7 @@ bool IsFatalEnabled /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Info([Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Info([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Info level using the specified parameter and formatting it with the supplied format provider. @@ -515,7 +517,7 @@ bool IsFatalEnabled /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument); /// /// Writes the diagnostic message at the Info level using the specified parameter. @@ -536,7 +538,7 @@ bool IsFatalEnabled /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the Info level using the specified parameters. @@ -547,7 +549,7 @@ bool IsFatalEnabled /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Info([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Info([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the Info level using the specified arguments formatting it with the supplied format provider. @@ -561,7 +563,7 @@ bool IsFatalEnabled /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); /// /// Writes the diagnostic message at the Info level using the specified parameters. @@ -574,7 +576,7 @@ bool IsFatalEnabled /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Info([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Info([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); /// /// Obsolete and replaced by - Writes the diagnostic message and exception at the Info level. @@ -584,7 +586,7 @@ bool IsFatalEnabled /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. [Obsolete("Use Info(Exception exception, string message) method instead. Marked obsolete with v4.3.11")] [EditorBrowsable(EditorBrowsableState.Never)] - void Info([Localizable(false)] string message, Exception exception); + void Info([Localizable(false)] string message, Exception? exception); /// /// Obsolete and replaced by - Writes the diagnostic message and exception at the Info level. @@ -594,7 +596,7 @@ bool IsFatalEnabled /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. [Obsolete("Use Info(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] [EditorBrowsable(EditorBrowsableState.Never)] - void InfoException([Localizable(false)] string message, Exception exception); + void InfoException([Localizable(false)] string message, Exception? exception); #endregion @@ -608,7 +610,7 @@ bool IsFatalEnabled /// /// Type of the value. /// The value to be written. - void Warn(T value); + void Warn(T? value); /// /// Writes the diagnostic message at the Warn level. @@ -616,7 +618,7 @@ bool IsFatalEnabled /// Type of the value. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - void Warn(IFormatProvider formatProvider, T value); + void Warn(IFormatProvider? formatProvider, T? value); /// /// Writes the diagnostic message at the Warn level. @@ -629,7 +631,7 @@ bool IsFatalEnabled /// /// A to be written. /// An exception to be logged. - void Warn(Exception exception, [Localizable(false)] string message); + void Warn(Exception? exception, [Localizable(false)] string message); /// /// Writes the diagnostic message and exception at the Warn level. @@ -638,7 +640,7 @@ bool IsFatalEnabled /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Warn(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Warn(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message and exception at the Warn level. @@ -648,7 +650,7 @@ bool IsFatalEnabled /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Warn(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Warn(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Warn level using the specified parameters and formatting them with the supplied format provider. @@ -657,7 +659,7 @@ bool IsFatalEnabled /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Warn level. @@ -671,7 +673,7 @@ bool IsFatalEnabled /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Warn([Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Warn([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Warn level using the specified parameter and formatting it with the supplied format provider. @@ -681,7 +683,7 @@ bool IsFatalEnabled /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument); /// /// Writes the diagnostic message at the Warn level using the specified parameter. @@ -690,7 +692,7 @@ bool IsFatalEnabled /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - void Warn([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument); + void Warn([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument); /// /// Writes the diagnostic message at the Warn level using the specified arguments formatting it with the supplied format provider. @@ -702,7 +704,7 @@ bool IsFatalEnabled /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the Warn level using the specified parameters. @@ -713,7 +715,7 @@ bool IsFatalEnabled /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Warn([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Warn([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the Warn level using the specified arguments formatting it with the supplied format provider. @@ -727,7 +729,7 @@ bool IsFatalEnabled /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); /// /// Writes the diagnostic message at the Warn level using the specified parameters. @@ -740,7 +742,7 @@ bool IsFatalEnabled /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Warn([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Warn([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); /// /// Obsolete and replaced by - Writes the diagnostic message and exception at the Warn level. @@ -750,7 +752,7 @@ bool IsFatalEnabled /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. [Obsolete("Use Warn(Exception exception, string message) method instead. Marked obsolete with v4.3.11")] [EditorBrowsable(EditorBrowsableState.Never)] - void Warn([Localizable(false)] string message, Exception exception); + void Warn([Localizable(false)] string message, Exception? exception); /// /// Obsolete and replaced by - Writes the diagnostic message and exception at the Warn level. @@ -760,7 +762,7 @@ bool IsFatalEnabled /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. [Obsolete("Use Warn(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] [EditorBrowsable(EditorBrowsableState.Never)] - void WarnException([Localizable(false)] string message, Exception exception); + void WarnException([Localizable(false)] string message, Exception? exception); #endregion @@ -774,7 +776,7 @@ bool IsFatalEnabled /// /// Type of the value. /// The value to be written. - void Error(T value); + void Error(T? value); /// /// Writes the diagnostic message at the Error level. @@ -782,7 +784,7 @@ bool IsFatalEnabled /// Type of the value. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - void Error(IFormatProvider formatProvider, T value); + void Error(IFormatProvider? formatProvider, T? value); /// /// Writes the diagnostic message at the Error level. @@ -795,7 +797,7 @@ bool IsFatalEnabled /// /// A to be written. /// An exception to be logged. - void Error(Exception exception, [Localizable(false)] string message); + void Error(Exception? exception, [Localizable(false)] string message); /// /// Writes the diagnostic message and exception at the Error level. @@ -804,7 +806,7 @@ bool IsFatalEnabled /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Error(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Error(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message and exception at the Error level. @@ -814,7 +816,7 @@ bool IsFatalEnabled /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Error(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Error(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// @@ -824,7 +826,7 @@ bool IsFatalEnabled /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Error level. @@ -838,7 +840,7 @@ bool IsFatalEnabled /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Error([Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Error([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Error level using the specified parameter and formatting it with the supplied format provider. @@ -848,7 +850,7 @@ bool IsFatalEnabled /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument); /// /// Writes the diagnostic message at the Error level using the specified parameter. @@ -857,7 +859,7 @@ bool IsFatalEnabled /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - void Error([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument); + void Error([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument); /// /// Writes the diagnostic message at the Error level using the specified arguments formatting it with the supplied format provider. @@ -869,7 +871,7 @@ bool IsFatalEnabled /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the Error level using the specified parameters. @@ -880,7 +882,7 @@ bool IsFatalEnabled /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Error([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Error([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the Error level using the specified arguments formatting it with the supplied format provider. @@ -894,7 +896,7 @@ bool IsFatalEnabled /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); /// /// Writes the diagnostic message at the Error level using the specified parameters. @@ -907,7 +909,7 @@ bool IsFatalEnabled /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Error([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Error([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); /// /// Obsolete and replaced by - Writes the diagnostic message and exception at the Error level. @@ -917,7 +919,7 @@ bool IsFatalEnabled /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. [Obsolete("Use Error(Exception exception, string message) method instead. Marked obsolete with v4.3.11")] [EditorBrowsable(EditorBrowsableState.Never)] - void Error([Localizable(false)] string message, Exception exception); + void Error([Localizable(false)] string message, Exception? exception); /// /// Obsolete and replaced by - Writes the diagnostic message and exception at the Error level. @@ -927,7 +929,7 @@ bool IsFatalEnabled /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. [Obsolete("Use Error(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] [EditorBrowsable(EditorBrowsableState.Never)] - void ErrorException([Localizable(false)] string message, Exception exception); + void ErrorException([Localizable(false)] string message, Exception? exception); #endregion @@ -941,7 +943,7 @@ bool IsFatalEnabled /// /// Type of the value. /// The value to be written. - void Fatal(T value); + void Fatal(T? value); /// /// Writes the diagnostic message at the Fatal level. @@ -949,7 +951,7 @@ bool IsFatalEnabled /// Type of the value. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - void Fatal(IFormatProvider formatProvider, T value); + void Fatal(IFormatProvider? formatProvider, T? value); /// /// Writes the diagnostic message at the Fatal level. @@ -962,7 +964,7 @@ bool IsFatalEnabled /// /// A to be written. /// An exception to be logged. - void Fatal(Exception exception, [Localizable(false)] string message); + void Fatal(Exception? exception, [Localizable(false)] string message); /// /// Writes the diagnostic message and exception at the Fatal level. @@ -971,7 +973,7 @@ bool IsFatalEnabled /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Fatal(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Fatal(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message and exception at the Fatal level. @@ -981,7 +983,7 @@ bool IsFatalEnabled /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Fatal(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Fatal(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Fatal level using the specified parameters and formatting them with the supplied format provider. @@ -990,7 +992,7 @@ bool IsFatalEnabled /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Fatal level. @@ -1004,7 +1006,7 @@ bool IsFatalEnabled /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Fatal([Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Fatal([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the Fatal level using the specified parameter and formatting it with the supplied format provider. @@ -1014,7 +1016,7 @@ bool IsFatalEnabled /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument); /// /// Writes the diagnostic message at the Fatal level using the specified parameter. @@ -1023,7 +1025,7 @@ bool IsFatalEnabled /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - void Fatal([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument); + void Fatal([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument); /// /// Writes the diagnostic message at the Fatal level using the specified arguments formatting it with the supplied format provider. @@ -1035,7 +1037,7 @@ bool IsFatalEnabled /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the Fatal level using the specified parameters. @@ -1046,7 +1048,7 @@ bool IsFatalEnabled /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Fatal([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Fatal([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the Fatal level using the specified arguments formatting it with the supplied format provider. @@ -1060,7 +1062,7 @@ bool IsFatalEnabled /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); /// /// Writes the diagnostic message at the Fatal level using the specified parameters. @@ -1073,7 +1075,7 @@ bool IsFatalEnabled /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Fatal([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Fatal([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); /// /// Obsolete and replaced by - Writes the diagnostic message and exception at the Fatal level. @@ -1083,7 +1085,7 @@ bool IsFatalEnabled /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. [Obsolete("Use Fatal(Exception exception, string message) method instead. Marked obsolete with v4.3.11")] [EditorBrowsable(EditorBrowsableState.Never)] - void Fatal([Localizable(false)] string message, Exception exception); + void Fatal([Localizable(false)] string message, Exception? exception); /// /// Obsolete and replaced by - Writes the diagnostic message and exception at the Fatal level. @@ -1093,7 +1095,7 @@ bool IsFatalEnabled /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. [Obsolete("Use Fatal(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] [EditorBrowsable(EditorBrowsableState.Never)] - void FatalException([Localizable(false)] string message, Exception exception); + void FatalException([Localizable(false)] string message, Exception? exception); #endregion diff --git a/src/NLog/ILoggerBase-V1.cs b/src/NLog/ILoggerBase-V1.cs index 127ad80344..cc9c0038b6 100644 --- a/src/NLog/ILoggerBase-V1.cs +++ b/src/NLog/ILoggerBase-V1.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -55,7 +57,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, object value); + void Log(LogLevel level, object? value); /// /// Writes the diagnostic message at the specified level. @@ -67,7 +69,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, object value); + void Log(LogLevel level, IFormatProvider? formatProvider, object? value); /// /// Writes the diagnostic message at the specified level using the specified parameters. @@ -81,7 +83,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2); + void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2); /// /// Writes the diagnostic message at the specified level using the specified parameters. @@ -96,7 +98,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3); + void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter and formatting it with the supplied format provider. @@ -110,7 +112,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter. @@ -137,7 +139,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter. @@ -164,7 +166,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter. @@ -191,7 +193,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter. @@ -204,7 +206,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, string argument); + void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, string? argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter and formatting it with the supplied format provider. @@ -218,7 +220,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter. @@ -245,7 +247,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter. @@ -272,7 +274,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter. @@ -299,7 +301,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter. @@ -326,7 +328,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter. @@ -353,7 +355,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter. @@ -366,7 +368,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object argument); + void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object? argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter and formatting it with the supplied format provider. @@ -380,7 +382,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter. @@ -407,7 +409,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter. @@ -436,7 +438,7 @@ public partial interface ILoggerBase #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument); /// /// Writes the diagnostic message at the specified level using the specified value as a parameter. diff --git a/src/NLog/ILoggerBase.cs b/src/NLog/ILoggerBase.cs index ab44f9bed5..18b63f09d9 100644 --- a/src/NLog/ILoggerBase.cs +++ b/src/NLog/ILoggerBase.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -104,7 +106,7 @@ LogFactory Factory /// Type of the value. /// The log level. /// The value to be written. - void Log(LogLevel level, T value); + void Log(LogLevel level, T? value); /// /// Writes the diagnostic message at the specified level. @@ -113,7 +115,7 @@ LogFactory Factory /// The log level. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - void Log(LogLevel level, IFormatProvider formatProvider, T value); + void Log(LogLevel level, IFormatProvider? formatProvider, T? value); /// /// Writes the diagnostic message at the specified level. @@ -130,7 +132,7 @@ LogFactory Factory /// Arguments to format. /// An exception to be logged. [MessageTemplateFormatMethod("message")] - void Log(LogLevel level, Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Log(LogLevel level, Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message and exception at the specified level. @@ -141,7 +143,7 @@ LogFactory Factory /// Arguments to format. /// An exception to be logged. [MessageTemplateFormatMethod("message")] - void Log(LogLevel level, Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Log(LogLevel level, Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the specified level using the specified parameters and formatting them with the supplied format provider. @@ -151,7 +153,7 @@ LogFactory Factory /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the specified level. @@ -167,7 +169,7 @@ LogFactory Factory /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args); + void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args); /// /// Writes the diagnostic message at the specified level using the specified parameter and formatting it with the supplied format provider. @@ -178,7 +180,7 @@ LogFactory Factory /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument); /// /// Writes the diagnostic message at the specified level using the specified parameter. @@ -188,7 +190,7 @@ LogFactory Factory /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument); + void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument); /// /// Writes the diagnostic message at the specified level using the specified arguments formatting it with the supplied format provider. @@ -201,7 +203,7 @@ LogFactory Factory /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the specified level using the specified parameters. @@ -213,7 +215,7 @@ LogFactory Factory /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2); + void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2); /// /// Writes the diagnostic message at the specified level using the specified arguments formatting it with the supplied format provider. @@ -228,7 +230,7 @@ LogFactory Factory /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); /// /// Writes the diagnostic message at the specified level using the specified parameters. @@ -242,7 +244,7 @@ LogFactory Factory /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3); + void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); /// /// Obsolete and replaced by - Writes the diagnostic message and exception at the specified level. @@ -253,7 +255,7 @@ LogFactory Factory /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. [Obsolete("Use Log(LogLevel level, Exception exception, [Localizable(false)] string message, params object[] args) instead. Marked obsolete with v4.3.11")] [EditorBrowsable(EditorBrowsableState.Never)] - void Log(LogLevel level, [Localizable(false)] string message, Exception exception); + void Log(LogLevel level, [Localizable(false)] string message, Exception? exception); /// /// Obsolete and replaced by - Writes the diagnostic message and exception at the specified level. @@ -264,7 +266,7 @@ LogFactory Factory /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. [Obsolete("Use Log(LogLevel level, Exception exception, [Localizable(false)] string message, params object[] args) instead. Marked obsolete with v4.3.11")] [EditorBrowsable(EditorBrowsableState.Never)] - void LogException(LogLevel level, [Localizable(false)] string message, Exception exception); + void LogException(LogLevel level, [Localizable(false)] string message, Exception? exception); #endregion } } diff --git a/src/NLog/ILoggerExtensions.cs b/src/NLog/ILoggerExtensions.cs index 038878dc4b..5745803841 100644 --- a/src/NLog/ILoggerExtensions.cs +++ b/src/NLog/ILoggerExtensions.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -54,7 +56,7 @@ public static class ILoggerExtensions /// The logger to write the log event to. /// The log level. When not /// for chaining calls. - public static LogEventBuilder ForLogEvent([NotNull] this ILogger logger, LogLevel logLevel = null) + public static LogEventBuilder ForLogEvent([NotNull] this ILogger logger, LogLevel? logLevel = null) { return logLevel is null ? new LogEventBuilder(logger) : new LogEventBuilder(logger, logLevel); } @@ -126,7 +128,7 @@ public static LogEventBuilder ForFatalEvent([NotNull] this ILogger logger) /// The exception information of the logging event. /// The for the log event. Defaults to when not specified. /// for chaining calls. - public static LogEventBuilder ForExceptionEvent([NotNull] this ILogger logger, Exception exception, LogLevel logLevel = null) + public static LogEventBuilder ForExceptionEvent([NotNull] this ILogger logger, Exception? exception, LogLevel? logLevel = null) { return ForLogEvent(logger, logLevel ?? LogLevel.Error).Exception(exception); } @@ -143,7 +145,7 @@ public static LogEventBuilder ForExceptionEvent([NotNull] this ILogger logger, E /// A logger implementation that will handle the message. /// The value to be written. [Conditional("DEBUG")] - public static void ConditionalDebug([NotNull] this ILogger logger, T value) + public static void ConditionalDebug([NotNull] this ILogger logger, T? value) { logger.Debug(value); } @@ -156,7 +158,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, T value) /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. [Conditional("DEBUG")] - public static void ConditionalDebug([NotNull] this ILogger logger, IFormatProvider formatProvider, T value) + public static void ConditionalDebug([NotNull] this ILogger logger, IFormatProvider? formatProvider, T? value) { logger.Debug(formatProvider, value); } @@ -181,7 +183,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, LogMessageGen /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public static void ConditionalDebug([NotNull] this ILogger logger, Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Debug(exception, message, args); } @@ -196,7 +198,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, Exception exc /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public static void ConditionalDebug([NotNull] this ILogger logger, Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Debug(exception, formatProvider, message, args); } @@ -209,7 +211,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, Exception exc [Conditional("DEBUG")] public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)] string message) { - logger.Debug(message, default(object[])); + logger.Debug(message); } /// @@ -220,7 +222,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable( /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Debug(message, args); } @@ -234,7 +236,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable( /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public static void ConditionalDebug([NotNull] this ILogger logger, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Debug(formatProvider, message, args); } @@ -248,7 +250,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, IFormatProvid /// The argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (logger.IsDebugEnabled) { @@ -267,7 +269,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, [L /// The second argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (logger.IsDebugEnabled) { @@ -288,7 +290,7 @@ public static void ConditionalDebug([NotNull] this ILogg /// The third argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (logger.IsDebugEnabled) { @@ -310,7 +312,7 @@ public static void ConditionalDebug([NotNull /// A logger implementation that will handle the message. /// The value to be written. [Conditional("DEBUG")] - public static void ConditionalTrace([NotNull] this ILogger logger, T value) + public static void ConditionalTrace([NotNull] this ILogger logger, T? value) { logger.Trace(value); } @@ -323,7 +325,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, T value) /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. [Conditional("DEBUG")] - public static void ConditionalTrace([NotNull] this ILogger logger, IFormatProvider formatProvider, T value) + public static void ConditionalTrace([NotNull] this ILogger logger, IFormatProvider? formatProvider, T? value) { logger.Trace(formatProvider, value); } @@ -348,7 +350,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, LogMessageGen /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public static void ConditionalTrace([NotNull] this ILogger logger, Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Trace(exception, message, args); } @@ -363,7 +365,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, Exception exc /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public static void ConditionalTrace([NotNull] this ILogger logger, Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Trace(exception, formatProvider, message, args); } @@ -376,7 +378,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, Exception exc [Conditional("DEBUG")] public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)] string message) { - logger.Trace(message, default(object[])); + logger.Trace(message); } /// @@ -387,7 +389,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable( /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Trace(message, args); } @@ -401,7 +403,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable( /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public static void ConditionalTrace([NotNull] this ILogger logger, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Trace(formatProvider, message, args); } @@ -415,7 +417,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, IFormatProvid /// The argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (logger.IsTraceEnabled) { @@ -434,7 +436,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, [L /// The second argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (logger.IsTraceEnabled) { @@ -455,7 +457,7 @@ public static void ConditionalTrace([NotNull] this ILogg /// The third argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (logger.IsTraceEnabled) { @@ -472,11 +474,11 @@ public static void ConditionalTrace([NotNull /// The log level. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Log([NotNull] this ILogger logger, LogLevel level, Exception exception, LogMessageGenerator messageFunc) + public static void Log([NotNull] this ILogger logger, LogLevel level, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsEnabled(level)) { - logger.Log(level, exception, messageFunc(), null); + logger.Log(level, exception, messageFunc(), ArrayHelper.Empty()); } } @@ -486,7 +488,7 @@ public static void Log([NotNull] this ILogger logger, LogLevel level, Exception /// A logger implementation that will handle the message. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Trace([NotNull] this ILogger logger, Exception exception, LogMessageGenerator messageFunc) + public static void Trace([NotNull] this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsTraceEnabled) { @@ -502,7 +504,7 @@ public static void Trace([NotNull] this ILogger logger, Exception exception, Log /// A logger implementation that will handle the message. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Debug([NotNull] this ILogger logger, Exception exception, LogMessageGenerator messageFunc) + public static void Debug([NotNull] this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsDebugEnabled) { @@ -518,7 +520,7 @@ public static void Debug([NotNull] this ILogger logger, Exception exception, Log /// A logger implementation that will handle the message. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Info([NotNull] this ILogger logger, Exception exception, LogMessageGenerator messageFunc) + public static void Info([NotNull] this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsInfoEnabled) { @@ -534,7 +536,7 @@ public static void Info([NotNull] this ILogger logger, Exception exception, LogM /// A logger implementation that will handle the message. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Warn([NotNull] this ILogger logger, Exception exception, LogMessageGenerator messageFunc) + public static void Warn([NotNull] this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsWarnEnabled) { @@ -550,7 +552,7 @@ public static void Warn([NotNull] this ILogger logger, Exception exception, LogM /// A logger implementation that will handle the message. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Error([NotNull] this ILogger logger, Exception exception, LogMessageGenerator messageFunc) + public static void Error([NotNull] this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsErrorEnabled) { @@ -566,7 +568,7 @@ public static void Error([NotNull] this ILogger logger, Exception exception, Log /// A logger implementation that will handle the message. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Fatal([NotNull] this ILogger logger, Exception exception, LogMessageGenerator messageFunc) + public static void Fatal([NotNull] this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsFatalEnabled) { diff --git a/src/NLog/ISuppress.cs b/src/NLog/ISuppress.cs index 4247f4f365..aab961ee17 100644 --- a/src/NLog/ISuppress.cs +++ b/src/NLog/ISuppress.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -61,7 +63,7 @@ public interface ISuppress /// Return type of the provided function. /// Function to run. /// Result returned by the provided function or the default value of type in case of exception. - T Swallow(Func func); + T? Swallow(Func func); /// /// Runs the provided function and returns its result. If an exception is thrown, it is logged at Error level. @@ -71,7 +73,7 @@ public interface ISuppress /// Function to run. /// Fallback value to return in case of exception. /// Result returned by the provided function or fallback value in case of exception. - T Swallow(Func func, T fallback); + T? Swallow(Func func, T? fallback); #if !NET35 && !NET40 /// @@ -103,7 +105,7 @@ public interface ISuppress /// Return type of the provided function. /// Async function to run. /// A task that represents the completion of the supplied task. If the supplied task ends in the state, the result of the new task will be the result of the supplied task; otherwise, the result of the new task will be the default value of type . - Task SwallowAsync(Func> asyncFunc); + Task SwallowAsync(Func> asyncFunc); /// /// Runs the provided async function and returns its result. If the task does not run to completion, an exception is logged at Error level. @@ -113,7 +115,7 @@ public interface ISuppress /// Async function to run. /// Fallback value to return if the task does not end in the state. /// A task that represents the completion of the supplied task. If the supplied task ends in the state, the result of the new task will be the result of the supplied task; otherwise, the result of the new task will be the fallback value. - Task SwallowAsync(Func> asyncFunc, TResult fallback); + Task SwallowAsync(Func> asyncFunc, TResult? fallback); #endif } } diff --git a/src/NLog/Internal/LogMessageTemplateFormatter.cs b/src/NLog/Internal/LogMessageTemplateFormatter.cs index 5e280e0f96..5a10d37dde 100644 --- a/src/NLog/Internal/LogMessageTemplateFormatter.cs +++ b/src/NLog/Internal/LogMessageTemplateFormatter.cs @@ -125,7 +125,7 @@ public string FormatMessage(LogEventInfo logEvent) private void AppendToBuilder(LogEventInfo logEvent, StringBuilder builder) { Render(logEvent.Message, logEvent.FormatProvider ?? _logFactory.DefaultCultureInfo ?? CultureInfo.CurrentCulture, logEvent.Parameters, builder, out var messageTemplateParameterList); - logEvent.CreateOrUpdatePropertiesInternal(false, messageTemplateParameterList ?? ArrayHelper.Empty()); + logEvent.TryCreatePropertiesInternal(messageTemplateParameterList ?? ArrayHelper.Empty()); } /// diff --git a/src/NLog/Internal/MemberNotNullAttribute.cs b/src/NLog/Internal/MemberNotNullAttribute.cs new file mode 100644 index 0000000000..d53d30a104 --- /dev/null +++ b/src/NLog/Internal/MemberNotNullAttribute.cs @@ -0,0 +1,171 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#if !NETSTANDARD2_1_OR_GREATER && !NETCOREAPP3_0_OR_GREATER +namespace System.Diagnostics.CodeAnalysis +{ + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute { } + + /// Specifies that null is disallowed as an input even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class DisallowNullAttribute : Attribute { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class MaybeNullAttribute : Attribute { } + + /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class NotNullAttribute : Attribute { } + + /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that the output will be non-null if the named parameter is non-null. + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] + internal sealed class NotNullIfNotNullAttribute : Attribute + { + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } + } + + /// Applied to a method that will never return under any circumstance. + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class DoesNotReturnAttribute : Attribute { } + + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class DoesNotReturnIfAttribute : Attribute + { + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullAttribute : Attribute + { + /// Initializes the attribute with a field or property member. + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullAttribute(string member) => Members = new[] { member }; + + /// Initializes the attribute with the list of field and property members. + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullAttribute(params string[] members) => Members = members; + + /// Gets field or property member names. + public string[] Members { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition and a field or property member. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, string member) + { + ReturnValue = returnValue; + Members = new[] { member }; + } + + /// Initializes the attribute with the specified return value condition and list of field and property members. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + + /// Gets the return value condition. + public bool ReturnValue { get; } + + /// Gets field or property member names. + public string[] Members { get; } + } +} +#endif diff --git a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs index 7efd6c3750..7f36e47ccd 100644 --- a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs @@ -172,7 +172,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) bool includeSeparator = false; if (logEvent.HasProperties) { - using (var propertyEnumerator = logEvent.CreateOrUpdatePropertiesInternal().GetPropertyEnumerator()) + using (var propertyEnumerator = logEvent.TryCreatePropertiesInternal().GetPropertyEnumerator()) { while (propertyEnumerator.MoveNext()) { diff --git a/src/NLog/Layouts/JSON/JsonLayout.cs b/src/NLog/Layouts/JSON/JsonLayout.cs index bb20aaf3ac..20c3882b7a 100644 --- a/src/NLog/Layouts/JSON/JsonLayout.cs +++ b/src/NLog/Layouts/JSON/JsonLayout.cs @@ -349,7 +349,7 @@ private void RenderJsonFormattedMessage(LogEventInfo logEvent, StringBuilder sb) if (IncludeEventProperties && logEvent.HasProperties) { bool checkExcludeProperties = ExcludeProperties.Count > 0; - using (var propertyEnumerator = logEvent.CreateOrUpdatePropertiesInternal().GetPropertyEnumerator()) + using (var propertyEnumerator = logEvent.TryCreatePropertiesInternal().GetPropertyEnumerator()) { while (propertyEnumerator.MoveNext()) { diff --git a/src/NLog/Layouts/XML/XmlElementBase.cs b/src/NLog/Layouts/XML/XmlElementBase.cs index 2584943e41..a8cccb4da4 100644 --- a/src/NLog/Layouts/XML/XmlElementBase.cs +++ b/src/NLog/Layouts/XML/XmlElementBase.cs @@ -406,7 +406,7 @@ private void AppendLogEventProperties(LogEventInfo logEventInfo, StringBuilder s return; bool checkExcludeProperties = ExcludeProperties.Count > 0; - using (var propertyEnumerator = logEventInfo.CreateOrUpdatePropertiesInternal().GetPropertyEnumerator()) + using (var propertyEnumerator = logEventInfo.TryCreatePropertiesInternal().GetPropertyEnumerator()) { while (propertyEnumerator.MoveNext()) { diff --git a/src/NLog/LogEventBuilder.cs b/src/NLog/LogEventBuilder.cs index dd92d2c4bd..895a844762 100644 --- a/src/NLog/LogEventBuilder.cs +++ b/src/NLog/LogEventBuilder.cs @@ -38,6 +38,8 @@ using JetBrains.Annotations; using NLog.Internal; +#nullable enable + namespace NLog { /// @@ -47,7 +49,7 @@ namespace NLog public struct LogEventBuilder { private readonly ILogger _logger; - private readonly LogEventInfo _logEvent; + private readonly LogEventInfo? _logEvent; /// /// Initializes a new instance of the class. @@ -90,14 +92,14 @@ public LogEventBuilder([NotNull] ILogger logger, [NotNull] LogLevel logLevel) /// Logging event that will be written /// [CanBeNull] - public LogEventInfo LogEvent => _logEvent is null ? null : ResolveLogEvent(_logEvent); + public LogEventInfo? LogEvent => _logEvent is null ? null : ResolveLogEvent(_logEvent); /// /// Sets a per-event context property on the logging event. /// /// The name of the context property. /// The value of the context property. - public LogEventBuilder Property([NotNull] string propertyName, T propertyValue) + public LogEventBuilder Property([NotNull] string propertyName, T? propertyValue) { Guard.ThrowIfNull(propertyName); @@ -112,7 +114,7 @@ public LogEventBuilder Property([NotNull] string propertyName, T propertyValu /// Sets multiple per-event context properties on the logging event. /// /// The properties to set. - public LogEventBuilder Properties([NotNull] IEnumerable> properties) + public LogEventBuilder Properties([NotNull] IEnumerable> properties) { Guard.ThrowIfNull(properties); @@ -136,7 +138,7 @@ public LogEventBuilder Properties(params ReadOnlySpan<(string, object)> properti if (_logEvent.Parameters is null) { - var eventProperties = _logEvent.CreateOrUpdatePropertiesInternal(false, null); + var eventProperties = _logEvent.TryCreatePropertiesInternal(); if (eventProperties is null) { // Now allocate PropertiesDictionary and copy from properties @@ -145,7 +147,7 @@ public LogEventBuilder Properties(params ReadOnlySpan<(string, object)> properti { messageProperties[i] = new MessageTemplates.MessageTemplateParameter(properties[i].Item1, properties[i].Item2, null); } - _logEvent.CreateOrUpdatePropertiesInternal(false, messageProperties); + _logEvent.TryCreatePropertiesInternal(messageProperties); return this; } } @@ -160,7 +162,7 @@ public LogEventBuilder Properties(params ReadOnlySpan<(string, object)> properti /// Sets the information of the logging event. /// /// The exception information of the logging event. - public LogEventBuilder Exception(Exception exception) + public LogEventBuilder Exception(Exception? exception) { if (_logEvent != null) { @@ -203,12 +205,12 @@ public LogEventBuilder Message([Localizable(false)] string message) /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (_logEvent != null) { _logEvent.Message = message; - _logEvent.Parameters = new object[] { argument }; + _logEvent.Parameters = new object?[] { argument }; } return this; } @@ -222,12 +224,12 @@ public LogEventBuilder Message([Localizable(false)][StructuredMessage /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (_logEvent != null) { _logEvent.Message = message; - _logEvent.Parameters = new object[] { argument1, argument2 }; + _logEvent.Parameters = new object?[] { argument1, argument2 }; } return this; } @@ -243,12 +245,12 @@ public LogEventBuilder Message([Localizable(false)][Stru /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (_logEvent != null) { _logEvent.Message = message; - _logEvent.Parameters = new object[] { argument1, argument2, argument3 }; + _logEvent.Parameters = new object?[] { argument1, argument2, argument3 }; } return this; } @@ -259,7 +261,7 @@ public LogEventBuilder Message([Localizable( /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (_logEvent != null) { @@ -276,7 +278,7 @@ public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] s /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (_logEvent != null) { @@ -294,7 +296,7 @@ public LogEventBuilder Message([Localizable(false)][StructuredMessageTemplate] s /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public LogEventBuilder Message(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public LogEventBuilder Message(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (_logEvent != null) { @@ -313,14 +315,14 @@ public LogEventBuilder Message(IFormatProvider formatProvider, [Localizable(fals /// The full path of the source file that contains the caller. This is set at by the compiler. /// The line number in the source file at which the method is called. This is set at by the compiler. #if !NET35 - public LogEventBuilder Callsite(string callerClassName = null, - [CallerMemberName] string callerMemberName = null, - [CallerFilePath] string callerFilePath = null, + public LogEventBuilder Callsite(string? callerClassName = null, + [CallerMemberName] string? callerMemberName = null, + [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) #else - public LogEventBuilder Callsite(string callerClassName = null, - string callerMemberName = null, - string callerFilePath = null, + public LogEventBuilder Callsite(string? callerClassName = null, + string? callerMemberName = null, + string? callerFilePath = null, int callerLineNumber = 0) #endif { @@ -339,14 +341,14 @@ public LogEventBuilder Callsite(string callerClassName = null, /// The full path of the source file that contains the caller. This is set at by the compiler. /// The line number in the source file at which the method is called. This is set at by the compiler. #if !NET35 - public void Log(LogLevel logLevel = null, - [CallerMemberName] string callerMemberName = null, - [CallerFilePath] string callerFilePath = null, + public void Log(LogLevel? logLevel = null, + [CallerMemberName] string? callerMemberName = null, + [CallerFilePath] string? callerFilePath = null, [CallerLineNumber] int callerLineNumber = 0) #else - public void Log(LogLevel logLevel = null, - string callerMemberName = null, - string callerFilePath = null, + public void Log(LogLevel? logLevel = null, + string? callerMemberName = null, + string? callerFilePath = null, int callerLineNumber = 0) #endif { @@ -378,7 +380,7 @@ public void Log(Type wrapperType) } } - private LogEventInfo ResolveLogEvent(LogEventInfo logEvent, LogLevel logLevel = null) + private LogEventInfo ResolveLogEvent(LogEventInfo logEvent, LogLevel? logLevel = null) { if (logLevel is null) { @@ -390,7 +392,7 @@ private LogEventInfo ResolveLogEvent(LogEventInfo logEvent, LogLevel logLevel = logEvent.Level = logLevel; } - if (logEvent.Message is null && logEvent.Exception != null && _logger.IsEnabled(logEvent.Level)) + if ((logEvent.Message is null || ReferenceEquals(logEvent.Message, string.Empty)) && logEvent.Exception != null && _logger.IsEnabled(logEvent.Level)) { logEvent.FormatProvider = NLog.Internal.ExceptionMessageFormatProvider.Instance; logEvent.Message = "{0}"; diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index 4029a691b7..6ae24a7306 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -59,18 +61,18 @@ public class LogEventInfo /// /// The formatted log message. /// - private string _formattedMessage; + private string? _formattedMessage; /// /// The log message including any parameter placeholders /// private string _message; - private object[] _parameters; - private IFormatProvider _formatProvider; - private LogMessageFormatter _messageFormatter; - private IDictionary _layoutCache; - private PropertiesDictionary _properties; + private object?[]? _parameters; + private IFormatProvider? _formatProvider; + private LogMessageFormatter? _messageFormatter; + private IDictionary? _layoutCache; + private PropertiesDictionary? _properties; private int _sequenceId; /// @@ -79,6 +81,9 @@ public class LogEventInfo public LogEventInfo() { TimeStamp = TimeSource.Current.Time; + _message = string.Empty; + LoggerName = string.Empty; + Level = LogLevel.Off; } /// @@ -87,7 +92,7 @@ public LogEventInfo() /// Log level. /// Override default Logger name. Default is used when null /// Log message including parameter placeholders. - public LogEventInfo(LogLevel level, string loggerName, [Localizable(false)] string message) + public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] string message) : this(level, loggerName, null, message, null, null) { } @@ -99,7 +104,7 @@ public LogEventInfo(LogLevel level, string loggerName, [Localizable(false)] stri /// Override default Logger name. Default is used when null /// Log message including parameter placeholders. /// Already parsed message template parameters. - public LogEventInfo(LogLevel level, string loggerName, [Localizable(false)] string message, IList messageTemplateParameters) + public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] string message, IList? messageTemplateParameters) : this(level, loggerName, null, message, null, null) { if (messageTemplateParameters != null) @@ -123,7 +128,7 @@ public LogEventInfo(LogLevel level, string loggerName, [Localizable(false)] stri /// Pre-formatted log message for ${message}. /// Log message-template including parameter placeholders for ${message:raw=true}. /// Already parsed message template parameters. - public LogEventInfo(LogLevel level, string loggerName, [Localizable(false)] string formattedMessage, [Localizable(false)] string messageTemplate, IList messageTemplateParameters) + public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] string formattedMessage, [Localizable(false)] string messageTemplate, IList? messageTemplateParameters) : this(level, loggerName, messageTemplate, messageTemplateParameters) { _formattedMessage = formattedMessage; @@ -138,7 +143,7 @@ public LogEventInfo(LogLevel level, string loggerName, [Localizable(false)] stri /// Override default Logger name. Default is used when null /// Log message. /// List of event-properties - public LogEventInfo(LogLevel level, string loggerName, [Localizable(false)] string message, IReadOnlyList> eventProperties) + public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] string message, IReadOnlyList>? eventProperties) : this(level, loggerName, null, message, null, null) { if (eventProperties?.Count > 0) @@ -156,7 +161,7 @@ public LogEventInfo(LogLevel level, string loggerName, [Localizable(false)] stri /// An IFormatProvider that supplies culture-specific formatting information. /// Log message including parameter placeholders. /// Parameter array. - public LogEventInfo(LogLevel level, string loggerName, IFormatProvider formatProvider, [Localizable(false)] string message, object[] parameters) + public LogEventInfo(LogLevel level, string? loggerName, IFormatProvider? formatProvider, [Localizable(false)] string message, object?[]? parameters) : this(level, loggerName, formatProvider, message, parameters, null) { } @@ -170,11 +175,11 @@ public LogEventInfo(LogLevel level, string loggerName, IFormatProvider formatPro /// Log message including parameter placeholders. /// Parameter array. /// Exception information. - public LogEventInfo(LogLevel level, string loggerName, IFormatProvider formatProvider, [Localizable(false)] string message, object[] parameters, Exception exception) - : this() + public LogEventInfo(LogLevel level, string? loggerName, IFormatProvider? formatProvider, [Localizable(false)] string message, object?[]? parameters, Exception? exception) { + TimeStamp = TimeSource.Current.Time; Level = level; - LoggerName = loggerName; + LoggerName = loggerName ?? string.Empty; _formatProvider = formatProvider; _message = message; Parameters = parameters; @@ -206,7 +211,7 @@ public int SequenceID /// public LogLevel Level { get; set; } - [CanBeNull] internal CallSiteInformation CallSiteInformation { get; private set; } + [CanBeNull] internal CallSiteInformation? CallSiteInformation { get; private set; } [NotNull] internal CallSiteInformation GetCallSiteInformationInternal() { return CallSiteInformation ?? (CallSiteInformation = new CallSiteInformation()); } @@ -222,7 +227,7 @@ public int SequenceID /// Gets the stack frame of the method that did the logging. /// [Obsolete("Instead use ${callsite} or CallerMemberName. Marked obsolete with NLog 5.3")] - public StackFrame UserStackFrame => CallSiteInformation?.UserStackFrame; + public StackFrame? UserStackFrame => CallSiteInformation?.UserStackFrame; /// /// Gets the number index of the stack frame that represents the user @@ -234,22 +239,22 @@ public int SequenceID /// /// Gets the entire stack trace. /// - public StackTrace StackTrace => CallSiteInformation?.StackTrace; + public StackTrace? StackTrace => CallSiteInformation?.StackTrace; /// /// Gets the callsite class name /// - public string CallerClassName => CallSiteInformation?.GetCallerClassName(null, true, true, true); + public string? CallerClassName => CallSiteInformation?.GetCallerClassName(null, true, true, true); /// /// Gets the callsite member function name /// - public string CallerMemberName => CallSiteInformation?.GetCallerMethodName(null, false, true, true); + public string? CallerMemberName => CallSiteInformation?.GetCallerMethodName(null, false, true, true); /// /// Gets the callsite source file path /// - public string CallerFilePath => CallSiteInformation?.GetCallerFilePath(0); + public string? CallerFilePath => CallSiteInformation?.GetCallerFilePath(0); /// /// Gets the callsite source file line number @@ -259,13 +264,11 @@ public int SequenceID /// /// Gets or sets the exception information. /// - [CanBeNull] - public Exception Exception { get; set; } + public Exception? Exception { get; set; } /// /// Gets or sets the logger name. /// - [CanBeNull] public string LoggerName { get; set; } /// @@ -285,7 +288,7 @@ public string Message /// /// Gets or sets the parameter values or null if no parameters have been specified. /// - public object[] Parameters + public object?[]? Parameters { get => _parameters; set @@ -300,7 +303,7 @@ public object[] Parameters /// Gets or sets the format provider that was provided while logging or /// when no formatProvider was specified. /// - public IFormatProvider FormatProvider + public IFormatProvider? FormatProvider { get => _formatProvider; set @@ -344,7 +347,7 @@ public string FormattedMessage CalcFormattedMessage(); } - return _formattedMessage; + return _formattedMessage ?? Message; } } @@ -355,49 +358,50 @@ public bool HasProperties { get { - if (_properties != null) - { - return _properties.Count > 0; - } - else + if (_properties is null) { - return CreateOrUpdatePropertiesInternal(false)?.Count > 0; + return TryCreatePropertiesInternal()?.Count > 0; } + return _properties.Count > 0; } } /// /// Gets the dictionary of per-event context properties. /// - public IDictionary Properties => CreateOrUpdatePropertiesInternal(); + public IDictionary Properties => _properties ?? CreatePropertiesInternal(); /// /// Gets the dictionary of per-event context properties. - /// Internal helper for the PropertiesDictionary type. /// - /// Create the event-properties dictionary, even if no initial template parameters /// Provided when having parsed the message template and capture template parameters (else null) - /// - internal PropertiesDictionary CreateOrUpdatePropertiesInternal(bool forceCreate = true, IList templateParameters = null) + internal PropertiesDictionary? TryCreatePropertiesInternal(IList? templateParameters = null) { var properties = _properties; if (properties is null) { - if (forceCreate || templateParameters?.Count > 0 || (templateParameters is null && HasMessageTemplateParameters)) + if (templateParameters?.Count > 0 || (templateParameters is null && HasMessageTemplateParameters)) { - properties = new PropertiesDictionary(templateParameters); - Interlocked.CompareExchange(ref _properties, properties, null); - if (templateParameters is null && (!forceCreate || HasMessageTemplateParameters)) - { - // Trigger capture of MessageTemplateParameters from logevent-message - CalcFormattedMessage(); - } + return CreatePropertiesInternal(templateParameters); } } else if (templateParameters != null) { properties.MessageProperties = templateParameters; } + return properties; + } + + private PropertiesDictionary CreatePropertiesInternal(IList? templateParameters = null) + { + PropertiesDictionary properties = new PropertiesDictionary(templateParameters); + Interlocked.CompareExchange(ref _properties, properties, null); + if (templateParameters is null && HasMessageTemplateParameters) + { + // Trigger capture of MessageTemplateParameters from logevent-message + CalcFormattedMessage(); + } + return _properties; } @@ -422,7 +426,7 @@ public MessageTemplateParameters MessageTemplateParameters { get { - if (_properties != null && _properties.MessageProperties.Count > 0) + if (_properties?.MessageProperties.Count > 0) { return new MessageTemplateParameters(_properties.MessageProperties, _message, _parameters); } @@ -453,7 +457,7 @@ public static LogEventInfo CreateNullEvent() /// Override default Logger name. Default is used when null /// The message. /// Instance of . - public static LogEventInfo Create(LogLevel logLevel, string loggerName, [Localizable(false)] string message) + public static LogEventInfo Create(LogLevel logLevel, string? loggerName, [Localizable(false)] string message) { return new LogEventInfo(logLevel, loggerName, null, message, null, null); } @@ -468,7 +472,7 @@ public static LogEventInfo Create(LogLevel logLevel, string loggerName, [Localiz /// The parameters. /// Instance of . [MessageTemplateFormatMethod("message")] - public static LogEventInfo Create(LogLevel logLevel, string loggerName, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object[] parameters) + public static LogEventInfo Create(LogLevel logLevel, string? loggerName, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object?[]? parameters) { return new LogEventInfo(logLevel, loggerName, formatProvider, message, parameters, null); } @@ -481,12 +485,12 @@ public static LogEventInfo Create(LogLevel logLevel, string loggerName, IFormatP /// The format provider. /// The message. /// Instance of . - public static LogEventInfo Create(LogLevel logLevel, string loggerName, IFormatProvider formatProvider, object message) + public static LogEventInfo Create(LogLevel logLevel, string? loggerName, IFormatProvider? formatProvider, object? message) { - Exception exception = message as Exception; + Exception? exception = message as Exception; if (exception is null && message is LogEventInfo logEvent) { - logEvent.LoggerName = loggerName; + logEvent.LoggerName = loggerName ?? logEvent.LoggerName; logEvent.Level = logLevel; logEvent.FormatProvider = formatProvider ?? logEvent.FormatProvider; return logEvent; @@ -504,7 +508,7 @@ public static LogEventInfo Create(LogLevel logLevel, string loggerName, IFormatP /// The format provider. /// The message. /// Instance of . - public static LogEventInfo Create(LogLevel logLevel, string loggerName, Exception exception, IFormatProvider formatProvider, [Localizable(false)] string message) + public static LogEventInfo Create(LogLevel logLevel, string? loggerName, Exception? exception, IFormatProvider? formatProvider, [Localizable(false)] string message) { return new LogEventInfo(logLevel, loggerName, formatProvider, message, null, exception); } @@ -520,7 +524,7 @@ public static LogEventInfo Create(LogLevel logLevel, string loggerName, Exceptio /// The parameters. /// Instance of . [MessageTemplateFormatMethod("message")] - public static LogEventInfo Create(LogLevel logLevel, string loggerName, Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object[] parameters) + public static LogEventInfo Create(LogLevel logLevel, string? loggerName, Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object?[]? parameters) { return new LogEventInfo(logLevel, loggerName, formatProvider, message, parameters, exception); } @@ -571,16 +575,16 @@ public void SetStackTrace(StackTrace stackTrace, int userStackFrame) /// /// /// - public void SetCallerInfo(string callerClassName, string callerMemberName, string callerFilePath, int callerLineNumber) + public void SetCallerInfo(string? callerClassName, string? callerMemberName, string? callerFilePath, int callerLineNumber) { GetCallSiteInformationInternal().SetCallerInfo(callerClassName, callerMemberName, callerFilePath, callerLineNumber); } - internal void AddCachedLayoutValue(Layout layout, object value) + internal void AddCachedLayoutValue(Layout layout, object? value) { if (_layoutCache is null) { - var dictionary = new Dictionary(); + var dictionary = new Dictionary(); dictionary[layout] = value; // Faster than collection initializer if (Interlocked.CompareExchange(ref _layoutCache, dictionary, null) is null) { @@ -593,7 +597,7 @@ internal void AddCachedLayoutValue(Layout layout, object value) } } - internal bool TryGetCachedLayoutValue(Layout layout, out object value) + internal bool TryGetCachedLayoutValue(Layout layout, out object? value) { if (_layoutCache is null) { @@ -609,7 +613,7 @@ internal bool TryGetCachedLayoutValue(Layout layout, out object value) } } - private static bool NeedToPreformatMessage(object[] parameters) + private static bool NeedToPreformatMessage(object?[]? parameters) { // we need to preformat message if it contains any parameters which could possibly // do logging in their ToString() @@ -629,7 +633,7 @@ private static bool NeedToPreformatMessage(object[] parameters) } #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER - internal static bool NeedToPreformatMessage(in ReadOnlySpan parameters) + internal static bool NeedToPreformatMessage(in ReadOnlySpan parameters) { if (parameters.IsEmpty) return false; @@ -647,7 +651,7 @@ internal static bool NeedToPreformatMessage(in ReadOnlySpan parameters) } #endif - private static bool IsSafeToDeferFormatting(object value) + private static bool IsSafeToDeferFormatting(object? value) { return Convert.GetTypeCode(value) != TypeCode.Object; } @@ -660,7 +664,7 @@ internal bool IsLogEventThreadAgnosticImmutable() if (_formattedMessage != null && _parameters?.Length > 0) return false; - var properties = CreateOrUpdatePropertiesInternal(false); + var properties = TryCreatePropertiesInternal(); if (properties is null || properties.Count == 0) return true; // No mutable state, no need to precalculate @@ -688,10 +692,13 @@ private static bool HasImmutableProperties(PropertiesDictionary properties) else { // Already spent the time on allocating a Dictionary, also have time for an enumerator - foreach (var property in properties) + using (var propertyEnumerator = properties.GetPropertyEnumerator()) { - if (!IsSafeToDeferFormatting(property.Value)) - return false; + while (propertyEnumerator.MoveNext()) + { + if (!IsSafeToDeferFormatting(propertyEnumerator.Current.Value)) + return false; + } } } @@ -783,14 +790,16 @@ private void ResetFormattedMessage(bool rebuildMessageTemplateParameters) private bool ResetMessageTemplateParameters() { - if (_properties != null) - { - if (HasMessageTemplateParameters) - _properties.MessageProperties = null; + if (_properties is null) + return false; - return _properties.MessageProperties.Count == 0; + if (HasMessageTemplateParameters) + { + _properties.MessageProperties = null; + return true; } - return false; + + return _properties.MessageProperties.Count == 0; } } } diff --git a/src/NLog/Logger-Conditional.cs b/src/NLog/Logger-Conditional.cs index 47996e3d44..3daa0da1eb 100644 --- a/src/NLog/Logger-Conditional.cs +++ b/src/NLog/Logger-Conditional.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -63,7 +65,7 @@ public partial class Logger /// Type of the value. /// The value to be written. [Conditional("DEBUG")] - public void ConditionalDebug(T value) + public void ConditionalDebug(T? value) { Debug(value); } @@ -75,7 +77,7 @@ public void ConditionalDebug(T value) /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. [Conditional("DEBUG")] - public void ConditionalDebug(IFormatProvider formatProvider, T value) + public void ConditionalDebug(IFormatProvider? formatProvider, T? value) { Debug(formatProvider, value); } @@ -98,7 +100,7 @@ public void ConditionalDebug(LogMessageGenerator messageFunc) /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalDebug(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void ConditionalDebug(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { Debug(exception, message, args); } @@ -112,7 +114,7 @@ public void ConditionalDebug(Exception exception, [Localizable(false)][Structure /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalDebug(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void ConditionalDebug(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { Debug(exception, message, args); } @@ -127,7 +129,7 @@ public void ConditionalDebug(Exception exception, [Localizable(false)][Structure /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalDebug(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void ConditionalDebug(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { Debug(exception, formatProvider, message, args); } @@ -140,7 +142,7 @@ public void ConditionalDebug(Exception exception, IFormatProvider formatProvider /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { Debug(formatProvider, message, args); } @@ -162,7 +164,7 @@ public void ConditionalDebug([Localizable(false)] string message) /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { Debug(message, args); } @@ -175,7 +177,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { Debug(message, args); } @@ -190,7 +192,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// The argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { Debug(formatProvider, message, argument); } @@ -203,7 +205,7 @@ public void ConditionalDebug(IFormatProvider formatProvider, [Localiz /// The argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { Debug(message, argument); } @@ -219,7 +221,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTe /// The second argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { Debug(formatProvider, message, argument1, argument2); } @@ -234,7 +236,7 @@ public void ConditionalDebug(IFormatProvider formatProvi /// The second argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { Debug(message, argument1, argument2); } @@ -252,7 +254,7 @@ public void ConditionalDebug([Localizable(false)][Struct /// The third argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { Debug(formatProvider, message, argument1, argument2, argument3); } @@ -269,7 +271,7 @@ public void ConditionalDebug(IFormatProvider /// The third argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { Debug(message, argument1, argument2, argument3); } @@ -283,7 +285,7 @@ public void ConditionalDebug([Localizable(fa #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug(object value) + public void ConditionalDebug(object? value) { Debug(value); } @@ -298,7 +300,7 @@ public void ConditionalDebug(object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug(IFormatProvider formatProvider, object value) + public void ConditionalDebug(IFormatProvider? formatProvider, object? value) { Debug(formatProvider, value); } @@ -315,7 +317,7 @@ public void ConditionalDebug(IFormatProvider formatProvider, object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) + public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2) { Debug(message, arg1, arg2); } @@ -333,7 +335,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) + public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3) { Debug(message, arg1, arg2, arg3); } @@ -350,7 +352,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { Debug(formatProvider, message, argument); } @@ -383,7 +385,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { Debug(formatProvider, message, argument); } @@ -416,7 +418,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { Debug(formatProvider, message, argument); } @@ -449,7 +451,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument) { Debug(formatProvider, message, argument); } @@ -465,7 +467,7 @@ public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, string? argument) { Debug(message, argument); } @@ -482,7 +484,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { Debug(formatProvider, message, argument); } @@ -515,7 +517,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { Debug(formatProvider, message, argument); } @@ -548,7 +550,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { Debug(formatProvider, message, argument); } @@ -581,7 +583,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { Debug(formatProvider, message, argument); } @@ -614,7 +616,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { Debug(formatProvider, message, argument); } @@ -647,7 +649,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void ConditionalDebug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument) { Debug(formatProvider, message, argument); } @@ -663,7 +665,7 @@ public void ConditionalDebug(IFormatProvider formatProvider, [Localizable(false) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] string message, object? argument) { Debug(message, argument); } @@ -681,7 +683,7 @@ public void ConditionalDebug([Localizable(false)][StructuredMessageTemplate] str /// Type of the value. /// The value to be written. [Conditional("DEBUG")] - public void ConditionalTrace(T value) + public void ConditionalTrace(T? value) { Trace(value); } @@ -693,7 +695,7 @@ public void ConditionalTrace(T value) /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. [Conditional("DEBUG")] - public void ConditionalTrace(IFormatProvider formatProvider, T value) + public void ConditionalTrace(IFormatProvider? formatProvider, T? value) { Trace(formatProvider, value); } @@ -716,7 +718,7 @@ public void ConditionalTrace(LogMessageGenerator messageFunc) /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalTrace(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void ConditionalTrace(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { Trace(exception, message, args); } @@ -730,7 +732,7 @@ public void ConditionalTrace(Exception exception, [Localizable(false)][Structure /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalTrace(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void ConditionalTrace(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { Trace(exception, message, args); } @@ -745,7 +747,7 @@ public void ConditionalTrace(Exception exception, [Localizable(false)][Structure /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalTrace(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void ConditionalTrace(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { Trace(exception, formatProvider, message, args); } @@ -759,7 +761,7 @@ public void ConditionalTrace(Exception exception, IFormatProvider formatProvider /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { Trace(formatProvider, message, args); } @@ -781,7 +783,7 @@ public void ConditionalTrace([Localizable(false)] string message) /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { Trace(message, args); } @@ -794,7 +796,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { Trace(message, args); } @@ -809,7 +811,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str /// The argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { Trace(formatProvider, message, argument); } @@ -822,7 +824,7 @@ public void ConditionalTrace(IFormatProvider formatProvider, [Localiz /// The argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { Trace(message, argument); } @@ -838,7 +840,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTe /// The second argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { Trace(formatProvider, message, argument1, argument2); } @@ -853,7 +855,7 @@ public void ConditionalTrace(IFormatProvider formatProvi /// The second argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { Trace(message, argument1, argument2); } @@ -871,7 +873,7 @@ public void ConditionalTrace([Localizable(false)][Struct /// The third argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { Trace(formatProvider, message, argument1, argument2, argument3); } @@ -888,7 +890,7 @@ public void ConditionalTrace(IFormatProvider /// The third argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { Trace(message, argument1, argument2, argument3); } @@ -917,7 +919,7 @@ public void ConditionalTrace(object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace(IFormatProvider formatProvider, object value) + public void ConditionalTrace(IFormatProvider? formatProvider, object? value) { Trace(formatProvider, value); } @@ -934,7 +936,7 @@ public void ConditionalTrace(IFormatProvider formatProvider, object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) + public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2) { Trace(message, arg1, arg2); } @@ -952,7 +954,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) + public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3) { Trace(message, arg1, arg2, arg3); } @@ -969,7 +971,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { Trace(formatProvider, message, argument); } @@ -1002,7 +1004,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { Trace(formatProvider, message, argument); } @@ -1035,7 +1037,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { Trace(formatProvider, message, argument); } @@ -1068,7 +1070,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument) { Trace(formatProvider, message, argument); } @@ -1084,7 +1086,7 @@ public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, string? argument) { Trace(message, argument); } @@ -1101,7 +1103,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { Trace(formatProvider, message, argument); } @@ -1134,7 +1136,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { Trace(formatProvider, message, argument); } @@ -1167,7 +1169,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { Trace(formatProvider, message, argument); } @@ -1200,7 +1202,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { Trace(formatProvider, message, argument); } @@ -1233,7 +1235,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { Trace(formatProvider, message, argument); } @@ -1266,7 +1268,7 @@ public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void ConditionalTrace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument) { Trace(formatProvider, message, argument); } @@ -1282,7 +1284,7 @@ public void ConditionalTrace(IFormatProvider formatProvider, [Localizable(false) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void ConditionalTrace([Localizable(false)][StructuredMessageTemplate] string message, object? argument) { Trace(message, argument); } diff --git a/src/NLog/Logger-V1Compat.cs b/src/NLog/Logger-V1Compat.cs index 278d6ca1a4..58bf83f161 100644 --- a/src/NLog/Logger-V1Compat.cs +++ b/src/NLog/Logger-V1Compat.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -55,7 +57,7 @@ public partial class Logger : ILogger #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, object value) + public void Log(LogLevel level, object? value) { if (IsEnabled(level)) { @@ -73,7 +75,7 @@ public void Log(LogLevel level, object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, object value) + public void Log(LogLevel level, IFormatProvider? formatProvider, object? value) { if (IsEnabled(level)) { @@ -93,7 +95,7 @@ public void Log(LogLevel level, IFormatProvider formatProvider, object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) + public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); @@ -122,7 +124,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) + public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); @@ -150,7 +152,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsEnabled(level)) { @@ -189,7 +191,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsEnabled(level)) { @@ -228,7 +230,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsEnabled(level)) { @@ -267,11 +269,11 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsEnabled(level)) { - WriteToTargets(level, formatProvider, message, new object[] { argument }); + WriteToTargets(level, formatProvider, message, new object?[] { argument }); } } @@ -286,11 +288,11 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, message, new object?[] { argument }); } } @@ -306,7 +308,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, string message, int argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, string message, int argument) { if (IsEnabled(level)) { @@ -345,7 +347,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsEnabled(level)) { @@ -384,7 +386,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsEnabled(level)) { @@ -423,7 +425,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsEnabled(level)) { @@ -462,7 +464,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsEnabled(level)) { @@ -501,7 +503,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); @@ -512,7 +514,7 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal #else if (IsEnabled(level)) { - WriteToTargets(level, formatProvider, message, new object[] { argument }); + WriteToTargets(level, formatProvider, message, new object?[] { argument }); } #endif } @@ -528,7 +530,7 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(fal #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object? argument) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); @@ -539,7 +541,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #else if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, message, new object?[] { argument }); } #endif } @@ -557,7 +559,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsEnabled(level)) { @@ -598,7 +600,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsEnabled(level)) { @@ -639,7 +641,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsEnabled(level)) { @@ -679,7 +681,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(object value) + public void Trace(object? value) { if (IsTraceEnabled) { @@ -696,7 +698,7 @@ public void Trace(object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, object value) + public void Trace(IFormatProvider? formatProvider, object? value) { if (IsTraceEnabled) { @@ -715,14 +717,14 @@ public void Trace(IFormatProvider formatProvider, object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) + public void Trace([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2) { if (IsTraceEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, arg1, arg2); #else - WriteToTargets(LogLevel.Trace, message, new[] { arg1, arg2 }); + WriteToTargets(LogLevel.Trace, message, new object?[] { arg1, arg2 }); #endif } } @@ -739,14 +741,14 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) + public void Trace([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3) { if (IsTraceEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); #else - WriteToTargets(LogLevel.Trace, message, new[] { arg1, arg2, arg3 }); + WriteToTargets(LogLevel.Trace, message, new object?[] { arg1, arg2, arg3 }); #endif } } @@ -762,7 +764,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsTraceEnabled) { @@ -799,7 +801,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsTraceEnabled) { @@ -836,7 +838,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsTraceEnabled) { @@ -873,11 +875,11 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, formatProvider, message, new object?[] { argument }); } } @@ -891,11 +893,11 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace([Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Trace([Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, message, new object?[] { argument }); } } @@ -910,7 +912,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, string message, int argument) + public void Trace(IFormatProvider? formatProvider, string message, int argument) { if (IsTraceEnabled) { @@ -947,7 +949,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsTraceEnabled) { @@ -984,7 +986,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsTraceEnabled) { @@ -1021,7 +1023,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsTraceEnabled) { @@ -1058,7 +1060,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsTraceEnabled) { @@ -1095,14 +1097,14 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument) { if (IsTraceEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, formatProvider, message, argument); #else - WriteToTargets(LogLevel.Trace, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, formatProvider, message, new object?[] { argument }); #endif } } @@ -1117,14 +1119,14 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false)][Structure #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace([Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Trace([Localizable(false)][StructuredMessageTemplate] string message, object? argument) { if (IsTraceEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, message, new object?[] { argument }); #endif } } @@ -1141,7 +1143,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsTraceEnabled) { @@ -1180,7 +1182,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsTraceEnabled) { @@ -1219,7 +1221,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsTraceEnabled) { @@ -1258,7 +1260,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(object value) + public void Debug(object? value) { if (IsDebugEnabled) { @@ -1275,7 +1277,7 @@ public void Debug(object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, object value) + public void Debug(IFormatProvider? formatProvider, object? value) { if (IsDebugEnabled) { @@ -1294,14 +1296,14 @@ public void Debug(IFormatProvider formatProvider, object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) + public void Debug([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2) { if (IsDebugEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, arg1, arg2); #else - WriteToTargets(LogLevel.Debug, message, new[] { arg1, arg2 }); + WriteToTargets(LogLevel.Debug, message, new object?[] { arg1, arg2 }); #endif } } @@ -1318,14 +1320,14 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) + public void Debug([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3) { if (IsDebugEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); #else - WriteToTargets(LogLevel.Debug, message, new[] { arg1, arg2, arg3 }); + WriteToTargets(LogLevel.Debug, message, new object?[] { arg1, arg2, arg3 }); #endif } } @@ -1341,7 +1343,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsDebugEnabled) { @@ -1378,7 +1380,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsDebugEnabled) { @@ -1415,7 +1417,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsDebugEnabled) { @@ -1452,11 +1454,11 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, formatProvider, message, new object?[] { argument }); } } @@ -1470,11 +1472,11 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug([Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Debug([Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, message, new object?[] { argument }); } } @@ -1489,7 +1491,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsDebugEnabled) { @@ -1526,7 +1528,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsDebugEnabled) { @@ -1563,7 +1565,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsDebugEnabled) { @@ -1600,7 +1602,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsDebugEnabled) { @@ -1637,7 +1639,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsDebugEnabled) { @@ -1674,14 +1676,14 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument) { if (IsDebugEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, formatProvider, message, argument); #else - WriteToTargets(LogLevel.Debug, formatProvider, message, new[] { argument }); + WriteToTargets(LogLevel.Debug, formatProvider, message, new object?[] { argument }); #endif } } @@ -1696,14 +1698,14 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false)][Structure #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug([Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Debug([Localizable(false)][StructuredMessageTemplate] string message, object? argument) { if (IsDebugEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Debug, message, new[] { argument }); + WriteToTargets(LogLevel.Debug, message, new object?[] { argument }); #endif } } @@ -1720,7 +1722,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsDebugEnabled) { @@ -1759,7 +1761,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsDebugEnabled) { @@ -1798,7 +1800,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsDebugEnabled) { @@ -1837,7 +1839,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(object value) + public void Info(object? value) { if (IsInfoEnabled) { @@ -1854,7 +1856,7 @@ public void Info(object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, object value) + public void Info(IFormatProvider? formatProvider, object? value) { if (IsInfoEnabled) { @@ -1873,14 +1875,14 @@ public void Info(IFormatProvider formatProvider, object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) + public void Info([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2) { if (IsInfoEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, arg1, arg2); #else - WriteToTargets(LogLevel.Info, message, new[] { arg1, arg2 }); + WriteToTargets(LogLevel.Info, message, new object?[] { arg1, arg2 }); #endif } } @@ -1897,14 +1899,14 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) + public void Info([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3) { if (IsInfoEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); #else - WriteToTargets(LogLevel.Info, message, new[] { arg1, arg2, arg3 }); + WriteToTargets(LogLevel.Info, message, new object?[] { arg1, arg2, arg3 }); #endif } } @@ -1920,7 +1922,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsInfoEnabled) { @@ -1957,7 +1959,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsInfoEnabled) { @@ -1994,7 +1996,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsInfoEnabled) { @@ -2031,11 +2033,11 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, formatProvider, message, new object?[] { argument }); } } @@ -2049,11 +2051,11 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info([Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Info([Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, message, new object?[] { argument }); } } @@ -2068,7 +2070,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsInfoEnabled) { @@ -2105,7 +2107,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsInfoEnabled) { @@ -2142,7 +2144,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsInfoEnabled) { @@ -2179,7 +2181,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsInfoEnabled) { @@ -2216,7 +2218,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsInfoEnabled) { @@ -2253,14 +2255,14 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument) { if (IsInfoEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, formatProvider, message, argument); #else - WriteToTargets(LogLevel.Info, formatProvider, message, new[] { argument }); + WriteToTargets(LogLevel.Info, formatProvider, message, new object?[] { argument }); #endif } } @@ -2275,14 +2277,14 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)][Structured #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info([Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Info([Localizable(false)][StructuredMessageTemplate] string message, object? argument) { if (IsInfoEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Info, message, new[] { argument }); + WriteToTargets(LogLevel.Info, message, new object?[] { argument }); #endif } } @@ -2299,7 +2301,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsInfoEnabled) { @@ -2338,7 +2340,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsInfoEnabled) { @@ -2377,7 +2379,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsInfoEnabled) { @@ -2416,7 +2418,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(object value) + public void Warn(object? value) { if (IsWarnEnabled) { @@ -2433,7 +2435,7 @@ public void Warn(object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, object value) + public void Warn(IFormatProvider? formatProvider, object? value) { if (IsWarnEnabled) { @@ -2452,14 +2454,14 @@ public void Warn(IFormatProvider formatProvider, object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) + public void Warn([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2) { if (IsWarnEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, arg1, arg2); #else - WriteToTargets(LogLevel.Warn, message, new[] { arg1, arg2 }); + WriteToTargets(LogLevel.Warn, message, new object?[] { arg1, arg2 }); #endif } } @@ -2476,14 +2478,14 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) + public void Warn([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3) { if (IsWarnEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); #else - WriteToTargets(LogLevel.Warn, message, new[] { arg1, arg2, arg3 }); + WriteToTargets(LogLevel.Warn, message, new object?[] { arg1, arg2, arg3 }); #endif } } @@ -2499,7 +2501,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsWarnEnabled) { @@ -2536,7 +2538,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsWarnEnabled) { @@ -2573,7 +2575,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsWarnEnabled) { @@ -2610,11 +2612,11 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, formatProvider, message, new object?[] { argument }); } } @@ -2628,11 +2630,11 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn([Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Warn([Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, message, new object?[] { argument }); } } @@ -2647,7 +2649,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsWarnEnabled) { @@ -2684,7 +2686,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsWarnEnabled) { @@ -2721,7 +2723,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsWarnEnabled) { @@ -2758,7 +2760,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsWarnEnabled) { @@ -2795,7 +2797,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsWarnEnabled) { @@ -2832,14 +2834,14 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument) { if (IsWarnEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, formatProvider, message, argument); #else - WriteToTargets(LogLevel.Warn, formatProvider, message, new[] { argument }); + WriteToTargets(LogLevel.Warn, formatProvider, message, new object?[] { argument }); #endif } } @@ -2854,14 +2856,14 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)][Structured #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn([Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Warn([Localizable(false)][StructuredMessageTemplate] string message, object? argument) { if (IsWarnEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Warn, message, new[] { argument }); + WriteToTargets(LogLevel.Warn, message, new object?[] { argument }); #endif } } @@ -2878,7 +2880,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsWarnEnabled) { @@ -2917,7 +2919,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsWarnEnabled) { @@ -2956,7 +2958,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsWarnEnabled) { @@ -2995,7 +2997,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(object value) + public void Error(object? value) { if (IsErrorEnabled) { @@ -3012,7 +3014,7 @@ public void Error(object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, object value) + public void Error(IFormatProvider? formatProvider, object? value) { if (IsErrorEnabled) { @@ -3031,14 +3033,14 @@ public void Error(IFormatProvider formatProvider, object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) + public void Error([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2) { if (IsErrorEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, arg1, arg2); #else - WriteToTargets(LogLevel.Error, message, new[] { arg1, arg2 }); + WriteToTargets(LogLevel.Error, message, new object?[] { arg1, arg2 }); #endif } } @@ -3055,14 +3057,14 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) + public void Error([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3) { if (IsErrorEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); #else - WriteToTargets(LogLevel.Error, message, new[] { arg1, arg2, arg3 }); + WriteToTargets(LogLevel.Error, message, new object?[] { arg1, arg2, arg3 }); #endif } } @@ -3078,7 +3080,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsErrorEnabled) { @@ -3115,7 +3117,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsErrorEnabled) { @@ -3152,7 +3154,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsErrorEnabled) { @@ -3189,11 +3191,11 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, formatProvider, message, new object?[] { argument }); } } @@ -3207,11 +3209,11 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error([Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Error([Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, message, new object?[] { argument }); } } @@ -3226,7 +3228,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsErrorEnabled) { @@ -3263,7 +3265,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsErrorEnabled) { @@ -3300,7 +3302,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsErrorEnabled) { @@ -3337,7 +3339,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsErrorEnabled) { @@ -3374,7 +3376,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsErrorEnabled) { @@ -3411,14 +3413,14 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument) { if (IsErrorEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, formatProvider, message, argument); #else - WriteToTargets(LogLevel.Error, formatProvider, message, new[] { argument }); + WriteToTargets(LogLevel.Error, formatProvider, message, new object?[] { argument }); #endif } } @@ -3433,14 +3435,14 @@ public void Error(IFormatProvider formatProvider, [Localizable(false)][Structure #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error([Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Error([Localizable(false)][StructuredMessageTemplate] string message, object? argument) { if (IsErrorEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Error, message, new[] { argument }); + WriteToTargets(LogLevel.Error, message, new object?[] { argument }); #endif } } @@ -3457,7 +3459,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsErrorEnabled) { @@ -3496,7 +3498,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsErrorEnabled) { @@ -3535,7 +3537,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsErrorEnabled) { @@ -3574,7 +3576,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(object value) + public void Fatal(object? value) { if (IsFatalEnabled) { @@ -3591,7 +3593,7 @@ public void Fatal(object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, object value) + public void Fatal(IFormatProvider? formatProvider, object? value) { if (IsFatalEnabled) { @@ -3610,14 +3612,14 @@ public void Fatal(IFormatProvider formatProvider, object value) #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2) + public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2) { if (IsFatalEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, arg1, arg2); #else - WriteToTargets(LogLevel.Fatal, message, new[] { arg1, arg2 }); + WriteToTargets(LogLevel.Fatal, message, new object?[] { arg1, arg2 }); #endif } } @@ -3634,14 +3636,14 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object arg1, object arg2, object arg3) + public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3) { if (IsFatalEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); #else - WriteToTargets(LogLevel.Fatal, message, new[] { arg1, arg2, arg3 }); + WriteToTargets(LogLevel.Fatal, message, new object?[] { arg1, arg2, arg3 }); #endif } } @@ -3657,7 +3659,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, bool argument) { if (IsFatalEnabled) { @@ -3694,7 +3696,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, char argument) { if (IsFatalEnabled) { @@ -3731,7 +3733,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, byte argument) { if (IsFatalEnabled) { @@ -3768,11 +3770,11 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, formatProvider, message, new object?[] { argument }); } } @@ -3786,11 +3788,11 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, string argument) + public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, string? argument) { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, message, new object?[] { argument }); } } @@ -3805,7 +3807,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, int argument) { if (IsFatalEnabled) { @@ -3842,7 +3844,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, long argument) { if (IsFatalEnabled) { @@ -3879,7 +3881,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, float argument) { if (IsFatalEnabled) { @@ -3916,7 +3918,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, double argument) { if (IsFatalEnabled) { @@ -3953,7 +3955,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, decimal argument) { if (IsFatalEnabled) { @@ -3990,14 +3992,14 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument) { if (IsFatalEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, formatProvider, message, argument); #else - WriteToTargets(LogLevel.Fatal, formatProvider, message, new[] { argument }); + WriteToTargets(LogLevel.Fatal, formatProvider, message, new object?[] { argument }); #endif } } @@ -4012,14 +4014,14 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false)][Structure #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object argument) + public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, object? argument) { if (IsFatalEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Fatal, message, new[] { argument }); + WriteToTargets(LogLevel.Fatal, message, new object?[] { argument }); #endif } } @@ -4036,7 +4038,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, sbyte argument) { if (IsFatalEnabled) { @@ -4075,7 +4077,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, uint argument) { if (IsFatalEnabled) { @@ -4114,7 +4116,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER [OverloadResolutionPriority(-1)] #endif - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, ulong argument) { if (IsFatalEnabled) { @@ -4145,12 +4147,12 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message // end of generated code - void ILoggerBase.LogException(LogLevel level, [Localizable(false)] string message, Exception exception) + void ILoggerBase.LogException(LogLevel level, [Localizable(false)] string message, Exception? exception) { Log(level, exception, message, NLog.Internal.ArrayHelper.Empty()); } - void ILoggerBase.Log(LogLevel level, [Localizable(false)] string message, Exception exception) + void ILoggerBase.Log(LogLevel level, [Localizable(false)] string message, Exception? exception) { Log(level, exception, message, NLog.Internal.ArrayHelper.Empty()); } @@ -4158,12 +4160,12 @@ void ILoggerBase.Log(LogLevel level, [Localizable(false)] string message, Except /// [Obsolete("Use Trace(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] [EditorBrowsable(EditorBrowsableState.Never)] - public void TraceException([Localizable(false)] string message, Exception exception) + public void TraceException([Localizable(false)] string message, Exception? exception) { Trace(exception, message); } - void ILogger.Trace([Localizable(false)] string message, Exception exception) + void ILogger.Trace([Localizable(false)] string message, Exception? exception) { Trace(exception, message); } @@ -4171,12 +4173,12 @@ void ILogger.Trace([Localizable(false)] string message, Exception exception) /// [Obsolete("Use Debug(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] [EditorBrowsable(EditorBrowsableState.Never)] - public void DebugException([Localizable(false)] string message, Exception exception) + public void DebugException([Localizable(false)] string message, Exception? exception) { Debug(exception, message); } - void ILogger.Debug([Localizable(false)] string message, Exception exception) + void ILogger.Debug([Localizable(false)] string message, Exception? exception) { Debug(exception, message); } @@ -4184,12 +4186,12 @@ void ILogger.Debug([Localizable(false)] string message, Exception exception) /// [Obsolete("Use Info(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] [EditorBrowsable(EditorBrowsableState.Never)] - public void InfoException([Localizable(false)] string message, Exception exception) + public void InfoException([Localizable(false)] string message, Exception? exception) { Info(exception, message); } - void ILogger.Info([Localizable(false)] string message, Exception exception) + void ILogger.Info([Localizable(false)] string message, Exception? exception) { Info(exception, message); } @@ -4197,12 +4199,12 @@ void ILogger.Info([Localizable(false)] string message, Exception exception) /// [Obsolete("Use Warn(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] [EditorBrowsable(EditorBrowsableState.Never)] - public void WarnException([Localizable(false)] string message, Exception exception) + public void WarnException([Localizable(false)] string message, Exception? exception) { Warn(exception, message); } - void ILogger.Warn([Localizable(false)] string message, Exception exception) + void ILogger.Warn([Localizable(false)] string message, Exception? exception) { Warn(exception, message); } @@ -4210,12 +4212,12 @@ void ILogger.Warn([Localizable(false)] string message, Exception exception) /// [Obsolete("Use Error(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] [EditorBrowsable(EditorBrowsableState.Never)] - public void ErrorException([Localizable(false)] string message, Exception exception) + public void ErrorException([Localizable(false)] string message, Exception? exception) { Error(exception, message); } - void ILogger.Error([Localizable(false)] string message, Exception exception) + void ILogger.Error([Localizable(false)] string message, Exception? exception) { Error(exception, message); } @@ -4223,12 +4225,12 @@ void ILogger.Error([Localizable(false)] string message, Exception exception) /// [Obsolete("Use Fatal(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] [EditorBrowsable(EditorBrowsableState.Never)] - public void FatalException([Localizable(false)] string message, Exception exception) + public void FatalException([Localizable(false)] string message, Exception? exception) { Fatal(exception, message); } - void ILogger.Fatal([Localizable(false)] string message, Exception exception) + void ILogger.Fatal([Localizable(false)] string message, Exception? exception) { Fatal(exception, message); } diff --git a/src/NLog/Logger-generated.cs b/src/NLog/Logger-generated.cs index a7d28fb266..3d3b26ed44 100644 --- a/src/NLog/Logger-generated.cs +++ b/src/NLog/Logger-generated.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -108,7 +110,7 @@ public bool IsFatalEnabled /// /// Type of the value. /// The value to be written. - public void Trace(T value) + public void Trace(T? value) { if (IsTraceEnabled) { @@ -122,7 +124,7 @@ public void Trace(T value) /// Type of the value. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - public void Trace(IFormatProvider formatProvider, T value) + public void Trace(IFormatProvider? formatProvider, T? value) { if (IsTraceEnabled) { @@ -150,7 +152,7 @@ public void Trace(LogMessageGenerator messageFunc) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsTraceEnabled) { @@ -176,7 +178,7 @@ public void Trace([Localizable(false)] string message) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Trace([Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Trace([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsTraceEnabled) { @@ -191,7 +193,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Trace([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Trace([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsTraceEnabled) { @@ -206,7 +208,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Trace(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Trace(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsTraceEnabled) { @@ -220,7 +222,7 @@ public void Trace(Exception exception, [Localizable(false)][StructuredMessageTem /// /// A to be written. /// An exception to be logged. - public void Trace(Exception exception, [Localizable(false)] string message) + public void Trace(Exception? exception, [Localizable(false)] string message) { if (IsTraceEnabled) { @@ -235,7 +237,7 @@ public void Trace(Exception exception, [Localizable(false)] string message) /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Trace(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Trace(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsTraceEnabled) { @@ -251,7 +253,7 @@ public void Trace(Exception exception, [Localizable(false)][StructuredMessageTem /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Trace(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Trace(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsTraceEnabled) { @@ -267,14 +269,14 @@ public void Trace(Exception exception, IFormatProvider formatProvider, [Localiza /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (IsTraceEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, formatProvider, message, argument); #else - WriteToTargets(LogLevel.Trace, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, formatProvider, message, new object?[] { argument }); #endif } } @@ -286,14 +288,14 @@ public void Trace(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Trace([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Trace([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (IsTraceEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, message, new object?[] { argument }); #endif } } @@ -308,14 +310,14 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] str /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (IsTraceEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, formatProvider, message, argument1, argument2); #else - WriteToTargets(LogLevel.Trace, formatProvider, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.Trace, formatProvider, message, new object?[] { argument1, argument2 }); #endif } } @@ -329,14 +331,14 @@ public void Trace(IFormatProvider formatProvider, [Local /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Trace([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Trace([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (IsTraceEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.Trace, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.Trace, message, new object?[] { argument1, argument2 }); #endif } } @@ -353,14 +355,14 @@ public void Trace([Localizable(false)][StructuredMessage /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Trace(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Trace(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (IsTraceEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, formatProvider, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Trace, formatProvider, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Trace, formatProvider, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -376,14 +378,14 @@ public void Trace(IFormatProvider formatProv /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Trace([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Trace([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (IsTraceEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Trace, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Trace, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -400,7 +402,7 @@ public void Trace([Localizable(false)][Struc /// /// Type of the value. /// The value to be written. - public void Debug(T value) + public void Debug(T? value) { if (IsDebugEnabled) { @@ -414,7 +416,7 @@ public void Debug(T value) /// Type of the value. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - public void Debug(IFormatProvider formatProvider, T value) + public void Debug(IFormatProvider? formatProvider, T? value) { if (IsDebugEnabled) { @@ -442,7 +444,7 @@ public void Debug(LogMessageGenerator messageFunc) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsDebugEnabled) { @@ -468,7 +470,7 @@ public void Debug([Localizable(false)] string message) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Debug([Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Debug([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsDebugEnabled) { @@ -483,7 +485,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Debug([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Debug([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsDebugEnabled) { @@ -498,7 +500,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Debug(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Debug(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsDebugEnabled) { @@ -512,7 +514,7 @@ public void Debug(Exception exception, [Localizable(false)][StructuredMessageTem /// /// A to be written. /// An exception to be logged. - public void Debug(Exception exception, [Localizable(false)] string message) + public void Debug(Exception? exception, [Localizable(false)] string message) { if (IsDebugEnabled) { @@ -527,7 +529,7 @@ public void Debug(Exception exception, [Localizable(false)] string message) /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Debug(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Debug(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsDebugEnabled) { @@ -543,7 +545,7 @@ public void Debug(Exception exception, [Localizable(false)][StructuredMessageTem /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Debug(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Debug(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsDebugEnabled) { @@ -559,14 +561,14 @@ public void Debug(Exception exception, IFormatProvider formatProvider, [Localiza /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (IsDebugEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, formatProvider, message, argument); #else - WriteToTargets(LogLevel.Debug, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, formatProvider, message, new object?[] { argument }); #endif } } @@ -578,14 +580,14 @@ public void Debug(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Debug([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Debug([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (IsDebugEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, message, new object?[] { argument }); #endif } } @@ -600,14 +602,14 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] str /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (IsDebugEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, formatProvider, message, argument1, argument2); #else - WriteToTargets(LogLevel.Debug, formatProvider, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.Debug, formatProvider, message, new object?[] { argument1, argument2 }); #endif } } @@ -621,14 +623,14 @@ public void Debug(IFormatProvider formatProvider, [Local /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Debug([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Debug([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (IsDebugEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.Debug, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.Debug, message, new object?[] { argument1, argument2 }); #endif } } @@ -645,14 +647,14 @@ public void Debug([Localizable(false)][StructuredMessage /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Debug(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Debug(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (IsDebugEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, formatProvider, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Debug, formatProvider, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Debug, formatProvider, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -668,14 +670,14 @@ public void Debug(IFormatProvider formatProv /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Debug([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Debug([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (IsDebugEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Debug, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Debug, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -692,7 +694,7 @@ public void Debug([Localizable(false)][Struc /// /// Type of the value. /// The value to be written. - public void Info(T value) + public void Info(T? value) { if (IsInfoEnabled) { @@ -706,7 +708,7 @@ public void Info(T value) /// Type of the value. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - public void Info(IFormatProvider formatProvider, T value) + public void Info(IFormatProvider? formatProvider, T? value) { if (IsInfoEnabled) { @@ -734,7 +736,7 @@ public void Info(LogMessageGenerator messageFunc) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsInfoEnabled) { @@ -760,7 +762,7 @@ public void Info([Localizable(false)] string message) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Info([Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Info([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsInfoEnabled) { @@ -775,7 +777,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Info([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Info([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsInfoEnabled) { @@ -790,7 +792,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Info(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Info(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsInfoEnabled) { @@ -804,7 +806,7 @@ public void Info(Exception exception, [Localizable(false)][StructuredMessageTemp /// /// A to be written. /// An exception to be logged. - public void Info(Exception exception, [Localizable(false)] string message) + public void Info(Exception? exception, [Localizable(false)] string message) { if (IsInfoEnabled) { @@ -819,7 +821,7 @@ public void Info(Exception exception, [Localizable(false)] string message) /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Info(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Info(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsInfoEnabled) { @@ -835,7 +837,7 @@ public void Info(Exception exception, [Localizable(false)][StructuredMessageTemp /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Info(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Info(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsInfoEnabled) { @@ -851,14 +853,14 @@ public void Info(Exception exception, IFormatProvider formatProvider, [Localizab /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (IsInfoEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, formatProvider, message, argument); #else - WriteToTargets(LogLevel.Info, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, formatProvider, message, new object?[] { argument }); #endif } } @@ -870,14 +872,14 @@ public void Info(IFormatProvider formatProvider, [Localizable(false)] /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Info([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Info([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (IsInfoEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Info, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, message, new object?[] { argument }); #endif } } @@ -892,14 +894,14 @@ public void Info([Localizable(false)][StructuredMessageTemplate] stri /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (IsInfoEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, formatProvider, message, argument1, argument2); #else - WriteToTargets(LogLevel.Info, formatProvider, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.Info, formatProvider, message, new object?[] { argument1, argument2 }); #endif } } @@ -913,14 +915,14 @@ public void Info(IFormatProvider formatProvider, [Locali /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Info([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Info([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (IsInfoEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.Info, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.Info, message, new object?[] { argument1, argument2 }); #endif } } @@ -937,14 +939,14 @@ public void Info([Localizable(false)][StructuredMessageT /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Info(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Info(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (IsInfoEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, formatProvider, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Info, formatProvider, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Info, formatProvider, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -960,14 +962,14 @@ public void Info(IFormatProvider formatProvi /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Info([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Info([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (IsInfoEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Info, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Info, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -984,7 +986,7 @@ public void Info([Localizable(false)][Struct /// /// Type of the value. /// The value to be written. - public void Warn(T value) + public void Warn(T? value) { if (IsWarnEnabled) { @@ -998,7 +1000,7 @@ public void Warn(T value) /// Type of the value. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - public void Warn(IFormatProvider formatProvider, T value) + public void Warn(IFormatProvider? formatProvider, T? value) { if (IsWarnEnabled) { @@ -1026,7 +1028,7 @@ public void Warn(LogMessageGenerator messageFunc) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsWarnEnabled) { @@ -1052,7 +1054,7 @@ public void Warn([Localizable(false)] string message) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Warn([Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Warn([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsWarnEnabled) { @@ -1067,7 +1069,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Warn([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Warn([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsWarnEnabled) { @@ -1082,7 +1084,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Warn(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Warn(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsWarnEnabled) { @@ -1096,7 +1098,7 @@ public void Warn(Exception exception, [Localizable(false)][StructuredMessageTemp /// /// A to be written. /// An exception to be logged. - public void Warn(Exception exception, [Localizable(false)] string message) + public void Warn(Exception? exception, [Localizable(false)] string message) { if (IsWarnEnabled) { @@ -1111,7 +1113,7 @@ public void Warn(Exception exception, [Localizable(false)] string message) /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Warn(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Warn(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsWarnEnabled) { @@ -1127,7 +1129,7 @@ public void Warn(Exception exception, [Localizable(false)][StructuredMessageTemp /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Warn(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Warn(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsWarnEnabled) { @@ -1143,14 +1145,14 @@ public void Warn(Exception exception, IFormatProvider formatProvider, [Localizab /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (IsWarnEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, formatProvider, message, argument); #else - WriteToTargets(LogLevel.Warn, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, formatProvider, message, new object?[] { argument }); #endif } } @@ -1162,14 +1164,14 @@ public void Warn(IFormatProvider formatProvider, [Localizable(false)] /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Warn([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Warn([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (IsWarnEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, message, new object?[] { argument }); #endif } } @@ -1184,14 +1186,14 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] stri /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (IsWarnEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, formatProvider, message, argument1, argument2); #else - WriteToTargets(LogLevel.Warn, formatProvider, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.Warn, formatProvider, message, new object?[] { argument1, argument2 }); #endif } } @@ -1205,14 +1207,14 @@ public void Warn(IFormatProvider formatProvider, [Locali /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Warn([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Warn([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (IsWarnEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.Warn, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.Warn, message, new object?[] { argument1, argument2 }); #endif } } @@ -1229,14 +1231,14 @@ public void Warn([Localizable(false)][StructuredMessageT /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Warn(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Warn(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (IsWarnEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, formatProvider, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Warn, formatProvider, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Warn, formatProvider, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -1252,14 +1254,14 @@ public void Warn(IFormatProvider formatProvi /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Warn([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Warn([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (IsWarnEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Warn, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Warn, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -1276,7 +1278,7 @@ public void Warn([Localizable(false)][Struct /// /// Type of the value. /// The value to be written. - public void Error(T value) + public void Error(T? value) { if (IsErrorEnabled) { @@ -1290,7 +1292,7 @@ public void Error(T value) /// Type of the value. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - public void Error(IFormatProvider formatProvider, T value) + public void Error(IFormatProvider? formatProvider, T? value) { if (IsErrorEnabled) { @@ -1318,7 +1320,7 @@ public void Error(LogMessageGenerator messageFunc) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsErrorEnabled) { @@ -1344,7 +1346,7 @@ public void Error([Localizable(false)] string message) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Error([Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Error([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsErrorEnabled) { @@ -1359,7 +1361,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Error([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Error([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsErrorEnabled) { @@ -1374,7 +1376,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Error(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Error(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsErrorEnabled) { @@ -1388,7 +1390,7 @@ public void Error(Exception exception, [Localizable(false)][StructuredMessageTem /// /// A to be written. /// An exception to be logged. - public void Error(Exception exception, [Localizable(false)] string message) + public void Error(Exception? exception, [Localizable(false)] string message) { if (IsErrorEnabled) { @@ -1403,7 +1405,7 @@ public void Error(Exception exception, [Localizable(false)] string message) /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Error(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Error(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsErrorEnabled) { @@ -1419,7 +1421,7 @@ public void Error(Exception exception, [Localizable(false)][StructuredMessageTem /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Error(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Error(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsErrorEnabled) { @@ -1435,14 +1437,14 @@ public void Error(Exception exception, IFormatProvider formatProvider, [Localiza /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (IsErrorEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, formatProvider, message, argument); #else - WriteToTargets(LogLevel.Error, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, formatProvider, message, new object?[] { argument }); #endif } } @@ -1454,14 +1456,14 @@ public void Error(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Error([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Error([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (IsErrorEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Error, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, message, new object?[] { argument }); #endif } } @@ -1476,14 +1478,14 @@ public void Error([Localizable(false)][StructuredMessageTemplate] str /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (IsErrorEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, formatProvider, message, argument1, argument2); #else - WriteToTargets(LogLevel.Error, formatProvider, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.Error, formatProvider, message, new object?[] { argument1, argument2 }); #endif } } @@ -1497,14 +1499,14 @@ public void Error(IFormatProvider formatProvider, [Local /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Error([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Error([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (IsErrorEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.Error, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.Error, message, new object?[] { argument1, argument2 }); #endif } } @@ -1521,14 +1523,14 @@ public void Error([Localizable(false)][StructuredMessage /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Error(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Error(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (IsErrorEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, formatProvider, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Error, formatProvider, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Error, formatProvider, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -1544,14 +1546,14 @@ public void Error(IFormatProvider formatProv /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Error([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Error([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (IsErrorEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Error, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Error, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -1568,7 +1570,7 @@ public void Error([Localizable(false)][Struc /// /// Type of the value. /// The value to be written. - public void Fatal(T value) + public void Fatal(T? value) { if (IsFatalEnabled) { @@ -1582,7 +1584,7 @@ public void Fatal(T value) /// Type of the value. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - public void Fatal(IFormatProvider formatProvider, T value) + public void Fatal(IFormatProvider? formatProvider, T? value) { if (IsFatalEnabled) { @@ -1610,7 +1612,7 @@ public void Fatal(LogMessageGenerator messageFunc) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsFatalEnabled) { @@ -1636,7 +1638,7 @@ public void Fatal([Localizable(false)] string message) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsFatalEnabled) { @@ -1651,7 +1653,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsFatalEnabled) { @@ -1666,7 +1668,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Fatal(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Fatal(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsFatalEnabled) { @@ -1680,7 +1682,7 @@ public void Fatal(Exception exception, [Localizable(false)][StructuredMessageTem /// /// A to be written. /// An exception to be logged. - public void Fatal(Exception exception, [Localizable(false)] string message) + public void Fatal(Exception? exception, [Localizable(false)] string message) { if (IsFatalEnabled) { @@ -1695,7 +1697,7 @@ public void Fatal(Exception exception, [Localizable(false)] string message) /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Fatal(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Fatal(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsFatalEnabled) { @@ -1711,7 +1713,7 @@ public void Fatal(Exception exception, [Localizable(false)][StructuredMessageTem /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Fatal(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Fatal(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsFatalEnabled) { @@ -1727,14 +1729,14 @@ public void Fatal(Exception exception, IFormatProvider formatProvider, [Localiza /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (IsFatalEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, formatProvider, message, argument); #else - WriteToTargets(LogLevel.Fatal, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, formatProvider, message, new object?[] { argument }); #endif } } @@ -1746,14 +1748,14 @@ public void Fatal(IFormatProvider formatProvider, [Localizable(false) /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (IsFatalEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, message, new object?[] { argument }); #endif } } @@ -1768,14 +1770,14 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] str /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (IsFatalEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, formatProvider, message, argument1, argument2); #else - WriteToTargets(LogLevel.Fatal, formatProvider, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.Fatal, formatProvider, message, new object?[] { argument1, argument2 }); #endif } } @@ -1789,14 +1791,14 @@ public void Fatal(IFormatProvider formatProvider, [Local /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (IsFatalEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.Fatal, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.Fatal, message, new object?[] { argument1, argument2 }); #endif } } @@ -1813,14 +1815,14 @@ public void Fatal([Localizable(false)][StructuredMessage /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Fatal(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Fatal(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (IsFatalEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, formatProvider, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Fatal, formatProvider, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Fatal, formatProvider, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -1836,18 +1838,18 @@ public void Fatal(IFormatProvider formatProv /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Fatal([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (IsFatalEnabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Fatal, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Fatal, message, new object?[] { argument1, argument2, argument3 }); #endif } } #endregion } -} +} \ No newline at end of file diff --git a/src/NLog/Logger-generated.tt b/src/NLog/Logger-generated.tt index aa69b206ba..4edbc42116 100644 --- a/src/NLog/Logger-generated.tt +++ b/src/NLog/Logger-generated.tt @@ -40,6 +40,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -84,7 +86,7 @@ namespace NLog /// /// Type of the value. /// The value to be written. - public void <#=level#>(T value) + public void <#=level#>(T? value) { if (Is<#=level#>Enabled) { @@ -98,7 +100,7 @@ namespace NLog /// Type of the value. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - public void <#=level#>(IFormatProvider formatProvider, T value) + public void <#=level#>(IFormatProvider? formatProvider, T? value) { if (Is<#=level#>Enabled) { @@ -126,7 +128,7 @@ namespace NLog /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void <#=level#>(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void <#=level#>(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (Is<#=level#>Enabled) { @@ -152,7 +154,7 @@ namespace NLog /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (Is<#=level#>Enabled) { @@ -167,11 +169,11 @@ namespace NLog /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (Is<#=level#>Enabled) { - WriteToTargets(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, args); + WriteToTargetsWithSpan(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, args); } } @@ -182,11 +184,11 @@ namespace NLog /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void <#=level#>(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void <#=level#>(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (Is<#=level#>Enabled) { - WriteToTargets(LogLevel.<#=level#>, exception, Factory.DefaultCultureInfo, message, args); + WriteToTargetsWithSpan(LogLevel.<#=level#>, exception, Factory.DefaultCultureInfo, message, args); } } #endif @@ -196,7 +198,7 @@ namespace NLog /// /// A to be written. /// An exception to be logged. - public void <#=level#>(Exception exception, [Localizable(false)] string message) + public void <#=level#>(Exception? exception, [Localizable(false)] string message) { if (Is<#=level#>Enabled) { @@ -211,7 +213,7 @@ namespace NLog /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void <#=level#>(Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void <#=level#>(Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (Is<#=level#>Enabled) { @@ -227,7 +229,7 @@ namespace NLog /// An exception to be logged. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void <#=level#>(Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void <#=level#>(Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (Is<#=level#>Enabled) { @@ -243,14 +245,14 @@ namespace NLog /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void <#=level#>(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void <#=level#>(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (Is<#=level#>Enabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER - WriteToTargets(LogLevel.<#=level#>, null, formatProvider, message, argument); + WriteToTargetsWithSpan(LogLevel.<#=level#>, null, formatProvider, message, argument); #else - WriteToTargets(LogLevel.<#=level#>, formatProvider, message, new object[] { argument }); + WriteToTargets(LogLevel.<#=level#>, formatProvider, message, new object?[] { argument }); #endif } } @@ -262,14 +264,14 @@ namespace NLog /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (Is<#=level#>Enabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER - WriteToTargets(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, argument); + WriteToTargetsWithSpan(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.<#=level#>, message, new object[] { argument }); + WriteToTargets(LogLevel.<#=level#>, message, new object?[] { argument }); #endif } } @@ -284,14 +286,14 @@ namespace NLog /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void <#=level#>(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void <#=level#>(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (Is<#=level#>Enabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER - WriteToTargets(LogLevel.<#=level#>, null, formatProvider, message, argument1, argument2); + WriteToTargetsWithSpan(LogLevel.<#=level#>, null, formatProvider, message, argument1, argument2); #else - WriteToTargets(LogLevel.<#=level#>, formatProvider, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.<#=level#>, formatProvider, message, new object?[] { argument1, argument2 }); #endif } } @@ -305,14 +307,14 @@ namespace NLog /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (Is<#=level#>Enabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER - WriteToTargets(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, argument1, argument2); + WriteToTargetsWithSpan(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.<#=level#>, message, new object[] { argument1, argument2 }); + WriteToTargets(LogLevel.<#=level#>, message, new object?[] { argument1, argument2 }); #endif } } @@ -329,14 +331,14 @@ namespace NLog /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void <#=level#>(IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void <#=level#>(IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (Is<#=level#>Enabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER - WriteToTargets(LogLevel.<#=level#>, null, formatProvider, message, argument1, argument2, argument3); + WriteToTargetsWithSpan(LogLevel.<#=level#>, null, formatProvider, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.<#=level#>, formatProvider, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.<#=level#>, formatProvider, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -352,14 +354,14 @@ namespace NLog /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (Is<#=level#>Enabled) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER - WriteToTargets(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); + WriteToTargetsWithSpan(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.<#=level#>, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.<#=level#>, message, new object?[] { argument1, argument2, argument3 }); #endif } } diff --git a/src/NLog/Logger.cs b/src/NLog/Logger.cs index 82aa6be296..4ceec59174 100644 --- a/src/NLog/Logger.cs +++ b/src/NLog/Logger.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -51,7 +53,7 @@ public partial class Logger : ILogger internal static readonly Type DefaultLoggerType = typeof(Logger); private ITargetWithFilterChain[] _targetsByLevel = TargetWithFilterChain.NoTargetsByLevel; private Logger _contextLogger; - private ThreadSafeDictionary _contextProperties; + private ThreadSafeDictionary? _contextProperties; private volatile bool _isTraceEnabled; private volatile bool _isDebugEnabled; private volatile bool _isInfoEnabled; @@ -65,12 +67,14 @@ public partial class Logger : ILogger protected internal Logger() { _contextLogger = this; + Name = string.Empty; + Factory = LogManager.LogFactory; } /// /// Occurs when logger configuration changes. /// - public event EventHandler LoggerReconfigured; + public event EventHandler? LoggerReconfigured; /// /// Gets the name of the logger. @@ -89,7 +93,7 @@ protected internal Logger() /// It is recommended to use for modifying context properties /// when same named logger is used at multiple locations or shared by different thread contexts. /// - public IDictionary Properties => _contextProperties ?? System.Threading.Interlocked.CompareExchange(ref _contextProperties, CreateContextPropertiesDictionary(null), null) ?? _contextProperties; + public IDictionary Properties => _contextProperties ?? System.Threading.Interlocked.CompareExchange(ref _contextProperties, CreateContextPropertiesDictionary(null), null) ?? _contextProperties; /// /// Gets a value indicating whether logging is enabled for the specified level. @@ -109,13 +113,13 @@ public bool IsEnabled(LogLevel level) /// Property Name /// Property Value /// New Logger object that automatically appends specified property - public Logger WithProperty(string propertyKey, object propertyValue) + public Logger WithProperty(string propertyKey, object? propertyValue) { if (string.IsNullOrEmpty(propertyKey)) throw new ArgumentException(nameof(propertyKey)); Logger newLogger = CreateChildLogger(); - newLogger._contextProperties[propertyKey] = propertyValue; + newLogger.Properties[propertyKey] = propertyValue; return newLogger; } @@ -131,9 +135,10 @@ public Logger WithProperties(IEnumerable> propertie Guard.ThrowIfNull(properties); Logger newLogger = CreateChildLogger(); + var newProperties = newLogger.Properties; foreach (KeyValuePair property in properties) { - newLogger._contextProperties[property.Key] = property.Value; + newProperties[property.Key] = property.Value; } return newLogger; } @@ -154,7 +159,7 @@ public Logger WithProperties(IEnumerable> propertie /// Property Value [Obsolete("Instead use WithProperty which is safe. If really necessary then one can use Properties-property. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] - public void SetProperty(string propertyKey, object propertyValue) + public void SetProperty(string propertyKey, object? propertyValue) { if (string.IsNullOrEmpty(propertyKey)) throw new ArgumentException(nameof(propertyKey)); @@ -162,11 +167,11 @@ public void SetProperty(string propertyKey, object propertyValue) Properties[propertyKey] = propertyValue; } - private static ThreadSafeDictionary CreateContextPropertiesDictionary(ThreadSafeDictionary contextProperties) + private static ThreadSafeDictionary CreateContextPropertiesDictionary(ThreadSafeDictionary? contextProperties) { - contextProperties = contextProperties != null - ? new ThreadSafeDictionary(contextProperties) - : new ThreadSafeDictionary(); + contextProperties = contextProperties is null + ? new ThreadSafeDictionary() + : new ThreadSafeDictionary(contextProperties); return contextProperties; } @@ -177,7 +182,7 @@ private static ThreadSafeDictionary CreateContextPropertiesDicti /// Value of property /// A disposable object that removes the properties from logical context scope on dispose. /// property-dictionary-keys are case-insensitive - public IDisposable PushScopeProperty(string propertyName, object propertyValue) + public IDisposable PushScopeProperty(string propertyName, object? propertyValue) { return ScopeContext.PushProperty(propertyName, propertyValue); } @@ -189,7 +194,7 @@ public IDisposable PushScopeProperty(string propertyName, object propertyValue) /// Value of property /// A disposable object that removes the properties from logical context scope on dispose. /// property-dictionary-keys are case-insensitive - public IDisposable PushScopeProperty(string propertyName, TValue propertyValue) + public IDisposable PushScopeProperty(string propertyName, TValue? propertyValue) { return ScopeContext.PushProperty(propertyName, propertyValue); } @@ -223,7 +228,7 @@ public IDisposable PushScopeProperties(IReadOnlyCollection /// Value to added to the scope stack /// A disposable object that pops the nested scope state on dispose. - public IDisposable PushScopeNested(T nestedState) + public IDisposable PushScopeNested(T? nestedState) { return ScopeContext.PushNestedState(nestedState); } @@ -233,7 +238,7 @@ public IDisposable PushScopeNested(T nestedState) /// /// Value to added to the scope stack /// A disposable object that pops the nested scope state on dispose. - public IDisposable PushScopeNested(object nestedState) + public IDisposable PushScopeNested(object? nestedState) { return ScopeContext.PushNestedState(nestedState); } @@ -247,7 +252,7 @@ public void Log(LogEventInfo logEvent) var targetsForLevel = GetTargetsForLevelSafe(logEvent.Level); if (targetsForLevel != null) { - if (logEvent.LoggerName is null) + if (logEvent.LoggerName is null || ReferenceEquals(logEvent.LoggerName, string.Empty)) logEvent.LoggerName = Name; if (logEvent.FormatProvider is null) logEvent.FormatProvider = Factory.DefaultCultureInfo; @@ -265,7 +270,7 @@ public void Log(Type wrapperType, LogEventInfo logEvent) var targetsForLevel = GetTargetsForLevelSafe(logEvent.Level); if (targetsForLevel != null) { - if (logEvent.LoggerName is null) + if (logEvent.LoggerName is null || ReferenceEquals(logEvent.LoggerName, string.Empty)) logEvent.LoggerName = Name; if (logEvent.FormatProvider is null) logEvent.FormatProvider = Factory.DefaultCultureInfo; @@ -284,7 +289,7 @@ public void Log(Type wrapperType, LogEventInfo logEvent) /// Type of the value. /// The log level. /// The value to be written. - public void Log(LogLevel level, T value) + public void Log(LogLevel level, T? value) { if (IsEnabled(level)) { @@ -299,7 +304,7 @@ public void Log(LogLevel level, T value) /// The log level. /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. - public void Log(LogLevel level, IFormatProvider formatProvider, T value) + public void Log(LogLevel level, IFormatProvider? formatProvider, T? value) { if (IsEnabled(level)) { @@ -330,7 +335,7 @@ public void Log(LogLevel level, LogMessageGenerator messageFunc) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsEnabled(level)) { @@ -358,7 +363,7 @@ public void Log(LogLevel level, [Localizable(false)] string message) /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsEnabled(level)) { @@ -374,7 +379,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// A to be written. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Log(LogLevel level, Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsEnabled(level)) { @@ -391,7 +396,7 @@ public void Log(LogLevel level, Exception exception, [Localizable(false)][Struct /// A to be written. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, Exception exception, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object[] args) + public void Log(LogLevel level, Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { if (IsEnabled(level)) { @@ -408,7 +413,7 @@ public void Log(LogLevel level, Exception exception, IFormatProvider formatProvi /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); @@ -419,7 +424,7 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Loca #else if (IsEnabled(level)) { - WriteToTargets(level, formatProvider, message, new object[] { argument }); + WriteToTargets(level, formatProvider, message, new object?[] { argument }); } #endif } @@ -432,7 +437,7 @@ public void Log(LogLevel level, IFormatProvider formatProvider, [Loca /// A containing one format item. /// The argument to format. [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument argument) + public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); @@ -443,7 +448,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessag #else if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, message, new object?[] { argument }); } #endif } @@ -459,7 +464,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessag /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); @@ -470,7 +475,7 @@ public void Log(LogLevel level, IFormatProvider formatPr #else if (IsEnabled(level)) { - WriteToTargets(level, formatProvider, message, new object[] { argument1, argument2 }); + WriteToTargets(level, formatProvider, message, new object?[] { argument1, argument2 }); } #endif } @@ -485,7 +490,7 @@ public void Log(LogLevel level, IFormatProvider formatPr /// The first argument to format. /// The second argument to format. [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2) + public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); @@ -496,7 +501,7 @@ public void Log(LogLevel level, [Localizable(false)][Str #else if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument1, argument2 }); + WriteToTargets(level, message, new object?[] { argument1, argument2 }); } #endif } @@ -514,7 +519,7 @@ public void Log(LogLevel level, [Localizable(false)][Str /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, IFormatProvider formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); @@ -525,7 +530,7 @@ public void Log(LogLevel level, IFormatProvi #else if (IsEnabled(level)) { - WriteToTargets(level, formatProvider, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(level, formatProvider, message, new object?[] { argument1, argument2, argument3 }); } #endif } @@ -542,7 +547,7 @@ public void Log(LogLevel level, IFormatProvi /// The second argument to format. /// The third argument to format. [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1 argument1, TArgument2 argument2, TArgument3 argument3) + public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); @@ -553,7 +558,7 @@ public void Log(LogLevel level, [Localizable #else if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument1, argument2, argument3 }); + WriteToTargets(level, message, new object?[] { argument1, argument2, argument3 }); } #endif } @@ -566,7 +571,7 @@ public void Log(LogLevel level, [Localizable /// A containing format items. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { var targetsForLevel = GetTargetsForLevelSafe(level); if (targetsForLevel != null) @@ -583,7 +588,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] /// A to be written. /// Arguments to format. [MessageTemplateFormatMethod("message")] - public void Log(LogLevel level, Exception exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public void Log(LogLevel level, Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { var targetsForLevel = GetTargetsForLevelSafe(level); if (targetsForLevel != null) @@ -592,7 +597,7 @@ public void Log(LogLevel level, Exception exception, [Localizable(false)][Struct } } - private void WriteToTargetsWithSpan(LogLevel level, Exception exception, IFormatProvider formatProvider, string message, params ReadOnlySpan args) + private void WriteToTargetsWithSpan(LogLevel level, Exception? exception, IFormatProvider? formatProvider, string message, params ReadOnlySpan args) { var targetsForLevel = GetTargetsForLevel(level); if (targetsForLevel != null) @@ -601,7 +606,7 @@ private void WriteToTargetsWithSpan(LogLevel level, Exception exception, IFormat } } - private void WriteToTargetsWithSpan(ITargetWithFilterChain targetsForLevel, LogLevel level, Exception exception, IFormatProvider formatProvider, string message, params ReadOnlySpan args) + private void WriteToTargetsWithSpan(ITargetWithFilterChain targetsForLevel, LogLevel level, Exception? exception, IFormatProvider? formatProvider, string message, params ReadOnlySpan args) { if (Factory.AutoMessageTemplateFormatter is null || !LogEventInfo.NeedToPreformatMessage(args)) { @@ -671,7 +676,7 @@ public void Swallow(Action action) /// Return type of the provided function. /// Function to run. /// Result returned by the provided function or the default value of type in case of exception. - public T Swallow(Func func) + public T? Swallow(Func func) { return Swallow(func, default(T)); } @@ -684,7 +689,7 @@ public T Swallow(Func func) /// Function to run. /// Fallback value to return in case of exception. /// Result returned by the provided function or fallback value in case of exception. - public T Swallow(Func func, T fallback) + public T? Swallow(Func func, T? fallback) { try { @@ -755,7 +760,7 @@ public async Task SwallowAsync(Func asyncAction) /// Return type of the provided function. /// Async function to run. /// A task that represents the completion of the supplied task. If the supplied task ends in the state, the result of the new task will be the result of the supplied task; otherwise, the result of the new task will be the default value of type . - public async Task SwallowAsync(Func> asyncFunc) + public async Task SwallowAsync(Func> asyncFunc) { return await SwallowAsync(asyncFunc, default(TResult)).ConfigureAwait(false); } @@ -768,7 +773,7 @@ public async Task SwallowAsync(Func> asyncFunc) /// Async function to run. /// Fallback value to return if the task does not end in the state. /// A task that represents the completion of the supplied task. If the supplied task ends in the state, the result of the new task will be the result of the supplied task; otherwise, the result of the new task will be the fallback value. - public async Task SwallowAsync(Func> asyncFunc, TResult fallback) + public async Task SwallowAsync(Func> asyncFunc, TResult? fallback) { try { @@ -789,12 +794,12 @@ internal void Initialize(string name, ITargetWithFilterChain[] targetsByLevel, L SetConfiguration(targetsByLevel); } - private void WriteToTargets(LogLevel level, string message, object[] args) + private void WriteToTargets(LogLevel level, string message, object?[] args) { WriteToTargets(level, Factory.DefaultCultureInfo, message, args); } - private void WriteToTargets(LogLevel level, IFormatProvider formatProvider, string message, object[] args) + private void WriteToTargets(LogLevel level, IFormatProvider? formatProvider, string message, object?[] args) { var targetsForLevel = GetTargetsForLevel(level); if (targetsForLevel != null) @@ -811,12 +816,12 @@ private void WriteToTargets(LogLevel level, string message) { // please note that this overload calls the overload of LogEventInfo.Create with object[] parameter on purpose - // to avoid unnecessary string.Format (in case of calling Create(LogLevel, string, IFormatProvider, object)) - var logEvent = LogEventInfo.Create(level, Name, Factory.DefaultCultureInfo, message, (object[])null); + var logEvent = LogEventInfo.Create(level, Name, Factory.DefaultCultureInfo, message, (object[]?)null); WriteLogEventToTargets(logEvent, targetsForLevel); } } - private void WriteToTargets(LogLevel level, IFormatProvider formatProvider, T value) + private void WriteToTargets(LogLevel level, IFormatProvider? formatProvider, T? value) { var targetsForLevel = GetTargetsForLevel(level); if (targetsForLevel != null) @@ -826,7 +831,7 @@ private void WriteToTargets(LogLevel level, IFormatProvider formatProvider, T } } - private void WriteToTargets(LogLevel level, Exception ex, string message, object[] args) + private void WriteToTargets(LogLevel level, Exception? ex, string message, object?[]? args) { var targetsForLevel = GetTargetsForLevel(level); if (targetsForLevel != null) @@ -834,12 +839,12 @@ private void WriteToTargets(LogLevel level, Exception ex, string message, object // Translate Exception with missing LogEvent message as log single value var logEvent = message is null && ex != null && !(args?.Length > 0) ? LogEventInfo.Create(level, Name, ExceptionMessageFormatProvider.Instance, ex) : - LogEventInfo.Create(level, Name, ex, Factory.DefaultCultureInfo, message, args); + LogEventInfo.Create(level, Name, ex, Factory.DefaultCultureInfo, message ?? string.Empty, args); WriteLogEventToTargets(logEvent, targetsForLevel); } } - private void WriteToTargets(LogLevel level, Exception ex, IFormatProvider formatProvider, string message, object[] args) + private void WriteToTargets(LogLevel level, Exception? ex, IFormatProvider? formatProvider, string message, object?[] args) { var targetsForLevel = GetTargetsForLevel(level); if (targetsForLevel != null) @@ -904,7 +909,7 @@ internal void SetConfiguration(ITargetWithFilterChain[] targetsByLevel) OnLoggerReconfigured(EventArgs.Empty); } - private ITargetWithFilterChain GetTargetsForLevelSafe(LogLevel level) + private ITargetWithFilterChain GetTargetsForLevelSafe(LogLevel? level) { if (level is null) { diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index daa6a3669d..0941ddd353 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -66,6 +66,7 @@ For all config options and platform support, check https://nlog-project.org/conf true true true + 9 13 diff --git a/src/NLog/Targets/TargetWithContext.cs b/src/NLog/Targets/TargetWithContext.cs index e32259445a..2d9964b0df 100644 --- a/src/NLog/Targets/TargetWithContext.cs +++ b/src/NLog/Targets/TargetWithContext.cs @@ -260,7 +260,7 @@ protected IDictionary GetAllProperties(LogEventInfo logEvent, ID combinedProperties = combinedProperties ?? CreateNewDictionary(logEvent.Properties.Count + (ContextProperties?.Count ?? 0)); bool checkForDuplicates = combinedProperties.Count > 0; bool checkExcludeProperties = ExcludeProperties?.Count > 0; - using (var propertyEnumerator = logEvent.CreateOrUpdatePropertiesInternal().GetPropertyEnumerator()) + using (var propertyEnumerator = logEvent.TryCreatePropertiesInternal().GetPropertyEnumerator()) { while (propertyEnumerator.MoveNext()) { diff --git a/tests/NLog.UnitTests/LoggerTests.cs b/tests/NLog.UnitTests/LoggerTests.cs index 5f16a7713c..9faac2749a 100644 --- a/tests/NLog.UnitTests/LoggerTests.cs +++ b/tests/NLog.UnitTests/LoggerTests.cs @@ -2116,8 +2116,9 @@ public void When_Logging_LogEvent_Without_Level_Defined_No_Exception_Should_Be_T config.LoggingRules.Add(new LoggingRule("*", LogLevel.Trace, target)); LogManager.Configuration = config; var logger = LogManager.GetLogger("A"); - - Assert.Throws(() => logger.Log(new LogEventInfo())); + var logEvent = new LogEventInfo(); + logger.Log(new LogEventInfo()); + Assert.Same(LogLevel.Off, logEvent.Level); } [Fact] @@ -2136,14 +2137,14 @@ public void When_Logging_LogEvent_Without_Logger_Defined_UseLoggerName() Assert.NotNull(target.LastEvent); Assert.Equal(LogLevel.Info, target.LastEvent.Level); Assert.Equal(logger.Name, target.LastEvent.LoggerName); - logger.Log(new LogEventInfo() { Level = LogLevel.Warn, Message = "Hello", LoggerName = string.Empty }); + logger.Log(new LogEventInfo() { Level = LogLevel.Warn, Message = "Hello", LoggerName = " " }); Assert.NotNull(target.LastEvent); Assert.Equal(LogLevel.Warn, target.LastEvent.Level); - Assert.Equal(string.Empty, target.LastEvent.LoggerName); - logger.Log(logger.GetType(), new LogEventInfo() { Level = LogLevel.Error, Message = "Hello", LoggerName = string.Empty }); + Assert.Equal(" ", target.LastEvent.LoggerName); + logger.Log(logger.GetType(), new LogEventInfo() { Level = LogLevel.Error, Message = "Hello", LoggerName = " " }); Assert.NotNull(target.LastEvent); Assert.Equal(LogLevel.Error, target.LastEvent.Level); - Assert.Equal(string.Empty, target.LastEvent.LoggerName); + Assert.Equal(" ", target.LastEvent.LoggerName); } [Fact] From 8100876df087f8e7670877bfafc30a3e421b0a7c Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 8 May 2025 18:02:49 +0200 Subject: [PATCH 108/224] LogFactory with nullable references (#5806) --- .../LoggingConfigurationChangedEventArgs.cs | 8 +- src/NLog/Config/ServiceRepository.cs | 5 +- .../Config/ServiceRepositoryExtensions.cs | 2 + src/NLog/Config/ServiceRepositoryInternal.cs | 23 +- src/NLog/Internal/PropertiesDictionary.cs | 218 ++++++++++++------ src/NLog/LogEventInfo.cs | 4 +- src/NLog/LogFactory.cs | 65 +++--- src/NLog/LogManager.cs | 6 +- src/NLog/Targets/FileTarget.cs | 4 +- 9 files changed, 218 insertions(+), 117 deletions(-) diff --git a/src/NLog/Config/LoggingConfigurationChangedEventArgs.cs b/src/NLog/Config/LoggingConfigurationChangedEventArgs.cs index 17eab604cd..63d9ee1bd7 100644 --- a/src/NLog/Config/LoggingConfigurationChangedEventArgs.cs +++ b/src/NLog/Config/LoggingConfigurationChangedEventArgs.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -45,7 +47,7 @@ public class LoggingConfigurationChangedEventArgs : EventArgs /// /// The new configuration. /// The old configuration. - public LoggingConfigurationChangedEventArgs(LoggingConfiguration activatedConfiguration, LoggingConfiguration deactivatedConfiguration) + public LoggingConfigurationChangedEventArgs(LoggingConfiguration? activatedConfiguration, LoggingConfiguration? deactivatedConfiguration) { ActivatedConfiguration = activatedConfiguration; DeactivatedConfiguration = deactivatedConfiguration; @@ -55,7 +57,7 @@ public LoggingConfigurationChangedEventArgs(LoggingConfiguration activatedConfig /// Gets the old configuration. /// /// The old configuration. - public LoggingConfiguration DeactivatedConfiguration { get; } + public LoggingConfiguration? DeactivatedConfiguration { get; } /// /// Gets the new configuration. @@ -64,6 +66,6 @@ public LoggingConfigurationChangedEventArgs(LoggingConfiguration activatedConfig /// New value can be null when unloading configuration during shutdown. /// /// The new configuration. - public LoggingConfiguration ActivatedConfiguration { get; } + public LoggingConfiguration? ActivatedConfiguration { get; } } } diff --git a/src/NLog/Config/ServiceRepository.cs b/src/NLog/Config/ServiceRepository.cs index ea7e6ef6b3..93436c8472 100644 --- a/src/NLog/Config/ServiceRepository.cs +++ b/src/NLog/Config/ServiceRepository.cs @@ -31,9 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; + using System.Diagnostics.CodeAnalysis; /// /// Interface to register available configuration objects type @@ -53,7 +56,7 @@ public abstract class ServiceRepository : IServiceProvider /// Avoid calling this while handling a LogEvent, since random deadlocks can occur. public abstract object GetService(Type serviceType); - internal abstract bool TryGetService(out T serviceInstance) where T : class; + internal abstract bool TryGetService([NotNullWhen(returnValue: true)] out T? serviceInstance) where T : class; internal ServiceRepository() { diff --git a/src/NLog/Config/ServiceRepositoryExtensions.cs b/src/NLog/Config/ServiceRepositoryExtensions.cs index 8560cc8a55..80e521cd6c 100644 --- a/src/NLog/Config/ServiceRepositoryExtensions.cs +++ b/src/NLog/Config/ServiceRepositoryExtensions.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; diff --git a/src/NLog/Config/ServiceRepositoryInternal.cs b/src/NLog/Config/ServiceRepositoryInternal.cs index 1c2e47f056..58172dc12c 100644 --- a/src/NLog/Config/ServiceRepositoryInternal.cs +++ b/src/NLog/Config/ServiceRepositoryInternal.cs @@ -31,10 +31,13 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using NLog.Internal; /// @@ -44,7 +47,7 @@ internal sealed class ServiceRepositoryInternal : ServiceRepository { private readonly Dictionary> _creatorMap = new Dictionary>(); private readonly object _lockObject = new object(); - public event EventHandler TypeRegistered; + public event EventHandler? TypeRegistered; /// /// Initializes a new instance of the class. @@ -69,18 +72,18 @@ public override void RegisterService(Type type, object instance) public override object GetService(Type serviceType) { - object serviceInstance = TryGetService(serviceType); + object? serviceInstance = TryGetService(serviceType); if (serviceInstance is null) throw new NLogDependencyResolveException("Type not registered in Service Provider", serviceType); return serviceInstance; } - private object TryGetService(Type serviceType) + private object? TryGetService(Type serviceType) { Guard.ThrowIfNull(serviceType); - Func objectResolver = null; + Func? objectResolver = null; lock (_lockObject) { _creatorMap.TryGetValue(serviceType, out objectResolver); @@ -89,10 +92,16 @@ private object TryGetService(Type serviceType) return objectResolver?.Invoke(); } - internal override bool TryGetService(out T serviceInstance) + internal override bool TryGetService([NotNullWhen(returnValue: true)] out T? serviceInstance) where T : class { - serviceInstance = TryGetService(typeof(T)) as T; - return !(serviceInstance is null); + if (TryGetService(typeof(T)) is T service) + { + serviceInstance = service; + return true; + } + + serviceInstance = default(T); + return false; } } } diff --git a/src/NLog/Internal/PropertiesDictionary.cs b/src/NLog/Internal/PropertiesDictionary.cs index d11105f6d8..b1fb4d9bd6 100644 --- a/src/NLog/Internal/PropertiesDictionary.cs +++ b/src/NLog/Internal/PropertiesDictionary.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -47,21 +49,21 @@ namespace NLog.Internal /// in the collection, and in positional order. /// [DebuggerDisplay("Count = {Count}")] - internal sealed class PropertiesDictionary : IDictionary + internal sealed class PropertiesDictionary : IDictionary { private struct PropertyValue { /// /// Value of the property /// - public readonly object Value; + public readonly object? Value; /// /// Has property been captured from message-template ? /// public readonly bool IsMessageProperty; - public PropertyValue(object value, bool isMessageProperty) + public PropertyValue(object? value, bool isMessageProperty) { Value = value; IsMessageProperty = isMessageProperty; @@ -71,21 +73,21 @@ public PropertyValue(object value, bool isMessageProperty) /// /// The properties of the logEvent /// - private Dictionary _eventProperties; + private Dictionary? _eventProperties; /// /// The properties extracted from the message-template /// - private IList _messageProperties; + private IList? _messageProperties; /// /// Wraps the list of message-template-parameters as IDictionary-interface /// /// Message-template-parameters - public PropertiesDictionary(IList messageParameters = null) + public PropertiesDictionary(IList? messageParameters = null) { if (messageParameters?.Count > 0) { - MessageProperties = messageParameters; + _messageProperties = SetMessageProperties(messageParameters, null); } } @@ -126,10 +128,14 @@ private Dictionary EventProperties public IList MessageProperties { get => _messageProperties ?? ArrayHelper.Empty(); - internal set => _messageProperties = SetMessageProperties(value, _messageProperties); } - private IList SetMessageProperties(IList newMessageProperties, IList oldMessageProperties) + public void ResetMessageProperties(IList? newMessageProperties = null) + { + _messageProperties = SetMessageProperties(newMessageProperties, _messageProperties); + } + + private IList? SetMessageProperties(IList? newMessageProperties, IList? oldMessageProperties) { if (_eventProperties is null && VerifyUniqueMessageTemplateParametersFast(newMessageProperties)) { @@ -168,7 +174,7 @@ private static void RemoveOldMessageProperties(IList o } } - private static Dictionary BuildEventProperties(IList messageProperties) + private static Dictionary BuildEventProperties(IList? messageProperties) { if (messageProperties?.Count > 0) { @@ -183,7 +189,7 @@ private static Dictionary BuildEventProperties(IList - public object this[object key] + public object? this[object key] { get { @@ -198,17 +204,14 @@ public object this[object key] } /// - public ICollection Keys => KeyCollection; + public ICollection Keys => IsEmpty ? EmptyKeyCollection : new KeyCollection(this); /// - public ICollection Values => ValueCollection; - - private DictionaryCollection KeyCollection => IsEmpty ? EmptyKeyCollection : new DictionaryCollection(this, true); + public ICollection Values => IsEmpty ? EmptyValueCollection : new ValueCollection(this); - private DictionaryCollection ValueCollection => IsEmpty ? EmptyValueCollection : new DictionaryCollection(this, false); - private static readonly DictionaryCollection EmptyKeyCollection = new DictionaryCollection(new PropertiesDictionary(), true); - private static readonly DictionaryCollection EmptyValueCollection = new DictionaryCollection(new PropertiesDictionary(), false); + private static readonly KeyCollection EmptyKeyCollection = new KeyCollection(new PropertiesDictionary()); + private static readonly ValueCollection EmptyValueCollection = new ValueCollection(new PropertiesDictionary()); /// public int Count => (_eventProperties?.Count) ?? (_messageProperties?.Count) ?? 0; @@ -217,13 +220,13 @@ public object this[object key] public bool IsReadOnly => false; /// - public void Add(object key, object value) + public void Add(object key, object? value) { EventProperties.Add(key, new PropertyValue(value, false)); } /// - public void Add(KeyValuePair item) + public void Add(KeyValuePair item) { Add(item.Key, item.Value); } @@ -238,7 +241,7 @@ public void Clear() } /// - public bool Contains(KeyValuePair item) + public bool Contains(KeyValuePair item) { if (!IsEmpty && (_eventProperties != null || ContainsKey(item.Key))) { @@ -258,7 +261,7 @@ public bool ContainsKey(object key) } /// - public void CopyTo(KeyValuePair[] array, int arrayIndex) + public void CopyTo(KeyValuePair[] array, int arrayIndex) { Guard.ThrowIfNull(array); if (arrayIndex < 0) @@ -279,9 +282,9 @@ internal PropertyDictionaryEnumerator GetPropertyEnumerator() } /// - public IEnumerator> GetEnumerator() + public IEnumerator> GetEnumerator() { - return IsEmpty ? System.Linq.Enumerable.Empty>().GetEnumerator() : new PropertyDictionaryEnumerator(this); + return IsEmpty ? System.Linq.Enumerable.Empty>().GetEnumerator() : new PropertyDictionaryEnumerator(this); } /// @@ -301,14 +304,14 @@ public bool Remove(object key) } /// - public bool Remove(KeyValuePair item) + public bool Remove(KeyValuePair item) { if (!IsEmpty && (_eventProperties != null || ContainsKey(item.Key))) { - if (((IDictionary)EventProperties).Remove(new KeyValuePair(item.Key, new PropertyValue(item.Value, false)))) + if (((ICollection>)EventProperties).Remove(new KeyValuePair(item.Key, new PropertyValue(item.Value, false)))) return true; - if (((IDictionary)EventProperties).Remove(new KeyValuePair(item.Key, new PropertyValue(item.Value, true)))) + if (((ICollection>)EventProperties).Remove(new KeyValuePair(item.Key, new PropertyValue(item.Value, true)))) return true; } @@ -316,17 +319,18 @@ public bool Remove(KeyValuePair item) } /// - public bool TryGetValue(object key, out object value) + public bool TryGetValue(object key, out object? value) { if (!IsEmpty) { - if (_eventProperties is null && _messageProperties?.Count < 5) + if (_eventProperties is null) { return TryLookupMessagePropertyValue(key, out value); } - else if (EventProperties.TryGetValue(key, out var valueItem)) + + if (EventProperties.TryGetValue(key, out var eventProperty)) { - value = valueItem.Value; + value = eventProperty.Value; return true; } } @@ -335,9 +339,23 @@ public bool TryGetValue(object key, out object value) return false; } - private bool TryLookupMessagePropertyValue(object key, out object propertyValue) + private bool TryLookupMessagePropertyValue(object key, out object? propertyValue) { - if (key is string keyString) + if (_messageProperties is null || _messageProperties.Count == 0) + { + propertyValue = null; + return false; + } + + if (_messageProperties.Count > 10) + { + if (EventProperties.TryGetValue(key, out var eventProperty)) + { + propertyValue = eventProperty.Value; + return true; + } + } + else if (key is string keyString) { for (int i = 0; i < _messageProperties.Count; ++i) { @@ -369,7 +387,7 @@ private bool TryLookupMessagePropertyValue(object key, out object propertyValue) /// /// Message-template-parameters /// Are all parameter names unique (true / false) - private static bool VerifyUniqueMessageTemplateParametersFast(IList parameterList) + private static bool VerifyUniqueMessageTemplateParametersFast(IList? parameterList) { if (parameterList is null) return true; @@ -434,7 +452,7 @@ internal static string GenerateUniquePropertyName(string originalN return newItemName; } - public struct PropertyDictionaryEnumerator : IEnumerator> + public struct PropertyDictionaryEnumerator : IEnumerator> { private readonly PropertiesDictionary _dictionary; private Dictionary.Enumerator _eventEnumerator; @@ -447,18 +465,18 @@ public PropertyDictionaryEnumerator(PropertiesDictionary dictionary) _messagePropertiesIndex = dictionary._messageProperties?.Count > 0 ? -1 : default(int?); } - public KeyValuePair Current + public KeyValuePair Current { get { if (_messagePropertiesIndex.HasValue) { - var property = _dictionary._messageProperties[_messagePropertiesIndex.Value]; - return new KeyValuePair(property.Name, property.Value); + var property = _dictionary.MessageProperties[_messagePropertiesIndex.Value]; + return new KeyValuePair(property.Name, property.Value); } if (_dictionary._eventProperties != null) { - return new KeyValuePair(_eventEnumerator.Current.Key, _eventEnumerator.Current.Value.Value); + return new KeyValuePair(_eventEnumerator.Current.Key, _eventEnumerator.Current.Value.Value); } throw new InvalidOperationException(); } @@ -470,7 +488,7 @@ public MessageTemplateParameter CurrentParameter { if (_messagePropertiesIndex.HasValue) { - return _dictionary._messageProperties[_messagePropertiesIndex.Value]; + return _dictionary.MessageProperties[_messagePropertiesIndex.Value]; } if (_dictionary._eventProperties != null) { @@ -481,19 +499,19 @@ public MessageTemplateParameter CurrentParameter } } - public KeyValuePair CurrentProperty + public KeyValuePair CurrentProperty { get { if (_messagePropertiesIndex.HasValue) { - var property = _dictionary._messageProperties[_messagePropertiesIndex.Value]; - return new KeyValuePair(property.Name, property.Value); + var property = _dictionary.MessageProperties[_messagePropertiesIndex.Value]; + return new KeyValuePair(property.Name, property.Value); } if (_dictionary._eventProperties != null) { string propertyName = XmlHelper.XmlConvertToString(_eventEnumerator.Current.Key ?? string.Empty) ?? string.Empty; - return new KeyValuePair(propertyName, _eventEnumerator.Current.Value.Value); + return new KeyValuePair(propertyName, _eventEnumerator.Current.Value.Value); } throw new InvalidOperationException(); } @@ -528,7 +546,8 @@ private bool MoveNextValidEventProperty() private bool MoveNextValidMessageParameter() { - if (_messagePropertiesIndex.Value + 1 < _dictionary._messageProperties.Count) + var messageProperties = _dictionary.MessageProperties; + if (_messagePropertiesIndex.HasValue && messageProperties != null && _messagePropertiesIndex.Value + 1 < messageProperties.Count) { var eventProperties = _dictionary._eventProperties; if (eventProperties is null) @@ -537,9 +556,9 @@ private bool MoveNextValidMessageParameter() return true; } - for (int i = _messagePropertiesIndex.Value + 1; i < _dictionary._messageProperties.Count; ++i) + for (int i = _messagePropertiesIndex.Value + 1; i < messageProperties.Count; ++i) { - if (eventProperties.TryGetValue(_dictionary._messageProperties[i].Name, out var valueItem) && valueItem.IsMessageProperty) + if (eventProperties.TryGetValue(messageProperties[i].Name, out var valueItem) && valueItem.IsMessageProperty) { _messagePropertiesIndex = i; return true; @@ -564,15 +583,13 @@ public void Reset() } [DebuggerDisplay("Count = {Count}")] - private sealed class DictionaryCollection : ICollection + private sealed class ValueCollection : ICollection { private readonly PropertiesDictionary _dictionary; - private readonly bool _keyCollection; - public DictionaryCollection(PropertiesDictionary dictionary, bool keyCollection) + public ValueCollection(PropertiesDictionary dictionary) { _dictionary = dictionary; - _keyCollection = keyCollection; } /// @@ -581,23 +598,18 @@ public DictionaryCollection(PropertiesDictionary dictionary, bool keyCollection) /// public bool IsReadOnly => true; - /// Will always throw, as collection is readonly - public void Add(object item) { throw new NotSupportedException(); } + public void Add(object? item) { throw new NotSupportedException(); } /// Will always throw, as collection is readonly public void Clear() { throw new NotSupportedException(); } /// Will always throw, as collection is readonly - public bool Remove(object item) { throw new NotSupportedException(); } + public bool Remove(object? item) { throw new NotSupportedException(); } /// - public bool Contains(object item) + public bool Contains(object? item) { - if (_keyCollection) - { - return _dictionary.ContainsKey(item); - } if (!_dictionary.IsEmpty) { if (_dictionary.EventProperties.ContainsValue(new PropertyValue(item, false))) @@ -609,6 +621,82 @@ public bool Contains(object item) return false; } + /// + public void CopyTo(object?[] array, int arrayIndex) + { + Guard.ThrowIfNull(array); + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + + if (!_dictionary.IsEmpty) + { + foreach (var propertyItem in _dictionary) + { + array[arrayIndex++] = propertyItem.Value; + } + } + } + + /// + public IEnumerator GetEnumerator() + { + return new ValueCollectionEnumerator(_dictionary); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + private sealed class ValueCollectionEnumerator : IEnumerator + { + PropertyDictionaryEnumerator _enumerator; + + public ValueCollectionEnumerator(PropertiesDictionary dictionary) + { + _enumerator = dictionary.GetPropertyEnumerator(); + } + + /// + public object? Current => _enumerator.Current.Value; + public void Dispose() => _enumerator.Dispose(); + public bool MoveNext() => _enumerator.MoveNext(); + public void Reset() => _enumerator.Reset(); + } + } + + [DebuggerDisplay("Count = {Count}")] + private sealed class KeyCollection : ICollection + { + private readonly PropertiesDictionary _dictionary; + + public KeyCollection(PropertiesDictionary dictionary) + { + _dictionary = dictionary; + } + + /// + public int Count => _dictionary.Count; + + /// + public bool IsReadOnly => true; + + /// Will always throw, as collection is readonly + public void Add(object item) { throw new NotSupportedException(); } + + /// Will always throw, as collection is readonly + public void Clear() { throw new NotSupportedException(); } + + /// Will always throw, as collection is readonly + public bool Remove(object item) { throw new NotSupportedException(); } + + /// + public bool Contains(object item) + { + return _dictionary.ContainsKey(item); + } + /// public void CopyTo(object[] array, int arrayIndex) { @@ -620,7 +708,7 @@ public void CopyTo(object[] array, int arrayIndex) { foreach (var propertyItem in _dictionary) { - array[arrayIndex++] = _keyCollection ? propertyItem.Key : propertyItem.Value; + array[arrayIndex++] = propertyItem.Key; } } } @@ -628,7 +716,7 @@ public void CopyTo(object[] array, int arrayIndex) /// public IEnumerator GetEnumerator() { - return new DictionaryCollectionEnumerator(_dictionary, _keyCollection); + return new KeyCollectionEnumerator(_dictionary); } /// @@ -637,19 +725,17 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } - private sealed class DictionaryCollectionEnumerator : IEnumerator + private sealed class KeyCollectionEnumerator : IEnumerator { PropertyDictionaryEnumerator _enumerator; - private readonly bool _keyCollection; - public DictionaryCollectionEnumerator(PropertiesDictionary dictionary, bool keyCollection) + public KeyCollectionEnumerator(PropertiesDictionary dictionary) { _enumerator = dictionary.GetPropertyEnumerator(); - _keyCollection = keyCollection; } /// - public object Current => _keyCollection ? _enumerator.Current.Key : _enumerator.Current.Value; + public object Current => _enumerator.Current.Key; public void Dispose() => _enumerator.Dispose(); public bool MoveNext() => _enumerator.MoveNext(); public void Reset() => _enumerator.Reset(); diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index 6ae24a7306..0f88018cbd 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -387,7 +387,7 @@ public bool HasProperties } else if (templateParameters != null) { - properties.MessageProperties = templateParameters; + properties.ResetMessageProperties(templateParameters); } return properties; } @@ -795,7 +795,7 @@ private bool ResetMessageTemplateParameters() if (HasMessageTemplateParameters) { - _properties.MessageProperties = null; + _properties.ResetMessageProperties(); return true; } diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 2da1ca3220..4b018bec1e 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -57,19 +59,19 @@ public class LogFactory : IDisposable { private static readonly TimeSpan DefaultFlushTimeout = TimeSpan.FromSeconds(15); - private static AppEnvironmentWrapper defaultAppEnvironment; + private static AppEnvironmentWrapper? defaultAppEnvironment; /// /// Internal for unit tests /// internal readonly object _syncRoot = new object(); private readonly LoggerCache _loggerCache = new LoggerCache(); - [NotNull] private ServiceRepositoryInternal _serviceRepository; - private IAppEnvironment _currentAppEnvironment; - internal LoggingConfiguration _config; + private readonly ServiceRepositoryInternal _serviceRepository; + private readonly IAppEnvironment _currentAppEnvironment; + internal LoggingConfiguration? _config; internal LogMessageFormatter ActiveMessageFormatter; - internal LogMessageFormatter SingleTargetMessageFormatter; - internal LogMessageTemplateFormatter AutoMessageTemplateFormatter; + internal LogMessageFormatter? SingleTargetMessageFormatter; + internal LogMessageTemplateFormatter? AutoMessageTemplateFormatter; private LogLevel _globalThreshold = LogLevel.MinLevel; private bool _configLoaded; private int _supendLoggingCounter; @@ -79,7 +81,7 @@ public class LogFactory : IDisposable /// When this property is null, the default file paths ( are used. /// [Obsolete("Replaced by LogFactory.Setup().LoadConfigurationFromFile(). Marked obsolete on NLog 5.2")] - private List _candidateConfigFilePaths; + private List? _candidateConfigFilePaths; private readonly ILoggingConfigurationLoader _configLoader; @@ -89,7 +91,7 @@ public class LogFactory : IDisposable /// /// Note can be null when unloading configuration at shutdown. /// - public event EventHandler ConfigurationChanged; + public event EventHandler? ConfigurationChanged; /// /// Event that is raised when the current Process / AppDomain terminates. @@ -115,7 +117,7 @@ private static event EventHandler LoggerShutdown } } } - private static event EventHandler _loggerShutdown; + private static event EventHandler? _loggerShutdown; /// /// Initializes a new instance of the class. @@ -123,7 +125,6 @@ private static event EventHandler LoggerShutdown public LogFactory() : this(new LoggingConfigurationFileLoader(DefaultAppEnvironment)) { - } /// @@ -145,14 +146,14 @@ public LogFactory(LoggingConfiguration config) /// /// The config loader /// The custom AppEnvironmnet override - internal LogFactory(ILoggingConfigurationLoader configLoader, IAppEnvironment appEnvironment = null) + internal LogFactory(ILoggingConfigurationLoader configLoader, IAppEnvironment? appEnvironment = null) { _configLoader = configLoader; - _currentAppEnvironment = appEnvironment; + _currentAppEnvironment = appEnvironment ?? DefaultAppEnvironment; LoggerShutdown += OnStopLogging; _serviceRepository = new ServiceRepositoryInternal(this); _serviceRepository.TypeRegistered += ServiceRepository_TypeRegistered; - RefreshMessageFormatter(); + ActiveMessageFormatter = RefreshMessageFormatter(); } internal static IAppEnvironment DefaultAppEnvironment @@ -163,16 +164,11 @@ internal static IAppEnvironment DefaultAppEnvironment } } - internal IAppEnvironment CurrentAppEnvironment - { - get => _currentAppEnvironment ?? DefaultAppEnvironment; - set => _currentAppEnvironment = value; - } + internal IAppEnvironment CurrentAppEnvironment => _currentAppEnvironment; /// /// Repository of interfaces used by NLog to allow override for dependency injection /// - [NotNull] public ServiceRepository ServiceRepository => _serviceRepository; /// @@ -225,7 +221,7 @@ public bool AutoShutdown /// /// Setter will re-configure all -objects, so no need to also call /// - public LoggingConfiguration Configuration + public LoggingConfiguration? Configuration { get { @@ -251,7 +247,7 @@ public LoggingConfiguration Configuration { lock (_syncRoot) { - LoggingConfiguration oldConfig = _config; + LoggingConfiguration? oldConfig = _config; if (oldConfig != null) { InternalLogger.Info("Closing old configuration."); @@ -300,7 +296,7 @@ private void ServiceRepository_TypeRegistered(object sender, ServiceRepositoryUp } } - private void RefreshMessageFormatter() + private LogMessageFormatter RefreshMessageFormatter() { var messageFormatter = _serviceRepository.GetService(); ActiveMessageFormatter = messageFormatter.FormatMessage; @@ -315,6 +311,7 @@ private void RefreshMessageFormatter() SingleTargetMessageFormatter = null; AutoMessageTemplateFormatter = null; } + return ActiveMessageFormatter; } /// @@ -345,7 +342,7 @@ public LogLevel GlobalThreshold /// Specific culture info or null to use /// [CanBeNull] - public CultureInfo DefaultCultureInfo + public CultureInfo? DefaultCultureInfo { get => _config is null ? _defaultCultureInfo : _config.DefaultCultureInfo; set @@ -355,7 +352,7 @@ public CultureInfo DefaultCultureInfo _defaultCultureInfo = value; } } - internal CultureInfo _defaultCultureInfo; + internal CultureInfo? _defaultCultureInfo; internal static void LogNLogAssemblyVersion() { @@ -637,13 +634,13 @@ public void Flush(AsyncContinuation asyncContinuation, TimeSpan timeout) FlushInternal(timeout, asyncContinuation); } - private bool FlushInternal(TimeSpan flushTimeout, AsyncContinuation asyncContinuation) + private bool FlushInternal(TimeSpan flushTimeout, AsyncContinuation? asyncContinuation) { InternalLogger.Debug("LogFactory Starting Flush with timeout={0} secs", flushTimeout.TotalSeconds); try { - LoggingConfiguration config; + LoggingConfiguration? config; lock (_syncRoot) { config = _config; // Flush should not attempt to auto-load Configuration @@ -694,14 +691,14 @@ public System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken { InternalLogger.Debug("LogFactory Starting Flush Async"); - LoggingConfiguration config; + LoggingConfiguration? config; lock (_syncRoot) { config = _config; if (config is null || !_configLoaded) { #if NET45 - return System.Threading.Tasks.Task.FromResult(null); + return System.Threading.Tasks.Task.FromResult(null); #else return System.Threading.Tasks.Task.CompletedTask; #endif @@ -713,7 +710,7 @@ public System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken if (flushTimeoutHandler is null) { #if NET45 - return System.Threading.Tasks.Task.FromResult(null); + return System.Threading.Tasks.Task.FromResult(null); #else return System.Threading.Tasks.Task.CompletedTask; #endif @@ -962,7 +959,7 @@ public void ResetCandidateConfigFilePath() _candidateConfigFilePaths = null; } - private Logger GetLoggerThreadSafe(string name, Type loggerType, Func loggerCreator) + private Logger GetLoggerThreadSafe(string name, Type loggerType, Func loggerCreator) { if (name is null) throw new ArgumentNullException(nameof(name), "Name of logger cannot be null"); @@ -971,7 +968,7 @@ private Logger GetLoggerThreadSafe(string name, Type loggerType, Func loggerCreator) + internal Logger CreateNewLogger(Type loggerType, Func loggerCreator) { try { - Logger newLogger = loggerCreator(loggerType); + Logger? newLogger = loggerCreator(loggerType); if (newLogger is null) { if (Logger.DefaultLoggerType.IsAssignableFrom(loggerType)) @@ -1156,7 +1153,7 @@ public void InsertOrUpdate(LoggerCacheKey cacheKey, Logger logger) _loggerCache[cacheKey] = new WeakReference(logger); } - public Logger Retrieve(LoggerCacheKey cacheKey) + public Logger? Retrieve(LoggerCacheKey cacheKey) { if (_loggerCache.TryGetValue(cacheKey, out var loggerReference)) { diff --git a/src/NLog/LogManager.cs b/src/NLog/LogManager.cs index 1af43ebb75..d92230afd2 100644 --- a/src/NLog/LogManager.cs +++ b/src/NLog/LogManager.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -54,7 +56,7 @@ public static class LogManager /// Gets the instance used in the . /// public static LogFactory LogFactory => _logFactory ?? CreateLogFactorySingleton(); - private static LogFactory _logFactory; + private static LogFactory? _logFactory; private static LogFactory CreateLogFactorySingleton() { @@ -126,7 +128,7 @@ public static bool AutoShutdown /// /// Setter will re-configure all -objects, so no need to also call /// - public static LoggingConfiguration Configuration + public static LoggingConfiguration? Configuration { get => LogFactory.Configuration; set => LogFactory.Configuration = value; diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index 538a07217f..a083c41d68 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -231,7 +231,7 @@ public bool WriteBom /// Obsolete and only here for Legacy reasons, instead use . /// /// - [Obsolete("Instead use ArchiveSuffixFormat")] + [Obsolete("Instead use ArchiveSuffixFormat. Marked obsolete with NLog 6.0")] public string ArchiveDateFormat { get => _archiveDateFormat; @@ -365,7 +365,7 @@ public int MaxArchiveDays /// Obsolete and only here for Legacy reasons, instead use . /// /// - [Obsolete("Instead use ArchiveSuffixFormat")] + [Obsolete("Instead use ArchiveSuffixFormat. Marked obsolete with NLog 6.0")] public string ArchiveNumbering { get => _archiveNumbering ?? "Sequence"; From f8891218c37ecc48e8850b6f6af87629a662c4eb Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 8 May 2025 21:55:53 +0200 Subject: [PATCH 109/224] ScopeContext with nullable references (#5807) --- src/NLog/GlobalDiagnosticsContext.cs | 32 ++-- src/NLog/IJsonConverter.cs | 4 +- src/NLog/IObjectTypeTransformer.cs | 4 +- src/NLog/IValueFormatter.cs | 10 +- src/NLog/Internal/ScopeContextAsyncState.cs | 148 +++++++++--------- .../ScopeContextPropertyEnumerator.cs | 36 +++-- src/NLog/LogEventInfo.cs | 2 +- src/NLog/LogFactory-T.cs | 2 + src/NLog/LogLevel.cs | 26 +-- src/NLog/LogLevelTypeConverter.cs | 8 +- src/NLog/LogMessageFormatter.cs | 4 +- src/NLog/LogMessageGenerator.cs | 2 + src/NLog/Logger.cs | 8 +- src/NLog/LoggerImpl.cs | 4 +- src/NLog/MappedDiagnosticsContext.cs | 22 ++- src/NLog/MappedDiagnosticsLogicalContext.cs | 27 ++-- src/NLog/NLogConfigurationException.cs | 8 +- src/NLog/NLogRuntimeException.cs | 7 +- src/NLog/NestedDiagnosticsContext.cs | 12 +- src/NLog/NestedDiagnosticsLogicalContext.cs | 17 +- src/NLog/NullLogger.cs | 2 + src/NLog/ScopeContext.cs | 129 ++++++++------- 22 files changed, 293 insertions(+), 221 deletions(-) diff --git a/src/NLog/GlobalDiagnosticsContext.cs b/src/NLog/GlobalDiagnosticsContext.cs index c6027dd250..883f314532 100644 --- a/src/NLog/GlobalDiagnosticsContext.cs +++ b/src/NLog/GlobalDiagnosticsContext.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -44,17 +46,17 @@ namespace NLog public static class GlobalDiagnosticsContext { private static readonly object _lockObject = new object(); - private static Dictionary _dict = new Dictionary(StringComparer.OrdinalIgnoreCase); - private static Dictionary _dictReadOnly; // Reset cache on change + private static Dictionary _dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + private static Dictionary? _dictReadOnly; // Reset cache on change /// /// Sets the value with the specified key in the Global Diagnostics Context (GDC) dictionary /// /// Item name. /// Item value. - public static void Set(string item, string value) + public static void Set(string item, string? value) { - Set(item, (object)value); + Set(item, (object?)value); } /// @@ -62,8 +64,9 @@ public static void Set(string item, string value) /// /// Item name. /// Item value. - public static void Set(string item, object value) + public static void Set(string item, object? value) { + Guard.ThrowIfNull(item); lock (_lockObject) { GetWritableDict()[item] = value; @@ -78,6 +81,7 @@ public static void Set(string item, object value) /// If the value isn't a already, this call locks the for reading the needed for converting to . public static string Get(string item) { + Guard.ThrowIfNull(item); return Get(item, null); } @@ -88,8 +92,9 @@ public static string Get(string item) /// to use when converting the item's value to a string. /// The value of as a string, if defined; otherwise . /// If is null and the value isn't a already, this call locks the for reading the needed for converting to . - public static string Get(string item, IFormatProvider formatProvider) + public static string Get(string item, IFormatProvider? formatProvider) { + Guard.ThrowIfNull(item); return FormatHelper.ConvertToString(GetObject(item), formatProvider); } @@ -98,8 +103,9 @@ public static string Get(string item, IFormatProvider formatProvider) /// /// Item name. /// The item value, if defined; otherwise null. - public static object GetObject(string item) + public static object? GetObject(string item) { + Guard.ThrowIfNull(item); GetReadOnlyDict().TryGetValue(item, out var o); return o; } @@ -120,6 +126,7 @@ public static ICollection GetNames() /// A boolean indicating whether the specified item exists in current thread GDC. public static bool Contains(string item) { + Guard.ThrowIfNull(item); return GetReadOnlyDict().ContainsKey(item); } @@ -129,6 +136,7 @@ public static bool Contains(string item) /// Item name. public static void Remove(string item) { + Guard.ThrowIfNull(item); lock (_lockObject) { if (_dict.ContainsKey(item)) @@ -152,7 +160,7 @@ public static void Clear() } } - internal static Dictionary GetReadOnlyDict() + internal static Dictionary GetReadOnlyDict() { var readOnly = _dictReadOnly; if (readOnly is null) @@ -165,20 +173,20 @@ internal static Dictionary GetReadOnlyDict() return readOnly; } - private static Dictionary GetWritableDict(bool clearDictionary = false) + private static Dictionary GetWritableDict(bool clearDictionary = false) { if (_dictReadOnly != null) { - Dictionary newDict = CopyDictionaryOnWrite(clearDictionary); + Dictionary newDict = CopyDictionaryOnWrite(clearDictionary); _dict = newDict; _dictReadOnly = null; } return _dict; } - private static Dictionary CopyDictionaryOnWrite(bool clearDictionary) + private static Dictionary CopyDictionaryOnWrite(bool clearDictionary) { - var newDict = new Dictionary(clearDictionary ? 0 : _dict.Count + 1, _dict.Comparer); + var newDict = new Dictionary(clearDictionary ? 0 : _dict.Count + 1, _dict.Comparer); if (!clearDictionary) { // Less allocation with enumerator than Dictionary-constructor diff --git a/src/NLog/IJsonConverter.cs b/src/NLog/IJsonConverter.cs index e797da985f..a330a7d45a 100644 --- a/src/NLog/IJsonConverter.cs +++ b/src/NLog/IJsonConverter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { /// @@ -44,6 +46,6 @@ public interface IJsonConverter /// The object to serialize to JSON. /// Output destination. /// Serialize succeeded (true/false) - bool SerializeObject(object value, System.Text.StringBuilder builder); + bool SerializeObject(object? value, System.Text.StringBuilder builder); } } diff --git a/src/NLog/IObjectTypeTransformer.cs b/src/NLog/IObjectTypeTransformer.cs index 2778a1a89d..6b3f26c4e9 100644 --- a/src/NLog/IObjectTypeTransformer.cs +++ b/src/NLog/IObjectTypeTransformer.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { /// @@ -44,6 +46,6 @@ internal interface IObjectTypeTransformer /// /// Null if unknown object, or object cannot be handled /// - object TryTransformObject(object obj); + object? TryTransformObject(object? obj); } } diff --git a/src/NLog/IValueFormatter.cs b/src/NLog/IValueFormatter.cs index 2ca8f195ba..bc8b81906c 100644 --- a/src/NLog/IValueFormatter.cs +++ b/src/NLog/IValueFormatter.cs @@ -31,12 +31,14 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Text; -using NLog.MessageTemplates; +#nullable enable namespace NLog { + using System; + using System.Text; + using NLog.MessageTemplates; + /// /// Render a message template property to a string /// @@ -51,6 +53,6 @@ public interface IValueFormatter /// An object that supplies culture-specific formatting information. /// Output destination. /// Serialize succeeded (true/false) - bool FormatValue(object value, string format, CaptureType captureType, IFormatProvider formatProvider, StringBuilder builder); + bool FormatValue(object? value, string? format, CaptureType captureType, IFormatProvider? formatProvider, StringBuilder builder); } } diff --git a/src/NLog/Internal/ScopeContextAsyncState.cs b/src/NLog/Internal/ScopeContextAsyncState.cs index 4e9880d648..2af2994d67 100644 --- a/src/NLog/Internal/ScopeContextAsyncState.cs +++ b/src/NLog/Internal/ScopeContextAsyncState.cs @@ -33,6 +33,8 @@ #if !NET35 && !NET40 && !NET45 +#nullable enable + namespace NLog.Internal { using System; @@ -43,10 +45,10 @@ namespace NLog.Internal /// internal abstract class ScopeContextAsyncState : IDisposable { - public IScopeContextAsyncState Parent { get; } + public IScopeContextAsyncState? Parent { get; } private bool _disposed; - protected ScopeContextAsyncState(IScopeContextAsyncState parent) + protected ScopeContextAsyncState(IScopeContextAsyncState? parent) { Parent = parent; } @@ -66,28 +68,28 @@ void IDisposable.Dispose() /// internal interface IScopeContextAsyncState : IDisposable { - IScopeContextAsyncState Parent { get; } - object NestedState { get; } + IScopeContextAsyncState? Parent { get; } + object? NestedState { get; } long NestedStateTimestamp { get; } - IReadOnlyCollection> CaptureContextProperties(ref ScopeContextPropertyCollector contextCollector); - IList CaptureNestedContext(ref ScopeContextNestedStateCollector contextCollector); + IReadOnlyCollection>? CaptureContextProperties(ref ScopeContextPropertyCollector contextCollector); + IList? CaptureNestedContext(ref ScopeContextNestedStateCollector contextCollector); } struct ScopeContextPropertyCollector { - IReadOnlyCollection> _allProperties; - List> _propertyCollector; + IReadOnlyCollection>? _allProperties; + List> _propertyCollector; public bool IsCollectorEmpty => _allProperties is null || (_allProperties.Count == 0 && _propertyCollector is null); public bool IsCollectorInactive => _allProperties is null; - public ScopeContextPropertyCollector(List> propertyCollector = null) + public ScopeContextPropertyCollector(List> propertyCollector) { _allProperties = _propertyCollector = propertyCollector; } - public IReadOnlyCollection> StartCaptureProperties(IScopeContextAsyncState state) + public IReadOnlyCollection> StartCaptureProperties(IScopeContextAsyncState? state) { while (state != null) { @@ -100,7 +102,7 @@ public IReadOnlyCollection> StartCaptureProperties( return CaptureCompleted(null); } - public IReadOnlyCollection> CaptureCompleted(IReadOnlyCollection> properties) + public IReadOnlyCollection> CaptureCompleted(IReadOnlyCollection>? properties) { if (_allProperties?.Count > 0) { @@ -108,7 +110,7 @@ public IReadOnlyCollection> CaptureCompleted(IReadO { if (_propertyCollector is null) { - return _allProperties = MergeUniqueProperties(properties); + return _allProperties = MergeUniqueProperties(_allProperties, properties); } AddProperties(properties); @@ -121,19 +123,19 @@ public IReadOnlyCollection> CaptureCompleted(IReadO if (properties?.Count > 0) return _allProperties = EnsureUniqueProperties(properties); else - return _allProperties = Array.Empty>(); + return _allProperties = Array.Empty>(); } } - private IReadOnlyCollection> MergeUniqueProperties(IReadOnlyCollection> properties) + private static IReadOnlyCollection> MergeUniqueProperties(IReadOnlyCollection> currentProperties, IReadOnlyCollection> properties) { - var scopeProperties = new Dictionary(_allProperties.Count + properties.Count, ScopeContext.DefaultComparer); + var scopeProperties = new Dictionary(currentProperties.Count + properties.Count, ScopeContext.DefaultComparer); ScopeContextPropertyEnumerator.CopyScopePropertiesToDictionary(properties, scopeProperties); - ScopeContextPropertyEnumerator.CopyScopePropertiesToDictionary(_allProperties, scopeProperties); + ScopeContextPropertyEnumerator.CopyScopePropertiesToDictionary(currentProperties, scopeProperties); return scopeProperties; } - private static IReadOnlyCollection> EnsureUniqueProperties(IReadOnlyCollection> properties) + private static IReadOnlyCollection> EnsureUniqueProperties(IReadOnlyCollection> properties) { var scopePropertyCount = properties.Count; if (scopePropertyCount > 1) @@ -143,9 +145,9 @@ private static IReadOnlyCollection> EnsureUniquePro { return properties; } - else if (scopePropertyCount > 10 || !ScopeContextPropertyEnumerator.HasUniqueCollectionKeys(properties, ScopeContext.DefaultComparer)) + else if (scopePropertyCount > 10 || !ScopeContextPropertyEnumerator.HasUniqueCollectionKeys(properties, ScopeContext.DefaultComparer)) { - var scopeProperties = new Dictionary(Math.Min(scopePropertyCount, 10), ScopeContext.DefaultComparer); + var scopeProperties = new Dictionary(Math.Min(scopePropertyCount, 10), ScopeContext.DefaultComparer); ScopeContextPropertyEnumerator.CopyScopePropertiesToDictionary(properties, scopeProperties); return scopeProperties; } @@ -154,31 +156,31 @@ private static IReadOnlyCollection> EnsureUniquePro return properties; } - public void AddProperty(string propertyName, object propertyValue) + public void AddProperty(string propertyName, object? propertyValue) { - if (IsCollectorEmpty) + if (_allProperties is null || IsCollectorEmpty) { - _allProperties = new[] { new KeyValuePair(propertyName, propertyValue) }; + _allProperties = new[] { new KeyValuePair(propertyName, propertyValue) }; } else { if (_propertyCollector is null) { - _propertyCollector = new List>(Math.Max(4, _allProperties.Count + 1)); - _propertyCollector.Add(new KeyValuePair(propertyName, propertyValue)); + _propertyCollector = new List>(Math.Max(4, _allProperties.Count + 1)); + _propertyCollector.Add(new KeyValuePair(propertyName, propertyValue)); CollectProperties(_allProperties); _allProperties = _propertyCollector; } else { - _propertyCollector.Insert(0, new KeyValuePair(propertyName, propertyValue)); + _propertyCollector.Insert(0, new KeyValuePair(propertyName, propertyValue)); } } } - public void AddProperties(IReadOnlyCollection> properties) + public void AddProperties(IReadOnlyCollection> properties) { - if (IsCollectorEmpty) + if (_allProperties is null || IsCollectorEmpty) { _allProperties = properties; } @@ -186,7 +188,7 @@ public void AddProperties(IReadOnlyCollection> prop { if (_propertyCollector is null) { - _propertyCollector = new List>(Math.Max(4, _allProperties.Count + properties.Count)); + _propertyCollector = new List>(Math.Max(4, _allProperties.Count + properties.Count)); CollectProperties(properties); CollectProperties(_allProperties); _allProperties = _propertyCollector; @@ -198,7 +200,7 @@ public void AddProperties(IReadOnlyCollection> prop else { int insertPosition = 0; - using (var scopeEnumerator = new ScopeContextPropertyEnumerator(properties)) + using (var scopeEnumerator = new ScopeContextPropertyEnumerator(properties)) { while (scopeEnumerator.MoveNext()) { @@ -210,9 +212,9 @@ public void AddProperties(IReadOnlyCollection> prop } } - private void CollectProperties(IReadOnlyCollection> properties) + private void CollectProperties(IReadOnlyCollection> properties) { - using (var scopeEnumerator = new ScopeContextPropertyEnumerator(properties)) + using (var scopeEnumerator = new ScopeContextPropertyEnumerator(properties)) { while (scopeEnumerator.MoveNext()) { @@ -232,7 +234,7 @@ struct ScopeContextNestedStateCollector public bool IsCollectorInactive => _allNestedStates is null; - public IList StartCaptureNestedStates(IScopeContextAsyncState state) + public IList StartCaptureNestedStates(IScopeContextAsyncState? state) { _allNestedStates = _allNestedStates ?? Array.Empty(); @@ -277,47 +279,51 @@ internal sealed class ScopedContextNestedAsyncState : ScopeContextAsyncState, { private readonly T _value; - public ScopedContextNestedAsyncState(IScopeContextAsyncState parent, T state) + public ScopedContextNestedAsyncState(IScopeContextAsyncState? parent, T state) : base(parent) { NestedStateTimestamp = ScopeContext.GetNestedContextTimestampNow(); _value = state; } - object IScopeContextAsyncState.NestedState => _value; + object? IScopeContextAsyncState.NestedState => _value; public long NestedStateTimestamp { get; } - IList IScopeContextAsyncState.CaptureNestedContext(ref ScopeContextNestedStateCollector contextCollector) + IList? IScopeContextAsyncState.CaptureNestedContext(ref ScopeContextNestedStateCollector contextCollector) { + object? objectValue = _value; + if (contextCollector.IsCollectorEmpty) { if (Parent is null) { - return new object[] { _value }; // We are done + return objectValue is null ? Array.Empty() : new object[] { objectValue }; // We are done } else if (contextCollector.IsCollectorInactive) { - contextCollector.PushNestedState(_value); // Mark as active + if (objectValue is not null) + contextCollector.PushNestedState(objectValue); // Mark as active return contextCollector.StartCaptureNestedStates(Parent); } } - contextCollector.PushNestedState(_value); + if (objectValue is not null) + contextCollector.PushNestedState(objectValue); return null; // continue with parent } - IReadOnlyCollection> IScopeContextAsyncState.CaptureContextProperties(ref ScopeContextPropertyCollector contextCollector) + IReadOnlyCollection>? IScopeContextAsyncState.CaptureContextProperties(ref ScopeContextPropertyCollector contextCollector) { if (contextCollector.IsCollectorInactive) { if (Parent is null) { - return Array.Empty>(); // We are done + return Array.Empty>(); // We are done } else { - contextCollector.AddProperties(Array.Empty>()); // Mark as active + contextCollector.AddProperties(Array.Empty>()); // Mark as active return contextCollector.StartCaptureProperties(Parent); // Start parent enumeration } } @@ -339,19 +345,19 @@ public override string ToString() internal sealed class ScopeContextPropertyAsyncState : ScopeContextAsyncState, IScopeContextPropertiesAsyncState { long IScopeContextAsyncState.NestedStateTimestamp => 0; - object IScopeContextAsyncState.NestedState => null; + object? IScopeContextAsyncState.NestedState => null; public string Name { get; } - public TValue Value { get; } - private IReadOnlyCollection> _allProperties; + public TValue? Value { get; } + private IReadOnlyCollection>? _allProperties; - public ScopeContextPropertyAsyncState(IScopeContextAsyncState parent, string name, TValue value) + public ScopeContextPropertyAsyncState(IScopeContextAsyncState? parent, string name, TValue? value) : base(parent) { Name = name; Value = value; } - IList IScopeContextAsyncState.CaptureNestedContext(ref ScopeContextNestedStateCollector contextCollector) + IList? IScopeContextAsyncState.CaptureNestedContext(ref ScopeContextNestedStateCollector contextCollector) { if (contextCollector.IsCollectorInactive) { @@ -368,7 +374,7 @@ IList IScopeContextAsyncState.CaptureNestedContext(ref ScopeContextNeste return null; // Continue with Parent } - IReadOnlyCollection> IScopeContextAsyncState.CaptureContextProperties(ref ScopeContextPropertyCollector contextCollector) + IReadOnlyCollection>? IScopeContextAsyncState.CaptureContextProperties(ref ScopeContextPropertyCollector contextCollector) { if (contextCollector.IsCollectorEmpty) { @@ -402,35 +408,37 @@ public override string ToString() /// /// Immutable state for ScopeContext Multiple Properties (MDLC) /// - internal sealed class ScopeContextPropertiesAsyncState : ScopeContextAsyncState, IScopeContextPropertiesAsyncState, IReadOnlyCollection> + internal sealed class ScopeContextPropertiesAsyncState : ScopeContextAsyncState, IScopeContextPropertiesAsyncState, IReadOnlyCollection> { public long NestedStateTimestamp { get; } - public object NestedState { get; } + public object? NestedState { get; } - private readonly IReadOnlyCollection> _scopeProperties; - private IReadOnlyCollection> _allProperties; + private readonly IReadOnlyCollection> _scopeProperties; + private IReadOnlyCollection>? _allProperties; - public ScopeContextPropertiesAsyncState(IScopeContextAsyncState parent, Dictionary allProperties) + public ScopeContextPropertiesAsyncState(IScopeContextAsyncState? parent, Dictionary allProperties) : base(parent) { _allProperties = allProperties; // Collapsed dictionary that includes all properties from parent scopes with case-insensitive-comparer + _scopeProperties = Array.Empty>(); } - public ScopeContextPropertiesAsyncState(IScopeContextAsyncState parent, Dictionary allProperties, object nestedState) + public ScopeContextPropertiesAsyncState(IScopeContextAsyncState? parent, Dictionary allProperties, object? nestedState) : base(parent) { _allProperties = allProperties; // Collapsed dictionary that includes all properties from parent scopes with case-insensitive-comparer + _scopeProperties = Array.Empty>(); NestedState = nestedState; NestedStateTimestamp = ScopeContext.GetNestedContextTimestampNow(); } - public ScopeContextPropertiesAsyncState(IScopeContextAsyncState parent, IReadOnlyCollection> scopeProperties) + public ScopeContextPropertiesAsyncState(IScopeContextAsyncState? parent, IReadOnlyCollection> scopeProperties) : base(parent) { _scopeProperties = scopeProperties; } - public ScopeContextPropertiesAsyncState(IScopeContextAsyncState parent, IReadOnlyCollection> scopeProperties, object nestedState) + public ScopeContextPropertiesAsyncState(IScopeContextAsyncState? parent, IReadOnlyCollection> scopeProperties, object? nestedState) : base(parent) { _scopeProperties = scopeProperties; @@ -438,7 +446,7 @@ public ScopeContextPropertiesAsyncState(IScopeContextAsyncState parent, IReadOnl NestedStateTimestamp = ScopeContext.GetNestedContextTimestampNow(); } - IList IScopeContextAsyncState.CaptureNestedContext(ref ScopeContextNestedStateCollector contextCollector) + IList? IScopeContextAsyncState.CaptureNestedContext(ref ScopeContextNestedStateCollector contextCollector) { if (NestedState is null) { @@ -467,13 +475,13 @@ IList IScopeContextAsyncState.CaptureNestedContext(ref ScopeContextNeste } } - IReadOnlyCollection> IScopeContextAsyncState.CaptureContextProperties(ref ScopeContextPropertyCollector contextCollector) + IReadOnlyCollection>? IScopeContextAsyncState.CaptureContextProperties(ref ScopeContextPropertyCollector contextCollector) { if (contextCollector.IsCollectorEmpty) { if (_allProperties is null) { - contextCollector.AddProperties(_scopeProperties as IReadOnlyCollection> ?? this); + contextCollector.AddProperties(_scopeProperties as IReadOnlyCollection> ?? this); _allProperties = contextCollector.StartCaptureProperties(Parent); // Capture all properties from parents } return _allProperties; // We are done @@ -482,7 +490,7 @@ IReadOnlyCollection> IScopeContextAsyncState.Captur { if (_allProperties is null) { - contextCollector.AddProperties(_scopeProperties as IReadOnlyCollection> ?? this); + contextCollector.AddProperties(_scopeProperties as IReadOnlyCollection> ?? this); return null; // Continue with Parent } else @@ -499,9 +507,9 @@ public override string ToString() public int Count => _scopeProperties.Count; - IEnumerator> IEnumerable>.GetEnumerator() => new ScopeContextPropertyEnumerator(_scopeProperties); + IEnumerator> IEnumerable>.GetEnumerator() => new ScopeContextPropertyEnumerator(_scopeProperties); - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => new ScopeContextPropertyEnumerator(_scopeProperties); + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => new ScopeContextPropertyEnumerator(_scopeProperties); } /// @@ -511,10 +519,10 @@ public override string ToString() internal sealed class ScopeContextLegacyAsyncState : ScopeContextAsyncState, IScopeContextAsyncState { public object[] NestedContext { get; } - public IReadOnlyCollection> MappedContext { get; } + public IReadOnlyCollection>? MappedContext { get; } public long NestedStateTimestamp { get; } - public ScopeContextLegacyAsyncState(IReadOnlyCollection> allProperties, object[] nestedContext, long nestedContextTimestamp) + public ScopeContextLegacyAsyncState(IReadOnlyCollection>? allProperties, object[] nestedContext, long nestedContextTimestamp) : base(null) // Always top parent { MappedContext = allProperties; @@ -522,13 +530,13 @@ public ScopeContextLegacyAsyncState(IReadOnlyCollection allProperties, out object[] nestedContext, out long nestedContextTimestamp) + public static void CaptureLegacyContext(IScopeContextAsyncState? contextState, out Dictionary allProperties, out object[] nestedContext, out long nestedContextTimestamp) { var nestedStateCollector = new ScopeContextNestedStateCollector(); var propertyCollector = new ScopeContextPropertyCollector(); var nestedStates = contextState?.CaptureNestedContext(ref nestedStateCollector) ?? Array.Empty(); - var scopeProperties = contextState?.CaptureContextProperties(ref propertyCollector) ?? Array.Empty>(); - allProperties = new Dictionary(scopeProperties.Count, ScopeContext.DefaultComparer); + var scopeProperties = contextState?.CaptureContextProperties(ref propertyCollector) ?? Array.Empty>(); + allProperties = new Dictionary(scopeProperties.Count, ScopeContext.DefaultComparer); ScopeContextPropertyEnumerator.CopyScopePropertiesToDictionary(scopeProperties, allProperties); nestedContextTimestamp = 0L; @@ -545,9 +553,7 @@ public static void CaptureLegacyContext(IScopeContextAsyncState contextState, ou if (nestedContextTimestamp == 0L) nestedContextTimestamp = ScopeContext.GetNestedContextTimestampNow(); - nestedContext = nestedStates as object[]; - if (nestedContext == null) - nestedContext = System.Linq.Enumerable.ToArray(nestedStates); + nestedContext = nestedStates as object[] ?? System.Linq.Enumerable.ToArray(nestedStates); } else { @@ -555,7 +561,7 @@ public static void CaptureLegacyContext(IScopeContextAsyncState contextState, ou } } - object IScopeContextAsyncState.NestedState => NestedContext?.Length > 0 ? NestedContext[0] : null; + object? IScopeContextAsyncState.NestedState => NestedContext?.Length > 0 ? NestedContext[0] : null; IList IScopeContextAsyncState.CaptureNestedContext(ref ScopeContextNestedStateCollector contextCollector) { @@ -581,7 +587,7 @@ IList IScopeContextAsyncState.CaptureNestedContext(ref ScopeContextNeste } } - IReadOnlyCollection> IScopeContextAsyncState.CaptureContextProperties(ref ScopeContextPropertyCollector contextCollector) + IReadOnlyCollection>? IScopeContextAsyncState.CaptureContextProperties(ref ScopeContextPropertyCollector contextCollector) { if (contextCollector.IsCollectorEmpty) { diff --git a/src/NLog/Internal/ScopeContextPropertyEnumerator.cs b/src/NLog/Internal/ScopeContextPropertyEnumerator.cs index bb45a27aed..c99cb4f862 100644 --- a/src/NLog/Internal/ScopeContextPropertyEnumerator.cs +++ b/src/NLog/Internal/ScopeContextPropertyEnumerator.cs @@ -31,28 +31,30 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System.Collections.Generic; +#nullable enable namespace NLog.Internal { - internal struct ScopeContextPropertyEnumerator : IEnumerator> + using System.Collections.Generic; + + internal struct ScopeContextPropertyEnumerator : IEnumerator> { - readonly IEnumerator> _scopeEnumerator; + readonly IEnumerator>? _scopeEnumerator; #if !NET35 && !NET40 - readonly IReadOnlyList> _scopeList; + readonly IReadOnlyList>? _scopeList; int _scopeIndex; #endif - Dictionary.Enumerator _dictionaryEnumerator; + Dictionary.Enumerator _dictionaryEnumerator; public ScopeContextPropertyEnumerator(IEnumerable> scopeProperties) { #if !NET35 && !NET40 - if (scopeProperties is IReadOnlyList> scopeList) + if (scopeProperties is IReadOnlyList> scopeList) { _scopeEnumerator = null; _scopeList = scopeList; _scopeIndex = -1; - _dictionaryEnumerator = default(Dictionary.Enumerator); + _dictionaryEnumerator = default(Dictionary.Enumerator); return; } else @@ -62,27 +64,27 @@ public ScopeContextPropertyEnumerator(IEnumerable> } #endif - if (scopeProperties is Dictionary scopeDictionary) + if (scopeProperties is Dictionary scopeDictionary) { _scopeEnumerator = null; _dictionaryEnumerator = scopeDictionary.GetEnumerator(); } - else if (scopeProperties is IEnumerable> scopeEnumerator) + else if (scopeProperties is IEnumerable> scopeEnumerator) { _scopeEnumerator = scopeEnumerator.GetEnumerator(); - _dictionaryEnumerator = default(Dictionary.Enumerator); + _dictionaryEnumerator = default(Dictionary.Enumerator); } else { _scopeEnumerator = CreateScopeEnumerable(scopeProperties).GetEnumerator(); - _dictionaryEnumerator = default(Dictionary.Enumerator); + _dictionaryEnumerator = default(Dictionary.Enumerator); } } #if !NET35 && !NET40 - public static void CopyScopePropertiesToDictionary(IReadOnlyCollection> parentContext, Dictionary scopeDictionary) + public static void CopyScopePropertiesToDictionary(IReadOnlyCollection> parentContext, Dictionary scopeDictionary) { - using (var propertyEnumerator = new ScopeContextPropertyEnumerator(parentContext)) + using (var propertyEnumerator = new ScopeContextPropertyEnumerator(parentContext)) { while (propertyEnumerator.MoveNext()) { @@ -136,13 +138,13 @@ public static bool HasUniqueCollectionKeys(IEnumerable> CreateScopeEnumerable(IEnumerable> scopeProperties) + private static IEnumerable> CreateScopeEnumerable(IEnumerable> scopeProperties) { foreach (var property in scopeProperties) - yield return new KeyValuePair(property.Key, property.Value); + yield return new KeyValuePair(property.Key, property.Value); } - public KeyValuePair Current + public KeyValuePair Current { get { @@ -216,7 +218,7 @@ public void Reset() } else { - _dictionaryEnumerator = default(Dictionary.Enumerator); + _dictionaryEnumerator = default(Dictionary.Enumerator); } } } diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index 0f88018cbd..128f09494a 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -705,7 +705,7 @@ private static bool HasImmutableProperties(PropertiesDictionary properties) return true; } - internal void SetMessageFormatter([NotNull] LogMessageFormatter messageFormatter, [CanBeNull] LogMessageFormatter singleTargetMessageFormatter) + internal void SetMessageFormatter([NotNull] LogMessageFormatter messageFormatter, [CanBeNull] LogMessageFormatter? singleTargetMessageFormatter) { bool hasCustomMessageFormatter = _messageFormatter != null; if (!hasCustomMessageFormatter) diff --git a/src/NLog/LogFactory-T.cs b/src/NLog/LogFactory-T.cs index dbd2649b5a..e4af9d5601 100644 --- a/src/NLog/LogFactory-T.cs +++ b/src/NLog/LogFactory-T.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System.Diagnostics; diff --git a/src/NLog/LogLevel.cs b/src/NLog/LogLevel.cs index 826e0095d4..ff58cc4fac 100644 --- a/src/NLog/LogLevel.cs +++ b/src/NLog/LogLevel.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -157,7 +159,7 @@ private LogLevel(string name, int ordinal) /// The first level. /// The second level. /// The value of level1.Ordinal == level2.Ordinal. - public static bool operator ==(LogLevel level1, LogLevel level2) + public static bool operator ==(LogLevel? level1, LogLevel? level2) { if (ReferenceEquals(level1, level2)) return true; @@ -173,7 +175,7 @@ private LogLevel(string name, int ordinal) /// The first level. /// The second level. /// The value of level1.Ordinal != level2.Ordinal. - public static bool operator !=(LogLevel level1, LogLevel level2) + public static bool operator !=(LogLevel? level1, LogLevel? level2) { if (ReferenceEquals(level1, level2)) return false; @@ -189,7 +191,7 @@ private LogLevel(string name, int ordinal) /// The first level. /// The second level. /// The value of level1.Ordinal > level2.Ordinal. - public static bool operator >(LogLevel level1, LogLevel level2) + public static bool operator >(LogLevel? level1, LogLevel? level2) { if (ReferenceEquals(level1, level2)) return false; @@ -205,7 +207,7 @@ private LogLevel(string name, int ordinal) /// The first level. /// The second level. /// The value of level1.Ordinal >= level2.Ordinal. - public static bool operator >=(LogLevel level1, LogLevel level2) + public static bool operator >=(LogLevel? level1, LogLevel? level2) { if (ReferenceEquals(level1, level2)) return true; @@ -221,7 +223,7 @@ private LogLevel(string name, int ordinal) /// The first level. /// The second level. /// The value of level1.Ordinal < level2.Ordinal. - public static bool operator <(LogLevel level1, LogLevel level2) + public static bool operator <(LogLevel? level1, LogLevel? level2) { if (ReferenceEquals(level1, level2)) return false; @@ -237,7 +239,7 @@ private LogLevel(string name, int ordinal) /// The first level. /// The second level. /// The value of level1.Ordinal <= level2.Ordinal. - public static bool operator <=(LogLevel level1, LogLevel level2) + public static bool operator <=(LogLevel? level1, LogLevel? level2) { if (ReferenceEquals(level1, level2)) return true; @@ -345,7 +347,7 @@ public override string ToString() return _name; } - string IFormattable.ToString(string format, IFormatProvider formatProvider) + string IFormattable.ToString(string? format, IFormatProvider? formatProvider) { if (format is null || (!"D".Equals(format, StringComparison.OrdinalIgnoreCase))) return _name; @@ -360,7 +362,7 @@ public override int GetHashCode() } /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { return Equals(obj as LogLevel); } @@ -371,7 +373,7 @@ public override bool Equals(object obj) /// The to compare with this instance. /// Value of true if the specified is equal to /// this instance; otherwise, false. - public bool Equals(LogLevel other) + public bool Equals(LogLevel? other) { return _ordinal == other?._ordinal; } @@ -386,9 +388,9 @@ public bool Equals(LogLevel other) /// greater than zero when this ordinal is greater than the /// other ordinal. /// - public int CompareTo(object obj) + public int CompareTo(object? obj) { - return CompareTo((LogLevel)obj); + return CompareTo(obj as LogLevel); } /// @@ -401,7 +403,7 @@ public int CompareTo(object obj) /// greater than zero when this ordinal is greater than the /// other ordinal. /// - public int CompareTo(LogLevel other) + public int CompareTo(LogLevel? other) { return _ordinal - (other ?? LogLevel.Off)._ordinal; } diff --git a/src/NLog/LogLevelTypeConverter.cs b/src/NLog/LogLevelTypeConverter.cs index f5d5ae3af0..329ddb8546 100644 --- a/src/NLog/LogLevelTypeConverter.cs +++ b/src/NLog/LogLevelTypeConverter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Attributes { using System; @@ -53,7 +55,7 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c { var valueType = value?.GetType(); if (typeof(string).Equals(valueType)) - return LogLevel.FromString(value?.ToString()); + return LogLevel.FromString(value?.ToString() ?? string.Empty); else if (IsNumericType(valueType)) return LogLevel.FromOrdinal(Convert.ToInt32(value, CultureInfo.InvariantCulture)); else @@ -79,8 +81,10 @@ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo cul return base.ConvertTo(context, culture, value, destinationType); } - private static bool IsNumericType(Type sourceType) + private static bool IsNumericType(Type? sourceType) { + if (sourceType is null) + return false; if (typeof(int).Equals(sourceType)) return true; if (typeof(uint).Equals(sourceType)) diff --git a/src/NLog/LogMessageFormatter.cs b/src/NLog/LogMessageFormatter.cs index 5cd1650340..94fd333033 100644 --- a/src/NLog/LogMessageFormatter.cs +++ b/src/NLog/LogMessageFormatter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { /// @@ -38,5 +40,5 @@ namespace NLog /// /// Log event. /// Formatted message - public delegate string LogMessageFormatter(LogEventInfo logEvent); + public delegate string? LogMessageFormatter(LogEventInfo logEvent); } diff --git a/src/NLog/LogMessageGenerator.cs b/src/NLog/LogMessageGenerator.cs index 2c3d253a53..fd037011ed 100644 --- a/src/NLog/LogMessageGenerator.cs +++ b/src/NLog/LogMessageGenerator.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { /// diff --git a/src/NLog/Logger.cs b/src/NLog/Logger.cs index 4ceec59174..45cef9d9a2 100644 --- a/src/NLog/Logger.cs +++ b/src/NLog/Logger.cs @@ -206,7 +206,7 @@ public IDisposable PushScopeProperty(string propertyName, TValue? proper /// Properties being added to the scope dictionary /// A disposable object that removes the properties from logical context scope on dispose. /// property-dictionary-keys are case-insensitive - public IDisposable PushScopeProperties(IReadOnlyCollection> scopeProperties) + public IDisposable PushScopeProperties(IReadOnlyCollection> scopeProperties) { return ScopeContext.PushProperties(scopeProperties); } @@ -217,7 +217,7 @@ public IDisposable PushScopeProperties(IReadOnlyCollectionProperties being added to the scope dictionary /// A disposable object that removes the properties from logical context scope on dispose. /// property-dictionary-keys are case-insensitive - public IDisposable PushScopeProperties(IReadOnlyCollection> scopeProperties) + public IDisposable PushScopeProperties(IReadOnlyCollection> scopeProperties) { return ScopeContext.PushProperties(scopeProperties); } @@ -228,7 +228,7 @@ public IDisposable PushScopeProperties(IReadOnlyCollection /// Value to added to the scope stack /// A disposable object that pops the nested scope state on dispose. - public IDisposable PushScopeNested(T? nestedState) + public IDisposable PushScopeNested(T nestedState) { return ScopeContext.PushNestedState(nestedState); } @@ -238,7 +238,7 @@ public IDisposable PushScopeNested(T? nestedState) /// /// Value to added to the scope stack /// A disposable object that pops the nested scope state on dispose. - public IDisposable PushScopeNested(object? nestedState) + public IDisposable PushScopeNested(object nestedState) { return ScopeContext.PushNestedState(nestedState); } diff --git a/src/NLog/LoggerImpl.cs b/src/NLog/LoggerImpl.cs index 04595e409b..16d617c904 100644 --- a/src/NLog/LoggerImpl.cs +++ b/src/NLog/LoggerImpl.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -59,7 +61,7 @@ internal static void Write([NotNull] Type loggerType, [NotNull] TargetWithFilter bool attemptCallSiteOptimization = targetsForLevel.TryCallSiteClassNameOptimization(stu, logEvent); if (attemptCallSiteOptimization && targetsForLevel.TryLookupCallSiteClassName(logEvent, out string callSiteClassName)) { - logEvent.CallSiteInformation.CallerClassName = callSiteClassName; + logEvent.GetCallSiteInformationInternal().CallerClassName = callSiteClassName; } else if (attemptCallSiteOptimization || targetsForLevel.MustCaptureStackTrace(stu, logEvent)) { diff --git a/src/NLog/MappedDiagnosticsContext.cs b/src/NLog/MappedDiagnosticsContext.cs index 40d5da995e..58820fbdf1 100644 --- a/src/NLog/MappedDiagnosticsContext.cs +++ b/src/NLog/MappedDiagnosticsContext.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -54,8 +56,9 @@ public static class MappedDiagnosticsContext /// Item value. /// An that can be used to remove the item from the current thread MDC. [Obsolete("Replaced by ScopeContext.PushProperty or Logger.PushScopeProperty using ${scopeproperty}. Marked obsolete on NLog 5.0")] - public static IDisposable SetScoped(string item, string value) + public static IDisposable SetScoped(string item, string? value) { + Guard.ThrowIfNull(item); return ScopeContext.PushProperty(item, value); } @@ -66,8 +69,9 @@ public static IDisposable SetScoped(string item, string value) /// Item value. /// >An that can be used to remove the item from the current thread MDC. [Obsolete("Replaced by ScopeContext.PushProperty or Logger.PushScopeProperty using ${scopeproperty}. Marked obsolete on NLog 5.0")] - public static IDisposable SetScoped(string item, object value) + public static IDisposable SetScoped(string item, object? value) { + Guard.ThrowIfNull(item); return ScopeContext.PushProperty(item, value); } @@ -77,8 +81,9 @@ public static IDisposable SetScoped(string item, object value) /// Item name. /// Item value. [Obsolete("Replaced by ScopeContext.PushProperty or Logger.PushScopeProperty using ${scopeproperty}. Marked obsolete on NLog 5.0")] - public static void Set(string item, string value) + public static void Set(string item, string? value) { + Guard.ThrowIfNull(item); ScopeContext.SetMappedContextLegacy(item, value); } @@ -88,8 +93,9 @@ public static void Set(string item, string value) /// Item name. /// Item value. [Obsolete("Replaced by ScopeContext.PushProperty or Logger.PushScopeProperty using ${scopeproperty}. Marked obsolete on NLog 5.0")] - public static void Set(string item, object value) + public static void Set(string item, object? value) { + Guard.ThrowIfNull(item); ScopeContext.SetMappedContextLegacy(item, value); } @@ -102,6 +108,7 @@ public static void Set(string item, object value) [Obsolete("Replaced by ScopeContext.TryGetProperty. Marked obsolete on NLog 5.0")] public static string Get(string item) { + Guard.ThrowIfNull(item); return Get(item, null); } @@ -113,7 +120,7 @@ public static string Get(string item) /// The value of , if defined; otherwise . /// If is null and the value isn't a already, this call locks the for reading the needed for converting to . [Obsolete("Replaced by ScopeContext.TryGetProperty. Marked obsolete on NLog 5.0")] - public static string Get(string item, IFormatProvider formatProvider) + public static string Get(string item, IFormatProvider? formatProvider) { return FormatHelper.ConvertToString(GetObject(item), formatProvider); } @@ -124,8 +131,9 @@ public static string Get(string item, IFormatProvider formatProvider) /// Item name. /// The value of , if defined; otherwise null. [Obsolete("Replaced by ScopeContext.TryGetProperty. Marked obsolete on NLog 5.0")] - public static object GetObject(string item) + public static object? GetObject(string item) { + Guard.ThrowIfNull(item); if (ScopeContext.TryGetProperty(item, out var value)) return value; else @@ -150,6 +158,7 @@ public static ICollection GetNames() [Obsolete("Replaced by ScopeContext.TryGetProperty. Marked obsolete on NLog 5.0")] public static bool Contains(string item) { + Guard.ThrowIfNull(item); return ScopeContext.TryGetProperty(item, out var _); } @@ -160,6 +169,7 @@ public static bool Contains(string item) [Obsolete("Replaced by dispose of return value from ScopeContext.PushProperty. Marked obsolete on NLog 5.0")] public static void Remove(string item) { + Guard.ThrowIfNull(item); ScopeContext.RemoveMappedContextLegacy(item); } diff --git a/src/NLog/MappedDiagnosticsLogicalContext.cs b/src/NLog/MappedDiagnosticsLogicalContext.cs index 4dc464e844..8b5cb2930c 100644 --- a/src/NLog/MappedDiagnosticsLogicalContext.cs +++ b/src/NLog/MappedDiagnosticsLogicalContext.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -71,7 +73,7 @@ public static string Get(string item) /// The value of , if defined; otherwise . /// If is null and the value isn't a already, this call locks the for reading the needed for converting to . [Obsolete("Replaced by ScopeContext.TryGetProperty. Marked obsolete on NLog 5.0")] - public static string Get(string item, IFormatProvider formatProvider) + public static string Get(string item, IFormatProvider? formatProvider) { return FormatHelper.ConvertToString(GetObject(item), formatProvider); } @@ -82,8 +84,9 @@ public static string Get(string item, IFormatProvider formatProvider) /// Item name. /// The value of , if defined; otherwise null. [Obsolete("Replaced by ScopeContext.TryGetProperty. Marked obsolete on NLog 5.0")] - public static object GetObject(string item) + public static object? GetObject(string item) { + Guard.ThrowIfNull(item); if (ScopeContext.TryGetProperty(item, out var value)) return value; else @@ -97,8 +100,9 @@ public static object GetObject(string item) /// Item value. /// >An that can be used to remove the item from the current logical context. [Obsolete("Replaced by ScopeContext.PushProperty or Logger.PushScopeProperty using ${scopeproperty}. Marked obsolete on NLog 5.0")] - public static IDisposable SetScoped(string item, string value) + public static IDisposable SetScoped(string item, string? value) { + Guard.ThrowIfNull(item); return SetScoped(item, value); } @@ -109,8 +113,9 @@ public static IDisposable SetScoped(string item, string value) /// Item value. /// >An that can be used to remove the item from the current logical context. [Obsolete("Replaced by ScopeContext.PushProperty or Logger.PushScopeProperty using ${scopeproperty}. Marked obsolete on NLog 5.0")] - public static IDisposable SetScoped(string item, object value) + public static IDisposable SetScoped(string item, object? value) { + Guard.ThrowIfNull(item); return SetScoped(item, value); } @@ -121,8 +126,9 @@ public static IDisposable SetScoped(string item, object value) /// Item value. /// >An that can be used to remove the item from the current logical context. [Obsolete("Replaced by ScopeContext.PushProperty or Logger.PushScopeProperty using ${scopeproperty}. Marked obsolete on NLog 5.0")] - public static IDisposable SetScoped(string item, T value) + public static IDisposable SetScoped(string item, T? value) { + Guard.ThrowIfNull(item); return ScopeContext.PushProperty(item, value); } @@ -133,7 +139,7 @@ public static IDisposable SetScoped(string item, T value) /// . /// >An that can be used to remove the item from the current logical context (null if no items). [Obsolete("Replaced by ScopeContext.PushProperties or Logger.PushScopeProperty using ${scopeproperty}. Marked obsolete on NLog 5.0")] - public static IDisposable SetScoped(IReadOnlyList> items) + public static IDisposable SetScoped(IReadOnlyList> items) { return ScopeContext.PushProperties(items); } @@ -144,7 +150,7 @@ public static IDisposable SetScoped(IReadOnlyList> /// Item name. /// Item value. [Obsolete("Replaced by ScopeContext.PushProperty or Logger.PushScopeProperty using ${scopeproperty}. Marked obsolete on NLog 5.0")] - public static void Set(string item, string value) + public static void Set(string item, string? value) { Set(item, value); } @@ -155,7 +161,7 @@ public static void Set(string item, string value) /// Item name. /// Item value. [Obsolete("Replaced by ScopeContext.PushProperty or Logger.PushScopeProperty using ${scopeproperty}. Marked obsolete on NLog 5.0")] - public static void Set(string item, object value) + public static void Set(string item, object? value) { Set(item, value); } @@ -166,8 +172,9 @@ public static void Set(string item, object value) /// Item name. /// Item value. [Obsolete("Replaced by ScopeContext.PushProperty or Logger.PushScopeProperty using ${scopeproperty}. Marked obsolete on NLog 5.0")] - public static void Set(string item, T value) + public static void Set(string item, T? value) { + Guard.ThrowIfNull(item); ScopeContext.SetMappedContextLegacy(item, value); } @@ -189,6 +196,7 @@ public static ICollection GetNames() [Obsolete("Replaced by ScopeContext.TryGetProperty. Marked obsolete on NLog 5.0")] public static bool Contains(string item) { + Guard.ThrowIfNull(item); return ScopeContext.TryGetProperty(item, out var _); } @@ -199,6 +207,7 @@ public static bool Contains(string item) [Obsolete("Replaced by dispose of return value from ScopeContext.PushProperty. Marked obsolete on NLog 5.0")] public static void Remove(string item) { + Guard.ThrowIfNull(item); ScopeContext.RemoveMappedContextLegacy(item); } diff --git a/src/NLog/NLogConfigurationException.cs b/src/NLog/NLogConfigurationException.cs index 38732608a5..50a6242a88 100644 --- a/src/NLog/NLogConfigurationException.cs +++ b/src/NLog/NLogConfigurationException.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -70,7 +72,7 @@ public NLogConfigurationException(string message) [Obsolete("Instead use string interpolation. Marked obsolete with NLog 5.0")] [StringFormatMethod("message")] [EditorBrowsable(EditorBrowsableState.Never)] - public NLogConfigurationException(string message, params object[] messageParameters) + public NLogConfigurationException(string message, params object?[] messageParameters) : base(string.Format(message, messageParameters)) { } @@ -85,7 +87,7 @@ public NLogConfigurationException(string message, params object[] messageParamet [Obsolete("Instead use string interpolation. Marked obsolete with NLog 5.0")] [StringFormatMethod("message")] [EditorBrowsable(EditorBrowsableState.Never)] - public NLogConfigurationException(Exception innerException, string message, params object[] messageParameters) + public NLogConfigurationException(Exception? innerException, string message, params object?[] messageParameters) : base(string.Format(message, messageParameters), innerException) { } @@ -95,7 +97,7 @@ public NLogConfigurationException(Exception innerException, string message, para /// /// The message. /// The inner exception. - public NLogConfigurationException(string message, Exception innerException) + public NLogConfigurationException(string message, Exception? innerException) : base(message, innerException) { } diff --git a/src/NLog/NLogRuntimeException.cs b/src/NLog/NLogRuntimeException.cs index 176fbbf702..b123545534 100644 --- a/src/NLog/NLogRuntimeException.cs +++ b/src/NLog/NLogRuntimeException.cs @@ -31,12 +31,13 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using JetBrains.Annotations; +#nullable enable namespace NLog { using System; using System.ComponentModel; + using JetBrains.Annotations; /// /// Exception thrown during log event processing. @@ -71,7 +72,7 @@ public NLogRuntimeException(string message) [Obsolete("Instead use string interpolation. Marked obsolete with NLog 5.0")] [StringFormatMethod("message")] [EditorBrowsable(EditorBrowsableState.Never)] - public NLogRuntimeException(string message, params object[] messageParameters) + public NLogRuntimeException(string message, params object?[] messageParameters) : base(string.Format(message, messageParameters)) { } @@ -81,7 +82,7 @@ public NLogRuntimeException(string message, params object[] messageParameters) /// /// The message. /// The inner exception. - public NLogRuntimeException(string message, Exception innerException) + public NLogRuntimeException(string message, Exception? innerException) : base(message, innerException) { } diff --git a/src/NLog/NestedDiagnosticsContext.cs b/src/NLog/NestedDiagnosticsContext.cs index 44c4492c5e..cdabd3fa98 100644 --- a/src/NLog/NestedDiagnosticsContext.cs +++ b/src/NLog/NestedDiagnosticsContext.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using System; @@ -58,7 +60,7 @@ public static class NestedDiagnosticsContext /// /// The object at the top of the NDC stack if defined; otherwise null. [Obsolete("Replaced by ScopeContext.PeekNestedState. Marked obsolete on NLog 5.0")] - public static object TopObject => PeekObject(); + public static object? TopObject => PeekObject(); /// /// Pushes the specified text on current thread NDC. @@ -98,7 +100,7 @@ public static string Pop() /// The to use when converting the value to a string. /// The top message, which is removed from the stack, as a string value. [Obsolete("Replaced by dispose of return value from ScopeContext.PushNestedState or Logger.PushScopeNested. Marked obsolete on NLog 5.0")] - public static string Pop(IFormatProvider formatProvider) + public static string Pop(IFormatProvider? formatProvider) { return FormatHelper.ConvertToString(PopObject() ?? string.Empty, formatProvider); } @@ -108,7 +110,7 @@ public static string Pop(IFormatProvider formatProvider) /// /// The object from the top of the NDC stack, if defined; otherwise null. [Obsolete("Replaced by dispose of return value from ScopeContext.PushNestedState or Logger.PushScopeNested. Marked obsolete on NLog 5.0")] - public static object PopObject() + public static object? PopObject() { return ScopeContext.PopNestedContextLegacy(); } @@ -118,7 +120,7 @@ public static object PopObject() /// /// The object from the top of the NDC stack, if defined; otherwise null. [Obsolete("Replaced by ScopeContext.PeekNestedState. Marked obsolete on NLog 5.0")] - public static object PeekObject() + public static object? PeekObject() { return ScopeContext.PeekNestedState(); } @@ -148,7 +150,7 @@ public static string[] GetAllMessages() /// The to use when converting a value to a string. /// Array of strings. [Obsolete("Replaced by ScopeContext.GetAllNestedStates. Marked obsolete on NLog 5.0")] - public static string[] GetAllMessages(IFormatProvider formatProvider) + public static string[] GetAllMessages(IFormatProvider? formatProvider) { var stack = GetAllObjects(); if (stack.Length == 0) diff --git a/src/NLog/NestedDiagnosticsLogicalContext.cs b/src/NLog/NestedDiagnosticsLogicalContext.cs index 6d02ece540..ecefc627d2 100644 --- a/src/NLog/NestedDiagnosticsLogicalContext.cs +++ b/src/NLog/NestedDiagnosticsLogicalContext.cs @@ -31,6 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable namespace NLog { @@ -75,7 +76,7 @@ public static IDisposable PushObject(object value) /// The top message which is no longer on the stack. /// this methods returns a object instead of string, this because of backwards-compatibility [Obsolete("Replaced by dispose of return value from ScopeContext.PushNestedState or Logger.PushScopeNested. Marked obsolete on NLog 5.0")] - public static object Pop() + public static object? Pop() { return PopObject(); } @@ -96,7 +97,7 @@ public static string Pop(IFormatProvider formatProvider) /// /// The object from the top of the NDLC stack, if defined; otherwise null. [Obsolete("Replaced by dispose of return value from ScopeContext.PushNestedState or Logger.PushScopeNested. Marked obsolete on NLog 5.0")] - public static object PopObject() + public static object? PopObject() { return ScopeContext.PopNestedContextLegacy(); } @@ -106,7 +107,7 @@ public static object PopObject() /// /// The object from the top of the NDLC stack, if defined; otherwise null. [Obsolete("Replaced by ScopeContext.PeekNestedState. Marked obsolete on NLog 5.0")] - public static object PeekObject() + public static object? PeekObject() { return ScopeContext.PeekNestedState(); } @@ -136,7 +137,7 @@ public static string[] GetAllMessages() /// The to use when converting a value to a string. /// Array of strings. [Obsolete("Replaced by ScopeContext.GetAllNestedStates. Marked obsolete on NLog 5.0")] - public static string[] GetAllMessages(IFormatProvider formatProvider) + public static string[] GetAllMessages(IFormatProvider? formatProvider) { return GetAllObjects().Select((o) => FormatHelper.ConvertToString(o, formatProvider)).ToArray(); } @@ -156,7 +157,7 @@ interface INestedContext : IDisposable { INestedContext Parent { get; } int FrameLevel { get; } - object Value { get; } + object? Value { get; } long CreatedTimeUtcTicks { get; } } @@ -172,11 +173,11 @@ sealed class NestedContext : INestedContext public int FrameLevel { get; } private int _disposed; - object INestedContext.Value + object? INestedContext.Value { get { - object value = Value; + object? value = Value; #if NET35 || NET40 || NET45 if (value is ObjectHandleSerializer objectHandle) { @@ -205,7 +206,7 @@ void IDisposable.Dispose() public override string ToString() { - object value = Value; + object? value = Value; return value?.ToString() ?? "null"; } } diff --git a/src/NLog/NullLogger.cs b/src/NLog/NullLogger.cs index 8a30ddc704..538d6a2a4a 100644 --- a/src/NLog/NullLogger.cs +++ b/src/NLog/NullLogger.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog { using Internal; diff --git a/src/NLog/ScopeContext.cs b/src/NLog/ScopeContext.cs index 7bc7363167..c62ab644e7 100644 --- a/src/NLog/ScopeContext.cs +++ b/src/NLog/ScopeContext.cs @@ -31,13 +31,15 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Collections.Generic; -using System.Linq; -using NLog.Internal; +#nullable enable namespace NLog { + using System; + using System.Collections.Generic; + using System.Linq; + using NLog.Internal; + /// /// stores state in the async thread execution context. All LogEvents created /// within a scope can include the scope state in the target output. The logical context scope supports @@ -61,9 +63,9 @@ public static class ScopeContext /// Properties being added to the scope dictionary /// A disposable object that pops the nested scope state on dispose (including properties). /// Scope dictionary keys are case-insensitive - public static IDisposable PushNestedStateProperties(object nestedState, IReadOnlyCollection> properties) + public static IDisposable PushNestedStateProperties(object? nestedState, IReadOnlyCollection>? properties) { - properties = properties ?? ArrayHelper.Empty>(); + properties = properties ?? ArrayHelper.Empty>(); if (properties.Count > 0 || nestedState is null) { #if !NET45 @@ -74,9 +76,9 @@ public static IDisposable PushNestedStateProperties(object nestedState, IReadOnl if (allProperties != null) { // Collapse all 3 property-scopes into a collapsed scope, and return bookmark that can restore original parent (Avoid huge object-graphs) - ScopeContextPropertyEnumerator.CopyScopePropertiesToDictionary(properties, allProperties); + ScopeContextPropertyEnumerator.CopyScopePropertiesToDictionary(properties, allProperties); - var collapsedState = new ScopeContextPropertiesAsyncState(parent.Parent.Parent, allProperties, nestedState); + var collapsedState = new ScopeContextPropertiesAsyncState(parent?.Parent?.Parent, allProperties, nestedState); SetAsyncLocalContext(collapsedState); return new ScopeContextPropertiesCollapsed(parent, collapsedState); } @@ -105,7 +107,7 @@ public static IDisposable PushNestedStateProperties(object nestedState, IReadOnl /// Properties being added to the scope dictionary /// A disposable object that removes the properties from logical context scope on dispose. /// Scope dictionary keys are case-insensitive - public static IDisposable PushProperties(IReadOnlyCollection> properties) + public static IDisposable PushProperties(IReadOnlyCollection> properties) { return PushProperties(properties); } @@ -116,7 +118,7 @@ public static IDisposable PushProperties(IReadOnlyCollectionProperties being added to the scope dictionary /// A disposable object that removes the properties from logical context scope on dispose. /// Scope dictionary keys are case-insensitive - public static IDisposable PushProperties(IReadOnlyCollection> properties) + public static IDisposable PushProperties(IReadOnlyCollection> properties) { #if !NET45 var parent = GetAsyncLocalContext(); @@ -127,7 +129,7 @@ public static IDisposable PushProperties(IReadOnlyCollection.CopyScopePropertiesToDictionary(properties, allProperties); - var collapsedState = new ScopeContextPropertiesAsyncState(parent.Parent.Parent, allProperties); + var collapsedState = new ScopeContextPropertiesAsyncState(parent?.Parent?.Parent, allProperties); SetAsyncLocalContext(collapsedState); return new ScopeContextPropertiesCollapsed(parent, collapsedState); } @@ -149,8 +151,9 @@ public static IDisposable PushProperties(IReadOnlyCollectionValue of property /// A disposable object that removes the properties from logical context scope on dispose. /// Scope dictionary keys are case-insensitive - public static IDisposable PushProperty(string key, TValue value) + public static IDisposable PushProperty(string key, TValue? value) { + Guard.ThrowIfNull(key); #if !NET35 && !NET40 && !NET45 var parent = GetAsyncLocalContext(); @@ -160,7 +163,7 @@ public static IDisposable PushProperty(string key, TValue value) // Collapse all 3 property-scopes into a collapsed scope, and return bookmark that can restore original parent (Avoid huge object-graphs) allProperties[key] = value; - var collapsedState = new ScopeContextPropertiesAsyncState(parent.Parent.Parent, allProperties); + var collapsedState = new ScopeContextPropertiesAsyncState(parent?.Parent?.Parent, allProperties); SetAsyncLocalContext(collapsedState); return new ScopeContextPropertiesCollapsed(parent, collapsedState); } @@ -181,7 +184,7 @@ public static IDisposable PushProperty(string key, TValue value) /// Value of property /// A disposable object that removes the properties from logical context scope on dispose. /// Scope dictionary keys are case-insensitive - public static IDisposable PushProperty(string key, object value) + public static IDisposable PushProperty(string key, object? value) { return PushProperty(key, value); } @@ -200,7 +203,13 @@ public static IDisposable PushNestedState(T nestedState) SetAsyncLocalContext(current); return current; #else - object objectValue = nestedState; + object? objectValue = nestedState; + if (objectValue is null) + { + var oldContext = GetNestedContextCallContext(); + return new ScopeContextNestedState(oldContext, objectValue); + } + var oldNestedContext = PushNestedStateCallContext(objectValue); return new ScopeContextNestedState(oldNestedContext, objectValue); #endif @@ -233,12 +242,12 @@ public static void Clear() /// Retrieves all properties stored within the logical context scopes /// /// Collection of all properties - public static IEnumerable> GetAllProperties() + public static IEnumerable> GetAllProperties() { #if !NET35 && !NET40 && !NET45 var contextState = GetAsyncLocalContext(); var propertyCollector = new ScopeContextPropertyCollector(); - return contextState?.CaptureContextProperties(ref propertyCollector) ?? ArrayHelper.Empty>(); + return contextState?.CaptureContextProperties(ref propertyCollector) ?? ArrayHelper.Empty>(); #else var mappedContext = GetMappedContextCallContext(); if (mappedContext?.Count > 0) @@ -252,13 +261,13 @@ public static IEnumerable> GetAllProperties() } return mappedContext; } - return ArrayHelper.Empty>(); + return ArrayHelper.Empty>(); #endif } - internal static ScopeContextPropertyEnumerator GetAllPropertiesEnumerator() + internal static ScopeContextPropertyEnumerator GetAllPropertiesEnumerator() { - return new ScopeContextPropertyEnumerator(GetAllProperties()); + return new ScopeContextPropertyEnumerator(GetAllProperties()); } /// @@ -268,7 +277,7 @@ internal static ScopeContextPropertyEnumerator GetAllPropertiesEnumerato /// When this method returns, contains the value associated with the specified key /// Returns true when value is found with the specified key /// Scope dictionary keys are case-insensitive - public static bool TryGetProperty(string key, out object value) + public static bool TryGetProperty(string key, out object? value) { #if !NET35 && !NET40 && !NET45 var contextState = GetAsyncLocalContext(); @@ -350,7 +359,7 @@ internal static IList GetAllNestedStateList() /// Peeks the top value from the logical context scope stack /// /// Value from the top of the stack. - public static object PeekNestedState() + public static object? PeekNestedState() { #if !NET35 && !NET40 && !NET45 var parent = GetAsyncLocalContext(); @@ -426,15 +435,15 @@ public static object PeekNestedState() } #if !NET35 && !NET40 && !NET45 - private static bool TryLookupProperty(IReadOnlyCollection> scopeProperties, string key, out object value) + private static bool TryLookupProperty(IReadOnlyCollection> scopeProperties, string key, out object? value) { - if (scopeProperties is Dictionary mappedDictionary && ReferenceEquals(mappedDictionary.Comparer, DefaultComparer)) + if (scopeProperties is Dictionary mappedDictionary && ReferenceEquals(mappedDictionary.Comparer, DefaultComparer)) { return mappedDictionary.TryGetValue(key, out value); } else { - using (var scopeEnumerator = new ScopeContextPropertyEnumerator(scopeProperties)) + using (var scopeEnumerator = new ScopeContextPropertyEnumerator(scopeProperties)) { while (scopeEnumerator.MoveNext()) { @@ -473,28 +482,28 @@ private static TimeSpan GetNestedStateDuration(long scopeTimestamp, long current /// private sealed class ScopeContextPropertiesCollapsed : IDisposable { - private readonly IScopeContextAsyncState _parent; + private readonly IScopeContextAsyncState? _parent; private readonly IScopeContextPropertiesAsyncState _collapsed; private bool _disposed; - public ScopeContextPropertiesCollapsed(IScopeContextAsyncState parent, IScopeContextPropertiesAsyncState collapsed) + public ScopeContextPropertiesCollapsed(IScopeContextAsyncState? parent, IScopeContextPropertiesAsyncState collapsed) { _parent = parent; _collapsed = collapsed; } - public static Dictionary BuildCollapsedDictionary(IScopeContextAsyncState parent, int initialCapacity) + public static Dictionary? BuildCollapsedDictionary(IScopeContextAsyncState? parent, int initialCapacity) { if (parent is IScopeContextPropertiesAsyncState parentProperties && parentProperties.Parent is IScopeContextPropertiesAsyncState grandParentProperties) { if (parentProperties.NestedState is null && grandParentProperties.NestedState is null) { - var propertyCollectorList = new List>(); // Marks the collector as active + var propertyCollectorList = new List>(); // Marks the collector as active var propertyCollector = new ScopeContextPropertyCollector(propertyCollectorList); var propertyCollection = propertyCollector.StartCaptureProperties(parent); - if (propertyCollectorList.Count > 0 && propertyCollection is Dictionary propertyDictionary) + if (propertyCollectorList.Count > 0 && propertyCollection is Dictionary propertyDictionary) return propertyDictionary; // New property collector was built from the list - propertyDictionary = new Dictionary(propertyCollection.Count + initialCapacity, ScopeContext.DefaultComparer); + propertyDictionary = new Dictionary(propertyCollection.Count + initialCapacity, ScopeContext.DefaultComparer); ScopeContextPropertyEnumerator.CopyScopePropertiesToDictionary(propertyCollection, propertyDictionary); return propertyDictionary; } @@ -518,27 +527,27 @@ public override string ToString() } } - internal static void SetAsyncLocalContext(IScopeContextAsyncState newValue) + internal static void SetAsyncLocalContext(IScopeContextAsyncState? newValue) { AsyncNestedDiagnosticsContext.Value = newValue; } - private static IScopeContextAsyncState GetAsyncLocalContext() + private static IScopeContextAsyncState? GetAsyncLocalContext() { return AsyncNestedDiagnosticsContext.Value; } - private static readonly System.Threading.AsyncLocal AsyncNestedDiagnosticsContext = new System.Threading.AsyncLocal(); + private static readonly System.Threading.AsyncLocal AsyncNestedDiagnosticsContext = new System.Threading.AsyncLocal(); #endif #if NET45 private sealed class ScopeContextNestedStateProperties : IDisposable { - private readonly LinkedList _parentNestedContext; - private readonly Dictionary _parentMappedContext; + private readonly LinkedList? _parentNestedContext; + private readonly Dictionary? _parentMappedContext; - public ScopeContextNestedStateProperties(LinkedList parentNestedContext, Dictionary parentMappedContext) + public ScopeContextNestedStateProperties(LinkedList? parentNestedContext, Dictionary? parentMappedContext) { _parentNestedContext = parentNestedContext; _parentMappedContext = parentMappedContext; @@ -555,7 +564,7 @@ public void Dispose() [Obsolete("Replaced by ScopeContext.PushProperty. Marked obsolete on NLog 5.0")] - internal static void SetMappedContextLegacy(string key, TValue value) + internal static void SetMappedContextLegacy(string key, TValue? value) { #if !NET35 && !NET40 && !NET45 PushProperty(key, value); @@ -611,7 +620,7 @@ internal static void RemoveMappedContextLegacy(string key) } [Obsolete("Replaced by disposing return value from ScopeContext.PushNestedState. Marked obsolete on NLog 5.0")] - internal static object PopNestedContextLegacy() + internal static object? PopNestedContextLegacy() { #if !NET35 && !NET40 && !NET45 var contextState = GetAsyncLocalContext(); @@ -627,7 +636,7 @@ internal static object PopNestedContextLegacy() // Replace with new legacy-scope, the legacy-scope can be discarded when previous parent scope is restored var propertyCollector = new ScopeContextPropertyCollector(); var stackTopValue = nestedStates[0]; - var allProperties = contextState.CaptureContextProperties(ref propertyCollector) ?? ArrayHelper.Empty>(); + var allProperties = contextState.CaptureContextProperties(ref propertyCollector) ?? ArrayHelper.Empty>(); var nestedContext = ArrayHelper.Empty(); if (nestedStates.Count > 1) { @@ -720,7 +729,7 @@ internal static void ClearNestedContextLegacy() #if NET35 || NET40 || NET45 #if !NET35 && !NET40 - private static Dictionary PushPropertiesCallContext(IReadOnlyCollection> properties) + private static Dictionary? PushPropertiesCallContext(IReadOnlyCollection> properties) { var oldContext = GetMappedContextCallContext(); var newContext = CloneMappedContext(oldContext, properties.Count); @@ -737,7 +746,7 @@ private static Dictionary PushPropertiesCallContext(IRea } #endif - private static Dictionary PushPropertyCallContext(string propertyName, TValue propertyValue) + private static Dictionary? PushPropertyCallContext(string propertyName, TValue propertyValue) { var oldContext = GetMappedContextCallContext(); var newContext = CloneMappedContext(oldContext, 1); @@ -751,13 +760,13 @@ private static void ClearMappedContextCallContext() SetMappedContextCallContext(null); } - private static IEnumerable> GetAllPropertiesUnwrapped(Dictionary properties) + private static IEnumerable> GetAllPropertiesUnwrapped(Dictionary properties) { foreach (var item in properties) { if (item.Value is ObjectHandleSerializer objectHandle) { - yield return new KeyValuePair(item.Key, objectHandle.Unwrap()); + yield return new KeyValuePair(item.Key, objectHandle.Unwrap()); } else { @@ -766,22 +775,22 @@ private static IEnumerable> GetAllPropertiesUnwrapp } } - private static Dictionary CloneMappedContext(Dictionary oldContext, int initialCapacity = 0) + private static Dictionary CloneMappedContext(Dictionary? oldContext, int initialCapacity = 0) { if (oldContext?.Count > 0) { - var dictionary = new Dictionary(oldContext.Count + initialCapacity, DefaultComparer); + var dictionary = new Dictionary(oldContext.Count + initialCapacity, DefaultComparer); foreach (var keyValue in oldContext) dictionary[keyValue.Key] = keyValue.Value; return dictionary; } - return new Dictionary(initialCapacity, DefaultComparer); + return new Dictionary(initialCapacity, DefaultComparer); } - private static void SetPropertyCallContext(string item, TValue value, IDictionary mappedContext) + private static void SetPropertyCallContext(string item, TValue? value, IDictionary mappedContext) { - object objectValue = value; + object? objectValue = value; if (Convert.GetTypeCode(objectValue) != TypeCode.Object) mappedContext[item] = objectValue; else @@ -790,10 +799,10 @@ private static void SetPropertyCallContext(string item, TValue value, ID private sealed class ScopeContextProperties : IDisposable { - private readonly Dictionary _oldContext; + private readonly Dictionary? _oldContext; private bool _diposed; - public ScopeContextProperties(Dictionary oldContext) + public ScopeContextProperties(Dictionary? oldContext) { _oldContext = oldContext; } @@ -808,7 +817,7 @@ public void Dispose() } } - private static void SetMappedContextCallContext(Dictionary newValue) + private static void SetMappedContextCallContext(Dictionary? newValue) { if (newValue is null) System.Runtime.Remoting.Messaging.CallContext.FreeNamedDataSlot(MappedContextDataSlotName); @@ -816,14 +825,14 @@ private static void SetMappedContextCallContext(Dictionary newVa System.Runtime.Remoting.Messaging.CallContext.LogicalSetData(MappedContextDataSlotName, newValue); } - internal static Dictionary GetMappedContextCallContext() + internal static Dictionary? GetMappedContextCallContext() { - return System.Runtime.Remoting.Messaging.CallContext.LogicalGetData(MappedContextDataSlotName) as Dictionary; + return System.Runtime.Remoting.Messaging.CallContext.LogicalGetData(MappedContextDataSlotName) as Dictionary; } private const string MappedContextDataSlotName = "NLog.AsyncableMappedDiagnosticsContext"; - private static LinkedList PushNestedStateCallContext(object objectValue) + private static LinkedList? PushNestedStateCallContext(object objectValue) { var oldContext = GetNestedContextCallContext(); var newContext = oldContext?.Count > 0 ? new LinkedList(oldContext) : new LinkedList(); @@ -841,11 +850,11 @@ private static void ClearNestedContextCallContext() private sealed class ScopeContextNestedState : IDisposable { - private readonly LinkedList _oldContext; - private readonly object _nestedState; + private readonly LinkedList? _oldContext; + private readonly object? _nestedState; private bool _diposed; - public ScopeContextNestedState(LinkedList oldContext, object nestedState) + public ScopeContextNestedState(LinkedList? oldContext, object? nestedState) { _oldContext = oldContext; _nestedState = nestedState; @@ -867,7 +876,7 @@ public override string ToString() } [System.Security.SecuritySafeCriticalAttribute] - private static void SetNestedContextCallContext(LinkedList nestedContext) + private static void SetNestedContextCallContext(LinkedList? nestedContext) { if (nestedContext is null) System.Runtime.Remoting.Messaging.CallContext.FreeNamedDataSlot(NestedContextDataSlotName); @@ -877,7 +886,7 @@ private static void SetNestedContextCallContext(LinkedList nestedContext [System.Security.SecuritySafeCriticalAttribute] - private static LinkedList GetNestedContextCallContext() + private static LinkedList? GetNestedContextCallContext() { return System.Runtime.Remoting.Messaging.CallContext.LogicalGetData(NestedContextDataSlotName) as LinkedList; } From 0ebefa9a5f5c64c47bbecea6609a8e45bd2ab0f4 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 10 May 2025 12:07:04 +0200 Subject: [PATCH 110/224] Internal classes with nullable references (#5809) --- .../Internal/SortHelpers.cs | 167 ++-------------- src/NLog/Config/LoggingConfigurationParser.cs | 40 ++-- src/NLog/Internal/AppEnvironmentWrapper.cs | 30 +-- src/NLog/Internal/AppendBuilderCreator.cs | 6 +- src/NLog/Internal/ArrayHelper.cs | 2 + src/NLog/Internal/AssemblyHelpers.cs | 3 + .../Internal/AssemblyMetadataAttribute.cs | 3 + src/NLog/Internal/AsyncOperationCounter.cs | 12 +- src/NLog/Internal/CallSiteInformation.cs | 57 +++--- src/NLog/Internal/CollectionExtensions.cs | 10 +- .../Internal/ConfigVariablesDictionary.cs | 21 ++- src/NLog/Internal/ConfigurationManager.cs | 2 + .../Internal/DictionaryEntryEnumerable.cs | 36 +++- .../DynamicallyAccessedMemberTypes.cs | 10 +- src/NLog/Internal/EnvironmentHelper.cs | 13 +- src/NLog/Internal/ExceptionHelper.cs | 4 +- .../ExceptionMessageFormatProvider.cs | 4 +- src/NLog/Internal/FileInfoHelper.cs | 8 +- src/NLog/Internal/FormatHelper.cs | 10 +- src/NLog/Internal/Guard.cs | 3 + src/NLog/Internal/IAppEnvironment.cs | 2 + src/NLog/Internal/IConfigurationManager.cs | 2 + src/NLog/Internal/IFileSystem.cs | 2 + src/NLog/Internal/ILogMessageFormatter.cs | 2 + src/NLog/Internal/IRawValue.cs | 4 +- src/NLog/Internal/IRenderable.cs | 2 + src/NLog/Internal/IStringValueRenderer.cs | 4 +- src/NLog/Internal/ISupportsInitialize.cs | 4 +- src/NLog/Internal/ITargetWithFilterChain.cs | 2 + .../Internal/LogMessageStringFormatter.cs | 2 + .../Internal/LogMessageTemplateFormatter.cs | 27 +-- src/NLog/Internal/MruCache.cs | 6 +- src/NLog/Internal/MultiFileWatcher.cs | 6 +- src/NLog/Internal/NativeMethods.cs | 2 + src/NLog/Internal/ObjectGraphScanner.cs | 10 +- src/NLog/Internal/ObjectHandleSerializer.cs | 10 +- src/NLog/Internal/ObjectPropertyPath.cs | 8 +- src/NLog/Internal/ObjectReflectionCache.cs | 146 +++++++------- .../OverloadResolutionPriorityAttribute.cs | 2 + src/NLog/Internal/PathHelpers.cs | 6 +- src/NLog/Internal/PlatformDetector.cs | 2 + src/NLog/Internal/PropertyHelper.cs | 44 ++--- src/NLog/Internal/ReflectionHelpers.cs | 10 +- .../Internal/ReusableAsyncLogEventList.cs | 6 +- src/NLog/Internal/ReusableBufferCreator.cs | 2 + src/NLog/Internal/ReusableBuilderCreator.cs | 4 +- src/NLog/Internal/ReusableObjectCreator.cs | 6 +- src/NLog/Internal/ReusableStreamCreator.cs | 8 +- src/NLog/Internal/RuntimeOS.cs | 2 + src/NLog/Internal/SetupBuilder.cs | 2 + .../SetupConfigurationLoggingRuleBuilder.cs | 4 +- .../SetupConfigurationTargetBuilder.cs | 6 +- src/NLog/Internal/SetupExtensionsBuilder.cs | 2 + .../Internal/SetupInternalLoggerBuilder.cs | 2 + .../Internal/SetupLoadConfigurationBuilder.cs | 4 +- src/NLog/Internal/SetupLogFactoryBuilder.cs | 2 + .../Internal/SetupSerializationBuilder.cs | 2 + src/NLog/Internal/SimpleStringReader.cs | 2 + src/NLog/Internal/SingleCallContinuation.cs | 6 +- .../Internal/SingleItemOptimizedHashSet.cs | 29 +-- src/NLog/Internal/SortHelpers.cs | 177 +++-------------- src/NLog/Internal/StackTraceUsageUtils.cs | 28 +-- src/NLog/Internal/StringBuilderExt.cs | 17 +- src/NLog/Internal/StringBuilderPool.cs | 12 +- src/NLog/Internal/StringHelpers.cs | 16 +- src/NLog/Internal/StringSplitter.cs | 8 +- src/NLog/Internal/TargetWithFilterChain.cs | 31 +-- src/NLog/Internal/ThreadSafeDictionary.cs | 4 +- src/NLog/Internal/TimeoutContinuation.cs | 6 +- src/NLog/Internal/UrlHelper.cs | 6 +- src/NLog/Internal/XmlHelper.cs | 178 +++++++++--------- src/NLog/Internal/XmlParser.cs | 33 ++-- .../LayoutRenderers/CallSiteLayoutRenderer.cs | 8 +- src/NLog/Layouts/Typed/Layout.cs | 2 +- src/NLog/LoggerImpl.cs | 2 +- src/NLog/ScopeContext.cs | 2 +- tests/NLog.UnitTests/ApiTests.cs | 1 + .../Internal/SortHelpersTests.cs | 172 +---------------- .../NLog.UnitTests/Internal/XmlParserTests.cs | 15 +- 79 files changed, 643 insertions(+), 910 deletions(-) diff --git a/src/NLog.Targets.ConcurrentFile/Internal/SortHelpers.cs b/src/NLog.Targets.ConcurrentFile/Internal/SortHelpers.cs index 46f45e278a..7a31cdf718 100644 --- a/src/NLog.Targets.ConcurrentFile/Internal/SortHelpers.cs +++ b/src/NLog.Targets.ConcurrentFile/Internal/SortHelpers.cs @@ -62,7 +62,7 @@ internal static class SortHelpers /// /// Dictionary where keys are unique input keys, and values are lists of . /// - public static ReadOnlySingleBucketDictionary> BucketSort(this IList inputs, KeySelector keySelector) + public static ReadOnlySingleBucketGroupBy> BucketSort(this IList inputs, KeySelector keySelector) { return BucketSort(inputs, keySelector, EqualityComparer.Default); } @@ -78,18 +78,17 @@ public static ReadOnlySingleBucketDictionary> BucketSort /// Dictionary where keys are unique input keys, and values are lists of . /// - public static ReadOnlySingleBucketDictionary> BucketSort(this IList inputs, KeySelector keySelector, IEqualityComparer keyComparer) + public static ReadOnlySingleBucketGroupBy> BucketSort(this IList inputs, KeySelector keySelector, IEqualityComparer keyComparer) { + if (inputs.Count == 0) + return new ReadOnlySingleBucketGroupBy>(singleBucket: null, keyComparer); + Dictionary> buckets = null; - TKey singleBucketKey = default(TKey); - for (int i = 0; i < inputs.Count; i++) + TKey singleBucketKey = keySelector(inputs[0]); + for (int i = 1; i < inputs.Count; i++) { TKey keyValue = keySelector(inputs[i]); - if (i == 0) - { - singleBucketKey = keyValue; - } - else if (buckets is null) + if (buckets is null) { if (!keyComparer.Equals(singleBucketKey, keyValue)) { @@ -109,9 +108,9 @@ public static ReadOnlySingleBucketDictionary> BucketSort>(new KeyValuePair>(singleBucketKey, inputs), keyComparer); + return new ReadOnlySingleBucketGroupBy>(new KeyValuePair>(singleBucketKey, inputs), keyComparer); else - return new ReadOnlySingleBucketDictionary>(buckets, keyComparer); + return new ReadOnlySingleBucketGroupBy>(buckets, keyComparer); } private static Dictionary> CreateBucketDictionaryWithValue(IList inputs, IEqualityComparer keyComparer, int currentIndex, TKey firstBucketKey, TKey nextBucketKey) @@ -140,7 +139,7 @@ private static Dictionary> CreateBucketDictionaryWithValue : IDictionary + struct ReadOnlySingleBucketGroupBy : IEnumerable> { private #if !NETFRAMEWORK @@ -151,24 +150,24 @@ struct ReadOnlySingleBucketDictionary : IDictionary private readonly IEqualityComparer _comparer; public IEqualityComparer Comparer => _comparer; - public ReadOnlySingleBucketDictionary(KeyValuePair singleBucket) + public ReadOnlySingleBucketGroupBy(KeyValuePair singleBucket) : this(singleBucket, EqualityComparer.Default) { } - public ReadOnlySingleBucketDictionary(Dictionary multiBucket) + public ReadOnlySingleBucketGroupBy(Dictionary multiBucket) : this(multiBucket, EqualityComparer.Default) { } - public ReadOnlySingleBucketDictionary(KeyValuePair singleBucket, IEqualityComparer comparer) + public ReadOnlySingleBucketGroupBy(KeyValuePair? singleBucket, IEqualityComparer comparer) { _comparer = comparer; _multiBucket = null; _singleBucket = singleBucket; } - public ReadOnlySingleBucketDictionary(Dictionary multiBucket, IEqualityComparer comparer) + public ReadOnlySingleBucketGroupBy(Dictionary multiBucket, IEqualityComparer comparer) { _comparer = comparer; _multiBucket = multiBucket; @@ -176,58 +175,7 @@ public ReadOnlySingleBucketDictionary(Dictionary multiBucket, IEqu } /// - public int Count { get { if (_multiBucket != null) return _multiBucket.Count; else if (_singleBucket.HasValue) return 1; else return 0; } } - - /// - public ICollection Keys - { - get - { - if (_multiBucket != null) - return _multiBucket.Keys; - else if (_singleBucket.HasValue) - return new[] { _singleBucket.Value.Key }; - else - return (ICollection)System.Linq.Enumerable.Empty(); - } - } - - /// - public ICollection Values - { - get - { - if (_multiBucket != null) - return _multiBucket.Values; - else if (_singleBucket.HasValue) - return new TValue[] { _singleBucket.Value.Value }; - else - return (ICollection)System.Linq.Enumerable.Empty(); - } - } - - /// - public bool IsReadOnly => true; - - /// - /// Allows direct lookup of existing keys. If trying to access non-existing key exception is thrown. - /// Consider to use instead for better safety. - /// - /// Key value for lookup - /// Mapped value found - public TValue this[TKey key] - { - get - { - if (_multiBucket != null) - return _multiBucket[key]; - else if (_singleBucket.HasValue && _comparer.Equals(_singleBucket.Value.Key, key)) - return _singleBucket.Value.Value; - else - throw new KeyNotFoundException(); - } - set => throw new NotSupportedException("Readonly"); - } + public int Count => _multiBucket?.Count ?? (_singleBucket.HasValue ? 1 : 0); /// /// Non-Allocating struct-enumerator @@ -257,7 +205,7 @@ public KeyValuePair Current get { if (_multiBuckets != null) - return new KeyValuePair(_multiBuckets.Current.Key, _multiBuckets.Current.Value); + return _multiBuckets.Current; else return new KeyValuePair(_singleBucket.Key, _singleBucket.Value); } @@ -279,7 +227,6 @@ public bool MoveNext() return false; else return _singleBucketFirstRead = true; - } public void Reset() @@ -312,86 +259,6 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - - /// - public bool ContainsKey(TKey key) - { - if (_multiBucket != null) - return _multiBucket.ContainsKey(key); - else if (_singleBucket.HasValue) - return _comparer.Equals(_singleBucket.Value.Key, key); - else - return false; - } - - /// Will always throw, as dictionary is readonly - public void Add(TKey key, TValue value) - { - throw new NotSupportedException(); // Readonly - } - - /// Will always throw, as dictionary is readonly - public bool Remove(TKey key) - { - throw new NotSupportedException(); // Readonly - } - - /// - public bool TryGetValue(TKey key, out TValue value) - { - if (_multiBucket != null) - { - return _multiBucket.TryGetValue(key, out value); - } - else if (_singleBucket.HasValue && _comparer.Equals(_singleBucket.Value.Key, key)) - { - value = _singleBucket.Value.Value; - return true; - } - else - { - value = default(TValue); - return false; - } - } - - /// Will always throw, as dictionary is readonly - public void Add(KeyValuePair item) - { - throw new NotSupportedException(); // Readonly - } - - /// Will always throw, as dictionary is readonly - public void Clear() - { - throw new NotSupportedException(); // Readonly - } - - /// - public bool Contains(KeyValuePair item) - { - if (_multiBucket != null) - return ((IDictionary)_multiBucket).Contains(item); - else if (_singleBucket.HasValue) - return _comparer.Equals(_singleBucket.Value.Key, item.Key) && EqualityComparer.Default.Equals(_singleBucket.Value.Value, item.Value); - else - return false; - } - - /// - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - if (_multiBucket != null) - ((IDictionary)_multiBucket).CopyTo(array, arrayIndex); - else if (_singleBucket.HasValue) - array[arrayIndex] = _singleBucket.Value; - } - - /// Will always throw, as dictionary is readonly - public bool Remove(KeyValuePair item) - { - throw new NotSupportedException(); // Readonly - } } } } diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index b866e84f9f..a07246be81 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -207,12 +207,13 @@ private void SetNLogElementSettings(ILoggingConfigurationElement nlogConfig) /// private ICollection> CreateUniqueSortedListFromConfig(ILoggingConfigurationElement nlogConfig) { - var dict = ValidatedConfigurationElement.Create(nlogConfig, LogFactory).ValueLookup; + var configurationElement = ValidatedConfigurationElement.Create(nlogConfig, LogFactory); + var dict = configurationElement.Values; if (dict.Count <= 1) return dict; var sortedList = new List>(dict.Count); - var highPriorityList = new[] + var highPriorityList = new HashSet(StringComparer.OrdinalIgnoreCase) { "ThrowExceptions", "ThrowConfigExceptions", @@ -222,15 +223,18 @@ private ICollection> CreateUniqueSortedListFromConf }; foreach (var highPrioritySetting in highPriorityList) { - if (dict.TryGetValue(highPrioritySetting, out var settingValue)) + var settingValue = configurationElement.GetOptionalValue(highPrioritySetting, null); + if (settingValue != null) { sortedList.Add(new KeyValuePair(highPrioritySetting, settingValue)); - dict.Remove(highPrioritySetting); } } - foreach (var configItem in dict) + foreach (var configItem in configurationElement.Values) { - sortedList.Add(configItem); + if (!highPriorityList.Contains(configItem.Key)) + { + sortedList.Add(configItem); + } } return sortedList; } @@ -1063,7 +1067,7 @@ private bool ParseCompoundTarget( private void ConfigureObjectFromAttributes(T targetObject, ValidatedConfigurationElement element, bool ignoreType = true) where T : class { - foreach (var kvp in element.ValueLookup) + foreach (var kvp in element.Values) { string childName = kvp.Key; string childValue = kvp.Value; @@ -1297,7 +1301,7 @@ private bool TryCreateSimpleLayoutInstance(ValidatedConfigurationElement element { if (!element.ValidChildren.Any()) { - var valueLookup = element.ValueLookup; + var valueLookup = element.Values; if (valueLookup.Count == 2) { var simpleLayoutValue = (nameof(SimpleLayout.Text).Equals(valueLookup.First().Key, StringComparison.OrdinalIgnoreCase) ? (valueLookup.First().Value ?? string.Empty) : null) ?? @@ -1538,8 +1542,6 @@ private static string GetName(Target target) /// private sealed class ValidatedConfigurationElement : ILoggingConfigurationElement { - private static readonly IDictionary EmptyDefaultDictionary = new SortHelpers.ReadOnlySingleBucketDictionary(); - private readonly ILoggingConfigurationElement _element; private readonly bool _throwConfigExceptions; private IList _validChildren; @@ -1557,13 +1559,14 @@ public ValidatedConfigurationElement(ILoggingConfigurationElement element, bool { _throwConfigExceptions = throwConfigExceptions; Name = element.Name.Trim(); - ValueLookup = CreateValueLookup(element, throwConfigExceptions); + _valueLookup = CreateValueLookup(element, throwConfigExceptions); _element = element; } public string Name { get; } - public IDictionary ValueLookup { get; } + public ICollection> Values => _valueLookup ?? (ICollection>)ArrayHelper.Empty>(); + private readonly IDictionary _valueLookup; public IEnumerable ValidChildren { @@ -1589,13 +1592,13 @@ IEnumerable YieldAndCacheValidChildren() _validChildren = validChildren ?? ArrayHelper.Empty(); } - public IEnumerable> Values => ValueLookup; - /// /// Explicit cast because NET35 doesn't support covariance. /// IEnumerable ILoggingConfigurationElement.Children => ValidChildren.Cast(); + IEnumerable> ILoggingConfigurationElement.Values => Values; + public string GetRequiredValue(string attributeName, string section) { string value = GetOptionalValue(attributeName, null); @@ -1615,7 +1618,10 @@ public string GetRequiredValue(string attributeName, string section) public string GetOptionalValue(string attributeName, string defaultValue) { - ValueLookup.TryGetValue(attributeName, out string value); + if (_valueLookup is null) + return defaultValue; + + _valueLookup.TryGetValue(attributeName, out string value); return value ?? defaultValue; } @@ -1643,11 +1649,13 @@ private static IDictionary CreateValueLookup(ILoggingConfigurati } } } + if (throwConfigExceptions && warnings?.Count > 0) { throw new NLogConfigurationException(StringHelpers.Join(Environment.NewLine, warnings)); } - return valueLookup ?? EmptyDefaultDictionary; + + return valueLookup; } public override string ToString() diff --git a/src/NLog/Internal/AppEnvironmentWrapper.cs b/src/NLog/Internal/AppEnvironmentWrapper.cs index 748e7e20d0..2519364c7f 100644 --- a/src/NLog/Internal/AppEnvironmentWrapper.cs +++ b/src/NLog/Internal/AppEnvironmentWrapper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal.Fakeables { using System; @@ -43,16 +45,16 @@ internal sealed class AppEnvironmentWrapper : IAppEnvironment { const string LongUNCPrefix = @"\\?\UNC\"; - private const string UnknownProcessName = ""; + private const string UnknownProcessName = "[unknown]"; - private string _entryAssemblyLocation; - private string _entryAssemblyFileName; - private string _currentProcessFilePath; - private string _currentProcessBaseName; - private string _appDomainBaseDirectory; - private string _appDomainConfigFile; - private string _appDomainFriendlyName; - private IEnumerable _appDomainPrivateBinPath; + private string? _entryAssemblyLocation; + private string? _entryAssemblyFileName; + private string? _currentProcessFilePath; + private string? _currentProcessBaseName; + private string? _appDomainBaseDirectory; + private string? _appDomainConfigFile; + private string? _appDomainFriendlyName; + private IEnumerable? _appDomainPrivateBinPath; private int? _appDomainId; private int? _currentProcessId; @@ -234,12 +236,12 @@ private static string LookupEntryAssemblyFriendlyName() { try { - string assemblyName = System.Reflection.Assembly.GetEntryAssembly()?.GetName()?.Name; - return string.IsNullOrEmpty(assemblyName) ? null : assemblyName; + var assemblyName = System.Reflection.Assembly.GetEntryAssembly()?.GetName()?.Name; + return assemblyName ?? UnknownProcessName; } catch { - return null; + return UnknownProcessName; } } @@ -296,7 +298,7 @@ private static string LookupCurrentProcessFilePathWithFallback() } } - private static string LookupCurrentProcessFilePath() + private static string? LookupCurrentProcessFilePath() { try { @@ -369,7 +371,7 @@ private static string LookupCurrentProcessNameWithFallback() } } - private static string LookupCurrentProcessName() + private static string? LookupCurrentProcessName() { try { diff --git a/src/NLog/Internal/AppendBuilderCreator.cs b/src/NLog/Internal/AppendBuilderCreator.cs index ee9324c7fd..99af5f7067 100644 --- a/src/NLog/Internal/AppendBuilderCreator.cs +++ b/src/NLog/Internal/AppendBuilderCreator.cs @@ -31,11 +31,13 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Text; +#nullable enable namespace NLog.Internal { + using System; + using System.Text; + /// /// Allocates new builder and appends to the provided target builder on dispose /// diff --git a/src/NLog/Internal/ArrayHelper.cs b/src/NLog/Internal/ArrayHelper.cs index 26c9d8ca01..f214150411 100644 --- a/src/NLog/Internal/ArrayHelper.cs +++ b/src/NLog/Internal/ArrayHelper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { internal static class ArrayHelper diff --git a/src/NLog/Internal/AssemblyHelpers.cs b/src/NLog/Internal/AssemblyHelpers.cs index ad3e848716..20ff0954a5 100644 --- a/src/NLog/Internal/AssemblyHelpers.cs +++ b/src/NLog/Internal/AssemblyHelpers.cs @@ -30,6 +30,9 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF // THE POSSIBILITY OF SUCH DAMAGE. // + +#nullable enable + namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/AssemblyMetadataAttribute.cs b/src/NLog/Internal/AssemblyMetadataAttribute.cs index 6c1e46c42b..fec40782df 100644 --- a/src/NLog/Internal/AssemblyMetadataAttribute.cs +++ b/src/NLog/Internal/AssemblyMetadataAttribute.cs @@ -32,6 +32,9 @@ // #if NET35 + +#nullable enable + namespace System.Reflection { [AttributeUsage(AttributeTargets.Assembly)] diff --git a/src/NLog/Internal/AsyncOperationCounter.cs b/src/NLog/Internal/AsyncOperationCounter.cs index 10bbb85b9d..12236a0ace 100644 --- a/src/NLog/Internal/AsyncOperationCounter.cs +++ b/src/NLog/Internal/AsyncOperationCounter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -43,7 +45,7 @@ namespace NLog.Internal internal sealed class AsyncOperationCounter { private int _pendingOperationCounter; - private readonly LinkedList _pendingCompletionList = new LinkedList(); + private readonly LinkedList _pendingCompletionList = new LinkedList(); /// /// Mark operation has started @@ -57,12 +59,12 @@ public void BeginOperation() /// Mark operation has completed /// /// Exception coming from the completed operation [optional] - public void CompleteOperation(Exception exception) + public void CompleteOperation(Exception? exception) { NotifyCompletion(exception); } - private int NotifyCompletion(Exception exception) + private int NotifyCompletion(Exception? exception) { int pendingOperations = System.Threading.Interlocked.Decrement(ref _pendingOperationCounter); @@ -75,7 +77,7 @@ private int NotifyCompletion(Exception exception) { var nodeValue = nodeNext.Value; nodeNext = nodeNext.Next; - nodeValue(exception); // Will modify _pendingCompletionList + nodeValue?.Invoke(exception); // Will modify _pendingCompletionList } } } @@ -110,7 +112,7 @@ public AsyncContinuation RegisterCompletionNotification(AsyncContinuation asyncC return asyncContinuation; // No active operations } - var pendingCompletion = new LinkedListNode(null); + var pendingCompletion = new LinkedListNode(null); _pendingCompletionList.AddLast(pendingCompletion); remainingCompletionCounter = System.Threading.Interlocked.Increment(ref _pendingOperationCounter); if (remainingCompletionCounter <= 0) diff --git a/src/NLog/Internal/CallSiteInformation.cs b/src/NLog/Internal/CallSiteInformation.cs index 6a71ca1cca..7ab9e5f294 100644 --- a/src/NLog/Internal/CallSiteInformation.cs +++ b/src/NLog/Internal/CallSiteInformation.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -103,7 +105,7 @@ public static void AddCallSiteHiddenClassType(Type classType) /// The stack trace. /// Index of the first user stack frame within the stack trace. /// Type of the logger or logger wrapper. This is still Logger if it's a subclass of Logger. - public void SetStackTrace(StackTrace stackTrace, int? userStackFrame = null, Type loggerType = null) + public void SetStackTrace(StackTrace stackTrace, int? userStackFrame = null, Type? loggerType = null) { StackTrace = stackTrace; if (!userStackFrame.HasValue && stackTrace != null) @@ -128,7 +130,7 @@ public void SetStackTrace(StackTrace stackTrace, int? userStackFrame = null, Typ /// /// /// - public void SetCallerInfo(string callerClassName, string callerMethodName, string callerFilePath, int callerLineNumber) + public void SetCallerInfo(string? callerClassName, string? callerMethodName, string? callerFilePath, int callerLineNumber) { CallerClassName = callerClassName; CallerMethodName = callerMethodName; @@ -142,7 +144,7 @@ public void SetCallerInfo(string callerClassName, string callerMethodName, strin /// Gets the stack frame of the method that did the logging. /// [Obsolete("Instead use ${callsite} or CallerMemberName. Marked obsolete on NLog 5.3")] - public StackFrame UserStackFrame => StackTrace?.GetFrame(UserStackFrameNumberLegacy ?? UserStackFrameNumber); + public StackFrame? UserStackFrame => StackTrace?.GetFrame(UserStackFrameNumberLegacy ?? UserStackFrameNumber); /// /// Gets the number index of the stack frame that represents the user @@ -158,29 +160,30 @@ public void SetCallerInfo(string callerClassName, string callerMethodName, strin /// /// Gets the entire stack trace. /// - public StackTrace StackTrace { get; private set; } + public StackTrace? StackTrace { get; private set; } - public MethodBase GetCallerStackFrameMethod(int skipFrames) + public MethodBase? GetCallerStackFrameMethod(int skipFrames) { - StackFrame frame = StackTrace?.GetFrame(UserStackFrameNumber + skipFrames); + StackFrame? frame = StackTrace?.GetFrame(UserStackFrameNumber + skipFrames); return StackTraceUsageUtils.GetStackMethod(frame); } - public string GetCallerClassName(MethodBase method, bool includeNameSpace, bool cleanAsyncMoveNext, bool cleanAnonymousDelegates) + public string GetCallerClassName(MethodBase? method, bool includeNameSpace, bool cleanAsyncMoveNext, bool cleanAnonymousDelegates) { - if (!string.IsNullOrEmpty(CallerClassName)) + var callerClassName = CallerClassName ?? string.Empty; + if (!string.IsNullOrEmpty(callerClassName)) { if (includeNameSpace) { - return CallerClassName; + return callerClassName; } else { - int lastDot = CallerClassName.LastIndexOf('.'); - if (lastDot < 0 || lastDot >= CallerClassName.Length - 1) - return CallerClassName; + int lastDot = callerClassName.LastIndexOf('.'); + if (lastDot < 0 || lastDot >= callerClassName.Length - 1) + return callerClassName; else - return CallerClassName.Substring(lastDot + 1); + return callerClassName.Substring(lastDot + 1); } } @@ -193,10 +196,11 @@ public string GetCallerClassName(MethodBase method, bool includeNameSpace, bool return StackTraceUsageUtils.GetStackFrameMethodClassName(method, includeNameSpace, cleanAsyncMoveNext, cleanAnonymousDelegates) ?? string.Empty; } - public string GetCallerMethodName(MethodBase method, bool includeMethodInfo, bool cleanAsyncMoveNext, bool cleanAnonymousDelegates) + public string GetCallerMethodName(MethodBase? method, bool includeMethodInfo, bool cleanAsyncMoveNext, bool cleanAnonymousDelegates) { - if (!string.IsNullOrEmpty(CallerMethodName)) - return CallerMethodName; + var callerMethodName = CallerMethodName ?? string.Empty; + if (!string.IsNullOrEmpty(callerMethodName)) + return callerMethodName; method = method ?? GetCallerStackFrameMethod(0); if (method is null) @@ -209,10 +213,11 @@ public string GetCallerMethodName(MethodBase method, bool includeMethodInfo, boo public string GetCallerFilePath(int skipFrames) { - if (!string.IsNullOrEmpty(CallerFilePath)) - return CallerFilePath; + var callerFilePath = CallerFilePath ?? string.Empty; + if (!string.IsNullOrEmpty(callerFilePath)) + return callerFilePath; - StackFrame frame = StackTrace?.GetFrame(UserStackFrameNumber + skipFrames); + StackFrame? frame = StackTrace?.GetFrame(UserStackFrameNumber + skipFrames); return frame?.GetFileName() ?? string.Empty; } @@ -221,13 +226,13 @@ public int GetCallerLineNumber(int skipFrames) if (CallerLineNumber.HasValue) return CallerLineNumber.Value; - StackFrame frame = StackTrace?.GetFrame(UserStackFrameNumber + skipFrames); + StackFrame? frame = StackTrace?.GetFrame(UserStackFrameNumber + skipFrames); return frame?.GetFileLineNumber() ?? 0; } - public string CallerClassName { get; internal set; } - public string CallerMethodName { get; private set; } - public string CallerFilePath { get; private set; } + public string? CallerClassName { get; internal set; } + public string? CallerMethodName { get; private set; } + public string? CallerFilePath { get; private set; } public int? CallerLineNumber { get; private set; } /// @@ -302,7 +307,7 @@ private static int SkipToUserStackFrameLegacy(StackFrame[] stackFrames, int firs /// /// Skip StackFrame when from hidden Assembly / ClassType /// - private static bool SkipStackFrameWhenHidden(MethodBase stackMethod) + private static bool SkipStackFrameWhenHidden(MethodBase? stackMethod) { var assembly = StackTraceUsageUtils.LookupAssemblyFromMethod(stackMethod); if (assembly is null || IsHiddenAssembly(assembly)) @@ -316,9 +321,9 @@ private static bool SkipStackFrameWhenHidden(MethodBase stackMethod) /// /// Skip StackFrame when type of the logger /// - private static bool SkipStackFrameWhenLoggerType(MethodBase stackMethod, Type loggerType) + private static bool SkipStackFrameWhenLoggerType(MethodBase? stackMethod, Type loggerType) { - Type declaringType = stackMethod?.DeclaringType; + Type? declaringType = stackMethod?.DeclaringType; var isLoggerType = declaringType != null && (loggerType == declaringType || declaringType.IsSubclassOf(loggerType) || loggerType.IsAssignableFrom(declaringType)); return isLoggerType; } diff --git a/src/NLog/Internal/CollectionExtensions.cs b/src/NLog/Internal/CollectionExtensions.cs index c5ea21c2e4..01b32d84b8 100644 --- a/src/NLog/Internal/CollectionExtensions.cs +++ b/src/NLog/Internal/CollectionExtensions.cs @@ -31,12 +31,14 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Collections.Generic; -using JetBrains.Annotations; +#nullable enable namespace NLog.Internal { + using System; + using System.Collections.Generic; + using JetBrains.Annotations; + internal static class CollectionExtensions { /// @@ -47,7 +49,7 @@ internal static class CollectionExtensions public static IList Filter([NotNull] this IList items, TState state, Func filter) { var hasIgnoredLogEvents = false; - IList filterLogEvents = null; + IList? filterLogEvents = null; for (var i = 0; i < items.Count; ++i) { var item = items[i]; diff --git a/src/NLog/Internal/ConfigVariablesDictionary.cs b/src/NLog/Internal/ConfigVariablesDictionary.cs index 01567eca31..841d695578 100644 --- a/src/NLog/Internal/ConfigVariablesDictionary.cs +++ b/src/NLog/Internal/ConfigVariablesDictionary.cs @@ -31,22 +31,24 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using NLog.Config; -using NLog.Layouts; +#nullable enable namespace NLog.Internal { + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics; + using NLog.Config; + using NLog.Layouts; + [DebuggerDisplay("Count = {Count}")] internal sealed class ConfigVariablesDictionary : IDictionary { private readonly ThreadSafeDictionary _variables = new ThreadSafeDictionary(StringComparer.OrdinalIgnoreCase); private readonly LoggingConfiguration _configuration; - private ThreadSafeDictionary _dynamicVariables; - private ThreadSafeDictionary _apiVariables; + private ThreadSafeDictionary? _dynamicVariables; + private ThreadSafeDictionary? _apiVariables; public ConfigVariablesDictionary(LoggingConfiguration configuration) { @@ -84,9 +86,8 @@ public bool TryLookupDynamicVariable(string key, out Layout dynamicLayout) if (dynamicLayout != null) { dynamicLayout.Initialize(_configuration); + _dynamicVariables[key] = dynamicLayout; } - - _dynamicVariables[key] = dynamicLayout; } } diff --git a/src/NLog/Internal/ConfigurationManager.cs b/src/NLog/Internal/ConfigurationManager.cs index 31447a29ab..d5fffaf813 100644 --- a/src/NLog/Internal/ConfigurationManager.cs +++ b/src/NLog/Internal/ConfigurationManager.cs @@ -33,6 +33,8 @@ #if NETFRAMEWORK +#nullable enable + namespace NLog.Internal { using System.Collections.Specialized; diff --git a/src/NLog/Internal/DictionaryEntryEnumerable.cs b/src/NLog/Internal/DictionaryEntryEnumerable.cs index 76581b926d..9182d4dd1f 100644 --- a/src/NLog/Internal/DictionaryEntryEnumerable.cs +++ b/src/NLog/Internal/DictionaryEntryEnumerable.cs @@ -31,12 +31,14 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Collections; -using System.Collections.Generic; +#nullable enable namespace NLog.Internal { + using System; + using System.Collections; + using System.Collections.Generic; + /// /// Ensures that IDictionary.GetEnumerator returns DictionaryEntry values /// @@ -46,16 +48,16 @@ namespace NLog.Internal #endif struct DictionaryEntryEnumerable : IEnumerable { - private readonly IDictionary _dictionary; + private readonly IDictionary? _dictionary; - public DictionaryEntryEnumerable(IDictionary dictionary) + public DictionaryEntryEnumerable(IDictionary? dictionary) { _dictionary = dictionary; } public DictionaryEntryEnumerator GetEnumerator() { - return new DictionaryEntryEnumerator(_dictionary?.Count > 0 ? _dictionary : null); + return new DictionaryEntryEnumerator(_dictionary); } IEnumerator IEnumerable.GetEnumerator() @@ -78,9 +80,9 @@ struct DictionaryEntryEnumerator : IEnumerator public DictionaryEntry Current => _entryEnumerator.Entry; - public DictionaryEntryEnumerator(IDictionary dictionary) + public DictionaryEntryEnumerator(IDictionary? dictionary) { - _entryEnumerator = dictionary?.GetEnumerator(); + _entryEnumerator = dictionary?.Count > 0 ? dictionary.GetEnumerator() : EmptyDictionaryEnumerator.Default; } object IEnumerator.Current => Current; @@ -93,12 +95,26 @@ public void Dispose() public bool MoveNext() { - return _entryEnumerator?.MoveNext() ?? false; + return _entryEnumerator.MoveNext(); } public void Reset() { - _entryEnumerator?.Reset(); + _entryEnumerator.Reset(); + } + } + + private sealed class EmptyDictionaryEnumerator : IDictionaryEnumerator + { + public static readonly IDictionaryEnumerator Default = new EmptyDictionaryEnumerator(); + public DictionaryEntry Entry => default; + public object? Key => default; + public object? Value => default; + object? IEnumerator.Current => Entry; + public bool MoveNext() => false; + public void Reset() + { + // SONAR: Nothing to reset } } } diff --git a/src/NLog/Internal/DynamicallyAccessedMemberTypes.cs b/src/NLog/Internal/DynamicallyAccessedMemberTypes.cs index 8b1287eb60..7f5b0210ec 100644 --- a/src/NLog/Internal/DynamicallyAccessedMemberTypes.cs +++ b/src/NLog/Internal/DynamicallyAccessedMemberTypes.cs @@ -33,6 +33,8 @@ #if !NET5_0_OR_GREATER +#nullable enable + namespace System.Diagnostics.CodeAnalysis { [AttributeUsage( @@ -198,7 +200,7 @@ public UnconditionalSuppressMessageAttribute(string category, string checkId) /// The Scope property is an optional argument that specifies the metadata scope for which /// the attribute is relevant. /// - public string Scope { get; set; } + public string? Scope { get; set; } /// /// Gets or sets a fully qualified path that represents the target of the attribute. @@ -209,7 +211,7 @@ public UnconditionalSuppressMessageAttribute(string category, string checkId) /// Because it is fully qualified, it can be long, particularly for targets such as parameters. /// The analysis tool user interface should be capable of automatically formatting the parameter. /// - public string Target { get; set; } + public string? Target { get; set; } /// /// Gets or sets an optional argument expanding on exclusion criteria. @@ -221,12 +223,12 @@ public UnconditionalSuppressMessageAttribute(string category, string checkId) /// and it may be desirable to suppress a violation against a statement in the method that will /// give a rule violation, but not against all statements in the method. /// - public string MessageId { get; set; } + public string? MessageId { get; set; } /// /// Gets or sets the justification for suppressing the code analysis message. /// - public string Justification { get; set; } + public string? Justification { get; set; } } } diff --git a/src/NLog/Internal/EnvironmentHelper.cs b/src/NLog/Internal/EnvironmentHelper.cs index 373a62ef2a..0de106123b 100644 --- a/src/NLog/Internal/EnvironmentHelper.cs +++ b/src/NLog/Internal/EnvironmentHelper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -65,18 +67,11 @@ internal static string GetSafeEnvironmentVariable(string name) { try { - string s = Environment.GetEnvironmentVariable(name); - - if (string.IsNullOrEmpty(s)) - { - return null; - } - - return s; + return Environment.GetEnvironmentVariable(name) ?? string.Empty; } catch (SecurityException) { - return null; + return string.Empty; } } } diff --git a/src/NLog/Internal/ExceptionHelper.cs b/src/NLog/Internal/ExceptionHelper.cs index e3771bf3a0..c8df26f564 100644 --- a/src/NLog/Internal/ExceptionHelper.cs +++ b/src/NLog/Internal/ExceptionHelper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -80,7 +82,7 @@ public static bool IsLoggedToInternalLogger(this Exception exception) /// Target Object context of the exception. /// Target Method context of the exception. /// trueif the must be rethrown, false otherwise. - public static bool MustBeRethrown(this Exception exception, IInternalLoggerContext loggerContext = null, string callerMemberName = null) + public static bool MustBeRethrown(this Exception exception, IInternalLoggerContext? loggerContext = null, string? callerMemberName = null) { if (exception.MustBeRethrownImmediately()) { diff --git a/src/NLog/Internal/ExceptionMessageFormatProvider.cs b/src/NLog/Internal/ExceptionMessageFormatProvider.cs index 3bddd69dcd..0b7a6d02eb 100644 --- a/src/NLog/Internal/ExceptionMessageFormatProvider.cs +++ b/src/NLog/Internal/ExceptionMessageFormatProvider.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -77,7 +79,7 @@ private static Exception GetPrimaryException(Exception exception) return exception; } - object IFormatProvider.GetFormat(Type formatType) + object? IFormatProvider.GetFormat(Type formatType) { return (formatType == typeof(ICustomFormatter)) ? this : null; } diff --git a/src/NLog/Internal/FileInfoHelper.cs b/src/NLog/Internal/FileInfoHelper.cs index 9cdd47636c..6b9cecccec 100644 --- a/src/NLog/Internal/FileInfoHelper.cs +++ b/src/NLog/Internal/FileInfoHelper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -43,7 +45,7 @@ internal static class FileInfoHelper return LookupValidFileCreationTimeUtc(fileInfo, (f) => f.CreationTimeUtc, (f) => f.LastWriteTimeUtc); } - private static DateTime? LookupValidFileCreationTimeUtc(T fileInfo, Func primary, Func fallback, Func finalFallback = null) + private static DateTime? LookupValidFileCreationTimeUtc(T fileInfo, Func primary, Func fallback, Func? finalFallback = null) { DateTime? fileCreationTime = primary(fileInfo); @@ -61,9 +63,7 @@ internal static class FileInfoHelper public static bool IsRelativeFilePath(string filepath) { - if (filepath?.Length > 0) - filepath = filepath.TrimStart(ArrayHelper.Empty()); - + filepath = filepath?.TrimStart(ArrayHelper.Empty()) ?? string.Empty; if (string.IsNullOrEmpty(filepath)) return false; diff --git a/src/NLog/Internal/FormatHelper.cs b/src/NLog/Internal/FormatHelper.cs index 5c71ecb343..d28aed87df 100644 --- a/src/NLog/Internal/FormatHelper.cs +++ b/src/NLog/Internal/FormatHelper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -46,7 +48,7 @@ internal static class FormatHelper /// /// If is null and isn't a already, then the will get a locked by /// - internal static string ConvertToString(object value, IFormatProvider formatProvider) + internal static string ConvertToString(object? value, IFormatProvider? formatProvider) { if (value is string stringValue) return stringValue; @@ -65,7 +67,7 @@ internal static string ConvertToString(object value, IFormatProvider formatProvi return Convert.ToString(value, formatProvider); } - internal static string TryFormatToString(object value, string format, IFormatProvider formatProvider) + internal static string TryFormatToString(object? value, string? format, IFormatProvider? formatProvider) { if (value is IFormattable formattable) { @@ -77,11 +79,11 @@ internal static string TryFormatToString(object value, string format, IFormatPro } else if (value is System.Collections.IEnumerable) { - return null; + return string.Empty; } else { - return value?.ToString(); + return value?.ToString() ?? string.Empty; } } } diff --git a/src/NLog/Internal/Guard.cs b/src/NLog/Internal/Guard.cs index 1462834295..3a85b573de 100644 --- a/src/NLog/Internal/Guard.cs +++ b/src/NLog/Internal/Guard.cs @@ -31,7 +31,10 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + #if !NETCOREAPP3_1_OR_GREATER + namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Parameter)] diff --git a/src/NLog/Internal/IAppEnvironment.cs b/src/NLog/Internal/IAppEnvironment.cs index ff2d09191e..d8b40624a4 100644 --- a/src/NLog/Internal/IAppEnvironment.cs +++ b/src/NLog/Internal/IAppEnvironment.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal.Fakeables { using System; diff --git a/src/NLog/Internal/IConfigurationManager.cs b/src/NLog/Internal/IConfigurationManager.cs index 8415fd06b6..938bfacb13 100644 --- a/src/NLog/Internal/IConfigurationManager.cs +++ b/src/NLog/Internal/IConfigurationManager.cs @@ -33,6 +33,8 @@ #if NETFRAMEWORK +#nullable enable + namespace NLog.Internal { using System.Collections.Specialized; diff --git a/src/NLog/Internal/IFileSystem.cs b/src/NLog/Internal/IFileSystem.cs index 0f04118cce..44da88ec58 100644 --- a/src/NLog/Internal/IFileSystem.cs +++ b/src/NLog/Internal/IFileSystem.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal.Fakeables { using System.IO; diff --git a/src/NLog/Internal/ILogMessageFormatter.cs b/src/NLog/Internal/ILogMessageFormatter.cs index 53981628f4..09666e360e 100644 --- a/src/NLog/Internal/ILogMessageFormatter.cs +++ b/src/NLog/Internal/ILogMessageFormatter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System.Text; diff --git a/src/NLog/Internal/IRawValue.cs b/src/NLog/Internal/IRawValue.cs index caeeeb22b6..63cd9a95a8 100644 --- a/src/NLog/Internal/IRawValue.cs +++ b/src/NLog/Internal/IRawValue.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { /// @@ -47,6 +49,6 @@ internal interface IRawValue /// /// The value /// RawValue supported? - bool TryGetRawValue(LogEventInfo logEvent, out object value); + bool TryGetRawValue(LogEventInfo logEvent, out object? value); } } diff --git a/src/NLog/Internal/IRenderable.cs b/src/NLog/Internal/IRenderable.cs index cfba216bb8..241b81443c 100644 --- a/src/NLog/Internal/IRenderable.cs +++ b/src/NLog/Internal/IRenderable.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { /// diff --git a/src/NLog/Internal/IStringValueRenderer.cs b/src/NLog/Internal/IStringValueRenderer.cs index 61e975f710..33bbf5cc94 100644 --- a/src/NLog/Internal/IStringValueRenderer.cs +++ b/src/NLog/Internal/IStringValueRenderer.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { /// @@ -46,6 +48,6 @@ interface IStringValueRenderer /// /// /// null if not possible or unknown - string GetFormattedString(LogEventInfo logEvent); + string? GetFormattedString(LogEventInfo logEvent); } } diff --git a/src/NLog/Internal/ISupportsInitialize.cs b/src/NLog/Internal/ISupportsInitialize.cs index 581a21b947..a7d3eb53f8 100644 --- a/src/NLog/Internal/ISupportsInitialize.cs +++ b/src/NLog/Internal/ISupportsInitialize.cs @@ -31,9 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { - using Config; + using NLog.Config; /// /// Supports object initialization and termination. diff --git a/src/NLog/Internal/ITargetWithFilterChain.cs b/src/NLog/Internal/ITargetWithFilterChain.cs index 2b7714ba8f..5b687f5be0 100644 --- a/src/NLog/Internal/ITargetWithFilterChain.cs +++ b/src/NLog/Internal/ITargetWithFilterChain.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/LogMessageStringFormatter.cs b/src/NLog/Internal/LogMessageStringFormatter.cs index e3246a8092..92af25fb14 100644 --- a/src/NLog/Internal/LogMessageStringFormatter.cs +++ b/src/NLog/Internal/LogMessageStringFormatter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System.Globalization; diff --git a/src/NLog/Internal/LogMessageTemplateFormatter.cs b/src/NLog/Internal/LogMessageTemplateFormatter.cs index 5a10d37dde..f5537e066b 100644 --- a/src/NLog/Internal/LogMessageTemplateFormatter.cs +++ b/src/NLog/Internal/LogMessageTemplateFormatter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -47,7 +49,7 @@ internal sealed class LogMessageTemplateFormatter : ILogMessageFormatter private readonly LogFactory _logFactory; private IValueFormatter ValueFormatter => _valueFormatter ?? (_valueFormatter = _logFactory.ServiceRepository.GetService()); - private IValueFormatter _valueFormatter; + private IValueFormatter? _valueFormatter; /// /// When true: Do not fallback to StringBuilder.Format for positional templates @@ -98,7 +100,7 @@ public bool HasProperties(LogEventInfo logEvent) public void AppendFormattedMessage(LogEventInfo logEvent, StringBuilder builder) { - if (_singleTargetOnly) + if (_singleTargetOnly && logEvent.Parameters?.Length > 0) { Render(logEvent.Message, logEvent.FormatProvider ?? _logFactory.DefaultCultureInfo ?? CultureInfo.CurrentCulture, logEvent.Parameters, builder, out _); } @@ -110,11 +112,12 @@ public void AppendFormattedMessage(LogEventInfo logEvent, StringBuilder builder) public string FormatMessage(LogEventInfo logEvent) { - if (LogMessageStringFormatter.HasParameters(logEvent)) + var parameters = logEvent.Parameters; + if (parameters?.Length > 0 && !string.IsNullOrEmpty(logEvent.Message)) { using (var builder = _builderPool.Acquire()) { - AppendToBuilder(logEvent, builder.Item); + AppendToBuilder(logEvent, parameters, builder.Item); return builder.Item.ToString(); } } @@ -122,9 +125,9 @@ public string FormatMessage(LogEventInfo logEvent) return logEvent.Message; } - private void AppendToBuilder(LogEventInfo logEvent, StringBuilder builder) + private void AppendToBuilder(LogEventInfo logEvent, object?[] parameters, StringBuilder builder) { - Render(logEvent.Message, logEvent.FormatProvider ?? _logFactory.DefaultCultureInfo ?? CultureInfo.CurrentCulture, logEvent.Parameters, builder, out var messageTemplateParameterList); + Render(logEvent.Message, logEvent.FormatProvider ?? _logFactory.DefaultCultureInfo ?? CultureInfo.CurrentCulture, parameters, builder, out var messageTemplateParameterList); logEvent.TryCreatePropertiesInternal(messageTemplateParameterList ?? ArrayHelper.Empty()); } @@ -136,7 +139,7 @@ private void AppendToBuilder(LogEventInfo logEvent, StringBuilder builder) /// Parameters for the holes. /// The String Builder destination. /// Parameters for the holes. - private void Render(string template, IFormatProvider formatProvider, object[] parameters, StringBuilder sb, out IList messageTemplateParameters) + private void Render(string template, IFormatProvider? formatProvider, object?[] parameters, StringBuilder sb, out IList? messageTemplateParameters) { messageTemplateParameters = null; @@ -201,7 +204,7 @@ private void Render(string template, IFormatProvider formatProvider, object[] pa } #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER - internal string Render(ref TemplateEnumerator templateEnumerator, IFormatProvider formatProvider, in ReadOnlySpan parameters, out IList messageTemplateParameters) + internal string Render(ref TemplateEnumerator templateEnumerator, IFormatProvider? formatProvider, in ReadOnlySpan parameters, out IList? messageTemplateParameters) { // Handle message-template-format or string-format or mixed-format messageTemplateParameters = null; @@ -265,7 +268,7 @@ internal string Render(ref TemplateEnumerator templateEnumerator, IFormatProvide } #endif - private static IList VerifyMessageTemplateParameters(IList messageTemplateParameters, int holeIndex) + private static IList? VerifyMessageTemplateParameters(IList? messageTemplateParameters, int holeIndex) { if (messageTemplateParameters != null && holeIndex != messageTemplateParameters.Count) { @@ -278,7 +281,7 @@ private static IList VerifyMessageTemplateParameters(I return messageTemplateParameters; } - private void RenderHolePositional(StringBuilder sb, in Hole hole, IFormatProvider formatProvider, object value) + private void RenderHolePositional(StringBuilder sb, in Hole hole, IFormatProvider? formatProvider, object? value) { if (value is null) { @@ -294,12 +297,12 @@ private void RenderHolePositional(StringBuilder sb, in Hole hole, IFormatProvide } } - private void RenderHole(StringBuilder sb, in Hole hole, IFormatProvider formatProvider, object value) + private void RenderHole(StringBuilder sb, in Hole hole, IFormatProvider? formatProvider, object? value) { RenderHole(sb, hole.CaptureType, hole.Format, formatProvider, value); } - private void RenderHole(StringBuilder sb, CaptureType captureType, string holeFormat, IFormatProvider formatProvider, object value) + private void RenderHole(StringBuilder sb, CaptureType captureType, string? holeFormat, IFormatProvider? formatProvider, object? value) { if (value is null) { diff --git a/src/NLog/Internal/MruCache.cs b/src/NLog/Internal/MruCache.cs index 2a5a13c458..33e6f06acc 100644 --- a/src/NLog/Internal/MruCache.cs +++ b/src/NLog/Internal/MruCache.cs @@ -31,10 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System.Collections.Generic; +#nullable enable namespace NLog.Internal { + using System.Collections.Generic; + /// /// Most-Recently-Used-Cache, that discards less frequently used items on overflow /// @@ -152,7 +154,7 @@ private void PruneCache() /// Key of the item to be searched in the cache. /// Output value of the item found in the cache. /// True when the key is found in the cache, false otherwise. - public bool TryGetValue(TKey key, out TValue value) + public bool TryGetValue(TKey key, out TValue? value) { MruCacheItem item; try diff --git a/src/NLog/Internal/MultiFileWatcher.cs b/src/NLog/Internal/MultiFileWatcher.cs index a9c573814d..fc77e8ff14 100644 --- a/src/NLog/Internal/MultiFileWatcher.cs +++ b/src/NLog/Internal/MultiFileWatcher.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -54,7 +56,7 @@ internal sealed class MultiFileWatcher : IDisposable /// /// Occurs when a change is detected in one of the monitored files. /// - public event FileSystemEventHandler FileChanged; + public event FileSystemEventHandler? FileChanged; public MultiFileWatcher() : this(NotifyFilters.LastWrite | NotifyFilters.CreationTime | NotifyFilters.Size | NotifyFilters.Security | NotifyFilters.Attributes) @@ -139,7 +141,7 @@ private bool TryAddWatch(string fileName, string directory, string fileFilter) if (_watcherMap.ContainsKey(fileName)) return false; - FileSystemWatcher watcher = null; + FileSystemWatcher? watcher = null; try { diff --git a/src/NLog/Internal/NativeMethods.cs b/src/NLog/Internal/NativeMethods.cs index f9c185653b..272e4598f0 100644 --- a/src/NLog/Internal/NativeMethods.cs +++ b/src/NLog/Internal/NativeMethods.cs @@ -33,6 +33,8 @@ #if NETFRAMEWORK +#nullable enable + namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/ObjectGraphScanner.cs b/src/NLog/Internal/ObjectGraphScanner.cs index 531e3f7559..7b3e6d998e 100644 --- a/src/NLog/Internal/ObjectGraphScanner.cs +++ b/src/NLog/Internal/ObjectGraphScanner.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -68,7 +70,7 @@ public static List FindReachableObjects(ConfigurationItemFactory configFac var visitedObjects = new HashSet(SingleItemOptimizedHashSet.ReferenceEqualityComparer.Default); foreach (var rootObject in rootObjects) { - if (PropertyHelper.IsConfigurationItemType(configFactory, rootObject?.GetType())) + if (PropertyHelper.IsConfigurationItemType(configFactory, rootObject.GetType())) { ScanProperties(configFactory, aggressiveSearch, rootObject, result, 0, visitedObjects); } @@ -76,7 +78,7 @@ public static List FindReachableObjects(ConfigurationItemFactory configFac return result; } - private static void ScanProperties(ConfigurationItemFactory configFactory, bool aggressiveSearch, object targetObject, List result, int level, HashSet visitedObjects) + private static void ScanProperties(ConfigurationItemFactory configFactory, bool aggressiveSearch, object? targetObject, List result, int level, HashSet visitedObjects) where T : class { if (targetObject is null) @@ -113,7 +115,7 @@ private static void ScanProperties(ConfigurationItemFactory configFactory, bo if (!PropertyHelper.IsConfigurationItemType(configFactory, propInfo.PropertyType)) continue; - object propValue = ScanPropertyValue(targetObject, type, propInfo); + var propValue = ScanPropertyValue(targetObject, type, propInfo); if (propValue is null) continue; @@ -122,7 +124,7 @@ private static void ScanProperties(ConfigurationItemFactory configFactory, bo } } - private static object ScanPropertyValue(object targetObject, Type type, PropertyInfo propInfo) + private static object? ScanPropertyValue(object targetObject, Type type, PropertyInfo propInfo) { try { diff --git a/src/NLog/Internal/ObjectHandleSerializer.cs b/src/NLog/Internal/ObjectHandleSerializer.cs index b58a2c41aa..835332bfed 100644 --- a/src/NLog/Internal/ObjectHandleSerializer.cs +++ b/src/NLog/Internal/ObjectHandleSerializer.cs @@ -33,6 +33,8 @@ #if NET35 || NET40 || NET45 +#nullable enable + namespace NLog.Internal { using System; @@ -43,7 +45,7 @@ namespace NLog.Internal internal class ObjectHandleSerializer : ISerializable { [NonSerialized] - private readonly object _wrapped; + private readonly object? _wrapped; public ObjectHandleSerializer() { @@ -56,7 +58,7 @@ public ObjectHandleSerializer(object wrapped) protected ObjectHandleSerializer(SerializationInfo info, StreamingContext context) { - Type type = null; + Type? type = null; try { type = (Type)info.GetValue("wrappedtype", typeof(Type)); @@ -85,11 +87,11 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) string serializedString = string.Empty; try { - serializedString = _wrapped?.ToString(); + serializedString = _wrapped?.ToString() ?? string.Empty; } finally { - info.AddValue("wrappedvalue", serializedString ?? string.Empty); + info.AddValue("wrappedvalue", serializedString); } } } diff --git a/src/NLog/Internal/ObjectPropertyPath.cs b/src/NLog/Internal/ObjectPropertyPath.cs index 54557b7183..76188f29c2 100644 --- a/src/NLog/Internal/ObjectPropertyPath.cs +++ b/src/NLog/Internal/ObjectPropertyPath.cs @@ -31,19 +31,21 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { internal struct ObjectPropertyPath { - public string[] PathNames { get; private set; } + public string[]? PathNames { get; private set; } /// /// Object Path to check /// - public string Value + public string? Value { get => PathNames?.Length > 0 ? string.Join(".", PathNames) : null; - set => PathNames = StringHelpers.IsNullOrWhiteSpace(value) ? null : value.SplitAndTrimTokens('.'); + set => PathNames = StringHelpers.IsNullOrWhiteSpace(value) ? null : value?.SplitAndTrimTokens('.'); } } } diff --git a/src/NLog/Internal/ObjectReflectionCache.cs b/src/NLog/Internal/ObjectReflectionCache.cs index c3ae725b28..fe704b214c 100644 --- a/src/NLog/Internal/ObjectReflectionCache.cs +++ b/src/NLog/Internal/ObjectReflectionCache.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -51,17 +53,17 @@ namespace NLog.Internal internal sealed class ObjectReflectionCache : IObjectTypeTransformer { private MruCache ObjectTypeCache => _objectTypeCache ?? System.Threading.Interlocked.CompareExchange(ref _objectTypeCache, new MruCache(10000), null) ?? _objectTypeCache; - private MruCache _objectTypeCache; + private MruCache? _objectTypeCache; private readonly IServiceProvider _serviceProvider; private IObjectTypeTransformer ObjectTypeTransformation => _objectTypeTransformation ?? (_objectTypeTransformation = _serviceProvider?.GetService() ?? this); - private IObjectTypeTransformer _objectTypeTransformation; + private IObjectTypeTransformer? _objectTypeTransformation; public ObjectReflectionCache(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } - object IObjectTypeTransformer.TryTransformObject(object obj) + object? IObjectTypeTransformer.TryTransformObject(object? obj) { return null; } @@ -101,7 +103,7 @@ public ObjectPropertyList LookupObjectProperties(object value) /// /// Try get value from , using , and set into /// - public bool TryGetObjectProperty(object value, string[] objectPath, out object foundValue) + public bool TryGetObjectProperty(object? value, string[]? objectPath, out object? foundValue) { foundValue = null; @@ -166,7 +168,7 @@ public bool TryLookupExpandoObject(object value, out ObjectPropertyList objectPr var dictionaryEnumerator = TryGetDictionaryEnumerator(value); if (dictionaryEnumerator != null) { - propertyInfos = new ObjectPropertyInfos(null, new[] { new FastPropertyLookup(string.Empty, TypeCode.Object, (o, p) => dictionaryEnumerator.GetEnumerator(o)) }); + propertyInfos = new ObjectPropertyInfos(ArrayHelper.Empty(), new[] { new FastPropertyLookup(string.Empty, TypeCode.Object, (o, p) => dictionaryEnumerator.GetEnumerator(o)) }); ObjectTypeCache.TryAddValue(objectType, propertyInfos); objectPropertyList = new ObjectPropertyList(value, propertyInfos.Properties, propertyInfos.FastLookup); return true; @@ -224,7 +226,7 @@ private static bool ConvertSimpleToString(Type objectType) private static PropertyInfo[] GetPublicProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { - PropertyInfo[] properties = null; + PropertyInfo[]? properties = null; try { @@ -281,7 +283,7 @@ struct ObjectPropertyList : IEnumerable private static readonly FastPropertyLookup[] CreateIDictionaryEnumerator = new[] { new FastPropertyLookup(string.Empty, TypeCode.Object, (o, p) => ((IDictionary)o).GetEnumerator()) }; private readonly object _object; private readonly PropertyInfo[] _properties; - private readonly FastPropertyLookup[] _fastLookup; + private readonly FastPropertyLookup[]? _fastLookup; public #if !NETFRAMEWORK @@ -290,7 +292,7 @@ struct ObjectPropertyList : IEnumerable struct PropertyValue { public readonly string Name; - public readonly object Value; + public readonly object? Value; public TypeCode TypeCode => Value is null ? TypeCode.Empty : _typecode; private readonly TypeCode _typecode; public bool HasNameAndValue => Name != null && Value != null; @@ -321,7 +323,7 @@ public PropertyValue(object owner, FastPropertyLookup fastProperty) _typecode = fastProperty.TypeCode; try { - Value = fastProperty.ValueLookup(owner, null); + Value = fastProperty.ValueLookup(owner, ArrayHelper.Empty()); } catch { @@ -330,11 +332,11 @@ public PropertyValue(object owner, FastPropertyLookup fastProperty) } } - public bool IsSimpleValue => _properties?.Length == 0; + public bool IsSimpleValue => _properties.Length == 0 && (_fastLookup is null || _fastLookup.Length == 0); public object ObjectValue => _object; - internal ObjectPropertyList(object value, PropertyInfo[] properties, FastPropertyLookup[] fastLookup) + internal ObjectPropertyList(object value, PropertyInfo[] properties, FastPropertyLookup[]? fastLookup) { _object = value; _properties = properties; @@ -344,55 +346,45 @@ internal ObjectPropertyList(object value, PropertyInfo[] properties, FastPropert public ObjectPropertyList(IDictionary value) { _object = value; // Expando objects - _properties = null; + _properties = ArrayHelper.Empty(); _fastLookup = CreateIDictionaryEnumerator; } public bool TryGetPropertyValue(string name, out PropertyValue propertyValue) { - if (_properties != null) + if (_properties.Length == 0) { - if (_fastLookup != null) - { - return TryFastLookupPropertyValue(name, out propertyValue); - } - else + if (_object is IDictionary expandoObject) { - return TrySlowLookupPropertyValue(name, out propertyValue); + if (expandoObject.TryGetValue(name, out var objectValue)) + { + propertyValue = new PropertyValue(name, objectValue, TypeCode.Object); + return true; + } + propertyValue = default(PropertyValue); + return false; } + + return TryListLookupPropertyValue(name, out propertyValue); } - else if (_object is IDictionary expandoObject) + + if (_fastLookup != null) { - if (expandoObject.TryGetValue(name, out var objectValue)) + // Scans properties for name (Skips string-compare and value-lookup until finding match) + int nameHashCode = NameComparer.GetHashCode(name); + foreach (var fastProperty in _fastLookup) { - propertyValue = new PropertyValue(name, objectValue, TypeCode.Object); - return true; + if (fastProperty.NameHashCode == nameHashCode && NameComparer.Equals(fastProperty.Name, name)) + { + propertyValue = new PropertyValue(_object, fastProperty); + return true; + } } propertyValue = default(PropertyValue); return false; } - else - { - return TryListLookupPropertyValue(name, out propertyValue); - } - } - /// - /// Scans properties for name (Skips string-compare and value-lookup until finding match) - /// - private bool TryFastLookupPropertyValue(string name, out PropertyValue propertyValue) - { - int nameHashCode = NameComparer.GetHashCode(name); - foreach (var fastProperty in _fastLookup) - { - if (fastProperty.NameHashCode == nameHashCode && NameComparer.Equals(fastProperty.Name, name)) - { - propertyValue = new PropertyValue(_object, fastProperty); - return true; - } - } - propertyValue = default(PropertyValue); - return false; + return TrySlowLookupPropertyValue(name, out propertyValue); } /// @@ -436,10 +428,11 @@ public override string ToString() public Enumerator GetEnumerator() { - if (_properties != null) + if (_properties.Length > 0 || _fastLookup is null || _fastLookup.Length == 0) return new Enumerator(_object, _properties, _fastLookup); - else - return new Enumerator((IEnumerator>)_fastLookup[0].ValueLookup(_object, null)); + + var expandoObject = _fastLookup[0].ValueLookup(_object, ArrayHelper.Empty()); + return expandoObject is null ? default(Enumerator) : new Enumerator((IEnumerator>)expandoObject); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -448,12 +441,12 @@ public Enumerator GetEnumerator() public struct Enumerator : IEnumerator { private readonly object _owner; - private readonly PropertyInfo[] _properties; - private readonly FastPropertyLookup[] _fastLookup; - private readonly IEnumerator> _enumerator; + private readonly PropertyInfo[]? _properties; + private readonly FastPropertyLookup[]? _fastLookup; + private readonly IEnumerator>? _enumerator; private int _index; - internal Enumerator(object owner, PropertyInfo[] properties, FastPropertyLookup[] fastLookup) + internal Enumerator(object owner, PropertyInfo[] properties, FastPropertyLookup[]? fastLookup) { _owner = owner; _properties = properties; @@ -481,8 +474,10 @@ public PropertyValue Current return new PropertyValue(_owner, _fastLookup[_index]); else if (_properties != null) return new PropertyValue(_owner, _properties[_index]); - else + else if (_enumerator != null) return new PropertyValue(_enumerator.Current.Key, _enumerator.Current.Value, TypeCode.Object); + else + return default(PropertyValue); } catch (Exception ex) { @@ -504,7 +499,7 @@ public bool MoveNext() if (_properties != null) return ++_index < (_fastLookup?.Length ?? _properties.Length); else - return _enumerator.MoveNext(); + return _enumerator?.MoveNext() == true; } public void Reset() @@ -512,7 +507,7 @@ public void Reset() if (_properties != null) _index = -1; else - _enumerator.Reset(); + _enumerator?.Reset(); } } } @@ -540,11 +535,11 @@ public FastPropertyLookup(string name, TypeCode typeCode, ReflectionHelpers.Late private struct ObjectPropertyInfos : IEquatable { public readonly PropertyInfo[] Properties; - public readonly FastPropertyLookup[] FastLookup; + public readonly FastPropertyLookup[]? FastLookup; public static readonly ObjectPropertyInfos SimpleToString = new ObjectPropertyInfos(ArrayHelper.Empty(), ArrayHelper.Empty()); - public ObjectPropertyInfos(PropertyInfo[] properties, FastPropertyLookup[] fastLookup) + public ObjectPropertyInfos(PropertyInfo[] properties, FastPropertyLookup[]? fastLookup) { Properties = properties; FastLookup = fastLookup; @@ -590,7 +585,8 @@ public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, Dy } } #endif - private static IDictionaryEnumerator TryGetDictionaryEnumerator(object value) + + private static IDictionaryEnumerator? TryGetDictionaryEnumerator(object value) { if (!(value is IEnumerable) || value is string) return null; @@ -618,7 +614,7 @@ private static IDictionaryEnumerator TryGetDictionaryEnumerator(object value) } [UnconditionalSuppressMessage("Trimming - Allow reflection of message args", "IL2075")] - private static IDictionaryEnumerator TryBuildDictionaryEnumerator(object value) + private static IDictionaryEnumerator? TryBuildDictionaryEnumerator(object value) { foreach (var interfaceType in value.GetType().GetInterfaces()) { @@ -644,14 +640,14 @@ private static IDictionaryEnumerator TryBuildDictionaryEnumerator(object value) private interface IDictionaryEnumerator { - IEnumerator> GetEnumerator(object value); + IEnumerator> GetEnumerator(object value); } private sealed class DictionaryEnumerator : IDictionaryEnumerator { - private Func>> _enumerateCollection; + private Func>>? _enumerateCollection; - IEnumerator> IDictionaryEnumerator.GetEnumerator(object value) + IEnumerator> IDictionaryEnumerator.GetEnumerator(object value) { if (value is IDictionary dictionary) { @@ -665,13 +661,13 @@ IEnumerator> IDictionaryEnumerator.GetEnumerator(ob return EmptyDictionaryEnumerator.Default; } - private static IEnumerator> YieldEnumerator(IDictionary dictionary) + private static IEnumerator> YieldEnumerator(IDictionary dictionary) { foreach (var item in new DictionaryEntryEnumerable(dictionary)) - yield return new KeyValuePair(item.Key.ToString(), item.Value); + yield return new KeyValuePair(item.Key?.ToString() ?? string.Empty, item.Value); } - private IEnumerator> YieldEnumerator(IEnumerable collection) + private IEnumerator> YieldEnumerator(IEnumerable collection) { if (_enumerateCollection is null) { @@ -685,7 +681,7 @@ private IEnumerator> YieldEnumerator(IEnumerable co } [UnconditionalSuppressMessage("Trimming - Allow reflection of message args", "IL2075")] - private Func>> BuildEnumerator(object firstItem) + private Func>> BuildEnumerator(object firstItem) { if (firstItem.GetType().IsGenericType && firstItem.GetType().GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) { @@ -702,20 +698,20 @@ private Func>> BuildEnumer } } - private static IEnumerator> EnumerateItems(IEnumerable collection, PropertyInfo getKeyProperty, PropertyInfo getValueProperty) + private static IEnumerator> EnumerateItems(IEnumerable collection, PropertyInfo getKeyProperty, PropertyInfo getValueProperty) { foreach (var item in collection) { var keyString = getKeyProperty.GetValue(item, null); var valueObject = getValueProperty.GetValue(item, null); - yield return new KeyValuePair(keyString.ToString(), valueObject); + yield return new KeyValuePair(keyString?.ToString() ?? string.Empty, valueObject); } } } private sealed class DictionaryEnumerator : IDictionaryEnumerator { - public IEnumerator> GetEnumerator(object value) + public IEnumerator> GetEnumerator(object value) { if (value is IDictionary dictionary) { @@ -732,28 +728,28 @@ public IEnumerator> GetEnumerator(object value) return EmptyDictionaryEnumerator.Default; } - private static IEnumerator> YieldEnumerator(IDictionary dictionary) + private static IEnumerator> YieldEnumerator(IDictionary dictionary) { foreach (var item in dictionary) - yield return new KeyValuePair(item.Key.ToString(), item.Value); + yield return new KeyValuePair(item.Key?.ToString() ?? string.Empty, item.Value); } #if !NET35 - private static IEnumerator> YieldEnumerator(IReadOnlyDictionary dictionary) + private static IEnumerator> YieldEnumerator(IReadOnlyDictionary dictionary) { foreach (var item in dictionary) - yield return new KeyValuePair(item.Key.ToString(), item.Value); + yield return new KeyValuePair(item.Key?.ToString() ?? string.Empty, item.Value); } #endif } - private sealed class EmptyDictionaryEnumerator : IEnumerator> + private sealed class EmptyDictionaryEnumerator : IEnumerator> { public static readonly EmptyDictionaryEnumerator Default = new EmptyDictionaryEnumerator(); - KeyValuePair IEnumerator>.Current => default(KeyValuePair); + KeyValuePair IEnumerator>.Current => default(KeyValuePair); - object IEnumerator.Current => default(KeyValuePair); + object IEnumerator.Current => default(KeyValuePair); bool IEnumerator.MoveNext() => false; diff --git a/src/NLog/Internal/OverloadResolutionPriorityAttribute.cs b/src/NLog/Internal/OverloadResolutionPriorityAttribute.cs index bea34420d8..f09fd630ca 100644 --- a/src/NLog/Internal/OverloadResolutionPriorityAttribute.cs +++ b/src/NLog/Internal/OverloadResolutionPriorityAttribute.cs @@ -33,6 +33,8 @@ #if NETSTANDARD2_1_OR_GREATER && !NET9_0_OR_GREATER +#nullable enable + namespace System.Runtime.CompilerServices { /// diff --git a/src/NLog/Internal/PathHelpers.cs b/src/NLog/Internal/PathHelpers.cs index 736a4a0e05..967b54c15d 100644 --- a/src/NLog/Internal/PathHelpers.cs +++ b/src/NLog/Internal/PathHelpers.cs @@ -31,10 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System.IO; +#nullable enable namespace NLog.Internal { + using System.IO; + internal static class PathHelpers { /// @@ -72,7 +74,7 @@ public static string TrimDirectorySeparators(string path) { var newpath = path?.TrimEnd(DirectorySeparatorChars) ?? string.Empty; if (newpath.EndsWith(":", System.StringComparison.Ordinal)) - return path; // Support root-path on Windows (But Linux root-path is off limits) + return path ?? string.Empty; // Support root-path on Windows (But Linux root-path is off limits) else return newpath; } diff --git a/src/NLog/Internal/PlatformDetector.cs b/src/NLog/Internal/PlatformDetector.cs index 61c5a85d35..ab5aa07ec1 100644 --- a/src/NLog/Internal/PlatformDetector.cs +++ b/src/NLog/Internal/PlatformDetector.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/PropertyHelper.cs b/src/NLog/Internal/PropertyHelper.cs index 0cf0918d7b..5849ba3d83 100644 --- a/src/NLog/Internal/PropertyHelper.cs +++ b/src/NLog/Internal/PropertyHelper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -52,7 +54,7 @@ namespace NLog.Internal /// internal static class PropertyHelper { - private static readonly Dictionary> _parameterInfoCache = new Dictionary>(); + private static readonly Dictionary?> _parameterInfoCache = new Dictionary?>(); private static readonly Dictionary> _propertyConversionMapper = BuildPropertyConversionMapper(); #pragma warning disable S1144 // Unused private types or members should be removed. BUT they help CoreRT to provide config through reflection @@ -84,7 +86,7 @@ private static Dictionary> internal static void SetPropertyFromString(object targetObject, PropertyInfo propInfo, string stringValue, ConfigurationItemFactory configurationItemFactory) { - object propertyValue = null; + object? propertyValue = null; try { @@ -119,7 +121,7 @@ internal static void SetPropertyFromString(object targetObject, PropertyInfo pro SetPropertyValueForObject(targetObject, propertyValue, propInfo); } - internal static void SetPropertyValueForObject(object targetObject, object value, PropertyInfo propInfo) + internal static void SetPropertyValueForObject(object targetObject, object? value, PropertyInfo propInfo) { try { @@ -148,7 +150,7 @@ internal static void SetPropertyValueForObject(object targetObject, object value /// property name on /// result when success. /// success. - internal static bool TryGetPropertyInfo(ConfigurationItemFactory configFactory, object obj, string propertyName, out PropertyInfo result) + internal static bool TryGetPropertyInfo(ConfigurationItemFactory configFactory, object obj, string propertyName, out PropertyInfo? result) { var configProperties = TryLookupConfigItemProperties(configFactory, obj.GetType()); if (configProperties is null) @@ -169,7 +171,7 @@ internal static bool TryGetPropertyInfo(ConfigurationItemFactory configFactory, [UnconditionalSuppressMessage("Trimming - Ignore since obsolete", "IL2075")] [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - private static bool TryGetPropertyInfo(object obj, string propertyName, out PropertyInfo result) + private static bool TryGetPropertyInfo(object obj, string propertyName, out PropertyInfo? result) { InternalLogger.Debug("Object reflection needed to configure instance of type: {0} (Lookup property={1})", obj.GetType(), propertyName); @@ -184,13 +186,13 @@ private static bool TryGetPropertyInfo(object obj, string propertyName, out Prop return false; } - internal static Type GetArrayItemType(PropertyInfo propInfo) + internal static Type? GetArrayItemType(PropertyInfo propInfo) { var arrayParameterAttribute = propInfo.GetFirstCustomAttribute(); return arrayParameterAttribute?.ItemType; } - internal static bool IsConfigurationItemType(ConfigurationItemFactory configFactory, Type type) + internal static bool IsConfigurationItemType(ConfigurationItemFactory configFactory, Type? type) { if (type is null || IsSimplePropertyType(type)) return false; @@ -209,7 +211,7 @@ internal static Dictionary GetAllConfigItemProperties(Conf return TryLookupConfigItemProperties(configFactory, type) ?? new Dictionary(); } - private static Dictionary TryLookupConfigItemProperties(ConfigurationItemFactory configFactory, Type type) + private static Dictionary? TryLookupConfigItemProperties(ConfigurationItemFactory configFactory, Type type) { lock (_parameterInfoCache) { @@ -271,7 +273,7 @@ internal static bool IsSimplePropertyType(Type type) } [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2070")] - private static bool TryImplicitConversion(Type resultType, string value, out object result) + private static bool TryImplicitConversion(Type resultType, string value, out object? result) { try { @@ -300,7 +302,7 @@ private static bool TryImplicitConversion(Type resultType, string value, out obj } [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2067")] - private static bool TryNLogSpecificConversion(Type propertyType, string value, ConfigurationItemFactory configurationItemFactory, out object newValue) + private static bool TryNLogSpecificConversion(Type propertyType, string value, ConfigurationItemFactory configurationItemFactory, out object? newValue) { if (_propertyConversionMapper.TryGetValue(propertyType, out var objectConverter)) { @@ -319,7 +321,7 @@ private static bool TryNLogSpecificConversion(Type propertyType, string value, C return false; } - private static bool TryGetEnumValue(Type resultType, string value, out object result) + private static bool TryGetEnumValue(Type resultType, string value, out object? result) { if (!resultType.IsEnum) { @@ -370,7 +372,7 @@ private static object TryParseConditionValue(string stringValue, ConfigurationIt /// /// If there is a comma in the value, then (single) quote the value. For single quotes, use the backslash as escape /// - private static bool TryFlatListConversion(object obj, PropertyInfo propInfo, string valueRaw, ConfigurationItemFactory configurationItemFactory, out object newValue) + private static bool TryFlatListConversion(object obj, PropertyInfo propInfo, string valueRaw, ConfigurationItemFactory configurationItemFactory, out object? newValue) { var collectionType = propInfo.PropertyType; if (!collectionType.IsGenericType || !typeof(IEnumerable).IsAssignableFrom(collectionType)) @@ -394,7 +396,7 @@ private static bool TryFlatListConversion(object obj, PropertyInfo propInfo, str newValue = Convert.ChangeType(value, propertyType, CultureInfo.InvariantCulture); } - collectionAddMethod.Invoke(newList, new object[] { newValue }); + collectionAddMethod.Invoke(newList, new object?[] { newValue }); } newValue = newList; @@ -412,7 +414,7 @@ private static bool TryFlatListConversion(object obj, PropertyInfo propInfo, str [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2072")] [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2075")] - private static bool TryCreateCollectionObject(object obj, PropertyInfo propInfo, out object collectionObject, out MethodInfo collectionAddMethod, out Type collectionItemType) + private static bool TryCreateCollectionObject(object obj, PropertyInfo propInfo, out object? collectionObject, [NotNullWhen(returnValue: true)] out MethodInfo? collectionAddMethod, [NotNullWhen(returnValue: true)] out Type? collectionItemType) { collectionObject = null; collectionAddMethod = null; @@ -455,7 +457,7 @@ private static bool TryCreateCollectionObject(object obj, PropertyInfo propInfo, if (existingValue.GetType().GetGenericTypeDefinition() == typeof(HashSet<>)) { - object hashSetComparer = null; + object? hashSetComparer = null; var existingType = existingValue.GetType(); var comparerPropInfo = existingType.GetProperty("Comparer", BindingFlags.Public | BindingFlags.Instance); if (comparerPropInfo.IsValidPublicProperty()) @@ -498,7 +500,7 @@ private static bool TryCreateCollectionObject(object obj, PropertyInfo propInfo, return TryCreateTypeCollection(collectionType, out collectionObject, out collectionAddMethod, out collectionItemType); } - private static bool TryCreateListCollection(Type collectionType, out object collectionObject, out MethodInfo collectionAddMethod, out Type collectionItemType) + private static bool TryCreateListCollection(Type collectionType, [NotNullWhen(returnValue: true)] out object? collectionObject, [NotNullWhen(returnValue: true)] out MethodInfo? collectionAddMethod, [NotNullWhen(returnValue: true)] out Type? collectionItemType) { if (collectionType.IsAssignableFrom(typeof(List))) { @@ -514,7 +516,7 @@ private static bool TryCreateListCollection(Type collectionType, out object c return false; } - private static bool TryCreateHashSetCollection(Type collectionType, out object collectionObject, out MethodInfo collectionAddMethod, out Type collectionItemType) + private static bool TryCreateHashSetCollection(Type collectionType, [NotNullWhen(returnValue: true)] out object? collectionObject, [NotNullWhen(returnValue: true)] out MethodInfo? collectionAddMethod, [NotNullWhen(returnValue: true)] out Type? collectionItemType) { if (collectionType.IsAssignableFrom(typeof(HashSet))) { @@ -555,7 +557,7 @@ private static bool TryCreateTypeCollection(Type propertyType, out object collec [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2026")] [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2067")] [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2072")] - internal static bool TryTypeConverterConversion(Type type, string value, out object newValue) + internal static bool TryTypeConverterConversion(Type type, string value, out object? newValue) { if (typeof(IConvertible).IsAssignableFrom(type) || type.IsAssignableFrom(typeof(string))) { @@ -585,7 +587,7 @@ internal static bool TryTypeConverterConversion(Type type, string value, out obj } } - private static bool TryCreatePropertyInfoDictionary(ConfigurationItemFactory configFactory, Type objectType, out Dictionary result) + private static bool TryCreatePropertyInfoDictionary(ConfigurationItemFactory configFactory, Type objectType, out Dictionary? result) { result = null; @@ -654,13 +656,13 @@ private static bool HasCustomConfigurationProperties(Type objectType, Dictionary return false; } - private static bool IncludeConfigurationPropertyInfo(Type objectType, PropertyInfo propInfo, bool checkDefaultValue, out string overridePropertyName) + private static bool IncludeConfigurationPropertyInfo(Type objectType, PropertyInfo propInfo, bool checkDefaultValue, out string? overridePropertyName) { overridePropertyName = null; try { - var propertyType = propInfo?.PropertyType; + var propertyType = propInfo.PropertyType; if (propertyType is null) return false; diff --git a/src/NLog/Internal/ReflectionHelpers.cs b/src/NLog/Internal/ReflectionHelpers.cs index 81bc739f34..a1d7b55f28 100644 --- a/src/NLog/Internal/ReflectionHelpers.cs +++ b/src/NLog/Internal/ReflectionHelpers.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -63,7 +65,7 @@ public static bool IsStaticClass(this Type type) /// /// Object instance, use null for static methods. /// Complete list of parameters that matches the method, including optional/default parameters. - public delegate object LateBoundMethod(object target, object[] arguments); + public delegate object? LateBoundMethod(object target, object[] arguments); /// /// Creates an optimized delegate for calling the MethodInfo using Expression-Trees @@ -160,20 +162,20 @@ private static UnaryExpression CreateParameterExpression(ParameterInfo parameter } [CanBeNull] - public static TAttr GetFirstCustomAttribute(this Type type) where TAttr : Attribute + public static TAttr? GetFirstCustomAttribute(this Type type) where TAttr : Attribute { return Attribute.GetCustomAttributes(type, typeof(TAttr)).FirstOrDefault() as TAttr; } [CanBeNull] - public static TAttr GetFirstCustomAttribute(this PropertyInfo info) + public static TAttr? GetFirstCustomAttribute(this PropertyInfo info) where TAttr : Attribute { return Attribute.GetCustomAttributes(info, typeof(TAttr)).FirstOrDefault() as TAttr; } [CanBeNull] - public static TAttr GetFirstCustomAttribute(this Assembly assembly) + public static TAttr? GetFirstCustomAttribute(this Assembly assembly) where TAttr : Attribute { return Attribute.GetCustomAttributes(assembly, typeof(TAttr)).FirstOrDefault() as TAttr; diff --git a/src/NLog/Internal/ReusableAsyncLogEventList.cs b/src/NLog/Internal/ReusableAsyncLogEventList.cs index 8189e0a67d..01bbb95165 100644 --- a/src/NLog/Internal/ReusableAsyncLogEventList.cs +++ b/src/NLog/Internal/ReusableAsyncLogEventList.cs @@ -31,11 +31,13 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System.Collections.Generic; -using NLog.Common; +#nullable enable namespace NLog.Internal { + using System.Collections.Generic; + using NLog.Common; + /// /// Controls a single allocated AsyncLogEventInfo-List for reuse (only one active user) /// diff --git a/src/NLog/Internal/ReusableBufferCreator.cs b/src/NLog/Internal/ReusableBufferCreator.cs index 85a851eac8..e784ba5d79 100644 --- a/src/NLog/Internal/ReusableBufferCreator.cs +++ b/src/NLog/Internal/ReusableBufferCreator.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { /// diff --git a/src/NLog/Internal/ReusableBuilderCreator.cs b/src/NLog/Internal/ReusableBuilderCreator.cs index abae711d22..d43754ddba 100644 --- a/src/NLog/Internal/ReusableBuilderCreator.cs +++ b/src/NLog/Internal/ReusableBuilderCreator.cs @@ -31,10 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System.Text; +#nullable enable namespace NLog.Internal { + using System.Text; + /// /// Controls a single allocated StringBuilder for reuse (only one active user) /// diff --git a/src/NLog/Internal/ReusableObjectCreator.cs b/src/NLog/Internal/ReusableObjectCreator.cs index f2ef85611f..29eb119d5c 100644 --- a/src/NLog/Internal/ReusableObjectCreator.cs +++ b/src/NLog/Internal/ReusableObjectCreator.cs @@ -31,16 +31,18 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; +#nullable enable namespace NLog.Internal { + using System; + /// /// Controls a single allocated object for reuse (only one active user) /// internal class ReusableObjectCreator where T : class { - protected T _reusableObject; + protected T? _reusableObject; private readonly Action _clearObject; private readonly Func _createObject; diff --git a/src/NLog/Internal/ReusableStreamCreator.cs b/src/NLog/Internal/ReusableStreamCreator.cs index 479e3acc89..f9d9d6c56d 100644 --- a/src/NLog/Internal/ReusableStreamCreator.cs +++ b/src/NLog/Internal/ReusableStreamCreator.cs @@ -31,11 +31,13 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.IO; +#nullable enable namespace NLog.Internal { + using System; + using System.IO; + /// /// Controls a single allocated MemoryStream for reuse (only one active user) /// @@ -73,7 +75,7 @@ private static void ResetBatchCapacity(MemoryStream memoryStream) void IDisposable.Dispose() { - _reusableObject.Dispose(); + _reusableObject?.Dispose(); } } } diff --git a/src/NLog/Internal/RuntimeOS.cs b/src/NLog/Internal/RuntimeOS.cs index 79da0a101d..8eccbf8362 100644 --- a/src/NLog/Internal/RuntimeOS.cs +++ b/src/NLog/Internal/RuntimeOS.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { /// diff --git a/src/NLog/Internal/SetupBuilder.cs b/src/NLog/Internal/SetupBuilder.cs index b5b6a9b165..8372da8871 100644 --- a/src/NLog/Internal/SetupBuilder.cs +++ b/src/NLog/Internal/SetupBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using NLog.Config; diff --git a/src/NLog/Internal/SetupConfigurationLoggingRuleBuilder.cs b/src/NLog/Internal/SetupConfigurationLoggingRuleBuilder.cs index 2cbf21c198..9d635ceb7b 100644 --- a/src/NLog/Internal/SetupConfigurationLoggingRuleBuilder.cs +++ b/src/NLog/Internal/SetupConfigurationLoggingRuleBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System.Collections; @@ -40,7 +42,7 @@ namespace NLog.Internal internal sealed class SetupConfigurationLoggingRuleBuilder : ISetupConfigurationLoggingRuleBuilder, IList { - public SetupConfigurationLoggingRuleBuilder(LogFactory logFactory, LoggingConfiguration configuration, string loggerNamePattern = null, string ruleName = null) + public SetupConfigurationLoggingRuleBuilder(LogFactory logFactory, LoggingConfiguration configuration, string? loggerNamePattern = null, string? ruleName = null) { LoggingRule = new LoggingRule(ruleName) { LoggerNamePattern = loggerNamePattern ?? "*" }; Configuration = configuration; diff --git a/src/NLog/Internal/SetupConfigurationTargetBuilder.cs b/src/NLog/Internal/SetupConfigurationTargetBuilder.cs index d548042ff3..3a1374c901 100644 --- a/src/NLog/Internal/SetupConfigurationTargetBuilder.cs +++ b/src/NLog/Internal/SetupConfigurationTargetBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -42,9 +44,9 @@ namespace NLog.Internal internal sealed class SetupConfigurationTargetBuilder : ISetupConfigurationTargetBuilder, IList { private readonly IList _targets = new List(); - private string _targetName; + private string? _targetName; - public SetupConfigurationTargetBuilder(LogFactory logFactory, LoggingConfiguration configuration, string targetName = null) + public SetupConfigurationTargetBuilder(LogFactory logFactory, LoggingConfiguration configuration, string? targetName = null) { Configuration = configuration; LogFactory = logFactory; diff --git a/src/NLog/Internal/SetupExtensionsBuilder.cs b/src/NLog/Internal/SetupExtensionsBuilder.cs index ce09dd721d..e3a3cef6e8 100644 --- a/src/NLog/Internal/SetupExtensionsBuilder.cs +++ b/src/NLog/Internal/SetupExtensionsBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using NLog.Config; diff --git a/src/NLog/Internal/SetupInternalLoggerBuilder.cs b/src/NLog/Internal/SetupInternalLoggerBuilder.cs index 8f16c4629f..d615b6e71a 100644 --- a/src/NLog/Internal/SetupInternalLoggerBuilder.cs +++ b/src/NLog/Internal/SetupInternalLoggerBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using NLog.Config; diff --git a/src/NLog/Internal/SetupLoadConfigurationBuilder.cs b/src/NLog/Internal/SetupLoadConfigurationBuilder.cs index ba4ff4f1f5..a9b872845d 100644 --- a/src/NLog/Internal/SetupLoadConfigurationBuilder.cs +++ b/src/NLog/Internal/SetupLoadConfigurationBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using NLog.Config; @@ -51,6 +53,6 @@ public LoggingConfiguration Configuration set => _configuration = value; } - internal LoggingConfiguration _configuration; + internal LoggingConfiguration? _configuration; } } diff --git a/src/NLog/Internal/SetupLogFactoryBuilder.cs b/src/NLog/Internal/SetupLogFactoryBuilder.cs index 172e21a93a..80b0ea1844 100644 --- a/src/NLog/Internal/SetupLogFactoryBuilder.cs +++ b/src/NLog/Internal/SetupLogFactoryBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using NLog.Config; diff --git a/src/NLog/Internal/SetupSerializationBuilder.cs b/src/NLog/Internal/SetupSerializationBuilder.cs index 84e1061665..e8ed3c82ec 100644 --- a/src/NLog/Internal/SetupSerializationBuilder.cs +++ b/src/NLog/Internal/SetupSerializationBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using NLog.Config; diff --git a/src/NLog/Internal/SimpleStringReader.cs b/src/NLog/Internal/SimpleStringReader.cs index a30522619f..c9bb3fef78 100644 --- a/src/NLog/Internal/SimpleStringReader.cs +++ b/src/NLog/Internal/SimpleStringReader.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/SingleCallContinuation.cs b/src/NLog/Internal/SingleCallContinuation.cs index 10002dc4b6..bdd36afe6a 100644 --- a/src/NLog/Internal/SingleCallContinuation.cs +++ b/src/NLog/Internal/SingleCallContinuation.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -44,13 +46,13 @@ internal sealed class SingleCallContinuation { internal static readonly AsyncContinuation Completed = new SingleCallContinuation(null).CompletedFunction; - private AsyncContinuation _asyncContinuation; + private AsyncContinuation? _asyncContinuation; /// /// Initializes a new instance of the class. /// /// The asynchronous continuation. - public SingleCallContinuation(AsyncContinuation asyncContinuation) + public SingleCallContinuation(AsyncContinuation? asyncContinuation) { _asyncContinuation = asyncContinuation; } diff --git a/src/NLog/Internal/SingleItemOptimizedHashSet.cs b/src/NLog/Internal/SingleItemOptimizedHashSet.cs index 6f620ff7ca..8520ab3cee 100644 --- a/src/NLog/Internal/SingleItemOptimizedHashSet.cs +++ b/src/NLog/Internal/SingleItemOptimizedHashSet.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -43,16 +45,16 @@ namespace NLog.Internal /// internal struct SingleItemOptimizedHashSet : ICollection { - private readonly T _singleItem; - private HashSet _hashset; - private readonly IEqualityComparer _comparer; + private readonly T? _singleItem; + private HashSet? _hashset; + private readonly IEqualityComparer? _comparer; private IEqualityComparer Comparer => _comparer ?? EqualityComparer.Default; public struct SingleItemScopedInsert : IDisposable { private readonly T _singleItem; - private readonly HashSet _hashset; + private readonly HashSet? _hashset; /// /// Insert single item on scope start, and remove on scope exit @@ -91,11 +93,11 @@ public void Dispose() } } - public int Count => _hashset?.Count ?? (EqualityComparer.Default.Equals(_singleItem, default(T)) ? 0 : 1); // Object Equals to default value + public int Count => _hashset?.Count ?? (EqualityComparer.Default.Equals(_singleItem, default(T)) ? 0 : 1); // Object Equals to default value public bool IsReadOnly => false; - public SingleItemOptimizedHashSet(T singleItem, SingleItemOptimizedHashSet existing, IEqualityComparer comparer = null) + public SingleItemOptimizedHashSet(T singleItem, SingleItemOptimizedHashSet existing, IEqualityComparer? comparer = null) { _comparer = existing._comparer ?? comparer ?? EqualityComparer.Default; if (existing._hashset != null) @@ -104,7 +106,7 @@ public SingleItemOptimizedHashSet(T singleItem, SingleItemOptimizedHashSet ex _hashset.Add(singleItem); _singleItem = default(T); } - else if (existing.Count == 1) + else if (existing.Count == 1 && existing._singleItem is not null) { _hashset = new HashSet(_comparer); _hashset.Add(existing._singleItem); @@ -131,7 +133,7 @@ public void Add(T item) else { var hashset = new HashSet(Comparer); - if (Count != 0) + if (Count == 1 && _singleItem is not null) { hashset.Add(_singleItem); } @@ -168,7 +170,7 @@ public bool Contains(T item) } else { - return Count == 1 && Comparer.Equals(_singleItem, item); + return Count == 1 && _singleItem is not null && Comparer.Equals(_singleItem, item); } } @@ -183,11 +185,12 @@ public bool Remove(T item) { return _hashset.Remove(item); } - else + else if (Count == 1 && _singleItem is not null && Comparer.Equals(_singleItem, item)) { _hashset = new HashSet(Comparer); - return Comparer.Equals(_singleItem, item); + return true; } + return false; } /// @@ -201,7 +204,7 @@ public void CopyTo(T[] array, int arrayIndex) { _hashset.CopyTo(array, arrayIndex); } - else if (Count == 1) + else if (Count == 1 && _singleItem is not null) { array[arrayIndex] = _singleItem; } @@ -225,7 +228,7 @@ public IEnumerator GetEnumerator() private IEnumerator SingleItemEnumerator() { - if (Count != 0) + if (Count == 1 && _singleItem is not null) yield return _singleItem; } diff --git a/src/NLog/Internal/SortHelpers.cs b/src/NLog/Internal/SortHelpers.cs index 8cbd7960b4..3afd5c2665 100644 --- a/src/NLog/Internal/SortHelpers.cs +++ b/src/NLog/Internal/SortHelpers.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -62,7 +64,7 @@ internal static class SortHelpers /// /// Dictionary where keys are unique input keys, and values are lists of . /// - public static Dictionary> BucketSort(this IEnumerable inputs, KeySelector keySelector) + public static Dictionary> BucketSort(this IEnumerable inputs, KeySelector keySelector) where TKey : notnull { var buckets = new Dictionary>(); @@ -91,7 +93,7 @@ public static Dictionary> BucketSort(this IEnum /// /// Dictionary where keys are unique input keys, and values are lists of . /// - public static ReadOnlySingleBucketDictionary> BucketSort(this IList inputs, KeySelector keySelector) + public static ReadOnlySingleBucketGroupBy> BucketSort(this IList inputs, KeySelector keySelector) where TKey : notnull { return BucketSort(inputs, keySelector, EqualityComparer.Default); } @@ -107,18 +109,17 @@ public static ReadOnlySingleBucketDictionary> BucketSort /// Dictionary where keys are unique input keys, and values are lists of . /// - public static ReadOnlySingleBucketDictionary> BucketSort(this IList inputs, KeySelector keySelector, IEqualityComparer keyComparer) + public static ReadOnlySingleBucketGroupBy> BucketSort(this IList inputs, KeySelector keySelector, IEqualityComparer keyComparer) where TKey : notnull { - Dictionary> buckets = null; - TKey singleBucketKey = default(TKey); - for (int i = 0; i < inputs.Count; i++) + if (inputs.Count == 0) + return new ReadOnlySingleBucketGroupBy>(singleBucket: null, keyComparer); + + Dictionary>? buckets = null; + TKey singleBucketKey = keySelector(inputs[0]); + for (int i = 1; i < inputs.Count; i++) { TKey keyValue = keySelector(inputs[i]); - if (i == 0) - { - singleBucketKey = keyValue; - } - else if (buckets is null) + if (buckets is null) { if (!keyComparer.Equals(singleBucketKey, keyValue)) { @@ -138,9 +139,9 @@ public static ReadOnlySingleBucketDictionary> BucketSort>(new KeyValuePair>(singleBucketKey, inputs), keyComparer); + return new ReadOnlySingleBucketGroupBy>(new KeyValuePair>(singleBucketKey, inputs), keyComparer); else - return new ReadOnlySingleBucketDictionary>(buckets, keyComparer); + return new ReadOnlySingleBucketGroupBy>(buckets, keyComparer); } private static Dictionary> CreateBucketDictionaryWithValue(IList inputs, IEqualityComparer keyComparer, int currentIndex, TKey firstBucketKey, TKey nextBucketKey) @@ -169,35 +170,35 @@ private static Dictionary> CreateBucketDictionaryWithValue : IDictionary + struct ReadOnlySingleBucketGroupBy : IEnumerable> where TKey : notnull { private #if !NETFRAMEWORK readonly #endif KeyValuePair? _singleBucket; // Not readonly to avoid struct-copy, and to avoid VerificationException when medium-trust AppDomain - private readonly Dictionary _multiBucket; + private readonly Dictionary? _multiBucket; private readonly IEqualityComparer _comparer; public IEqualityComparer Comparer => _comparer; - public ReadOnlySingleBucketDictionary(KeyValuePair singleBucket) + public ReadOnlySingleBucketGroupBy(KeyValuePair singleBucket) : this(singleBucket, EqualityComparer.Default) { } - public ReadOnlySingleBucketDictionary(Dictionary multiBucket) + public ReadOnlySingleBucketGroupBy(Dictionary multiBucket) : this(multiBucket, EqualityComparer.Default) { } - public ReadOnlySingleBucketDictionary(KeyValuePair singleBucket, IEqualityComparer comparer) + public ReadOnlySingleBucketGroupBy(KeyValuePair? singleBucket, IEqualityComparer comparer) { _comparer = comparer; _multiBucket = null; _singleBucket = singleBucket; } - public ReadOnlySingleBucketDictionary(Dictionary multiBucket, IEqualityComparer comparer) + public ReadOnlySingleBucketGroupBy(Dictionary multiBucket, IEqualityComparer comparer) { _comparer = comparer; _multiBucket = multiBucket; @@ -205,58 +206,7 @@ public ReadOnlySingleBucketDictionary(Dictionary multiBucket, IEqu } /// - public int Count { get { if (_multiBucket != null) return _multiBucket.Count; else if (_singleBucket.HasValue) return 1; else return 0; } } - - /// - public ICollection Keys - { - get - { - if (_multiBucket != null) - return _multiBucket.Keys; - else if (_singleBucket.HasValue) - return new[] { _singleBucket.Value.Key }; - else - return ArrayHelper.Empty(); - } - } - - /// - public ICollection Values - { - get - { - if (_multiBucket != null) - return _multiBucket.Values; - else if (_singleBucket.HasValue) - return new TValue[] { _singleBucket.Value.Value }; - else - return ArrayHelper.Empty(); - } - } - - /// - public bool IsReadOnly => true; - - /// - /// Allows direct lookup of existing keys. If trying to access non-existing key exception is thrown. - /// Consider to use instead for better safety. - /// - /// Key value for lookup - /// Mapped value found - public TValue this[TKey key] - { - get - { - if (_multiBucket != null) - return _multiBucket[key]; - else if (_singleBucket.HasValue && _comparer.Equals(_singleBucket.Value.Key, key)) - return _singleBucket.Value.Value; - else - throw new KeyNotFoundException(); - } - set => throw new NotSupportedException("Readonly"); - } + public int Count => _multiBucket?.Count ?? (_singleBucket.HasValue ? 1 : 0); /// /// Non-Allocating struct-enumerator @@ -265,7 +215,7 @@ public struct Enumerator : IEnumerator> { bool _singleBucketFirstRead; KeyValuePair _singleBucket; // Not readonly to avoid struct-copy, and to avoid VerificationException when medium-trust AppDomain - readonly IEnumerator> _multiBuckets; + readonly IEnumerator>? _multiBuckets; internal Enumerator(Dictionary multiBucket) { @@ -286,7 +236,7 @@ public KeyValuePair Current get { if (_multiBuckets != null) - return new KeyValuePair(_multiBuckets.Current.Key, _multiBuckets.Current.Value); + return _multiBuckets.Current; else return new KeyValuePair(_singleBucket.Key, _singleBucket.Value); } @@ -308,7 +258,6 @@ public bool MoveNext() return false; else return _singleBucketFirstRead = true; - } public void Reset() @@ -341,86 +290,6 @@ IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - - /// - public bool ContainsKey(TKey key) - { - if (_multiBucket != null) - return _multiBucket.ContainsKey(key); - else if (_singleBucket.HasValue) - return _comparer.Equals(_singleBucket.Value.Key, key); - else - return false; - } - - /// Will always throw, as dictionary is readonly - public void Add(TKey key, TValue value) - { - throw new NotSupportedException(); // Readonly - } - - /// Will always throw, as dictionary is readonly - public bool Remove(TKey key) - { - throw new NotSupportedException(); // Readonly - } - - /// - public bool TryGetValue(TKey key, out TValue value) - { - if (_multiBucket != null) - { - return _multiBucket.TryGetValue(key, out value); - } - else if (_singleBucket.HasValue && _comparer.Equals(_singleBucket.Value.Key, key)) - { - value = _singleBucket.Value.Value; - return true; - } - else - { - value = default(TValue); - return false; - } - } - - /// Will always throw, as dictionary is readonly - public void Add(KeyValuePair item) - { - throw new NotSupportedException(); // Readonly - } - - /// Will always throw, as dictionary is readonly - public void Clear() - { - throw new NotSupportedException(); // Readonly - } - - /// - public bool Contains(KeyValuePair item) - { - if (_multiBucket != null) - return ((IDictionary)_multiBucket).Contains(item); - else if (_singleBucket.HasValue) - return _comparer.Equals(_singleBucket.Value.Key, item.Key) && EqualityComparer.Default.Equals(_singleBucket.Value.Value, item.Value); - else - return false; - } - - /// - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - if (_multiBucket != null) - ((IDictionary)_multiBucket).CopyTo(array, arrayIndex); - else if (_singleBucket.HasValue) - array[arrayIndex] = _singleBucket.Value; - } - - /// Will always throw, as dictionary is readonly - public bool Remove(KeyValuePair item) - { - throw new NotSupportedException(); // Readonly - } } } } diff --git a/src/NLog/Internal/StackTraceUsageUtils.cs b/src/NLog/Internal/StackTraceUsageUtils.cs index f29e5d04a1..cb6ab19119 100644 --- a/src/NLog/Internal/StackTraceUsageUtils.cs +++ b/src/NLog/Internal/StackTraceUsageUtils.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -75,7 +77,7 @@ public static int GetFrameCount(this StackTrace strackTrace) public static string GetStackFrameMethodName(MethodBase method, bool includeMethodInfo, bool cleanAsyncMoveNext, bool cleanAnonymousDelegates) { if (method is null) - return null; + return string.Empty; string methodName = method.Name; @@ -113,7 +115,7 @@ public static string GetStackFrameMethodName(MethodBase method, bool includeMeth public static string GetStackFrameMethodClassName(MethodBase method, bool includeNameSpace, bool cleanAsyncMoveNext, bool cleanAnonymousDelegates) { if (method is null) - return null; + return string.Empty; var callerClassType = method.DeclaringType; if (cleanAsyncMoveNext @@ -126,10 +128,10 @@ public static string GetStackFrameMethodClassName(MethodBase method, bool includ callerClassType = callerClassType.DeclaringType; } - string className = includeNameSpace ? callerClassType?.FullName : callerClassType?.Name; - if (cleanAnonymousDelegates && className?.IndexOf("<>", StringComparison.Ordinal) >= 0) + var className = (includeNameSpace ? callerClassType?.FullName : callerClassType?.Name) ?? string.Empty; + if (cleanAnonymousDelegates && className.IndexOf("<>", StringComparison.Ordinal) >= 0) { - if (!includeNameSpace && callerClassType.DeclaringType != null && callerClassType.IsNested) + if (!includeNameSpace && callerClassType != null && callerClassType.DeclaringType != null && callerClassType.IsNested) { className = callerClassType.DeclaringType.Name; } @@ -144,7 +146,7 @@ public static string GetStackFrameMethodClassName(MethodBase method, bool includ } } - if (includeNameSpace && className?.IndexOf('.') == -1) + if (includeNameSpace && className.IndexOf('.') == -1) { var typeNamespace = GetNamespaceFromTypeAssembly(callerClassType); className = string.IsNullOrEmpty(typeNamespace) ? className : string.Concat(typeNamespace, ".", className); @@ -153,9 +155,9 @@ public static string GetStackFrameMethodClassName(MethodBase method, bool includ return className; } - private static string GetNamespaceFromTypeAssembly(Type callerClassType) + private static string GetNamespaceFromTypeAssembly(Type? callerClassType) { - var classAssembly = callerClassType.Assembly; + var classAssembly = callerClassType?.Assembly; if (classAssembly != null && classAssembly != mscorlibAssembly && classAssembly != systemAssembly) { var assemblyFullName = classAssembly.FullName; @@ -165,11 +167,11 @@ private static string GetNamespaceFromTypeAssembly(Type callerClassType) } } - return null; + return string.Empty; } [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow callsite logic", "IL2026")] - public static MethodBase GetStackMethod(StackFrame stackFrame) + public static MethodBase? GetStackMethod(StackFrame? stackFrame) { return stackFrame?.GetMethod(); } @@ -180,7 +182,7 @@ public static MethodBase GetStackMethod(StackFrame stackFrame) /// /// StackFrame from the calling method /// Fully qualified class name - public static string GetClassFullName(StackFrame stackFrame) + public static string GetClassFullName(StackFrame? stackFrame) { string className = LookupClassNameFromStackFrame(stackFrame); if (string.IsNullOrEmpty(className)) @@ -213,7 +215,7 @@ private static string GetClassFullName(StackTrace stackTrace) /// Returns the assembly from the provided StackFrame (If not internal assembly) /// /// Valid assembly, or null if assembly was internal - public static Assembly LookupAssemblyFromMethod(MethodBase method) + public static Assembly? LookupAssemblyFromMethod(MethodBase? method) { var assembly = method?.DeclaringType?.Assembly ?? method?.Module?.Assembly; @@ -241,7 +243,7 @@ public static Assembly LookupAssemblyFromMethod(MethodBase method) /// /// /// Valid class name, or empty string if assembly was internal - public static string LookupClassNameFromStackFrame(StackFrame stackFrame) + public static string LookupClassNameFromStackFrame(StackFrame? stackFrame) { var method = GetStackMethod(stackFrame); if (method != null && LookupAssemblyFromMethod(method) != null) diff --git a/src/NLog/Internal/StringBuilderExt.cs b/src/NLog/Internal/StringBuilderExt.cs index a032bc6b84..4608c156cc 100644 --- a/src/NLog/Internal/StringBuilderExt.cs +++ b/src/NLog/Internal/StringBuilderExt.cs @@ -31,14 +31,16 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Globalization; -using System.IO; -using System.Text; -using NLog.MessageTemplates; +#nullable enable namespace NLog.Internal { + using System; + using System.Globalization; + using System.IO; + using System.Text; + using NLog.MessageTemplates; + /// /// Helpers for , which is used in e.g. layout renderers. /// @@ -54,10 +56,9 @@ internal static class StringBuilderExt /// NLog string.Format interface public static void AppendFormattedValue(this StringBuilder builder, object value, string format, IFormatProvider formatProvider, IValueFormatter valueFormatter) { - string stringValue = value as string; - if (stringValue != null && string.IsNullOrEmpty(format)) + if (value is string stringValue && string.IsNullOrEmpty(format)) { - builder.Append(value); // Avoid automatic quotes + builder.Append(stringValue); // Avoid automatic quotes } else if (format == MessageTemplates.ValueFormatter.FormatAsJson) { diff --git a/src/NLog/Internal/StringBuilderPool.cs b/src/NLog/Internal/StringBuilderPool.cs index d7e7ef6c16..627fd51d97 100644 --- a/src/NLog/Internal/StringBuilderPool.cs +++ b/src/NLog/Internal/StringBuilderPool.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -39,8 +41,8 @@ namespace NLog.Internal internal sealed class StringBuilderPool { - private StringBuilder _fastPool; - private readonly StringBuilder[] _slowPool; + private StringBuilder? _fastPool; + private readonly StringBuilder?[] _slowPool; private readonly int _maxBuilderCapacity; /// @@ -66,7 +68,7 @@ public StringBuilderPool(int poolCapacity, int initialBuilderCapacity = 1024, in /// Allow return to pool public ItemHolder Acquire() { - StringBuilder item = _fastPool; + StringBuilder? item = _fastPool; if (item is null || item != Interlocked.CompareExchange(ref _fastPool, null, item)) { for (int i = 0; i < _slowPool.Length; i++) @@ -123,10 +125,10 @@ private void Release(StringBuilder stringBuilder, int poolIndex) public struct ItemHolder : IDisposable { public readonly StringBuilder Item; - readonly StringBuilderPool _owner; + readonly StringBuilderPool? _owner; readonly int _poolIndex; - public ItemHolder(StringBuilder stringBuilder, StringBuilderPool owner, int poolIndex) + public ItemHolder(StringBuilder stringBuilder, StringBuilderPool? owner, int poolIndex) { Item = stringBuilder; _owner = owner; diff --git a/src/NLog/Internal/StringHelpers.cs b/src/NLog/Internal/StringHelpers.cs index e403cc303a..f6e8c5d841 100644 --- a/src/NLog/Internal/StringHelpers.cs +++ b/src/NLog/Internal/StringHelpers.cs @@ -31,14 +31,16 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using JetBrains.Annotations; +#nullable enable namespace NLog.Internal { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using JetBrains.Annotations; + /// /// Helpers for . /// @@ -50,7 +52,7 @@ internal static class StringHelpers /// /// [ContractAnnotation("value:null => true")] - internal static bool IsNullOrWhiteSpace(string value) + internal static bool IsNullOrWhiteSpace(string? value) { #if !NET35 return string.IsNullOrWhiteSpace(value); @@ -97,7 +99,7 @@ public static string Replace([NotNull] string str, [NotNull] string oldValue, st Guard.ThrowIfNullOrEmpty(oldValue); - StringBuilder sb = null; + StringBuilder? sb = null; int previousIndex = 0; int index = str.IndexOf(oldValue, comparison); diff --git a/src/NLog/Internal/StringSplitter.cs b/src/NLog/Internal/StringSplitter.cs index 4989708295..605556b770 100644 --- a/src/NLog/Internal/StringSplitter.cs +++ b/src/NLog/Internal/StringSplitter.cs @@ -31,12 +31,14 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Collections.Generic; -using System.Text; +#nullable enable namespace NLog.Internal { + using System; + using System.Collections.Generic; + using System.Text; + /// /// Split a string /// diff --git a/src/NLog/Internal/TargetWithFilterChain.cs b/src/NLog/Internal/TargetWithFilterChain.cs index 7c9fafe31a..42ade5dc4a 100644 --- a/src/NLog/Internal/TargetWithFilterChain.cs +++ b/src/NLog/Internal/TargetWithFilterChain.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -50,7 +52,7 @@ internal sealed class TargetWithFilterChain : ITargetWithFilterChain private static TargetWithFilterChain[] CreateLoggerConfiguration() => new TargetWithFilterChain[LogLevel.MaxLevel.Ordinal + 2]; // +2 to include LogLevel.Off - private MruCache _callSiteClassNameCache; + private MruCache? _callSiteClassNameCache; /// /// Initializes a new instance of the class. @@ -82,7 +84,7 @@ public TargetWithFilterChain(Target target, IList filterChain, FilterRes /// /// The next item in the chain. /// This is for example the 'target2' logger in writeTo='target1,target2' - public TargetWithFilterChain NextInChain { get; set; } + public TargetWithFilterChain? NextInChain { get; set; } /// /// Gets the stack trace usage. @@ -129,7 +131,7 @@ static internal TargetWithFilterChain[] BuildLoggerConfiguration(string loggerNa static private bool GetTargetsByLevelForLogger(string name, LoggingRule[] loggingRules, LogLevel globalLogLevel, TargetWithFilterChain[] targetsByLevel, TargetWithFilterChain[] lastTargetsByLevel, bool[] suppressedLevels) { - IList>> finalMinLevelWithFilters = null; + IList>>? finalMinLevelWithFilters = null; bool targetsFound = false; foreach (LoggingRule rule in loggingRules) { @@ -163,7 +165,7 @@ static private bool GetTargetsByLevelForLogger(string name, LoggingRule[] loggin if (finalMinLevelWithFilters?.Count > 0) { var finalMinLevelFilters = finalMinLevelWithFilters[i]; - if (finalMinLevelFilters.Value?.Count > 0) + if (finalMinLevelFilters.Value?.Count > 0 && finalMinLevelFilters.Key.HasValue) { targetsByLevel[i] = tfc = AppendFinalMinLevelFilters(tfc, finalMinLevelFilters.Value, finalMinLevelFilters.Key.Value); } @@ -180,7 +182,7 @@ private static bool LoggingRuleHasFinalMinLevelFilters(LoggingRule rule) return rule.FinalMinLevel != LogLevel.Off && rule.Filters.Count != 0 && rule.Targets.Count == 0; } - private static void CollectFinalMinLevelFiltersFromRule(LoggingRule rule, ref IList>> finalMinLevelWithFilters) + private static void CollectFinalMinLevelFiltersFromRule(LoggingRule rule, ref IList>>? finalMinLevelWithFilters) { var finalMinLevel = rule.FinalMinLevel; if (finalMinLevel is null) @@ -192,10 +194,10 @@ private static void CollectFinalMinLevelFiltersFromRule(LoggingRule rule, ref IL if (i < finalMinLevel.Ordinal) continue; - if (finalMinLevelWithFilters[i].Key.HasValue && finalMinLevelWithFilters[i].Key.Value != rule.FilterDefaultAction) + var newFilterResult = finalMinLevelWithFilters[i].Key ?? rule.FilterDefaultAction; + if (newFilterResult != rule.FilterDefaultAction) continue; - var newFilterResult = finalMinLevelWithFilters[i].Key ?? rule.FilterDefaultAction; var newFilterChain = finalMinLevelWithFilters[i].Value?.Count > 0 ? System.Linq.Enumerable.ToArray(System.Linq.Enumerable.Concat(finalMinLevelWithFilters[i].Value, rule.Filters)) : rule.Filters; finalMinLevelWithFilters[i] = new KeyValuePair>(newFilterResult, newFilterChain); } @@ -292,14 +294,14 @@ private static bool SuppressLogLevel(LoggingRule rule, bool[] ruleLogLevels, Log return false; } - private static TargetWithFilterChain CreateTargetChainFromLoggingRule(LoggingRule rule, Target target, TargetWithFilterChain existingTargets) + private static TargetWithFilterChain? CreateTargetChainFromLoggingRule(LoggingRule rule, Target target, TargetWithFilterChain existingTargets) { var filterChain = rule.Filters.Count == 0 ? ArrayHelper.Empty() : rule.Filters; var newTarget = new TargetWithFilterChain(target, filterChain, rule.FilterDefaultAction); if (existingTargets != null && newTarget.FilterChain.Count == 0) { - for (TargetWithFilterChain afc = existingTargets; afc != null; afc = afc.NextInChain) + for (TargetWithFilterChain? afc = existingTargets; afc != null; afc = afc.NextInChain) { if (ReferenceEquals(target, afc.Target) && afc.FilterChain.Count == 0) { @@ -344,7 +346,7 @@ internal bool TryRememberCallSiteClassName(LogEventInfo logEvent) if (string.IsNullOrEmpty(logEvent.CallSiteInformation?.CallerFilePath)) return false; - string className = logEvent.CallSiteInformation.GetCallerClassName(null, true, true, true); + var className = logEvent.CallSiteInformation?.GetCallerClassName(null, true, true, true); if (string.IsNullOrEmpty(className)) return false; @@ -359,7 +361,7 @@ internal bool TryRememberCallSiteClassName(LogEventInfo logEvent) return _callSiteClassNameCache.TryAddValue(callSiteKey, internClassName); } - internal bool TryLookupCallSiteClassName(LogEventInfo logEvent, out string callSiteClassName) + internal bool TryLookupCallSiteClassName(LogEventInfo logEvent, out string? callSiteClassName) { callSiteClassName = logEvent.CallSiteInformation?.CallerClassName; if (!string.IsNullOrEmpty(callSiteClassName)) @@ -379,9 +381,12 @@ public void WriteToLoggerTargets(Type loggerType, LogEventInfo logEvent, LogFact LoggerImpl.Write(loggerType, this, logEvent, logFactory); } - struct CallSiteKey : IEquatable +#if !NETFRAMEWORK + readonly +#endif + struct CallSiteKey : IEquatable { - public CallSiteKey(string methodName, string fileSourceName, int fileSourceLineNumber) + public CallSiteKey(string? methodName, string? fileSourceName, int fileSourceLineNumber) { MethodName = methodName ?? string.Empty; FileSourceName = fileSourceName ?? string.Empty; diff --git a/src/NLog/Internal/ThreadSafeDictionary.cs b/src/NLog/Internal/ThreadSafeDictionary.cs index cb99eeaa7f..1c3f55bfdc 100644 --- a/src/NLog/Internal/ThreadSafeDictionary.cs +++ b/src/NLog/Internal/ThreadSafeDictionary.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System.Collections; @@ -43,7 +45,7 @@ internal class ThreadSafeDictionary : IDictionary { private readonly object _lockObject = new object(); private Dictionary _dict; - private Dictionary _dictReadOnly; // Reset cache on change + private Dictionary? _dictReadOnly; // Reset cache on change public ThreadSafeDictionary() : this(EqualityComparer.Default) diff --git a/src/NLog/Internal/TimeoutContinuation.cs b/src/NLog/Internal/TimeoutContinuation.cs index 21ac604e45..e780d44697 100644 --- a/src/NLog/Internal/TimeoutContinuation.cs +++ b/src/NLog/Internal/TimeoutContinuation.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -44,8 +46,8 @@ namespace NLog.Internal [Obsolete("Marked obsolete on NLog 6.0")] internal sealed class TimeoutContinuation : IDisposable { - private AsyncContinuation _asyncContinuation; - private Timer _timeoutTimer; + private AsyncContinuation? _asyncContinuation; + private Timer? _timeoutTimer; /// /// Initializes a new instance of the class. diff --git a/src/NLog/Internal/UrlHelper.cs b/src/NLog/Internal/UrlHelper.cs index e65b802221..d37058b5f7 100644 --- a/src/NLog/Internal/UrlHelper.cs +++ b/src/NLog/Internal/UrlHelper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -77,8 +79,8 @@ public static void EscapeDataEncode(string source, StringBuilder target, EscapeE bool isNLogLegacy = Contains(options, EscapeEncodingOptions.NLogLegacy); #pragma warning restore CS0618 // Type or member is obsolete - char[] charArray = null; - byte[] byteArray = null; + char[]? charArray = null; + byte[]? byteArray = null; char[] hexChars = isLowerCaseHex ? hexLowerChars : hexUpperChars; for (int i = 0; i < source.Length; ++i) diff --git a/src/NLog/Internal/XmlHelper.cs b/src/NLog/Internal/XmlHelper.cs index d3fbb061a9..39a4364274 100644 --- a/src/NLog/Internal/XmlHelper.cs +++ b/src/NLog/Internal/XmlHelper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; @@ -164,7 +166,7 @@ private static bool RequiresXmlEscape(StringBuilder target, int startPos, bool x private static readonly char[] XmlEscapeChars = new char[] { '<', '>', '&', '\'', '"' }; private static readonly char[] XmlEscapeNewlineChars = new char[] { '<', '>', '&', '\'', '"', '\r', '\n' }; - internal static string EscapeXmlString(string text, bool xmlEncodeNewlines, StringBuilder result = null) + internal static string EscapeXmlString(string text, bool xmlEncodeNewlines, StringBuilder? result = null) { if (result is null && SmallAndNoEscapeNeeded(text, xmlEncodeNewlines)) { @@ -216,7 +218,7 @@ internal static string EscapeXmlString(string text, bool xmlEncodeNewlines, Stri } } - return result is null ? sb.ToString() : null; + return result is null ? sb.ToString() : string.Empty; } /// @@ -235,7 +237,7 @@ private static bool SmallAndNoEscapeNeeded(string text, bool xmlEncodeNewlines) /// /// Object value /// Object value converted to string - internal static string XmlConvertToStringSafe(object value) + internal static string XmlConvertToStringSafe(object? value) { return XmlConvertToString(value, true); } @@ -245,7 +247,7 @@ internal static string XmlConvertToStringSafe(object value) /// /// Object value /// Object value converted to string - internal static string XmlConvertToString(object value) + internal static string XmlConvertToString(object? value) { return value is string stringValue ? stringValue : XmlConvertToString(value, false); } @@ -283,6 +285,89 @@ internal static string XmlConvertToString(DateTime value) return value.ToString("yyyy-MM-ddTHH:mm:ss.FFFFFFFK", CultureInfo.InvariantCulture); } + /// + /// Converts object value to invariant format (understood by JavaScript) + /// + /// Object value + /// Object TypeCode + /// Check and remove unusual unicode characters from the result string. + /// Object value converted to string + internal static string XmlConvertToString(IConvertible? value, TypeCode objTypeCode, bool safeConversion = false) + { + if (objTypeCode == TypeCode.Empty || value is null) + { + return "null"; + } + + switch (objTypeCode) + { + case TypeCode.Boolean: + return value.ToBoolean(CultureInfo.InvariantCulture) ? "true" : "false"; // boolean as lowercase + case TypeCode.Byte: + return value.ToByte(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); + case TypeCode.SByte: + return value.ToSByte(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); + case TypeCode.Int16: + return value.ToInt16(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); + case TypeCode.Int32: + return value.ToInt32(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); + case TypeCode.Int64: + return value.ToInt64(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); + case TypeCode.UInt16: + return value.ToUInt16(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); + case TypeCode.UInt32: + return value.ToUInt32(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); + case TypeCode.UInt64: + return value.ToUInt64(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); + case TypeCode.Single: + return XmlConvertToString(value.ToSingle(CultureInfo.InvariantCulture)); + case TypeCode.Double: + return XmlConvertToString(value.ToDouble(CultureInfo.InvariantCulture)); + case TypeCode.Decimal: + return XmlConvertToString(value.ToDecimal(CultureInfo.InvariantCulture)); + case TypeCode.DateTime: + return XmlConvertToString(value.ToDateTime(CultureInfo.InvariantCulture)); + case TypeCode.Char: + return safeConversion ? RemoveInvalidXmlChars(value.ToString(CultureInfo.InvariantCulture)) : value.ToString(CultureInfo.InvariantCulture); + case TypeCode.String: + return safeConversion ? RemoveInvalidXmlChars(value.ToString(CultureInfo.InvariantCulture)) : value.ToString(CultureInfo.InvariantCulture); + default: + return XmlConvertToStringInvariant(value, safeConversion); + } + } + + private static string XmlConvertToString(object? value, bool safeConversion) + { + try + { + var convertibleValue = value as IConvertible; + var objTypeCode = convertibleValue?.GetTypeCode() ?? (value is null ? TypeCode.Empty : TypeCode.Object); + if (objTypeCode != TypeCode.Object) + { + return XmlConvertToString(convertibleValue, objTypeCode, safeConversion); + } + + return XmlConvertToStringInvariant(value, safeConversion); + } + catch + { + return string.Empty; + } + } + + private static string XmlConvertToStringInvariant(object? value, bool safeConversion) + { + try + { + string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); + return safeConversion ? RemoveInvalidXmlChars(valueString) : valueString; + } + catch + { + return string.Empty; + } + } + /// /// XML elements must follow these naming rules: /// - Element names are case-sensitive @@ -300,7 +385,7 @@ internal static string XmlConvertToElementName(string xmlElementName) bool allowNamespace = true; - StringBuilder sb = null; + StringBuilder? sb = null; for (int i = 0; i < xmlElementName.Length; ++i) { char chr = xmlElementName[i]; @@ -368,89 +453,6 @@ StringBuilder CreateStringBuilder(string orgValue, int i) } } - private static string XmlConvertToString(object value, bool safeConversion) - { - try - { - var convertibleValue = value as IConvertible; - var objTypeCode = convertibleValue?.GetTypeCode() ?? (value is null ? TypeCode.Empty : TypeCode.Object); - if (objTypeCode != TypeCode.Object) - { - return XmlConvertToString(convertibleValue, objTypeCode, safeConversion); - } - - return XmlConvertToStringInvariant(value, safeConversion); - } - catch - { - return safeConversion ? string.Empty : null; - } - } - - private static string XmlConvertToStringInvariant(object value, bool safeConversion) - { - try - { - string valueString = Convert.ToString(value, CultureInfo.InvariantCulture); - return safeConversion ? RemoveInvalidXmlChars(valueString) : valueString; - } - catch - { - return safeConversion ? string.Empty : null; - } - } - - /// - /// Converts object value to invariant format (understood by JavaScript) - /// - /// Object value - /// Object TypeCode - /// Check and remove unusual unicode characters from the result string. - /// Object value converted to string - internal static string XmlConvertToString(IConvertible value, TypeCode objTypeCode, bool safeConversion = false) - { - if (objTypeCode == TypeCode.Empty || value is null) - { - return "null"; - } - - switch (objTypeCode) - { - case TypeCode.Boolean: - return value.ToBoolean(CultureInfo.InvariantCulture) ? "true" : "false"; // boolean as lowercase - case TypeCode.Byte: - return value.ToByte(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); - case TypeCode.SByte: - return value.ToSByte(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); - case TypeCode.Int16: - return value.ToInt16(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); - case TypeCode.Int32: - return value.ToInt32(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); - case TypeCode.Int64: - return value.ToInt64(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); - case TypeCode.UInt16: - return value.ToUInt16(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); - case TypeCode.UInt32: - return value.ToUInt32(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); - case TypeCode.UInt64: - return value.ToUInt64(CultureInfo.InvariantCulture).ToString(null, NumberFormatInfo.InvariantInfo); - case TypeCode.Single: - return XmlConvertToString(value.ToSingle(CultureInfo.InvariantCulture)); - case TypeCode.Double: - return XmlConvertToString(value.ToDouble(CultureInfo.InvariantCulture)); - case TypeCode.Decimal: - return XmlConvertToString(value.ToDecimal(CultureInfo.InvariantCulture)); - case TypeCode.DateTime: - return XmlConvertToString(value.ToDateTime(CultureInfo.InvariantCulture)); - case TypeCode.Char: - return safeConversion ? RemoveInvalidXmlChars(value.ToString(CultureInfo.InvariantCulture)) : value.ToString(CultureInfo.InvariantCulture); - case TypeCode.String: - return safeConversion ? RemoveInvalidXmlChars(value.ToString(CultureInfo.InvariantCulture)) : value.ToString(CultureInfo.InvariantCulture); - default: - return XmlConvertToStringInvariant(value, safeConversion); - } - } - private static string EnsureDecimalPlace(string text) { if (text.IndexOf('.') != -1 || text.IndexOfAny(DecimalScientificExponent) != -1) diff --git a/src/NLog/Internal/XmlParser.cs b/src/NLog/Internal/XmlParser.cs index 2143365d29..0a2ee2ba28 100644 --- a/src/NLog/Internal/XmlParser.cs +++ b/src/NLog/Internal/XmlParser.cs @@ -31,11 +31,14 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Internal { using System; using System.Collections; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Text; @@ -59,7 +62,7 @@ public XmlParser(string xmlSource) _xmlSource = new CharEnumerator(new StringReader(xmlSource)); } - public XmlParserElement LoadDocument(out IList processingInstructions) + public XmlParserElement LoadDocument(out IList? processingInstructions) { try { @@ -126,7 +129,7 @@ public XmlParserElement LoadDocument(out IList processingInstr } } - public bool TryReadProcessingInstructions(out IList processingInstructions) + public bool TryReadProcessingInstructions(out IList? processingInstructions) { SkipWhiteSpaces(); @@ -164,7 +167,7 @@ public bool TryReadProcessingInstructions(out IList processing /// /// True if start element was found. /// Something unexpected has failed. - public bool TryReadStartElement(out string name, out List> attributes) + public bool TryReadStartElement([NotNullWhen(returnValue: true)] out string? name, out List>? attributes) { SkipWhiteSpaces(); @@ -315,7 +318,7 @@ private void SkipXmlComment() } /// Something unexpected has failed. - private bool TryReadAttributes(out List> attributes, bool expectsProcessingInstruction = false) + private bool TryReadAttributes(out List>? attributes, bool expectsProcessingInstruction = false) { SkipWhiteSpaces(); @@ -369,7 +372,7 @@ private bool TryReadAttributes(out List> attributes /// Consumer of this method should handle safe position. /// /// Something unexpected has failed. - private bool TryBeginReadStartElement(out string name, bool processingInstruction = false) + private bool TryBeginReadStartElement([NotNullWhen(returnValue: true)] out string? name, bool processingInstruction = false) { if (_xmlSource.Current != '<' || _xmlSource.Peek() == '/' || _xmlSource.Peek() == '!') { @@ -571,7 +574,7 @@ private int TryParseUnicodeValueHex() return unicode; } - private bool TryParseSpecialXmlToken(out string xmlToken) + private bool TryParseSpecialXmlToken(out string? xmlToken) { foreach (var token in _specialTokens) { @@ -622,21 +625,23 @@ private static bool CharIsSpace(char c) public sealed class XmlParserElement { public string Name { get; set; } - public string InnerText { get; set; } - public IList Children { get; private set; } - public IList> Attributes { get; } + public string? InnerText { get; set; } + public IList Children => _children ?? ArrayHelper.Empty(); + private IList? _children; + public IList> Attributes => _attributes ?? ArrayHelper.Empty>(); + private readonly IList>? _attributes; - public XmlParserElement(string name, IList> attributes) + public XmlParserElement(string name, IList>? attributes) { Name = name; - Attributes = attributes; + _attributes = attributes; } public void AddChild(XmlParserElement child) { - if (Children is null) - Children = new List(); - Children.Add(child); + if (_children is null) + _children = new List(); + _children.Add(child); } } diff --git a/src/NLog/LayoutRenderers/CallSiteLayoutRenderer.cs b/src/NLog/LayoutRenderers/CallSiteLayoutRenderer.cs index 61e3995163..6f8974c803 100644 --- a/src/NLog/LayoutRenderers/CallSiteLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CallSiteLayoutRenderer.cs @@ -132,13 +132,13 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) if (ClassName) { - string className = logEventCallSize.GetCallerClassName(method, IncludeNamespace, CleanNamesOfAsyncContinuations, CleanNamesOfAnonymousDelegates); + var className = logEventCallSize.GetCallerClassName(method, IncludeNamespace, CleanNamesOfAsyncContinuations, CleanNamesOfAnonymousDelegates); builder.Append(string.IsNullOrEmpty(className) ? "" : className); } if (MethodName) { - string methodName = logEventCallSize.GetCallerMethodName(method, false, CleanNamesOfAsyncContinuations, CleanNamesOfAnonymousDelegates); + var methodName = logEventCallSize.GetCallerMethodName(method, false, CleanNamesOfAsyncContinuations, CleanNamesOfAnonymousDelegates); if (ClassName) { builder.Append('.'); @@ -183,7 +183,7 @@ private void AppendExceptionCallSite(StringBuilder builder, LogEventInfo logEven { if (ClassName) { - var className = StackTraceUsageUtils.GetStackFrameMethodClassName(targetSite, true, CleanNamesOfAsyncContinuations, CleanNamesOfAnonymousDelegates) ?? string.Empty; + var className = StackTraceUsageUtils.GetStackFrameMethodClassName(targetSite, true, CleanNamesOfAsyncContinuations, CleanNamesOfAnonymousDelegates); builder.Append(className); } @@ -193,7 +193,7 @@ private void AppendExceptionCallSite(StringBuilder builder, LogEventInfo logEven { builder.Append('.'); } - var methodName = StackTraceUsageUtils.GetStackFrameMethodName(targetSite, false, CleanNamesOfAsyncContinuations, CleanNamesOfAnonymousDelegates) ?? string.Empty; + var methodName = StackTraceUsageUtils.GetStackFrameMethodName(targetSite, false, CleanNamesOfAsyncContinuations, CleanNamesOfAnonymousDelegates); builder.Append(methodName); } } diff --git a/src/NLog/Layouts/Typed/Layout.cs b/src/NLog/Layouts/Typed/Layout.cs index aad8b3640c..b1f8827de5 100644 --- a/src/NLog/Layouts/Typed/Layout.cs +++ b/src/NLog/Layouts/Typed/Layout.cs @@ -191,7 +191,7 @@ private object RenderObjectValue([CanBeNull] LogEventInfo logEvent, [CanBeNull] protected override string GetFormattedMessage(LogEventInfo logEvent) { var objectValue = IsFixed ? FixedObjectValue : RenderObjectValue(logEvent, null); - return FormatHelper.TryFormatToString(objectValue, null, CultureInfo.InvariantCulture) ?? string.Empty; + return FormatHelper.TryFormatToString(objectValue, null, CultureInfo.InvariantCulture); } /// diff --git a/src/NLog/LoggerImpl.cs b/src/NLog/LoggerImpl.cs index 16d617c904..5410554d99 100644 --- a/src/NLog/LoggerImpl.cs +++ b/src/NLog/LoggerImpl.cs @@ -59,7 +59,7 @@ internal static void Write([NotNull] Type loggerType, [NotNull] TargetWithFilter if (stu != StackTraceUsage.None) { bool attemptCallSiteOptimization = targetsForLevel.TryCallSiteClassNameOptimization(stu, logEvent); - if (attemptCallSiteOptimization && targetsForLevel.TryLookupCallSiteClassName(logEvent, out string callSiteClassName)) + if (attemptCallSiteOptimization && targetsForLevel.TryLookupCallSiteClassName(logEvent, out var callSiteClassName)) { logEvent.GetCallSiteInformationInternal().CallerClassName = callSiteClassName; } diff --git a/src/NLog/ScopeContext.cs b/src/NLog/ScopeContext.cs index c62ab644e7..1c48028edd 100644 --- a/src/NLog/ScopeContext.cs +++ b/src/NLog/ScopeContext.cs @@ -791,7 +791,7 @@ private static void ClearMappedContextCallContext() private static void SetPropertyCallContext(string item, TValue? value, IDictionary mappedContext) { object? objectValue = value; - if (Convert.GetTypeCode(objectValue) != TypeCode.Object) + if (objectValue is null || Convert.GetTypeCode(objectValue) != TypeCode.Object) mappedContext[item] = objectValue; else mappedContext[item] = new ObjectHandleSerializer(objectValue); diff --git a/tests/NLog.UnitTests/ApiTests.cs b/tests/NLog.UnitTests/ApiTests.cs index 6f197f1155..6fe0dc2571 100644 --- a/tests/NLog.UnitTests/ApiTests.cs +++ b/tests/NLog.UnitTests/ApiTests.cs @@ -503,6 +503,7 @@ public void ShouldNotHaveExplicitStaticConstructors() "NLog.LayoutRenderers.LevelLayoutRenderer", "NLog.Internal.AppendBuilderCreator", "NLog.Internal.CallSiteInformation", + "NLog.Internal.DictionaryEntryEnumerable+EmptyDictionaryEnumerator", "NLog.Internal.ExceptionMessageFormatProvider", "NLog.Internal.FactoryHelper", "NLog.Internal.LogMessageStringFormatter", diff --git a/tests/NLog.UnitTests/Internal/SortHelpersTests.cs b/tests/NLog.UnitTests/Internal/SortHelpersTests.cs index 88239c6a35..ae0db17e2e 100644 --- a/tests/NLog.UnitTests/Internal/SortHelpersTests.cs +++ b/tests/NLog.UnitTests/Internal/SortHelpersTests.cs @@ -44,7 +44,7 @@ public class SortHelpersTests [Fact] public void SingleBucketDictionary_NoBucketTest() { - SortHelpers.ReadOnlySingleBucketDictionary> dict = new SortHelpers.ReadOnlySingleBucketDictionary>(); + SortHelpers.ReadOnlySingleBucketGroupBy> dict = new SortHelpers.ReadOnlySingleBucketGroupBy>(); Assert.Empty(dict); Assert.Empty(dict); @@ -52,43 +52,19 @@ public void SingleBucketDictionary_NoBucketTest() foreach (var _ in dict) Assert.False(true); - - Assert.Empty(dict.Keys); - foreach (var _ in dict.Keys) - Assert.False(true); - - Assert.Empty(dict.Values); - foreach (var _ in dict.Values) - Assert.False(true); - - IList bucket; - Assert.False(dict.TryGetValue("Bucket1", out bucket) || bucket != null); - Assert.False(dict.TryGetValue(string.Empty, out bucket) || bucket != null); - Assert.False(dict.TryGetValue(null, out bucket) || bucket != null); - - Assert.Throws(() => dict[string.Empty] = ArrayHelper.Empty()); } [Fact] public void SingleBucketDictionary_OneBucketEmptyTest() { IList bucket = ArrayHelper.Empty(); - SortHelpers.ReadOnlySingleBucketDictionary> dict = new SortHelpers.ReadOnlySingleBucketDictionary>(new KeyValuePair>("Bucket1", bucket)); + SortHelpers.ReadOnlySingleBucketGroupBy> dict = new SortHelpers.ReadOnlySingleBucketGroupBy>(new KeyValuePair>("Bucket1", bucket)); Assert.Single(dict); Assert.Contains(new KeyValuePair>("Bucket1", bucket), dict); Assert.DoesNotContain(new KeyValuePair>("Bucket1", null), dict); Assert.DoesNotContain(new KeyValuePair>(string.Empty, bucket), dict); - Assert.True(dict.ContainsKey("Bucket1")); - Assert.False(dict.ContainsKey(string.Empty)); - - KeyValuePair>[] copyToResult = new KeyValuePair>[10]; - dict.CopyTo(copyToResult, 0); - Assert.Equal("Bucket1", copyToResult[0].Key); - Assert.Empty(copyToResult[0].Value); - - Assert.Single(dict); Assert.Equal(1, dict.Count(val => val.Key == "Bucket1")); foreach (var item in dict) @@ -96,47 +72,19 @@ public void SingleBucketDictionary_OneBucketEmptyTest() Assert.Equal("Bucket1", item.Key); Assert.Empty(item.Value); } - - Assert.Single(dict.Keys); - foreach (var key in dict.Keys) - { - Assert.Equal("Bucket1", key); - } - - Assert.Single(dict.Values); - foreach (var val in dict.Values) - { - Assert.Empty(val); - } - - Assert.Empty(dict["Bucket1"]); - - Assert.True(dict.TryGetValue("Bucket1", out bucket) && bucket.Count == 0); - Assert.False(dict.TryGetValue(string.Empty, out bucket) || bucket != null); - Assert.False(dict.TryGetValue(null, out bucket) || bucket != null); - Assert.Throws(() => dict[string.Empty] = ArrayHelper.Empty()); } [Fact] public void SingleBucketDictionary_OneBucketOneItem() { IList bucket = new string[] { "Bucket1Item1" }; - SortHelpers.ReadOnlySingleBucketDictionary> dict = new SortHelpers.ReadOnlySingleBucketDictionary>(new KeyValuePair>("Bucket1", bucket)); + SortHelpers.ReadOnlySingleBucketGroupBy> dict = new SortHelpers.ReadOnlySingleBucketGroupBy>(new KeyValuePair>("Bucket1", bucket)); Assert.Single(dict); Assert.Contains(new KeyValuePair>("Bucket1", bucket), dict); Assert.DoesNotContain(new KeyValuePair>("Bucket1", null), dict); Assert.DoesNotContain(new KeyValuePair>(string.Empty, bucket), dict); - Assert.True(dict.ContainsKey("Bucket1")); - Assert.False(dict.ContainsKey(string.Empty)); - - KeyValuePair>[] copyToResult = new KeyValuePair>[10]; - dict.CopyTo(copyToResult, 0); - Assert.Equal("Bucket1", copyToResult[0].Key); - Assert.Single(copyToResult[0].Value); - Assert.Equal("Bucket1Item1", copyToResult[0].Value[0]); - Assert.Single(dict); Assert.Equal(1, dict.Count(val => val.Key == "Bucket1")); @@ -146,48 +94,19 @@ public void SingleBucketDictionary_OneBucketOneItem() Assert.Single(item.Value); Assert.Equal("Bucket1Item1", item.Value[0]); } - - Assert.Single(dict.Keys); - foreach (var key in dict.Keys) - { - Assert.Equal("Bucket1", key); - } - - Assert.Single(dict.Values); - foreach (var val in dict.Values) - { - Assert.Single(val); - Assert.Equal("Bucket1Item1", val[0]); - } - - Assert.Single(dict["Bucket1"]); - Assert.True(dict.TryGetValue("Bucket1", out bucket) && bucket.Count == 1); - Assert.False(dict.TryGetValue(string.Empty, out bucket) || bucket != null); - Assert.False(dict.TryGetValue(null, out bucket) || bucket != null); - Assert.Throws(() => dict[string.Empty] = ArrayHelper.Empty()); } [Fact] public void SingleBucketDictionary_OneBucketTwoItemsTest() { IList bucket = new string[] { "Bucket1Item1", "Bucket1Item2" }; - SortHelpers.ReadOnlySingleBucketDictionary> dict = new SortHelpers.ReadOnlySingleBucketDictionary>(new KeyValuePair>("Bucket1", bucket)); + SortHelpers.ReadOnlySingleBucketGroupBy> dict = new SortHelpers.ReadOnlySingleBucketGroupBy>(new KeyValuePair>("Bucket1", bucket)); Assert.Single(dict); Assert.Contains(new KeyValuePair>("Bucket1", bucket), dict); Assert.DoesNotContain(new KeyValuePair>("Bucket1", null), dict); Assert.DoesNotContain(new KeyValuePair>(string.Empty, bucket), dict); - Assert.True(dict.ContainsKey("Bucket1")); - Assert.False(dict.ContainsKey(string.Empty)); - - KeyValuePair>[] copyToResult = new KeyValuePair>[10]; - dict.CopyTo(copyToResult, 0); - Assert.Equal("Bucket1", copyToResult[0].Key); - Assert.Equal(2, copyToResult[0].Value.Count); - Assert.Equal("Bucket1Item1", copyToResult[0].Value[0]); - Assert.Equal("Bucket1Item2", copyToResult[0].Value[1]); - Assert.Single(dict); Assert.Equal(1, dict.Count(val => val.Key == "Bucket1")); @@ -198,26 +117,6 @@ public void SingleBucketDictionary_OneBucketTwoItemsTest() Assert.Equal("Bucket1Item1", item.Value[0]); Assert.Equal("Bucket1Item2", item.Value[1]); } - - Assert.Single(dict.Keys); - foreach (var key in dict.Keys) - { - Assert.Equal("Bucket1", key); - } - - Assert.Single(dict.Values); - foreach (var val in dict.Values) - { - Assert.Equal(2, val.Count); - Assert.Equal("Bucket1Item1", val[0]); - Assert.Equal("Bucket1Item2", val[1]); - } - - Assert.Equal(2, dict["Bucket1"].Count); - Assert.True(dict.TryGetValue("Bucket1", out bucket) && bucket.Count == 2); - Assert.False(dict.TryGetValue(string.Empty, out bucket) || bucket != null); - Assert.False(dict.TryGetValue(null, out bucket) || bucket != null); - Assert.Throws(() => dict[string.Empty] = ArrayHelper.Empty()); } [Fact] @@ -229,7 +128,7 @@ public void SingleBucketDictionary_TwoBucketEmptyTest() buckets["Bucket1"] = bucket1; buckets["Bucket2"] = bucket2; - SortHelpers.ReadOnlySingleBucketDictionary> dict = new SortHelpers.ReadOnlySingleBucketDictionary>(buckets); + SortHelpers.ReadOnlySingleBucketGroupBy> dict = new SortHelpers.ReadOnlySingleBucketGroupBy>(buckets); Assert.Equal(2, dict.Count); Assert.Contains(new KeyValuePair>("Bucket1", bucket1), dict); @@ -240,17 +139,6 @@ public void SingleBucketDictionary_TwoBucketEmptyTest() Assert.DoesNotContain(new KeyValuePair>("Bucket2", null), dict); Assert.DoesNotContain(new KeyValuePair>(string.Empty, bucket2), dict); - Assert.True(dict.ContainsKey("Bucket1")); - Assert.True(dict.ContainsKey("Bucket2")); - Assert.False(dict.ContainsKey(string.Empty)); - - KeyValuePair>[] copyToResult = new KeyValuePair>[10]; - dict.CopyTo(copyToResult, 0); - Assert.Equal("Bucket1", copyToResult[0].Key); - Assert.Equal("Bucket2", copyToResult[1].Key); - Assert.Empty(copyToResult[0].Value); - Assert.Empty(copyToResult[1].Value); - Assert.Equal(2, dict.Count()); Assert.Equal(1, dict.Count(val => val.Key == "Bucket1")); Assert.Equal(1, dict.Count(val => val.Key == "Bucket2")); @@ -260,24 +148,6 @@ public void SingleBucketDictionary_TwoBucketEmptyTest() Assert.True(item.Key == "Bucket1" || item.Key == "Bucket2"); Assert.Empty(item.Value); } - - Assert.Equal(2, dict.Keys.Count); - foreach (var key in dict.Keys) - { - Assert.True(key == "Bucket1" || key == "Bucket2"); - } - - Assert.Equal(2, dict.Values.Count); - foreach (var val in dict.Values) - { - Assert.Empty(val); - } - - Assert.Empty(dict["Bucket1"]); - Assert.Empty(dict["Bucket2"]); - Assert.True(dict.TryGetValue("Bucket1", out bucket1) && bucket1.Count == 0); - Assert.True(dict.TryGetValue("Bucket2", out bucket2) && bucket2.Count == 0); - Assert.Throws(() => dict[string.Empty] = ArrayHelper.Empty()); } [Fact] @@ -289,7 +159,7 @@ public void SingleBucketDictionary_TwoBuckettOneItemTest() buckets["Bucket1"] = bucket1; buckets["Bucket2"] = bucket2; - SortHelpers.ReadOnlySingleBucketDictionary> dict = new SortHelpers.ReadOnlySingleBucketDictionary>(buckets); + SortHelpers.ReadOnlySingleBucketGroupBy> dict = new SortHelpers.ReadOnlySingleBucketGroupBy>(buckets); Assert.Equal(2, dict.Count); Assert.Contains(new KeyValuePair>("Bucket1", bucket1), dict); @@ -300,17 +170,6 @@ public void SingleBucketDictionary_TwoBuckettOneItemTest() Assert.DoesNotContain(new KeyValuePair>("Bucket2", null), dict); Assert.DoesNotContain(new KeyValuePair>(string.Empty, bucket2), dict); - Assert.True(dict.ContainsKey("Bucket1")); - Assert.True(dict.ContainsKey("Bucket2")); - Assert.False(dict.ContainsKey(string.Empty)); - - KeyValuePair>[] copyToResult = new KeyValuePair>[10]; - dict.CopyTo(copyToResult, 0); - Assert.Equal("Bucket1", copyToResult[0].Key); - Assert.Equal("Bucket2", copyToResult[1].Key); - Assert.Single(copyToResult[0].Value); - Assert.Single(copyToResult[1].Value); - Assert.Equal(2, dict.Count()); Assert.Equal(1, dict.Count(val => val.Key == "Bucket1")); Assert.Equal(1, dict.Count(val => val.Key == "Bucket2")); @@ -321,25 +180,6 @@ public void SingleBucketDictionary_TwoBuckettOneItemTest() Assert.Single(item.Value); Assert.Equal("Bucket1Item1", item.Value[0]); } - - Assert.Equal(2, dict.Keys.Count); - foreach (var key in dict.Keys) - { - Assert.True(key == "Bucket1" || key == "Bucket2"); - } - - Assert.Equal(2, dict.Values.Count); - foreach (var val in dict.Values) - { - Assert.Single(val); - Assert.Equal("Bucket1Item1", val[0]); - } - - Assert.Single(dict["Bucket1"]); - Assert.Single(dict["Bucket2"]); - Assert.True(dict.TryGetValue("Bucket1", out bucket1) && bucket1.Count == 1); - Assert.True(dict.TryGetValue("Bucket2", out bucket2) && bucket2.Count == 1); - Assert.Throws(() => dict[string.Empty] = ArrayHelper.Empty()); } } } diff --git a/tests/NLog.UnitTests/Internal/XmlParserTests.cs b/tests/NLog.UnitTests/Internal/XmlParserTests.cs index d3086d17c8..5e35c4afdc 100644 --- a/tests/NLog.UnitTests/Internal/XmlParserTests.cs +++ b/tests/NLog.UnitTests/Internal/XmlParserTests.cs @@ -183,8 +183,8 @@ public void XmlParse_EmptyDocument(string xmlSource) var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); Assert.NotNull(xmlDocument); Assert.Equal("nlog", xmlDocument.Name.ToLower()); - Assert.Null(xmlDocument.Children); - Assert.Null(xmlDocument.Attributes); + Assert.Empty(xmlDocument.Children); + Assert.Empty(xmlDocument.Attributes); } [Theory] @@ -206,7 +206,7 @@ public void XmlParse_Attributes(string xmlSource) var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); Assert.NotNull(xmlDocument); Assert.Equal("nlog", xmlDocument.Name); - Assert.Null(xmlDocument.Children); + Assert.Empty(xmlDocument.Children); Assert.Single(xmlDocument.Attributes); Assert.Equal("throwExceptions", xmlDocument.Attributes[0].Key); Assert.Equal("false", xmlDocument.Attributes[0].Value); @@ -222,7 +222,7 @@ public void XmlParse_Attributes_Multiple(string xmlSource) var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); Assert.NotNull(xmlDocument); Assert.Equal("nlog", xmlDocument.Name); - Assert.Null(xmlDocument.Children); + Assert.Empty(xmlDocument.Children); Assert.Equal(2, xmlDocument.Attributes.Count); Assert.Equal("throwExceptions", xmlDocument.Attributes[0].Key); Assert.Equal("false", xmlDocument.Attributes[0].Value); @@ -250,7 +250,7 @@ public void XmlParse_Attributes_Tokens(string xmlSource, string value) var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); Assert.NotNull(xmlDocument); Assert.Equal("nlog", xmlDocument.Name); - Assert.Null(xmlDocument.Children); + Assert.Empty(xmlDocument.Children); Assert.Single(xmlDocument.Attributes); Assert.Equal("internalLogFile", xmlDocument.Attributes[0].Key); Assert.Equal(value, xmlDocument.Attributes[0].Value); @@ -282,8 +282,8 @@ public void XmlParse_InnerText_Tokens(string xmlSource, string value) var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); Assert.NotNull(xmlDocument); Assert.Equal("nlog", xmlDocument.Name); - Assert.Null(xmlDocument.Children); - Assert.Null(xmlDocument.Attributes); + Assert.Empty(xmlDocument.Children); + Assert.Empty(xmlDocument.Attributes); Assert.Equal(value, xmlDocument.InnerText); } @@ -313,7 +313,6 @@ public void XmlParse_Children_Multiple(string xmlSource) var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); Assert.NotNull(xmlDocument); Assert.Equal("nlog", xmlDocument.Name); - Assert.NotNull(xmlDocument.Children); Assert.Equal(2, xmlDocument.Children.Count); Assert.Equal("variable", xmlDocument.Children[0].Name); Assert.Equal("variable", xmlDocument.Children[1].Name); From 871a75471fa4a8c16b7dd08075a06fb77450ed85 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 10 May 2025 22:54:36 +0200 Subject: [PATCH 111/224] Config classes with nullable references (#5811) --- src/NLog/Common/AsyncContinuation.cs | 4 +- src/NLog/Common/AsyncHelpers.cs | 12 +- src/NLog/Common/AsyncLogEventInfo.cs | 2 + src/NLog/Common/AsynchronousAction.cs | 2 + src/NLog/Common/ConversionHelpers.cs | 4 +- src/NLog/Common/IInternalLoggerContext.cs | 6 +- .../Common/InternalEventOccurredHandler.cs | 4 +- src/NLog/Common/InternalLogEventArgs.cs | 10 +- src/NLog/Common/InternalLogger-generated.cs | 112 +++--- src/NLog/Common/InternalLogger-generated.tt | 20 +- src/NLog/Common/InternalLogger.cs | 63 ++-- src/NLog/Common/LogEventInfoBuffer.cs | 2 + src/NLog/Config/AdvancedAttribute.cs | 2 + .../Config/AppDomainFixedOutputAttribute.cs | 2 + src/NLog/Config/ArrayParameterAttribute.cs | 3 +- src/NLog/Config/AssemblyExtensionLoader.cs | 22 +- src/NLog/Config/AssemblyExtensionTypes.cs | 2 + src/NLog/Config/AssemblyExtensionTypes.tt | 2 + src/NLog/Config/ConfigSectionHandler.cs | 8 +- src/NLog/Config/ConfigurationItemCreator.cs | 46 --- src/NLog/Config/ConfigurationItemFactory.cs | 28 +- src/NLog/Config/DefaultParameterAttribute.cs | 3 +- src/NLog/Config/DynamicLogLevelFilter.cs | 20 +- src/NLog/Config/DynamicRangeLevelFilter.cs | 33 +- src/NLog/Config/Factory.cs | 19 +- src/NLog/Config/IFactory.cs | 4 +- src/NLog/Config/IIncludeContext.cs | 2 + src/NLog/Config/IInstallable.cs | 2 + .../Config/ILoggingConfigurationElement.cs | 2 + .../Config/ILoggingConfigurationLoader.cs | 6 +- src/NLog/Config/ILoggingRuleLevelFilter.cs | 4 +- src/NLog/Config/IPropertyTypeConverter.cs | 4 +- src/NLog/Config/ISetupBuilder.cs | 2 + .../ISetupConfigurationLoggingRuleBuilder.cs | 2 + .../ISetupConfigurationTargetBuilder.cs | 2 + src/NLog/Config/ISetupExtensionsBuilder.cs | 2 + .../Config/ISetupInternalLoggerBuilder.cs | 2 + .../Config/ISetupLoadConfigurationBuilder.cs | 2 + src/NLog/Config/ISetupLogFactoryBuilder.cs | 2 + src/NLog/Config/ISetupSerializationBuilder.cs | 2 + src/NLog/Config/IUsesStackTrace.cs | 2 + src/NLog/Config/InstallationContext.cs | 8 +- src/NLog/Config/LoggerNameMatcher.cs | 12 +- src/NLog/Config/LoggingConfiguration.cs | 88 ++--- .../LoggingConfigurationElementExtensions.cs | 32 +- .../Config/LoggingConfigurationFileLoader.cs | 16 +- src/NLog/Config/LoggingConfigurationParser.cs | 324 +++++++++--------- src/NLog/Config/LoggingRule.cs | 14 +- src/NLog/Config/LoggingRuleLevelFilter.cs | 8 +- src/NLog/Config/MethodFactory.cs | 22 +- src/NLog/Config/MutableUnsafeAttribute.cs | 2 + ...LogConfigurationIgnorePropertyAttribute.cs | 2 + .../Config/NLogConfigurationItemAttribute.cs | 3 +- .../Config/NLogDependencyResolveException.cs | 8 +- src/NLog/Config/NameBaseAttribute.cs | 9 +- src/NLog/Config/PropertyTypeConverter.cs | 32 +- src/NLog/Config/RequiredParameterAttribute.cs | 3 +- .../ServiceRepositoryUpdateEventArgs.cs | 7 +- src/NLog/Config/SimpleConfigurator.cs | 2 + src/NLog/Config/ThreadAgnosticAttribute.cs | 2 + .../ThreadAgnosticImmutableAttribute.cs | 2 + src/NLog/Config/ThreadSafeAttribute.cs | 2 + src/NLog/Config/XmlLoggingConfiguration.cs | 54 +-- .../Config/XmlLoggingConfigurationElement.cs | 33 +- .../Config/XmlParserConfigurationElement.cs | 44 ++- src/NLog/Config/XmlParserException.cs | 4 +- src/NLog/Internal/PropertyHelper.cs | 8 +- src/NLog/Internal/SingleCallContinuation.cs | 4 +- src/NLog/Internal/TargetWithFilterChain.cs | 2 +- src/NLog/Internal/TimeoutContinuation.cs | 2 +- src/NLog/LogFactory.cs | 2 +- .../Config/RuleConfigurationTests.cs | 4 +- tests/NLog.UnitTests/Config/XmlConfigTests.cs | 4 +- 73 files changed, 666 insertions(+), 566 deletions(-) delete mode 100644 src/NLog/Config/ConfigurationItemCreator.cs diff --git a/src/NLog/Common/AsyncContinuation.cs b/src/NLog/Common/AsyncContinuation.cs index 1f8f7c7d06..cb759240a1 100644 --- a/src/NLog/Common/AsyncContinuation.cs +++ b/src/NLog/Common/AsyncContinuation.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Common { using System; @@ -41,5 +43,5 @@ namespace NLog.Common /// /// Exception during asynchronous processing or null if no exception /// was thrown. - public delegate void AsyncContinuation(Exception exception); + public delegate void AsyncContinuation(Exception? exception); } diff --git a/src/NLog/Common/AsyncHelpers.cs b/src/NLog/Common/AsyncHelpers.cs index b91779ee36..cf42f45b9e 100644 --- a/src/NLog/Common/AsyncHelpers.cs +++ b/src/NLog/Common/AsyncHelpers.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Common { using System; @@ -50,7 +52,7 @@ internal static int GetManagedThreadId() return Thread.CurrentThread.ManagedThreadId; } - internal static void StartAsyncTask(WaitCallback asyncDelegate, object state) + internal static void StartAsyncTask(WaitCallback asyncDelegate, object? state) { ThreadPool.QueueUserWorkItem(asyncDelegate, state); } @@ -77,7 +79,7 @@ public static void ForEachItemSequentially(IEnumerable items, AsyncContinu IEnumerator enumerator = items.GetEnumerator(); - void InvokeNext(Exception ex) + void InvokeNext(Exception? ex) { if (ex != null) { @@ -109,7 +111,7 @@ public static void Repeat(int repeatCount, AsyncContinuation asyncContinuation, action = ExceptionGuard(action); int remaining = repeatCount; - void InvokeNext(Exception ex) + void InvokeNext(Exception? ex) { if (ex != null) { @@ -256,7 +258,7 @@ public static void ForEachItemInParallel(IEnumerable values, AsyncContinua public static void RunSynchronously(AsynchronousAction action) { var ev = new ManualResetEvent(false); - Exception lastException = null; + Exception? lastException = null; action(PreventMultipleCalls(ex => { lastException = ex; ev.Set(); })); ev.WaitOne(); @@ -287,7 +289,7 @@ public static AsyncContinuation PreventMultipleCalls(AsyncContinuation asyncCont /// /// The exceptions. /// Combined exception or null if no exception was thrown. - public static Exception GetCombinedException(IList exceptions) + public static Exception? GetCombinedException(IList exceptions) { if (exceptions.Count == 0) { diff --git a/src/NLog/Common/AsyncLogEventInfo.cs b/src/NLog/Common/AsyncLogEventInfo.cs index 454b8c234a..a5be70ca43 100644 --- a/src/NLog/Common/AsyncLogEventInfo.cs +++ b/src/NLog/Common/AsyncLogEventInfo.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Common { /// diff --git a/src/NLog/Common/AsynchronousAction.cs b/src/NLog/Common/AsynchronousAction.cs index 0397832be4..3ac7febde5 100644 --- a/src/NLog/Common/AsynchronousAction.cs +++ b/src/NLog/Common/AsynchronousAction.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Common { /// diff --git a/src/NLog/Common/ConversionHelpers.cs b/src/NLog/Common/ConversionHelpers.cs index 9e3d0397ac..b72011c73c 100644 --- a/src/NLog/Common/ConversionHelpers.cs +++ b/src/NLog/Common/ConversionHelpers.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Common { using System; @@ -64,7 +66,7 @@ public static class ConversionHelpers /// Input value /// The type of the enum /// Output value. Null if parse failed - internal static bool TryParseEnum(string inputValue, Type enumType, out object resultValue) + internal static bool TryParseEnum(string inputValue, Type enumType, out object? resultValue) { if (StringHelpers.IsNullOrWhiteSpace(inputValue)) { diff --git a/src/NLog/Common/IInternalLoggerContext.cs b/src/NLog/Common/IInternalLoggerContext.cs index eb454502e8..211f375e31 100644 --- a/src/NLog/Common/IInternalLoggerContext.cs +++ b/src/NLog/Common/IInternalLoggerContext.cs @@ -31,10 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using JetBrains.Annotations; +#nullable enable namespace NLog.Common { + using JetBrains.Annotations; + /// /// Enables to extract extra context details for /// @@ -49,6 +51,6 @@ internal interface IInternalLoggerContext /// The current LogFactory next to LogManager /// [CanBeNull] - LogFactory LogFactory { get; } + LogFactory? LogFactory { get; } } } diff --git a/src/NLog/Common/InternalEventOccurredHandler.cs b/src/NLog/Common/InternalEventOccurredHandler.cs index 47363f774e..914048f64f 100644 --- a/src/NLog/Common/InternalEventOccurredHandler.cs +++ b/src/NLog/Common/InternalEventOccurredHandler.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Common { /// @@ -39,5 +41,5 @@ namespace NLog.Common /// /// Never use/call NLog Logger-objects when handling these internal events, as it will lead to deadlock / stackoverflow. /// - public delegate void InternalEventOccurredHandler(object sender, InternalLogEventArgs e); + public delegate void InternalEventOccurredHandler(object? sender, InternalLogEventArgs e); } diff --git a/src/NLog/Common/InternalLogEventArgs.cs b/src/NLog/Common/InternalLogEventArgs.cs index 1372d36536..928e8f4a97 100644 --- a/src/NLog/Common/InternalLogEventArgs.cs +++ b/src/NLog/Common/InternalLogEventArgs.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Common { using System; @@ -55,23 +57,23 @@ public readonly struct InternalLogEventArgs /// The exception. Could be null. /// [CanBeNull] - public Exception Exception { get; } + public Exception? Exception { get; } /// /// The type that triggered this internal log event, for example the FileTarget. /// This property is not always populated. /// [CanBeNull] - public Type SenderType { get; } + public Type? SenderType { get; } /// /// The context name that triggered this internal log event, for example the name of the Target. /// This property is not always populated. /// [CanBeNull] - public string SenderName { get; } + public string? SenderName { get; } - internal InternalLogEventArgs(string message, LogLevel level, [CanBeNull] Exception exception, [CanBeNull] Type senderType, [CanBeNull] string senderName) + internal InternalLogEventArgs(string message, LogLevel level, [CanBeNull] Exception? exception, [CanBeNull] Type? senderType, [CanBeNull] string? senderName) { Message = message; Level = level; diff --git a/src/NLog/Common/InternalLogger-generated.cs b/src/NLog/Common/InternalLogger-generated.cs index 04b2a080ac..cffe363f10 100644 --- a/src/NLog/Common/InternalLogger-generated.cs +++ b/src/NLog/Common/InternalLogger-generated.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Common { using System; @@ -77,7 +79,7 @@ public static partial class InternalLogger /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Trace([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public static void Trace([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsTraceEnabled) Write(null, LogLevel.Trace, message, args.IsEmpty ? null : args.ToArray()); @@ -90,7 +92,7 @@ public static void Trace([Localizable(false)][StructuredMessageTemplate] string /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Trace(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + public static void Trace(Exception? ex, [Localizable(false)] string message, params ReadOnlySpan args) { if (IsTraceEnabled) Write(ex, LogLevel.Trace, message, args.IsEmpty ? null : args.ToArray()); @@ -103,7 +105,7 @@ public static void Trace(Exception ex, [Localizable(false)] string message, para /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Trace([Localizable(false)] string message, params object[] args) + public static void Trace([Localizable(false)] string message, params object?[] args) { Write(null, LogLevel.Trace, message, args); } @@ -137,7 +139,7 @@ public static void Trace(Func messageFunc) /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Trace(Exception ex, [Localizable(false)] string message, params object[] args) + public static void Trace(Exception? ex, [Localizable(false)] string message, params object?[] args) { Write(ex, LogLevel.Trace, message, args); } @@ -149,7 +151,7 @@ public static void Trace(Exception ex, [Localizable(false)] string message, para /// Message which may include positional parameters. /// Argument {0} to the message. [StringFormatMethod("message")] - public static void Trace([Localizable(false)] string message, TArgument1 arg0) + public static void Trace([Localizable(false)] string message, TArgument1? arg0) { if (IsTraceEnabled) Log(null, LogLevel.Trace, message, arg0); @@ -164,7 +166,7 @@ public static void Trace([Localizable(false)] string message, TArgum /// Argument {0} to the message. /// Argument {1} to the message. [StringFormatMethod("message")] - public static void Trace([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1) + public static void Trace([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1) { if (IsTraceEnabled) Log(null, LogLevel.Trace, message, arg0, arg1); @@ -181,7 +183,7 @@ public static void Trace([Localizable(false)] string mes /// Argument {1} to the message. /// Argument {2} to the message. [StringFormatMethod("message")] - public static void Trace([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1, TArgument3 arg2) + public static void Trace([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1, TArgument3? arg2) { if (IsTraceEnabled) Log(null, LogLevel.Trace, message, arg0, arg1, arg2); @@ -192,7 +194,7 @@ public static void Trace([Localizable(false) /// /// Exception to be logged. /// Log message. - public static void Trace(Exception ex, [Localizable(false)] string message) + public static void Trace(Exception? ex, [Localizable(false)] string message) { Write(ex, LogLevel.Trace, message, null); } @@ -205,7 +207,7 @@ public static void Trace(Exception ex, [Localizable(false)] string message) /// Function that returns the log message. [Obsolete("Avoid delegate capture allocations. Marked obsolete with v5.3")] [EditorBrowsable(EditorBrowsableState.Never)] - public static void Trace(Exception ex, Func messageFunc) + public static void Trace(Exception? ex, Func messageFunc) { if (IsTraceEnabled) Write(ex, LogLevel.Trace, messageFunc(), null); @@ -218,7 +220,7 @@ public static void Trace(Exception ex, Func messageFunc) /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Debug([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public static void Debug([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsDebugEnabled) Write(null, LogLevel.Debug, message, args.IsEmpty ? null : args.ToArray()); @@ -231,7 +233,7 @@ public static void Debug([Localizable(false)][StructuredMessageTemplate] string /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Debug(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + public static void Debug(Exception? ex, [Localizable(false)] string message, params ReadOnlySpan args) { if (IsDebugEnabled) Write(ex, LogLevel.Debug, message, args.IsEmpty ? null : args.ToArray()); @@ -244,7 +246,7 @@ public static void Debug(Exception ex, [Localizable(false)] string message, para /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Debug([Localizable(false)] string message, params object[] args) + public static void Debug([Localizable(false)] string message, params object?[] args) { Write(null, LogLevel.Debug, message, args); } @@ -278,7 +280,7 @@ public static void Debug(Func messageFunc) /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Debug(Exception ex, [Localizable(false)] string message, params object[] args) + public static void Debug(Exception? ex, [Localizable(false)] string message, params object?[] args) { Write(ex, LogLevel.Debug, message, args); } @@ -290,7 +292,7 @@ public static void Debug(Exception ex, [Localizable(false)] string message, para /// Message which may include positional parameters. /// Argument {0} to the message. [StringFormatMethod("message")] - public static void Debug([Localizable(false)] string message, TArgument1 arg0) + public static void Debug([Localizable(false)] string message, TArgument1? arg0) { if (IsDebugEnabled) Log(null, LogLevel.Debug, message, arg0); @@ -305,7 +307,7 @@ public static void Debug([Localizable(false)] string message, TArgum /// Argument {0} to the message. /// Argument {1} to the message. [StringFormatMethod("message")] - public static void Debug([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1) + public static void Debug([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1) { if (IsDebugEnabled) Log(null, LogLevel.Debug, message, arg0, arg1); @@ -322,7 +324,7 @@ public static void Debug([Localizable(false)] string mes /// Argument {1} to the message. /// Argument {2} to the message. [StringFormatMethod("message")] - public static void Debug([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1, TArgument3 arg2) + public static void Debug([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1, TArgument3? arg2) { if (IsDebugEnabled) Log(null, LogLevel.Debug, message, arg0, arg1, arg2); @@ -333,7 +335,7 @@ public static void Debug([Localizable(false) /// /// Exception to be logged. /// Log message. - public static void Debug(Exception ex, [Localizable(false)] string message) + public static void Debug(Exception? ex, [Localizable(false)] string message) { Write(ex, LogLevel.Debug, message, null); } @@ -346,7 +348,7 @@ public static void Debug(Exception ex, [Localizable(false)] string message) /// Function that returns the log message. [Obsolete("Avoid delegate capture allocations. Marked obsolete with v5.3")] [EditorBrowsable(EditorBrowsableState.Never)] - public static void Debug(Exception ex, Func messageFunc) + public static void Debug(Exception? ex, Func messageFunc) { if (IsDebugEnabled) Write(ex, LogLevel.Debug, messageFunc(), null); @@ -359,7 +361,7 @@ public static void Debug(Exception ex, Func messageFunc) /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Info([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public static void Info([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsInfoEnabled) Write(null, LogLevel.Info, message, args.IsEmpty ? null : args.ToArray()); @@ -372,7 +374,7 @@ public static void Info([Localizable(false)][StructuredMessageTemplate] string m /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Info(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + public static void Info(Exception? ex, [Localizable(false)] string message, params ReadOnlySpan args) { if (IsInfoEnabled) Write(ex, LogLevel.Info, message, args.IsEmpty ? null : args.ToArray()); @@ -385,7 +387,7 @@ public static void Info(Exception ex, [Localizable(false)] string message, param /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Info([Localizable(false)] string message, params object[] args) + public static void Info([Localizable(false)] string message, params object?[] args) { Write(null, LogLevel.Info, message, args); } @@ -419,7 +421,7 @@ public static void Info(Func messageFunc) /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Info(Exception ex, [Localizable(false)] string message, params object[] args) + public static void Info(Exception? ex, [Localizable(false)] string message, params object?[] args) { Write(ex, LogLevel.Info, message, args); } @@ -431,7 +433,7 @@ public static void Info(Exception ex, [Localizable(false)] string message, param /// Message which may include positional parameters. /// Argument {0} to the message. [StringFormatMethod("message")] - public static void Info([Localizable(false)] string message, TArgument1 arg0) + public static void Info([Localizable(false)] string message, TArgument1? arg0) { if (IsInfoEnabled) Log(null, LogLevel.Info, message, arg0); @@ -446,7 +448,7 @@ public static void Info([Localizable(false)] string message, TArgume /// Argument {0} to the message. /// Argument {1} to the message. [StringFormatMethod("message")] - public static void Info([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1) + public static void Info([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1) { if (IsInfoEnabled) Log(null, LogLevel.Info, message, arg0, arg1); @@ -463,7 +465,7 @@ public static void Info([Localizable(false)] string mess /// Argument {1} to the message. /// Argument {2} to the message. [StringFormatMethod("message")] - public static void Info([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1, TArgument3 arg2) + public static void Info([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1, TArgument3? arg2) { if (IsInfoEnabled) Log(null, LogLevel.Info, message, arg0, arg1, arg2); @@ -474,7 +476,7 @@ public static void Info([Localizable(false)] /// /// Exception to be logged. /// Log message. - public static void Info(Exception ex, [Localizable(false)] string message) + public static void Info(Exception? ex, [Localizable(false)] string message) { Write(ex, LogLevel.Info, message, null); } @@ -487,7 +489,7 @@ public static void Info(Exception ex, [Localizable(false)] string message) /// Function that returns the log message. [Obsolete("Avoid delegate capture allocations. Marked obsolete with v5.3")] [EditorBrowsable(EditorBrowsableState.Never)] - public static void Info(Exception ex, Func messageFunc) + public static void Info(Exception? ex, Func messageFunc) { if (IsInfoEnabled) Write(ex, LogLevel.Info, messageFunc(), null); @@ -500,7 +502,7 @@ public static void Info(Exception ex, Func messageFunc) /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Warn([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public static void Warn([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsWarnEnabled) Write(null, LogLevel.Warn, message, args.IsEmpty ? null : args.ToArray()); @@ -513,7 +515,7 @@ public static void Warn([Localizable(false)][StructuredMessageTemplate] string m /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Warn(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + public static void Warn(Exception? ex, [Localizable(false)] string message, params ReadOnlySpan args) { if (IsWarnEnabled) Write(ex, LogLevel.Warn, message, args.IsEmpty ? null : args.ToArray()); @@ -526,7 +528,7 @@ public static void Warn(Exception ex, [Localizable(false)] string message, param /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Warn([Localizable(false)] string message, params object[] args) + public static void Warn([Localizable(false)] string message, params object?[] args) { Write(null, LogLevel.Warn, message, args); } @@ -560,7 +562,7 @@ public static void Warn(Func messageFunc) /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Warn(Exception ex, [Localizable(false)] string message, params object[] args) + public static void Warn(Exception? ex, [Localizable(false)] string message, params object?[] args) { Write(ex, LogLevel.Warn, message, args); } @@ -572,7 +574,7 @@ public static void Warn(Exception ex, [Localizable(false)] string message, param /// Message which may include positional parameters. /// Argument {0} to the message. [StringFormatMethod("message")] - public static void Warn([Localizable(false)] string message, TArgument1 arg0) + public static void Warn([Localizable(false)] string message, TArgument1? arg0) { if (IsWarnEnabled) Log(null, LogLevel.Warn, message, arg0); @@ -587,7 +589,7 @@ public static void Warn([Localizable(false)] string message, TArgume /// Argument {0} to the message. /// Argument {1} to the message. [StringFormatMethod("message")] - public static void Warn([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1) + public static void Warn([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1) { if (IsWarnEnabled) Log(null, LogLevel.Warn, message, arg0, arg1); @@ -604,7 +606,7 @@ public static void Warn([Localizable(false)] string mess /// Argument {1} to the message. /// Argument {2} to the message. [StringFormatMethod("message")] - public static void Warn([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1, TArgument3 arg2) + public static void Warn([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1, TArgument3? arg2) { if (IsWarnEnabled) Log(null, LogLevel.Warn, message, arg0, arg1, arg2); @@ -615,7 +617,7 @@ public static void Warn([Localizable(false)] /// /// Exception to be logged. /// Log message. - public static void Warn(Exception ex, [Localizable(false)] string message) + public static void Warn(Exception? ex, [Localizable(false)] string message) { Write(ex, LogLevel.Warn, message, null); } @@ -628,7 +630,7 @@ public static void Warn(Exception ex, [Localizable(false)] string message) /// Function that returns the log message. [Obsolete("Avoid delegate capture allocations. Marked obsolete with v5.3")] [EditorBrowsable(EditorBrowsableState.Never)] - public static void Warn(Exception ex, Func messageFunc) + public static void Warn(Exception? ex, Func messageFunc) { if (IsWarnEnabled) Write(ex, LogLevel.Warn, messageFunc(), null); @@ -641,7 +643,7 @@ public static void Warn(Exception ex, Func messageFunc) /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Error([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public static void Error([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsErrorEnabled) Write(null, LogLevel.Error, message, args.IsEmpty ? null : args.ToArray()); @@ -654,7 +656,7 @@ public static void Error([Localizable(false)][StructuredMessageTemplate] string /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Error(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + public static void Error(Exception? ex, [Localizable(false)] string message, params ReadOnlySpan args) { if (IsErrorEnabled) Write(ex, LogLevel.Error, message, args.IsEmpty ? null : args.ToArray()); @@ -667,7 +669,7 @@ public static void Error(Exception ex, [Localizable(false)] string message, para /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Error([Localizable(false)] string message, params object[] args) + public static void Error([Localizable(false)] string message, params object?[] args) { Write(null, LogLevel.Error, message, args); } @@ -701,7 +703,7 @@ public static void Error(Func messageFunc) /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Error(Exception ex, [Localizable(false)] string message, params object[] args) + public static void Error(Exception? ex, [Localizable(false)] string message, params object?[] args) { Write(ex, LogLevel.Error, message, args); } @@ -713,7 +715,7 @@ public static void Error(Exception ex, [Localizable(false)] string message, para /// Message which may include positional parameters. /// Argument {0} to the message. [StringFormatMethod("message")] - public static void Error([Localizable(false)] string message, TArgument1 arg0) + public static void Error([Localizable(false)] string message, TArgument1? arg0) { if (IsErrorEnabled) Log(null, LogLevel.Error, message, arg0); @@ -728,7 +730,7 @@ public static void Error([Localizable(false)] string message, TArgum /// Argument {0} to the message. /// Argument {1} to the message. [StringFormatMethod("message")] - public static void Error([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1) + public static void Error([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1) { if (IsErrorEnabled) Log(null, LogLevel.Error, message, arg0, arg1); @@ -745,7 +747,7 @@ public static void Error([Localizable(false)] string mes /// Argument {1} to the message. /// Argument {2} to the message. [StringFormatMethod("message")] - public static void Error([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1, TArgument3 arg2) + public static void Error([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1, TArgument3? arg2) { if (IsErrorEnabled) Log(null, LogLevel.Error, message, arg0, arg1, arg2); @@ -756,7 +758,7 @@ public static void Error([Localizable(false) /// /// Exception to be logged. /// Log message. - public static void Error(Exception ex, [Localizable(false)] string message) + public static void Error(Exception? ex, [Localizable(false)] string message) { Write(ex, LogLevel.Error, message, null); } @@ -769,7 +771,7 @@ public static void Error(Exception ex, [Localizable(false)] string message) /// Function that returns the log message. [Obsolete("Avoid delegate capture allocations. Marked obsolete with v5.3")] [EditorBrowsable(EditorBrowsableState.Never)] - public static void Error(Exception ex, Func messageFunc) + public static void Error(Exception? ex, Func messageFunc) { if (IsErrorEnabled) Write(ex, LogLevel.Error, messageFunc(), null); @@ -782,7 +784,7 @@ public static void Error(Exception ex, Func messageFunc) /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Fatal([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public static void Fatal([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (IsFatalEnabled) Write(null, LogLevel.Fatal, message, args.IsEmpty ? null : args.ToArray()); @@ -795,7 +797,7 @@ public static void Fatal([Localizable(false)][StructuredMessageTemplate] string /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Fatal(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + public static void Fatal(Exception? ex, [Localizable(false)] string message, params ReadOnlySpan args) { if (IsFatalEnabled) Write(ex, LogLevel.Fatal, message, args.IsEmpty ? null : args.ToArray()); @@ -808,7 +810,7 @@ public static void Fatal(Exception ex, [Localizable(false)] string message, para /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Fatal([Localizable(false)] string message, params object[] args) + public static void Fatal([Localizable(false)] string message, params object?[] args) { Write(null, LogLevel.Fatal, message, args); } @@ -842,7 +844,7 @@ public static void Fatal(Func messageFunc) /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Fatal(Exception ex, [Localizable(false)] string message, params object[] args) + public static void Fatal(Exception? ex, [Localizable(false)] string message, params object?[] args) { Write(ex, LogLevel.Fatal, message, args); } @@ -854,7 +856,7 @@ public static void Fatal(Exception ex, [Localizable(false)] string message, para /// Message which may include positional parameters. /// Argument {0} to the message. [StringFormatMethod("message")] - public static void Fatal([Localizable(false)] string message, TArgument1 arg0) + public static void Fatal([Localizable(false)] string message, TArgument1? arg0) { if (IsFatalEnabled) Log(null, LogLevel.Fatal, message, arg0); @@ -869,7 +871,7 @@ public static void Fatal([Localizable(false)] string message, TArgum /// Argument {0} to the message. /// Argument {1} to the message. [StringFormatMethod("message")] - public static void Fatal([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1) + public static void Fatal([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1) { if (IsFatalEnabled) Log(null, LogLevel.Fatal, message, arg0, arg1); @@ -886,7 +888,7 @@ public static void Fatal([Localizable(false)] string mes /// Argument {1} to the message. /// Argument {2} to the message. [StringFormatMethod("message")] - public static void Fatal([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1, TArgument3 arg2) + public static void Fatal([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1, TArgument3? arg2) { if (IsFatalEnabled) Log(null, LogLevel.Fatal, message, arg0, arg1, arg2); @@ -897,7 +899,7 @@ public static void Fatal([Localizable(false) /// /// Exception to be logged. /// Log message. - public static void Fatal(Exception ex, [Localizable(false)] string message) + public static void Fatal(Exception? ex, [Localizable(false)] string message) { Write(ex, LogLevel.Fatal, message, null); } @@ -910,10 +912,10 @@ public static void Fatal(Exception ex, [Localizable(false)] string message) /// Function that returns the log message. [Obsolete("Avoid delegate capture allocations. Marked obsolete with v5.3")] [EditorBrowsable(EditorBrowsableState.Never)] - public static void Fatal(Exception ex, Func messageFunc) + public static void Fatal(Exception? ex, Func messageFunc) { if (IsFatalEnabled) Write(ex, LogLevel.Fatal, messageFunc(), null); } } -} \ No newline at end of file +} diff --git a/src/NLog/Common/InternalLogger-generated.tt b/src/NLog/Common/InternalLogger-generated.tt index 63c5c25b9d..3439149877 100644 --- a/src/NLog/Common/InternalLogger-generated.tt +++ b/src/NLog/Common/InternalLogger-generated.tt @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Common { using System; @@ -63,7 +65,7 @@ namespace NLog.Common /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) + public static void <#=level#>([Localizable(false)][StructuredMessageTemplate] string message, params ReadOnlySpan args) { if (Is<#=level#>Enabled) Write(null, LogLevel.<#=level#>, message, args.IsEmpty ? null : args.ToArray()); @@ -76,7 +78,7 @@ namespace NLog.Common /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void <#=level#>(Exception ex, [Localizable(false)] string message, params ReadOnlySpan args) + public static void <#=level#>(Exception? ex, [Localizable(false)] string message, params ReadOnlySpan args) { if (Is<#=level#>Enabled) Write(ex, LogLevel.<#=level#>, message, args.IsEmpty ? null : args.ToArray()); @@ -89,7 +91,7 @@ namespace NLog.Common /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void <#=level#>([Localizable(false)] string message, params object[] args) + public static void <#=level#>([Localizable(false)] string message, params object?[] args) { Write(null, LogLevel.<#=level#>, message, args); } @@ -123,7 +125,7 @@ namespace NLog.Common /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void <#=level#>(Exception ex, [Localizable(false)] string message, params object[] args) + public static void <#=level#>(Exception? ex, [Localizable(false)] string message, params object?[] args) { Write(ex, LogLevel.<#=level#>, message, args); } @@ -135,7 +137,7 @@ namespace NLog.Common /// Message which may include positional parameters. /// Argument {0} to the message. [StringFormatMethod("message")] - public static void <#=level#>([Localizable(false)] string message, TArgument1 arg0) + public static void <#=level#>([Localizable(false)] string message, TArgument1? arg0) { if (Is<#=level#>Enabled) Log(null, LogLevel.<#=level#>, message, arg0); @@ -150,7 +152,7 @@ namespace NLog.Common /// Argument {0} to the message. /// Argument {1} to the message. [StringFormatMethod("message")] - public static void <#=level#>([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1) + public static void <#=level#>([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1) { if (Is<#=level#>Enabled) Log(null, LogLevel.<#=level#>, message, arg0, arg1); @@ -167,7 +169,7 @@ namespace NLog.Common /// Argument {1} to the message. /// Argument {2} to the message. [StringFormatMethod("message")] - public static void <#=level#>([Localizable(false)] string message, TArgument1 arg0, TArgument2 arg1, TArgument3 arg2) + public static void <#=level#>([Localizable(false)] string message, TArgument1? arg0, TArgument2? arg1, TArgument3? arg2) { if (Is<#=level#>Enabled) Log(null, LogLevel.<#=level#>, message, arg0, arg1, arg2); @@ -178,7 +180,7 @@ namespace NLog.Common /// /// Exception to be logged. /// Log message. - public static void <#=level#>(Exception ex, [Localizable(false)] string message) + public static void <#=level#>(Exception? ex, [Localizable(false)] string message) { Write(ex, LogLevel.<#=level#>, message, null); } @@ -191,7 +193,7 @@ namespace NLog.Common /// Function that returns the log message. [Obsolete("Avoid delegate capture allocations. Marked obsolete with v5.3")] [EditorBrowsable(EditorBrowsableState.Never)] - public static void <#=level#>(Exception ex, Func messageFunc) + public static void <#=level#>(Exception? ex, Func messageFunc) { if (Is<#=level#>Enabled) Write(ex, LogLevel.<#=level#>, messageFunc(), null); diff --git a/src/NLog/Common/InternalLogger.cs b/src/NLog/Common/InternalLogger.cs index 803d76ba52..4f5e1906f7 100644 --- a/src/NLog/Common/InternalLogger.cs +++ b/src/NLog/Common/InternalLogger.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Common { using System; @@ -64,7 +66,7 @@ public static void Reset() IncludeTimestamp = true; LogToConsole = false; LogToConsoleError = false; - LogFile = string.Empty; + LogFile = null; } /// @@ -118,7 +120,7 @@ public static bool LogToConsoleError /// Gets or sets the file path of the internal log file. /// /// A value of value disables internal logging to a file. - public static string LogFile + public static string? LogFile { get { @@ -135,19 +137,20 @@ public static string LogFile _logFile = value; } - if (!string.IsNullOrEmpty(value)) + var logFile = (value != null && !string.IsNullOrEmpty(value)) ? ExpandFilePathVariables(value) : null; + _logFile = logFile; + if (logFile != null) { - _logFile = ExpandFilePathVariables(value); - CreateDirectoriesIfNeeded(_logFile); + CreateDirectoriesIfNeeded(logFile); } } } - private static string _logFile; + private static string? _logFile; /// /// Gets or sets the text writer that will receive internal logs. /// - public static TextWriter LogWriter { get; set; } + public static TextWriter? LogWriter { get; set; } /// /// Internal LogEvent written to the InternalLogger @@ -157,7 +160,7 @@ public static string LogFile /// /// Never use/call NLog Logger-objects when handling these internal events, as it will lead to deadlock / stackoverflow. /// - public static event InternalEventOccurredHandler InternalEventOccurred; + public static event InternalEventOccurredHandler? InternalEventOccurred; /// /// Gets or sets a value indicating whether timestamp should be included in internal log output. @@ -176,7 +179,7 @@ public static string LogFile /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Log(LogLevel level, [Localizable(false)] string message, params object[] args) + public static void Log(LogLevel level, [Localizable(false)] string message, params object?[] args) { Write(null, level, message, args); } @@ -189,7 +192,7 @@ public static void Log(LogLevel level, [Localizable(false)] string message, para /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Log(LogLevel level, [Localizable(false)] string message, params ReadOnlySpan args) + public static void Log(LogLevel level, [Localizable(false)] string message, params ReadOnlySpan args) { if (IsLogLevelEnabled(level)) Write(null, level, message, args.IsEmpty ? null : args.ToArray()); @@ -203,7 +206,7 @@ public static void Log(LogLevel level, [Localizable(false)] string message, para /// Message which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Log(Exception ex, LogLevel level, [Localizable(false)] string message, params ReadOnlySpan args) + public static void Log(Exception? ex, LogLevel level, [Localizable(false)] string message, params ReadOnlySpan args) { if (IsLogLevelEnabled(level)) Write(ex, level, message, args.IsEmpty ? null : args.ToArray()); @@ -241,7 +244,7 @@ public static void Log(LogLevel level, [Localizable(false)] Func message /// Exception to be logged. /// Log level. /// Function that returns the log message. - public static void Log(Exception ex, LogLevel level, [Localizable(false)] Func messageFunc) + public static void Log(Exception? ex, LogLevel level, [Localizable(false)] Func messageFunc) { if (IsLogLevelEnabled(level)) { @@ -257,7 +260,7 @@ public static void Log(Exception ex, LogLevel level, [Localizable(false)] FuncMessage which may include positional parameters. /// Arguments to the message. [StringFormatMethod("message")] - public static void Log(Exception ex, LogLevel level, [Localizable(false)] string message, params object[] args) + public static void Log(Exception? ex, LogLevel level, [Localizable(false)] string message, params object?[] args) { Write(ex, level, message, args); } @@ -268,7 +271,7 @@ public static void Log(Exception ex, LogLevel level, [Localizable(false)] string /// Exception to be logged. /// Log level. /// Log message. - public static void Log(Exception ex, LogLevel level, [Localizable(false)] string message) + public static void Log(Exception? ex, LogLevel level, [Localizable(false)] string message) { Write(ex, level, message, null); } @@ -280,7 +283,7 @@ public static void Log(Exception ex, LogLevel level, [Localizable(false)] string /// level /// message /// optional args for - private static void Write([CanBeNull] Exception ex, LogLevel level, string message, [CanBeNull] object[] args) + private static void Write([CanBeNull] Exception? ex, LogLevel level, string message, [CanBeNull] object?[]? args) { if (!IsLogLevelEnabled(level)) { @@ -332,7 +335,7 @@ private static void Write([CanBeNull] Exception ex, LogLevel level, string messa } } - private static void WriteToLog(LogLevel level, Exception ex, string fullMessage, IInternalLoggerContext loggerContext) + private static void WriteToLog(LogLevel level, Exception? ex, string fullMessage, IInternalLoggerContext? loggerContext) { if (LogWriter != null) { @@ -345,7 +348,7 @@ private static void WriteToLog(LogLevel level, Exception ex, string fullMessage, if (InternalEventOccurred != null) { - var loggerContextName = string.IsNullOrEmpty(loggerContext?.Name) ? loggerContext?.ToString() : loggerContext.Name; + var loggerContextName = (loggerContext is null || string.IsNullOrEmpty(loggerContext.Name)) ? loggerContext?.ToString() : loggerContext.Name; InternalEventOccurred?.Invoke(null, new InternalLogEventArgs(fullMessage, level, ex, loggerContext?.GetType(), loggerContextName)); } } @@ -353,7 +356,7 @@ private static void WriteToLog(LogLevel level, Exception ex, string fullMessage, /// /// Create log line with timestamp, exception message etc (if configured) /// - private static string CreateLogLine([CanBeNull] Exception ex, LogLevel level, string fullMessage) + private static string CreateLogLine([CanBeNull] Exception? ex, LogLevel level, string fullMessage) { const string timeStampFormat = "yyyy-MM-dd HH:mm:ss.ffff"; const string fieldSeparator = " "; @@ -385,7 +388,7 @@ private static string CreateLogLine([CanBeNull] Exception ex, LogLevel level, st /// /// The exception to check. /// true if logging should be avoided; otherwise, false. - private static bool IsSeriousException(Exception exception) + private static bool IsSeriousException(Exception? exception) { return exception != null && exception.MustBeRethrownImmediately(); } @@ -442,19 +445,19 @@ private static string ExpandFilePathVariables(string internalLogFile) { try { - if (ContainsSubStringIgnoreCase(internalLogFile, "${currentdir}", out string currentDirToken)) + if (ContainsSubStringIgnoreCase(internalLogFile, "${currentdir}", out var currentDirToken)) internalLogFile = internalLogFile.Replace(currentDirToken, System.IO.Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar.ToString()); - if (ContainsSubStringIgnoreCase(internalLogFile, "${basedir}", out string baseDirToken)) + if (ContainsSubStringIgnoreCase(internalLogFile, "${basedir}", out var baseDirToken)) internalLogFile = internalLogFile.Replace(baseDirToken, LogManager.LogFactory.CurrentAppEnvironment.AppDomainBaseDirectory + System.IO.Path.DirectorySeparatorChar.ToString()); - if (ContainsSubStringIgnoreCase(internalLogFile, "${tempdir}", out string tempDirToken)) + if (ContainsSubStringIgnoreCase(internalLogFile, "${tempdir}", out var tempDirToken)) internalLogFile = internalLogFile.Replace(tempDirToken, LogManager.LogFactory.CurrentAppEnvironment.UserTempFilePath + System.IO.Path.DirectorySeparatorChar.ToString()); - if (ContainsSubStringIgnoreCase(internalLogFile, "${processdir}", out string processDirToken)) + if (ContainsSubStringIgnoreCase(internalLogFile, "${processdir}", out var processDirToken)) internalLogFile = internalLogFile.Replace(processDirToken, System.IO.Path.GetDirectoryName(LogManager.LogFactory.CurrentAppEnvironment.CurrentProcessFilePath) + System.IO.Path.DirectorySeparatorChar.ToString()); - if (ContainsSubStringIgnoreCase(internalLogFile, "${commonApplicationDataDir}", out string commonAppDataDirToken)) + if (ContainsSubStringIgnoreCase(internalLogFile, "${commonApplicationDataDir}", out var commonAppDataDirToken)) internalLogFile = internalLogFile.Replace(commonAppDataDirToken, NLog.LayoutRenderers.SpecialFolderLayoutRenderer.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + System.IO.Path.DirectorySeparatorChar.ToString()); - if (ContainsSubStringIgnoreCase(internalLogFile, "${userApplicationDataDir}", out string appDataDirToken)) + if (ContainsSubStringIgnoreCase(internalLogFile, "${userApplicationDataDir}", out var appDataDirToken)) internalLogFile = internalLogFile.Replace(appDataDirToken, NLog.LayoutRenderers.SpecialFolderLayoutRenderer.GetFolderPath(Environment.SpecialFolder.ApplicationData) + System.IO.Path.DirectorySeparatorChar.ToString()); - if (ContainsSubStringIgnoreCase(internalLogFile, "${userLocalApplicationDataDir}", out string localapplicationdatadir)) + if (ContainsSubStringIgnoreCase(internalLogFile, "${userLocalApplicationDataDir}", out var localapplicationdatadir)) internalLogFile = internalLogFile.Replace(localapplicationdatadir, NLog.LayoutRenderers.SpecialFolderLayoutRenderer.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + System.IO.Path.DirectorySeparatorChar.ToString()); if (internalLogFile.IndexOf('%') >= 0) internalLogFile = Environment.ExpandEnvironmentVariables(internalLogFile); @@ -470,26 +473,26 @@ private static string ExpandFilePathVariables(string internalLogFile) } } - private static bool ContainsSubStringIgnoreCase(string haystack, string needle, out string result) + private static bool ContainsSubStringIgnoreCase(string haystack, string needle, out string? result) { int needlePos = haystack.IndexOf(needle, StringComparison.OrdinalIgnoreCase); result = needlePos >= 0 ? haystack.Substring(needlePos, needle.Length) : null; return result != null; } - private static void LogToConsoleSubscription(object sender, InternalLogEventArgs eventArgs) + private static void LogToConsoleSubscription(object? sender, InternalLogEventArgs eventArgs) { var logLine = CreateLogLine(eventArgs.Exception, eventArgs.Level, eventArgs.Message); NLog.Targets.ConsoleTargetHelper.WriteLineThreadSafe(Console.Out, logLine); } - private static void LogToConsoleErrorSubscription(object sender, InternalLogEventArgs eventArgs) + private static void LogToConsoleErrorSubscription(object? sender, InternalLogEventArgs eventArgs) { var logLine = CreateLogLine(eventArgs.Exception, eventArgs.Level, eventArgs.Message); NLog.Targets.ConsoleTargetHelper.WriteLineThreadSafe(Console.Error, logLine); } - private static void LogToFileSubscription(object sender, InternalLogEventArgs eventArgs) + private static void LogToFileSubscription(object? sender, InternalLogEventArgs eventArgs) { var logLine = CreateLogLine(eventArgs.Exception, eventArgs.Level, eventArgs.Message); lock (LockObject) diff --git a/src/NLog/Common/LogEventInfoBuffer.cs b/src/NLog/Common/LogEventInfoBuffer.cs index e5e420f08d..b7d8a13e97 100644 --- a/src/NLog/Common/LogEventInfoBuffer.cs +++ b/src/NLog/Common/LogEventInfoBuffer.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Common { using System; diff --git a/src/NLog/Config/AdvancedAttribute.cs b/src/NLog/Config/AdvancedAttribute.cs index b0a632efed..c2bbcd45b8 100644 --- a/src/NLog/Config/AdvancedAttribute.cs +++ b/src/NLog/Config/AdvancedAttribute.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; diff --git a/src/NLog/Config/AppDomainFixedOutputAttribute.cs b/src/NLog/Config/AppDomainFixedOutputAttribute.cs index 9cfc5634d2..3face60b14 100644 --- a/src/NLog/Config/AppDomainFixedOutputAttribute.cs +++ b/src/NLog/Config/AppDomainFixedOutputAttribute.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; diff --git a/src/NLog/Config/ArrayParameterAttribute.cs b/src/NLog/Config/ArrayParameterAttribute.cs index 6f5959a0d5..b4bea5c056 100644 --- a/src/NLog/Config/ArrayParameterAttribute.cs +++ b/src/NLog/Config/ArrayParameterAttribute.cs @@ -31,11 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using JetBrains.Annotations; +#nullable enable namespace NLog.Config { using System; + using JetBrains.Annotations; /// /// Used to mark configurable parameters which are arrays. diff --git a/src/NLog/Config/AssemblyExtensionLoader.cs b/src/NLog/Config/AssemblyExtensionLoader.cs index f86e6301bc..90d02ce160 100644 --- a/src/NLog/Config/AssemblyExtensionLoader.cs +++ b/src/NLog/Config/AssemblyExtensionLoader.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -164,9 +166,9 @@ private static bool SkipAlreadyLoadedAssembly(ConfigurationItemFactory factory, return false; } - private static Dictionary ResolveLoadedAssemblyTypes(ConfigurationItemFactory factory) + private static Dictionary ResolveLoadedAssemblyTypes(ConfigurationItemFactory factory) { - var loadedAssemblies = new Dictionary(); + var loadedAssemblies = new Dictionary(); foreach (var itemType in factory.ItemTypes) { var assembly = itemType.Assembly; @@ -189,7 +191,7 @@ private static Dictionary ResolveLoadedAssemblyTypes(Configurati return loadedAssemblies; } - private static bool IsNLogConfigurationItemType(Type itemType) + private static bool IsNLogConfigurationItemType(Type? itemType) { if (itemType is null) { @@ -219,7 +221,7 @@ private static bool IsNLogConfigurationItemType(Type itemType) return false; } - private static bool IsNLogItemTypeAlreadyRegistered(ConfigurationItemFactory factory, Type itemType, string itemNamePrefix) + private static bool IsNLogItemTypeAlreadyRegistered(ConfigurationItemFactory factory, Type? itemType, string itemNamePrefix) { if (itemType is null) { @@ -249,16 +251,16 @@ private static bool IsNLogItemTypeAlreadyRegistered(IFact where TAttribute : NameBaseAttribute where TBaseType : class { - var nameAttribute = itemType.GetFirstCustomAttribute(); - if (!string.IsNullOrEmpty(nameAttribute?.Name)) + var nameAttribute = itemType.GetFirstCustomAttribute()?.Name ?? string.Empty; + if (!string.IsNullOrEmpty(nameAttribute)) { - var typeAlias = string.IsNullOrEmpty(itemNamePrefix) ? nameAttribute.Name : itemNamePrefix + nameAttribute.Name; + var typeAlias = string.IsNullOrEmpty(itemNamePrefix) ? nameAttribute : itemNamePrefix + nameAttribute; return factory.TryCreateInstance(typeAlias, out var _); } return false; } - public void LoadAssemblyFromPath(ConfigurationItemFactory factory, string assemblyFileName, string baseDirectory, string itemNamePrefix) + public void LoadAssemblyFromPath(ConfigurationItemFactory factory, string assemblyFileName, string? baseDirectory, string itemNamePrefix) { var assembly = LoadAssemblyFromPath(assemblyFileName, baseDirectory); if (assembly != null) @@ -466,7 +468,7 @@ private static string[] GetNLogExtensionFiles(string assemblyLocation) /// [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2026")] [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - private static Assembly LoadAssemblyFromPath(string assemblyFileName, string baseDirectory = null) + private static Assembly LoadAssemblyFromPath(string assemblyFileName, string? baseDirectory = null) { string fullFileName = assemblyFileName; if (!string.IsNullOrEmpty(baseDirectory)) @@ -525,7 +527,7 @@ internal interface IAssemblyExtensionLoader { void ScanForAutoLoadExtensions(ConfigurationItemFactory factory); - void LoadAssemblyFromPath(ConfigurationItemFactory factory, string assemblyFileName, string baseDirectory, string itemNamePrefix); + void LoadAssemblyFromPath(ConfigurationItemFactory factory, string assemblyFileName, string? baseDirectory, string itemNamePrefix); void LoadAssemblyFromName(ConfigurationItemFactory factory, string assemblyName, string itemNamePrefix); diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index 5fb32260c3..526353f9c5 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { /// diff --git a/src/NLog/Config/AssemblyExtensionTypes.tt b/src/NLog/Config/AssemblyExtensionTypes.tt index 6a1d70d35a..0c83ff3574 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.tt +++ b/src/NLog/Config/AssemblyExtensionTypes.tt @@ -41,6 +41,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { /// diff --git a/src/NLog/Config/ConfigSectionHandler.cs b/src/NLog/Config/ConfigSectionHandler.cs index 12e1f31714..6d8778f020 100644 --- a/src/NLog/Config/ConfigSectionHandler.cs +++ b/src/NLog/Config/ConfigSectionHandler.cs @@ -33,6 +33,8 @@ #if NETFRAMEWORK +#nullable enable + namespace NLog.Config { using System; @@ -51,13 +53,13 @@ namespace NLog.Config /// public sealed class ConfigSectionHandler : ConfigurationSection { - private XmlLoggingConfiguration _config; + private XmlLoggingConfiguration? _config; /// /// Gets the default object by parsing /// the application configuration file (app.exe.config). /// - public static LoggingConfiguration AppConfig + public static LoggingConfiguration? AppConfig { get { @@ -112,7 +114,7 @@ protected override void DeserializeElement(XmlReader reader, bool serializeColle /// /// A instance, that has been deserialized from app.config. /// - protected override object GetRuntimeObject() + protected override object? GetRuntimeObject() { return _config; } diff --git a/src/NLog/Config/ConfigurationItemCreator.cs b/src/NLog/Config/ConfigurationItemCreator.cs deleted file mode 100644 index d8e1bea7a3..0000000000 --- a/src/NLog/Config/ConfigurationItemCreator.cs +++ /dev/null @@ -1,46 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -namespace NLog.Config -{ - using System; - - /// - /// Obsolete since dynamic tyope loading is not compatible with publish as trimmed application. - /// Constructs a new instance the configuration item (target, layout, layout renderer, etc.) given its type. - /// - /// Type of the item. - /// Created object of the specified type. - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - public delegate object ConfigurationItemCreator(Type itemType); -} diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index 5f33e713bd..860a891324 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -54,7 +56,7 @@ namespace NLog.Config /// public sealed class ConfigurationItemFactory { - private static ConfigurationItemFactory _defaultInstance; + private static ConfigurationItemFactory? _defaultInstance; internal static readonly object SyncRoot = new object(); @@ -75,9 +77,9 @@ public sealed class ConfigurationItemFactory private struct ItemFactory { public readonly Func> ItemProperties; - public readonly Func ItemCreator; + public readonly Func ItemCreator; - public ItemFactory(Func> itemProperties, Func itemCreator) + public ItemFactory(Func> itemProperties, Func itemCreator) { ItemProperties = itemProperties; ItemCreator = itemCreator; @@ -337,13 +339,13 @@ public void Clear() /// The item name prefix. [Obsolete("Instead use NLog.LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] [EditorBrowsable(EditorBrowsableState.Never)] - public void RegisterType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] Type type, string itemNamePrefix) + public void RegisterType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicMethods)] Type type, string? itemNamePrefix) { lock (SyncRoot) { foreach (IFactory f in _allFactories) { - f.RegisterType(type, itemNamePrefix); + f.RegisterType(type, itemNamePrefix ?? string.Empty); } } } @@ -361,13 +363,13 @@ public void RegisterType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTy } } - internal void RegisterTypeProperties<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] TType>(Func itemCreator) + internal void RegisterTypeProperties<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] TType>(Func itemCreator) { lock (SyncRoot) { if (!_itemFactories.ContainsKey(typeof(TType))) { - Dictionary properties = null; + Dictionary? properties = null; var itemProperties = new Func>(() => properties ?? (properties = typeof(TType).GetProperties(BindingFlags.Public | BindingFlags.Instance).ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase))); var itemFactory = new ItemFactory(itemProperties, itemCreator); _itemFactories[typeof(TType)] = itemFactory; @@ -375,13 +377,13 @@ public void RegisterType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTy } } - internal void RegisterTypeProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] Type itemType, Func itemCreator) + internal void RegisterTypeProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] Type itemType, Func itemCreator) { lock (SyncRoot) { if (!_itemFactories.ContainsKey(itemType)) { - Dictionary properties = null; + Dictionary? properties = null; var itemProperties = new Func>(() => properties ?? (properties = itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase))); var itemFactory = new ItemFactory(itemProperties, itemCreator); _itemFactories[itemType] = itemFactory; @@ -425,9 +427,9 @@ private Dictionary ResolveTypePropertiesLegacy(Type itemTy return properties; } - internal bool TryCreateInstance(Type itemType, out object instance) + internal bool TryCreateInstance(Type itemType, out object? instance) { - Func itemCreator = null; + Func? itemCreator = null; lock (SyncRoot) { @@ -454,9 +456,9 @@ internal bool TryCreateInstance(Type itemType, out object instance) [UnconditionalSuppressMessage("Trimming - Ignore since obsolete", "IL2070")] [UnconditionalSuppressMessage("Trimming - Ignore since obsolete", "IL2072")] [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] - private object ResolveCreateInstanceLegacy(Type itemType) + private object? ResolveCreateInstanceLegacy(Type itemType) { - Dictionary properties = null; + Dictionary? properties = null; var itemProperties = new Func>(() => properties ?? (properties = itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase))); var itemFactory = new ItemFactory(itemProperties, () => Activator.CreateInstance(itemType)); lock (SyncRoot) diff --git a/src/NLog/Config/DefaultParameterAttribute.cs b/src/NLog/Config/DefaultParameterAttribute.cs index 8f11b15bcb..cad2d7c8a6 100644 --- a/src/NLog/Config/DefaultParameterAttribute.cs +++ b/src/NLog/Config/DefaultParameterAttribute.cs @@ -31,11 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using JetBrains.Annotations; +#nullable enable namespace NLog.Config { using System; + using JetBrains.Annotations; /// /// Attribute used to mark the default parameters for layout renderers. diff --git a/src/NLog/Config/DynamicLogLevelFilter.cs b/src/NLog/Config/DynamicLogLevelFilter.cs index 0d1d95a9c9..588219104e 100644 --- a/src/NLog/Config/DynamicLogLevelFilter.cs +++ b/src/NLog/Config/DynamicLogLevelFilter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -45,15 +47,15 @@ namespace NLog.Config internal sealed class DynamicLogLevelFilter : ILoggingRuleLevelFilter { private readonly LoggingRule _loggingRule; - private readonly SimpleLayout _levelFilter; - private readonly SimpleLayout _finalMinLevelFilter; + private readonly SimpleLayout? _levelFilter; + private readonly SimpleLayout? _finalMinLevelFilter; private KeyValuePair _activeFilter; public bool[] LogLevels => GenerateLogLevels(); - public LogLevel FinalMinLevel => GenerateFinalMinLevel(); + public LogLevel? FinalMinLevel => GenerateFinalMinLevel(); - public DynamicLogLevelFilter(LoggingRule loggingRule, SimpleLayout levelFilter, SimpleLayout finalMinLevelFilter) + public DynamicLogLevelFilter(LoggingRule loggingRule, SimpleLayout? levelFilter, SimpleLayout? finalMinLevelFilter) { _loggingRule = loggingRule; _levelFilter = levelFilter; @@ -68,7 +70,7 @@ public LoggingRuleLevelFilter GetSimpleFilterForUpdate() private bool[] GenerateLogLevels() { - var levelFilter = _levelFilter.Render(LogEventInfo.CreateNullEvent()); + var levelFilter = _levelFilter?.Render(LogEventInfo.CreateNullEvent())?.Trim() ?? string.Empty; if (string.IsNullOrEmpty(levelFilter)) return LoggingRuleLevelFilter.Off.LogLevels; @@ -94,9 +96,9 @@ private bool[] GenerateLogLevels() return activeFilter.Value; } - private LogLevel GenerateFinalMinLevel() + private LogLevel? GenerateFinalMinLevel() { - var levelFilter = _finalMinLevelFilter?.Render(LogEventInfo.CreateNullEvent()); + var levelFilter = _finalMinLevelFilter?.Render(LogEventInfo.CreateNullEvent())?.Trim() ?? string.Empty; return ParseLogLevel(levelFilter, null); } @@ -111,14 +113,14 @@ private bool[] ParseSingleLevel(string levelFilter) return logLevels; } - private LogLevel ParseLogLevel(string logLevel, LogLevel levelIfEmpty) + private LogLevel? ParseLogLevel(string logLevel, LogLevel? levelIfEmpty) { try { if (string.IsNullOrEmpty(logLevel)) return levelIfEmpty; - return LogLevel.FromString(logLevel.Trim()); + return LogLevel.FromString(logLevel); } catch (ArgumentException ex) { diff --git a/src/NLog/Config/DynamicRangeLevelFilter.cs b/src/NLog/Config/DynamicRangeLevelFilter.cs index 4b6effdbcb..9c818856da 100644 --- a/src/NLog/Config/DynamicRangeLevelFilter.cs +++ b/src/NLog/Config/DynamicRangeLevelFilter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -44,16 +46,16 @@ namespace NLog.Config internal sealed class DynamicRangeLevelFilter : ILoggingRuleLevelFilter { private readonly LoggingRule _loggingRule; - private readonly SimpleLayout _minLevel; - private readonly SimpleLayout _maxLevel; - private readonly SimpleLayout _finalMinLevelFilter; + private readonly SimpleLayout? _minLevel; + private readonly SimpleLayout? _maxLevel; + private readonly SimpleLayout? _finalMinLevelFilter; private KeyValuePair _activeFilter; public bool[] LogLevels => GenerateLogLevels(); - public LogLevel FinalMinLevel => GenerateFinalMinLevel(); + public LogLevel? FinalMinLevel => GenerateFinalMinLevel(); - public DynamicRangeLevelFilter(LoggingRule loggingRule, SimpleLayout minLevel, SimpleLayout maxLevel, SimpleLayout finalMinLevelFilter) + public DynamicRangeLevelFilter(LoggingRule loggingRule, SimpleLayout? minLevel, SimpleLayout? maxLevel, SimpleLayout? finalMinLevelFilter) { _loggingRule = loggingRule; _minLevel = minLevel; @@ -69,10 +71,8 @@ public LoggingRuleLevelFilter GetSimpleFilterForUpdate() private bool[] GenerateLogLevels() { - var minLevelFilter = _minLevel?.Render(LogEventInfo.CreateNullEvent()) ?? string.Empty; - var maxLevelFilter = _maxLevel?.Render(LogEventInfo.CreateNullEvent()) ?? string.Empty; - if (string.IsNullOrEmpty(minLevelFilter) && string.IsNullOrEmpty(maxLevelFilter)) - return LoggingRuleLevelFilter.Off.LogLevels; + var minLevelFilter = _minLevel?.Render(LogEventInfo.CreateNullEvent())?.Trim() ?? string.Empty; + var maxLevelFilter = _maxLevel?.Render(LogEventInfo.CreateNullEvent())?.Trim() ?? string.Empty; var activeFilter = _activeFilter; if (!activeFilter.Key.Equals(new MinMaxLevels(minLevelFilter, maxLevelFilter))) @@ -83,16 +83,19 @@ private bool[] GenerateLogLevels() return activeFilter.Value; } - private LogLevel GenerateFinalMinLevel() + private LogLevel? GenerateFinalMinLevel() { - var levelFilter = _finalMinLevelFilter?.Render(LogEventInfo.CreateNullEvent()); + var levelFilter = _finalMinLevelFilter?.Render(LogEventInfo.CreateNullEvent())?.Trim() ?? string.Empty; return ParseLogLevel(levelFilter, null); } private bool[] ParseLevelRange(string minLevelFilter, string maxLevelFilter) { - LogLevel minLevel = ParseLogLevel(minLevelFilter, LogLevel.MinLevel); - LogLevel maxLevel = ParseLogLevel(maxLevelFilter, LogLevel.MaxLevel); + if (string.IsNullOrEmpty(minLevelFilter) && string.IsNullOrEmpty(maxLevelFilter)) + return LoggingRuleLevelFilter.Off.LogLevels; + + var minLevel = ParseLogLevel(minLevelFilter, LogLevel.MinLevel); + var maxLevel = ParseLogLevel(maxLevelFilter, LogLevel.MaxLevel); bool[] logLevels = new bool[LogLevel.MaxLevel.Ordinal + 1]; if (minLevel != null && maxLevel != null) @@ -105,14 +108,14 @@ private bool[] ParseLevelRange(string minLevelFilter, string maxLevelFilter) return logLevels; } - private LogLevel ParseLogLevel(string logLevel, LogLevel levelIfEmpty) + private LogLevel? ParseLogLevel(string logLevel, LogLevel? levelIfEmpty) { try { if (string.IsNullOrEmpty(logLevel)) return levelIfEmpty; - return LogLevel.FromString(logLevel.Trim()); + return LogLevel.FromString(logLevel); } catch (ArgumentException ex) { diff --git a/src/NLog/Config/Factory.cs b/src/NLog/Config/Factory.cs index 1cb13c05fd..1647fbbe2c 100644 --- a/src/NLog/Config/Factory.cs +++ b/src/NLog/Config/Factory.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -50,7 +52,7 @@ internal class Factory : where TBaseType : class where TAttributeType : NameBaseAttribute { - private readonly Dictionary> _items; + private readonly Dictionary> _items; private readonly ConfigurationItemFactory _parentFactory; public bool Initialized { get; private set; } @@ -58,7 +60,7 @@ internal class Factory : internal Factory(ConfigurationItemFactory parentFactory) { _parentFactory = parentFactory; - _items = new Dictionary>(16, StringComparer.OrdinalIgnoreCase); + _items = new Dictionary>(16, StringComparer.OrdinalIgnoreCase); } private delegate Type GetTypeDelegate(); @@ -115,7 +117,7 @@ public void RegisterNamedType(string itemName, string typeName) { itemName = FactoryExtensions.NormalizeName(itemName); - Type itemType = null; + Type? itemType = null; GetTypeDelegate typeLookup = () => { @@ -127,7 +129,7 @@ public void RegisterNamedType(string itemName, string typeName) return itemType; }; - Func typeCreator = () => + Func typeCreator = () => { var type = typeLookup(); return type != null ? (TBaseType)Activator.CreateInstance(type) : null; @@ -196,7 +198,7 @@ private void RegisterDefinition([DynamicallyAccessedMembers(DynamicallyAccessedM } } - private bool TryGetItemFactory(string typeAlias, out Func itemFactory) + private bool TryGetItemFactory(string typeAlias, out Func? itemFactory) { lock (ConfigurationItemFactory.SyncRoot) { @@ -205,7 +207,7 @@ private bool TryGetItemFactory(string typeAlias, out Func itemFactory } /// - public virtual bool TryCreateInstance(string typeAlias, out TBaseType result) + public virtual bool TryCreateInstance(string typeAlias, out TBaseType? result) { typeAlias = FactoryExtensions.NormalizeName(typeAlias); @@ -228,6 +230,9 @@ public static TBaseType CreateInstance(this IFactory facto { if (factory.TryCreateInstance(typeAlias, out var result)) { + if (result is null) + throw new NLogConfigurationException($"Failed to create {typeof(TBaseType).Name} of type: '{typeAlias}' - Factory method returned null"); + return result; } } @@ -335,7 +340,7 @@ public void RegisterFuncLayout(string itemName, FuncLayoutRenderer renderer) } /// - public override bool TryCreateInstance(string typeAlias, out LayoutRenderer result) + public override bool TryCreateInstance(string typeAlias, out LayoutRenderer? result) { //first try func renderers, as they should have the possibility to overwrite a current one. typeAlias = FactoryExtensions.NormalizeName(typeAlias); diff --git a/src/NLog/Config/IFactory.cs b/src/NLog/Config/IFactory.cs index 4e7321c853..458cadbd30 100644 --- a/src/NLog/Config/IFactory.cs +++ b/src/NLog/Config/IFactory.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -55,6 +57,6 @@ public interface IFactory where TBaseType : class /// Tries to create an item instance with type-alias /// /// True if instance was created successfully, false otherwise. - bool TryCreateInstance(string typeAlias, out TBaseType result); + bool TryCreateInstance(string typeAlias, out TBaseType? result); } } diff --git a/src/NLog/Config/IIncludeContext.cs b/src/NLog/Config/IIncludeContext.cs index f42bc94049..b9bd8ad570 100644 --- a/src/NLog/Config/IIncludeContext.cs +++ b/src/NLog/Config/IIncludeContext.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { /// diff --git a/src/NLog/Config/IInstallable.cs b/src/NLog/Config/IInstallable.cs index b6d0635201..a949428f36 100644 --- a/src/NLog/Config/IInstallable.cs +++ b/src/NLog/Config/IInstallable.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { /// diff --git a/src/NLog/Config/ILoggingConfigurationElement.cs b/src/NLog/Config/ILoggingConfigurationElement.cs index b0088a8c50..220bcdc494 100644 --- a/src/NLog/Config/ILoggingConfigurationElement.cs +++ b/src/NLog/Config/ILoggingConfigurationElement.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System.Collections.Generic; diff --git a/src/NLog/Config/ILoggingConfigurationLoader.cs b/src/NLog/Config/ILoggingConfigurationLoader.cs index f7c097a605..70ba3af5db 100644 --- a/src/NLog/Config/ILoggingConfigurationLoader.cs +++ b/src/NLog/Config/ILoggingConfigurationLoader.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -47,13 +49,13 @@ internal interface ILoggingConfigurationLoader : IDisposable /// LogFactory that owns the NLog configuration /// Name of NLog.config file (optional) /// NLog configuration (or null if none found) - LoggingConfiguration Load(LogFactory logFactory, string filename = null); + LoggingConfiguration? Load(LogFactory logFactory, string? filename = null); /// /// Get file paths (including filename) for the possible NLog config files. /// /// Name of NLog.config file (optional) /// The file paths to the possible config file - IEnumerable GetDefaultCandidateConfigFilePaths(string filename = null); + IEnumerable GetDefaultCandidateConfigFilePaths(string? filename = null); } } diff --git a/src/NLog/Config/ILoggingRuleLevelFilter.cs b/src/NLog/Config/ILoggingRuleLevelFilter.cs index e1eccdedaf..43324f7bb4 100644 --- a/src/NLog/Config/ILoggingRuleLevelFilter.cs +++ b/src/NLog/Config/ILoggingRuleLevelFilter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { internal interface ILoggingRuleLevelFilter @@ -40,7 +42,7 @@ internal interface ILoggingRuleLevelFilter /// bool[] LogLevels { get; } - LogLevel FinalMinLevel { get; } + LogLevel? FinalMinLevel { get; } /// /// Converts the filter into a simple diff --git a/src/NLog/Config/IPropertyTypeConverter.cs b/src/NLog/Config/IPropertyTypeConverter.cs index 05af05979d..0387e28c97 100644 --- a/src/NLog/Config/IPropertyTypeConverter.cs +++ b/src/NLog/Config/IPropertyTypeConverter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -48,6 +50,6 @@ public interface IPropertyTypeConverter /// Format to use when parsing /// Culture to use when parsing /// Output value with wanted type - object Convert(object propertyValue, Type propertyType, string format, IFormatProvider formatProvider); + object? Convert(object? propertyValue, Type propertyType, string? format, IFormatProvider? formatProvider); } } diff --git a/src/NLog/Config/ISetupBuilder.cs b/src/NLog/Config/ISetupBuilder.cs index fa5001a0a6..90be76f421 100644 --- a/src/NLog/Config/ISetupBuilder.cs +++ b/src/NLog/Config/ISetupBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { /// diff --git a/src/NLog/Config/ISetupConfigurationLoggingRuleBuilder.cs b/src/NLog/Config/ISetupConfigurationLoggingRuleBuilder.cs index c8e95c0e61..44c85f4461 100644 --- a/src/NLog/Config/ISetupConfigurationLoggingRuleBuilder.cs +++ b/src/NLog/Config/ISetupConfigurationLoggingRuleBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { /// diff --git a/src/NLog/Config/ISetupConfigurationTargetBuilder.cs b/src/NLog/Config/ISetupConfigurationTargetBuilder.cs index 9eac334bc9..4f2914a4c9 100644 --- a/src/NLog/Config/ISetupConfigurationTargetBuilder.cs +++ b/src/NLog/Config/ISetupConfigurationTargetBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System.Collections.Generic; diff --git a/src/NLog/Config/ISetupExtensionsBuilder.cs b/src/NLog/Config/ISetupExtensionsBuilder.cs index 18000173c9..8bfbf74e43 100644 --- a/src/NLog/Config/ISetupExtensionsBuilder.cs +++ b/src/NLog/Config/ISetupExtensionsBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { /// diff --git a/src/NLog/Config/ISetupInternalLoggerBuilder.cs b/src/NLog/Config/ISetupInternalLoggerBuilder.cs index 9216062465..a0ff7e6523 100644 --- a/src/NLog/Config/ISetupInternalLoggerBuilder.cs +++ b/src/NLog/Config/ISetupInternalLoggerBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { /// diff --git a/src/NLog/Config/ISetupLoadConfigurationBuilder.cs b/src/NLog/Config/ISetupLoadConfigurationBuilder.cs index 8f6cbcd68c..bb32feea82 100644 --- a/src/NLog/Config/ISetupLoadConfigurationBuilder.cs +++ b/src/NLog/Config/ISetupLoadConfigurationBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { /// diff --git a/src/NLog/Config/ISetupLogFactoryBuilder.cs b/src/NLog/Config/ISetupLogFactoryBuilder.cs index ae7f00950b..4f00aa6db6 100644 --- a/src/NLog/Config/ISetupLogFactoryBuilder.cs +++ b/src/NLog/Config/ISetupLogFactoryBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { /// diff --git a/src/NLog/Config/ISetupSerializationBuilder.cs b/src/NLog/Config/ISetupSerializationBuilder.cs index a86d83e36f..4d49e4e638 100644 --- a/src/NLog/Config/ISetupSerializationBuilder.cs +++ b/src/NLog/Config/ISetupSerializationBuilder.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { /// diff --git a/src/NLog/Config/IUsesStackTrace.cs b/src/NLog/Config/IUsesStackTrace.cs index 39e7590656..a22bf53196 100644 --- a/src/NLog/Config/IUsesStackTrace.cs +++ b/src/NLog/Config/IUsesStackTrace.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { /// diff --git a/src/NLog/Config/InstallationContext.cs b/src/NLog/Config/InstallationContext.cs index d4777ae3a6..afcc3f338e 100644 --- a/src/NLog/Config/InstallationContext.cs +++ b/src/NLog/Config/InstallationContext.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -96,12 +98,12 @@ public InstallationContext(TextWriter logOutput) /// /// Gets the installation parameters. /// - public IDictionary Parameters { get; private set; } + public IDictionary Parameters { get; } /// /// Gets or sets the log output. /// - public TextWriter LogOutput { get; set; } + public TextWriter? LogOutput { get; set; } /// /// Logs the specified trace message. @@ -193,7 +195,7 @@ private void Log(LogLevel logLevel, string message, object[] arguments) try { - LogOutput.WriteLine(message); + LogOutput?.WriteLine(message); } finally { diff --git a/src/NLog/Config/LoggerNameMatcher.cs b/src/NLog/Config/LoggerNameMatcher.cs index be9d0f1cba..7c286f0a64 100644 --- a/src/NLog/Config/LoggerNameMatcher.cs +++ b/src/NLog/Config/LoggerNameMatcher.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -41,8 +43,10 @@ namespace NLog.Config /// All subclasses defines immutable objects. /// Concrete subclasses defines various matching rules through /// - abstract class LoggerNameMatcher + internal abstract class LoggerNameMatcher { + public static LoggerNameMatcher Off => NoneLoggerNameMatcher.Instance; + /// /// Creates a concrete based on . /// @@ -101,9 +105,9 @@ public static LoggerNameMatcher Create(string loggerNamePattern) /// public string Pattern { get; } - protected readonly string _matchingArgument; + protected readonly string? _matchingArgument; private readonly string _toString; - protected LoggerNameMatcher(string pattern, string matchingArgument) + protected LoggerNameMatcher(string pattern, string? matchingArgument) { Pattern = pattern; _matchingArgument = matchingArgument; @@ -131,7 +135,7 @@ private sealed class NoneLoggerNameMatcher : LoggerNameMatcher protected override string MatchMode => "None"; public static readonly NoneLoggerNameMatcher Instance = new NoneLoggerNameMatcher(); private NoneLoggerNameMatcher() - : base(null, null) + : base(string.Empty, null) { } diff --git a/src/NLog/Config/LoggingConfiguration.cs b/src/NLog/Config/LoggingConfiguration.cs index a817992fa5..8ed8938183 100644 --- a/src/NLog/Config/LoggingConfiguration.cs +++ b/src/NLog/Config/LoggingConfiguration.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -120,9 +122,9 @@ public LoggingConfiguration(LogFactory logFactory) private bool TryGetTargetThreadSafe(string name, out Target target) { lock (_targets) return _targets.TryGetValue(name, out target); } private List GetAllTargetsThreadSafe() { lock (_targets) return _targets.Values.ToList(); } - private Target RemoveTargetThreadSafe(string name) + private Target? RemoveTargetThreadSafe(string name) { - Target target; + Target? target; lock (_targets) { if (_targets.TryGetValue(name, out target)) @@ -139,19 +141,15 @@ private Target RemoveTargetThreadSafe(string name) return target; } - private void AddTargetThreadSafe(Target target, string targetAlias = null) + private void AddTargetThreadSafe(Target target, string? targetAlias = null) { - if (string.IsNullOrEmpty(target.Name) && string.IsNullOrEmpty(targetAlias)) - return; - lock (_targets) { - if (string.IsNullOrEmpty(targetAlias)) + if (targetAlias is null || string.IsNullOrEmpty(targetAlias)) { - if (_targets.ContainsKey(target.Name)) + targetAlias = target.Name ?? string.Empty; + if (_targets.ContainsKey(targetAlias)) return; - - targetAlias = target.Name; } if (_targets.TryGetValue(targetAlias, out var oldTarget) && ReferenceEquals(oldTarget, target)) @@ -177,7 +175,7 @@ private void AddTargetThreadSafe(Target target, string targetAlias = null) /// Specific culture info or null to use /// [CanBeNull] - public CultureInfo DefaultCultureInfo { get; set; } + public CultureInfo? DefaultCultureInfo { get; set; } /// /// Gets all targets. @@ -253,15 +251,16 @@ public void AddTarget(string name, [NotNull] Target target) /// /// Found target or when the target is not found. /// - public Target FindTargetByName(string name) + public Target? FindTargetByName(string name) { - Target value; - if (!TryGetTargetThreadSafe(name, out value)) + Guard.ThrowIfNull(name); + + if (TryGetTargetThreadSafe(name, out var target)) { - return null; + return target; } - return value; + return null; } /// @@ -274,9 +273,11 @@ public Target FindTargetByName(string name) /// /// Found target or when the target is not found of not of type /// - public TTarget FindTargetByName(string name) + public TTarget? FindTargetByName(string name) where TTarget : Target { + Guard.ThrowIfNull(name); + var target = FindTargetByName(name); if (target is TTarget specificTarget) { @@ -302,6 +303,8 @@ public TTarget FindTargetByName(string name) /// Logger name pattern. It may include the '*' wildcard at the beginning, at the end or at both ends. public void AddRule(LogLevel minLevel, LogLevel maxLevel, string targetName, string loggerNamePattern = "*") { + Guard.ThrowIfNull(targetName); + var target = FindTargetByName(targetName); if (target is null) { @@ -345,6 +348,7 @@ public void AddRule(LogLevel minLevel, LogLevel maxLevel, Target target, string /// rule object to add public void AddRule(LoggingRule rule) { + Guard.ThrowIfNull(rule); AddLoggingRulesThreadSafe(rule); } @@ -356,6 +360,8 @@ public void AddRule(LoggingRule rule) /// Logger name pattern. It may include the '*' wildcard at the beginning, at the end or at both ends. public void AddRuleForOneLevel(LogLevel level, string targetName, string loggerNamePattern = "*") { + Guard.ThrowIfNull(targetName); + var target = FindTargetByName(targetName); if (target is null) { @@ -400,6 +406,8 @@ public void AddRuleForOneLevel(LogLevel level, Target target, string loggerNameP /// Logger name pattern. It may include the '*' wildcard at the beginning, at the end or at both ends. public void AddRuleForAllLevels(string targetName, string loggerNamePattern = "*") { + Guard.ThrowIfNull(targetName); + var target = FindTargetByName(targetName); if (target is null) { @@ -440,10 +448,9 @@ public void AddRuleForAllLevels(Target target, string loggerNamePattern, bool fi /// /// The name of the logging rule to be found. /// Found logging rule or when not found. - public LoggingRule FindRuleByName(string ruleName) + public LoggingRule? FindRuleByName(string ruleName) { - if (ruleName is null) - return null; + Guard.ThrowIfNull(ruleName); var loggingRules = GetLoggingRulesThreadSafe(); for (int i = loggingRules.Length - 1; i >= 0; i--) @@ -463,8 +470,7 @@ public LoggingRule FindRuleByName(string ruleName) /// Found one or more logging rule to remove, or when not found. public bool RemoveRuleByName(string ruleName) { - if (ruleName is null) - return false; + Guard.ThrowIfNull(ruleName); HashSet removedRules = new HashSet(); var loggingRules = GetLoggingRulesThreadSafe(); @@ -522,7 +528,7 @@ protected void PrepareForReload(LoggingConfiguration oldConfig) /// Notify the configuration when has been assigned / unassigned. /// /// LogFactory that configuration has been assigned to. - protected internal virtual void OnConfigurationAssigned(LogFactory logFactory) + protected internal virtual void OnConfigurationAssigned(LogFactory? logFactory) { if (!ReferenceEquals(logFactory, LogFactory) && logFactory != null) { @@ -539,8 +545,10 @@ protected internal virtual void OnConfigurationAssigned(LogFactory logFactory) /// Name of the target. public void RemoveTarget(string name) { + Guard.ThrowIfNull(name); + HashSet removedTargets = new HashSet(); - Target removedTarget = RemoveTargetThreadSafe(name); + var removedTarget = RemoveTargetThreadSafe(name); if (removedTarget != null) { removedTargets.Add(removedTarget); @@ -569,7 +577,7 @@ public void RemoveTarget(string name) } } - private void CleanupRulesForRemovedTarget(string name, Target removedTarget, HashSet removedTargets) + private void CleanupRulesForRemovedTarget(string name, Target? removedTarget, HashSet removedTargets) { var loggingRules = GetLoggingRulesThreadSafe(); @@ -846,12 +854,13 @@ internal void CheckForMissingServiceTypes(Type serviceType) private static bool IsMissingServiceType(NLogDependencyResolveException resolveException, Type serviceType) { if (resolveException.ServiceType.IsAssignableFrom(serviceType)) + { return true; + } - resolveException = resolveException.InnerException as NLogDependencyResolveException; - if (resolveException != null) + if (resolveException.InnerException is NLogDependencyResolveException dependencyResolveException) { - return IsMissingServiceType(resolveException, serviceType); + return IsMissingServiceType(dependencyResolveException, serviceType); } return false; @@ -878,20 +887,21 @@ private List GetSupportsInitializes(bool reverse = false) /// /// [NotNull] - internal string ExpandSimpleVariables(string input) + internal string ExpandSimpleVariables(string? input) { return ExpandSimpleVariables(input, out var _); } [NotNull] - internal string ExpandSimpleVariables(string input, out string matchingVariableName) + internal string ExpandSimpleVariables(string? input, out string? matchingVariableName) { - string output = input; - var culture = StringComparison.OrdinalIgnoreCase; + var output = input; matchingVariableName = null; - if (Variables.Count > 0 && output?.IndexOf('$') >= 0) + if (output != null && !StringHelpers.IsNullOrWhiteSpace(output) && Variables.Count > 0 && output.IndexOf('$') >= 0) { + var culture = StringComparison.OrdinalIgnoreCase; + foreach (var kvp in _variables) { var layout = kvp.Value; @@ -915,7 +925,7 @@ internal string ExpandSimpleVariables(string input, out string matchingVariableN } else { - if (string.Equals(layoutText, input.Trim(), culture)) + if (string.Equals(layoutText, input?.Trim() ?? string.Empty, culture)) { matchingVariableName = kvp.Key; } @@ -982,7 +992,7 @@ bool IsUnusedInList(Target target1, ILookup targets) InternalLogger.Debug("Unused target checking is completed. Total Rule Count: {0}, Total Target Count: {1}, Unused Target Count: {2}", LoggingRules.Count, configuredNamedTargets.Count, unusedCount); } - internal AsyncContinuation FlushAllTargets(AsyncContinuation flushCompletion) + internal AsyncContinuation? FlushAllTargets(AsyncContinuation flushCompletion) { var pendingTargets = GetAllTargetsToFlush(); if (pendingTargets.Count == 0) @@ -993,9 +1003,9 @@ internal AsyncContinuation FlushAllTargets(AsyncContinuation flushCompletion) InternalLogger.Trace("Flushing all {0} targets...", pendingTargets.Count); - Exception lastException = null; + Exception? lastException = null; - Action flushAction = (t, ex) => + Action flushAction = (t, ex) => { if (ex != null) { @@ -1072,7 +1082,7 @@ private static bool DumpTargetConfigurationForLogger(string loggerName, TargetWi if (targetsByLevel is null) return false; - System.Text.StringBuilder sb = null; + System.Text.StringBuilder? sb = null; for (int i = 0; i <= LogLevel.MaxLevel.Ordinal; ++i) { if (sb != null) @@ -1081,7 +1091,7 @@ private static bool DumpTargetConfigurationForLogger(string loggerName, TargetWi sb.AppendFormat(CultureInfo.InvariantCulture, "Logger {0} [{1}] =>", loggerName, LogLevel.FromOrdinal(i)); } - for (TargetWithFilterChain afc = targetsByLevel[i]; afc != null; afc = afc.NextInChain) + for (TargetWithFilterChain? afc = targetsByLevel[i]; afc != null; afc = afc.NextInChain) { if (sb is null) { diff --git a/src/NLog/Config/LoggingConfigurationElementExtensions.cs b/src/NLog/Config/LoggingConfigurationElementExtensions.cs index 3ead46d7c9..d2c5f12fd2 100644 --- a/src/NLog/Config/LoggingConfigurationElementExtensions.cs +++ b/src/NLog/Config/LoggingConfigurationElementExtensions.cs @@ -31,15 +31,17 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using NLog.Common; -using NLog.Internal; +#nullable enable namespace NLog.Config { + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using NLog.Common; + using NLog.Internal; + internal static class LoggingConfigurationElementExtensions { public static bool MatchesName(this ILoggingConfigurationElement section, string expectedName) @@ -61,7 +63,7 @@ public static void AssertName(this ILoggingConfigurationElement section, params public static string GetRequiredValue(this ILoggingConfigurationElement element, string attributeName, string section) { - string value = element.GetOptionalValue(attributeName, null); + var value = element.GetOptionalValue(attributeName, null); if (value is null) { throw new NLogConfigurationException($"Expected {attributeName} on {element.Name} in {section}"); @@ -76,7 +78,7 @@ public static string GetRequiredValue(this ILoggingConfigurationElement element, return value; } - public static string GetOptionalValue(this ILoggingConfigurationElement element, string attributeName, string defaultValue) + public static string? GetOptionalValue(this ILoggingConfigurationElement element, string attributeName, string? defaultValue) { return element.Values .Where(configItem => string.Equals(configItem.Key, attributeName, StringComparison.OrdinalIgnoreCase)) @@ -93,7 +95,7 @@ public static string GetOptionalValue(this ILoggingConfigurationElement element, public static bool GetOptionalBooleanValue(this ILoggingConfigurationElement element, string attributeName, bool defaultValue) { - string value = element.GetOptionalValue(attributeName, null); + var value = element.GetOptionalValue(attributeName, null)?.Trim() ?? string.Empty; if (string.IsNullOrEmpty(value)) { return defaultValue; @@ -101,7 +103,7 @@ public static bool GetOptionalBooleanValue(this ILoggingConfigurationElement ele try { - return Convert.ToBoolean(value.Trim(), CultureInfo.InvariantCulture); + return Convert.ToBoolean(value, CultureInfo.InvariantCulture); } catch (Exception exception) { @@ -115,10 +117,10 @@ public static bool GetOptionalBooleanValue(this ILoggingConfigurationElement ele } } - public static string GetConfigItemTypeAttribute(this ILoggingConfigurationElement element, string sectionNameForRequiredValue = null) + public static string GetConfigItemTypeAttribute(this ILoggingConfigurationElement element, string? sectionNameForRequiredValue = null) { var typeAttributeValue = sectionNameForRequiredValue != null ? element.GetRequiredValue("type", sectionNameForRequiredValue) : element.GetOptionalValue("type", null); - return StripOptionalNamespacePrefix(typeAttributeValue)?.Trim(); + return StripOptionalNamespacePrefix(typeAttributeValue ?? string.Empty).Trim(); } /// @@ -149,15 +151,11 @@ public static IEnumerable FilterChildren(this ILog private static string StripOptionalNamespacePrefix(string attributeValue) { if (attributeValue is null) - { - return null; - } + return string.Empty; int p = attributeValue.IndexOf(':'); if (p < 0) - { return attributeValue; - } return attributeValue.Substring(p + 1); } diff --git a/src/NLog/Config/LoggingConfigurationFileLoader.cs b/src/NLog/Config/LoggingConfigurationFileLoader.cs index bece0d27cd..b6ba2998fc 100644 --- a/src/NLog/Config/LoggingConfigurationFileLoader.cs +++ b/src/NLog/Config/LoggingConfigurationFileLoader.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -53,7 +55,7 @@ public LoggingConfigurationFileLoader(IAppEnvironment appEnvironment) _appEnvironment = appEnvironment; } - public LoggingConfiguration Load(LogFactory logFactory, string filename = null) + public LoggingConfiguration? Load(LogFactory logFactory, string? filename = null) { #if NETFRAMEWORK if (string.IsNullOrEmpty(filename)) @@ -64,7 +66,7 @@ public LoggingConfiguration Load(LogFactory logFactory, string filename = null) } #endif - if (string.IsNullOrEmpty(filename) || FileInfoHelper.IsRelativeFilePath(filename)) + if (filename is null || StringHelpers.IsNullOrWhiteSpace(filename) || FileInfoHelper.IsRelativeFilePath(filename)) { return TryLoadFromFilePaths(logFactory, filename); } @@ -76,7 +78,7 @@ public LoggingConfiguration Load(LogFactory logFactory, string filename = null) return null; } - private LoggingConfiguration TryLoadFromFilePaths(LogFactory logFactory, string filename) + private LoggingConfiguration? TryLoadFromFilePaths(LogFactory logFactory, string? filename) { #pragma warning disable CS0618 // Type or member is obsolete var configFileNames = logFactory.GetCandidateConfigFilePaths(filename); @@ -90,7 +92,7 @@ private LoggingConfiguration TryLoadFromFilePaths(LogFactory logFactory, string return null; } - private bool TryLoadLoggingConfiguration(LogFactory logFactory, string configFile, out LoggingConfiguration config) + private bool TryLoadLoggingConfiguration(LogFactory logFactory, string configFile, out LoggingConfiguration? config) { try { @@ -205,7 +207,7 @@ private static bool ScanForBooleanParameter(string fileContent, string parameter /// /// Get default file paths (including filename) for possible NLog config files. /// - public IEnumerable GetDefaultCandidateConfigFilePaths(string filename = null) + public IEnumerable GetDefaultCandidateConfigFilePaths(string? filename = null) { string baseDirectory = PathHelpers.TrimDirectorySeparators(_appEnvironment.AppDomainBaseDirectory); string entryAssemblyLocation = PathHelpers.TrimDirectorySeparators(_appEnvironment.EntryAssemblyLocation); @@ -244,12 +246,12 @@ public IEnumerable GetDefaultCandidateConfigFilePaths(string filename = foreach (var filePath in GetPrivateBinPathNLogLocations(baseDirectory, nlogConfigFile, platformFileSystemCaseInsensitive ? nLogConfigFileLowerCase : string.Empty)) yield return filePath; - string nlogAssemblyLocation = filename is null ? LookupNLogAssemblyLocation() : null; + var nlogAssemblyLocation = filename is null ? LookupNLogAssemblyLocation() : null; if (!string.IsNullOrEmpty(nlogAssemblyLocation)) yield return nlogAssemblyLocation + ".nlog"; } - private static string LookupNLogAssemblyLocation() + private static string? LookupNLogAssemblyLocation() { var nlogAssembly = typeof(LogFactory).Assembly; // Get path to NLog.dll.nlog only if the assembly is not in the GAC diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index a07246be81..08ffc94aab 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -31,23 +31,25 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; -using JetBrains.Annotations; -using NLog.Common; -using NLog.Filters; -using NLog.Internal; -using NLog.Layouts; -using NLog.Targets; -using NLog.Targets.Wrappers; -using NLog.Time; +#nullable enable namespace NLog.Config { + using System; + using System.Collections; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Reflection; + using JetBrains.Annotations; + using NLog.Common; + using NLog.Filters; + using NLog.Internal; + using NLog.Layouts; + using NLog.Targets; + using NLog.Targets.Wrappers; + using NLog.Time; + /// /// Loads NLog configuration from /// @@ -73,7 +75,7 @@ protected LoggingConfigurationParser(LogFactory logFactory) /// /// /// Directory where the NLog-config-file was loaded from - protected void LoadConfig(ILoggingConfigurationElement nlogConfig, string basePath) + protected void LoadConfig(ILoggingConfigurationElement nlogConfig, string? basePath) { InternalLogger.Trace("ParseNLogConfig"); nlogConfig.AssertName("nlog"); @@ -123,7 +125,7 @@ private void SetNLogElementSettings(ILoggingConfigurationElement nlogConfig) { var sortedList = CreateUniqueSortedListFromConfig(nlogConfig); - CultureInfo defaultCultureInfo = DefaultCultureInfo ?? LogFactory._defaultCultureInfo; + CultureInfo? defaultCultureInfo = DefaultCultureInfo ?? LogFactory._defaultCultureInfo; bool? parseMessageTemplates = null; bool internalLoggerEnabled = false; bool autoLoadExtensions = false; @@ -132,47 +134,47 @@ private void SetNLogElementSettings(ILoggingConfigurationElement nlogConfig) switch (configItem.Key.ToUpperInvariant()) { case "THROWEXCEPTIONS": - LogFactory.ThrowExceptions = ParseBooleanValue(configItem.Key, configItem.Value, LogFactory.ThrowExceptions); + LogFactory.ThrowExceptions = ParseBooleanValue(configItem.Key, configItem.Value ?? string.Empty, LogFactory.ThrowExceptions); break; case "THROWCONFIGEXCEPTIONS": - LogFactory.ThrowConfigExceptions = ParseNullableBooleanValue(configItem.Key, configItem.Value, false); + LogFactory.ThrowConfigExceptions = ParseNullableBooleanValue(configItem.Key, configItem.Value ?? string.Empty, false); break; case "INTERNALLOGLEVEL": - InternalLogger.LogLevel = ParseLogLevelSafe(configItem.Key, configItem.Value, InternalLogger.LogLevel); + InternalLogger.LogLevel = ParseLogLevelSafe(configItem.Key, configItem.Value ?? string.Empty, InternalLogger.LogLevel); internalLoggerEnabled = InternalLogger.LogLevel != LogLevel.Off; break; case "USEINVARIANTCULTURE": - if (ParseBooleanValue(configItem.Key, configItem.Value, false)) + if (ParseBooleanValue(configItem.Key, configItem.Value ?? string.Empty, false)) defaultCultureInfo = DefaultCultureInfo = CultureInfo.InvariantCulture; break; case "KEEPVARIABLESONRELOAD": - LogFactory.KeepVariablesOnReload = ParseBooleanValue(configItem.Key, configItem.Value, LogFactory.KeepVariablesOnReload); + LogFactory.KeepVariablesOnReload = ParseBooleanValue(configItem.Key, configItem.Value ?? string.Empty, LogFactory.KeepVariablesOnReload); break; case "INTERNALLOGTOCONSOLE": - InternalLogger.LogToConsole = ParseBooleanValue(configItem.Key, configItem.Value, InternalLogger.LogToConsole); + InternalLogger.LogToConsole = ParseBooleanValue(configItem.Key, configItem.Value ?? string.Empty, InternalLogger.LogToConsole); break; case "INTERNALLOGTOCONSOLEERROR": - InternalLogger.LogToConsoleError = ParseBooleanValue(configItem.Key, configItem.Value, InternalLogger.LogToConsoleError); + InternalLogger.LogToConsoleError = ParseBooleanValue(configItem.Key, configItem.Value ?? string.Empty, InternalLogger.LogToConsoleError); break; case "INTERNALLOGFILE": InternalLogger.LogFile = configItem.Value?.Trim(); break; case "INTERNALLOGINCLUDETIMESTAMP": - InternalLogger.IncludeTimestamp = ParseBooleanValue(configItem.Key, configItem.Value, InternalLogger.IncludeTimestamp); + InternalLogger.IncludeTimestamp = ParseBooleanValue(configItem.Key, configItem.Value ?? string.Empty, InternalLogger.IncludeTimestamp); break; case "GLOBALTHRESHOLD": - LogFactory.GlobalThreshold = ParseLogLevelSafe(configItem.Key, configItem.Value, LogFactory.GlobalThreshold); + LogFactory.GlobalThreshold = ParseLogLevelSafe(configItem.Key, configItem.Value ?? string.Empty, LogFactory.GlobalThreshold); break; // expanding variables not possible here, they are created later case "PARSEMESSAGETEMPLATES": - parseMessageTemplates = ParseNullableBooleanValue(configItem.Key, configItem.Value, true); + parseMessageTemplates = ParseNullableBooleanValue(configItem.Key, configItem.Value ?? string.Empty, true); break; case "AUTOSHUTDOWN": - LogFactory.AutoShutdown = ParseBooleanValue(configItem.Key, configItem.Value, true); + LogFactory.AutoShutdown = ParseBooleanValue(configItem.Key, configItem.Value ?? string.Empty, true); break; case "AUTORELOAD": break; // Ignore here, used by other logic case "AUTOLOADEXTENSIONS": - autoLoadExtensions = ParseBooleanValue(configItem.Key, configItem.Value, false); + autoLoadExtensions = ParseBooleanValue(configItem.Key, configItem.Value ?? string.Empty, false); break; default: var configException = new NLogConfigurationException($"Unrecognized value '{configItem.Key}'='{configItem.Value}' for element '{nlogConfig.Name}'"); @@ -250,8 +252,7 @@ private LogLevel ParseLogLevelSafe(string propertyName, string propertyValue, Lo { try { - var internalLogLevel = LogLevel.FromString(propertyValue?.Trim()); - return internalLogLevel; + return LogLevel.FromString(propertyValue?.Trim() ?? string.Empty); } catch (Exception exception) { @@ -296,22 +297,22 @@ protected virtual bool ParseNLogSection(ILoggingConfigurationElement configSecti return false; } - private void ParseExtensionsElement(ValidatedConfigurationElement extensionsElement, string baseDirectory) + private void ParseExtensionsElement(ValidatedConfigurationElement extensionsElement, string? baseDirectory) { extensionsElement.AssertName("extensions"); foreach (var childItem in extensionsElement.ValidChildren) { - string prefix = null; - string type = null; - string assemblyFile = null; - string assemblyName = null; + string itemNamePrefix = string.Empty; + string? type = null; + string? assemblyFile = null; + string? assemblyName = null; foreach (var childProperty in childItem.Values) { if (MatchesName(childProperty.Key, "prefix")) { - prefix = childProperty.Value + "."; + itemNamePrefix = childProperty.Value + "."; } else if (MatchesName(childProperty.Key, "type")) { @@ -333,20 +334,20 @@ private void ParseExtensionsElement(ValidatedConfigurationElement extensionsElem } } - if (!StringHelpers.IsNullOrWhiteSpace(type)) + if (type != null && !StringHelpers.IsNullOrWhiteSpace(type)) { - RegisterExtension(type, prefix); + RegisterExtension(type, itemNamePrefix); } - if (!StringHelpers.IsNullOrWhiteSpace(assemblyFile)) + if (assemblyFile != null && !StringHelpers.IsNullOrWhiteSpace(assemblyFile)) { - ParseExtensionWithAssemblyFile(baseDirectory, assemblyFile, prefix); + ParseExtensionWithAssemblyFile(assemblyFile, baseDirectory, itemNamePrefix); continue; } - if (!StringHelpers.IsNullOrWhiteSpace(assemblyName)) + if (assemblyName != null && !StringHelpers.IsNullOrWhiteSpace(assemblyName)) { - ParseExtensionWithAssemblyName(assemblyName?.Trim(), prefix); + ParseExtensionWithAssemblyName(assemblyName.Trim(), itemNamePrefix); } } } @@ -369,7 +370,7 @@ private void RegisterExtension(string typeName, string itemNamePrefix) } } - private void ParseExtensionWithAssemblyFile(string baseDirectory, string assemblyFile, string prefix) + private void ParseExtensionWithAssemblyFile(string assemblyFile, string? baseDirectory, string prefix) { try { @@ -393,11 +394,11 @@ private bool RegisterExtensionFromAssemblyName(string assemblyName, string origi return ParseExtensionWithAssemblyName(assemblyName, string.Empty); } - private bool ParseExtensionWithAssemblyName(string assemblyName, string prefix) + private bool ParseExtensionWithAssemblyName(string assemblyName, string itemNamePrefix) { try { - ConfigurationItemFactory.Default.AssemblyLoader.LoadAssemblyFromName(ConfigurationItemFactory.Default, assemblyName, prefix); + ConfigurationItemFactory.Default.AssemblyLoader.LoadAssemblyFromName(ConfigurationItemFactory.Default, assemblyName, itemNamePrefix); return true; } catch (Exception exception) @@ -416,8 +417,8 @@ private bool ParseExtensionWithAssemblyName(string assemblyName, string prefix) private void ParseVariableElement(ValidatedConfigurationElement variableElement) { - string variableName = null; - string variableValue = null; + string? variableName = null; + string? variableValue = null; foreach (var childProperty in variableElement.Values) { if (MatchesName(childProperty.Key, "name")) @@ -432,20 +433,20 @@ private void ParseVariableElement(ValidatedConfigurationElement variableElement) } } - if (!AssertNonEmptyValue(variableName, "name", variableElement.Name, "variables")) + if (!AssertNonEmptyValue(variableName, "name", variableElement.Name, "variables") || variableName is null) return; - Layout variableLayout = variableValue is null + Layout? variableLayout = variableValue is null ? ParseVariableLayoutValue(variableElement) : CreateSimpleLayout(ExpandSimpleVariables(variableValue)); - if (!AssertNotNullValue(variableLayout, "value", variableElement.Name, "variables")) + if (!AssertNotNullValue(variableLayout, "value", variableElement.Name, "variables") || variableLayout is null) return; InsertParsedConfigVariable(variableName, variableLayout); } - private Layout ParseVariableLayoutValue(ValidatedConfigurationElement variableElement) + private Layout? ParseVariableLayoutValue(ValidatedConfigurationElement variableElement) { var childElement = variableElement.ValidChildren.FirstOrDefault(); if (childElement != null) @@ -470,7 +471,7 @@ private void ParseTimeElement(ValidatedConfigurationElement timeElement) { timeElement.AssertName("time"); - string timeSourceType = null; + string? timeSourceType = null; foreach (var childProperty in timeElement.Values) { if (MatchesName(childProperty.Key, "type")) @@ -485,10 +486,10 @@ private void ParseTimeElement(ValidatedConfigurationElement timeElement) } } - if (!AssertNonEmptyValue(timeSourceType, "type", timeElement.Name, string.Empty)) + if (!AssertNonEmptyValue(timeSourceType, "type", timeElement.Name, string.Empty) || timeSourceType is null) return; - TimeSource newTimeSource = FactoryCreateInstance(timeSourceType, ConfigurationItemFactory.Default.TimeSourceFactory); + var newTimeSource = FactoryCreateInstance(timeSourceType, ConfigurationItemFactory.Default.TimeSourceFactory); if (newTimeSource != null) { ConfigureFromAttributesAndElements(newTimeSource, timeElement); @@ -498,7 +499,7 @@ private void ParseTimeElement(ValidatedConfigurationElement timeElement) } [ContractAnnotation("value:notnull => true")] - private bool AssertNotNullValue(object value, string propertyName, string elementName, string sectionName) + private bool AssertNotNullValue(object? value, string propertyName, string elementName, string sectionName) { if (value is null) return AssertNonEmptyValue(string.Empty, propertyName, elementName, sectionName); @@ -507,7 +508,7 @@ private bool AssertNotNullValue(object value, string propertyName, string elemen } [ContractAnnotation("value:null => false")] - private bool AssertNonEmptyValue(string value, string propertyName, string elementName, string sectionName) + private bool AssertNonEmptyValue(string? value, string propertyName, string elementName, string sectionName) { if (!StringHelpers.IsNullOrWhiteSpace(value)) return true; @@ -531,7 +532,7 @@ private void ParseRulesElement(ValidatedConfigurationElement rulesElement, IList foreach (var childItem in rulesElement.ValidChildren) { - LoggingRule loggingRule = ParseRuleElement(childItem); + var loggingRule = ParseRuleElement(childItem); if (loggingRule != null) { lock (rulesCollection) @@ -542,28 +543,23 @@ private void ParseRulesElement(ValidatedConfigurationElement rulesElement, IList } } - private LogLevel LogLevelFromString(string text) - { - return LogLevel.FromString(ExpandSimpleVariables(text).Trim()); - } - /// /// Parse {Logger} xml element /// /// - private LoggingRule ParseRuleElement(ValidatedConfigurationElement loggerElement) + private LoggingRule? ParseRuleElement(ValidatedConfigurationElement loggerElement) { - string minLevel = null; - string maxLevel = null; - string finalMinLevel = null; - string enableLevels = null; + string? minLevel = null; + string? maxLevel = null; + string? finalMinLevel = null; + string? enableLevels = null; - string ruleName = null; - string namePattern = null; + string? ruleName = null; + string? namePattern = null; bool enabled = true; bool final = false; - string writeTargets = null; - string filterDefaultAction = null; + string? writeTargets = null; + string? filterDefaultAction = null; foreach (var childProperty in loggerElement.Values) { switch (childProperty.Key?.Trim().ToUpperInvariant()) @@ -652,48 +648,50 @@ private LoggingRule ParseRuleElement(ValidatedConfigurationElement loggerElement private void EnableLevelsForRule( LoggingRule rule, - string enableLevels, - string minLevel, - string maxLevel, - string finalMinLevel) + string? enableLevels, + string? minLevel, + string? maxLevel, + string? finalMinLevel) { if (enableLevels != null) { - enableLevels = ExpandSimpleVariables(enableLevels); - finalMinLevel = finalMinLevel != null ? ExpandSimpleVariables(finalMinLevel) : finalMinLevel; + enableLevels = ExpandSimpleVariables(enableLevels).Trim(); + finalMinLevel = ExpandSimpleVariables(finalMinLevel).Trim(); + if (IsLevelLayout(enableLevels) || IsLevelLayout(finalMinLevel)) { - SimpleLayout simpleLayout = ParseLevelLayout(enableLevels); - SimpleLayout finalMinLevelLayout = ParseLevelLayout(finalMinLevel); + var simpleLayout = ParseLevelLayout(enableLevels); + var finalMinLevelLayout = ParseLevelLayout(finalMinLevel); rule.EnableLoggingForLevelLayout(simpleLayout, finalMinLevelLayout); } else { foreach (var logLevel in enableLevels.SplitAndTrimTokens(',')) { - rule.EnableLoggingForLevel(LogLevelFromString(logLevel)); + rule.EnableLoggingForLevel(LogLevel.FromString(logLevel)); } - if (finalMinLevel != null) - rule.FinalMinLevel = LogLevelFromString(finalMinLevel); + if (!string.IsNullOrEmpty(finalMinLevel)) + rule.FinalMinLevel = LogLevel.FromString(finalMinLevel); } } else { - minLevel = minLevel != null ? ExpandSimpleVariables(minLevel) : minLevel; - maxLevel = maxLevel != null ? ExpandSimpleVariables(maxLevel) : maxLevel; - finalMinLevel = finalMinLevel != null ? ExpandSimpleVariables(finalMinLevel) : finalMinLevel; + minLevel = ExpandSimpleVariables(minLevel).Trim(); + maxLevel = ExpandSimpleVariables(maxLevel).Trim(); + finalMinLevel = ExpandSimpleVariables(finalMinLevel).Trim(); + if (IsLevelLayout(minLevel) || IsLevelLayout(maxLevel) || IsLevelLayout(finalMinLevel)) { - SimpleLayout finalMinLevelLayout = ParseLevelLayout(finalMinLevel); - SimpleLayout minLevelLayout = ParseLevelLayout(minLevel) ?? finalMinLevelLayout; - SimpleLayout maxLevelLayout = ParseLevelLayout(maxLevel); + var finalMinLevelLayout = ParseLevelLayout(finalMinLevel); + var minLevelLayout = ParseLevelLayout(minLevel) ?? finalMinLevelLayout; + var maxLevelLayout = ParseLevelLayout(maxLevel); rule.EnableLoggingForLevelsLayout(minLevelLayout, maxLevelLayout, finalMinLevelLayout); } else { - LogLevel finalMinLogLevel = finalMinLevel != null ? LogLevelFromString(finalMinLevel) : null; - LogLevel minLogLevel = minLevel != null ? LogLevelFromString(minLevel) : (finalMinLogLevel ?? LogLevel.MinLevel); - LogLevel maxLogLevel = maxLevel != null ? LogLevelFromString(maxLevel) : LogLevel.MaxLevel; + var finalMinLogLevel = string.IsNullOrEmpty(finalMinLevel) ? null : LogLevel.FromString(finalMinLevel); + var minLogLevel = string.IsNullOrEmpty(minLevel) ? (finalMinLogLevel ?? LogLevel.MinLevel) : LogLevel.FromString(minLevel); + var maxLogLevel = string.IsNullOrEmpty(maxLevel) ? LogLevel.MaxLevel : LogLevel.FromString(maxLevel); rule.SetLoggingLevels(minLogLevel, maxLogLevel); if (finalMinLogLevel != null) rule.FinalMinLevel = finalMinLogLevel; @@ -701,19 +699,22 @@ private void EnableLevelsForRule( } } - private static bool IsLevelLayout(string level) + private static bool IsLevelLayout(string? level) { return level?.IndexOf('{') >= 0; } - private SimpleLayout ParseLevelLayout(string levelLayout) + private SimpleLayout? ParseLevelLayout(string levelLayout) { - SimpleLayout simpleLayout = !StringHelpers.IsNullOrWhiteSpace(levelLayout) ? CreateSimpleLayout(levelLayout) : null; - simpleLayout?.Initialize(this); + if (levelLayout is null || StringHelpers.IsNullOrWhiteSpace(levelLayout)) + return null; + + var simpleLayout = CreateSimpleLayout(levelLayout); + simpleLayout.Initialize(this); return simpleLayout; } - private void ParseLoggingRuleTargets(string writeTargets, LoggingRule rule) + private void ParseLoggingRuleTargets(string? writeTargets, LoggingRule rule) { writeTargets = ExpandSimpleVariables(writeTargets); if (string.IsNullOrEmpty(writeTargets)) @@ -721,7 +722,7 @@ private void ParseLoggingRuleTargets(string writeTargets, LoggingRule rule) foreach (string targetName in writeTargets.SplitAndTrimTokens(',')) { - Target target = FindTargetByName(targetName); + var target = FindTargetByName(targetName); if (target != null) { rule.Targets.Add(target); @@ -737,11 +738,11 @@ private void ParseLoggingRuleTargets(string writeTargets, LoggingRule rule) } [Obsolete("Very exotic feature without any unit-tests, not sure if it works. Marked obsolete with NLog v5.3")] - private void ParseLoggingRuleChildren(ValidatedConfigurationElement loggerElement, LoggingRule rule, string filterDefaultAction = null) + private void ParseLoggingRuleChildren(ValidatedConfigurationElement loggerElement, LoggingRule rule, string? filterDefaultAction = null) { foreach (var child in loggerElement.ValidChildren) { - LoggingRule childRule = null; + LoggingRule? childRule = null; if (child.MatchesName("filters")) { ParseLoggingRuleFilters(rule, child, filterDefaultAction); @@ -773,14 +774,14 @@ private void ParseLoggingRuleChildren(ValidatedConfigurationElement loggerElemen } } - private void ParseLoggingRuleFilters(LoggingRule rule, ValidatedConfigurationElement filtersElement, string filterDefaultAction = null) + private void ParseLoggingRuleFilters(LoggingRule rule, ValidatedConfigurationElement filtersElement, string? filterDefaultAction = null) { filtersElement.AssertName("filters"); filterDefaultAction = filtersElement.GetOptionalValue("defaultAction", null) ?? filtersElement.GetOptionalValue(nameof(rule.FilterDefaultAction), null) ?? filterDefaultAction; if (filterDefaultAction != null) { - if (ConversionHelpers.TryParseEnum(filterDefaultAction, typeof(FilterResult), out var enumValue)) + if (ConversionHelpers.TryParseEnum(filterDefaultAction, typeof(FilterResult), out var enumValue) && enumValue != null) { rule.FilterDefaultAction = (FilterResult)enumValue; } @@ -795,9 +796,12 @@ private void ParseLoggingRuleFilters(LoggingRule rule, ValidatedConfigurationEle foreach (var filterElement in filtersElement.ValidChildren) { var filterType = filterElement.GetOptionalValue("type", null) ?? filterElement.Name; - Filter filter = FactoryCreateInstance(filterType, ConfigurationItemFactory.Default.FilterFactory); - ConfigureFromAttributesAndElements(filter, filterElement); - rule.Filters.Add(filter); + Filter? filter = FactoryCreateInstance(filterType, ConfigurationItemFactory.Default.FilterFactory); + if (filter != null) + { + ConfigureFromAttributesAndElements(filter, filterElement); + rule.Filters.Add(filter); + } } } @@ -821,16 +825,16 @@ private void ParseTargetsElement(ValidatedConfigurationElement targetsElement) { targetsElement.AssertName("targets", "appenders"); - bool asyncWrap = ParseBooleanValue("async", targetsElement.GetOptionalValue("async", "false"), false); + bool asyncWrap = ParseBooleanValue("async", targetsElement.GetOptionalValue("async", "false") ?? string.Empty, false); - ValidatedConfigurationElement defaultWrapperElement = null; - Dictionary typeNameToDefaultTargetParameters = null; + ValidatedConfigurationElement? defaultWrapperElement = null; + Dictionary? typeNameToDefaultTargetParameters = null; foreach (var targetElement in targetsElement.ValidChildren) { - string targetTypeName = targetElement.GetConfigItemTypeAttribute(); - string targetValueName = targetElement.GetOptionalValue("name", null); - targetValueName = string.IsNullOrEmpty(targetValueName) ? targetElement.Name : $"{targetElement.Name}(Name={targetValueName})"; + var targetTypeName = targetElement.GetConfigItemTypeAttribute(); + var targetValueName = targetElement.GetOptionalValue("name", null) ?? string.Empty; + targetValueName = string.IsNullOrEmpty(targetValueName) ? (targetElement.Name ?? string.Empty) : $"{targetElement.Name}(Name={targetValueName})"; switch (targetElement.Name?.Trim().ToUpperInvariant()) { @@ -870,7 +874,7 @@ private void ParseTargetsElement(ValidatedConfigurationElement targetsElement) } } - private static Dictionary RegisterNewTargetDefaultParameters(Dictionary typeNameToDefaultTargetParameters, ValidatedConfigurationElement targetElement, string targetTypeName) + private static Dictionary RegisterNewTargetDefaultParameters(Dictionary? typeNameToDefaultTargetParameters, ValidatedConfigurationElement targetElement, string targetTypeName) { if (typeNameToDefaultTargetParameters is null) typeNameToDefaultTargetParameters = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -878,9 +882,9 @@ private static Dictionary RegisterNewTarg return typeNameToDefaultTargetParameters; } - private void AddNewTargetFromConfig(string targetTypeName, ValidatedConfigurationElement targetElement, bool asyncWrap, Dictionary typeNameToDefaultTargetParameters = null, ValidatedConfigurationElement defaultWrapperElement = null) + private void AddNewTargetFromConfig(string targetTypeName, ValidatedConfigurationElement targetElement, bool asyncWrap, Dictionary? typeNameToDefaultTargetParameters = null, ValidatedConfigurationElement? defaultWrapperElement = null) { - Target newTarget = null; + Target? newTarget = null; try { @@ -918,16 +922,16 @@ private void AddNewTargetFromConfig(string targetTypeName, ValidatedConfiguratio } } - private Target CreateTargetType(string targetTypeName) + private Target? CreateTargetType(string targetTypeName) { return FactoryCreateInstance(targetTypeName, ConfigurationItemFactory.Default.TargetFactory); } - private void ParseTargetElement(Target target, ValidatedConfigurationElement targetElement, Dictionary typeNameToDefaultTargetParameters = null) + private void ParseTargetElement(Target target, ValidatedConfigurationElement targetElement, Dictionary? typeNameToDefaultTargetParameters = null) { - string targetTypeName = targetElement.GetConfigItemTypeAttribute("targets"); - if (typeNameToDefaultTargetParameters != null && - typeNameToDefaultTargetParameters.TryGetValue(targetTypeName, out var defaults)) + var targetTypeName = targetElement.GetConfigItemTypeAttribute("targets"); + if ( typeNameToDefaultTargetParameters != null + && typeNameToDefaultTargetParameters.TryGetValue(targetTypeName, out var defaults)) { ParseTargetElement(target, defaults, null); } @@ -958,13 +962,13 @@ private void ParseTargetElement(Target target, ValidatedConfigurationElement tar private bool ParseTargetWrapper( WrapperTargetBase wrapper, ValidatedConfigurationElement childElement, - Dictionary typeNameToDefaultTargetParameters) + Dictionary? typeNameToDefaultTargetParameters) { if (IsTargetRefElement(childElement.Name)) { var targetName = childElement.GetRequiredValue("name", GetName(wrapper)); - Target newTarget = FindTargetByName(targetName); + var newTarget = FindTargetByName(targetName); if (newTarget is null) { var configException = new NLogConfigurationException($"Referenced target '{targetName}' not found."); @@ -978,9 +982,9 @@ private bool ParseTargetWrapper( if (IsTargetElement(childElement.Name)) { - string targetTypeName = childElement.GetConfigItemTypeAttribute(GetName(wrapper)); + var targetTypeName = childElement.GetConfigItemTypeAttribute(GetName(wrapper)); - Target newTarget = CreateTargetType(targetTypeName); + var newTarget = CreateTargetType(targetTypeName); if (newTarget != null) { ParseTargetElement(newTarget, childElement, typeNameToDefaultTargetParameters); @@ -1012,8 +1016,8 @@ private bool ParseTargetWrapper( private bool ParseCompoundTarget( CompoundTargetBase compound, ValidatedConfigurationElement childElement, - Dictionary typeNameToDefaultTargetParameters, - string targetName) + Dictionary? typeNameToDefaultTargetParameters, + string? targetName) { if (MatchesName(childElement.Name, "targets") || MatchesName(childElement.Name, "appenders")) { @@ -1029,7 +1033,7 @@ private bool ParseCompoundTarget( { targetName = childElement.GetRequiredValue("name", GetName(compound)); - Target newTarget = FindTargetByName(targetName); + var newTarget = FindTargetByName(targetName); if (newTarget is null) { throw new NLogConfigurationException("Referenced target '" + targetName + "' not found."); @@ -1041,9 +1045,9 @@ private bool ParseCompoundTarget( if (IsTargetElement(childElement.Name)) { - string targetTypeName = childElement.GetConfigItemTypeAttribute(GetName(compound)); + var targetTypeName = childElement.GetConfigItemTypeAttribute(GetName(compound)); - Target newTarget = CreateTargetType(targetTypeName); + var newTarget = CreateTargetType(targetTypeName); if (newTarget != null) { if (targetName != null) @@ -1090,7 +1094,7 @@ private void SetPropertyValueFromString(T targetObject, string propertyName, throw new NLogConfigurationException($"'{typeof(T).Name}' is null, and cannot assign property '{propertyName}'='{propertyValue}'"); } - if (!PropertyHelper.TryGetPropertyInfo(ConfigurationItemFactory.Default, targetObject, propertyName, out var propertyInfo)) + if (!PropertyHelper.TryGetPropertyInfo(ConfigurationItemFactory.Default, targetObject, propertyName, out var propertyInfo) || propertyInfo is null) { throw new NLogConfigurationException($"'{targetObject.GetType()?.Name}' cannot assign unknown property '{propertyName}'='{propertyValue}'"); } @@ -1134,7 +1138,7 @@ private void SetPropertyValuesFromElement(T targetObject, ValidatedConfigurat return; } - if (!PropertyHelper.TryGetPropertyInfo(ConfigurationItemFactory.Default, targetObject, childElement.Name, out var propInfo)) + if (!PropertyHelper.TryGetPropertyInfo(ConfigurationItemFactory.Default, targetObject, childElement.Name, out var propInfo) || propInfo is null) { var configException = new NLogConfigurationException($"'{targetObject.GetType()?.Name}' cannot assign unknown property '{childElement.Name}' in section '{parentElement.Name}'"); if (MustThrowConfigException(configException)) @@ -1158,13 +1162,13 @@ private void SetPropertyValuesFromElement(T targetObject, ValidatedConfigurat return; } - if (TryGetPropertyValue(targetObject, propInfo, out var propertyValue)) + if (TryGetPropertyValue(targetObject, propInfo, out var propertyValue) && propertyValue is not null) { ConfigureFromAttributesAndElements(propertyValue, childElement); } } - private bool TryGetPropertyValue(T targetObject, PropertyInfo propInfo, out object propertyValue) where T : class + private bool TryGetPropertyValue(T targetObject, PropertyInfo propInfo, out object? propertyValue) where T : class { try { @@ -1187,10 +1191,10 @@ private bool TryGetPropertyValue(T targetObject, PropertyInfo propInfo, out o private bool AddArrayItemFromElement(object o, PropertyInfo propInfo, ValidatedConfigurationElement element) { - Type elementType = PropertyHelper.GetArrayItemType(propInfo); + var elementType = PropertyHelper.GetArrayItemType(propInfo); if (elementType != null) { - if (TryGetPropertyValue(o, propInfo, out var propertyValue)) + if (TryGetPropertyValue(o, propInfo, out var propertyValue) && propertyValue != null) { IList listValue = (IList)propertyValue; @@ -1206,7 +1210,7 @@ private bool AddArrayItemFromElement(object o, PropertyInfo propInfo, ValidatedC return true; } - object arrayItem = ParseArrayItemFromElement(elementType, element); + var arrayItem = ParseArrayItemFromElement(elementType, element); listValue.Add(arrayItem); return true; } @@ -1215,13 +1219,13 @@ private bool AddArrayItemFromElement(object o, PropertyInfo propInfo, ValidatedC return false; } - private object ParseArrayItemFromElement(Type elementType, ValidatedConfigurationElement element) + private object? ParseArrayItemFromElement(Type elementType, ValidatedConfigurationElement element) { - object arrayItem = TryCreateLayoutInstance(element, elementType); + object? arrayItem = TryCreateLayoutInstance(element, elementType); // arrayItem is not a layout if (arrayItem is null) { - if (!ConfigurationItemFactory.Default.TryCreateInstance(elementType, out arrayItem)) + if (!ConfigurationItemFactory.Default.TryCreateInstance(elementType, out arrayItem) || arrayItem is null) { throw new NLogConfigurationException($"Factory returned null for {elementType}"); } @@ -1246,7 +1250,7 @@ private bool SetLayoutFromElement(object o, PropertyInfo propInfo, ValidatedConf private bool SetFilterFromElement(object o, PropertyInfo propInfo, ValidatedConfigurationElement element) { - Filter filter = TryCreateFilterInstance(element, propInfo.PropertyType); + var filter = TryCreateFilterInstance(element, propInfo.PropertyType); // and is a Filter and 'type' attribute has been specified if (filter != null) { @@ -1262,15 +1266,15 @@ private SimpleLayout CreateSimpleLayout(string layoutText) return new SimpleLayout(layoutText, ConfigurationItemFactory.Default, LogFactory.ThrowConfigExceptions); } - private Layout TryCreateLayoutInstance(ValidatedConfigurationElement element, Type type) + private Layout? TryCreateLayoutInstance(ValidatedConfigurationElement element, Type type) { // Check if Layout type if (!typeof(Layout).IsAssignableFrom(type)) return null; // Check if the 'type' attribute has been specified - string classType = element.GetConfigItemTypeAttribute(); - if (classType is null) + var classType = element.GetConfigItemTypeAttribute(); + if (string.IsNullOrEmpty(classType)) return null; var expandedClassType = ExpandSimpleVariables(classType, out var matchingVariableName); @@ -1297,7 +1301,7 @@ private Layout TryCreateLayoutInstance(ValidatedConfigurationElement element, Ty return null; } - private bool TryCreateSimpleLayoutInstance(ValidatedConfigurationElement element, out SimpleLayout simpleLayout) + private bool TryCreateSimpleLayoutInstance(ValidatedConfigurationElement element, out SimpleLayout? simpleLayout) { if (!element.ValidChildren.Any()) { @@ -1319,7 +1323,7 @@ private bool TryCreateSimpleLayoutInstance(ValidatedConfigurationElement element return false; } - private Filter TryCreateFilterInstance(ValidatedConfigurationElement element, Type type) + private Filter? TryCreateFilterInstance(ValidatedConfigurationElement element, Type type) { var filter = TryCreateInstance(element, type, ConfigurationItemFactory.Default.FilterFactory); if (filter != null) @@ -1331,7 +1335,7 @@ private Filter TryCreateFilterInstance(ValidatedConfigurationElement element, Ty return null; } - private T TryCreateInstance(ValidatedConfigurationElement element, Type type, IFactory factory) + private T? TryCreateInstance(ValidatedConfigurationElement element, Type type, IFactory factory) where T : class { // Check if correct type @@ -1339,16 +1343,16 @@ private T TryCreateInstance(ValidatedConfigurationElement element, Type type, return null; // Check if the 'type' attribute has been specified - string classType = element.GetConfigItemTypeAttribute(); - if (classType is null) + var classType = element.GetConfigItemTypeAttribute(); + if (string.IsNullOrEmpty(classType)) return null; return FactoryCreateInstance(classType, factory); } - private T FactoryCreateInstance(string typeName, IFactory factory) where T : class + private T? FactoryCreateInstance(string typeName, IFactory factory) where T : class { - T newInstance = null; + T? newInstance = null; try { @@ -1433,14 +1437,14 @@ private static Target WrapWithAsyncTargetWrapper(Target target) private Target WrapWithDefaultWrapper(Target target, ValidatedConfigurationElement defaultWrapperElement) { - string wrapperTypeName = defaultWrapperElement.GetConfigItemTypeAttribute("targets"); - Target wrapperTargetInstance = CreateTargetType(wrapperTypeName); - WrapperTargetBase wtb = wrapperTargetInstance as WrapperTargetBase; - if (wtb is null) + var wrapperTypeName = defaultWrapperElement.GetConfigItemTypeAttribute("targets"); + var wrapperTargetInstance = CreateTargetType(wrapperTypeName) as WrapperTargetBase; + if (wrapperTargetInstance is null) { throw new NLogConfigurationException("Target type specified on is not a wrapper."); } + var wtb = wrapperTargetInstance; ParseTargetElement(wrapperTargetInstance, defaultWrapperElement); while (wtb.WrappedTarget != null) { @@ -1544,7 +1548,7 @@ private sealed class ValidatedConfigurationElement : ILoggingConfigurationElemen { private readonly ILoggingConfigurationElement _element; private readonly bool _throwConfigExceptions; - private IList _validChildren; + private IList? _validChildren; public static ValidatedConfigurationElement Create(ILoggingConfigurationElement element, LogFactory logFactory) { @@ -1566,7 +1570,7 @@ public ValidatedConfigurationElement(ILoggingConfigurationElement element, bool public string Name { get; } public ICollection> Values => _valueLookup ?? (ICollection>)ArrayHelper.Empty>(); - private readonly IDictionary _valueLookup; + private readonly IDictionary? _valueLookup; public IEnumerable ValidChildren { @@ -1581,7 +1585,7 @@ public IEnumerable ValidChildren IEnumerable YieldAndCacheValidChildren() { - IList validChildren = null; + IList? validChildren = null; foreach (var child in _element.Children) { validChildren = validChildren ?? new List(); @@ -1601,7 +1605,7 @@ IEnumerable YieldAndCacheValidChildren() public string GetRequiredValue(string attributeName, string section) { - string value = GetOptionalValue(attributeName, null); + var value = GetOptionalValue(attributeName, null); if (value is null) { throw new NLogConfigurationException($"Expected {attributeName} on {Name} in {section}"); @@ -1616,7 +1620,7 @@ public string GetRequiredValue(string attributeName, string section) return value; } - public string GetOptionalValue(string attributeName, string defaultValue) + public string? GetOptionalValue(string attributeName, string? defaultValue) { if (_valueLookup is null) return defaultValue; @@ -1625,10 +1629,10 @@ public string GetOptionalValue(string attributeName, string defaultValue) return value ?? defaultValue; } - private static IDictionary CreateValueLookup(ILoggingConfigurationElement element, bool throwConfigExceptions) + private static IDictionary? CreateValueLookup(ILoggingConfigurationElement element, bool throwConfigExceptions) { - IDictionary valueLookup = null; - List warnings = null; + IDictionary? valueLookup = null; + List? warnings = null; foreach (var attribute in element.Values) { var attributeKey = attribute.Key?.Trim() ?? string.Empty; diff --git a/src/NLog/Config/LoggingRule.cs b/src/NLog/Config/LoggingRule.cs index b297a9ffc6..3ca8aea3c7 100644 --- a/src/NLog/Config/LoggingRule.cs +++ b/src/NLog/Config/LoggingRule.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -50,7 +52,7 @@ namespace NLog.Config public class LoggingRule { private ILoggingRuleLevelFilter _logLevelFilter = LoggingRuleLevelFilter.Off; - private LoggerNameMatcher _loggerNameMatcher = LoggerNameMatcher.Create(null); + private LoggerNameMatcher _loggerNameMatcher = LoggerNameMatcher.Off; private readonly List _targets = new List(); /// @@ -64,7 +66,7 @@ public LoggingRule() /// /// Create an empty . /// - public LoggingRule(string ruleName) + public LoggingRule(string? ruleName) { RuleName = ruleName; } @@ -113,7 +115,7 @@ public LoggingRule(string loggerNamePattern, Target target) /// /// Rule identifier to allow rule lookup /// - public string RuleName { get; set; } + public string? RuleName { get; set; } /// /// Gets a collection of targets that should be written to when this rule matches. @@ -151,7 +153,7 @@ public LoggingRule(string loggerNamePattern, Target target) /// /// Loggers matching will be restricted to specified minimum level for following rules. /// - public LogLevel FinalMinLevel + public LogLevel? FinalMinLevel { get => _logLevelFilter.FinalMinLevel; set => _logLevelFilter = _logLevelFilter.GetSimpleFilterForUpdate().SetFinalMinLevel(value); @@ -243,12 +245,12 @@ public void EnableLoggingForLevels(LogLevel minLevel, LogLevel maxLevel) _logLevelFilter = _logLevelFilter.GetSimpleFilterForUpdate().SetLoggingLevels(minLevel, maxLevel, true); } - internal void EnableLoggingForLevelLayout(NLog.Layouts.SimpleLayout simpleLayout, NLog.Layouts.SimpleLayout finalMinLevel) + internal void EnableLoggingForLevelLayout(NLog.Layouts.SimpleLayout? simpleLayout, NLog.Layouts.SimpleLayout? finalMinLevel) { _logLevelFilter = new DynamicLogLevelFilter(this, simpleLayout, finalMinLevel); } - internal void EnableLoggingForLevelsLayout(Layouts.SimpleLayout minLevel, Layouts.SimpleLayout maxLevel, NLog.Layouts.SimpleLayout finalMinLevel) + internal void EnableLoggingForLevelsLayout(Layouts.SimpleLayout? minLevel, Layouts.SimpleLayout? maxLevel, NLog.Layouts.SimpleLayout? finalMinLevel) { _logLevelFilter = new DynamicRangeLevelFilter(this, minLevel, maxLevel, finalMinLevel); } diff --git a/src/NLog/Config/LoggingRuleLevelFilter.cs b/src/NLog/Config/LoggingRuleLevelFilter.cs index b17c7b6346..0392185a73 100644 --- a/src/NLog/Config/LoggingRuleLevelFilter.cs +++ b/src/NLog/Config/LoggingRuleLevelFilter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -42,9 +44,9 @@ internal sealed class LoggingRuleLevelFilter : ILoggingRuleLevelFilter { public static readonly ILoggingRuleLevelFilter Off = new LoggingRuleLevelFilter(); public bool[] LogLevels { get; } - public LogLevel FinalMinLevel { get; private set; } + public LogLevel? FinalMinLevel { get; private set; } - public LoggingRuleLevelFilter(bool[] logLevels = null, LogLevel finalMinLevel = null) + public LoggingRuleLevelFilter(bool[]? logLevels = null, LogLevel? finalMinLevel = null) { LogLevels = new bool[LogLevel.MaxLevel.Ordinal + 1]; if (logLevels != null) @@ -70,7 +72,7 @@ public LoggingRuleLevelFilter SetLoggingLevels(LogLevel minLevel, LogLevel maxLe return this; } - public LoggingRuleLevelFilter SetFinalMinLevel(LogLevel finalMinLevel) + public LoggingRuleLevelFilter SetFinalMinLevel(LogLevel? finalMinLevel) { FinalMinLevel = finalMinLevel; return this; diff --git a/src/NLog/Config/MethodFactory.cs b/src/NLog/Config/MethodFactory.cs index ec4265dc36..39e55d722a 100644 --- a/src/NLog/Config/MethodFactory.cs +++ b/src/NLog/Config/MethodFactory.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -295,7 +297,7 @@ private static object[] ResolveMethodParameters(object[] defaultMethodParameters return methodParameters; } - public void RegisterNoParameters(string methodName, Func noParameters, MethodInfo legacyMethodInfo = null) + public void RegisterNoParameters(string methodName, Func noParameters, MethodInfo? legacyMethodInfo = null) { lock (_nameToMethodDetails) { @@ -305,7 +307,7 @@ public void RegisterNoParameters(string methodName, Func n } } - public void RegisterOneParameter(string methodName, Func oneParameter, MethodInfo legacyMethodInfo = null) + public void RegisterOneParameter(string methodName, Func oneParameter, MethodInfo? legacyMethodInfo = null) { lock (_nameToMethodDetails) { @@ -315,7 +317,7 @@ public void RegisterOneParameter(string methodName, Func twoParameters, MethodInfo legacyMethodInfo = null) + public void RegisterTwoParameters(string methodName, Func twoParameters, MethodInfo? legacyMethodInfo = null) { lock (_nameToMethodDetails) { @@ -325,7 +327,7 @@ public void RegisterTwoParameters(string methodName, Func threeParameters, MethodInfo legacyMethodInfo = null) + public void RegisterThreeParameters(string methodName, Func threeParameters, MethodInfo? legacyMethodInfo = null) { lock (_nameToMethodDetails) { @@ -335,7 +337,7 @@ public void RegisterThreeParameters(string methodName, Func manyParameters, int manyParameterMinCount, int manyParameterMaxCount, bool manyParameterWithLogEvent, MethodInfo legacyMethodInfo = null) + public void RegisterManyParameters(string methodName, Func manyParameters, int manyParameterMinCount, int manyParameterMaxCount, bool manyParameterWithLogEvent, MethodInfo? legacyMethodInfo = null) { lock (_nameToMethodDetails) { @@ -345,7 +347,7 @@ public void RegisterManyParameters(string methodName, Func man } } - public Func TryCreateInstanceWithNoParameters(string methodName) + public Func? TryCreateInstanceWithNoParameters(string methodName) { lock (_nameToMethodDetails) { @@ -356,7 +358,7 @@ public Func TryCreateInstanceWithNoParameters(string metho } } - public Func TryCreateInstanceWithOneParameter(string methodName) + public Func? TryCreateInstanceWithOneParameter(string methodName) { lock (_nameToMethodDetails) { @@ -367,7 +369,7 @@ public Func TryCreateInstanceWithOneParameter(stri } } - public Func TryCreateInstanceWithTwoParameters(string methodName) + public Func? TryCreateInstanceWithTwoParameters(string methodName) { lock (_nameToMethodDetails) { @@ -378,7 +380,7 @@ public Func TryCreateInstanceWithTwoParame } } - public Func TryCreateInstanceWithThreeParameters(string methodName) + public Func? TryCreateInstanceWithThreeParameters(string methodName) { lock (_nameToMethodDetails) { @@ -389,7 +391,7 @@ public Func TryCreateInstanceWithT } } - public Func TryCreateInstanceWithManyParameters(string methodName, out int manyParameterMinCount, out int manyParameterMaxCount, out bool manyParameterWithLogEvent) + public Func? TryCreateInstanceWithManyParameters(string methodName, out int manyParameterMinCount, out int manyParameterMaxCount, out bool manyParameterWithLogEvent) { lock (_nameToMethodDetails) { diff --git a/src/NLog/Config/MutableUnsafeAttribute.cs b/src/NLog/Config/MutableUnsafeAttribute.cs index e6842ae39d..5740fa6533 100644 --- a/src/NLog/Config/MutableUnsafeAttribute.cs +++ b/src/NLog/Config/MutableUnsafeAttribute.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; diff --git a/src/NLog/Config/NLogConfigurationIgnorePropertyAttribute.cs b/src/NLog/Config/NLogConfigurationIgnorePropertyAttribute.cs index 36f6a43b81..e04df33255 100644 --- a/src/NLog/Config/NLogConfigurationIgnorePropertyAttribute.cs +++ b/src/NLog/Config/NLogConfigurationIgnorePropertyAttribute.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; diff --git a/src/NLog/Config/NLogConfigurationItemAttribute.cs b/src/NLog/Config/NLogConfigurationItemAttribute.cs index b7b8efb5a4..e3f20b8781 100644 --- a/src/NLog/Config/NLogConfigurationItemAttribute.cs +++ b/src/NLog/Config/NLogConfigurationItemAttribute.cs @@ -31,11 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using JetBrains.Annotations; +#nullable enable namespace NLog.Config { using System; + using JetBrains.Annotations; /// /// Marks the object as configuration item for NLog. diff --git a/src/NLog/Config/NLogDependencyResolveException.cs b/src/NLog/Config/NLogDependencyResolveException.cs index 78542e2c6f..caac0be9f2 100644 --- a/src/NLog/Config/NLogDependencyResolveException.cs +++ b/src/NLog/Config/NLogDependencyResolveException.cs @@ -31,12 +31,14 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using JetBrains.Annotations; -using NLog.Internal; +#nullable enable namespace NLog.Config { + using System; + using JetBrains.Annotations; + using NLog.Internal; + /// /// Failed to resolve the interface of service type /// diff --git a/src/NLog/Config/NameBaseAttribute.cs b/src/NLog/Config/NameBaseAttribute.cs index d71aa7fc90..6a8821fa18 100644 --- a/src/NLog/Config/NameBaseAttribute.cs +++ b/src/NLog/Config/NameBaseAttribute.cs @@ -31,14 +31,15 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using JetBrains.Annotations; +#nullable enable namespace NLog.Config { using System; - using LayoutRenderers; - using Layouts; - using Targets; + using NLog.LayoutRenderers; + using NLog.Layouts; + using NLog.Targets; + using JetBrains.Annotations; /// /// Attaches a type-alias for an item (such as , diff --git a/src/NLog/Config/PropertyTypeConverter.cs b/src/NLog/Config/PropertyTypeConverter.cs index 6b649410d9..9025385a10 100644 --- a/src/NLog/Config/PropertyTypeConverter.cs +++ b/src/NLog/Config/PropertyTypeConverter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -49,12 +51,12 @@ internal sealed class PropertyTypeConverter : IPropertyTypeConverter /// public static PropertyTypeConverter Instance { get; } = new PropertyTypeConverter(); - private static Dictionary> StringConverterLookup => _stringConverters ?? (_stringConverters = BuildStringConverterLookup()); - private static Dictionary> _stringConverters; + private static Dictionary> StringConverterLookup => _stringConverters ?? (_stringConverters = BuildStringConverterLookup()); + private static Dictionary>? _stringConverters; - private static Dictionary> BuildStringConverterLookup() + private static Dictionary> BuildStringConverterLookup() { - return new Dictionary>() + return new Dictionary>() { { typeof(System.Text.Encoding), (stringvalue, format, formatProvider) => ConvertToEncoding(stringvalue) }, { typeof(System.Globalization.CultureInfo), (stringvalue, format, formatProvider) => ConvertToCultureInfo(stringvalue) }, @@ -81,7 +83,7 @@ internal static bool IsComplexType(Type type) } /// - public object Convert(object propertyValue, Type propertyType, string format, IFormatProvider formatProvider) + public object? Convert(object? propertyValue, Type propertyType, string? format, IFormatProvider? formatProvider) { if (propertyValue is null || propertyType is null || propertyType.Equals(typeof(object))) { @@ -113,7 +115,7 @@ public object Convert(object propertyValue, Type propertyType, string format, IF return ChangeObjectType(propertyValue, propertyType, format, formatProvider); } - private static bool TryConvertFromString(string propertyString, Type propertyType, string format, IFormatProvider formatProvider, out object propertyValue) + private static bool TryConvertFromString(string propertyString, Type propertyType, string? format, IFormatProvider? formatProvider, out object? propertyValue) { propertyValue = propertyString = propertyString.Trim(); @@ -137,11 +139,11 @@ private static bool TryConvertFromString(string propertyString, Type propertyTyp return false; } - private static object ChangeObjectType(object propertyValue, Type propertyType, string format, IFormatProvider formatProvider) + private static object? ChangeObjectType(object propertyValue, Type propertyType, string? format, IFormatProvider? formatProvider) { - if (propertyValue is string propertyString && TryConvertFromString(propertyString, propertyType, format, formatProvider, out propertyValue)) + if (propertyValue is string propertyString && TryConvertFromString(propertyString, propertyType, format, formatProvider, out var fromStringValue)) { - return propertyValue; + return fromStringValue; } if (propertyValue is IConvertible convertibleValue) @@ -168,7 +170,7 @@ private static object ChangeObjectType(object propertyValue, Type propertyType, [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2026")] [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2067")] [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2072")] - private static bool TryConvertToType(object propertyValue, Type propertyType, out object convertedValue) + private static bool TryConvertToType(object propertyValue, Type propertyType, out object? convertedValue) { if (propertyValue is null || propertyType.IsAssignableFrom(propertyValue.GetType())) { @@ -187,7 +189,7 @@ private static bool TryConvertToType(object propertyValue, Type propertyType, ou return false; } - private static object ConvertGuid(string format, string propertyString) + private static object ConvertGuid(string? format, string propertyString) { #if !NET35 return string.IsNullOrEmpty(format) ? Guid.Parse(propertyString) : Guid.ParseExact(propertyString, format); @@ -196,7 +198,7 @@ private static object ConvertGuid(string format, string propertyString) #endif } - internal static object ConvertToCultureInfo(string stringValue) + internal static object? ConvertToCultureInfo(string? stringValue) { if (StringHelpers.IsNullOrWhiteSpace(stringValue)) return null; @@ -215,7 +217,7 @@ internal static object ConvertToEncoding(string stringValue) return System.Text.Encoding.GetEncoding(stringValue); } - private static object ConvertToTimeSpan(string format, IFormatProvider formatProvider, string propertyString) + private static object ConvertToTimeSpan(string? format, IFormatProvider? formatProvider, string propertyString) { #if !NET35 if (!string.IsNullOrEmpty(format)) @@ -226,14 +228,14 @@ private static object ConvertToTimeSpan(string format, IFormatProvider formatPro #endif } - private static object ConvertToDateTimeOffset(string format, IFormatProvider formatProvider, string propertyString) + private static object ConvertToDateTimeOffset(string? format, IFormatProvider? formatProvider, string propertyString) { if (!string.IsNullOrEmpty(format)) return DateTimeOffset.ParseExact(propertyString, format, formatProvider); return DateTimeOffset.Parse(propertyString, formatProvider); } - private static object ConvertToDateTime(string format, IFormatProvider formatProvider, string propertyString) + private static object ConvertToDateTime(string? format, IFormatProvider? formatProvider, string propertyString) { if (!string.IsNullOrEmpty(format)) return DateTime.ParseExact(propertyString, format, formatProvider); diff --git a/src/NLog/Config/RequiredParameterAttribute.cs b/src/NLog/Config/RequiredParameterAttribute.cs index c7c03a2f35..402ebd17b8 100644 --- a/src/NLog/Config/RequiredParameterAttribute.cs +++ b/src/NLog/Config/RequiredParameterAttribute.cs @@ -31,11 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using JetBrains.Annotations; +#nullable enable namespace NLog.Config { using System; + using JetBrains.Annotations; /// /// Attribute used to mark the required parameters for targets, diff --git a/src/NLog/Config/ServiceRepositoryUpdateEventArgs.cs b/src/NLog/Config/ServiceRepositoryUpdateEventArgs.cs index a86e16d197..ca1ec68e1e 100644 --- a/src/NLog/Config/ServiceRepositoryUpdateEventArgs.cs +++ b/src/NLog/Config/ServiceRepositoryUpdateEventArgs.cs @@ -31,10 +31,13 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; +#nullable enable namespace NLog.Config { + using System; + using NLog.Internal; + /// /// Registered service type in the service repository /// @@ -46,7 +49,7 @@ public class ServiceRepositoryUpdateEventArgs : EventArgs /// Type of service that have been registered public ServiceRepositoryUpdateEventArgs(Type serviceType) { - ServiceType = serviceType; + ServiceType = Guard.ThrowIfNull(serviceType); } /// diff --git a/src/NLog/Config/SimpleConfigurator.cs b/src/NLog/Config/SimpleConfigurator.cs index 4f17ee8bc7..8e0325e3d3 100644 --- a/src/NLog/Config/SimpleConfigurator.cs +++ b/src/NLog/Config/SimpleConfigurator.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; diff --git a/src/NLog/Config/ThreadAgnosticAttribute.cs b/src/NLog/Config/ThreadAgnosticAttribute.cs index b262c597a1..65ecaf3058 100644 --- a/src/NLog/Config/ThreadAgnosticAttribute.cs +++ b/src/NLog/Config/ThreadAgnosticAttribute.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; diff --git a/src/NLog/Config/ThreadAgnosticImmutableAttribute.cs b/src/NLog/Config/ThreadAgnosticImmutableAttribute.cs index 26f399afa6..1c5e72b4d7 100644 --- a/src/NLog/Config/ThreadAgnosticImmutableAttribute.cs +++ b/src/NLog/Config/ThreadAgnosticImmutableAttribute.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; diff --git a/src/NLog/Config/ThreadSafeAttribute.cs b/src/NLog/Config/ThreadSafeAttribute.cs index 9b95a7f7ec..c60af0b1f1 100644 --- a/src/NLog/Config/ThreadSafeAttribute.cs +++ b/src/NLog/Config/ThreadSafeAttribute.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; diff --git a/src/NLog/Config/XmlLoggingConfiguration.cs b/src/NLog/Config/XmlLoggingConfiguration.cs index 0106c99d84..5e61501968 100644 --- a/src/NLog/Config/XmlLoggingConfiguration.cs +++ b/src/NLog/Config/XmlLoggingConfiguration.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -55,7 +57,7 @@ public class XmlLoggingConfiguration : LoggingConfigurationParser private static readonly Dictionary _watchers = new Dictionary(); private readonly Dictionary _fileMustAutoReloadLookup = new Dictionary(StringComparer.OrdinalIgnoreCase); - private string _originalFileName; + private string? _originalFileName; private readonly Stack _currentFilePath = new Stack(); @@ -98,7 +100,7 @@ public XmlLoggingConfiguration([NotNull] TextReader xmlSource) /// /// Configuration file to be read. /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. - public XmlLoggingConfiguration([NotNull] TextReader xmlSource, [CanBeNull] string filePath) + public XmlLoggingConfiguration([NotNull] TextReader xmlSource, string? filePath) : this(xmlSource, filePath, LogManager.LogFactory) { } @@ -109,7 +111,7 @@ public XmlLoggingConfiguration([NotNull] TextReader xmlSource, [CanBeNull] strin /// Configuration file to be read. /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The to which to apply any applicable configuration values. - public XmlLoggingConfiguration([NotNull] TextReader xmlSource, [CanBeNull] string filePath, LogFactory logFactory) + public XmlLoggingConfiguration([NotNull] TextReader xmlSource, string? filePath, LogFactory logFactory) : base(logFactory) { Guard.ThrowIfNull(xmlSource); @@ -131,7 +133,7 @@ public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader) /// XmlReader containing the configuration section. /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] - public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader, [CanBeNull] string fileName) + public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader, [CanBeNull] string? fileName) : this(reader, fileName, LogManager.LogFactory) { } @@ -142,7 +144,7 @@ public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader, [CanBeNull /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The to which to apply any applicable configuration values. [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] - public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader, [CanBeNull] string fileName, LogFactory logFactory) + public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader, [CanBeNull] string? fileName, LogFactory logFactory) : base(logFactory) { Guard.ThrowIfNull(reader); @@ -225,11 +227,12 @@ public IEnumerable AutoReloadFileNames /// Must assign the returned object to LogManager.Configuration to activate it public override LoggingConfiguration Reload() { - if (!string.IsNullOrEmpty(_originalFileName)) + var originalFileName = _originalFileName ?? string.Empty; + if (!string.IsNullOrEmpty(originalFileName)) { var newConfig = new XmlLoggingConfiguration(LogFactory); newConfig.PrepareForReload(this); - newConfig.LoadFromXmlFile(_originalFileName); + newConfig.LoadFromXmlFile(originalFileName); return newConfig; } @@ -237,7 +240,7 @@ public override LoggingConfiguration Reload() } /// - protected internal override void OnConfigurationAssigned(LogFactory logFactory) + protected internal override void OnConfigurationAssigned(LogFactory? logFactory) { base.OnConfigurationAssigned(logFactory); @@ -245,7 +248,7 @@ protected internal override void OnConfigurationAssigned(LogFactory logFactory) { var configFactory = logFactory ?? LogFactory ?? NLog.LogManager.LogFactory; - AutoReloadConfigFileWatcher fileWatcher = null; + AutoReloadConfigFileWatcher? fileWatcher = null; lock (_watchers) { _watchers.TryGetValue(configFactory, out fileWatcher); @@ -337,11 +340,11 @@ internal void LoadFromXmlContent(string xmlContents, string filePath) #if NETFRAMEWORK [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] - private void ParseFromXmlReader([NotNull] System.Xml.XmlReader reader, [CanBeNull] string filePath) + private void ParseFromXmlReader([NotNull] System.Xml.XmlReader reader, [CanBeNull] string? filePath) { try { - _originalFileName = string.IsNullOrEmpty(filePath) ? filePath : GetFileLookupKey(filePath); + _originalFileName = (filePath is null || StringHelpers.IsNullOrWhiteSpace(filePath)) ? null : GetFileLookupKey(filePath); reader.MoveToContent(); var content = new XmlLoggingConfigurationElement(reader); if (!string.IsNullOrEmpty(_originalFileName)) @@ -363,11 +366,11 @@ private void ParseFromXmlReader([NotNull] System.Xml.XmlReader reader, [CanBeNul } #endif - private void ParseFromTextReader(TextReader textReader, string filePath) + private void ParseFromTextReader(TextReader textReader, string? filePath) { try { - _originalFileName = string.IsNullOrEmpty(filePath) ? filePath : GetFileLookupKey(filePath); + _originalFileName = (filePath is null || StringHelpers.IsNullOrWhiteSpace(filePath)) ? null : GetFileLookupKey(filePath); var content = new XmlParserConfigurationElement(new XmlParser(textReader).LoadDocument(out var _)); if (!string.IsNullOrEmpty(_originalFileName)) { @@ -408,7 +411,7 @@ private void IncludeNewConfigFile([NotNull] string filePath, bool autoReloadDefa /// /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The default value for the autoReload option. - private void ParseTopLevel(ILoggingConfigurationElement content, [CanBeNull] string filePath, bool autoReloadDefault) + private void ParseTopLevel(ILoggingConfigurationElement content, [CanBeNull] string? filePath, bool autoReloadDefault) { content.AssertName("nlog", "configuration"); @@ -430,7 +433,7 @@ private void ParseTopLevel(ILoggingConfigurationElement content, [CanBeNull] str /// /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The default value for the autoReload option. - private void ParseConfigurationElement(ILoggingConfigurationElement configurationElement, [CanBeNull] string filePath, bool autoReloadDefault) + private void ParseConfigurationElement(ILoggingConfigurationElement configurationElement, [CanBeNull] string? filePath, bool autoReloadDefault) { InternalLogger.Trace("ParseConfigurationElement"); configurationElement.AssertName("configuration"); @@ -448,7 +451,7 @@ private void ParseConfigurationElement(ILoggingConfigurationElement configuratio /// /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The default value for the autoReload option. - private void ParseNLogElement(ILoggingConfigurationElement nlogElement, [CanBeNull] string filePath, bool autoReloadDefault) + private void ParseNLogElement(ILoggingConfigurationElement nlogElement, [CanBeNull] string? filePath, bool autoReloadDefault) { InternalLogger.Trace("ParseNLogElement"); nlogElement.AssertName("nlog"); @@ -457,8 +460,8 @@ private void ParseNLogElement(ILoggingConfigurationElement nlogElement, [CanBeNu try { - string baseDirectory = null; - if (!string.IsNullOrEmpty(filePath)) + string? baseDirectory = null; + if (filePath != null && !StringHelpers.IsNullOrWhiteSpace(filePath)) { _fileMustAutoReloadLookup[GetFileLookupKey(filePath)] = autoReload; _currentFilePath.Push(filePath); @@ -482,7 +485,7 @@ protected override bool ParseNLogSection(ILoggingConfigurationElement configSect { if (configSection.MatchesName("include")) { - string filePath = _currentFilePath.Peek(); + var filePath = _currentFilePath.Peek(); bool autoLoad = !string.IsNullOrEmpty(filePath) && _fileMustAutoReloadLookup[GetFileLookupKey(filePath)]; ParseIncludeElement(configSection, !string.IsNullOrEmpty(filePath) ? Path.GetDirectoryName(filePath) : null, autoLoad); return true; @@ -493,7 +496,7 @@ protected override bool ParseNLogSection(ILoggingConfigurationElement configSect } } - private void ParseIncludeElement(ILoggingConfigurationElement includeElement, string baseDirectory, bool autoReloadDefault) + private void ParseIncludeElement(ILoggingConfigurationElement includeElement, string? baseDirectory, bool autoReloadDefault) { includeElement.AssertName("include"); @@ -519,10 +522,9 @@ private void ParseIncludeElement(ILoggingConfigurationElement includeElement, st else { //is mask? - if (newFileName.IndexOf('*') >= 0) { - IncludeConfigFilesByMask(baseDirectory, newFileName, autoReloadDefault); + IncludeConfigFilesByMask(baseDirectory ?? ".", newFileName, autoReloadDefault); } else { @@ -572,12 +574,12 @@ private void IncludeConfigFilesByMask(string baseDirectory, string fileMask, boo } var filename = Path.GetFileName(fileMask); - if (filename is null) { InternalLogger.Warn("filename is empty for include of '{0}'", fileMask); return; } + fileMask = filename; } @@ -589,7 +591,7 @@ private void IncludeConfigFilesByMask(string baseDirectory, string fileMask, boo } } - private static string GetFileLookupKey([NotNull] string fileName) + private static string GetFileLookupKey(string fileName) { return Path.GetFullPath(fileName); } @@ -605,7 +607,7 @@ private sealed class AutoReloadConfigFileWatcher : IDisposable private readonly LogFactory _logFactory; private readonly MultiFileWatcher _fileWatcher = new MultiFileWatcher(); private readonly object _lockObject = new object(); - private Timer _reloadTimer; + private Timer? _reloadTimer; private bool _isDisposing; internal bool IsDisposed => _isDisposing; @@ -665,7 +667,7 @@ private void ReloadTimer(object state) } } - LoggingConfiguration newConfig = null; + LoggingConfiguration? newConfig = null; try { diff --git a/src/NLog/Config/XmlLoggingConfigurationElement.cs b/src/NLog/Config/XmlLoggingConfigurationElement.cs index ebba26606c..a14c3e6ec2 100644 --- a/src/NLog/Config/XmlLoggingConfigurationElement.cs +++ b/src/NLog/Config/XmlLoggingConfigurationElement.cs @@ -33,6 +33,8 @@ #if NETFRAMEWORK +#nullable enable + namespace NLog.Config { using System; @@ -58,15 +60,16 @@ public XmlLoggingConfigurationElement(XmlReader reader) public XmlLoggingConfigurationElement(XmlReader reader, bool nestedElement) { - Parse(reader, nestedElement, out var attributes, out var children); - AttributeValues = attributes ?? ArrayHelper.Empty>(); - Children = children ?? ArrayHelper.Empty(); + AttributeValues = ParseAttributes(reader, nestedElement); + LocalName = reader.LocalName; + Children = ParseChildren(reader, nestedElement, out var innerText); + Value = innerText; } /// /// Gets the element name. /// - public string LocalName { get; private set; } + public string LocalName { get; } /// /// Gets the dictionary of attribute values. @@ -81,7 +84,7 @@ public XmlLoggingConfigurationElement(XmlReader reader, bool nestedElement) /// /// Gets the value of the element. /// - public string Value { get; private set; } + public string? Value { get; } public string Name => LocalName; @@ -95,7 +98,7 @@ public IEnumerable> Values if (SingleValueElement(child)) { // Values assigned using nested node-elements. Maybe in combination with attributes - return AttributeValues.Concat(Children.Where(item => SingleValueElement(item)).Select(item => new KeyValuePair(item.Name, item.Value))); + return AttributeValues.Concat(Children.Where(item => SingleValueElement(item)).Select(item => new KeyValuePair(item.Name, item.Value ?? string.Empty))); } } return AttributeValues; @@ -140,13 +143,11 @@ public void AssertName(params string[] allowedNames) throw new InvalidOperationException($"Assertion failed. Expected element name '{string.Join("|", allowedNames)}', actual: '{LocalName}'."); } - private void Parse(XmlReader reader, bool nestedElement, out IList> attributes, out IList children) + private static IList ParseChildren(XmlReader reader, bool nestedElement, out string? innerText) { - ParseAttributes(reader, nestedElement, out attributes); - - LocalName = reader.LocalName; + IList? children = null; - children = null; + innerText = null; if (!reader.IsEmptyElement) { @@ -159,7 +160,7 @@ private void Parse(XmlReader reader, bool nestedElement, out IList(); } - private static void ParseAttributes(XmlReader reader, bool nestedElement, out IList> attributes) + private static IList> ParseAttributes(XmlReader reader, bool nestedElement) { - attributes = null; + IList>? attributes = null; if (reader.MoveToFirstAttribute()) { do @@ -191,6 +194,8 @@ private static void ParseAttributes(XmlReader reader, bool nestedElement, out IL while (reader.MoveToNextAttribute()); reader.MoveToElement(); } + + return attributes ?? ArrayHelper.Empty>(); } /// diff --git a/src/NLog/Config/XmlParserConfigurationElement.cs b/src/NLog/Config/XmlParserConfigurationElement.cs index f499d54c2b..9bc1051654 100644 --- a/src/NLog/Config/XmlParserConfigurationElement.cs +++ b/src/NLog/Config/XmlParserConfigurationElement.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -48,7 +50,7 @@ internal sealed class XmlParserConfigurationElement : ILoggingConfigurationEleme /// /// Gets the value of the element. /// - public string Value { get; private set; } + public string? Value { get; private set; } /// /// Gets the dictionary of attribute values. @@ -70,7 +72,7 @@ public IEnumerable> Values if (SingleValueElement(child)) { // Values assigned using nested node-elements. Maybe in combination with attributes - return AttributeValues.Concat(Children.Where(item => SingleValueElement(item)).Select(item => new KeyValuePair(item.Name, item.Value))); + return AttributeValues.Concat(Children.Where(item => SingleValueElement(item)).Select(item => new KeyValuePair(item.Name, item.Value ?? string.Empty))); } } return AttributeValues; @@ -99,9 +101,11 @@ public XmlParserConfigurationElement(XmlParser.XmlParserElement xmlElement) public XmlParserConfigurationElement(XmlParser.XmlParserElement xmlElement, bool nestedElement) { - Parse(xmlElement, nestedElement, out var attributes, out var children); - AttributeValues = attributes ?? ArrayHelper.Empty>(); - Children = children ?? ArrayHelper.Empty(); + var namePrefixIndex = xmlElement.Name.IndexOf(':'); + Name = namePrefixIndex >= 0 ? xmlElement.Name.Substring(namePrefixIndex + 1) : xmlElement.Name; + Value = xmlElement.InnerText; + AttributeValues = ParseAttributes(xmlElement, nestedElement); + Children = ParseChildren(xmlElement, nestedElement); } private static bool SingleValueElement(XmlParserConfigurationElement child) @@ -110,13 +114,9 @@ private static bool SingleValueElement(XmlParserConfigurationElement child) return child.Children.Count == 0 && child.AttributeValues.Count == 0 && child.Value != null; } - private void Parse(XmlParser.XmlParserElement xmlElement, bool nestedElement, out IList> attributes, out IList children) + private static IList> ParseAttributes(XmlParser.XmlParserElement xmlElement, bool nestedElement) { - var namePrefixIndex = xmlElement.Name.IndexOf(':'); - Name = namePrefixIndex >= 0 ? xmlElement.Name.Substring(namePrefixIndex + 1) : xmlElement.Name; - Value = xmlElement.InnerText; - attributes = xmlElement.Attributes; - + var attributes = xmlElement.Attributes; if (attributes?.Count > 0) { if (!nestedElement) @@ -139,17 +139,23 @@ private void Parse(XmlParser.XmlParserElement xmlElement, bool nestedElement, ou } } - children = null; + return attributes ?? ArrayHelper.Empty>(); + } + + private static IList ParseChildren(XmlParser.XmlParserElement xmlElement, bool nestedElement) + { + var childElements = xmlElement.Children; + if (childElements is null || childElements.Count == 0) + return ArrayHelper.Empty(); - if (xmlElement.Children?.Count > 0) + var children = new List(); + for (int i = 0; i < childElements.Count; ++i) { - foreach (var child in xmlElement.Children) - { - children = children ?? new List(); - var nestedChild = nestedElement || !string.Equals(child.Name, "nlog", StringComparison.OrdinalIgnoreCase); - children.Add(new XmlParserConfigurationElement(child, nestedChild)); - } + var child = childElements[i]; + var nestedChild = nestedElement || !string.Equals(child.Name, "nlog", StringComparison.OrdinalIgnoreCase); + children.Add(new XmlParserConfigurationElement(child, nestedChild)); } + return children; } /// diff --git a/src/NLog/Config/XmlParserException.cs b/src/NLog/Config/XmlParserException.cs index aa3aa97f96..8f234fbbce 100644 --- a/src/NLog/Config/XmlParserException.cs +++ b/src/NLog/Config/XmlParserException.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Config { using System; @@ -61,7 +63,7 @@ public XmlParserException(string message) /// /// The message. /// The inner exception. - public XmlParserException(string message, Exception innerException) + public XmlParserException(string message, Exception? innerException) : base(message, innerException) { } diff --git a/src/NLog/Internal/PropertyHelper.cs b/src/NLog/Internal/PropertyHelper.cs index 5849ba3d83..0022b085ac 100644 --- a/src/NLog/Internal/PropertyHelper.cs +++ b/src/NLog/Internal/PropertyHelper.cs @@ -55,20 +55,20 @@ namespace NLog.Internal internal static class PropertyHelper { private static readonly Dictionary?> _parameterInfoCache = new Dictionary?>(); - private static readonly Dictionary> _propertyConversionMapper = BuildPropertyConversionMapper(); + private static readonly Dictionary> _propertyConversionMapper = BuildPropertyConversionMapper(); #pragma warning disable S1144 // Unused private types or members should be removed. BUT they help CoreRT to provide config through reflection private static readonly RequiredParameterAttribute _requiredParameterAttribute = new RequiredParameterAttribute(); - private static readonly ArrayParameterAttribute _arrayParameterAttribute = new ArrayParameterAttribute(null, string.Empty); + private static readonly ArrayParameterAttribute _arrayParameterAttribute = new ArrayParameterAttribute(typeof(string), string.Empty); private static readonly DefaultParameterAttribute _defaultParameterAttribute = new DefaultParameterAttribute(); private static readonly NLogConfigurationIgnorePropertyAttribute _ignorePropertyAttribute = new NLogConfigurationIgnorePropertyAttribute(); private static readonly NLogConfigurationItemAttribute _configPropertyAttribute = new NLogConfigurationItemAttribute(); private static readonly FlagsAttribute _flagsAttribute = new FlagsAttribute(); #pragma warning restore S1144 // Unused private types or members should be removed - private static Dictionary> BuildPropertyConversionMapper() + private static Dictionary> BuildPropertyConversionMapper() { - return new Dictionary>() + return new Dictionary>() { { typeof(Layout), TryParseLayoutValue }, { typeof(SimpleLayout), TryParseLayoutValue }, diff --git a/src/NLog/Internal/SingleCallContinuation.cs b/src/NLog/Internal/SingleCallContinuation.cs index bdd36afe6a..61c6fcd402 100644 --- a/src/NLog/Internal/SingleCallContinuation.cs +++ b/src/NLog/Internal/SingleCallContinuation.cs @@ -61,7 +61,7 @@ public SingleCallContinuation(AsyncContinuation? asyncContinuation) /// Continuation function which implements the single-call guard. /// /// The exception. - public void Function(Exception exception) + public void Function(Exception? exception) { try { @@ -79,7 +79,7 @@ public void Function(Exception exception) } } - private void CompletedFunction(Exception exception) + private void CompletedFunction(Exception? exception) { // Completed, nothing to do } diff --git a/src/NLog/Internal/TargetWithFilterChain.cs b/src/NLog/Internal/TargetWithFilterChain.cs index 42ade5dc4a..34876da7fe 100644 --- a/src/NLog/Internal/TargetWithFilterChain.cs +++ b/src/NLog/Internal/TargetWithFilterChain.cs @@ -262,7 +262,7 @@ private static bool AddTargetsFromLoggingRule(LoggingRule rule, string loggerNam return targetsFound; } - private static bool SuppressLogLevel(LoggingRule rule, bool[] ruleLogLevels, LogLevel finalMinLevel, LogLevel globalLogLevel, int logLevelOrdinal, ref bool suppressedLevels) + private static bool SuppressLogLevel(LoggingRule rule, bool[] ruleLogLevels, LogLevel? finalMinLevel, LogLevel globalLogLevel, int logLevelOrdinal, ref bool suppressedLevels) { if (logLevelOrdinal < globalLogLevel.Ordinal) { diff --git a/src/NLog/Internal/TimeoutContinuation.cs b/src/NLog/Internal/TimeoutContinuation.cs index e780d44697..15db0ef325 100644 --- a/src/NLog/Internal/TimeoutContinuation.cs +++ b/src/NLog/Internal/TimeoutContinuation.cs @@ -64,7 +64,7 @@ public TimeoutContinuation(AsyncContinuation asyncContinuation, TimeSpan timeout /// Continuation function which implements the timeout logic. /// /// The exception. - public void Function(Exception exception) + public void Function(Exception? exception) { try { diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 4b018bec1e..4ce47ba7ec 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -921,7 +921,7 @@ public IEnumerable GetCandidateConfigFilePaths() /// /// The file paths to the possible config file [Obsolete("Replaced by chaining LogFactory.Setup().LoadConfigurationFromFile(). Marked obsolete on NLog 5.2")] - internal IEnumerable GetCandidateConfigFilePaths(string filename) + internal IEnumerable GetCandidateConfigFilePaths(string? filename) { if (_candidateConfigFilePaths != null) return GetCandidateConfigFilePaths(); diff --git a/tests/NLog.UnitTests/Config/RuleConfigurationTests.cs b/tests/NLog.UnitTests/Config/RuleConfigurationTests.cs index 0eee3f357c..47c56c3228 100644 --- a/tests/NLog.UnitTests/Config/RuleConfigurationTests.cs +++ b/tests/NLog.UnitTests/Config/RuleConfigurationTests.cs @@ -1012,7 +1012,7 @@ public void LoggingRule_FinalMinLevelLayoutAsVar_EnablesExpectedLevels(string in [InlineData("Off", "Fatal", null)] [InlineData("Error", "Debug", null)] [InlineData(" ", "", null)] - [InlineData(" ", "Fatal", null)] + [InlineData(" ", "Fatal", new[] { nameof(LogLevel.Trace), nameof(LogLevel.Debug), nameof(LogLevel.Info), nameof(LogLevel.Warn), nameof(LogLevel.Error), nameof(LogLevel.Fatal) })] [InlineData("", "", null)] [InlineData("", "Off", new[] { nameof(LogLevel.Trace), nameof(LogLevel.Debug), nameof(LogLevel.Info), nameof(LogLevel.Warn), nameof(LogLevel.Error), nameof(LogLevel.Fatal) })] [InlineData("", "Fatal", new[] { nameof(LogLevel.Trace), nameof(LogLevel.Debug), nameof(LogLevel.Info), nameof(LogLevel.Warn), nameof(LogLevel.Error), nameof(LogLevel.Fatal) })] @@ -1027,7 +1027,7 @@ public void LoggingRule_FinalMinLevelLayoutAsVar_EnablesExpectedLevels(string in [InlineData(" error", "", new[] { nameof(LogLevel.Error), nameof(LogLevel.Fatal) })] [InlineData("Error", "", new[] { nameof(LogLevel.Error), nameof(LogLevel.Fatal) })] [InlineData("Fatal", "", new[] { nameof(LogLevel.Fatal) })] - [InlineData("Trace", " ", null)] + [InlineData("Trace", " ", new[] { nameof(LogLevel.Trace), nameof(LogLevel.Debug), nameof(LogLevel.Info), nameof(LogLevel.Warn), nameof(LogLevel.Error), nameof(LogLevel.Fatal) })] [InlineData("Trace", "", new[] { nameof(LogLevel.Trace), nameof(LogLevel.Debug), nameof(LogLevel.Info), nameof(LogLevel.Warn), nameof(LogLevel.Error), nameof(LogLevel.Fatal) })] [InlineData("Trace", "Debug", new[] { nameof(LogLevel.Trace), nameof(LogLevel.Debug) })] [InlineData("Trace", "Trace", new[] { nameof(LogLevel.Trace), nameof(LogLevel.Trace) })] diff --git a/tests/NLog.UnitTests/Config/XmlConfigTests.cs b/tests/NLog.UnitTests/Config/XmlConfigTests.cs index 681c682651..13bd47b681 100644 --- a/tests/NLog.UnitTests/Config/XmlConfigTests.cs +++ b/tests/NLog.UnitTests/Config/XmlConfigTests.cs @@ -52,7 +52,7 @@ public void ParseNLogOptionsDefaultTest() var config = XmlLoggingConfiguration.CreateFromXmlString(xml); Assert.False(config.AutoReload); - Assert.Equal("", InternalLogger.LogFile); + Assert.Null(InternalLogger.LogFile); Assert.False(InternalLogger.LogToConsole); Assert.False(InternalLogger.LogToConsoleError); Assert.True(InternalLogger.IncludeTimestamp); @@ -72,7 +72,7 @@ public void ParseNLogOptionsTest() var config = XmlLoggingConfiguration.CreateFromXmlString(xml); Assert.False(config.AutoReload); - Assert.Equal("", InternalLogger.LogFile); + Assert.Null(InternalLogger.LogFile); Assert.True(InternalLogger.LogToConsole); Assert.True(InternalLogger.LogToConsoleError); Assert.False(InternalLogger.IncludeTimestamp); From e4d0200575630edb3d334fb6c43ad9d8e0099fdb Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 11 May 2025 08:49:00 +0200 Subject: [PATCH 112/224] Condition classes with nullable references (#5813) --- src/NLog/Conditions/ConditionAndExpression.cs | 14 ++-- .../ConditionEvaluationException.cs | 4 +- .../ConditionExceptionExpression.cs | 4 +- src/NLog/Conditions/ConditionExpression.cs | 6 +- .../Conditions/ConditionLayoutExpression.cs | 6 +- .../Conditions/ConditionLevelExpression.cs | 2 + .../Conditions/ConditionLiteralExpression.cs | 9 ++- .../ConditionLoggerNameExpression.cs | 2 + .../Conditions/ConditionMessageExpression.cs | 2 + .../Conditions/ConditionMethodAttribute.cs | 4 +- .../Conditions/ConditionMethodExpression.cs | 60 ++++++++-------- src/NLog/Conditions/ConditionMethods.cs | 14 ++-- .../Conditions/ConditionMethodsAttribute.cs | 2 + src/NLog/Conditions/ConditionNotExpression.cs | 5 +- src/NLog/Conditions/ConditionOrExpression.cs | 14 ++-- .../Conditions/ConditionParseException.cs | 2 + src/NLog/Conditions/ConditionParser.cs | 26 +++---- .../ConditionRelationalExpression.cs | 10 +-- .../Conditions/ConditionRelationalOperator.cs | 2 + src/NLog/Conditions/ConditionTokenizer.cs | 13 +--- src/NLog/Config/MethodFactory.cs | 70 +++++++++---------- .../Conditions/ConditionParserTests.cs | 3 +- 22 files changed, 149 insertions(+), 125 deletions(-) diff --git a/src/NLog/Conditions/ConditionAndExpression.cs b/src/NLog/Conditions/ConditionAndExpression.cs index e4730507ea..0b8a263cc6 100644 --- a/src/NLog/Conditions/ConditionAndExpression.cs +++ b/src/NLog/Conditions/ConditionAndExpression.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { /// @@ -75,17 +77,13 @@ public override string ToString() /// The value of the conjunction operator. protected override object EvaluateNode(LogEventInfo context) { - var bval1 = (bool)Left.Evaluate(context); - if (!bval1) - { + var leftValue = Left.Evaluate(context) ?? BoxedFalse; + if (!(bool)leftValue) return BoxedFalse; - } - var bval2 = (bool)Right.Evaluate(context); - if (!bval2) - { + var rightValue = Right.Evaluate(context) ?? BoxedFalse; + if (!(bool)rightValue) return BoxedFalse; - } return BoxedTrue; } diff --git a/src/NLog/Conditions/ConditionEvaluationException.cs b/src/NLog/Conditions/ConditionEvaluationException.cs index aa387fb1c4..42c5c65857 100644 --- a/src/NLog/Conditions/ConditionEvaluationException.cs +++ b/src/NLog/Conditions/ConditionEvaluationException.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { using System; @@ -64,7 +66,7 @@ public ConditionEvaluationException(string message) /// /// The message. /// The inner exception. - public ConditionEvaluationException(string message, Exception innerException) + public ConditionEvaluationException(string message, Exception? innerException) : base(message, innerException) { } diff --git a/src/NLog/Conditions/ConditionExceptionExpression.cs b/src/NLog/Conditions/ConditionExceptionExpression.cs index bca71e85ea..4ceac63bb4 100644 --- a/src/NLog/Conditions/ConditionExceptionExpression.cs +++ b/src/NLog/Conditions/ConditionExceptionExpression.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { /// @@ -49,7 +51,7 @@ public override string ToString() /// /// Evaluation context. /// The object. - protected override object EvaluateNode(LogEventInfo context) + protected override object? EvaluateNode(LogEventInfo context) { return context.Exception; } diff --git a/src/NLog/Conditions/ConditionExpression.cs b/src/NLog/Conditions/ConditionExpression.cs index 2a2faebfb0..349ecdba9d 100644 --- a/src/NLog/Conditions/ConditionExpression.cs +++ b/src/NLog/Conditions/ConditionExpression.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { using System; @@ -63,7 +65,7 @@ public static implicit operator ConditionExpression(string conditionExpressionTe /// /// Evaluation context. /// Expression result. - public object Evaluate(LogEventInfo context) + public object? Evaluate(LogEventInfo context) { try { @@ -92,6 +94,6 @@ public object Evaluate(LogEventInfo context) /// /// Evaluation context. /// Expression result. - protected abstract object EvaluateNode(LogEventInfo context); + protected abstract object? EvaluateNode(LogEventInfo context); } } diff --git a/src/NLog/Conditions/ConditionLayoutExpression.cs b/src/NLog/Conditions/ConditionLayoutExpression.cs index accbddcce6..143429d891 100644 --- a/src/NLog/Conditions/ConditionLayoutExpression.cs +++ b/src/NLog/Conditions/ConditionLayoutExpression.cs @@ -31,11 +31,13 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { using System.Text; - using Layouts; using NLog.Internal; + using NLog.Layouts; /// /// Condition layout expression (represented by a string literal @@ -44,7 +46,7 @@ namespace NLog.Conditions internal sealed class ConditionLayoutExpression : ConditionExpression { private readonly SimpleLayout _simpleLayout; - private StringBuilder _fastObjectPool; + private StringBuilder? _fastObjectPool; /// /// Initializes a new instance of the class. diff --git a/src/NLog/Conditions/ConditionLevelExpression.cs b/src/NLog/Conditions/ConditionLevelExpression.cs index b243df6b23..6e38531664 100644 --- a/src/NLog/Conditions/ConditionLevelExpression.cs +++ b/src/NLog/Conditions/ConditionLevelExpression.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { /// diff --git a/src/NLog/Conditions/ConditionLiteralExpression.cs b/src/NLog/Conditions/ConditionLiteralExpression.cs index b4c19c20bb..cd5cfab119 100644 --- a/src/NLog/Conditions/ConditionLiteralExpression.cs +++ b/src/NLog/Conditions/ConditionLiteralExpression.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { using System; @@ -45,7 +47,7 @@ internal sealed class ConditionLiteralExpression : ConditionExpression /// Initializes a new instance of the class. /// /// Literal value. - public ConditionLiteralExpression(object literalValue) + public ConditionLiteralExpression(object? literalValue) { LiteralValue = literalValue; } @@ -54,7 +56,7 @@ public ConditionLiteralExpression(object literalValue) /// Gets the literal value. /// /// The literal value. - public object LiteralValue { get; } + public object? LiteralValue { get; } /// public override string ToString() @@ -68,6 +70,7 @@ public override string ToString() { return $"'{stringValue}'"; } + if (LiteralValue is char charValue) { return $"'{charValue}'"; @@ -81,7 +84,7 @@ public override string ToString() /// /// Evaluation context. Ignored. /// The literal value as passed in the constructor. - protected override object EvaluateNode(LogEventInfo context) + protected override object? EvaluateNode(LogEventInfo context) { return LiteralValue; } diff --git a/src/NLog/Conditions/ConditionLoggerNameExpression.cs b/src/NLog/Conditions/ConditionLoggerNameExpression.cs index fd4a7d0120..dc88c2a93e 100644 --- a/src/NLog/Conditions/ConditionLoggerNameExpression.cs +++ b/src/NLog/Conditions/ConditionLoggerNameExpression.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { /// diff --git a/src/NLog/Conditions/ConditionMessageExpression.cs b/src/NLog/Conditions/ConditionMessageExpression.cs index a7c35b239e..73baa989b2 100644 --- a/src/NLog/Conditions/ConditionMessageExpression.cs +++ b/src/NLog/Conditions/ConditionMessageExpression.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { /// diff --git a/src/NLog/Conditions/ConditionMethodAttribute.cs b/src/NLog/Conditions/ConditionMethodAttribute.cs index bc30c267b6..62686425f1 100644 --- a/src/NLog/Conditions/ConditionMethodAttribute.cs +++ b/src/NLog/Conditions/ConditionMethodAttribute.cs @@ -31,10 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { using System; - using Config; + using NLog.Config; /// /// Marks class as a log event Condition and assigns a name to it. diff --git a/src/NLog/Conditions/ConditionMethodExpression.cs b/src/NLog/Conditions/ConditionMethodExpression.cs index 252fa26fb9..403391aa96 100644 --- a/src/NLog/Conditions/ConditionMethodExpression.cs +++ b/src/NLog/Conditions/ConditionMethodExpression.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { using System; @@ -57,7 +59,7 @@ private ConditionMethodExpression(string methodName, IList } /// - protected override object EvaluateNode(LogEventInfo context) + protected override object? EvaluateNode(LogEventInfo context) { return _method.EvaluateNode(context); } @@ -82,25 +84,25 @@ public override string ToString() return sb.ToString(); } - public static ConditionMethodExpression CreateMethodNoParameters(string conditionMethodName, Func method) + public static ConditionMethodExpression CreateMethodNoParameters(string conditionMethodName, Func method) { return new ConditionMethodExpression(conditionMethodName, ArrayHelper.Empty(), new EvaluateMethodNoParameters(method)); } - public static ConditionMethodExpression CreateMethodOneParameter(string conditionMethodName, Func method, IList methodParameters) + public static ConditionMethodExpression CreateMethodOneParameter(string conditionMethodName, Func method, IList methodParameters) { var methodParameter = methodParameters[0]; return new ConditionMethodExpression(conditionMethodName, methodParameters, new EvaluateMethodOneParameter(method, (logEvent) => methodParameter.Evaluate(logEvent))); } - public static ConditionMethodExpression CreateMethodTwoParameters(string conditionMethodName, Func method, IList methodParameters) + public static ConditionMethodExpression CreateMethodTwoParameters(string conditionMethodName, Func method, IList methodParameters) { var methodParameterArg1 = methodParameters[0]; var methodParameterArg2 = methodParameters[1]; return new ConditionMethodExpression(conditionMethodName, methodParameters, new EvaluateMethodTwoParameters(method, (logEvent) => methodParameterArg1.Evaluate(logEvent), (logEvent) => methodParameterArg2.Evaluate(logEvent))); } - public static ConditionMethodExpression CreateMethodThreeParameters(string conditionMethodName, Func method, IList methodParameters) + public static ConditionMethodExpression CreateMethodThreeParameters(string conditionMethodName, Func method, IList methodParameters) { var methodParameterArg1 = methodParameters[0]; var methodParameterArg2 = methodParameters[1]; @@ -108,26 +110,26 @@ public static ConditionMethodExpression CreateMethodThreeParameters(string condi return new ConditionMethodExpression(conditionMethodName, methodParameters, new EvaluateMethodThreeParameters(method, (logEvent) => methodParameterArg1.Evaluate(logEvent), (logEvent) => methodParameterArg2.Evaluate(logEvent), (logEvent) => methodParameterArg3.Evaluate(logEvent))); } - public static ConditionMethodExpression CreateMethodManyParameters(string conditionMethodName, Func method, IList methodParameters, bool includeLogEvent) + public static ConditionMethodExpression CreateMethodManyParameters(string conditionMethodName, Func method, IList methodParameters, bool includeLogEvent) { return new ConditionMethodExpression(conditionMethodName, methodParameters, new EvaluateMethodManyParameters(method, methodParameters, includeLogEvent)); } private interface IEvaluateMethod { - object EvaluateNode(LogEventInfo logEvent); + object? EvaluateNode(LogEventInfo logEvent); } private sealed class EvaluateMethodNoParameters : IEvaluateMethod { - private readonly Func _method; + private readonly Func _method; - public EvaluateMethodNoParameters(Func method) + public EvaluateMethodNoParameters(Func method) { _method = Guard.ThrowIfNull(method); } - public object EvaluateNode(LogEventInfo logEvent) + public object? EvaluateNode(LogEventInfo logEvent) { return _method(logEvent); } @@ -135,16 +137,16 @@ public object EvaluateNode(LogEventInfo logEvent) private sealed class EvaluateMethodOneParameter : IEvaluateMethod { - private readonly Func _method; - private readonly Func _methodParameter; + private readonly Func _method; + private readonly Func _methodParameter; - public EvaluateMethodOneParameter(Func method, Func methodParameter) + public EvaluateMethodOneParameter(Func method, Func methodParameter) { _method = Guard.ThrowIfNull(method); _methodParameter = Guard.ThrowIfNull(methodParameter); } - public object EvaluateNode(LogEventInfo logEvent) + public object? EvaluateNode(LogEventInfo logEvent) { var inputParameter = _methodParameter(logEvent); return _method(logEvent, inputParameter); @@ -153,18 +155,18 @@ public object EvaluateNode(LogEventInfo logEvent) private sealed class EvaluateMethodTwoParameters : IEvaluateMethod { - private readonly Func _method; - private readonly Func _methodParameterArg1; - private readonly Func _methodParameterArg2; + private readonly Func _method; + private readonly Func _methodParameterArg1; + private readonly Func _methodParameterArg2; - public EvaluateMethodTwoParameters(Func method, Func methodParameterArg1, Func methodParameterArg2) + public EvaluateMethodTwoParameters(Func method, Func methodParameterArg1, Func methodParameterArg2) { _method = Guard.ThrowIfNull(method); _methodParameterArg1 = Guard.ThrowIfNull(methodParameterArg1); _methodParameterArg2 = Guard.ThrowIfNull(methodParameterArg2); } - public object EvaluateNode(LogEventInfo logEvent) + public object? EvaluateNode(LogEventInfo logEvent) { var inputParameter1 = _methodParameterArg1(logEvent); var inputParameter2 = _methodParameterArg2(logEvent); @@ -174,12 +176,12 @@ public object EvaluateNode(LogEventInfo logEvent) private sealed class EvaluateMethodThreeParameters : IEvaluateMethod { - private readonly Func _method; - private readonly Func _methodParameterArg1; - private readonly Func _methodParameterArg2; - private readonly Func _methodParameterArg3; + private readonly Func _method; + private readonly Func _methodParameterArg1; + private readonly Func _methodParameterArg2; + private readonly Func _methodParameterArg3; - public EvaluateMethodThreeParameters(Func method, Func methodParameterArg1, Func methodParameterArg2, Func methodParameterArg3) + public EvaluateMethodThreeParameters(Func method, Func methodParameterArg1, Func methodParameterArg2, Func methodParameterArg3) { _method = Guard.ThrowIfNull(method); _methodParameterArg1 = Guard.ThrowIfNull(methodParameterArg1); @@ -187,7 +189,7 @@ public EvaluateMethodThreeParameters(Func _method; + private readonly Func _method; private readonly IList _methodParameters; private readonly bool _includeLogEvent; - public EvaluateMethodManyParameters(Func method, IList inputParameters, bool includeLogEvent) + public EvaluateMethodManyParameters(Func method, IList inputParameters, bool includeLogEvent) { _method = Guard.ThrowIfNull(method); _methodParameters = Guard.ThrowIfNull(inputParameters); _includeLogEvent = includeLogEvent; } - public object EvaluateNode(LogEventInfo logEvent) + public object? EvaluateNode(LogEventInfo logEvent) { var parameterIndex = _includeLogEvent ? 1 : 0; - var inputParameters = new object[_methodParameters.Count + parameterIndex]; + var inputParameters = new object?[_methodParameters.Count + parameterIndex]; if (_includeLogEvent) inputParameters[0] = logEvent; for (int i = 0; i < _methodParameters.Count; ++i) diff --git a/src/NLog/Conditions/ConditionMethods.cs b/src/NLog/Conditions/ConditionMethods.cs index 044a956c30..bebe6eea9a 100644 --- a/src/NLog/Conditions/ConditionMethods.cs +++ b/src/NLog/Conditions/ConditionMethods.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { using System; @@ -50,7 +52,7 @@ public static class ConditionMethods /// The second value. /// true when two objects are equal, false otherwise. [ConditionMethod("equals")] - public static bool Equals2(object firstValue, object secondValue) + public static bool Equals2(object? firstValue, object? secondValue) { return ReferenceEquals(firstValue, secondValue) || firstValue?.Equals(secondValue) == true; } @@ -63,7 +65,7 @@ public static bool Equals2(object firstValue, object secondValue) /// Optional. If true, case is ignored; if false (default), case is significant. /// true when two strings are equal, false otherwise. [ConditionMethod("strequals")] - public static bool Equals2(string firstValue, string secondValue, [Optional, DefaultParameterValue(false)] bool ignoreCase) + public static bool Equals2(string? firstValue, string? secondValue, [Optional, DefaultParameterValue(false)] bool ignoreCase) { return string.Equals(firstValue, secondValue, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } @@ -76,7 +78,7 @@ public static bool Equals2(string firstValue, string secondValue, [Optional, Def /// Optional. If true (default), case is ignored; if false, case is significant. /// true when the second string is a substring of the first string, false otherwise. [ConditionMethod("contains")] - public static bool Contains(string haystack, string needle, [Optional, DefaultParameterValue(true)] bool ignoreCase) + public static bool Contains(string? haystack, string? needle, [Optional, DefaultParameterValue(true)] bool ignoreCase) { return haystack?.IndexOf(needle, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) >= 0; } @@ -89,7 +91,7 @@ public static bool Contains(string haystack, string needle, [Optional, DefaultPa /// Optional. If true (default), case is ignored; if false, case is significant. /// true when the second string is a prefix of the first string, false otherwise. [ConditionMethod("starts-with")] - public static bool StartsWith(string haystack, string needle, [Optional, DefaultParameterValue(true)] bool ignoreCase) + public static bool StartsWith(string? haystack, string? needle, [Optional, DefaultParameterValue(true)] bool ignoreCase) { return haystack?.StartsWith(needle, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == true; } @@ -102,7 +104,7 @@ public static bool StartsWith(string haystack, string needle, [Optional, Default /// Optional. If true (default), case is ignored; if false, case is significant. /// true when the second string is a prefix of the first string, false otherwise. [ConditionMethod("ends-with")] - public static bool EndsWith(string haystack, string needle, [Optional, DefaultParameterValue(true)] bool ignoreCase) + public static bool EndsWith(string? haystack, string? needle, [Optional, DefaultParameterValue(true)] bool ignoreCase) { return haystack?.EndsWith(needle, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == true; } @@ -113,7 +115,7 @@ public static bool EndsWith(string haystack, string needle, [Optional, DefaultPa /// A string whose lengths is to be evaluated. /// The length of the string. [ConditionMethod("length")] - public static int Length(string text) + public static int Length(string? text) { return text?.Length ?? 0; } diff --git a/src/NLog/Conditions/ConditionMethodsAttribute.cs b/src/NLog/Conditions/ConditionMethodsAttribute.cs index 1adb01681f..ae76082de3 100644 --- a/src/NLog/Conditions/ConditionMethodsAttribute.cs +++ b/src/NLog/Conditions/ConditionMethodsAttribute.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { using System; diff --git a/src/NLog/Conditions/ConditionNotExpression.cs b/src/NLog/Conditions/ConditionNotExpression.cs index 136f08769c..5fb79cf5d5 100644 --- a/src/NLog/Conditions/ConditionNotExpression.cs +++ b/src/NLog/Conditions/ConditionNotExpression.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { /// @@ -62,7 +64,8 @@ public override string ToString() /// protected override object EvaluateNode(LogEventInfo context) { - return (bool)Expression.Evaluate(context) ? BoxedFalse : BoxedTrue; + var objectValue = Expression.Evaluate(context) ?? BoxedFalse; + return (bool)objectValue ? BoxedFalse : BoxedTrue; } } } diff --git a/src/NLog/Conditions/ConditionOrExpression.cs b/src/NLog/Conditions/ConditionOrExpression.cs index 9bc658c791..946daaa2ed 100644 --- a/src/NLog/Conditions/ConditionOrExpression.cs +++ b/src/NLog/Conditions/ConditionOrExpression.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { /// @@ -74,17 +76,13 @@ public override string ToString() /// The value of the alternative operator. protected override object EvaluateNode(LogEventInfo context) { - var bval1 = (bool)LeftExpression.Evaluate(context); - if (bval1) - { + var leftValue = LeftExpression.Evaluate(context) ?? BoxedFalse; + if ((bool)leftValue) return BoxedTrue; - } - var bval2 = (bool)RightExpression.Evaluate(context); - if (bval2) - { + var rightValue = RightExpression.Evaluate(context) ?? BoxedFalse; + if ((bool)rightValue) return BoxedTrue; - } return BoxedFalse; } diff --git a/src/NLog/Conditions/ConditionParseException.cs b/src/NLog/Conditions/ConditionParseException.cs index 5b6c1d73d1..ba5a17c2e1 100644 --- a/src/NLog/Conditions/ConditionParseException.cs +++ b/src/NLog/Conditions/ConditionParseException.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { using System; diff --git a/src/NLog/Conditions/ConditionParser.cs b/src/NLog/Conditions/ConditionParser.cs index a1571f3676..e90f9a2716 100644 --- a/src/NLog/Conditions/ConditionParser.cs +++ b/src/NLog/Conditions/ConditionParser.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { using System; @@ -69,6 +71,7 @@ private ConditionParser(SimpleStringReader stringReader, ConfigurationItemFactor /// The root of the expression syntax tree which can be used to get the value of the condition in a specified context. public static ConditionExpression ParseExpression(string expressionText) { + Guard.ThrowIfNull(expressionText); return ParseExpression(expressionText, ConfigurationItemFactory.Default); } @@ -81,10 +84,7 @@ public static ConditionExpression ParseExpression(string expressionText) /// The root of the expression syntax tree which can be used to get the value of the condition in a specified context. public static ConditionExpression ParseExpression(string expressionText, ConfigurationItemFactory configurationItemFactories) { - if (expressionText is null) - { - return null; - } + Guard.ThrowIfNull(expressionText); var parser = new ConditionParser(new SimpleStringReader(expressionText), configurationItemFactories); ConditionExpression expression = parser.ParseExpression(); @@ -109,7 +109,6 @@ internal static ConditionExpression ParseExpression(SimpleStringReader stringRea { var parser = new ConditionParser(stringReader, configurationItemFactories); ConditionExpression expression = parser.ParseExpression(); - return expression; } @@ -152,30 +151,30 @@ private ConditionMethodExpression CreateMethodExpression(string functionName, Li // Attempt to lookup functionName that can handle the provided number of input-parameters if (inputParameters.Count == 0) { - Func method = _configurationItemFactory.ConditionMethodFactory.TryCreateInstanceWithNoParameters(functionName); + Func? method = _configurationItemFactory.ConditionMethodFactory.TryCreateInstanceWithNoParameters(functionName); if (method != null) return ConditionMethodExpression.CreateMethodNoParameters(functionName, method); } else if (inputParameters.Count == 1) { - Func method = _configurationItemFactory.ConditionMethodFactory.TryCreateInstanceWithOneParameter(functionName); + Func? method = _configurationItemFactory.ConditionMethodFactory.TryCreateInstanceWithOneParameter(functionName); if (method != null) return ConditionMethodExpression.CreateMethodOneParameter(functionName, method, inputParameters); } else if (inputParameters.Count == 2) { - Func method = _configurationItemFactory.ConditionMethodFactory.TryCreateInstanceWithTwoParameters(functionName); + Func? method = _configurationItemFactory.ConditionMethodFactory.TryCreateInstanceWithTwoParameters(functionName); if (method != null) return ConditionMethodExpression.CreateMethodTwoParameters(functionName, method, inputParameters); } else if (inputParameters.Count == 3) { - Func method = _configurationItemFactory.ConditionMethodFactory.TryCreateInstanceWithThreeParameters(functionName); + Func? method = _configurationItemFactory.ConditionMethodFactory.TryCreateInstanceWithThreeParameters(functionName); if (method != null) return ConditionMethodExpression.CreateMethodThreeParameters(functionName, method, inputParameters); } - Func manyParameterMethod = _configurationItemFactory.ConditionMethodFactory.TryCreateInstanceWithManyParameters(functionName, out var manyParameterMinCount, out var manyParameterMaxCount, out var manyParameterWithLogEvent); + Func? manyParameterMethod = _configurationItemFactory.ConditionMethodFactory.TryCreateInstanceWithManyParameters(functionName, out var manyParameterMinCount, out var manyParameterMaxCount, out var manyParameterWithLogEvent); if (manyParameterMethod is null) throw new ConditionParseException($"Unknown condition method '{functionName}'"); if (manyParameterMinCount > inputParameters.Count) @@ -213,7 +212,8 @@ private ConditionExpression ParseLiteralExpression() if (_tokenizer.TokenType == ConditionTokenType.String) { - var simpleLayout = new SimpleLayout(_tokenizer.StringTokenValue, _configurationItemFactory); + var stringTokenValue = _tokenizer.TokenValue.Substring(1, _tokenizer.TokenValue.Length - 2).Replace("''", "'"); + var simpleLayout = new SimpleLayout(stringTokenValue, _configurationItemFactory); _tokenizer.GetNextToken(); if (simpleLayout.IsFixedText) return new ConditionLiteralExpression(simpleLayout.FixedText); @@ -225,7 +225,7 @@ private ConditionExpression ParseLiteralExpression() { string keyword = _tokenizer.EatKeyword(); - if (TryPlainKeywordToExpression(keyword, out var expression)) + if (TryPlainKeywordToExpression(keyword, out var expression) && expression != null) { return expression; } @@ -248,7 +248,7 @@ private ConditionExpression ParseLiteralExpression() /// /// /// success? - private bool TryPlainKeywordToExpression(string keyword, out ConditionExpression expression) + private bool TryPlainKeywordToExpression(string keyword, out ConditionExpression? expression) { if (string.Equals(keyword, "level", StringComparison.OrdinalIgnoreCase)) { diff --git a/src/NLog/Conditions/ConditionRelationalExpression.cs b/src/NLog/Conditions/ConditionRelationalExpression.cs index 73f0bca60f..3f7e5bc9f5 100644 --- a/src/NLog/Conditions/ConditionRelationalExpression.cs +++ b/src/NLog/Conditions/ConditionRelationalExpression.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { using System; @@ -84,8 +86,8 @@ public override string ToString() /// protected override object EvaluateNode(LogEventInfo context) { - object v1 = LeftExpression.Evaluate(context); - object v2 = RightExpression.Evaluate(context); + var v1 = LeftExpression.Evaluate(context); + var v2 = RightExpression.Evaluate(context); return Compare(v1, v2, RelationalOperator) ? BoxedTrue : BoxedFalse; } @@ -97,7 +99,7 @@ protected override object EvaluateNode(LogEventInfo context) /// The second value. /// The relational operator. /// Result of the given relational operator. - private static bool Compare(object leftValue, object rightValue, ConditionRelationalOperator relationalOperator) + private static bool Compare(object? leftValue, object? rightValue, ConditionRelationalOperator relationalOperator) { System.Collections.IComparer comparer = StringComparer.Ordinal; PromoteTypes(ref leftValue, ref rightValue); @@ -131,7 +133,7 @@ private static bool Compare(object leftValue, object rightValue, ConditionRelati /// /// /// - private static void PromoteTypes(ref object leftValue, ref object rightValue) + private static void PromoteTypes(ref object? leftValue, ref object? rightValue) { if (ReferenceEquals(leftValue, rightValue) || leftValue is null || rightValue is null) { diff --git a/src/NLog/Conditions/ConditionRelationalOperator.cs b/src/NLog/Conditions/ConditionRelationalOperator.cs index 55d9f92922..5fa2afd346 100644 --- a/src/NLog/Conditions/ConditionRelationalOperator.cs +++ b/src/NLog/Conditions/ConditionRelationalOperator.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { /// diff --git a/src/NLog/Conditions/ConditionTokenizer.cs b/src/NLog/Conditions/ConditionTokenizer.cs index 48f1ead58d..1fb5b35e09 100644 --- a/src/NLog/Conditions/ConditionTokenizer.cs +++ b/src/NLog/Conditions/ConditionTokenizer.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Conditions { using System; @@ -53,6 +55,7 @@ public ConditionTokenizer(SimpleStringReader stringReader) { _stringReader = stringReader; TokenType = ConditionTokenType.BeginningOfInput; + TokenValue = string.Empty; GetNextToken(); } @@ -60,16 +63,6 @@ public ConditionTokenizer(SimpleStringReader stringReader) public string TokenValue { get; private set; } - public string StringTokenValue - { - get - { - string s = TokenValue; - - return s.Substring(1, s.Length - 2).Replace("''", "'"); - } - } - /// /// Asserts current token type and advances to the next token. /// diff --git a/src/NLog/Config/MethodFactory.cs b/src/NLog/Config/MethodFactory.cs index 39e55d722a..265860b24d 100644 --- a/src/NLog/Config/MethodFactory.cs +++ b/src/NLog/Config/MethodFactory.cs @@ -52,22 +52,22 @@ internal sealed class MethodFactory : IFactory struct MethodDetails { public readonly MethodInfo MethodInfo; - public readonly Func NoParameters; - public readonly Func OneParameter; - public readonly Func TwoParameters; - public readonly Func ThreeParameters; - public readonly Func ManyParameters; + public readonly Func NoParameters; + public readonly Func OneParameter; + public readonly Func TwoParameters; + public readonly Func ThreeParameters; + public readonly Func ManyParameters; public readonly int ManyParameterMinCount; public readonly int ManyParameterMaxCount; public readonly bool ManyParameterWithLogEvent; public MethodDetails( MethodInfo methodInfo, - Func noParameters, - Func oneParameter, - Func twoParameters, - Func threeParameters, - Func manyParameters, + Func noParameters, + Func oneParameter, + Func twoParameters, + Func threeParameters, + Func manyParameters, int manyParameterMinCount, int manyParameterMaxCount, bool manyParameterWithLogEvent) @@ -203,7 +203,7 @@ internal void RegisterDefinition(string methodName, MethodInfo methodInfo) } } - private static object InvokeMethodInfo(MethodInfo methodInfo, object[] methodArgs) + private static object InvokeMethodInfo(MethodInfo methodInfo, object?[] methodArgs) { try { @@ -242,12 +242,12 @@ private static object[] ResolveDefaultMethodParameters(MethodInfo methodInfo, ou return defaultMethodParameters; } - private static object[] ResolveMethodParameters(object[] defaultMethodParameters, object[] inputParameters) + private static object?[] ResolveMethodParameters(object?[] defaultMethodParameters, object?[] inputParameters) { if (defaultMethodParameters.Length == inputParameters.Length) return inputParameters; - object[] methodParameters = new object[defaultMethodParameters.Length]; + object?[] methodParameters = new object[defaultMethodParameters.Length]; for (int i = 0; i < inputParameters.Length; ++i) methodParameters[i] = inputParameters[i]; for (int i = inputParameters.Length; i < defaultMethodParameters.Length; ++i) @@ -255,18 +255,18 @@ private static object[] ResolveMethodParameters(object[] defaultMethodParameters return methodParameters; } - private static object[] ResolveMethodParameters(object[] defaultMethodParameters, object inputParameterArg1) + private static object?[] ResolveMethodParameters(object?[] defaultMethodParameters, object? inputParameterArg1) { - object[] methodParameters = new object[defaultMethodParameters.Length]; + object?[] methodParameters = new object[defaultMethodParameters.Length]; methodParameters[0] = inputParameterArg1; for (int i = 1; i < defaultMethodParameters.Length; ++i) methodParameters[i] = defaultMethodParameters[i]; return methodParameters; } - private static object[] ResolveMethodParameters(object[] defaultMethodParameters, object inputParameterArg1, object inputParameterArg2) + private static object?[] ResolveMethodParameters(object?[] defaultMethodParameters, object? inputParameterArg1, object? inputParameterArg2) { - object[] methodParameters = new object[defaultMethodParameters.Length]; + object?[] methodParameters = new object[defaultMethodParameters.Length]; methodParameters[0] = inputParameterArg1; methodParameters[1] = inputParameterArg2; for (int i = 2; i < defaultMethodParameters.Length; ++i) @@ -274,9 +274,9 @@ private static object[] ResolveMethodParameters(object[] defaultMethodParameters return methodParameters; } - private static object[] ResolveMethodParameters(object[] defaultMethodParameters, object inputParameterArg1, object inputParameterArg2, object inputParameterArg3) + private static object?[] ResolveMethodParameters(object?[] defaultMethodParameters, object? inputParameterArg1, object? inputParameterArg2, object? inputParameterArg3) { - object[] methodParameters = new object[defaultMethodParameters.Length]; + object?[] methodParameters = new object[defaultMethodParameters.Length]; methodParameters[0] = inputParameterArg1; methodParameters[1] = inputParameterArg2; methodParameters[2] = inputParameterArg3; @@ -285,9 +285,9 @@ private static object[] ResolveMethodParameters(object[] defaultMethodParameters return methodParameters; } - private static object[] ResolveMethodParameters(object[] defaultMethodParameters, object inputParameterArg1, object inputParameterArg2, object inputParameterArg3, object inputParameterArg4) + private static object?[] ResolveMethodParameters(object?[] defaultMethodParameters, object? inputParameterArg1, object? inputParameterArg2, object? inputParameterArg3, object? inputParameterArg4) { - object[] methodParameters = new object[defaultMethodParameters.Length]; + object?[] methodParameters = new object[defaultMethodParameters.Length]; methodParameters[0] = inputParameterArg1; methodParameters[1] = inputParameterArg2; methodParameters[2] = inputParameterArg3; @@ -297,7 +297,7 @@ private static object[] ResolveMethodParameters(object[] defaultMethodParameters return methodParameters; } - public void RegisterNoParameters(string methodName, Func noParameters, MethodInfo? legacyMethodInfo = null) + public void RegisterNoParameters(string methodName, Func noParameters, MethodInfo? legacyMethodInfo = null) { lock (_nameToMethodDetails) { @@ -307,7 +307,7 @@ public void RegisterNoParameters(string methodName, Func n } } - public void RegisterOneParameter(string methodName, Func oneParameter, MethodInfo? legacyMethodInfo = null) + public void RegisterOneParameter(string methodName, Func oneParameter, MethodInfo? legacyMethodInfo = null) { lock (_nameToMethodDetails) { @@ -317,7 +317,7 @@ public void RegisterOneParameter(string methodName, Func twoParameters, MethodInfo? legacyMethodInfo = null) + public void RegisterTwoParameters(string methodName, Func twoParameters, MethodInfo? legacyMethodInfo = null) { lock (_nameToMethodDetails) { @@ -327,7 +327,7 @@ public void RegisterTwoParameters(string methodName, Func threeParameters, MethodInfo? legacyMethodInfo = null) + public void RegisterThreeParameters(string methodName, Func threeParameters, MethodInfo? legacyMethodInfo = null) { lock (_nameToMethodDetails) { @@ -337,7 +337,7 @@ public void RegisterThreeParameters(string methodName, Func manyParameters, int manyParameterMinCount, int manyParameterMaxCount, bool manyParameterWithLogEvent, MethodInfo? legacyMethodInfo = null) + public void RegisterManyParameters(string methodName, Func manyParameters, int manyParameterMinCount, int manyParameterMaxCount, bool manyParameterWithLogEvent, MethodInfo? legacyMethodInfo = null) { lock (_nameToMethodDetails) { @@ -347,7 +347,7 @@ public void RegisterManyParameters(string methodName, Func man } } - public Func? TryCreateInstanceWithNoParameters(string methodName) + public Func? TryCreateInstanceWithNoParameters(string methodName) { lock (_nameToMethodDetails) { @@ -358,7 +358,7 @@ public void RegisterManyParameters(string methodName, Func man } } - public Func? TryCreateInstanceWithOneParameter(string methodName) + public Func? TryCreateInstanceWithOneParameter(string methodName) { lock (_nameToMethodDetails) { @@ -369,7 +369,7 @@ public void RegisterManyParameters(string methodName, Func man } } - public Func? TryCreateInstanceWithTwoParameters(string methodName) + public Func? TryCreateInstanceWithTwoParameters(string methodName) { lock (_nameToMethodDetails) { @@ -380,7 +380,7 @@ public void RegisterManyParameters(string methodName, Func man } } - public Func? TryCreateInstanceWithThreeParameters(string methodName) + public Func? TryCreateInstanceWithThreeParameters(string methodName) { lock (_nameToMethodDetails) { @@ -391,7 +391,7 @@ public void RegisterManyParameters(string methodName, Func man } } - public Func? TryCreateInstanceWithManyParameters(string methodName, out int manyParameterMinCount, out int manyParameterMaxCount, out bool manyParameterWithLogEvent) + public Func? TryCreateInstanceWithManyParameters(string methodName, out int manyParameterMinCount, out int manyParameterMaxCount, out bool manyParameterWithLogEvent) { lock (_nameToMethodDetails) { @@ -409,28 +409,28 @@ public void RegisterManyParameters(string methodName, Func man manyParameterMaxCount = 3; manyParameterMinCount = methodDetails.TwoParameters is null ? 3 : 2; manyParameterWithLogEvent = true; - return new Func(args => methodDetails.ThreeParameters((LogEventInfo)args[0], args[1], args[2], args[3])); + return new Func(args => methodDetails.ThreeParameters((LogEventInfo)(args[0] ?? throw new ArgumentNullException(nameof(LogEventInfo))), args[1], args[2], args[3])); } else if (methodDetails.TwoParameters != null) { manyParameterMaxCount = 2; manyParameterMinCount = methodDetails.OneParameter is null ? 2 : 1; manyParameterWithLogEvent = true; - return new Func(args => methodDetails.TwoParameters((LogEventInfo)args[0], args[1], args[2])); + return new Func(args => methodDetails.TwoParameters((LogEventInfo)(args[0] ?? throw new ArgumentNullException(nameof(LogEventInfo))), args[1], args[2])); } else if (methodDetails.OneParameter != null) { manyParameterMaxCount = 1; manyParameterMinCount = methodDetails.NoParameters is null ? 1 : 0; manyParameterWithLogEvent = true; - return new Func(args => methodDetails.OneParameter((LogEventInfo)args[0], args[1])); + return new Func(args => methodDetails.OneParameter((LogEventInfo)(args[0] ?? throw new ArgumentNullException(nameof(LogEventInfo))), args[1])); } else if (methodDetails.NoParameters != null) { manyParameterMaxCount = 0; manyParameterMinCount = 0; manyParameterWithLogEvent = true; - return new Func(args => methodDetails.NoParameters((LogEventInfo)args[0])); + return new Func(args => methodDetails.NoParameters((LogEventInfo)(args[0] ?? throw new ArgumentNullException(nameof(LogEventInfo))))); } } manyParameterMinCount = 0; diff --git a/tests/NLog.UnitTests/Conditions/ConditionParserTests.cs b/tests/NLog.UnitTests/Conditions/ConditionParserTests.cs index f8a81430c0..998015e9eb 100644 --- a/tests/NLog.UnitTests/Conditions/ConditionParserTests.cs +++ b/tests/NLog.UnitTests/Conditions/ConditionParserTests.cs @@ -33,6 +33,7 @@ namespace NLog.UnitTests.Conditions { + using System; using NLog.Conditions; using NLog.Config; using NLog.Internal; @@ -45,7 +46,7 @@ public class ConditionParserTests : NLogTestBase [Fact] public void ParseNullText() { - Assert.Null(ConditionParser.ParseExpression(null)); + Assert.Throws(() => ConditionParser.ParseExpression(null)); } [Fact] From 5834733c9306d26b151847ed3274887a298a6658 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 11 May 2025 21:17:22 +0200 Subject: [PATCH 113/224] Replaced RequiredParameter-attribute with nullable references (#5814) --- src/NLog.Database/DatabaseCommandInfo.cs | 4 +- .../DatabaseObjectPropertyInfo.cs | 2 - src/NLog.Database/DatabaseParameterInfo.cs | 2 - src/NLog.Database/DatabaseTarget.cs | 43 ++++++++++-- .../RegexReplaceLayoutRendererWrapper.cs | 6 +- .../ConcurrentFileTarget.cs | 6 +- .../Internal/FilePathLayout.cs | 2 +- src/NLog.Targets.Mail/MailTarget.cs | 44 +++++++----- .../Layouts/GelfLayout.cs | 4 +- .../Layouts/Log4JXmlEventParameter.cs | 6 +- .../Layouts/SyslogLayout.cs | 2 +- .../WebServiceTarget.cs | 12 +++- .../RegistryLayoutRenderer.cs | 12 +++- src/NLog/Conditions/ConditionExpression.cs | 5 ++ .../Conditions/ConditionLiteralExpression.cs | 4 ++ src/NLog/Conditions/ConditionParser.cs | 11 ++- src/NLog/Config/LoggingConfiguration.cs | 18 ----- src/NLog/Config/LoggingConfigurationParser.cs | 18 +++-- src/NLog/Config/RequiredParameterAttribute.cs | 1 + src/NLog/Filters/ConditionBasedFilter.cs | 8 +-- src/NLog/Filters/Filter.cs | 2 + src/NLog/Filters/FilterAttribute.cs | 4 +- src/NLog/Filters/LayoutBasedFilter.cs | 8 +-- src/NLog/Filters/WhenContainsFilter.cs | 8 +-- src/NLog/Filters/WhenEqualFilter.cs | 6 +- src/NLog/Filters/WhenMethodFilter.cs | 2 + src/NLog/Filters/WhenNotContainsFilter.cs | 8 +-- src/NLog/Filters/WhenNotEqualFilter.cs | 6 +- src/NLog/Filters/WhenRepeatedFilter.cs | 16 +++-- src/NLog/Internal/PropertyHelper.cs | 27 ++------ src/NLog/Internal/StringBuilderExt.cs | 2 +- .../AllEventPropertiesLayoutRenderer.cs | 2 +- .../AppSettingLayoutRenderer.cs | 6 +- .../EnvironmentLayoutRenderer.cs | 10 ++- .../EventPropertiesLayoutRenderer.cs | 12 +++- .../ExceptionDataLayoutRenderer.cs | 10 ++- .../LayoutRenderers/FuncLayoutRenderer.cs | 2 +- src/NLog/LayoutRenderers/GdcLayoutRenderer.cs | 12 +++- .../InstallContextLayoutRenderer.cs | 1 - src/NLog/LayoutRenderers/LayoutRenderer.cs | 1 - .../ScopeContextNestedStatesLayoutRenderer.cs | 2 +- .../ScopeContextPropertyLayoutRenderer.cs | 12 +++- .../LayoutRenderers/VariableLayoutRenderer.cs | 4 +- .../Wrappers/ReplaceLayoutRendererWrapper.cs | 7 +- .../WhenEmptyLayoutRendererWrapper.cs | 6 +- .../Wrappers/WhenLayoutRendererWrapper.cs | 14 +++- src/NLog/Layouts/CSV/CsvColumn.cs | 3 +- src/NLog/Layouts/CSV/CsvLayout.cs | 11 +++ src/NLog/Layouts/JSON/JsonAttribute.cs | 2 - src/NLog/Layouts/JSON/JsonLayout.cs | 3 + src/NLog/Layouts/Layout.cs | 15 ++-- src/NLog/Layouts/LayoutWithHeaderAndFooter.cs | 2 +- src/NLog/Layouts/SimpleLayout.cs | 7 +- src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs | 7 +- src/NLog/Layouts/XML/XmlAttribute.cs | 2 - src/NLog/Layouts/XML/XmlElement.cs | 1 - src/NLog/Layouts/XML/XmlElementBase.cs | 16 +++-- src/NLog/Targets/AsyncTaskTarget.cs | 1 - .../Targets/ConsoleRowHighlightingRule.cs | 1 - src/NLog/Targets/EventLogTarget.cs | 6 +- src/NLog/Targets/FileTarget.cs | 8 ++- src/NLog/Targets/MethodCallParameter.cs | 1 - src/NLog/Targets/MethodCallTargetBase.cs | 2 +- src/NLog/Targets/Target.cs | 2 - src/NLog/Targets/TargetPropertyWithContext.cs | 2 - src/NLog/Targets/TargetWithContext.cs | 17 ++++- src/NLog/Targets/TargetWithLayout.cs | 1 - .../TargetWithLayoutHeaderAndFooter.cs | 1 - src/NLog/Targets/Wrappers/FilteringRule.cs | 4 +- .../Wrappers/FilteringTargetWrapper.cs | 10 ++- .../Targets/Wrappers/GroupByTargetWrapper.cs | 12 +++- .../Wrappers/PostFilteringTargetWrapper.cs | 15 ++++ .../Targets/Wrappers/WrapperTargetBase.cs | 10 ++- .../MailTargetTests.cs | 46 +++++++------ tests/NLog.UnitTests/ApiTests.cs | 19 +----- .../Config/TargetConfigurationTests.cs | 68 ++----------------- .../AllEventPropertiesTests.cs | 1 - .../ExceptionDataLayoutRendererTests.cs | 44 ------------ .../Layouts/SimpleLayoutOutputTests.cs | 2 + .../Targets/EventLogTargetTests.cs | 4 +- 80 files changed, 387 insertions(+), 359 deletions(-) diff --git a/src/NLog.Database/DatabaseCommandInfo.cs b/src/NLog.Database/DatabaseCommandInfo.cs index fc8868b6ac..d28cc9774b 100644 --- a/src/NLog.Database/DatabaseCommandInfo.cs +++ b/src/NLog.Database/DatabaseCommandInfo.cs @@ -49,7 +49,6 @@ public class DatabaseCommandInfo /// /// The type of the command. /// - [RequiredParameter] public CommandType CommandType { get; set; } = CommandType.Text; /// @@ -62,8 +61,7 @@ public class DatabaseCommandInfo /// Gets or sets the command text. /// /// - [RequiredParameter] - public Layout Text { get; set; } + public Layout Text { get; set; } = Layout.Empty; /// /// Gets or sets a value indicating whether to ignore failures. diff --git a/src/NLog.Database/DatabaseObjectPropertyInfo.cs b/src/NLog.Database/DatabaseObjectPropertyInfo.cs index 0af15a4723..432bd36e16 100644 --- a/src/NLog.Database/DatabaseObjectPropertyInfo.cs +++ b/src/NLog.Database/DatabaseObjectPropertyInfo.cs @@ -58,14 +58,12 @@ public DatabaseObjectPropertyInfo() /// Gets or sets the name for the object-property /// /// - [RequiredParameter] public string Name { get; set; } /// /// Gets or sets the value to assign on the object-property /// /// - [RequiredParameter] public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } /// diff --git a/src/NLog.Database/DatabaseParameterInfo.cs b/src/NLog.Database/DatabaseParameterInfo.cs index e56b1f17ff..7cb808e2a2 100644 --- a/src/NLog.Database/DatabaseParameterInfo.cs +++ b/src/NLog.Database/DatabaseParameterInfo.cs @@ -106,14 +106,12 @@ public DatabaseParameterInfo(string parameterName, Layout parameterLayout) /// Gets or sets the database parameter name. /// /// - [RequiredParameter] public string Name { get; set; } /// /// Gets or sets the layout that should be use to calculate the value for the parameter. /// /// - [RequiredParameter] public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } /// diff --git a/src/NLog.Database/DatabaseTarget.cs b/src/NLog.Database/DatabaseTarget.cs index 9ad6aab660..20daa639f7 100644 --- a/src/NLog.Database/DatabaseTarget.cs +++ b/src/NLog.Database/DatabaseTarget.cs @@ -88,8 +88,6 @@ public class DatabaseTarget : Target, IInstallable /// public DatabaseTarget() { - DBProvider = "sqlserver"; - DBHost = "."; #if NETFRAMEWORK ConnectionStringsSettings = ConfigurationManager.ConnectionStrings; #endif @@ -133,9 +131,8 @@ public DatabaseTarget(string name) : this() /// /// /// - [RequiredParameter] [DefaultValue("sqlserver")] - public string DBProvider { get; set; } + public string DBProvider { get; set; } = "sqlserver"; #if NETFRAMEWORK /// @@ -186,7 +183,7 @@ public DatabaseTarget(string name) : this() /// connection string. /// /// - public Layout DBHost { get; set; } + public Layout DBHost { get; set; } = "."; /// /// Gets or sets the database user name. If the ConnectionString is not provided @@ -237,8 +234,7 @@ public Layout DBPassword /// The layout renderers should be specified as <parameter /> elements instead. /// /// - [RequiredParameter] - public Layout CommandText { get; set; } + public Layout CommandText { get; set; } = Layout.Empty; /// /// Gets or sets the type of the SQL command to be run on each log level. @@ -378,6 +374,9 @@ protected override void InitializeTarget() { base.InitializeTarget(); + if (CommandText is null || ReferenceEquals(CommandText, Layout.Empty)) + throw new NLogConfigurationException("DatabaseTarget CommandText-property must be assigned. CommandText required for executing SQL commands."); + bool foundProvider = false; string providerName = string.Empty; @@ -432,6 +431,36 @@ protected override void InitializeTarget() throw; } } + + foreach (var parameter in Parameters) + { + if (string.IsNullOrEmpty(parameter.Name)) + throw new NLogConfigurationException($"{this}: Contains invalid Database-Parameter with unassigned Name-property"); + } + + foreach (var property in ConnectionProperties) + { + if (string.IsNullOrEmpty(property.Name)) + throw new NLogConfigurationException($"{this}: Contains invalid Connection-option with unassigned Name-property"); + } + + foreach (var property in CommandProperties) + { + if (string.IsNullOrEmpty(property.Name)) + throw new NLogConfigurationException($"{this}: Contains invalid Command-option with unassigned Name-property"); + } + + foreach (var command in InstallDdlCommands) + { + if (command.Text is null || ReferenceEquals(command.Text, Layout.Empty)) + throw new NLogConfigurationException($"{this}: Contains invalid Install-Command with unassigned Text-property"); + } + + foreach (var command in UninstallDdlCommands) + { + if (command.Text is null || ReferenceEquals(command.Text, Layout.Empty)) + throw new NLogConfigurationException($"{this}: Contains invalid Uninstall-Command with unassigned Text-property"); + } } private string InitConnectionString(string providerName) diff --git a/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs b/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs index f2e5bfe04d..1f36c70456 100644 --- a/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs +++ b/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs @@ -64,8 +64,7 @@ public sealed class RegexReplaceLayoutRendererWrapper : WrapperLayoutRendererBas /// /// The text search for. /// - [RequiredParameter] - public string SearchFor { get; set; } + public string SearchFor { get; set; } = string.Empty; /// /// Gets or sets the replacement string. @@ -107,6 +106,9 @@ protected override void InitializeLayoutRenderer() { base.InitializeLayoutRenderer(); + if (string.IsNullOrEmpty(SearchFor)) + throw new NLogConfigurationException("RegEx-Replace-LayoutRenderer SearchFor-property must be assigned. Searching for blank value not supported."); + _regexHelper = new RegexHelper() { IgnoreCase = IgnoreCase, diff --git a/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs b/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs index 9b51038beb..0e9c38cc81 100644 --- a/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs +++ b/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs @@ -183,12 +183,11 @@ public ConcurrentFileTarget(string name) : this() /// You can combine as many of the layout renderers as you want to produce an arbitrary log file name. /// /// - [RequiredParameter] public Layout FileName { get { - return _fullFileName?.GetLayout(); + return _fullFileName?.GetLayout() ?? Layout.Empty; } set { @@ -897,6 +896,9 @@ protected override void InitializeTarget() { base.InitializeTarget(); + if (FileName is null || ReferenceEquals(FileName, Layout.Empty)) + throw new NLogConfigurationException("FileTarget FileName-property must be assigned. FileName is needed for file writing."); + var appenderFactory = GetFileAppenderFactory(); if (InternalLogger.IsTraceEnabled) { diff --git a/src/NLog.Targets.ConcurrentFile/Internal/FilePathLayout.cs b/src/NLog.Targets.ConcurrentFile/Internal/FilePathLayout.cs index e388123bff..cb833aa98e 100644 --- a/src/NLog.Targets.ConcurrentFile/Internal/FilePathLayout.cs +++ b/src/NLog.Targets.ConcurrentFile/Internal/FilePathLayout.cs @@ -87,7 +87,7 @@ internal sealed class FilePathLayout /// Initializes a new instance of the class. public FilePathLayout(Layout layout, bool cleanupInvalidChars, FilePathKind filePathKind) { - _layout = layout ?? new SimpleLayout(null); + _layout = layout ?? Layout.Empty; _cleanupInvalidChars = cleanupInvalidChars; _filePathKind = filePathKind == FilePathKind.Unknown ? DetectFilePathKind(_layout as SimpleLayout) : filePathKind; _cleanedFixedResult = (_layout as SimpleLayout)?.FixedText; diff --git a/src/NLog.Targets.Mail/MailTarget.cs b/src/NLog.Targets.Mail/MailTarget.cs index bfd095c5a7..d7809b0acd 100644 --- a/src/NLog.Targets.Mail/MailTarget.cs +++ b/src/NLog.Targets.Mail/MailTarget.cs @@ -88,8 +88,6 @@ public class MailTarget : TargetWithLayoutHeaderAndFooter { private const string RequiredPropertyIsEmptyFormat = "After the processing of the MailTarget's '{0}' property it appears to be empty. The email message will not be sent."; - private Layout _from; - /// /// Initializes a new instance of the class. /// @@ -152,7 +150,6 @@ internal SmtpSection SmtpSection /// Gets or sets sender's email address (e.g. joe@domain.com). /// /// - [RequiredParameter] public Layout From { get @@ -164,35 +161,37 @@ public Layout From // It will do so only if the 'From' attribute in system.net/mailSettings/smtp is not empty. //only use from config when not set in current - if (UseSystemNetMailSettings && _from is null) + if (UseSystemNetMailSettings && (_from is null || ReferenceEquals(_from, Layout.Empty))) { var from = SmtpSection.From; - return from; + if (string.IsNullOrEmpty(from)) + return Layout.Empty; + _from = from; } #endif return _from; } set { _from = value; } } + private Layout _from = Layout.Empty; /// /// Gets or sets recipients' email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). /// /// - [RequiredParameter] - public Layout To { get; set; } + public Layout To { get; set; } = Layout.Empty; /// /// Gets or sets CC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). /// /// - public Layout CC { get; set; } + public Layout CC { get; set; } = Layout.Empty; /// /// Gets or sets BCC email addresses separated by semicolons (e.g. john@domain.com;jane@domain.com). /// /// - public Layout Bcc { get; set; } + public Layout Bcc { get; set; } = Layout.Empty; /// /// Gets or sets a value indicating whether to add new lines between log entries. @@ -205,7 +204,6 @@ public Layout From /// Gets or sets the mail subject. /// /// - [RequiredParameter] public Layout Subject { get; set; } = "Message from NLog on ${machinename}"; /// @@ -235,7 +233,7 @@ public Layout Body /// Gets or sets SMTP Server to be used for sending. /// /// - public Layout SmtpServer { get; set; } + public Layout SmtpServer { get; set; } = Layout.Empty; /// /// Gets or sets SMTP Authentication mode. @@ -247,13 +245,13 @@ public Layout Body /// Gets or sets the username used to connect to SMTP server (used when SmtpAuthentication is set to "basic"). /// /// - public Layout SmtpUserName { get; set; } + public Layout SmtpUserName { get; set; } = Layout.Empty; /// /// Gets or sets the password used to authenticate against SMTP server (used when SmtpAuthentication is set to "basic"). /// /// - public Layout SmtpPassword { get; set; } + public Layout SmtpPassword { get; set; } = Layout.Empty; /// /// Gets or sets a value indicating whether SSL (secure sockets layer) should be used when communicating with SMTP server. @@ -283,7 +281,7 @@ public Layout Body /// Gets or sets the folder where applications save mail messages to be processed by the local SMTP server. /// /// - public Layout PickupDirectoryLocation { get; set; } + public Layout PickupDirectoryLocation { get; set; } = Layout.Empty; /// /// Gets or sets the priority used for sending mails. @@ -528,14 +526,24 @@ internal static string ConvertDirectoryLocation(string pickupDirectoryLocation) private void CheckRequiredParameters() { - if (!UseSystemNetMailSettings && DeliveryMethod == SmtpDeliveryMethod.Network && SmtpServer is null) + if (To is null || ReferenceEquals(To, Layout.Empty)) + { + throw new NLogConfigurationException($"MailTarget '{nameof(To)}'-property must be assigned. Destination To-address required for email."); + } + + if (From is null || ReferenceEquals(From, Layout.Empty)) + { + throw new NLogConfigurationException($"MailTarget '{nameof(From)}'-property must be assigned. Sender From-address required for email."); + } + + if (!UseSystemNetMailSettings && DeliveryMethod == SmtpDeliveryMethod.Network && (SmtpServer is null || ReferenceEquals(SmtpServer, Layout.Empty))) { - throw new NLogConfigurationException($"The MailTarget's '{nameof(SmtpServer)}' properties are not set - but needed because useSystemNetMailSettings=false and DeliveryMethod=Network. The email message will not be sent."); + throw new NLogConfigurationException($"MailTarget '{nameof(SmtpServer)}'-property must be assigned. Required because useSystemNetMailSettings=false and DeliveryMethod=Network."); } - if (!UseSystemNetMailSettings && DeliveryMethod == SmtpDeliveryMethod.SpecifiedPickupDirectory && PickupDirectoryLocation is null) + if (!UseSystemNetMailSettings && DeliveryMethod == SmtpDeliveryMethod.SpecifiedPickupDirectory && (PickupDirectoryLocation is null || ReferenceEquals(PickupDirectoryLocation, Layout.Empty))) { - throw new NLogConfigurationException($"The MailTarget's '{nameof(PickupDirectoryLocation)}' properties are not set - but needed because useSystemNetMailSettings=false and DeliveryMethod=SpecifiedPickupDirectory. The email message will not be sent."); + throw new NLogConfigurationException($"MailTarget '{nameof(PickupDirectoryLocation)}'-property must be assigned. Required because useSystemNetMailSettings=false and DeliveryMethod=SpecifiedPickupDirectory."); } } diff --git a/src/NLog.Targets.Network/Layouts/GelfLayout.cs b/src/NLog.Targets.Network/Layouts/GelfLayout.cs index 899dd6a234..e6a6896f44 100644 --- a/src/NLog.Targets.Network/Layouts/GelfLayout.cs +++ b/src/NLog.Targets.Network/Layouts/GelfLayout.cs @@ -116,8 +116,8 @@ public Layout GelfFacility /// Disables to capture ScopeContext-properties from active thread context /// public LayoutRenderer DisableThreadAgnostic => IncludeScopeProperties ? _disableThreadAgnostic : (IncludeEventProperties ? _enableThreadAgnosticImmutable : null); - private static readonly LayoutRenderer _disableThreadAgnostic = new ScopeContextPropertyLayoutRenderer() { Item = string.Empty }; - private static readonly LayoutRenderer _enableThreadAgnosticImmutable = new ExceptionDataLayoutRenderer() { Item = string.Empty }; + private static readonly LayoutRenderer _disableThreadAgnostic = new FuncLayoutRenderer(string.Empty, (evt, cfg) => string.Empty); + private static readonly LayoutRenderer _enableThreadAgnosticImmutable = new ExceptionDataLayoutRenderer() { Item = " " }; /// /// Initializes a new instance of the class. diff --git a/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs index ea18e64d73..ddf2ea04e7 100644 --- a/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs @@ -53,15 +53,13 @@ public Log4JXmlEventParameter() /// Gets or sets viewer parameter name. /// /// - [RequiredParameter] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; /// /// Gets or sets the layout that should be use to calculate the value for the parameter. /// /// - [RequiredParameter] - public Layout Layout { get; set; } + public Layout Layout { get; set; } = Layout.Empty; /// /// Gets or sets whether an attribute with empty value should be included in the output diff --git a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs index 2e52213e34..bfe20263b3 100644 --- a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs +++ b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs @@ -147,7 +147,7 @@ public Layout SyslogProcessId /// Disables to capture volatile LogEvent-properties from active thread context /// public LayoutRenderer DisableThreadAgnostic => IncludeEventProperties ? _enableThreadAgnosticImmutable : null; - private static readonly LayoutRenderer _enableThreadAgnosticImmutable = new ExceptionDataLayoutRenderer() { Item = string.Empty }; + private static readonly LayoutRenderer _enableThreadAgnosticImmutable = new ExceptionDataLayoutRenderer() { Item = " " }; /// /// Initializes a new instance of the class. diff --git a/src/NLog.Targets.WebService/WebServiceTarget.cs b/src/NLog.Targets.WebService/WebServiceTarget.cs index 3d0a4897b0..eb107be71e 100644 --- a/src/NLog.Targets.WebService/WebServiceTarget.cs +++ b/src/NLog.Targets.WebService/WebServiceTarget.cs @@ -117,8 +117,7 @@ public WebServiceTarget(string name) : this() /// Gets or sets the web service URL. /// /// - [RequiredParameter] - public Layout Url { get; set; } + public Layout Url { get; set; } = new Layout((Uri)null); /// /// Gets or sets the value of the User-agent HTTP header. @@ -464,6 +463,15 @@ private void DoInvokeCompleted(AsyncContinuation continuation, Exception ex) continuation(ex); } + /// + protected override void InitializeTarget() + { + base.InitializeTarget(); + + if (Url is null || (Url.IsFixed && Url.FixedValue is null)) + throw new NLogConfigurationException("WebServiceTarget Url-property must be assigned. WebRequest requires Url-address."); + } + /// protected override void FlushAsync(AsyncContinuation asyncContinuation) { diff --git a/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs b/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs index 3883b68392..38aa9aa7ac 100644 --- a/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs +++ b/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs @@ -100,8 +100,16 @@ public class RegistryLayoutRenderer : LayoutRenderer /// /// /// - [RequiredParameter] - public Layout Key { get; set; } + public Layout Key { get; set; } = Layout.Empty; + + /// + protected override void InitializeLayoutRenderer() + { + base.InitializeLayoutRenderer(); + + if (Key is null || ReferenceEquals(Key, Layout.Empty)) + throw new NLogConfigurationException("Registry-LayoutRenderer Key-property must be assigned. Lookup blank value not supported."); + } /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/Conditions/ConditionExpression.cs b/src/NLog/Conditions/ConditionExpression.cs index 349ecdba9d..36526ac4dc 100644 --- a/src/NLog/Conditions/ConditionExpression.cs +++ b/src/NLog/Conditions/ConditionExpression.cs @@ -50,6 +50,11 @@ public abstract class ConditionExpression internal static readonly object BoxedTrue = true; internal static readonly object BoxedFalse = false; + /// + /// Default Condition-value that evalutes to string.Empty + /// + public static ConditionExpression Empty { get; } = new ConditionLiteralExpression(string.Empty); + /// /// Converts condition text to a condition expression tree. /// diff --git a/src/NLog/Conditions/ConditionLiteralExpression.cs b/src/NLog/Conditions/ConditionLiteralExpression.cs index cd5cfab119..a46b8091df 100644 --- a/src/NLog/Conditions/ConditionLiteralExpression.cs +++ b/src/NLog/Conditions/ConditionLiteralExpression.cs @@ -43,6 +43,10 @@ namespace NLog.Conditions /// internal sealed class ConditionLiteralExpression : ConditionExpression { + public static readonly ConditionLiteralExpression Null = new ConditionLiteralExpression(null); + public static readonly ConditionLiteralExpression True = new ConditionLiteralExpression(BoxedTrue); + public static readonly ConditionLiteralExpression False = new ConditionLiteralExpression(BoxedFalse); + /// /// Initializes a new instance of the class. /// diff --git a/src/NLog/Conditions/ConditionParser.cs b/src/NLog/Conditions/ConditionParser.cs index e90f9a2716..3de397b5c5 100644 --- a/src/NLog/Conditions/ConditionParser.cs +++ b/src/NLog/Conditions/ConditionParser.cs @@ -213,10 +213,10 @@ private ConditionExpression ParseLiteralExpression() if (_tokenizer.TokenType == ConditionTokenType.String) { var stringTokenValue = _tokenizer.TokenValue.Substring(1, _tokenizer.TokenValue.Length - 2).Replace("''", "'"); - var simpleLayout = new SimpleLayout(stringTokenValue, _configurationItemFactory); + var simpleLayout = string.IsNullOrEmpty(stringTokenValue) ? SimpleLayout.Default : new SimpleLayout(stringTokenValue, _configurationItemFactory); _tokenizer.GetNextToken(); if (simpleLayout.IsFixedText) - return new ConditionLiteralExpression(simpleLayout.FixedText); + return string.IsNullOrEmpty(simpleLayout.FixedText) ? ConditionLiteralExpression.Empty : new ConditionLiteralExpression(simpleLayout.FixedText); else return new ConditionLayoutExpression(simpleLayout); } @@ -283,19 +283,19 @@ private bool TryPlainKeywordToExpression(string keyword, out ConditionExpression if (string.Equals(keyword, "true", StringComparison.OrdinalIgnoreCase)) { - expression = new ConditionLiteralExpression(ConditionExpression.BoxedTrue); + expression = ConditionLiteralExpression.True; return true; } if (string.Equals(keyword, "false", StringComparison.OrdinalIgnoreCase)) { - expression = new ConditionLiteralExpression(ConditionExpression.BoxedFalse); + expression = ConditionLiteralExpression.False; return true; } if (string.Equals(keyword, "null", StringComparison.OrdinalIgnoreCase)) { - expression = new ConditionLiteralExpression(null); + expression = ConditionLiteralExpression.Null; return true; } @@ -315,7 +315,6 @@ private ConditionExpression ParseNumber(bool negative) if (numberString.IndexOf('.') >= 0) { var d = double.Parse(numberString, CultureInfo.InvariantCulture); - return new ConditionLiteralExpression(negative ? -d : d); } diff --git a/src/NLog/Config/LoggingConfiguration.cs b/src/NLog/Config/LoggingConfiguration.cs index 8ed8938183..40d809b215 100644 --- a/src/NLog/Config/LoggingConfiguration.cs +++ b/src/NLog/Config/LoggingConfiguration.cs @@ -760,24 +760,6 @@ internal void ValidateConfig() } _configItems = ObjectGraphScanner.FindReachableObjects(ConfigurationItemFactory.Default, true, roots.ToArray()); - - InternalLogger.Info("Validating config: {0}", this); - - foreach (object o in _configItems) - { - try - { - if (o is ISupportsInitialize) - continue; // Target + Layout + LayoutRenderer validate on Initialize() - - PropertyHelper.CheckRequiredParameters(ConfigurationItemFactory.Default, o); - } - catch (Exception ex) - { - if (ex.MustBeRethrown()) - throw; - } - } } internal void InitializeAll() diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index 08ffc94aab..15e7e0d666 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -716,7 +716,7 @@ private static bool IsLevelLayout(string? level) private void ParseLoggingRuleTargets(string? writeTargets, LoggingRule rule) { - writeTargets = ExpandSimpleVariables(writeTargets); + writeTargets = ExpandSimpleVariables(writeTargets).Trim(); if (string.IsNullOrEmpty(writeTargets)) return; @@ -1313,7 +1313,7 @@ private bool TryCreateSimpleLayoutInstance(ValidatedConfigurationElement element if (simpleLayoutValue != null) { var simpleLayoutText = ExpandSimpleVariables(simpleLayoutValue); - simpleLayout = new SimpleLayout(simpleLayoutText, ConfigurationItemFactory.Default); + simpleLayout = string.IsNullOrEmpty(simpleLayoutValue) ? SimpleLayout.Default : new SimpleLayout(simpleLayoutText, ConfigurationItemFactory.Default); return true; } } @@ -1356,7 +1356,7 @@ private bool TryCreateSimpleLayoutInstance(ValidatedConfigurationElement element try { - typeName = ExpandSimpleVariables(typeName); + typeName = ExpandSimpleVariables(typeName).Trim(); if (typeName.Contains(',')) { // Possible specification of assembly-name detected @@ -1441,19 +1441,17 @@ private Target WrapWithDefaultWrapper(Target target, ValidatedConfigurationEleme var wrapperTargetInstance = CreateTargetType(wrapperTypeName) as WrapperTargetBase; if (wrapperTargetInstance is null) { - throw new NLogConfigurationException("Target type specified on is not a wrapper."); + throw new NLogConfigurationException($"Target type '{wrapperTypeName}' cannot be used as default target wrapper."); } var wtb = wrapperTargetInstance; ParseTargetElement(wrapperTargetInstance, defaultWrapperElement); while (wtb.WrappedTarget != null) { - wtb = wtb.WrappedTarget as WrapperTargetBase; - if (wtb is null) - { - throw new NLogConfigurationException( - "Child target type specified on is not a wrapper."); - } + if (wtb.WrappedTarget is WrapperTargetBase nestedWrapper) + wtb = nestedWrapper; + else + throw new NLogConfigurationException($"Target type '{wrapperTypeName}' with nested {wtb.WrappedTarget.GetType()} cannot be used as default target wrapper."); } #if !NET35 diff --git a/src/NLog/Config/RequiredParameterAttribute.cs b/src/NLog/Config/RequiredParameterAttribute.cs index 402ebd17b8..4e83ad8b09 100644 --- a/src/NLog/Config/RequiredParameterAttribute.cs +++ b/src/NLog/Config/RequiredParameterAttribute.cs @@ -44,6 +44,7 @@ namespace NLog.Config /// [AttributeUsage(AttributeTargets.Property)] [MeansImplicitUse] + [Obsolete("Instead perform relevant config validation in InitializeTarget / InitializeLayout. Marked obsolete with NLog v6.0")] public sealed class RequiredParameterAttribute : Attribute { } diff --git a/src/NLog/Filters/ConditionBasedFilter.cs b/src/NLog/Filters/ConditionBasedFilter.cs index 655037d169..6369107b17 100644 --- a/src/NLog/Filters/ConditionBasedFilter.cs +++ b/src/NLog/Filters/ConditionBasedFilter.cs @@ -31,10 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Filters { using NLog.Conditions; - using NLog.Config; /// /// Matches when the specified condition is met. @@ -50,15 +51,14 @@ public class ConditionBasedFilter : Filter /// Gets or sets the condition expression. /// /// - [RequiredParameter] - public ConditionExpression Condition { get; set; } + public ConditionExpression Condition { get; set; } = ConditionExpression.Empty; internal FilterResult FilterDefaultAction { get; set; } = FilterResult.Neutral; /// protected override FilterResult Check(LogEventInfo logEvent) { - object val = Condition.Evaluate(logEvent); + var val = Condition.Evaluate(logEvent); return ConditionExpression.BoxedTrue.Equals(val) ? Action : FilterDefaultAction; } } diff --git a/src/NLog/Filters/Filter.cs b/src/NLog/Filters/Filter.cs index 779f4b7b0e..68165e9942 100644 --- a/src/NLog/Filters/Filter.cs +++ b/src/NLog/Filters/Filter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Filters { using NLog.Config; diff --git a/src/NLog/Filters/FilterAttribute.cs b/src/NLog/Filters/FilterAttribute.cs index 3f119c606a..e93c1175b0 100644 --- a/src/NLog/Filters/FilterAttribute.cs +++ b/src/NLog/Filters/FilterAttribute.cs @@ -31,10 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Filters { using System; - using Config; + using NLog.Config; /// /// Marks class as a layout renderer and assigns a name to it. diff --git a/src/NLog/Filters/LayoutBasedFilter.cs b/src/NLog/Filters/LayoutBasedFilter.cs index 85aaeada8c..4ed6b3e0d4 100644 --- a/src/NLog/Filters/LayoutBasedFilter.cs +++ b/src/NLog/Filters/LayoutBasedFilter.cs @@ -31,10 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Filters { - using Config; - using Layouts; + using NLog.Layouts; /// /// A base class for filters that are based on comparing a value to a layout. @@ -53,7 +54,6 @@ protected LayoutBasedFilter() /// /// The layout. /// - [RequiredParameter] - public Layout Layout { get; set; } + public Layout Layout { get; set; } = Layout.Empty; } } diff --git a/src/NLog/Filters/WhenContainsFilter.cs b/src/NLog/Filters/WhenContainsFilter.cs index 9e646ee1ef..f2a901322d 100644 --- a/src/NLog/Filters/WhenContainsFilter.cs +++ b/src/NLog/Filters/WhenContainsFilter.cs @@ -31,10 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Filters { using System; - using NLog.Config; /// /// Matches when the calculated layout contains the specified substring. @@ -53,8 +54,7 @@ public class WhenContainsFilter : LayoutBasedFilter /// Gets or sets the substring to be matched. /// /// - [RequiredParameter] - public string Substring { get; set; } + public string Substring { get; set; } = string.Empty; /// protected override FilterResult Check(LogEventInfo logEvent) @@ -63,7 +63,7 @@ protected override FilterResult Check(LogEventInfo logEvent) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; string result = Layout.Render(logEvent); - if (result.IndexOf(Substring, comparisonType) >= 0) + if (!string.IsNullOrEmpty(Substring) && result.IndexOf(Substring, comparisonType) >= 0) { return Action; } diff --git a/src/NLog/Filters/WhenEqualFilter.cs b/src/NLog/Filters/WhenEqualFilter.cs index 77e863e96e..799d2a4e9e 100644 --- a/src/NLog/Filters/WhenEqualFilter.cs +++ b/src/NLog/Filters/WhenEqualFilter.cs @@ -31,10 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Filters { using System; - using NLog.Config; /// /// Matches when the calculated layout is equal to the specified substring. @@ -53,8 +54,7 @@ public class WhenEqualFilter : LayoutBasedFilter /// Gets or sets a string to compare the layout to. /// /// - [RequiredParameter] - public string CompareTo { get; set; } + public string CompareTo { get; set; } = string.Empty; /// protected override FilterResult Check(LogEventInfo logEvent) diff --git a/src/NLog/Filters/WhenMethodFilter.cs b/src/NLog/Filters/WhenMethodFilter.cs index 237ae1fa68..111d0baae0 100644 --- a/src/NLog/Filters/WhenMethodFilter.cs +++ b/src/NLog/Filters/WhenMethodFilter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Filters { using System; diff --git a/src/NLog/Filters/WhenNotContainsFilter.cs b/src/NLog/Filters/WhenNotContainsFilter.cs index 86ded5613b..0754d88b5f 100644 --- a/src/NLog/Filters/WhenNotContainsFilter.cs +++ b/src/NLog/Filters/WhenNotContainsFilter.cs @@ -31,10 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Filters { using System; - using NLog.Config; /// /// Matches when the calculated layout does NOT contain the specified substring. @@ -47,8 +48,7 @@ public class WhenNotContainsFilter : LayoutBasedFilter /// Gets or sets the substring to be matched. /// /// - [RequiredParameter] - public string Substring { get; set; } + public string Substring { get; set; } = string.Empty; /// /// Gets or sets a value indicating whether to ignore case when comparing strings. @@ -63,7 +63,7 @@ protected override FilterResult Check(LogEventInfo logEvent) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; string result = Layout.Render(logEvent); - if (result.IndexOf(Substring, comparison) < 0) + if (!string.IsNullOrEmpty(Substring) && result.IndexOf(Substring, comparison) < 0) { return Action; } diff --git a/src/NLog/Filters/WhenNotEqualFilter.cs b/src/NLog/Filters/WhenNotEqualFilter.cs index 325f1665f1..41c9b9492e 100644 --- a/src/NLog/Filters/WhenNotEqualFilter.cs +++ b/src/NLog/Filters/WhenNotEqualFilter.cs @@ -31,10 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Filters { using System; - using NLog.Config; /// /// Matches when the calculated layout is NOT equal to the specified substring. @@ -54,8 +55,7 @@ public WhenNotEqualFilter() /// Gets or sets a string to compare the layout to. /// /// - [RequiredParameter] - public string CompareTo { get; set; } + public string CompareTo { get; set; } = string.Empty; /// /// Gets or sets a value indicating whether to ignore case when comparing strings. diff --git a/src/NLog/Filters/WhenRepeatedFilter.cs b/src/NLog/Filters/WhenRepeatedFilter.cs index ffc25cef4f..b97b13818e 100644 --- a/src/NLog/Filters/WhenRepeatedFilter.cs +++ b/src/NLog/Filters/WhenRepeatedFilter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Filters { using System; @@ -86,13 +88,13 @@ public class WhenRepeatedFilter : LayoutBasedFilter /// Insert FilterCount value into when an event is no longer filtered /// /// - public string FilterCountPropertyName { get; set; } + public string FilterCountPropertyName { get; set; } = string.Empty; /// /// Append FilterCount to the when an event is no longer filtered /// /// - public string FilterCountMessageAppendFormat { get; set; } + public string FilterCountMessageAppendFormat { get; set; } = string.Empty; /// /// Reuse internal buffers, and doesn't have to constantly allocate new buffers @@ -266,7 +268,7 @@ private FilterInfoKey RenderFilterInfoKey(LogEventInfo logEvent, StringBuilder t /// private FilterResult RefreshFilterInfo(LogEventInfo logEvent, FilterInfo filterInfo) { - if (filterInfo.HasExpired(logEvent.TimeStamp, TimeoutSeconds) || logEvent.Level.Ordinal > filterInfo.LogLevel.Ordinal) + if (filterInfo.HasExpired(logEvent.TimeStamp, TimeoutSeconds) || logEvent.Level > filterInfo.LogLevel) { int filterCount = filterInfo.FilterCount; if (filterCount > 0 && filterInfo.IsObsolete(logEvent.TimeStamp, TimeoutSeconds)) @@ -342,7 +344,7 @@ public bool HasExpired(DateTime logEventTime, int timeoutSeconds) } public StringBuilder StringBuffer { get; } - public LogLevel LogLevel { get; private set; } + public LogLevel? LogLevel { get; private set; } private DateTime LastLogTime { get; set; } private DateTime LastFilterTime { get; set; } public int FilterCount { get; private set; } @@ -353,11 +355,11 @@ public bool HasExpired(DateTime logEventTime, int timeoutSeconds) /// private struct FilterInfoKey : IEquatable { - private readonly StringBuilder _stringBuffer; - public readonly string StringValue; + private readonly StringBuilder? _stringBuffer; + public readonly string? StringValue; public readonly int StringHashCode; - public FilterInfoKey(StringBuilder stringBuffer, string stringValue, int? stringHashCode = null) + public FilterInfoKey(StringBuilder? stringBuffer, string? stringValue, int? stringHashCode = null) { _stringBuffer = stringBuffer; StringValue = stringValue; diff --git a/src/NLog/Internal/PropertyHelper.cs b/src/NLog/Internal/PropertyHelper.cs index 0022b085ac..5959c66dd0 100644 --- a/src/NLog/Internal/PropertyHelper.cs +++ b/src/NLog/Internal/PropertyHelper.cs @@ -58,7 +58,9 @@ internal static class PropertyHelper private static readonly Dictionary> _propertyConversionMapper = BuildPropertyConversionMapper(); #pragma warning disable S1144 // Unused private types or members should be removed. BUT they help CoreRT to provide config through reflection +#pragma warning disable CS0618 // Type or member is obsolete private static readonly RequiredParameterAttribute _requiredParameterAttribute = new RequiredParameterAttribute(); +#pragma warning restore CS0618 // Type or member is obsolete private static readonly ArrayParameterAttribute _arrayParameterAttribute = new ArrayParameterAttribute(typeof(string), string.Empty); private static readonly DefaultParameterAttribute _defaultParameterAttribute = new DefaultParameterAttribute(); private static readonly NLogConfigurationIgnorePropertyAttribute _ignorePropertyAttribute = new NLogConfigurationIgnorePropertyAttribute(); @@ -231,27 +233,6 @@ internal static Dictionary GetAllConfigItemProperties(Conf } } - internal static void CheckRequiredParameters(ConfigurationItemFactory configFactory, object o) - { - foreach (var configProp in GetAllConfigItemProperties(configFactory, o.GetType())) - { - var propInfo = configProp.Value; - var propertyType = propInfo.PropertyType; - if (propertyType != null && (propertyType.IsClass || Nullable.GetUnderlyingType(propertyType) != null)) - { - if (propInfo.IsDefined(_requiredParameterAttribute.GetType(), false)) - { - object value = propInfo.GetValue(o, null); - if (value is null) - { - throw new NLogConfigurationException( - $"Required parameter '{propInfo.Name}' on '{o}' was not specified."); - } - } - } - } - } - internal static bool IsSimplePropertyType(Type type) { if (Type.GetTypeCode(type) != TypeCode.Object) @@ -312,7 +293,7 @@ private static bool TryNLogSpecificConversion(Type propertyType, string value, C if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Layout<>)) { - var simpleLayout = new SimpleLayout(value, configurationItemFactory); + var simpleLayout = string.IsNullOrEmpty(value) ? SimpleLayout.Default : new SimpleLayout(value, configurationItemFactory); newValue = Activator.CreateInstance(propertyType, BindingFlags.Instance | BindingFlags.Public, null, new object[] { simpleLayout }, null); return true; } @@ -351,7 +332,7 @@ private static bool TryGetEnumValue(Type resultType, string value, out object? r private static object TryParseLayoutValue(string stringValue, ConfigurationItemFactory configurationItemFactory) { - return new SimpleLayout(stringValue, configurationItemFactory); + return string.IsNullOrEmpty(stringValue) ? SimpleLayout.Default : new SimpleLayout(stringValue, configurationItemFactory); } private static object TryParseConditionValue(string stringValue, ConfigurationItemFactory configurationItemFactory) diff --git a/src/NLog/Internal/StringBuilderExt.cs b/src/NLog/Internal/StringBuilderExt.cs index 4608c156cc..50ff935ba1 100644 --- a/src/NLog/Internal/StringBuilderExt.cs +++ b/src/NLog/Internal/StringBuilderExt.cs @@ -60,7 +60,7 @@ public static void AppendFormattedValue(this StringBuilder builder, object value { builder.Append(stringValue); // Avoid automatic quotes } - else if (format == MessageTemplates.ValueFormatter.FormatAsJson) + else if (ValueFormatter.FormatAsJson.Equals(format)) { valueFormatter.FormatValue(value, null, CaptureType.Serialize, formatProvider, builder); } diff --git a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs index 7f36e47ccd..f7c404e21d 100644 --- a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs @@ -110,7 +110,7 @@ public string Separator /// Disables to capture ScopeContext-properties from active thread context /// public LayoutRenderer DisableThreadAgnostic => IncludeScopeProperties ? _disableThreadAgnostic : null; - private static readonly LayoutRenderer _disableThreadAgnostic = new ScopeContextPropertyLayoutRenderer() { Item = string.Empty }; + private static readonly LayoutRenderer _disableThreadAgnostic = new FuncLayoutRenderer(string.Empty, (evt, cfg) => string.Empty); /// /// Gets or sets how key/value pairs will be formatted. diff --git a/src/NLog/LayoutRenderers/AppSettingLayoutRenderer.cs b/src/NLog/LayoutRenderers/AppSettingLayoutRenderer.cs index 040319334c..8f0348f092 100644 --- a/src/NLog/LayoutRenderers/AppSettingLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AppSettingLayoutRenderer.cs @@ -61,7 +61,6 @@ public sealed class AppSettingLayoutRenderer : LayoutRenderer, IStringValueRende /// The AppSetting item-name /// /// - [RequiredParameter] [DefaultParameter] public string Item { get; set; } @@ -85,9 +84,14 @@ public sealed class AppSettingLayoutRenderer : LayoutRenderer, IStringValueRende /// protected override void InitializeLayoutRenderer() { + base.InitializeLayoutRenderer(); + string connectionStringSection = "ConnectionStrings."; _connectionStringName = Item?.TrimStart().StartsWith(connectionStringSection, StringComparison.OrdinalIgnoreCase) == true ? Item.TrimStart().Substring(connectionStringSection.Length) : null; + + if (string.IsNullOrEmpty(Item)) + throw new NLogConfigurationException("AppSetting-LayoutRenderer Item-property must be assigned. Lookup blank value not supported."); } /// diff --git a/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs b/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs index ca9c54b639..f36a0c5a75 100644 --- a/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs @@ -53,7 +53,6 @@ public class EnvironmentLayoutRenderer : LayoutRenderer, IStringValueRenderer /// Gets or sets the name of the environment variable. /// /// - [RequiredParameter] [DefaultParameter] public string Variable { get; set; } @@ -65,6 +64,15 @@ public class EnvironmentLayoutRenderer : LayoutRenderer, IStringValueRenderer private System.Collections.Generic.KeyValuePair _cachedValue; + /// + protected override void InitializeLayoutRenderer() + { + base.InitializeLayoutRenderer(); + + if (string.IsNullOrEmpty(Variable)) + throw new NLogConfigurationException("Environment-LayoutRenderer Variable-property must be assigned. Lookup blank value not supported."); + } + /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { diff --git a/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs b/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs index 8e4a5d3f4d..08a57de95a 100644 --- a/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs @@ -60,7 +60,6 @@ public class EventPropertiesLayoutRenderer : LayoutRenderer, IRawValue, IStringV /// Gets or sets the name of the item. /// /// - [RequiredParameter] [DefaultParameter] public string Item { get => _item?.ToString(); set => _item = (value != null && IgnoreCase) ? new PropertiesDictionary.IgnoreCasePropertyKey(value) : (object)value; } private object _item; @@ -105,6 +104,15 @@ public bool IgnoreCase } private bool _ignoreCase = true; + /// + protected override void InitializeLayoutRenderer() + { + base.InitializeLayoutRenderer(); + + if (string.IsNullOrEmpty(Item)) + throw new NLogConfigurationException("EventProperty-LayoutRenderer Item-property must be assigned. Lookup blank value not supported."); + } + /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { @@ -149,7 +157,7 @@ private bool TryGetValue(LogEventInfo logEvent, out object value) private string GetStringValue(LogEventInfo logEvent) { - if (Format != MessageTemplates.ValueFormatter.FormatAsJson) + if (!MessageTemplates.ValueFormatter.FormatAsJson.Equals(Format)) { if (TryGetValue(logEvent, out var value)) { diff --git a/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs b/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs index 2d2ffd4b59..62f0b475bc 100644 --- a/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs @@ -58,7 +58,6 @@ public class ExceptionDataLayoutRenderer : LayoutRenderer /// /// [DefaultParameter] - [RequiredParameter] public string Item { get; set; } /// @@ -79,6 +78,15 @@ public class ExceptionDataLayoutRenderer : LayoutRenderer /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + /// + protected override void InitializeLayoutRenderer() + { + base.InitializeLayoutRenderer(); + + if (string.IsNullOrEmpty(Item)) + throw new NLogConfigurationException("ExceptionData-LayoutRenderer Item-property must be assigned. Lookup blank value not supported."); + } + private Exception GetTopException(LogEventInfo logEvent) { return BaseException ? logEvent.Exception?.GetBaseException() : logEvent.Exception; diff --git a/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs b/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs index 4ccad30454..eb7a72d902 100644 --- a/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs @@ -101,7 +101,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) private string GetStringValue(LogEventInfo logEvent) { - if (Format != MessageTemplates.ValueFormatter.FormatAsJson) + if (!MessageTemplates.ValueFormatter.FormatAsJson.Equals(Format)) { object value = RenderValue(logEvent); string stringValue = FormatHelper.TryFormatToString(value, Format, GetFormatProvider(logEvent, Culture)); diff --git a/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs b/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs index 11d4b85c8e..d9a0586194 100644 --- a/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs @@ -55,7 +55,6 @@ public class GdcLayoutRenderer : LayoutRenderer, IRawValue, IStringValueRenderer /// Gets or sets the name of the item. /// /// - [RequiredParameter] [DefaultParameter] public string Item { get; set; } @@ -71,6 +70,15 @@ public class GdcLayoutRenderer : LayoutRenderer, IRawValue, IStringValueRenderer /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + /// + protected override void InitializeLayoutRenderer() + { + base.InitializeLayoutRenderer(); + + if (string.IsNullOrEmpty(Item)) + throw new NLogConfigurationException("Gdc-LayoutRenderer Item-property must be assigned. Lookup blank value not supported."); + } + /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { @@ -91,7 +99,7 @@ bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) private string GetStringValue(LogEventInfo logEvent) { - if (Format != MessageTemplates.ValueFormatter.FormatAsJson) + if (!MessageTemplates.ValueFormatter.FormatAsJson.Equals(Format)) { object value = GetValue(); string stringValue = FormatHelper.TryFormatToString(value, Format, GetFormatProvider(logEvent, Culture)); diff --git a/src/NLog/LayoutRenderers/InstallContextLayoutRenderer.cs b/src/NLog/LayoutRenderers/InstallContextLayoutRenderer.cs index ee8fc21cf6..2667c43436 100644 --- a/src/NLog/LayoutRenderers/InstallContextLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/InstallContextLayoutRenderer.cs @@ -51,7 +51,6 @@ public class InstallContextLayoutRenderer : LayoutRenderer /// Gets or sets the name of the parameter. /// /// - [RequiredParameter] [DefaultParameter] public string Parameter { get; set; } diff --git a/src/NLog/LayoutRenderers/LayoutRenderer.cs b/src/NLog/LayoutRenderers/LayoutRenderer.cs index a243cb8900..ea9479d645 100644 --- a/src/NLog/LayoutRenderers/LayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LayoutRenderer.cs @@ -118,7 +118,6 @@ private void Initialize() { try { - PropertyHelper.CheckRequiredParameters(ConfigurationItemFactory.Default, this); InitializeLayoutRenderer(); } catch (Exception ex) diff --git a/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs index 4ed47cff94..701d5cd0d3 100644 --- a/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs @@ -132,7 +132,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) private void AppendNestedStates(StringBuilder builder, IList messages, int startPos, int endPos, LogEventInfo logEvent) { - bool formatAsJson = MessageTemplates.ValueFormatter.FormatAsJson.Equals(Format, StringComparison.Ordinal); + bool formatAsJson = MessageTemplates.ValueFormatter.FormatAsJson.Equals(Format); string separator = null; string itemSeparator = null; diff --git a/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs index a00357fac6..2151b340d4 100644 --- a/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs @@ -54,7 +54,6 @@ public sealed class ScopeContextPropertyLayoutRenderer : LayoutRenderer, IString /// Gets or sets the name of the item. /// /// - [RequiredParameter] [DefaultParameter] public string Item { get; set; } @@ -70,6 +69,15 @@ public sealed class ScopeContextPropertyLayoutRenderer : LayoutRenderer, IString /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + /// + protected override void InitializeLayoutRenderer() + { + base.InitializeLayoutRenderer(); + + if (string.IsNullOrEmpty(Item)) + throw new NLogConfigurationException("ScopeProperty-LayoutRenderer Item-property must be assigned. Lookup blank value not supported."); + } + /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { @@ -81,7 +89,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) private string GetStringValue(LogEventInfo logEvent) { - if (Format != MessageTemplates.ValueFormatter.FormatAsJson) + if (!MessageTemplates.ValueFormatter.FormatAsJson.Equals(Format)) { object value = GetValue(); string stringValue = FormatHelper.TryFormatToString(value, Format, GetFormatProvider(logEvent, Culture)); diff --git a/src/NLog/LayoutRenderers/VariableLayoutRenderer.cs b/src/NLog/LayoutRenderers/VariableLayoutRenderer.cs index d8f2a3e78a..aa5de0e0d6 100644 --- a/src/NLog/LayoutRenderers/VariableLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/VariableLayoutRenderer.cs @@ -51,7 +51,6 @@ public class VariableLayoutRenderer : LayoutRenderer /// Gets or sets the name of the NLog variable. /// /// - [RequiredParameter] [DefaultParameter] public string Name { get; set; } @@ -71,6 +70,9 @@ public class VariableLayoutRenderer : LayoutRenderer /// protected override void InitializeLayoutRenderer() { + if (string.IsNullOrEmpty(Name)) + throw new NLogConfigurationException("Var-LayoutRenderer Name-property must be assigned. Lookup blank value not supported."); + if (TryGetLayout(out var layout) && layout != null) { //pass loggingConfiguration to layout diff --git a/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs index c06b97af9e..5131d31551 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs @@ -57,7 +57,6 @@ public sealed class ReplaceLayoutRendererWrapper : WrapperLayoutRendererBase /// /// The text search for. /// - [RequiredParameter] public string SearchFor { get => _searchForOriginal ?? _searchFor; @@ -67,7 +66,7 @@ public string SearchFor _searchFor = Layouts.SimpleLayout.Evaluate(value, LoggingConfiguration, throwConfigExceptions: false); } } - private string _searchFor; + private string _searchFor = string.Empty; private string _searchForOriginal; /// @@ -105,6 +104,10 @@ public string ReplaceWith protected override void InitializeLayoutRenderer() { base.InitializeLayoutRenderer(); + + if (string.IsNullOrEmpty(SearchFor)) + throw new NLogConfigurationException("Replace-LayoutRenderer SearchFor-property must be assigned. Searching for blank value not supported."); + if (_searchForOriginal != null) _searchFor = Layouts.SimpleLayout.Evaluate(_searchForOriginal, LoggingConfiguration); if (_replaceWithOriginal != null) diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs index 6bd594ce2a..4f5cf5e268 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs @@ -53,12 +53,14 @@ public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBase, /// Gets or sets the layout to be rendered when original layout produced empty result. /// /// - [RequiredParameter] - public Layout WhenEmpty { get; set; } + public Layout WhenEmpty { get; set; } = Layout.Empty; /// protected override void InitializeLayoutRenderer() { + if (WhenEmpty is null) + throw new NLogConfigurationException("WhenEmpty-LayoutRenderer WhenEmpty-property must be assigned."); + base.InitializeLayoutRenderer(); WhenEmpty?.Initialize(LoggingConfiguration); _skipStringValueRenderer = !TryGetStringValue(out _, out _); diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs index 4f1f882497..7c5fb23b32 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs @@ -56,8 +56,7 @@ public sealed class WhenLayoutRendererWrapper : WrapperLayoutRendererBase, IRawV /// Gets or sets the condition that must be met for the layout to be printed. /// /// - [RequiredParameter] - public ConditionExpression When { get; set; } + public ConditionExpression When { get; set; } = ConditionExpression.Empty; /// /// If is not met, print this layout. @@ -65,6 +64,15 @@ public sealed class WhenLayoutRendererWrapper : WrapperLayoutRendererBase, IRawV /// public Layout Else { get; set; } + /// + protected override void InitializeLayoutRenderer() + { + if (When is null || ReferenceEquals(When, ConditionExpression.Empty)) + throw new NLogConfigurationException("When-LayoutRenderer When-property must be assigned."); + + base.InitializeLayoutRenderer(); + } + /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { @@ -95,7 +103,7 @@ protected override string Transform(string text) private bool ShouldRenderInner(LogEventInfo logEvent) { - return When is null || true.Equals(When.Evaluate(logEvent)); + return When is null || ReferenceEquals(When, ConditionExpression.Empty) || true.Equals(When.Evaluate(logEvent)); } bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) diff --git a/src/NLog/Layouts/CSV/CsvColumn.cs b/src/NLog/Layouts/CSV/CsvColumn.cs index 36df9586fb..6ad56fc9e8 100644 --- a/src/NLog/Layouts/CSV/CsvColumn.cs +++ b/src/NLog/Layouts/CSV/CsvColumn.cs @@ -45,7 +45,7 @@ public class CsvColumn /// Initializes a new instance of the class. /// public CsvColumn() - : this(null, null) + : this(string.Empty, Layout.Empty) { } @@ -70,7 +70,6 @@ public CsvColumn(string name, Layout layout) /// Gets or sets the layout of the column. /// /// - [RequiredParameter] public Layout Layout { get; set; } /// diff --git a/src/NLog/Layouts/CSV/CsvLayout.cs b/src/NLog/Layouts/CSV/CsvLayout.cs index 4a27d34008..548131bebe 100644 --- a/src/NLog/Layouts/CSV/CsvLayout.cs +++ b/src/NLog/Layouts/CSV/CsvLayout.cs @@ -152,6 +152,17 @@ protected override void InitializeLayout() _quotableCharacters = (QuoteChar + "\r\n" + _actualColumnDelimiter).ToCharArray(); _doubleQuoteChar = QuoteChar + QuoteChar; _precalculateLayouts = ResolveLayoutPrecalculation(Columns.Select(cln => cln.Layout)); + + foreach (var csvColumn in Columns) + { + if (string.IsNullOrEmpty(csvColumn.Name)) + throw new NLogConfigurationException("CsvLayout: Contains invalid CsvColumn with unassigned Name-property"); + + if (csvColumn.Layout is null) + { + Common.InternalLogger.Warn("CsvLayout: Contains invalid Column(Name={0}) with unassigned Layout-property.", csvColumn.Name); + } + } } /// diff --git a/src/NLog/Layouts/JSON/JsonAttribute.cs b/src/NLog/Layouts/JSON/JsonAttribute.cs index c333c3c339..df97ba640d 100644 --- a/src/NLog/Layouts/JSON/JsonAttribute.cs +++ b/src/NLog/Layouts/JSON/JsonAttribute.cs @@ -76,7 +76,6 @@ public JsonAttribute(string name, Layout layout, bool encode) /// Gets or sets the name of the attribute. /// /// - [RequiredParameter] public string Name { get => _name; @@ -100,7 +99,6 @@ public string Name /// Gets or sets the layout that will be rendered as the attribute's value. /// /// - [RequiredParameter] public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } /// diff --git a/src/NLog/Layouts/JSON/JsonLayout.cs b/src/NLog/Layouts/JSON/JsonLayout.cs index 20c3882b7a..a509885358 100644 --- a/src/NLog/Layouts/JSON/JsonLayout.cs +++ b/src/NLog/Layouts/JSON/JsonLayout.cs @@ -257,6 +257,9 @@ protected override void InitializeLayout() foreach (var attribute in _attributes) { + if (string.IsNullOrEmpty(attribute.Name)) + throw new NLogConfigurationException("JsonLayout: Contains invalid JsonAttribute with unassigned Name-property"); + if (!attribute.Encode && attribute.Layout is JsonLayout jsonLayout) { if (!attribute.IncludeEmptyValue && !jsonLayout._renderEmptyObject.HasValue) diff --git a/src/NLog/Layouts/Layout.cs b/src/NLog/Layouts/Layout.cs index b3bc535351..d1fe81751b 100644 --- a/src/NLog/Layouts/Layout.cs +++ b/src/NLog/Layouts/Layout.cs @@ -51,6 +51,11 @@ namespace NLog.Layouts [NLogConfigurationItem] public abstract class Layout : ISupportsInitialize, IRenderable { + /// + /// Default Layout-value that renders string.Empty + /// + public static Layout Empty => SimpleLayout.Default; + /// /// Is this layout initialized? See /// @@ -88,7 +93,7 @@ public abstract class Layout : ISupportsInitialize, IRenderable /// object represented by the text. public static implicit operator Layout([Localizable(false)] string text) { - return FromString(text, ConfigurationItemFactory.Default); + return text is null ? null : FromString(text, ConfigurationItemFactory.Default); } /// @@ -109,7 +114,7 @@ public static Layout FromString([Localizable(false)] string layoutText) /// Instance of . public static Layout FromString([Localizable(false)] string layoutText, ConfigurationItemFactory configurationItemFactory) { - return new SimpleLayout(layoutText, configurationItemFactory); + return string.IsNullOrEmpty(layoutText) ? SimpleLayout.Default : new SimpleLayout(layoutText, configurationItemFactory); } /// @@ -122,7 +127,7 @@ public static Layout FromString([Localizable(false)] string layoutText, bool thr { try { - return new SimpleLayout(layoutText, ConfigurationItemFactory.Default, throwConfigExceptions); + return string.IsNullOrEmpty(layoutText) ? SimpleLayout.Default : new SimpleLayout(layoutText, ConfigurationItemFactory.Default, throwConfigExceptions); } catch (NLogConfigurationException) { @@ -143,7 +148,7 @@ public static Layout FromString([Localizable(false)] string layoutText, bool thr public static Layout FromLiteral([Localizable(false)] string literalText) { if (string.IsNullOrEmpty(literalText)) - return new SimpleLayout(ArrayHelper.Empty(), string.Empty); + return SimpleLayout.Default; else return new SimpleLayout(new[] { new NLog.LayoutRenderers.LiteralLayoutRenderer(literalText) }, literalText); } @@ -324,8 +329,6 @@ internal void Initialize(LoggingConfiguration configuration) _scannedForObjects = false; - PropertyHelper.CheckRequiredParameters(ConfigurationItemFactory.Default, this); - InitializeLayout(); if (!_scannedForObjects) diff --git a/src/NLog/Layouts/LayoutWithHeaderAndFooter.cs b/src/NLog/Layouts/LayoutWithHeaderAndFooter.cs index b2079325ec..b7eb099a97 100644 --- a/src/NLog/Layouts/LayoutWithHeaderAndFooter.cs +++ b/src/NLog/Layouts/LayoutWithHeaderAndFooter.cs @@ -47,7 +47,7 @@ public class LayoutWithHeaderAndFooter : Layout /// Gets or sets the body layout (can be repeated multiple times). /// /// - public Layout Layout { get; set; } + public Layout Layout { get; set; } = Layout.Empty; /// /// Gets or sets the header layout. diff --git a/src/NLog/Layouts/SimpleLayout.cs b/src/NLog/Layouts/SimpleLayout.cs index 400c5135b2..70910fce99 100644 --- a/src/NLog/Layouts/SimpleLayout.cs +++ b/src/NLog/Layouts/SimpleLayout.cs @@ -62,11 +62,13 @@ public sealed class SimpleLayout : Layout, IUsesStackTrace, IStringValueRenderer private readonly IRawValue _rawValueRenderer; private IStringValueRenderer _stringValueRenderer; + internal static readonly SimpleLayout Default = new SimpleLayout(); + /// /// Initializes a new instance of the class. /// public SimpleLayout() - : this(string.Empty) + : this(ArrayHelper.Empty(), string.Empty) { } @@ -187,8 +189,7 @@ internal SimpleLayout(LayoutRenderer[] layoutRenderers, [Localizable(false)] str public static implicit operator SimpleLayout([Localizable(false)] string text) { if (text is null) return null; - - return new SimpleLayout(text); + return string.IsNullOrEmpty(text) ? SimpleLayout.Default : new SimpleLayout(text); } /// diff --git a/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs b/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs index 14dbaf73a3..307ad37654 100644 --- a/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs +++ b/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs @@ -58,13 +58,12 @@ public ValueTypeLayoutInfo() /// Gets or sets the layout that will render the result value /// /// - [RequiredParameter] public Layout Layout { get => _layout; set { - _layout = value; + _layout = value ?? Layout.Empty; if (ValueType is null && _layout is ILayoutTypeValue layoutTyped) { ValueType = layoutTyped.InnerType; @@ -72,7 +71,7 @@ public Layout Layout _layoutValue = null; } } - private Layout _layout; + private Layout _layout = Layout.Empty; /// /// Gets or sets the result value type, for conversion of layout rendering output @@ -212,7 +211,7 @@ private ILayoutTypeValue BuildLayoutTypeValue(Layout layout) } else { - layout = string.Empty; + layout = SimpleLayout.Empty; } } diff --git a/src/NLog/Layouts/XML/XmlAttribute.cs b/src/NLog/Layouts/XML/XmlAttribute.cs index 1ad88e06f1..902681157a 100644 --- a/src/NLog/Layouts/XML/XmlAttribute.cs +++ b/src/NLog/Layouts/XML/XmlAttribute.cs @@ -76,7 +76,6 @@ public XmlAttribute(string name, Layout layout, bool encode) /// Gets or sets the name of the attribute. /// /// - [RequiredParameter] public string Name { get => _name; set => _name = XmlHelper.XmlConvertToElementName(value?.Trim()); } private string _name; @@ -84,7 +83,6 @@ public XmlAttribute(string name, Layout layout, bool encode) /// Gets or sets the layout that will be rendered as the attribute's value. /// /// - [RequiredParameter] public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } /// diff --git a/src/NLog/Layouts/XML/XmlElement.cs b/src/NLog/Layouts/XML/XmlElement.cs index 7c178f50e4..0743051038 100644 --- a/src/NLog/Layouts/XML/XmlElement.cs +++ b/src/NLog/Layouts/XML/XmlElement.cs @@ -64,7 +64,6 @@ public XmlElement(string elementName, Layout elementValue) : base(elementName, e /// Default value "item" /// /// - [RequiredParameter] public string Name { get => base.ElementNameInternal; diff --git a/src/NLog/Layouts/XML/XmlElementBase.cs b/src/NLog/Layouts/XML/XmlElementBase.cs index a8cccb4da4..77753865ec 100644 --- a/src/NLog/Layouts/XML/XmlElementBase.cs +++ b/src/NLog/Layouts/XML/XmlElementBase.cs @@ -68,7 +68,7 @@ protected XmlElementBase(string elementName, Layout elementValue) /// /// Upgrade to private protected when using C# 7.2 internal string ElementNameInternal { get => _elementNameInternal; set => _elementNameInternal = XmlHelper.XmlConvertToElementName(value?.Trim()); } - private string _elementNameInternal; + private string _elementNameInternal = string.Empty; /// /// Value inside the XML element @@ -227,24 +227,26 @@ protected override void InitializeLayout() { base.InitializeLayout(); + if (string.IsNullOrEmpty(ElementNameInternal)) + throw new NLogConfigurationException("XmlLayout Name-property must be assigned. Name is required for valid XML element."); + if (IncludeScopeProperties) ThreadAgnostic = false; if (IncludeEventProperties) ThreadAgnosticImmutable = true; - if (Attributes.Count > 1) + if (Attributes.Count > 0) { HashSet attributeValidator = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var attribute in Attributes) { if (string.IsNullOrEmpty(attribute.Name)) + throw new NLogConfigurationException($"XmlElement(Name={ElementNameInternal}): Contains invalid XmlAttribute with unassigned Name-property"); + + if (attributeValidator.Contains(attribute.Name)) { - Common.InternalLogger.Warn("XmlElement(ElementName={0}): Contains attribute with missing name (Ignored)", ElementNameInternal); - } - else if (attributeValidator.Contains(attribute.Name)) - { - Common.InternalLogger.Warn("XmlElement(ElementName={0}): Contains duplicate attribute name: {1} (Invalid xml)", ElementNameInternal, attribute.Name); + Common.InternalLogger.Warn("XmlElement(ElementName={0}): Contains duplicate XmlAttribute(Name={1}) (Invalid xml)", ElementNameInternal, attribute.Name); } else { diff --git a/src/NLog/Targets/AsyncTaskTarget.cs b/src/NLog/Targets/AsyncTaskTarget.cs index 720b63d3d6..e3de69da52 100644 --- a/src/NLog/Targets/AsyncTaskTarget.cs +++ b/src/NLog/Targets/AsyncTaskTarget.cs @@ -58,7 +58,6 @@ namespace NLog.Targets /// this.Host = "localhost"; /// } /// - /// [RequiredParameter] /// public Layout Host { get; set; } /// /// protected override Task WriteAsyncTask(LogEventInfo logEvent, CancellationToken token) diff --git a/src/NLog/Targets/ConsoleRowHighlightingRule.cs b/src/NLog/Targets/ConsoleRowHighlightingRule.cs index 84861aaa32..676c71b043 100644 --- a/src/NLog/Targets/ConsoleRowHighlightingRule.cs +++ b/src/NLog/Targets/ConsoleRowHighlightingRule.cs @@ -72,7 +72,6 @@ public ConsoleRowHighlightingRule(ConditionExpression condition, ConsoleOutputCo /// Gets or sets the condition that must be met in order to set the specified foreground and background color. /// /// - [RequiredParameter] public ConditionExpression Condition { get; set; } /// diff --git a/src/NLog/Targets/EventLogTarget.cs b/src/NLog/Targets/EventLogTarget.cs index 4d7ae17009..6959723f52 100644 --- a/src/NLog/Targets/EventLogTarget.cs +++ b/src/NLog/Targets/EventLogTarget.cs @@ -128,7 +128,6 @@ internal EventLogTarget(IEventLogWrapper eventLogWrapper, string sourceName) /// By default this is the friendly name of the current AppDomain. /// /// - [RequiredParameter] public Layout Source { get; set; } /// @@ -212,9 +211,12 @@ protected override void InitializeTarget() { base.InitializeTarget(); + if (Source is null || ReferenceEquals(Source, Layout.Empty)) + throw new NLogConfigurationException("EventLogTarget Source-property must be assigned. Source is needed for EventLog writing."); + var maxKilobytes = MaxKilobytes?.IsFixed == true ? MaxKilobytes.FixedValue : 0; if (maxKilobytes > 0 && (maxKilobytes < 64 || maxKilobytes > 4194240 || (maxKilobytes % 64 != 0))) // Event log API restrictions - throw new NLogConfigurationException("EventLog MaxKilobytes must be a multiple of 64, and between 64 and 4194240"); + throw new NLogConfigurationException("EventLogTarget MaxKilobytes must be a multiple of 64, and between 64 and 4194240"); CreateEventSourceIfNeeded(GetFixedSource(), false); } diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index a083c41d68..e53b5c1f7a 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -72,7 +72,6 @@ public class FileTarget : TargetWithLayoutHeaderAndFooter /// You can combine as many of the layout renderers as you want to produce an arbitrary log file name. /// /// - [RequiredParameter] public Layout FileName { get => _fileName; @@ -82,8 +81,8 @@ public Layout FileName _fixedFileName = (value is SimpleLayout simpleLayout && simpleLayout.IsFixedText) ? simpleLayout.FixedText : null; } } - private Layout _fileName; - private string _fixedFileName; + private Layout _fileName = Layout.Empty; + private string _fixedFileName = string.Empty; /// /// Gets or sets a value indicating whether to create directories if they do not exist. @@ -535,6 +534,9 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation) /// protected override void InitializeTarget() { + if (FileName is null || ReferenceEquals(FileName, Layout.Empty)) + throw new NLogConfigurationException("FileTarget FileName-property must be assigned. FileName is needed for file writing."); + if (OpenFileMonitorTimerInterval > 0) { // Prepare Timer for periodic checking of flushing/closing open files (inactive until first file is opened) diff --git a/src/NLog/Targets/MethodCallParameter.cs b/src/NLog/Targets/MethodCallParameter.cs index 706c525612..43f66ce18f 100644 --- a/src/NLog/Targets/MethodCallParameter.cs +++ b/src/NLog/Targets/MethodCallParameter.cs @@ -99,7 +99,6 @@ public MethodCallParameter(string name, Layout layout, Type type) /// Gets or sets the layout that should be use to calculate the value for the parameter. /// /// - [RequiredParameter] public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } /// diff --git a/src/NLog/Targets/MethodCallTargetBase.cs b/src/NLog/Targets/MethodCallTargetBase.cs index e87bd04c5d..863bf44ccd 100644 --- a/src/NLog/Targets/MethodCallTargetBase.cs +++ b/src/NLog/Targets/MethodCallTargetBase.cs @@ -58,7 +58,7 @@ protected MethodCallTargetBase() /// /// [ArrayParameter(typeof(MethodCallParameter), "parameter")] - public IList Parameters { get; private set; } + public IList Parameters { get; } /// /// Prepares an array of parameters to be passed based on the logging event and calls DoInvoke(). diff --git a/src/NLog/Targets/Target.cs b/src/NLog/Targets/Target.cs index d2cf44d57a..09557fdea0 100644 --- a/src/NLog/Targets/Target.cs +++ b/src/NLog/Targets/Target.cs @@ -443,8 +443,6 @@ internal void Initialize(LoggingConfiguration configuration) { _scannedForLayouts = false; - PropertyHelper.CheckRequiredParameters(ConfigurationItemFactory.Default, this); - InitializeTarget(); _initializeException = null; if (!_scannedForLayouts) diff --git a/src/NLog/Targets/TargetPropertyWithContext.cs b/src/NLog/Targets/TargetPropertyWithContext.cs index 1f5da8558a..6952a2d3c2 100644 --- a/src/NLog/Targets/TargetPropertyWithContext.cs +++ b/src/NLog/Targets/TargetPropertyWithContext.cs @@ -66,14 +66,12 @@ public TargetPropertyWithContext(string name, Layout layout) /// Gets or sets the name of the attribute. /// /// - [RequiredParameter] public string Name { get; set; } /// /// Gets or sets the layout that will be rendered as the attribute's value. /// /// - [RequiredParameter] public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } /// diff --git a/src/NLog/Targets/TargetWithContext.cs b/src/NLog/Targets/TargetWithContext.cs index 2d9964b0df..744482bc68 100644 --- a/src/NLog/Targets/TargetWithContext.cs +++ b/src/NLog/Targets/TargetWithContext.cs @@ -56,7 +56,6 @@ namespace NLog.Targets /// this.Host = "localhost"; /// } /// - /// [RequiredParameter] /// public Layout Host { get; set; } /// /// protected override void Write(LogEventInfo logEvent) @@ -188,6 +187,21 @@ protected TargetWithContext() ExcludeProperties = new HashSet(StringComparer.OrdinalIgnoreCase); } + /// + protected override void InitializeTarget() + { + base.InitializeTarget(); + + if (ContextProperties?.Count > 0) + { + foreach (var contextProperty in ContextProperties) + { + if (string.IsNullOrEmpty(contextProperty.Name)) + throw new NLogConfigurationException($"{this}: Contains invalid ContextProperty with unassigned Name-property"); + } + } + } + /// /// Check if logevent has properties (or context properties) /// @@ -694,6 +708,7 @@ public TargetWithContextLayout(TargetWithContext owner, Layout targetLayout) protected override void InitializeLayout() { base.InitializeLayout(); + if (IncludeScopeProperties || IncludeScopeNested) ThreadAgnostic = false; if (IncludeEventProperties) diff --git a/src/NLog/Targets/TargetWithLayout.cs b/src/NLog/Targets/TargetWithLayout.cs index d36579ac2e..36d32da835 100644 --- a/src/NLog/Targets/TargetWithLayout.cs +++ b/src/NLog/Targets/TargetWithLayout.cs @@ -75,7 +75,6 @@ protected TargetWithLayout() /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// /// - [RequiredParameter] public virtual Layout Layout { get; set; } } } diff --git a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs index 26b876528d..7036f5af5a 100644 --- a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs +++ b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs @@ -58,7 +58,6 @@ protected TargetWithLayoutHeaderAndFooter() /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// /// - [RequiredParameter] public override Layout Layout { get => LHF.Layout; diff --git a/src/NLog/Targets/Wrappers/FilteringRule.cs b/src/NLog/Targets/Wrappers/FilteringRule.cs index 07fa97be5e..131c740ae0 100644 --- a/src/NLog/Targets/Wrappers/FilteringRule.cs +++ b/src/NLog/Targets/Wrappers/FilteringRule.cs @@ -46,7 +46,7 @@ public class FilteringRule /// Initializes a new instance of the FilteringRule class. /// public FilteringRule() - : this(null, null) + : this(ConditionExpression.Empty, ConditionExpression.Empty) { } @@ -65,14 +65,12 @@ public FilteringRule(ConditionExpression whenExistsExpression, ConditionExpressi /// Gets or sets the condition to be tested. /// /// - [RequiredParameter] public ConditionExpression Exists { get; set; } /// /// Gets or sets the resulting filter to be applied when the condition matches. /// /// - [RequiredParameter] public ConditionExpression Filter { get; set; } } } diff --git a/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs b/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs index 8df7c142f8..79026ac54b 100644 --- a/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs @@ -105,9 +105,17 @@ public FilteringTargetWrapper(Target wrappedTarget, ConditionExpression conditio /// Gets or sets the filter. Log events who evaluates to will be discarded /// /// - [RequiredParameter] public Filter Filter { get; set; } + /// + protected override void InitializeTarget() + { + if (Filter is null) + throw new NLogConfigurationException($"FilteringTargetWrapper Filter-property must be assigned. Filter LogEvents using blank Filter not supported."); + + base.InitializeTarget(); + } + /// /// Checks the condition against the passed log event. /// If the condition is met, the log event is forwarded to diff --git a/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs b/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs index 9dcddce0e7..bd1a66c61c 100644 --- a/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs @@ -54,7 +54,6 @@ public class GroupByTargetWrapper : WrapperTargetBase /// /// Identifier to perform group-by /// - [RequiredParameter] public Layout Key { get; set; } /// @@ -79,7 +78,7 @@ public GroupByTargetWrapper(Target wrappedTarget) /// The name of the target. /// The wrapped target. public GroupByTargetWrapper(string name, Target wrappedTarget) - : this(name, wrappedTarget, string.Empty) + : this(name, wrappedTarget, Layout.Empty) { } @@ -96,6 +95,15 @@ public GroupByTargetWrapper(string name, Target wrappedTarget, Layout key) Key = key; } + /// + protected override void InitializeTarget() + { + if (Key is null || ReferenceEquals(Key, Layout.Empty)) + throw new NLogConfigurationException($"GroupByTargetWrapper Key-property must be assigned. Group LogEvents using blank Key not supported."); + + base.InitializeTarget(); + } + /// protected override void Write(AsyncLogEventInfo logEvent) { diff --git a/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs b/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs index 4395a0627c..611920f795 100644 --- a/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs @@ -107,6 +107,21 @@ public PostFilteringTargetWrapper(string name, Target wrappedTarget) [ArrayParameter(typeof(FilteringRule), "when")] public IList Rules { get; } = new List(); + /// + protected override void InitializeTarget() + { + base.InitializeTarget(); + + foreach (var rule in Rules) + { + if (rule.Exists is null || ReferenceEquals(rule.Exists, ConditionExpression.Empty)) + throw new NLogConfigurationException("PostFilteringTargetWrapper When-Rules with unassigned Exists-property."); + + if (rule.Filter is null || ReferenceEquals(rule.Filter, ConditionExpression.Empty)) + throw new NLogConfigurationException("PostFilteringTargetWrapper When-Rules with unassigned Filter-property."); + } + } + /// protected override void Write(AsyncLogEventInfo logEvent) { diff --git a/src/NLog/Targets/Wrappers/WrapperTargetBase.cs b/src/NLog/Targets/Wrappers/WrapperTargetBase.cs index fe93d7890c..e1d05bb090 100644 --- a/src/NLog/Targets/Wrappers/WrapperTargetBase.cs +++ b/src/NLog/Targets/Wrappers/WrapperTargetBase.cs @@ -46,7 +46,6 @@ public abstract class WrapperTargetBase : Target /// Gets or sets the target that is wrapped by this target. /// /// - [RequiredParameter] public Target WrappedTarget { get => _wrappedTarget; @@ -83,6 +82,15 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation) WrappedTarget.Flush(asyncContinuation); } + /// + protected override void InitializeTarget() + { + if (WrappedTarget is null) + throw new NLogConfigurationException($"{GetType().Name}(Name={Name}): Missing valid target"); + + base.InitializeTarget(); + } + /// /// Writes logging event to the log target. Must be overridden in inheriting /// classes. diff --git a/tests/NLog.Targets.Mail.Tests/MailTargetTests.cs b/tests/NLog.Targets.Mail.Tests/MailTargetTests.cs index 44b8173ada..5ff5a43578 100644 --- a/tests/NLog.Targets.Mail.Tests/MailTargetTests.cs +++ b/tests/NLog.Targets.Mail.Tests/MailTargetTests.cs @@ -584,7 +584,7 @@ public void MailTarget_WithValidToAndEmptyBcc_SendsMail() } [Fact] - public void MailTarget_WithEmptyTo_ThrowsNLogRuntimeException() + public void MailTarget_WithEmptyTo_ThrowsConfigException() { var mmt = new MockMailTarget { @@ -596,17 +596,17 @@ public void MailTarget_WithEmptyTo_ThrowsNLogRuntimeException() Body = "${level} ${logger} ${message}", }; - var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => - { - cfg.LogFactory.ThrowExceptions = true; - cfg.Configuration.AddRuleForAllLevels(mmt); - }).LogFactory; - - Assert.Throws(() => logFactory.GetLogger("MyLogger").Info("log message 1")); + Assert.Throws(() => + new LogFactory().Setup().LoadConfiguration(cfg => + { + cfg.LogFactory.ThrowConfigExceptions = true; + cfg.Configuration.AddRuleForAllLevels(mmt); + }) + ); } [Fact] - public void MailTarget_WithEmptyFrom_ThrowsNLogRuntimeException() + public void MailTarget_WithEmptyFrom_ThrowsConfigException() { var mmt = new MockMailTarget { @@ -617,17 +617,18 @@ public void MailTarget_WithEmptyFrom_ThrowsNLogRuntimeException() SmtpPort = 27, Body = "${level} ${logger} ${message}" }; - var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => - { - cfg.LogFactory.ThrowExceptions = true; - cfg.Configuration.AddRuleForAllLevels(mmt); - }).LogFactory; - Assert.Throws(() => logFactory.GetLogger("MyLogger").Info("log message 1")); + Assert.Throws(() => + new LogFactory().Setup().LoadConfiguration(cfg => + { + cfg.LogFactory.ThrowConfigExceptions = true; + cfg.Configuration.AddRuleForAllLevels(mmt); + }) + ); } [Fact] - public void MailTarget_WithEmptySmtpServer_ThrowsNLogRuntimeException() + public void MailTarget_WithEmptySmtpServer_ThrowsConfigException() { var mmt = new MockMailTarget { @@ -638,13 +639,14 @@ public void MailTarget_WithEmptySmtpServer_ThrowsNLogRuntimeException() SmtpPort = 27, Body = "${level} ${logger} ${message}" }; - var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => - { - cfg.LogFactory.ThrowExceptions = true; - cfg.Configuration.AddRuleForAllLevels(mmt); - }).LogFactory; - Assert.Throws(() => logFactory.GetLogger("MyLogger").Info("log message 1")); + Assert.Throws(() => + new LogFactory().Setup().LoadConfiguration(cfg => + { + cfg.LogFactory.ThrowConfigExceptions = true; + cfg.Configuration.AddRuleForAllLevels(mmt); + }) + ); } [Fact] diff --git a/tests/NLog.UnitTests/ApiTests.cs b/tests/NLog.UnitTests/ApiTests.cs index 6fe0dc2571..0fad4e67f3 100644 --- a/tests/NLog.UnitTests/ApiTests.cs +++ b/tests/NLog.UnitTests/ApiTests.cs @@ -198,23 +198,6 @@ public void IStringValueRenderer_AppDomainFixedOutput_Attribute_NotRequired() } } - [Fact] - public void RequiredConfigOptionMustBeClass() - { - foreach (Type type in allTypes) - { - var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - foreach (var prop in properties) - { - var requiredParameter = prop.GetCustomAttribute(); - if (requiredParameter != null) - { - Assert.True(prop.PropertyType.IsClass, type.Name); - } - } - } - } - [Fact] public void SingleDefaultConfigOption() { @@ -495,6 +478,7 @@ public void ShouldNotHaveExplicitStaticConstructors() "NLog.MessageTemplates.TemplateEnumerator", "NLog.MessageTemplates.ValueFormatter", "NLog.Layouts.LayoutParser", + "NLog.Layouts.SimpleLayout", "NLog.Layouts.ValueTypeLayoutInfo", "NLog.Layouts.XmlElementBase", "NLog.LayoutRenderers.AllEventPropertiesLayoutRenderer", @@ -524,6 +508,7 @@ public void ShouldNotHaveExplicitStaticConstructors() "NLog.Config.PropertyTypeConverter", "NLog.Config.XmlLoggingConfiguration", "NLog.Conditions.ConditionExpression", + "NLog.Conditions.ConditionLiteralExpression", "NLog.Conditions.ConditionRelationalExpression", "NLog.Conditions.ConditionTokenizer", "NLog.Common.InternalLogger", diff --git a/tests/NLog.UnitTests/Config/TargetConfigurationTests.cs b/tests/NLog.UnitTests/Config/TargetConfigurationTests.cs index c1fc96b2a7..db3cb92f55 100644 --- a/tests/NLog.UnitTests/Config/TargetConfigurationTests.cs +++ b/tests/NLog.UnitTests/Config/TargetConfigurationTests.cs @@ -594,70 +594,6 @@ public void DontThrowExceptionsWhenMissingTargetName() } } - [Fact] - public void RequiredDataTypesNullableTest() - { - var c = new LogFactory().Setup().LoadConfigurationFromXml(@" - - - - - - - - - ").LogFactory.Configuration; - - var myTarget = c.FindTargetByName("myTarget") as MyRequiredTarget; - Assert.NotNull(myTarget); - - var missingStringValue = Assert.Throws(() => new LogFactory().Setup().LoadConfigurationFromXml(@" - - - - - - - - - ")); - Assert.Contains(nameof(MyRequiredTarget.StringProperty), missingStringValue.Message); - - var missingEnumValue = Assert.Throws(() => new LogFactory().Setup().LoadConfigurationFromXml(@" - - - - - - - - - ")); - Assert.Contains(nameof(MyRequiredTarget.EnumProperty), missingEnumValue.Message); - - var emptyEnumValue = Assert.Throws(() => new LogFactory().Setup().LoadConfigurationFromXml(@" - - - - - - - - - ")); - Assert.Contains(nameof(MyRequiredTarget.EnumProperty), emptyEnumValue.Message); - } - [Fact] public void DataTypesTest() { @@ -854,9 +790,13 @@ public MyNullableTarget(string name) : this() [Target("MyRequiredTarget")] public class MyRequiredTarget : Target { +#pragma warning disable CS0618 // Type or member is obsolete [RequiredParameter] +#pragma warning restore CS0618 // Type or member is obsolete public string StringProperty { get; set; } +#pragma warning disable CS0618 // Type or member is obsolete [RequiredParameter] +#pragma warning restore CS0618 // Type or member is obsolete public MyEnum? EnumProperty { get; set; } } diff --git a/tests/NLog.UnitTests/LayoutRenderers/AllEventPropertiesTests.cs b/tests/NLog.UnitTests/LayoutRenderers/AllEventPropertiesTests.cs index ab6a3f242d..82cb748bf8 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/AllEventPropertiesTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/AllEventPropertiesTests.cs @@ -127,7 +127,6 @@ public void IncludeScopeProperties() using (ScopeContext.PushProperty("Figure", "Circle")) { var logger = logFactory.GetLogger("B"); - logger.Debug(ev); } diff --git a/tests/NLog.UnitTests/LayoutRenderers/ExceptionDataLayoutRendererTests.cs b/tests/NLog.UnitTests/LayoutRenderers/ExceptionDataLayoutRendererTests.cs index c3ae3e6fad..6582b95545 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/ExceptionDataLayoutRendererTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/ExceptionDataLayoutRendererTests.cs @@ -130,50 +130,6 @@ public void BadDataForItemResultsInEmptyValueTest() logFactory.AssertDebugLastMessage(""); } - [Fact] - public void NoDatkeyTest() - { - const string exceptionMessage = "I don't like nullref exception!"; - const string exceptionDataKey = "testkey"; - const string exceptionDataValue = "testvalue"; - var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@" - - - - - - - - ").LogFactory; - - Exception ex = new ArgumentException(exceptionMessage); - ex.Data.Add(exceptionDataKey, exceptionDataValue); - logFactory.GetCurrentClassLogger().Error(ex); - logFactory.AssertDebugLastMessage(""); - } - - [Fact] - public void NoDatkeyUingParamTest() - { - const string exceptionMessage = "I don't like nullref exception!"; - const string exceptionDataKey = "testkey"; - const string exceptionDataValue = "testvalue"; - var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@" - - - - - - - - ").LogFactory; - - Exception ex = new ArgumentException(exceptionMessage); - ex.Data.Add(exceptionDataKey, exceptionDataValue); - logFactory.GetCurrentClassLogger().Error(ex); - logFactory.AssertDebugLastMessage(""); - } - [Fact] public void BaseExceptionFlippedTest() { diff --git a/tests/NLog.UnitTests/Layouts/SimpleLayoutOutputTests.cs b/tests/NLog.UnitTests/Layouts/SimpleLayoutOutputTests.cs index 6e8dc233cc..c83daddd32 100644 --- a/tests/NLog.UnitTests/Layouts/SimpleLayoutOutputTests.cs +++ b/tests/NLog.UnitTests/Layouts/SimpleLayoutOutputTests.cs @@ -253,7 +253,9 @@ public ThrowsExceptionRenderer() Message = "Some message."; } +#pragma warning disable CS0618 // Type or member is obsolete [RequiredParameter] +#pragma warning restore CS0618 // Type or member is obsolete [DefaultParameter] public string Message { get; set; } diff --git a/tests/NLog.UnitTests/Targets/EventLogTargetTests.cs b/tests/NLog.UnitTests/Targets/EventLogTargetTests.cs index d4740e9e1f..7e9965a14b 100644 --- a/tests/NLog.UnitTests/Targets/EventLogTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/EventLogTargetTests.cs @@ -111,7 +111,7 @@ public void Configuration_ShouldThrowException_WhenMaxKilobytesIsInvalid(long ma "; NLogConfigurationException ex = Assert.Throws(() => new LogFactory().Setup().LoadConfigurationFromXml(configrationText)); - Assert.Equal("EventLog MaxKilobytes must be a multiple of 64, and between 64 and 4194240", ex.Message); + Assert.Equal("EventLogTarget MaxKilobytes must be a multiple of 64, and between 64 and 4194240", ex.Message); } [Theory] @@ -126,7 +126,7 @@ public void MaxKilobytes_ShouldThrowException_WhenMaxKilobytesIsInvalid(long max target.Initialize(null); }); - Assert.Equal("EventLog MaxKilobytes must be a multiple of 64, and between 64 and 4194240", ex.Message); + Assert.Equal("EventLogTarget MaxKilobytes must be a multiple of 64, and between 64 and 4194240", ex.Message); } [Theory] From 761125ca60582ee75d4a168124ba1ee1bb50ced2 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 11 May 2025 22:20:09 +0200 Subject: [PATCH 114/224] WrapperTargetBase - No wrapped Target configured (#5815) --- src/NLog/Targets/Target.cs | 4 ++-- src/NLog/Targets/Wrappers/WrapperTargetBase.cs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/NLog/Targets/Target.cs b/src/NLog/Targets/Target.cs index 09557fdea0..c58e331a61 100644 --- a/src/NLog/Targets/Target.cs +++ b/src/NLog/Targets/Target.cs @@ -417,11 +417,11 @@ protected virtual void WriteFailedNotInitialized(AsyncLogEventInfo logEvent, Exc if (!_scannedForLayouts) { _scannedForLayouts = true; - InternalLogger.Error(_initializeException, "{0}: Disabled because NLog Target failed to initialize.", this); + InternalLogger.Error(_initializeException, "{0}: No output because target failed initialize.", this); } else { - InternalLogger.Debug("{0}: Disabled because NLog Target failed to initialize. {1} {2}", this, _initializeException?.GetType(), _initializeException?.Message); + InternalLogger.Debug("{0}: No output because target failed initialize. {1} {2}", this, _initializeException?.GetType(), _initializeException?.Message); } var initializeFailedException = new NLogRuntimeException($"Target {this} failed to initialize.", initializeException); logEvent.Continuation(initializeFailedException); diff --git a/src/NLog/Targets/Wrappers/WrapperTargetBase.cs b/src/NLog/Targets/Wrappers/WrapperTargetBase.cs index e1d05bb090..9a1e4ab952 100644 --- a/src/NLog/Targets/Wrappers/WrapperTargetBase.cs +++ b/src/NLog/Targets/Wrappers/WrapperTargetBase.cs @@ -35,7 +35,6 @@ namespace NLog.Targets.Wrappers { using System; using NLog.Common; - using NLog.Config; /// /// Base class for targets wrap other (single) targets. @@ -86,7 +85,7 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation) protected override void InitializeTarget() { if (WrappedTarget is null) - throw new NLogConfigurationException($"{GetType().Name}(Name={Name}): Missing valid target"); + throw new NLogConfigurationException($"{GetType().Name}(Name={Name}): No wrapped Target configured."); base.InitializeTarget(); } From ca0f4f9b274f8cb28d998a9284c9b5388bb9cfa8 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 15 May 2025 22:46:37 +0200 Subject: [PATCH 115/224] LoggingConfigurationParser - Prioritize LoggingRules from current config (#5818) --- src/NLog/Config/LoggingConfigurationParser.cs | 20 ++++++++++++++----- tests/NLog.UnitTests/Config/IncludeTests.cs | 3 +++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index 15e7e0d666..4fd4b57364 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -94,12 +94,19 @@ protected void LoadConfig(ILoggingConfigurationElement nlogConfig, string? baseP } var rulesList = new List(); + var rulesInsertPosition = LoggingRules.Count; //parse all other direct elements foreach (var child in validatedConfig.ValidChildren) { if (child.MatchesName("rules")) { + if (rulesList.Count == 0) + { + // Give higher priority to LoggingRules from current config-element + // But until having read first LoggingRule, then allow new LoggingRules from include-files + rulesInsertPosition = LoggingRules.Count; + } //postpone parsing to the end rulesList.Add(child); } @@ -117,7 +124,7 @@ protected void LoadConfig(ILoggingConfigurationElement nlogConfig, string? baseP foreach (var ruleChild in rulesList) { - ParseRulesElement(ruleChild, LoggingRules); + ParseRulesElement(ruleChild, LoggingRules, rulesInsertPosition); } } @@ -523,13 +530,16 @@ private bool AssertNonEmptyValue(string? value, string propertyName, string elem /// /// Parse {Rules} xml element /// - /// - /// Rules are added to this parameter. - private void ParseRulesElement(ValidatedConfigurationElement rulesElement, IList rulesCollection) + private void ParseRulesElement(ValidatedConfigurationElement rulesElement, IList rulesCollection, int rulesInsertPosition) { InternalLogger.Trace("ParseRulesElement"); rulesElement.AssertName("rules"); + if (rulesInsertPosition > rulesCollection.Count) + { + rulesInsertPosition = rulesCollection.Count; + } + foreach (var childItem in rulesElement.ValidChildren) { var loggingRule = ParseRuleElement(childItem); @@ -537,7 +547,7 @@ private void ParseRulesElement(ValidatedConfigurationElement rulesElement, IList { lock (rulesCollection) { - rulesCollection.Add(loggingRule); + rulesCollection.Insert(rulesInsertPosition++, loggingRule); } } } diff --git a/tests/NLog.UnitTests/Config/IncludeTests.cs b/tests/NLog.UnitTests/Config/IncludeTests.cs index 007367fc46..d306672d2a 100644 --- a/tests/NLog.UnitTests/Config/IncludeTests.cs +++ b/tests/NLog.UnitTests/Config/IncludeTests.cs @@ -69,6 +69,7 @@ private static void IncludeTest_inner(string includeAttrValue, string tempDir) Directory.CreateDirectory(tempDir); CreateConfigFile(tempDir, "included.nlog", @" + "); @@ -86,6 +87,8 @@ private static void IncludeTest_inner(string includeAttrValue, string tempDir) LogManager.Configuration = new XmlLoggingConfiguration(fileToLoad); LogManager.GetLogger("A").Debug("aaa"); + AssertDebugLastMessage("debug", ""); + LogManager.GetLogger("A").Info("aaa"); AssertDebugLastMessage("debug", "aaa"); } finally From ca809ddbf9c6b2f4d9ab23159be4becb909e0464 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 17 May 2025 17:24:40 +0200 Subject: [PATCH 116/224] NLog Layout with nullable reference (#5819) --- src/NLog/Config/LoggingConfiguration.cs | 7 +- .../Config/ServiceRepositoryExtensions.cs | 2 +- src/NLog/Internal/ISupportsInitialize.cs | 3 +- .../SetupConfigurationTargetBuilder.cs | 2 +- src/NLog/Internal/StringBuilderExt.cs | 2 +- src/NLog/Internal/StringHelpers.cs | 2 +- src/NLog/LayoutRenderers/LayoutRenderer.cs | 16 +- .../LayoutRendererAttribute.cs | 2 + .../WhenEmptyLayoutRendererWrapper.cs | 2 +- .../Wrappers/WrapperLayoutRendererBase.cs | 10 +- .../WrapperLayoutRendererBuilderBase.cs | 6 +- src/NLog/Layouts/CSV/CsvColumn.cs | 2 + src/NLog/Layouts/CSV/CsvLayout.cs | 67 +++++---- src/NLog/Layouts/CompoundLayout.cs | 3 +- src/NLog/Layouts/JSON/JsonArrayLayout.cs | 16 +- src/NLog/Layouts/JSON/JsonAttribute.cs | 11 +- src/NLog/Layouts/JSON/JsonLayout.cs | 39 +++-- src/NLog/Layouts/Layout.cs | 16 +- src/NLog/Layouts/LayoutAttribute.cs | 4 +- src/NLog/Layouts/LayoutParser.cs | 58 ++++---- src/NLog/Layouts/LayoutWithHeaderAndFooter.cs | 6 +- src/NLog/Layouts/SimpleLayout.cs | 57 ++++--- src/NLog/Layouts/Typed/Layout.cs | 140 +++++++++--------- .../Layouts/Typed/LayoutTypedExtensions.cs | 10 +- src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs | 50 ++++--- src/NLog/Layouts/XML/XmlAttribute.cs | 12 +- src/NLog/Layouts/XML/XmlElement.cs | 4 +- src/NLog/Layouts/XML/XmlElementBase.cs | 119 ++++++++------- src/NLog/Layouts/XML/XmlLayout.cs | 4 +- src/NLog/LogEventInfo.cs | 15 +- src/NLog/MessageTemplates/Hole.cs | 6 +- src/NLog/MessageTemplates/Literal.cs | 2 + src/NLog/MessageTemplates/LiteralHole.cs | 2 + .../MessageTemplateParameter.cs | 14 +- .../MessageTemplateParameters.cs | 28 ++-- .../MessageTemplates/TemplateEnumerator.cs | 14 +- .../TemplateParserException.cs | 4 +- src/NLog/MessageTemplates/ValueFormatter.cs | 55 ++++--- src/NLog/Targets/DefaultJsonSerializer.cs | 102 +++++++------ src/NLog/Targets/Target.cs | 38 ++--- src/NLog/Targets/TargetAttribute.cs | 4 +- src/NLog/Targets/TargetPropertyWithContext.cs | 8 +- src/NLog/Targets/TargetWithContext.cs | 87 +++++------ src/NLog/Targets/TargetWithLayout.cs | 2 + .../TargetWithLayoutHeaderAndFooter.cs | 6 +- .../Targets/Wrappers/WrapperTargetBase.cs | 6 +- src/NLog/Time/AccurateLocalTimeSource.cs | 2 + src/NLog/Time/AccurateUtcTimeSource.cs | 2 + src/NLog/Time/CachedTimeSource.cs | 2 + src/NLog/Time/FastLocalTimeSource.cs | 2 + src/NLog/Time/FastUtcTimeSource.cs | 2 + src/NLog/Time/TimeSource.cs | 6 +- src/NLog/Time/TimeSourceAttribute.cs | 4 +- .../Config/TargetConfigurationTests.cs | 2 +- .../Wrappers/WhenEmptyTests.cs | 4 +- 55 files changed, 599 insertions(+), 492 deletions(-) diff --git a/src/NLog/Config/LoggingConfiguration.cs b/src/NLog/Config/LoggingConfiguration.cs index 40d809b215..f1154cb7f9 100644 --- a/src/NLog/Config/LoggingConfiguration.cs +++ b/src/NLog/Config/LoggingConfiguration.cs @@ -932,11 +932,10 @@ internal void CheckUnusedTargets() var targetNamesAtRules = new HashSet(GetLoggingRulesThreadSafe().SelectMany(r => r.Targets).Select(t => t.Name)); var allTargets = AllTargets; - var wrappedTargets = allTargets.OfType().ToLookup(wt => wt.WrappedTarget, wt => wt); - var compoundTargets = allTargets.OfType().SelectMany(wt => wt.Targets.Select(t => new KeyValuePair(t, wt))).ToLookup(p => p.Key, p => p.Value); + ILookup wrappedTargets = allTargets.OfType().ToLookup(wt => wt.WrappedTarget, wt => (Target)wt); + ILookup compoundTargets = allTargets.OfType().SelectMany(wt => wt.Targets.Select(t => new KeyValuePair(t, wt))).ToLookup(p => p.Key, p => p.Value); - bool IsUnusedInList(Target target1, ILookup targets) - where T : Target + bool IsUnusedInList(Target target1, ILookup targets) { if (targets.Contains(target1)) { diff --git a/src/NLog/Config/ServiceRepositoryExtensions.cs b/src/NLog/Config/ServiceRepositoryExtensions.cs index 80e521cd6c..99fd2bbd01 100644 --- a/src/NLog/Config/ServiceRepositoryExtensions.cs +++ b/src/NLog/Config/ServiceRepositoryExtensions.cs @@ -42,7 +42,7 @@ namespace NLog.Config internal static class ServiceRepositoryExtensions { - internal static ServiceRepository GetServiceProvider([CanBeNull] this LoggingConfiguration loggingConfiguration) + internal static ServiceRepository GetServiceProvider([CanBeNull] this LoggingConfiguration? loggingConfiguration) { return loggingConfiguration?.LogFactory?.ServiceRepository ?? LogManager.LogFactory.ServiceRepository; } diff --git a/src/NLog/Internal/ISupportsInitialize.cs b/src/NLog/Internal/ISupportsInitialize.cs index a7d3eb53f8..4fe1111f0f 100644 --- a/src/NLog/Internal/ISupportsInitialize.cs +++ b/src/NLog/Internal/ISupportsInitialize.cs @@ -45,8 +45,7 @@ internal interface ISupportsInitialize /// /// Initializes this instance. /// - /// The configuration. - void Initialize(LoggingConfiguration configuration); + void Initialize(LoggingConfiguration? configuration); /// /// Closes this instance. diff --git a/src/NLog/Internal/SetupConfigurationTargetBuilder.cs b/src/NLog/Internal/SetupConfigurationTargetBuilder.cs index 3a1374c901..30e56ba6d0 100644 --- a/src/NLog/Internal/SetupConfigurationTargetBuilder.cs +++ b/src/NLog/Internal/SetupConfigurationTargetBuilder.cs @@ -61,7 +61,7 @@ public SetupConfigurationTargetBuilder(LogFactory logFactory, LoggingConfigurati private void UpdateTargetName(Target item) { - if (!string.IsNullOrEmpty(_targetName)) + if (_targetName != null && !string.IsNullOrEmpty(_targetName)) { item.Name = _targetName; _targetName = string.Empty; // Mark that target-name has been used diff --git a/src/NLog/Internal/StringBuilderExt.cs b/src/NLog/Internal/StringBuilderExt.cs index 50ff935ba1..3830786249 100644 --- a/src/NLog/Internal/StringBuilderExt.cs +++ b/src/NLog/Internal/StringBuilderExt.cs @@ -54,7 +54,7 @@ internal static class StringBuilderExt /// format string. If @, then serialize the value with the Default JsonConverter. /// provider, for example culture /// NLog string.Format interface - public static void AppendFormattedValue(this StringBuilder builder, object value, string format, IFormatProvider formatProvider, IValueFormatter valueFormatter) + public static void AppendFormattedValue(this StringBuilder builder, object? value, string? format, IFormatProvider? formatProvider, IValueFormatter valueFormatter) { if (value is string stringValue && string.IsNullOrEmpty(format)) { diff --git a/src/NLog/Internal/StringHelpers.cs b/src/NLog/Internal/StringHelpers.cs index f6e8c5d841..cf410c8aa2 100644 --- a/src/NLog/Internal/StringHelpers.cs +++ b/src/NLog/Internal/StringHelpers.cs @@ -165,7 +165,7 @@ internal static string Join(string separator, IEnumerable values) #endif } - internal static bool IsNullOrEmptyString(object objectValue) + internal static bool IsNullOrEmptyString(object? objectValue) { return objectValue is null || ReferenceEquals(string.Empty, objectValue) || (objectValue is string stringValue && stringValue.Length == 0); } diff --git a/src/NLog/LayoutRenderers/LayoutRenderer.cs b/src/NLog/LayoutRenderers/LayoutRenderer.cs index ea9479d645..0b05cd9c03 100644 --- a/src/NLog/LayoutRenderers/LayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LayoutRenderer.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.LayoutRenderers { using System; @@ -49,12 +51,12 @@ namespace NLog.LayoutRenderers public abstract class LayoutRenderer : ISupportsInitialize, IRenderable { private bool _isInitialized; - private IValueFormatter _valueFormatter; + private IValueFormatter? _valueFormatter; /// /// Gets the logging configuration this target is part of. /// - protected LoggingConfiguration LoggingConfiguration { get; private set; } + protected LoggingConfiguration? LoggingConfiguration { get; private set; } /// /// Value formatter @@ -88,7 +90,7 @@ public string Render(LogEventInfo logEvent) } /// - void ISupportsInitialize.Initialize(LoggingConfiguration configuration) + void ISupportsInitialize.Initialize(LoggingConfiguration? configuration) { Initialize(configuration); } @@ -103,7 +105,7 @@ void ISupportsInitialize.Close() /// Initializes this instance. /// /// The configuration. - internal void Initialize(LoggingConfiguration configuration) + internal void Initialize(LoggingConfiguration? configuration) { if (LoggingConfiguration is null) LoggingConfiguration = configuration; @@ -182,7 +184,7 @@ internal void RenderAppendBuilder(LogEventInfo logEvent, StringBuilder builder) /// Logging event. protected abstract void Append(StringBuilder builder, LogEventInfo logEvent); - internal void AppendFormattedValue(StringBuilder builder, LogEventInfo logEvent, object value, string format, CultureInfo culture) + internal void AppendFormattedValue(StringBuilder builder, LogEventInfo logEvent, object? value, string? format, CultureInfo culture) { if (format is null && value is string stringValue) { @@ -215,7 +217,7 @@ protected virtual void CloseLayoutRenderer() /// LogEvent with culture /// Culture in on Layout level /// - protected IFormatProvider GetFormatProvider(LogEventInfo logEvent, IFormatProvider layoutCulture = null) + protected IFormatProvider? GetFormatProvider(LogEventInfo logEvent, IFormatProvider? layoutCulture = null) { return layoutCulture ?? logEvent.FormatProvider ?? LoggingConfiguration?.DefaultCultureInfo; } @@ -229,7 +231,7 @@ protected IFormatProvider GetFormatProvider(LogEventInfo logEvent, IFormatProvid /// /// is preferred /// - protected CultureInfo GetCulture(LogEventInfo logEvent, CultureInfo layoutCulture = null) + protected CultureInfo GetCulture(LogEventInfo logEvent, CultureInfo? layoutCulture = null) { return layoutCulture ?? logEvent.FormatProvider as CultureInfo ?? LoggingConfiguration?.DefaultCultureInfo ?? CultureInfo.CurrentCulture; } diff --git a/src/NLog/LayoutRenderers/LayoutRendererAttribute.cs b/src/NLog/LayoutRenderers/LayoutRendererAttribute.cs index b8cf0ea91e..4e90b8e6be 100644 --- a/src/NLog/LayoutRenderers/LayoutRendererAttribute.cs +++ b/src/NLog/LayoutRenderers/LayoutRendererAttribute.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.LayoutRenderers { using System; diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs index 4f5cf5e268..8e8fdd76e0 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs @@ -62,7 +62,7 @@ protected override void InitializeLayoutRenderer() throw new NLogConfigurationException("WhenEmpty-LayoutRenderer WhenEmpty-property must be assigned."); base.InitializeLayoutRenderer(); - WhenEmpty?.Initialize(LoggingConfiguration); + WhenEmpty.Initialize(LoggingConfiguration); _skipStringValueRenderer = !TryGetStringValue(out _, out _); } diff --git a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs index f61eb0d6b2..fbd4917279 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.LayoutRenderers.Wrappers { using System.Text; @@ -56,7 +58,7 @@ public abstract class WrapperLayoutRendererBase : LayoutRenderer /// /// [DefaultParameter] - public Layout Inner { get; set; } + public Layout Inner { get; set; } = Layout.Empty; /// protected override void InitializeLayoutRenderer() @@ -68,12 +70,6 @@ protected override void InitializeLayoutRenderer() /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { - if (Inner is null) - { - InternalLogger.Warn("{0} has no configured Inner-Layout, so skipping", this); - return; - } - int orgLength = builder.Length; try { diff --git a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs index 0c5d0ac1b6..88d4d5a476 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs @@ -31,11 +31,13 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Text; +#nullable enable namespace NLog.LayoutRenderers.Wrappers { + using System; + using System.Text; + /// /// Base class for s which wrapping other s. /// diff --git a/src/NLog/Layouts/CSV/CsvColumn.cs b/src/NLog/Layouts/CSV/CsvColumn.cs index 6ad56fc9e8..96f7be1ee1 100644 --- a/src/NLog/Layouts/CSV/CsvColumn.cs +++ b/src/NLog/Layouts/CSV/CsvColumn.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using NLog.Config; diff --git a/src/NLog/Layouts/CSV/CsvLayout.cs b/src/NLog/Layouts/CSV/CsvLayout.cs index 548131bebe..0c2f690574 100644 --- a/src/NLog/Layouts/CSV/CsvLayout.cs +++ b/src/NLog/Layouts/CSV/CsvLayout.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using System.Collections.Generic; @@ -58,7 +60,7 @@ public class CsvLayout : LayoutWithHeaderAndFooter private string _actualColumnDelimiter; private string _doubleQuoteChar; private char[] _quotableCharacters; - private Layout[] _precalculateLayouts; + private Layout[]? _precalculateLayouts; /// /// Initializes a new instance of the class. @@ -68,6 +70,7 @@ public CsvLayout() Layout = this; Header = new CsvHeaderLayout(this); Footer = null; + ResolveQuoteChars(QuoteChar, out _actualColumnDelimiter, out _doubleQuoteChar, out _quotableCharacters); } /// @@ -106,7 +109,7 @@ public CsvLayout() /// Gets or sets the custom column delimiter value (valid when is set to ). /// /// - public string CustomColumnDelimiter { get; set; } + public string CustomColumnDelimiter { get; set; } = string.Empty; /// protected override void InitializeLayout() @@ -118,51 +121,51 @@ protected override void InitializeLayout() base.InitializeLayout(); - switch (Delimiter) + ResolveQuoteChars(QuoteChar, out _actualColumnDelimiter, out _doubleQuoteChar, out _quotableCharacters); + + _precalculateLayouts = ResolveLayoutPrecalculation(Columns.Select(cln => cln.Layout)); + + foreach (var csvColumn in Columns) + { + if (string.IsNullOrEmpty(csvColumn.Name)) + throw new NLogConfigurationException("CsvLayout: Contains invalid CsvColumn with unassigned Name-property"); + } + } + + private void ResolveQuoteChars(string quoteChar, out string actualColumnDelimiter, out string doubleQuoteChar, out char[] quotableCharacters) + { + actualColumnDelimiter = ResolveColumnDelimiter(Delimiter); + quotableCharacters = (quoteChar + "\r\n" + actualColumnDelimiter).ToCharArray(); + doubleQuoteChar = quoteChar + quoteChar; + } + + private string ResolveColumnDelimiter(CsvColumnDelimiterMode delimiter) + { + switch (delimiter) { case CsvColumnDelimiterMode.Auto: - _actualColumnDelimiter = CultureInfo.CurrentCulture.TextInfo.ListSeparator; - break; + return CultureInfo.CurrentCulture.TextInfo.ListSeparator; case CsvColumnDelimiterMode.Comma: - _actualColumnDelimiter = ","; - break; + return ","; case CsvColumnDelimiterMode.Semicolon: - _actualColumnDelimiter = ";"; - break; + return ";"; case CsvColumnDelimiterMode.Pipe: - _actualColumnDelimiter = "|"; - break; + return "|"; case CsvColumnDelimiterMode.Tab: - _actualColumnDelimiter = "\t"; - break; + return "\t"; case CsvColumnDelimiterMode.Space: - _actualColumnDelimiter = " "; - break; + return " "; case CsvColumnDelimiterMode.Custom: - _actualColumnDelimiter = CustomColumnDelimiter; - break; + return string.IsNullOrEmpty(CustomColumnDelimiter) ? ";" : CustomColumnDelimiter; } - _quotableCharacters = (QuoteChar + "\r\n" + _actualColumnDelimiter).ToCharArray(); - _doubleQuoteChar = QuoteChar + QuoteChar; - _precalculateLayouts = ResolveLayoutPrecalculation(Columns.Select(cln => cln.Layout)); - - foreach (var csvColumn in Columns) - { - if (string.IsNullOrEmpty(csvColumn.Name)) - throw new NLogConfigurationException("CsvLayout: Contains invalid CsvColumn with unassigned Name-property"); - - if (csvColumn.Layout is null) - { - Common.InternalLogger.Warn("CsvLayout: Contains invalid Column(Name={0}) with unassigned Layout-property.", csvColumn.Name); - } - } + return ";"; } /// @@ -275,7 +278,7 @@ private bool ColumnValueRequiresQuotes(CsvQuotingMode quoting, StringBuilder sb, internal sealed class CsvHeaderLayout : Layout { private readonly CsvLayout _parent; - private string _headerOutput; + private string? _headerOutput; /// /// Initializes a new instance of the class. diff --git a/src/NLog/Layouts/CompoundLayout.cs b/src/NLog/Layouts/CompoundLayout.cs index 94154d1d87..f7677960a9 100644 --- a/src/NLog/Layouts/CompoundLayout.cs +++ b/src/NLog/Layouts/CompoundLayout.cs @@ -31,6 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable namespace NLog.Layouts { @@ -50,7 +51,7 @@ namespace NLog.Layouts [AppDomainFixedOutput] public class CompoundLayout : Layout { - private Layout[] _precalculateLayouts; + private Layout[]? _precalculateLayouts; /// /// Initializes a new instance of the class. diff --git a/src/NLog/Layouts/JSON/JsonArrayLayout.cs b/src/NLog/Layouts/JSON/JsonArrayLayout.cs index b2efca31cc..0343e12fee 100644 --- a/src/NLog/Layouts/JSON/JsonArrayLayout.cs +++ b/src/NLog/Layouts/JSON/JsonArrayLayout.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using System.Collections.Generic; @@ -48,14 +50,10 @@ namespace NLog.Layouts [ThreadAgnostic] public class JsonArrayLayout : Layout { - private Layout[] _precalculateLayouts; + private Layout[]? _precalculateLayouts; - private IJsonConverter JsonConverter - { - get => _jsonConverter ?? (_jsonConverter = ResolveService()); - set => _jsonConverter = value; - } - private IJsonConverter _jsonConverter; + private IJsonConverter JsonConverter => _jsonConverter ?? (_jsonConverter = ResolveService()); + private IJsonConverter? _jsonConverter; /// /// Gets the array of items to include in JSON-Array @@ -86,7 +84,7 @@ protected override void InitializeLayout() /// protected override void CloseLayout() { - JsonConverter = null; + _jsonConverter = null; _precalculateLayouts = null; base.CloseLayout(); } @@ -148,7 +146,7 @@ private bool RenderLayoutJsonValue(LogEventInfo logEvent, Layout layout, StringB { layout.Render(logEvent, sb); } - else if (layout.TryGetRawValue(logEvent, out object rawValue)) + else if (layout.TryGetRawValue(logEvent, out var rawValue)) { if (!JsonConverter.SerializeObject(rawValue, sb)) { diff --git a/src/NLog/Layouts/JSON/JsonAttribute.cs b/src/NLog/Layouts/JSON/JsonAttribute.cs index df97ba640d..69c77e873f 100644 --- a/src/NLog/Layouts/JSON/JsonAttribute.cs +++ b/src/NLog/Layouts/JSON/JsonAttribute.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using System; @@ -49,7 +51,7 @@ public class JsonAttribute /// /// Initializes a new instance of the class. /// - public JsonAttribute() : this(null, null, true) { } + public JsonAttribute() : this(string.Empty, Layout.Empty, true) { } /// /// Initializes a new instance of the class. @@ -67,6 +69,7 @@ public JsonAttribute(string name, Layout layout) : this(name, layout, true) { } public JsonAttribute(string name, Layout layout, bool encode) { Name = name; + _name = Name; Layout = layout; Encode = encode; IncludeEmptyValue = false; @@ -88,7 +91,7 @@ public string Name else { var builder = new System.Text.StringBuilder(); - Targets.DefaultJsonSerializer.AppendStringEscape(builder, value, false); + Targets.DefaultJsonSerializer.AppendStringEscape(builder, value.Trim(), false); _name = builder.ToString(); } } @@ -105,13 +108,13 @@ public string Name /// Gets or sets the result value type, for conversion of layout rendering output /// /// - public Type ValueType { get => _layoutInfo.ValueType; set => _layoutInfo.ValueType = value; } + public Type? ValueType { get => _layoutInfo.ValueType; set => _layoutInfo.ValueType = value; } /// /// Gets or sets the fallback value when result value is not available /// /// - public Layout DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } + public Layout? DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } /// /// Gets or sets whether output should be encoded as Json-String-Property, or be treated as valid json. diff --git a/src/NLog/Layouts/JSON/JsonLayout.cs b/src/NLog/Layouts/JSON/JsonLayout.cs index a509885358..40600c75a0 100644 --- a/src/NLog/Layouts/JSON/JsonLayout.cs +++ b/src/NLog/Layouts/JSON/JsonLayout.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using System; @@ -52,25 +54,17 @@ namespace NLog.Layouts public class JsonLayout : Layout { private const int SpacesPerIndent = 2; - private Layout[] _precalculateLayouts; + private Layout[]? _precalculateLayouts; - private LimitRecursionJsonConvert JsonConverter - { - get => _jsonConverter ?? (_jsonConverter = new LimitRecursionJsonConvert(MaxRecursionLimit, SuppressSpaces, ResolveService())); - set => _jsonConverter = value; - } - private LimitRecursionJsonConvert _jsonConverter; - private IValueFormatter ValueFormatter - { - get => _valueFormatter ?? (_valueFormatter = ResolveService()); - set => _valueFormatter = value; - } - private IValueFormatter _valueFormatter; + private LimitRecursionJsonConvert JsonConverter => _jsonConverter ?? (_jsonConverter = new LimitRecursionJsonConvert(MaxRecursionLimit, SuppressSpaces, ResolveService())); + private LimitRecursionJsonConvert? _jsonConverter; + private IValueFormatter ValueFormatter => _valueFormatter ?? (_valueFormatter = ResolveService()); + private IValueFormatter? _valueFormatter; private sealed class LimitRecursionJsonConvert : IJsonConverter { private readonly IJsonConverter _converter; - private readonly Targets.DefaultJsonSerializer _serializer; + private readonly Targets.DefaultJsonSerializer? _serializer; private readonly Targets.JsonSerializeOptions _serializerOptions; public LimitRecursionJsonConvert(int maxRecursionLimit, bool suppressSpaces, IJsonConverter converter) @@ -80,7 +74,7 @@ public LimitRecursionJsonConvert(int maxRecursionLimit, bool suppressSpaces, IJs _serializerOptions = new Targets.JsonSerializeOptions() { MaxRecursionLimit = Math.Max(0, maxRecursionLimit), SuppressSpaces = suppressSpaces, SanitizeDictionaryKeys = true }; } - public bool SerializeObject(object value, StringBuilder builder) + public bool SerializeObject(object? value, StringBuilder builder) { if (_serializer != null) return _serializer.SerializeObject(value, builder, _serializerOptions); @@ -88,7 +82,7 @@ public bool SerializeObject(object value, StringBuilder builder) return _converter.SerializeObject(value, builder); } - public bool SerializeObjectNoLimit(object value, StringBuilder builder) + public bool SerializeObjectNoLimit(object? value, StringBuilder builder) { return _converter.SerializeObject(value, builder); } @@ -274,8 +268,8 @@ protected override void InitializeLayout() /// protected override void CloseLayout() { - JsonConverter = null; - ValueFormatter = null; + _jsonConverter = null; + _valueFormatter = null; _precalculateLayouts = null; base.CloseLayout(); } @@ -324,7 +318,8 @@ private void RenderJsonFormattedMessage(LogEventInfo logEvent, StringBuilder sb) { if (string.IsNullOrEmpty(key)) continue; - object propertyValue = GlobalDiagnosticsContext.GetObject(key); + + var propertyValue = GlobalDiagnosticsContext.GetObject(key); AppendJsonPropertyValue(key, propertyValue, sb, sb.Length == orgLength); } } @@ -352,7 +347,7 @@ private void RenderJsonFormattedMessage(LogEventInfo logEvent, StringBuilder sb) if (IncludeEventProperties && logEvent.HasProperties) { bool checkExcludeProperties = ExcludeProperties.Count > 0; - using (var propertyEnumerator = logEvent.TryCreatePropertiesInternal().GetPropertyEnumerator()) + using (var propertyEnumerator = logEvent.CreatePropertiesInternal().GetPropertyEnumerator()) { while (propertyEnumerator.MoveNext()) { @@ -409,7 +404,7 @@ private void RefreshJsonDelimiters() _completeJsonPropertyName = SuppressSpaces ? "\":" : "\": "; } - private void AppendJsonPropertyValue(string propName, object propertyValue, StringBuilder sb, bool beginJsonMessage) + private void AppendJsonPropertyValue(string propName, object? propertyValue, StringBuilder sb, bool beginJsonMessage) { if (ExcludeEmptyProperties && (propertyValue is null || ReferenceEquals(propertyValue, string.Empty))) return; @@ -428,7 +423,7 @@ private void AppendJsonPropertyValue(string propName, object propertyValue, Stri } } - private void AppendJsonPropertyValue(string propName, object propertyValue, string format, IFormatProvider formatProvider, MessageTemplates.CaptureType captureType, StringBuilder sb, bool beginJsonMessage) + private void AppendJsonPropertyValue(string propName, object? propertyValue, string? format, IFormatProvider? formatProvider, MessageTemplates.CaptureType captureType, StringBuilder sb, bool beginJsonMessage) { if (captureType == MessageTemplates.CaptureType.Serialize && MaxRecursionLimit <= 1) { diff --git a/src/NLog/Layouts/Layout.cs b/src/NLog/Layouts/Layout.cs index d1fe81751b..c90039c81f 100644 --- a/src/NLog/Layouts/Layout.cs +++ b/src/NLog/Layouts/Layout.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using System; @@ -84,14 +86,14 @@ public abstract class Layout : ISupportsInitialize, IRenderable /// Gets the logging configuration this target is part of. /// [CanBeNull] - protected internal LoggingConfiguration LoggingConfiguration { get; private set; } + protected internal LoggingConfiguration? LoggingConfiguration { get; private set; } /// /// Implicitly converts the specified string as LayoutRenderer-expression into a . /// /// Text to be converted. /// object represented by the text. - public static implicit operator Layout([Localizable(false)] string text) + public static implicit operator Layout?([Localizable(false)] string text) { return text is null ? null : FromString(text, ConfigurationItemFactory.Default); } @@ -302,7 +304,7 @@ protected virtual void RenderFormattedMessage(LogEventInfo logEvent, StringBuild /// Initializes this instance. /// /// The configuration. - void ISupportsInitialize.Initialize(LoggingConfiguration configuration) + void ISupportsInitialize.Initialize(LoggingConfiguration? configuration) { Initialize(configuration); } @@ -319,7 +321,7 @@ void ISupportsInitialize.Close() /// Initializes this instance. /// /// The configuration. - internal void Initialize(LoggingConfiguration configuration) + internal void Initialize(LoggingConfiguration? configuration) { if (!IsInitialized) { @@ -378,7 +380,7 @@ internal void PerformObjectScanning() _scannedForObjects = true; } - internal Layout[] ResolveLayoutPrecalculation(IEnumerable allLayouts) + internal Layout[]? ResolveLayoutPrecalculation(IEnumerable allLayouts) { if (!_scannedForObjects || (ThreadAgnostic && !ThreadAgnosticImmutable)) return null; @@ -481,7 +483,7 @@ public static void Register(string name, [DynamicallyAccessedMembers(Dynamically /// Optimized version of for internal Layouts, when /// override of is available. /// - internal void PrecalculateBuilderInternal(LogEventInfo logEvent, StringBuilder target, Layout[] precalculateLayout) + internal void PrecalculateBuilderInternal(LogEventInfo logEvent, StringBuilder target, Layout[]? precalculateLayout) { if (!ThreadAgnostic || ThreadAgnosticImmutable) { @@ -516,7 +518,7 @@ internal string ToStringWithNestedItems(IList nestedItems, Func /// /// rawValue if return result is true /// false if we could not determine the rawValue - internal virtual bool TryGetRawValue(LogEventInfo logEvent, out object rawValue) + internal virtual bool TryGetRawValue(LogEventInfo logEvent, out object? rawValue) { rawValue = null; return false; diff --git a/src/NLog/Layouts/LayoutAttribute.cs b/src/NLog/Layouts/LayoutAttribute.cs index 961a21422d..ffba558791 100644 --- a/src/NLog/Layouts/LayoutAttribute.cs +++ b/src/NLog/Layouts/LayoutAttribute.cs @@ -31,10 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using System; - using Config; + using NLog.Config; /// /// Marks class as Layout and attaches a type-alias name for use in NLog configuration. diff --git a/src/NLog/Layouts/LayoutParser.cs b/src/NLog/Layouts/LayoutParser.cs index c382da21fb..c4cfef5cb2 100644 --- a/src/NLog/Layouts/LayoutParser.cs +++ b/src/NLog/Layouts/LayoutParser.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using System; @@ -299,7 +301,7 @@ private static string ParseParameterValue(SimpleStringReader sr) return value; } - private static string EscapeUnicodeStringValue(string value, StringBuilder nameBuf = null) + private static string EscapeUnicodeStringValue(string value, StringBuilder? nameBuf = null) { bool escapeNext = false; @@ -424,10 +426,10 @@ private static LayoutRenderer ParseLayoutRenderer(ConfigurationItemFactory confi string typeName = ParseLayoutRendererTypeName(stringReader); var layoutRenderer = GetLayoutRenderer(typeName, configurationItemFactory, throwConfigExceptions); - Dictionary wrappers = null; - List orderedWrappers = null; + Dictionary? wrappers = null; + List? orderedWrappers = null; - string previousParameterName = null; + string? previousParameterName = null; ch = stringReader.Read(); while (ch != -1 && ch != '}') @@ -442,11 +444,12 @@ private static LayoutRenderer ParseLayoutRenderer(ConfigurationItemFactory confi if (!PropertyHelper.TryGetPropertyInfo(configurationItemFactory, layoutRenderer, parameterName, out var propertyInfo)) { - parameterTarget = LookupAmbientProperty(parameterName, configurationItemFactory, ref wrappers, ref orderedWrappers); - if (parameterTarget is null || !PropertyHelper.TryGetPropertyInfo(configurationItemFactory, parameterTarget, parameterName, out propertyInfo)) + if (TryResolveAmbientLayoutWrapper(parameterName, configurationItemFactory, ref wrappers, ref orderedWrappers, out var layoutWrapper) && layoutWrapper != null) { - parameterTarget = layoutRenderer; - propertyInfo = null; + if (PropertyHelper.TryGetPropertyInfo(configurationItemFactory, layoutWrapper, parameterName, out propertyInfo)) + { + parameterTarget = layoutWrapper; + } } } @@ -488,7 +491,7 @@ private static LayoutRenderer ParseLayoutRenderer(ConfigurationItemFactory confi return BuildCompleteLayoutRenderer(configurationItemFactory, layoutRenderer, orderedWrappers); } - private static LayoutRenderer BuildCompleteLayoutRenderer(ConfigurationItemFactory configurationItemFactory, LayoutRenderer layoutRenderer, List orderedWrappers = null) + private static LayoutRenderer BuildCompleteLayoutRenderer(ConfigurationItemFactory configurationItemFactory, LayoutRenderer layoutRenderer, List? orderedWrappers = null) { if (orderedWrappers != null) { @@ -505,7 +508,7 @@ private static LayoutRenderer BuildCompleteLayoutRenderer(ConfigurationItemFacto [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2072")] - private static object ParseLayoutRendererPropertyValue(ConfigurationItemFactory configurationItemFactory, SimpleStringReader stringReader, bool? throwConfigExceptions, string targetTypeName, PropertyInfo propertyInfo) + private static object? ParseLayoutRendererPropertyValue(ConfigurationItemFactory configurationItemFactory, SimpleStringReader stringReader, bool? throwConfigExceptions, string targetTypeName, PropertyInfo propertyInfo) { if (typeof(Layout).IsAssignableFrom(propertyInfo.PropertyType)) { @@ -546,7 +549,7 @@ private static object ParseLayoutRendererPropertyValue(ConfigurationItemFactory } } - private static string ValidatePreviousParameterName(string previousParameterName, string parameterName, LayoutRenderer layoutRenderer, bool? throwConfigExceptions) + private static string? ValidatePreviousParameterName(string? previousParameterName, string parameterName, LayoutRenderer layoutRenderer, bool? throwConfigExceptions) { if (parameterName?.Equals(previousParameterName, StringComparison.OrdinalIgnoreCase) == true) { @@ -564,28 +567,29 @@ private static string ValidatePreviousParameterName(string previousParameterName return previousParameterName; } - private static LayoutRenderer LookupAmbientProperty(string propertyName, ConfigurationItemFactory configurationItemFactory, ref Dictionary wrappers, ref List orderedWrappers) + private static bool TryResolveAmbientLayoutWrapper(string propertyName, ConfigurationItemFactory configurationItemFactory, ref Dictionary? wrappers, ref List? orderedWrappers, out LayoutRenderer? layoutRenderer) { - if (configurationItemFactory.AmbientRendererFactory.TryCreateInstance(propertyName, out var wrapperInstance)) + if (!configurationItemFactory.AmbientRendererFactory.TryCreateInstance(propertyName, out var wrapperInstance) || wrapperInstance is null) { - wrappers = wrappers ?? new Dictionary(); - orderedWrappers = orderedWrappers ?? new List(); - var wrapperType = wrapperInstance.GetType(); - if (!wrappers.TryGetValue(wrapperType, out var wrapperRenderer)) - { - wrappers[wrapperType] = wrapperInstance; - orderedWrappers.Add(wrapperInstance); - wrapperRenderer = wrapperInstance; - } - return wrapperRenderer; + layoutRenderer = null; + return false; } - return null; + wrappers = wrappers ?? new Dictionary(); + orderedWrappers = orderedWrappers ?? new List(); + var wrapperType = wrapperInstance.GetType(); + if (!wrappers.TryGetValue(wrapperType, out layoutRenderer)) + { + wrappers[wrapperType] = wrapperInstance; + orderedWrappers.Add(wrapperInstance); + layoutRenderer = wrapperInstance; + } + return true; } private static LayoutRenderer GetLayoutRenderer(string typeName, ConfigurationItemFactory configurationItemFactory, bool? throwConfigExceptions) { - LayoutRenderer layoutRenderer = null; + LayoutRenderer? layoutRenderer = null; try { @@ -621,7 +625,7 @@ private static string SetDefaultPropertyValue(string value, LayoutRenderer layou { // what we've just read is not a parameterName, but a value // assign it to a default property (denoted by empty string) - if (PropertyHelper.TryGetPropertyInfo(configurationItemFactory, layoutRenderer, string.Empty, out var propertyInfo)) + if (PropertyHelper.TryGetPropertyInfo(configurationItemFactory, layoutRenderer, string.Empty, out var propertyInfo) && propertyInfo != null) { if (!typeof(Layout).IsAssignableFrom(propertyInfo.PropertyType) && value.IndexOf('\\') >= 0) { @@ -639,7 +643,7 @@ private static string SetDefaultPropertyValue(string value, LayoutRenderer layou throw configException; } - return null; + return string.Empty; } } diff --git a/src/NLog/Layouts/LayoutWithHeaderAndFooter.cs b/src/NLog/Layouts/LayoutWithHeaderAndFooter.cs index b7eb099a97..9813f3b1eb 100644 --- a/src/NLog/Layouts/LayoutWithHeaderAndFooter.cs +++ b/src/NLog/Layouts/LayoutWithHeaderAndFooter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using NLog.Config; @@ -53,13 +55,13 @@ public class LayoutWithHeaderAndFooter : Layout /// Gets or sets the header layout. /// /// - public Layout Header { get; set; } + public Layout? Header { get; set; } /// /// Gets or sets the footer layout. /// /// - public Layout Footer { get; set; } + public Layout? Footer { get; set; } /// protected override string GetFormattedMessage(LogEventInfo logEvent) diff --git a/src/NLog/Layouts/SimpleLayout.cs b/src/NLog/Layouts/SimpleLayout.cs index 70910fce99..11400aaf83 100644 --- a/src/NLog/Layouts/SimpleLayout.cs +++ b/src/NLog/Layouts/SimpleLayout.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using System; @@ -59,8 +61,8 @@ namespace NLog.Layouts [AppDomainFixedOutput] public sealed class SimpleLayout : Layout, IUsesStackTrace, IStringValueRenderer { - private readonly IRawValue _rawValueRenderer; - private IStringValueRenderer _stringValueRenderer; + private readonly IRawValue? _rawValueRenderer; + private IStringValueRenderer? _stringValueRenderer; internal static readonly SimpleLayout Default = new SimpleLayout(); @@ -118,12 +120,14 @@ internal SimpleLayout(LayoutRenderer[] layoutRenderers, [Localizable(false)] str if (_layoutRenderers.Length == 0) { FixedText = string.Empty; + _stringValueRenderer = this; } else if (_layoutRenderers.Length == 1) { if (_layoutRenderers[0] is LiteralLayoutRenderer renderer) { FixedText = renderer.Text; + _stringValueRenderer = this; } else if (_layoutRenderers[0] is IStringValueRenderer stringValueRenderer) { @@ -156,7 +160,7 @@ internal SimpleLayout(LayoutRenderer[] layoutRenderers, [Localizable(false)] str /// /// Get the fixed text. Only set when is true /// - public string FixedText { get; } + public string? FixedText { get; } /// /// Is the message a simple formatted string? (Can skip StringBuilder) @@ -168,7 +172,7 @@ internal SimpleLayout(LayoutRenderer[] layoutRenderers, [Localizable(false)] str /// [NLogConfigurationIgnoreProperty] public ReadOnlyCollection Renderers => _renderers ?? (_renderers = new ReadOnlyCollection(_layoutRenderers)); - private ReadOnlyCollection _renderers; + private ReadOnlyCollection? _renderers; private readonly LayoutRenderer[] _layoutRenderers; /// @@ -186,7 +190,7 @@ internal SimpleLayout(LayoutRenderer[] layoutRenderers, [Localizable(false)] str /// /// Text to be converted. /// A object. - public static implicit operator SimpleLayout([Localizable(false)] string text) + public static implicit operator SimpleLayout?([Localizable(false)] string text) { if (text is null) return null; return string.IsNullOrEmpty(text) ? SimpleLayout.Default : new SimpleLayout(text); @@ -231,10 +235,10 @@ public static string Evaluate([Localizable(false)] string text, LogEventInfo log /// values provided by the appropriate layout renderers. public static string Evaluate([Localizable(false)] string text) { - return Evaluate(text, null); + return Evaluate(text, null, null); } - internal static string Evaluate(string text, LoggingConfiguration loggingConfiguration, LogEventInfo logEventInfo = null, bool? throwConfigExceptions = null) + internal static string Evaluate(string text, LoggingConfiguration? loggingConfiguration, LogEventInfo? logEventInfo = null, bool? throwConfigExceptions = null) { try { @@ -350,13 +354,13 @@ private bool PrecalculateMustRenderLayoutValue(LogEventInfo logEvent) return true; } - private static bool IsRawValueImmutable(object value) + private static bool IsRawValueImmutable(object? value) { return value != null && (Convert.GetTypeCode(value) != TypeCode.Object || value.GetType().IsValueType); } /// - internal override bool TryGetRawValue(LogEventInfo logEvent, out object rawValue) + internal override bool TryGetRawValue(LogEventInfo logEvent, out object? rawValue) { if (_rawValueRenderer is null) { @@ -366,10 +370,16 @@ internal override bool TryGetRawValue(LogEventInfo logEvent, out object rawValue return TryGetSafeRawValue(logEvent, out rawValue); } - private bool TryGetSafeRawValue(LogEventInfo logEvent, out object rawValue) + private bool TryGetSafeRawValue(LogEventInfo logEvent, out object? rawValue) { try { + if (_rawValueRenderer is null) + { + rawValue = null; + return false; + } + if (!IsInitialized) { Initialize(LoggingConfiguration); @@ -404,10 +414,10 @@ private bool TryGetSafeRawValue(LogEventInfo logEvent, out object rawValue) /// protected override string GetFormattedMessage(LogEventInfo logEvent) { - if (IsFixedText) - return FixedText; + var stringValue = FixedText; + if (stringValue != null) + return stringValue; - string stringValue = string.Empty; if (_stringValueRenderer is null || !TryGetSafeStringValue(logEvent, out stringValue)) return RenderAllocateBuilder(logEvent); else @@ -423,12 +433,14 @@ private bool TryGetSafeStringValue(LogEventInfo logEvent, out string stringValue Initialize(LoggingConfiguration); } - stringValue = _stringValueRenderer.GetFormattedString(logEvent); - if (stringValue is null) + var value = _stringValueRenderer?.GetFormattedString(logEvent); + if (value is null) { + stringValue = string.Empty; _stringValueRenderer = null; // Optimization is not possible return false; } + stringValue = value; return true; } catch (Exception exception) @@ -445,14 +457,15 @@ private bool TryGetSafeStringValue(LogEventInfo logEvent, out string stringValue } } - stringValue = null; + stringValue = string.Empty; return false; } /// protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) { - if (FixedText is null) + var fixedText = FixedText; + if (fixedText is null) { foreach (var renderer in _layoutRenderers) { @@ -461,17 +474,13 @@ protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuil } else { - target.Append(FixedText); + target.Append(fixedText); } } - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) + string? IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) { - if (IsFixedText) - return FixedText; - if (IsSimpleStringText) - return Render(logEvent); - return null; + return FixedText ?? (IsSimpleStringText ? Render(logEvent) : null); } } } diff --git a/src/NLog/Layouts/Typed/Layout.cs b/src/NLog/Layouts/Typed/Layout.cs index b1f8827de5..0cc2b3aa92 100644 --- a/src/NLog/Layouts/Typed/Layout.cs +++ b/src/NLog/Layouts/Typed/Layout.cs @@ -31,17 +31,19 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.ComponentModel; -using System.Globalization; -using System.Text; -using JetBrains.Annotations; -using NLog.Common; -using NLog.Config; -using NLog.Internal; +#nullable enable namespace NLog.Layouts { + using System; + using System.ComponentModel; + using System.Globalization; + using System.Text; + using JetBrains.Annotations; + using NLog.Common; + using NLog.Config; + using NLog.Internal; + /// /// Typed Layout for easy conversion from NLog Layout logic to a simple value (ex. integer or enum) /// @@ -49,15 +51,15 @@ namespace NLog.Layouts [ThreadAgnostic] public sealed class Layout : Layout, ILayoutTypeValue, IEquatable { - private readonly T _fixedValue; - private object _fixedObjectValue; + private readonly T? _fixedValue; + private object? _fixedObjectValue; ILayoutTypeValue ILayoutTypeValue.InnerLayout => _layoutValue; Type ILayoutTypeValue.InnerType => typeof(T); bool ILayoutTypeValue.ThreadAgnostic => true; bool ILayoutTypeValue.ThreadAgnosticImmutable => false; StackTraceUsage ILayoutTypeValue.StackTraceUsage => StackTraceUsage.None; - LoggingConfiguration ILayoutTypeValue.LoggingConfiguration => LoggingConfiguration; + LoggingConfiguration? ILayoutTypeValue.LoggingConfiguration => LoggingConfiguration; void ILayoutTypeValue.InitializeLayout() { // SONAR: Nothing to initialize @@ -66,12 +68,12 @@ void ILayoutTypeValue.CloseLayout() { // SONAR: Nothing to initialize } - bool ILayoutTypeValue.TryRenderValue(LogEventInfo logEvent, StringBuilder stringBuilder, out T value) + bool ILayoutTypeValue.TryRenderValue(LogEventInfo logEvent, StringBuilder? stringBuilder, out T? value) { value = _fixedValue; return true; } - object ILayoutTypeValue.RenderObjectValue(NLog.LogEventInfo logEvent, System.Text.StringBuilder stringBuilder) => FixedObjectValue; + object? ILayoutTypeValue.RenderObjectValue(NLog.LogEventInfo logEvent, StringBuilder? stringBuilder) => FixedObjectValue; private readonly ILayoutTypeValue _layoutValue; @@ -83,12 +85,12 @@ bool ILayoutTypeValue.TryRenderValue(LogEventInfo logEvent, StringBuilder str /// /// Fixed value /// - public T FixedValue => _fixedValue; + public T? FixedValue => _fixedValue; - private object FixedObjectValue => (_fixedObjectValue ?? (_fixedObjectValue = _fixedValue)); + private object? FixedObjectValue => _fixedObjectValue ?? (_fixedObjectValue = _fixedValue); private IPropertyTypeConverter ValueTypeConverter => _valueTypeConverter ?? (_valueTypeConverter = ResolveService()); - IPropertyTypeConverter _valueTypeConverter; + IPropertyTypeConverter? _valueTypeConverter; /// /// Initializes a new instance of the class. @@ -105,7 +107,7 @@ public Layout(Layout layout) /// Dynamic NLog Layout /// Format used for parsing string-value into result value type /// Culture used for parsing string-value into result value type - public Layout(Layout layout, string parseValueFormat, CultureInfo parseValueCulture) + public Layout(Layout layout, string? parseValueFormat, CultureInfo? parseValueCulture) { if (PropertyTypeConverter.IsComplexType(typeof(T))) { @@ -126,7 +128,7 @@ public Layout(Layout layout, string parseValueFormat, CultureInfo parseValueCult /// Initializes a new instance of the class. /// /// Fixed value - public Layout(T value) + public Layout(T? value) { _fixedValue = value; _layoutValue = this; @@ -144,12 +146,12 @@ private Layout(Func layoutMethod, LayoutRenderOptions options) /// Log event for rendering /// Fallback value when no value available /// Result value when available, else fallback to defaultValue - internal T RenderTypedValue([CanBeNull] LogEventInfo logEvent, T defaultValue = default(T)) + internal T? RenderTypedValue([CanBeNull] LogEventInfo? logEvent, T? defaultValue = default(T)) { return RenderTypedValue(logEvent, null, defaultValue); } - internal T RenderTypedValue([CanBeNull] LogEventInfo logEvent, [CanBeNull] StringBuilder stringBuilder, T defaultValue) + internal T? RenderTypedValue([CanBeNull] LogEventInfo? logEvent, [CanBeNull] StringBuilder? stringBuilder, T? defaultValue) { if (IsFixed) return _fixedValue; @@ -173,7 +175,7 @@ internal T RenderTypedValue([CanBeNull] LogEventInfo logEvent, [CanBeNull] Strin return defaultValue; } - private object RenderObjectValue([CanBeNull] LogEventInfo logEvent, [CanBeNull] StringBuilder stringBuilder) + private object? RenderObjectValue([CanBeNull] LogEventInfo logEvent, [CanBeNull] StringBuilder? stringBuilder) { if (logEvent is null) return null; @@ -222,7 +224,7 @@ public override void Precalculate(LogEventInfo logEvent) } /// - internal override void PrecalculateBuilder(LogEventInfo logEvent, StringBuilder target) + internal override void PrecalculateBuilder(LogEventInfo logEvent, StringBuilder? target) { PrecalculateInnerLayout(logEvent, target); } @@ -238,7 +240,7 @@ public static Layout FromMethod(Func layoutMethod, LayoutRen return new Layout(layoutMethod, options); } - private void PrecalculateInnerLayout(LogEventInfo logEvent, [CanBeNull] StringBuilder target) + private void PrecalculateInnerLayout(LogEventInfo logEvent, [CanBeNull] StringBuilder? target) { if (IsFixed || (_layoutValue.ThreadAgnostic && !_layoutValue.ThreadAgnosticImmutable)) return; @@ -253,7 +255,7 @@ private sealed class LayoutGenericTypeValue : LayoutTypeValue, ILayoutTypeValue< public override IPropertyTypeConverter ValueTypeConverter => _ownerLayout.ValueTypeConverter; - public LayoutGenericTypeValue(Layout layout, string parseValueFormat, CultureInfo parseValueCulture, Layout ownerLayout) + public LayoutGenericTypeValue(Layout layout, string? parseValueFormat, CultureInfo? parseValueCulture, Layout ownerLayout) : base(layout, typeof(T), parseValueFormat, parseValueCulture, null) { _ownerLayout = ownerLayout; @@ -269,7 +271,7 @@ public void CloseLayout() base.Close(); } - public bool TryRenderValue(LogEventInfo logEvent, StringBuilder stringBuilder, out T value) + public bool TryRenderValue(LogEventInfo logEvent, StringBuilder? stringBuilder, out T? value) { var objectValue = RenderObjectValue(logEvent, stringBuilder); if (objectValue is T typedValue) @@ -282,15 +284,15 @@ public bool TryRenderValue(LogEventInfo logEvent, StringBuilder stringBuilder, o } } - private bool TryParseFixedValue(Layout layout, string parseValueFormat, CultureInfo parseValueCulture, ref T fixedValue) + private bool TryParseFixedValue(Layout layout, string? parseValueFormat, CultureInfo? parseValueCulture, ref T? fixedValue) { if (layout is SimpleLayout simpleLayout && simpleLayout.IsFixedText) { - if (!string.IsNullOrEmpty(simpleLayout.FixedText)) + if (simpleLayout.FixedText != null && !string.IsNullOrEmpty(simpleLayout.FixedText)) { try { - fixedValue = (T)ParseValueFromObject(simpleLayout.FixedText, parseValueFormat, parseValueCulture); + fixedValue = (T?)ParseValueFromObject(simpleLayout.FixedText, parseValueFormat, parseValueCulture); return true; } catch (Exception ex) @@ -302,7 +304,7 @@ private bool TryParseFixedValue(Layout layout, string parseValueFormat, CultureI } else if (typeof(T) == typeof(string)) { - fixedValue = (T)(object)simpleLayout.FixedText; + fixedValue = (T)(object)(simpleLayout.FixedText ?? string.Empty); return true; } else if (Nullable.GetUnderlyingType(typeof(T)) != null) @@ -329,7 +331,7 @@ private sealed class FuncMethodValue : ILayoutTypeValue ILayoutTypeValue ILayoutTypeValue.InnerLayout => this; Type ILayoutTypeValue.InnerType => typeof(T); - LoggingConfiguration ILayoutTypeValue.LoggingConfiguration => null; + LoggingConfiguration? ILayoutTypeValue.LoggingConfiguration => null; bool ILayoutTypeValue.ThreadAgnosticImmutable => false; StackTraceUsage ILayoutTypeValue.StackTraceUsage => StackTraceUsage.None; @@ -349,13 +351,13 @@ public void CloseLayout() // SONAR: Nothing to close } - public bool TryRenderValue(LogEventInfo logEvent, StringBuilder stringBuilder, out T value) + public bool TryRenderValue(LogEventInfo logEvent, StringBuilder? stringBuilder, out T? value) { value = _layoutMethod(logEvent); return true; } - public object RenderObjectValue(LogEventInfo logEvent, StringBuilder stringBuilder) + public object? RenderObjectValue(LogEventInfo logEvent, StringBuilder? stringBuilder) { return _layoutMethod(logEvent); } @@ -366,7 +368,7 @@ public override string ToString() } } - private object ParseValueFromObject(object rawValue, string parseValueFormat, CultureInfo parseValueCulture) + private object? ParseValueFromObject(object rawValue, string? parseValueFormat, CultureInfo? parseValueCulture) { return ValueTypeConverter.Convert(rawValue, typeof(T), parseValueFormat, parseValueCulture); } @@ -418,7 +420,7 @@ public override int GetHashCode() /// Converts a given value to a . /// /// Text to be converted. - public static implicit operator Layout(T value) + public static implicit operator Layout?(T value) { if (object.Equals(value, default(T)) && !typeof(T).IsValueType) return null; @@ -429,11 +431,12 @@ public static implicit operator Layout(T value) /// Converts a given text to a . /// /// Text to be converted. - public static implicit operator Layout([Localizable(false)] string layout) + public static implicit operator Layout?([Localizable(false)] string layout) { - if (layout is null) return null; + Layout? innerLayout = layout; + if (innerLayout is null) return null; - return new Layout(layout); + return new Layout(innerLayout); } /// @@ -455,32 +458,32 @@ public static implicit operator Layout([Localizable(false)] string layout) internal interface ILayoutTypeValue { - Type InnerType { get; } + Type? InnerType { get; } ILayoutTypeValue InnerLayout { get; } - object RenderObjectValue(LogEventInfo logEvent, StringBuilder stringBuilder); + object? RenderObjectValue(LogEventInfo logEvent, StringBuilder? stringBuilder); } internal interface ILayoutTypeValue : ILayoutTypeValue { - LoggingConfiguration LoggingConfiguration { get; } + LoggingConfiguration? LoggingConfiguration { get; } bool ThreadAgnostic { get; } bool ThreadAgnosticImmutable { get; } StackTraceUsage StackTraceUsage { get; } void InitializeLayout(); void CloseLayout(); - bool TryRenderValue(LogEventInfo logEvent, StringBuilder stringBuilder, out T value); + bool TryRenderValue(LogEventInfo logEvent, StringBuilder? stringBuilder, out T? value); } - internal class LayoutTypeValue : ILayoutTypeValue + internal class LayoutTypeValue : ILayoutTypeValue, IPropertyTypeConverter { private readonly Layout _innerLayout; private readonly Type _valueType; - private readonly CultureInfo _parseValueCulture; - private readonly string _parseValueFormat; - private string _previousStringValue; - private object _previousValue; + private readonly CultureInfo? _parseValueCulture; + private readonly string? _parseValueFormat; + private string? _previousStringValue; + private object? _previousValue; - public LoggingConfiguration LoggingConfiguration => _innerLayout.LoggingConfiguration; + public LoggingConfiguration? LoggingConfiguration => _innerLayout.LoggingConfiguration; public bool ThreadAgnostic => _innerLayout.ThreadAgnostic; public bool ThreadAgnosticImmutable => _innerLayout.ThreadAgnosticImmutable; public StackTraceUsage StackTraceUsage => _innerLayout.StackTraceUsage; @@ -488,20 +491,20 @@ internal class LayoutTypeValue : ILayoutTypeValue ILayoutTypeValue ILayoutTypeValue.InnerLayout => this; Type ILayoutTypeValue.InnerType => _valueType; - public LayoutTypeValue(Layout layout, Type valueType, string parseValueFormat, CultureInfo parseValueCulture, IPropertyTypeConverter valueTypeConverter) + public LayoutTypeValue(Layout layout, Type valueType, string? parseValueFormat, CultureInfo? parseValueCulture, IPropertyTypeConverter? valueTypeConverter) { _innerLayout = layout; _valueType = valueType; _parseValueFormat = parseValueFormat; _parseValueCulture = parseValueCulture; - ValueTypeConverter = valueTypeConverter; + ValueTypeConverter = valueTypeConverter ?? this; } - public object TryParseFixedValue() + public object? TryParseFixedValue() { - if (_innerLayout is SimpleLayout simpleLayout && simpleLayout.IsFixedText) + if (_innerLayout is SimpleLayout simpleLayout && simpleLayout.FixedText != null) { - if (TryParseValueFromString(simpleLayout.FixedText, _parseValueFormat, _parseValueCulture, out object objectValue)) + if (TryParseValueFromString(simpleLayout.FixedText, out var objectValue)) { return objectValue; } @@ -523,7 +526,7 @@ protected void Close() _previousValue = null; } - public object RenderObjectValue(LogEventInfo logEvent, StringBuilder stringBuilder) + public object? RenderObjectValue(LogEventInfo logEvent, StringBuilder? stringBuilder) { if (_innerLayout.TryGetRawValue(logEvent, out var rawValue)) { @@ -531,7 +534,7 @@ public object RenderObjectValue(LogEventInfo logEvent, StringBuilder stringBuild { if (string.IsNullOrEmpty(rawStringValue)) { - TryParseValueFromString(rawStringValue, _parseValueFormat, _parseValueCulture, out var objectValue); + TryParseValueFromString(rawStringValue, out var objectValue); return objectValue; } } @@ -542,7 +545,7 @@ public object RenderObjectValue(LogEventInfo logEvent, StringBuilder stringBuild return null; } - TryParseValueFromObject(rawValue, _parseValueFormat, _parseValueCulture, out var objectValue); + TryParseValueFromObject(rawValue, out var objectValue); return objectValue; } } @@ -556,7 +559,7 @@ public object RenderObjectValue(LogEventInfo logEvent, StringBuilder stringBuild return previousValue; } - if (TryParseValueFromString(stringValue, _parseValueFormat, _parseValueCulture, out var parsedValue)) + if (TryParseValueFromString(stringValue, out var parsedValue)) { if (string.IsNullOrEmpty(previousStringValue) || stringValue?.Length < 3) { @@ -570,7 +573,7 @@ public object RenderObjectValue(LogEventInfo logEvent, StringBuilder stringBuild return null; } - private string RenderStringValue(LogEventInfo logEvent, StringBuilder stringBuilder, string previousStringValue) + private string RenderStringValue(LogEventInfo logEvent, StringBuilder? stringBuilder, string? previousStringValue) { if (_innerLayout is IStringValueRenderer stringLayout) { @@ -584,10 +587,10 @@ private string RenderStringValue(LogEventInfo logEvent, StringBuilder stringBuil _innerLayout.Render(logEvent, stringBuilder); if (stringBuilder.Length == 0) return string.Empty; - else if (!string.IsNullOrEmpty(previousStringValue) && stringBuilder.EqualTo(previousStringValue)) - return previousStringValue; - else + else if (previousStringValue is null || string.IsNullOrEmpty(previousStringValue) || !stringBuilder.EqualTo(previousStringValue)) return stringBuilder.ToString(); + else + return previousStringValue; } else { @@ -595,11 +598,11 @@ private string RenderStringValue(LogEventInfo logEvent, StringBuilder stringBuil } } - private bool TryParseValueFromObject(object rawValue, string parseValueFormat, CultureInfo parseValueCulture, out object parsedValue) + private bool TryParseValueFromObject(object rawValue, out object? parsedValue) { try { - parsedValue = ParseValueFromObject(rawValue, parseValueFormat, parseValueCulture); + parsedValue = ParseValueFromObject(rawValue); return true; } catch (Exception ex) @@ -610,12 +613,12 @@ private bool TryParseValueFromObject(object rawValue, string parseValueFormat, C } } - private object ParseValueFromObject(object rawValue, string parseValueFormat, CultureInfo parseValueCulture) + private object? ParseValueFromObject(object rawValue) { - return ValueTypeConverter.Convert(rawValue, _valueType, parseValueFormat, parseValueCulture); + return ValueTypeConverter.Convert(rawValue, _valueType, _parseValueFormat, _parseValueCulture); } - private bool TryParseValueFromString(string stringValue, string parseValueFormat, CultureInfo parseValueCulture, out object parsedValue) + private bool TryParseValueFromString(string stringValue, out object? parsedValue) { if (string.IsNullOrEmpty(stringValue)) { @@ -623,12 +626,17 @@ private bool TryParseValueFromString(string stringValue, string parseValueFormat return true; } - return TryParseValueFromObject(stringValue, parseValueFormat, parseValueCulture, out parsedValue); + return TryParseValueFromObject(stringValue, out parsedValue); } public override string ToString() { return _innerLayout.ToString(); } + + object? IPropertyTypeConverter.Convert(object? propertyValue, Type propertyType, string? format, IFormatProvider? formatProvider) + { + return null; + } } } diff --git a/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs b/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs index cc6c36c487..126e5c2fe3 100644 --- a/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs +++ b/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs @@ -31,12 +31,14 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using JetBrains.Annotations; -using NLog.Layouts; -using NLog.Targets; +#nullable enable namespace NLog { + using JetBrains.Annotations; + using NLog.Layouts; + using NLog.Targets; + /// /// Extensions for NLog . /// @@ -51,7 +53,7 @@ public static class LayoutTypedExtensions /// The logevent info. /// Fallback value when no value available /// Result value when available, else fallback to defaultValue - public static T RenderValue([CanBeNull] this Layout layout, [CanBeNull] LogEventInfo logEvent, T defaultValue = default(T)) + public static T? RenderValue([CanBeNull] this Layout? layout, [CanBeNull] LogEventInfo? logEvent, T? defaultValue = default(T)) { return layout is null ? defaultValue : layout.RenderTypedValue(logEvent, defaultValue); } diff --git a/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs b/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs index 307ad37654..5282b8e77e 100644 --- a/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs +++ b/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs @@ -31,14 +31,16 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Globalization; -using System.Text; -using NLog.Config; -using NLog.Internal; +#nullable enable namespace NLog.Layouts { + using System; + using System.Globalization; + using System.Text; + using NLog.Config; + using NLog.Internal; + /// /// Typed Value that is easily configured from NLog.config file /// @@ -77,7 +79,7 @@ public Layout Layout /// Gets or sets the result value type, for conversion of layout rendering output /// /// - public Type ValueType + public Type? ValueType { get => _valueType; [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow ValueType assign from config", "IL2067")] @@ -93,14 +95,14 @@ public Type ValueType _useDefaultWhenEmptyString = UseDefaultWhenEmptyString(_valueType, _defaultValue); } } - private Type _valueType; - private Func _createDefaultValue; + private Type? _valueType; + private Func? _createDefaultValue; /// /// Gets or sets the fallback value when result value is not available /// /// - public Layout DefaultValue + public Layout? DefaultValue { get => _defaultValue; set @@ -110,10 +112,10 @@ public Layout DefaultValue _useDefaultWhenEmptyString = UseDefaultWhenEmptyString(_valueType, _defaultValue); } } - private Layout _defaultValue; + private Layout? _defaultValue; private bool _useDefaultWhenEmptyString; - private static bool UseDefaultWhenEmptyString(Type valueType, Layout defaultValue) + private static bool UseDefaultWhenEmptyString(Type? valueType, Layout? defaultValue) { if (valueType is null || typeof(string).Equals(valueType) || typeof(object).Equals(valueType)) { @@ -146,7 +148,7 @@ public bool ForceDefaultValueNull /// Gets or sets format used for parsing parameter string-value for type-conversion /// /// - public string ValueParseFormat + public string? ValueParseFormat { get => _valueParseFormat; set @@ -156,13 +158,13 @@ public string ValueParseFormat _defaultLayoutValue = null; } } - private string _valueParseFormat; + private string? _valueParseFormat; /// /// Gets or sets the culture used for parsing parameter string-value for type-conversion /// /// - public CultureInfo ValueParseCulture + public CultureInfo? ValueParseCulture { get => _valueParseCulture; set @@ -172,14 +174,14 @@ public CultureInfo ValueParseCulture _defaultLayoutValue = null; } } - private CultureInfo _valueParseCulture; + private CultureInfo? _valueParseCulture; /// /// Render Result Value /// /// Log event for rendering /// Result value when available, else fallback to defaultValue - public object RenderValue(LogEventInfo logEvent) + public object? RenderValue(LogEventInfo logEvent) { var objectValue = LayoutValue.RenderObjectValue(logEvent, null); if (objectValue is null || (_useDefaultWhenEmptyString && StringHelpers.IsNullOrEmptyString(objectValue))) @@ -191,12 +193,12 @@ public object RenderValue(LogEventInfo logEvent) } private ILayoutTypeValue LayoutValue => _layoutValue ?? (_layoutValue = BuildLayoutTypeValue(Layout)); - private ILayoutTypeValue _layoutValue; + private ILayoutTypeValue? _layoutValue; private ILayoutTypeValue DefaultLayoutValue => _defaultLayoutValue ?? (_defaultLayoutValue = BuildLayoutTypeValue(DefaultValue)); - private ILayoutTypeValue _defaultLayoutValue; + private ILayoutTypeValue? _defaultLayoutValue; - private ILayoutTypeValue BuildLayoutTypeValue(Layout layout) + private ILayoutTypeValue BuildLayoutTypeValue(Layout? layout) { if (layout is null) { @@ -211,7 +213,7 @@ private ILayoutTypeValue BuildLayoutTypeValue(Layout layout) } else { - layout = SimpleLayout.Empty; + layout = Layout.Empty; } } @@ -233,16 +235,16 @@ private ILayoutTypeValue BuildLayoutTypeValue(Layout layout) private sealed class FixedLayoutTypeValue : ILayoutTypeValue { - private readonly object _fixedValue; + private readonly object? _fixedValue; public ILayoutTypeValue InnerLayout => this; - public Type InnerType => _fixedValue?.GetType(); + public Type? InnerType => _fixedValue?.GetType(); public FixedLayoutTypeValue(object fixedValue) { _fixedValue = fixedValue; } - public object RenderObjectValue(LogEventInfo logEvent, StringBuilder stringBuilder) + public object? RenderObjectValue(LogEventInfo logEvent, StringBuilder? stringBuilder) { return _fixedValue; } @@ -264,7 +266,7 @@ public StringLayoutTypeValue(Layout layout) _innerLayout = layout; } - public object RenderObjectValue(LogEventInfo logEvent, StringBuilder stringBuilder) + public object RenderObjectValue(LogEventInfo logEvent, StringBuilder? stringBuilder) { return _innerLayout.Render(logEvent); } diff --git a/src/NLog/Layouts/XML/XmlAttribute.cs b/src/NLog/Layouts/XML/XmlAttribute.cs index 902681157a..c60b215477 100644 --- a/src/NLog/Layouts/XML/XmlAttribute.cs +++ b/src/NLog/Layouts/XML/XmlAttribute.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using System; @@ -49,7 +51,7 @@ public class XmlAttribute /// /// Initializes a new instance of the class. /// - public XmlAttribute() : this(null, null, true) { } + public XmlAttribute() : this(string.Empty, Layout.Empty, true) { } /// /// Initializes a new instance of the class. @@ -76,8 +78,8 @@ public XmlAttribute(string name, Layout layout, bool encode) /// Gets or sets the name of the attribute. /// /// - public string Name { get => _name; set => _name = XmlHelper.XmlConvertToElementName(value?.Trim()); } - private string _name; + public string Name { get => _name; set => _name = XmlHelper.XmlConvertToElementName(value?.Trim() ?? string.Empty); } + private string _name = string.Empty; /// /// Gets or sets the layout that will be rendered as the attribute's value. @@ -89,13 +91,13 @@ public XmlAttribute(string name, Layout layout, bool encode) /// Gets or sets the result value type, for conversion of layout rendering output /// /// - public Type ValueType { get => _layoutInfo.ValueType; set => _layoutInfo.ValueType = value; } + public Type? ValueType { get => _layoutInfo.ValueType; set => _layoutInfo.ValueType = value; } /// /// Gets or sets the fallback value when result value is not available /// /// - public Layout DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } + public Layout? DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } /// /// Gets or sets whether output should be encoded with Xml-string escaping, or be treated as valid xml-attribute-value diff --git a/src/NLog/Layouts/XML/XmlElement.cs b/src/NLog/Layouts/XML/XmlElement.cs index 0743051038..60ff70d821 100644 --- a/src/NLog/Layouts/XML/XmlElement.cs +++ b/src/NLog/Layouts/XML/XmlElement.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using NLog.Config; @@ -46,7 +48,7 @@ public class XmlElement : XmlElementBase /// /// Initializes a new instance of the class. /// - public XmlElement() : this(DefaultElementName, null) + public XmlElement() : this(DefaultElementName, Layout.Empty) { } diff --git a/src/NLog/Layouts/XML/XmlElementBase.cs b/src/NLog/Layouts/XML/XmlElementBase.cs index 77753865ec..191bf5fa69 100644 --- a/src/NLog/Layouts/XML/XmlElementBase.cs +++ b/src/NLog/Layouts/XML/XmlElementBase.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using System; @@ -46,7 +48,7 @@ namespace NLog.Layouts /// public abstract class XmlElementBase : Layout { - private Layout[] _precalculateLayouts; + private Layout[]? _precalculateLayouts; private const string DefaultPropertyName = "property"; private const string DefaultPropertyKeyAttribute = "key"; private const string DefaultCollectionItemName = "item"; @@ -66,8 +68,7 @@ protected XmlElementBase(string elementName, Layout elementValue) /// /// Name of the XML element /// - /// Upgrade to private protected when using C# 7.2 - internal string ElementNameInternal { get => _elementNameInternal; set => _elementNameInternal = XmlHelper.XmlConvertToElementName(value?.Trim()); } + internal string ElementNameInternal { get => _elementNameInternal; set => _elementNameInternal = XmlHelper.XmlConvertToElementName(value?.Trim() ?? string.Empty); } private string _elementNameInternal = string.Empty; /// @@ -174,7 +175,7 @@ public string PropertiesElementName _propertiesElementName = value; _propertiesElementNameHasFormat = value?.IndexOf('{') >= 0; if (!_propertiesElementNameHasFormat) - _propertiesElementName = XmlHelper.XmlConvertToElementName(value?.Trim()); + _propertiesElementName = XmlHelper.XmlConvertToElementName(value?.Trim() ?? string.Empty); } } private string _propertiesElementName = DefaultPropertyName; @@ -203,7 +204,7 @@ public string PropertiesElementName /// Will replace newlines in attribute-value with /// /// - public string PropertiesElementValueAttribute { get; set; } + public string PropertiesElementValueAttribute { get; set; } = string.Empty; /// /// XML element name to use for rendering IList-collections items @@ -218,7 +219,7 @@ public string PropertiesElementName public int MaxRecursionLimit { get; set; } = 1; private ObjectReflectionCache ObjectReflectionCache => _objectReflectionCache ?? (_objectReflectionCache = new ObjectReflectionCache(LoggingConfiguration.GetServiceProvider())); - private ObjectReflectionCache _objectReflectionCache; + private ObjectReflectionCache? _objectReflectionCache; private static readonly IEqualityComparer _referenceEqualsComparer = SingleItemOptimizedHashSet.ReferenceEqualityComparer.Default; private const int MaxXmlLength = 512 * 1024; @@ -360,7 +361,8 @@ private void RenderXmlFormattedMessage(LogEventInfo logEvent, StringBuilder sb) private bool HasNestedXmlElements(LogEventInfo logEvent) { - if (LayoutWrapper.Inner != null) + var innerText = LayoutWrapper.Inner; + if (!ReferenceEquals(innerText, null) && !ReferenceEquals(innerText, Layout.Empty)) return true; if (Elements.Count > 0) @@ -408,7 +410,7 @@ private void AppendLogEventProperties(LogEventInfo logEventInfo, StringBuilder s return; bool checkExcludeProperties = ExcludeProperties.Count > 0; - using (var propertyEnumerator = logEventInfo.TryCreatePropertiesInternal().GetPropertyEnumerator()) + using (var propertyEnumerator = logEventInfo.CreatePropertiesInternal().GetPropertyEnumerator()) { while (propertyEnumerator.MoveNext()) { @@ -431,67 +433,74 @@ private void AppendLogEventProperties(LogEventInfo logEventInfo, StringBuilder s } } - private bool AppendXmlPropertyObjectValue(string propName, object propertyValue, StringBuilder sb, int orgLength, SingleItemOptimizedHashSet objectsInPath, int depth, bool ignorePropertiesElementName = false) + private bool AppendXmlPropertyObjectValue(string propName, object? propertyValue, StringBuilder sb, int orgLength, SingleItemOptimizedHashSet objectsInPath, int depth, bool ignorePropertiesElementName = false) { - var convertibleValue = propertyValue as IConvertible; - var objTypeCode = convertibleValue?.GetTypeCode() ?? (propertyValue is null ? TypeCode.Empty : TypeCode.Object); - if (objTypeCode != TypeCode.Object) + if (propertyValue is IConvertible convertibleValue) + { + var objTypeCode = convertibleValue.GetTypeCode(); + if (objTypeCode != TypeCode.Object) + { + string xmlValueString = XmlHelper.XmlConvertToString(convertibleValue, objTypeCode, true); + AppendXmlPropertyStringValue(propName, xmlValueString, sb, orgLength, false, ignorePropertiesElementName); + return true; + } + } + else if (propertyValue is null) { - string xmlValueString = XmlHelper.XmlConvertToString(convertibleValue, objTypeCode, true); + string xmlValueString = XmlHelper.XmlConvertToString(null, TypeCode.Empty, true); AppendXmlPropertyStringValue(propName, xmlValueString, sb, orgLength, false, ignorePropertiesElementName); + return true; } - else + + int beforeValueLength = sb.Length; + if (beforeValueLength > MaxXmlLength) { - int beforeValueLength = sb.Length; - if (beforeValueLength > MaxXmlLength) - { - return false; - } + return false; + } - int nextDepth = objectsInPath.Count == 0 ? depth : (depth + 1); // Allow serialization of list-items - if (nextDepth > MaxRecursionLimit) - { - return false; - } + int nextDepth = objectsInPath.Count == 0 ? depth : (depth + 1); // Allow serialization of list-items + if (nextDepth > MaxRecursionLimit) + { + return false; + } - if (objectsInPath.Contains(propertyValue)) - { - return false; - } + if (objectsInPath.Contains(propertyValue)) + { + return false; + } - if (propertyValue is System.Collections.IDictionary dict) + if (propertyValue is System.Collections.IDictionary dict) + { + using (StartCollectionScope(ref objectsInPath, dict)) { - using (StartCollectionScope(ref objectsInPath, dict)) - { - AppendXmlDictionaryObject(propName, dict, sb, orgLength, objectsInPath, nextDepth, ignorePropertiesElementName); - } + AppendXmlDictionaryObject(propName, dict, sb, orgLength, objectsInPath, nextDepth, ignorePropertiesElementName); } - else if (propertyValue is System.Collections.IEnumerable collection) + } + else if (propertyValue is System.Collections.IEnumerable collection) + { + if (ObjectReflectionCache.TryLookupExpandoObject(propertyValue, out var propertyValues)) { - if (ObjectReflectionCache.TryLookupExpandoObject(propertyValue, out var propertyValues)) - { - using (new SingleItemOptimizedHashSet.SingleItemScopedInsert(propertyValue, ref objectsInPath, false, _referenceEqualsComparer)) - { - AppendXmlObjectPropertyValues(propName, ref propertyValues, sb, orgLength, ref objectsInPath, nextDepth, ignorePropertiesElementName); - } - } - else + using (new SingleItemOptimizedHashSet.SingleItemScopedInsert(propertyValue, ref objectsInPath, false, _referenceEqualsComparer)) { - using (StartCollectionScope(ref objectsInPath, collection)) - { - AppendXmlCollectionObject(propName, collection, sb, orgLength, objectsInPath, nextDepth, ignorePropertiesElementName); - } + AppendXmlObjectPropertyValues(propName, ref propertyValues, sb, orgLength, ref objectsInPath, nextDepth, ignorePropertiesElementName); } } else { - using (new SingleItemOptimizedHashSet.SingleItemScopedInsert(propertyValue, ref objectsInPath, false, _referenceEqualsComparer)) + using (StartCollectionScope(ref objectsInPath, collection)) { - var propertyValues = ObjectReflectionCache.LookupObjectProperties(propertyValue); - AppendXmlObjectPropertyValues(propName, ref propertyValues, sb, orgLength, ref objectsInPath, nextDepth, ignorePropertiesElementName); + AppendXmlCollectionObject(propName, collection, sb, orgLength, objectsInPath, nextDepth, ignorePropertiesElementName); } } } + else + { + using (new SingleItemOptimizedHashSet.SingleItemScopedInsert(propertyValue, ref objectsInPath, false, _referenceEqualsComparer)) + { + var propertyValues = ObjectReflectionCache.LookupObjectProperties(propertyValue); + AppendXmlObjectPropertyValues(propName, ref propertyValues, sb, orgLength, ref objectsInPath, nextDepth, ignorePropertiesElementName); + } + } return true; } @@ -532,7 +541,11 @@ private void AppendXmlDictionaryObject(string propName, System.Collections.IDict if (beforeValueLength > MaxXmlLength) break; - if (!AppendXmlPropertyObjectValue(item.Key?.ToString(), item.Value, sb, orgLength, objectsInPath, depth)) + var propertyName = item.Key?.ToString() ?? string.Empty; + if (string.IsNullOrEmpty(propertyName)) + continue; + + if (!AppendXmlPropertyObjectValue(propertyName, item.Value, sb, orgLength, objectsInPath, depth)) { sb.Length = beforeValueLength; } @@ -565,7 +578,7 @@ private void AppendXmlObjectPropertyValues(string propName, ref ObjectReflection var propertyTypeCode = property.TypeCode; if (propertyTypeCode != TypeCode.Object) { - string xmlValueString = XmlHelper.XmlConvertToString((IConvertible)property.Value, propertyTypeCode, true); + string xmlValueString = XmlHelper.XmlConvertToString((IConvertible?)property.Value, propertyTypeCode, true); AppendXmlPropertyStringValue(property.Name, xmlValueString, sb, orgLength, false, ignorePropertiesElementName); } else @@ -580,7 +593,7 @@ private void AppendXmlObjectPropertyValues(string propName, ref ObjectReflection AppendClosingPropertyTag(propNameElement, sb, ignorePropertiesElementName); } - private string AppendXmlPropertyValue(string propName, object propertyValue, StringBuilder sb, int orgLength, bool ignoreValue = false, bool ignorePropertiesElementName = false) + private string AppendXmlPropertyValue(string propName, object? propertyValue, StringBuilder sb, int orgLength, bool ignoreValue = false, bool ignorePropertiesElementName = false) { string xmlValueString = ignoreValue ? string.Empty : XmlHelper.XmlConvertToStringSafe(propertyValue); return AppendXmlPropertyStringValue(propName, xmlValueString, sb, orgLength, ignoreValue, ignorePropertiesElementName); @@ -591,7 +604,7 @@ private string AppendXmlPropertyStringValue(string propName, string xmlValueStri if (string.IsNullOrEmpty(PropertiesElementName)) return string.Empty; // Not supported - propName = propName?.Trim(); + propName = propName?.Trim() ?? string.Empty; if (string.IsNullOrEmpty(propName)) return string.Empty; // Not supported diff --git a/src/NLog/Layouts/XML/XmlLayout.cs b/src/NLog/Layouts/XML/XmlLayout.cs index 566bbec4e4..98286bbbae 100644 --- a/src/NLog/Layouts/XML/XmlLayout.cs +++ b/src/NLog/Layouts/XML/XmlLayout.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Layouts { using NLog.Config; @@ -52,7 +54,7 @@ public class XmlLayout : XmlElementBase /// Initializes a new instance of the class. /// public XmlLayout() - : this(DefaultRootElementName, null) + : this(DefaultRootElementName, Layout.Empty) { } diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index 128f09494a..8ca1885a65 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -392,14 +392,17 @@ public bool HasProperties return properties; } - private PropertiesDictionary CreatePropertiesInternal(IList? templateParameters = null) + internal PropertiesDictionary CreatePropertiesInternal(IList? templateParameters = null) { - PropertiesDictionary properties = new PropertiesDictionary(templateParameters); - Interlocked.CompareExchange(ref _properties, properties, null); - if (templateParameters is null && HasMessageTemplateParameters) + if (_properties is null) { - // Trigger capture of MessageTemplateParameters from logevent-message - CalcFormattedMessage(); + PropertiesDictionary properties = new PropertiesDictionary(templateParameters); + Interlocked.CompareExchange(ref _properties, properties, null); + if (templateParameters is null && HasMessageTemplateParameters) + { + // Trigger capture of MessageTemplateParameters from logevent-message + CalcFormattedMessage(); + } } return _properties; diff --git a/src/NLog/MessageTemplates/Hole.cs b/src/NLog/MessageTemplates/Hole.cs index 12e606e9a9..8b0b67045e 100644 --- a/src/NLog/MessageTemplates/Hole.cs +++ b/src/NLog/MessageTemplates/Hole.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.MessageTemplates { /// @@ -45,7 +47,7 @@ struct Hole /// /// Constructor /// - public Hole(string name, string format, CaptureType captureType, short parameterIndex, short alignment) + public Hole(string name, string? format, CaptureType captureType, short parameterIndex, short alignment) { Name = name; Format = format; @@ -60,7 +62,7 @@ public Hole(string name, string format, CaptureType captureType, short parameter public readonly string Name; /// Format to render the parameter. /// This is everything between ":" and the first unescaped "}" - public readonly string Format; + public readonly string? Format; /// /// Type /// diff --git a/src/NLog/MessageTemplates/Literal.cs b/src/NLog/MessageTemplates/Literal.cs index c14380df55..19bd5a9039 100644 --- a/src/NLog/MessageTemplates/Literal.cs +++ b/src/NLog/MessageTemplates/Literal.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.MessageTemplates { /// diff --git a/src/NLog/MessageTemplates/LiteralHole.cs b/src/NLog/MessageTemplates/LiteralHole.cs index 7a86bb5bc1..880502c4ae 100644 --- a/src/NLog/MessageTemplates/LiteralHole.cs +++ b/src/NLog/MessageTemplates/LiteralHole.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.MessageTemplates { /// diff --git a/src/NLog/MessageTemplates/MessageTemplateParameter.cs b/src/NLog/MessageTemplates/MessageTemplateParameter.cs index 4e97f96f13..1ff4c285e7 100644 --- a/src/NLog/MessageTemplates/MessageTemplateParameter.cs +++ b/src/NLog/MessageTemplates/MessageTemplateParameter.cs @@ -31,11 +31,13 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using JetBrains.Annotations; -using NLog.Internal; +#nullable enable namespace NLog.MessageTemplates { + using JetBrains.Annotations; + using NLog.Internal; + /// /// Description of a single parameter extracted from a MessageTemplate /// @@ -52,14 +54,14 @@ public struct MessageTemplateParameter /// Parameter Value extracted from the -array /// [CanBeNull] - public object Value { get; } + public object? Value { get; } /// /// Format to render the parameter. /// This is everything between ":" and the first unescaped "}" /// [CanBeNull] - public string Format { get; } + public string? Format { get; } /// /// Parameter method that should be used to render the parameter @@ -102,7 +104,7 @@ public int? PositionalIndex /// Parameter Name /// Parameter Value /// Parameter Format - internal MessageTemplateParameter([NotNull] string name, object value, string format) + internal MessageTemplateParameter([NotNull] string name, object? value, string? format) { Name = Guard.ThrowIfNull(name); Value = value; @@ -117,7 +119,7 @@ internal MessageTemplateParameter([NotNull] string name, object value, string fo /// Parameter Value /// Parameter Format /// Parameter CaptureType - public MessageTemplateParameter([NotNull] string name, object value, string format, CaptureType captureType) + public MessageTemplateParameter([NotNull] string name, object? value, string? format, CaptureType captureType) { Name = Guard.ThrowIfNull(name); Value = value; diff --git a/src/NLog/MessageTemplates/MessageTemplateParameters.cs b/src/NLog/MessageTemplates/MessageTemplateParameters.cs index 65e4834bfd..71090c1559 100644 --- a/src/NLog/MessageTemplates/MessageTemplateParameters.cs +++ b/src/NLog/MessageTemplates/MessageTemplateParameters.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.MessageTemplates { using System; @@ -78,20 +80,26 @@ public sealed class MessageTemplateParameters : IEnumerable /// including any parameter placeholders /// All - internal MessageTemplateParameters(string message, object[] parameters) + internal MessageTemplateParameters(string message, object?[] parameters) { - var hasParameters = parameters?.Length > 0; - bool isPositional = hasParameters; - bool isValidTemplate = !hasParameters; - _parameters = hasParameters ? ParseMessageTemplate(message, parameters, out isPositional, out isValidTemplate) : ArrayHelper.Empty(); - IsPositional = isPositional; - IsValidTemplate = isValidTemplate; + if (parameters is null || parameters.Length == 0) + { + _parameters = ArrayHelper.Empty(); + IsPositional = false; + IsValidTemplate = true; + } + else + { + _parameters = ParseMessageTemplate(message, parameters, out var isPositional, out var isValidTemplate); + IsPositional = isPositional; + IsValidTemplate = isValidTemplate; + } } /// /// Constructor for named parameters that already has been parsed /// - internal MessageTemplateParameters(IList templateParameters, string message, object[] parameters) + internal MessageTemplateParameters(IList templateParameters, string message, object?[]? parameters) { _parameters = templateParameters ?? ArrayHelper.Empty(); if (parameters != null && _parameters.Count != parameters.Length) @@ -103,7 +111,7 @@ internal MessageTemplateParameters(IList templateParam /// /// Create MessageTemplateParameter from /// - private static IList ParseMessageTemplate(string template, object[] parameters, out bool isPositional, out bool isValidTemplate) + private static IList ParseMessageTemplate(string template, object?[] parameters, out bool isPositional, out bool isValidTemplate) { isPositional = true; isValidTemplate = true; @@ -184,7 +192,7 @@ private static short GetMaxHoleIndex(short maxHoleIndex, short holeIndex) return maxHoleIndex; } - private static object GetHoleValueSafe(object[] parameters, short holeIndex, ref bool isValidTemplate) + private static object? GetHoleValueSafe(object?[] parameters, short holeIndex, ref bool isValidTemplate) { if (parameters.Length > holeIndex) { diff --git a/src/NLog/MessageTemplates/TemplateEnumerator.cs b/src/NLog/MessageTemplates/TemplateEnumerator.cs index 82318d1f98..9c5d1ccd90 100644 --- a/src/NLog/MessageTemplates/TemplateEnumerator.cs +++ b/src/NLog/MessageTemplates/TemplateEnumerator.cs @@ -31,14 +31,16 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using NLog.Internal; +#nullable enable namespace NLog.MessageTemplates { + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + using NLog.Internal; + /// /// Parse templates. /// @@ -185,7 +187,7 @@ private void ParseHole(CaptureType type) int start = _position; string name = ParseName(out var parameterIndex); int alignment = 0; - string format = null; + string? format = null; if (Peek() != '}') { alignment = Peek() == ',' ? ParseAlignment() : 0; diff --git a/src/NLog/MessageTemplates/TemplateParserException.cs b/src/NLog/MessageTemplates/TemplateParserException.cs index ea3a4108bc..040adc8ab6 100644 --- a/src/NLog/MessageTemplates/TemplateParserException.cs +++ b/src/NLog/MessageTemplates/TemplateParserException.cs @@ -31,10 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; +#nullable enable namespace NLog.MessageTemplates { + using System; + /// /// Error when parsing a template. /// diff --git a/src/NLog/MessageTemplates/ValueFormatter.cs b/src/NLog/MessageTemplates/ValueFormatter.cs index bffde0509f..fb64c3955d 100644 --- a/src/NLog/MessageTemplates/ValueFormatter.cs +++ b/src/NLog/MessageTemplates/ValueFormatter.cs @@ -31,17 +31,19 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.Text; -using JetBrains.Annotations; -using NLog.Config; -using NLog.Internal; +#nullable enable namespace NLog.MessageTemplates { + using System; + using System.Collections; + using System.Collections.Generic; + using System.Globalization; + using System.Text; + using JetBrains.Annotations; + using NLog.Config; + using NLog.Internal; + /// /// Convert, Render or serialize a value, with optionally backwards-compatible with /// @@ -56,7 +58,7 @@ private IJsonConverter JsonConverter { get => _jsonConverter ?? (_jsonConverter = JsonConverterWithSpaces.CreateJsonConverter(_serviceProvider.GetService())); } - private IJsonConverter _jsonConverter; + private IJsonConverter? _jsonConverter; private sealed class JsonConverterWithSpaces : IJsonConverter { @@ -79,7 +81,7 @@ private JsonConverterWithSpaces(Targets.DefaultJsonSerializer jsonConverter) _exceptionSerializerOptions = new Targets.JsonSerializeOptions() { SuppressSpaces = false, SanitizeDictionaryKeys = true }; } - public bool SerializeObject(object value, StringBuilder builder) + public bool SerializeObject(object? value, StringBuilder builder) { if (value is Exception) return _serializer.SerializeObject(value, builder, _exceptionSerializerOptions); @@ -109,7 +111,7 @@ public ValueFormatter([NotNull] IServiceProvider serviceProvider, bool legacyStr /// An object that supplies culture-specific formatting information. /// Output destination. /// Serialize succeeded (true/false) - public bool FormatValue(object value, string format, CaptureType captureType, IFormatProvider formatProvider, StringBuilder builder) + public bool FormatValue(object? value, string? format, CaptureType captureType, IFormatProvider? formatProvider, StringBuilder builder) { switch (captureType) { @@ -139,15 +141,14 @@ public bool FormatValue(object value, string format, CaptureType captureType, IF /// /// /// - public bool FormatObject(object value, string format, IFormatProvider formatProvider, StringBuilder builder) + public bool FormatObject(object? value, string? format, IFormatProvider? formatProvider, StringBuilder builder) { if (SerializeSimpleObject(value, format, formatProvider, builder, false)) { return true; } - IEnumerable collection = value as IEnumerable; - if (collection != null) + if (value is IEnumerable collection) { return SerializeWithoutCyclicLoop(collection, format, formatProvider, builder, default(SingleItemOptimizedHashSet), 0); } @@ -159,7 +160,7 @@ public bool FormatObject(object value, string format, IFormatProvider formatProv /// /// Try serializing a scalar (string, int, NULL) or simple type (IFormattable) /// - private bool SerializeSimpleObject(object value, string format, IFormatProvider formatProvider, StringBuilder builder, bool convertToString = true) + private bool SerializeSimpleObject(object? value, string? format, IFormatProvider? formatProvider, StringBuilder builder, bool convertToString = true) { if (value is string stringValue) { @@ -197,7 +198,7 @@ private bool SerializeSimpleObject(object value, string format, IFormatProvider } } - private void SerializeConvertibleObject(IConvertible value, string format, IFormatProvider formatProvider, StringBuilder builder) + private void SerializeConvertibleObject(IConvertible value, string? format, IFormatProvider? formatProvider, StringBuilder builder) { TypeCode convertibleTypeCode = value.GetTypeCode(); if (convertibleTypeCode == TypeCode.String) @@ -255,7 +256,7 @@ private void SerializeConvertibleObject(IConvertible value, string format, IForm } } - private static void SerializeConvertToString(object value, IFormatProvider formatProvider, StringBuilder builder) + private static void SerializeConvertToString(object? value, IFormatProvider? formatProvider, StringBuilder builder) { #if !NETFRAMEWORK if (value is IFormattable) @@ -265,7 +266,7 @@ private static void SerializeConvertToString(object value, IFormatProvider forma builder.Append(Convert.ToString(value, formatProvider)); } - private void SerializeStringObject(string stringValue, string format, StringBuilder builder) + private void SerializeStringObject(string stringValue, string? format, StringBuilder builder) { bool includeQuotes = _legacyStringQuotes && format != LiteralFormatSymbol; if (includeQuotes) builder.Append('"'); @@ -283,20 +284,16 @@ private void AppendEnumAsString(StringBuilder sb, Enum value) sb.Append(textValue); } - private bool SerializeWithoutCyclicLoop(IEnumerable collection, string format, IFormatProvider formatProvider, StringBuilder builder, + private bool SerializeWithoutCyclicLoop(IEnumerable collection, string? format, IFormatProvider? formatProvider, StringBuilder builder, SingleItemOptimizedHashSet objectsInPath, int depth) { if (objectsInPath.Contains(collection)) - { return false; // detected reference loop, skip serialization - } + if (depth > MaxRecursionDepth) - { return false; // reached maximum recursion level, no further serialization - } - IDictionary dictionary = collection as IDictionary; - if (dictionary != null) + if (collection is IDictionary dictionary) { using (new SingleItemOptimizedHashSet.SingleItemScopedInsert(dictionary, ref objectsInPath, true, _referenceEqualsComparer)) { @@ -323,7 +320,7 @@ private bool SerializeWithoutCyclicLoop(IEnumerable collection, string format, I /// /// /// - private bool SerializeDictionaryObject(IDictionary dictionary, string format, IFormatProvider formatProvider, StringBuilder builder, SingleItemOptimizedHashSet objectsInPath, int depth) + private bool SerializeDictionaryObject(IDictionary dictionary, string? format, IFormatProvider? formatProvider, StringBuilder builder, SingleItemOptimizedHashSet objectsInPath, int depth) { bool separator = false; foreach (var item in new DictionaryEntryEnumerable(dictionary)) @@ -341,7 +338,7 @@ private bool SerializeDictionaryObject(IDictionary dictionary, string format, IF return true; } - private bool SerializeCollectionObject(IEnumerable collection, string format, IFormatProvider formatProvider, StringBuilder builder, SingleItemOptimizedHashSet objectsInPath, int depth) + private bool SerializeCollectionObject(IEnumerable collection, string? format, IFormatProvider? formatProvider, StringBuilder builder, SingleItemOptimizedHashSet objectsInPath, int depth) { bool separator = false; foreach (var item in collection) @@ -358,7 +355,7 @@ private bool SerializeCollectionObject(IEnumerable collection, string format, IF return true; } - private void SerializeCollectionItem(object item, string format, IFormatProvider formatProvider, StringBuilder builder, ref SingleItemOptimizedHashSet objectsInPath, int depth) + private void SerializeCollectionItem(object item, string? format, IFormatProvider? formatProvider, StringBuilder builder, ref SingleItemOptimizedHashSet objectsInPath, int depth) { if (item is IConvertible convertible) SerializeConvertibleObject(convertible, format, formatProvider, builder); @@ -375,7 +372,7 @@ private void SerializeCollectionItem(object item, string format, IFormatProvider /// Format sting for the value. /// Format provider for the value. /// Append to this - public static void FormatToString(object value, string format, IFormatProvider formatProvider, StringBuilder builder) + public static void FormatToString(object? value, string? format, IFormatProvider? formatProvider, StringBuilder builder) { var stringValue = value as string; if (stringValue != null) diff --git a/src/NLog/Targets/DefaultJsonSerializer.cs b/src/NLog/Targets/DefaultJsonSerializer.cs index b780d794df..a1f268bf4b 100644 --- a/src/NLog/Targets/DefaultJsonSerializer.cs +++ b/src/NLog/Targets/DefaultJsonSerializer.cs @@ -31,16 +31,18 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Globalization; -using System.Text; -using NLog.Internal; +#nullable enable namespace NLog.Targets { + using System; + using System.Collections; + using System.Collections.Generic; + using System.ComponentModel; + using System.Globalization; + using System.Text; + using NLog.Internal; + /// /// Default class for serialization of values to JSON format. /// @@ -61,7 +63,8 @@ public class DefaultJsonSerializer : IJsonConverter /// [Obsolete("Instead use ResolveService() in Layout / Target. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] - public static DefaultJsonSerializer Instance { get; } = new DefaultJsonSerializer(null); + public static DefaultJsonSerializer Instance => _instance ?? (_instance = new DefaultJsonSerializer(LogManager.LogFactory.ServiceRepository)); + private static DefaultJsonSerializer? _instance; /// /// Private. Use @@ -109,39 +112,38 @@ public string SerializeObject(object value, JsonSerializeOptions options) } return QuoteValue(str); } - else + else { - IConvertible convertibleValue = value as IConvertible; - TypeCode objTypeCode = convertibleValue?.GetTypeCode() ?? TypeCode.Object; - if (objTypeCode != TypeCode.Object && objTypeCode != TypeCode.Char) + if (value is IConvertible convertibleValue) { - Enum enumValue; - if (!options.EnumAsInteger && IsNumericTypeCode(objTypeCode, false) && (enumValue = value as Enum) != null) - { - return QuoteValue(EnumAsString(enumValue)); - } - else + TypeCode objTypeCode = convertibleValue.GetTypeCode(); + if (objTypeCode != TypeCode.Object) { - string xmlStr = XmlHelper.XmlConvertToString(convertibleValue, objTypeCode); - if (SkipQuotes(convertibleValue, objTypeCode)) + if (!options.EnumAsInteger && IsNumericTypeCode(objTypeCode, false) && convertibleValue is Enum enumValue) { - return xmlStr; + return QuoteValue(EnumAsString(enumValue)); } else { - return QuoteValue(xmlStr); + string xmlStr = XmlHelper.XmlConvertToString(convertibleValue, objTypeCode); + if (SkipQuotes(convertibleValue, objTypeCode)) + { + return xmlStr; + } + else + { + return QuoteValue(xmlStr); + } } } } - else + + StringBuilder sb = new StringBuilder(); + if (!SerializeObject(value, sb, options)) { - StringBuilder sb = new StringBuilder(); - if (!SerializeObject(value, sb, options)) - { - return null; - } - return sb.ToString(); + return string.Empty; } + return sb.ToString(); } } @@ -151,7 +153,7 @@ public string SerializeObject(object value, JsonSerializeOptions options) /// The object to serialize to JSON. /// Write the resulting JSON to this destination. /// Object serialized successfully (true/false). - public bool SerializeObject(object value, StringBuilder destination) + public bool SerializeObject(object? value, StringBuilder destination) { return SerializeObject(value, destination, DefaultSerializerOptions); } @@ -163,7 +165,7 @@ public bool SerializeObject(object value, StringBuilder destination) /// Write the resulting JSON to this destination. /// serialization options /// Object serialized successfully (true/false). - public bool SerializeObject(object value, StringBuilder destination, JsonSerializeOptions options) + public bool SerializeObject(object? value, StringBuilder destination, JsonSerializeOptions options) { return SerializeObject(value, destination, options, default(SingleItemOptimizedHashSet), 0); } @@ -177,7 +179,7 @@ public bool SerializeObject(object value, StringBuilder destination, JsonSeriali /// The objects in path (Avoid cyclic reference loop). /// The current depth (level) of recursion. /// Object serialized successfully (true/false). - private bool SerializeObject(object value, StringBuilder destination, JsonSerializeOptions options, SingleItemOptimizedHashSet objectsInPath, int depth) + private bool SerializeObject(object? value, StringBuilder destination, JsonSerializeOptions options, SingleItemOptimizedHashSet objectsInPath, int depth) { int originalLength = destination.Length; @@ -197,7 +199,7 @@ private bool SerializeObject(object value, StringBuilder destination, JsonSerial } } - private bool SerializeObjectWithReflection(object value, StringBuilder destination, JsonSerializeOptions options, ref SingleItemOptimizedHashSet objectsInPath, int depth) + private bool SerializeObjectWithReflection(object? value, StringBuilder destination, JsonSerializeOptions options, ref SingleItemOptimizedHashSet objectsInPath, int depth) { int originalLength = destination.Length; if (originalLength > MaxJsonLength) @@ -205,7 +207,7 @@ private bool SerializeObjectWithReflection(object value, StringBuilder destinati return false; } - if (objectsInPath.Contains(value)) + if (value is null || objectsInPath.Contains(value)) { return false; } @@ -241,14 +243,16 @@ private bool SerializeObjectWithReflection(object value, StringBuilder destinati } } - private bool SerializeSimpleObjectValue(object value, StringBuilder destination, JsonSerializeOptions options, bool forceToString = false) + private bool SerializeSimpleObjectValue(object? value, StringBuilder destination, JsonSerializeOptions options, bool forceToString = false) { - var convertibleValue = value as IConvertible; - var objTypeCode = convertibleValue?.GetTypeCode() ?? (value is null ? TypeCode.Empty : TypeCode.Object); - if (objTypeCode != TypeCode.Object) + if (value is IConvertible convertibleValue) { - SerializeSimpleTypeCodeValue(convertibleValue, objTypeCode, destination, options, forceToString); - return true; + var objTypeCode = convertibleValue.GetTypeCode(); + if (objTypeCode != TypeCode.Object) + { + SerializeSimpleTypeCodeValue(convertibleValue, objTypeCode, destination, options, forceToString); + return true; + } } if (value is IFormattable formattable) @@ -259,7 +263,6 @@ private bool SerializeSimpleObjectValue(object value, StringBuilder destination, return true; } - destination.Append('"'); #if NETFRAMEWORK var str = formattable.ToString(null, CultureInfo.InvariantCulture); @@ -273,6 +276,12 @@ private bool SerializeSimpleObjectValue(object value, StringBuilder destination, return true; } + if (value is null) + { + SerializeSimpleTypeCodeValue(null, TypeCode.Empty, destination, options, forceToString); + return true; + } + return false; // Not simple } @@ -425,7 +434,7 @@ private bool SerializeObjectPropertyList(object value, ref ObjectReflectionCache return SerializeObjectAsString(value, destination, options); } - private void SerializeSimpleTypeCodeValue(IConvertible value, TypeCode objTypeCode, StringBuilder destination, JsonSerializeOptions options, bool forceToString = false) + private void SerializeSimpleTypeCodeValue(IConvertible? value, TypeCode objTypeCode, StringBuilder destination, JsonSerializeOptions options, bool forceToString = false) { if (objTypeCode == TypeCode.Empty || value is null) { @@ -503,13 +512,12 @@ private static void QuoteValue(StringBuilder destination, string value) private string EnumAsString(Enum value) { - string textValue; - if (!_enumCache.TryGetValue(value, out textValue)) + if (!_enumCache.TryGetValue(value, out var textValue)) { textValue = Convert.ToString(value, CultureInfo.InvariantCulture); _enumCache.TryAddValue(value, textValue); } - return textValue; + return textValue ?? string.Empty; } /// @@ -686,7 +694,7 @@ private bool SerializeObjectProperties(ObjectReflectionCache.ObjectPropertyList { destination.Append('{'); - string jsonPropertyDelimeter = null; + string? jsonPropertyDelimeter = null; foreach (var propertyValue in objectPropertyList) { @@ -705,7 +713,7 @@ private bool SerializeObjectProperties(ObjectReflectionCache.ObjectPropertyList var objTypeCode = propertyValue.TypeCode; if (objTypeCode != TypeCode.Object) { - SerializeSimpleTypeCodeValue((IConvertible)propertyValue.Value, objTypeCode, destination, options); + SerializeSimpleTypeCodeValue((IConvertible?)propertyValue.Value, objTypeCode, destination, options); } else { diff --git a/src/NLog/Targets/Target.cs b/src/NLog/Targets/Target.cs index c58e331a61..078e3f6b6f 100644 --- a/src/NLog/Targets/Target.cs +++ b/src/NLog/Targets/Target.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets { using System; @@ -50,7 +52,7 @@ namespace NLog.Targets [NLogConfigurationItem] public abstract class Target : ISupportsInitialize, IInternalLoggerContext, IDisposable { - internal string _tostring; + internal string? _tostring; private Layout[] _allLayouts = ArrayHelper.Empty(); @@ -58,14 +60,14 @@ public abstract class Target : ISupportsInitialize, IInternalLoggerContext, IDis private bool _allLayoutsAreThreadAgnostic; private bool _anyLayoutsAreThreadAgnosticImmutable; private bool _scannedForLayouts; - private Exception _initializeException; + private Exception? _initializeException; /// /// The Max StackTraceUsage of all the in this Target /// internal StackTraceUsage StackTraceUsage { get; private set; } - internal Exception InitializeException => _initializeException; + internal Exception? InitializeException => _initializeException; /// /// Gets or sets the name of the target. @@ -80,7 +82,7 @@ public string Name _tostring = null; } } - private string _name; + private string _name = string.Empty; /// /// Target supports reuse of internal buffers, and doesn't have to constantly allocate new buffers @@ -115,9 +117,9 @@ public string Name /// /// Gets the logging configuration this target is part of. /// - protected LoggingConfiguration LoggingConfiguration { get; private set; } + protected LoggingConfiguration? LoggingConfiguration { get; private set; } - LogFactory IInternalLoggerContext.LogFactory => LoggingConfiguration?.LogFactory; + LogFactory? IInternalLoggerContext.LogFactory => LoggingConfiguration?.LogFactory; /// /// Gets a value indicating whether the target has been initialized. @@ -139,13 +141,13 @@ protected bool IsInitialized private volatile bool _isInitialized; internal readonly ReusableBuilderCreator ReusableLayoutBuilder = new ReusableBuilderCreator(); - private StringBuilderPool _precalculateStringBuilderPool; + private StringBuilderPool? _precalculateStringBuilderPool; /// /// Initializes this instance. /// /// The configuration. - void ISupportsInitialize.Initialize(LoggingConfiguration configuration) + void ISupportsInitialize.Initialize(LoggingConfiguration? configuration) { lock (SyncRoot) { @@ -285,7 +287,7 @@ public override string ToString() return _tostring ?? (_tostring = GenerateTargetToString(false)); } - internal string GenerateTargetToString(bool targetWrapper, string targetName = null) + internal string GenerateTargetToString(bool targetWrapper, string? targetName = null) { var targetAttribute = GetType().GetFirstCustomAttribute(); string targetType = (targetAttribute?.Name ?? GetType().Name).Trim(); @@ -431,7 +433,7 @@ protected virtual void WriteFailedNotInitialized(AsyncLogEventInfo logEvent, Exc /// Initializes this instance. /// /// The configuration. - internal void Initialize(LoggingConfiguration configuration) + internal void Initialize(LoggingConfiguration? configuration) { lock (SyncRoot) { @@ -701,7 +703,7 @@ protected void MergeEventProperties(LogEventInfo logEvent) protected string RenderLogEvent([CanBeNull] Layout layout, [CanBeNull] LogEventInfo logEvent) { if (layout is null || logEvent is null) - return null; // Signal that input was wrong + return string.Empty; // Signal that input was wrong if (layout is IStringValueRenderer stringLayout) { @@ -729,7 +731,7 @@ protected string RenderLogEvent([CanBeNull] Layout layout, [CanBeNull] LogEventI /// The logevent info. /// Fallback value when no value available /// Result value when available, else fallback to defaultValue - protected T RenderLogEvent([CanBeNull] Layout layout, [CanBeNull] LogEventInfo logEvent, T defaultValue = default(T)) + protected T? RenderLogEvent([CanBeNull] Layout? layout, [CanBeNull] LogEventInfo? logEvent, T? defaultValue = default(T)) { if (layout is null) return defaultValue; @@ -769,20 +771,20 @@ internal bool ExceptionMustBeRethrown(Exception exception, #if !NET35 [System.Runtime.CompilerServices.CallerMemberName] #endif - string callerMemberName = null) + string? callerMemberName = null) { return exception.MustBeRethrown(this, callerMemberName); } - private static bool TryGetCachedValue(Layout layout, LogEventInfo logEvent, out object value) + private static bool TryGetCachedValue(Layout layout, LogEventInfo logEvent, out object? value) { - if ((!layout.ThreadAgnostic || layout.ThreadAgnosticImmutable) && logEvent.TryGetCachedLayoutValue(layout, out value)) + if (layout.ThreadAgnostic && !layout.ThreadAgnosticImmutable) { - return true; + value = null; + return false; } - value = null; - return false; + return logEvent.TryGetCachedLayoutValue(layout, out value); } /// diff --git a/src/NLog/Targets/TargetAttribute.cs b/src/NLog/Targets/TargetAttribute.cs index 6dda7c6ae8..7a99cdb598 100644 --- a/src/NLog/Targets/TargetAttribute.cs +++ b/src/NLog/Targets/TargetAttribute.cs @@ -31,10 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets { using System; - using Config; + using NLog.Config; /// /// Marks class as logging target and attaches a type-alias name for use in NLog configuration. diff --git a/src/NLog/Targets/TargetPropertyWithContext.cs b/src/NLog/Targets/TargetPropertyWithContext.cs index 6952a2d3c2..aa79502c46 100644 --- a/src/NLog/Targets/TargetPropertyWithContext.cs +++ b/src/NLog/Targets/TargetPropertyWithContext.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets { using System; @@ -48,7 +50,7 @@ public class TargetPropertyWithContext /// /// Initializes a new instance of the class. /// - public TargetPropertyWithContext() : this(null, null) { } + public TargetPropertyWithContext() : this(string.Empty, Layout.Empty) { } /// /// Initializes a new instance of the class. @@ -84,7 +86,7 @@ public TargetPropertyWithContext(string name, Layout layout) /// Gets or sets the fallback value when result value is not available /// /// - public Layout DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } + public Layout? DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } /// /// Gets or sets when an empty value should cause the property to be included @@ -106,6 +108,6 @@ public bool IncludeEmptyValue /// /// Log event for rendering /// Result value when available, else fallback to defaultValue - public object RenderValue(LogEventInfo logEvent) => _layoutInfo.RenderValue(logEvent); + public object? RenderValue(LogEventInfo logEvent) => _layoutInfo.RenderValue(logEvent); } } diff --git a/src/NLog/Targets/TargetWithContext.cs b/src/NLog/Targets/TargetWithContext.cs index 744482bc68..df1aab1951 100644 --- a/src/NLog/Targets/TargetWithContext.cs +++ b/src/NLog/Targets/TargetWithContext.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets { using System; @@ -219,7 +221,7 @@ protected bool ShouldIncludeProperties(LogEventInfo logEvent) /// /// /// Dictionary with any context properties for the logEvent (Null if none found) - protected IDictionary GetContextProperties(LogEventInfo logEvent) + protected IDictionary? GetContextProperties(LogEventInfo logEvent) { return GetContextProperties(logEvent, null); } @@ -228,9 +230,9 @@ protected IDictionary GetContextProperties(LogEventInfo logEvent /// Checks if any context properties, and if any returns them as a single dictionary /// /// - /// Optional prefilled dictionary + /// Optional pre-allocated dictionary for the snapshot /// Dictionary with any context properties for the logEvent (Null if none found) - protected IDictionary GetContextProperties(LogEventInfo logEvent, IDictionary combinedProperties) + protected IDictionary? GetContextProperties(LogEventInfo logEvent, IDictionary? combinedProperties) { if (ContextProperties?.Count > 0) { @@ -255,7 +257,7 @@ protected IDictionary GetContextProperties(LogEventInfo logEvent /// /// /// Dictionary with all collected properties for logEvent - protected IDictionary GetAllProperties(LogEventInfo logEvent) + protected IDictionary GetAllProperties(LogEventInfo logEvent) { return GetAllProperties(logEvent, null); } @@ -266,15 +268,16 @@ protected IDictionary GetAllProperties(LogEventInfo logEvent) /// /// Optional prefilled dictionary /// Dictionary with all collected properties for logEvent - protected IDictionary GetAllProperties(LogEventInfo logEvent, IDictionary combinedProperties) + protected IDictionary GetAllProperties(LogEventInfo logEvent, IDictionary? combinedProperties) { if (IncludeEventProperties && logEvent.HasProperties) { // TODO Make Dictionary-Lazy-adapter for PropertiesDictionary to skip extra Dictionary-allocation combinedProperties = combinedProperties ?? CreateNewDictionary(logEvent.Properties.Count + (ContextProperties?.Count ?? 0)); bool checkForDuplicates = combinedProperties.Count > 0; - bool checkExcludeProperties = ExcludeProperties?.Count > 0; - using (var propertyEnumerator = logEvent.TryCreatePropertiesInternal().GetPropertyEnumerator()) + + var excludeProperties = (ExcludeProperties is null || ExcludeProperties.Count == 0) ? null : ExcludeProperties; + using (var propertyEnumerator = logEvent.CreatePropertiesInternal().GetPropertyEnumerator()) { while (propertyEnumerator.MoveNext()) { @@ -282,7 +285,7 @@ protected IDictionary GetAllProperties(LogEventInfo logEvent, ID if (string.IsNullOrEmpty(property.Key)) continue; - if (checkExcludeProperties && ExcludeProperties.Contains(property.Key)) + if (excludeProperties?.Contains(property.Key) == true) continue; AddContextProperty(logEvent, property.Key, property.Value, checkForDuplicates, combinedProperties); @@ -293,9 +296,9 @@ protected IDictionary GetAllProperties(LogEventInfo logEvent, ID return combinedProperties ?? CreateNewDictionary(0); } - private static IDictionary CreateNewDictionary(int initialCapacity) + private static IDictionary CreateNewDictionary(int initialCapacity) { - return new Dictionary(initialCapacity < 3 ? 0 : initialCapacity, StringComparer.Ordinal); + return new Dictionary(initialCapacity < 3 ? 0 : initialCapacity, StringComparer.Ordinal); } /// @@ -306,19 +309,19 @@ private static IDictionary CreateNewDictionary(int initialCapaci /// Item Value /// Dictionary of context values /// New (unique) value (or null to skip value). If the same value is used then the item will be overwritten - protected virtual string GenerateUniqueItemName(LogEventInfo logEvent, string itemName, object itemValue, IDictionary combinedProperties) + protected virtual string GenerateUniqueItemName(LogEventInfo logEvent, string itemName, object? itemValue, IDictionary combinedProperties) { return PropertiesDictionary.GenerateUniquePropertyName(itemName, combinedProperties, (newKey, props) => props.ContainsKey(newKey)); } - private bool CombineProperties(LogEventInfo logEvent, Layout contextLayout, ref IDictionary combinedProperties) + private bool CombineProperties(LogEventInfo logEvent, Layout contextLayout, ref IDictionary? combinedProperties) { - if (!logEvent.TryGetCachedLayoutValue(contextLayout, out object value)) + if (!logEvent.TryGetCachedLayoutValue(contextLayout, out var value)) { return false; } - if (value is IDictionary contextProperties) + if (value is IDictionary contextProperties) { if (combinedProperties != null) { @@ -336,13 +339,11 @@ private bool CombineProperties(LogEventInfo logEvent, Layout contextLayout, ref return true; } - private void AddContextProperty(LogEventInfo logEvent, string propertyName, object propertyValue, bool checkForDuplicates, IDictionary combinedProperties) + private void AddContextProperty(LogEventInfo logEvent, string propertyName, object? propertyValue, bool checkForDuplicates, IDictionary combinedProperties) { if (checkForDuplicates && combinedProperties.ContainsKey(propertyName)) { propertyName = GenerateUniqueItemName(logEvent, propertyName, propertyValue, combinedProperties); - if (propertyName is null) - return; } combinedProperties[propertyName] = propertyValue; @@ -353,11 +354,11 @@ private void AddContextProperty(LogEventInfo logEvent, string propertyName, obje /// /// /// Dictionary with ScopeContext properties if any, else null - protected IDictionary GetScopeContextProperties(LogEventInfo logEvent) + protected IDictionary? GetScopeContextProperties(LogEventInfo logEvent) { - if (logEvent.TryGetCachedLayoutValue(_contextLayout.ScopeContextPropertiesLayout, out object value)) + if (logEvent.TryGetCachedLayoutValue(_contextLayout.ScopeContextPropertiesLayout, out var value)) { - return value as IDictionary; + return value as IDictionary; } return CaptureScopeContextProperties(logEvent, null); } @@ -367,22 +368,22 @@ protected IDictionary GetScopeContextProperties(LogEventInfo log /// /// /// Collection of nested state objects if any, else null - protected IList GetScopeContextNested(LogEventInfo logEvent) + protected IList? GetScopeContextNested(LogEventInfo logEvent) { - if (logEvent.TryGetCachedLayoutValue(_contextLayout.ScopeContextNestedStatesLayout, out object value)) + if (logEvent.TryGetCachedLayoutValue(_contextLayout.ScopeContextNestedStatesLayout, out var value)) { return value as IList; } return CaptureScopeContextNested(logEvent); } - private IDictionary CaptureContextProperties(LogEventInfo logEvent, IDictionary combinedProperties) + private IDictionary? CaptureContextProperties(LogEventInfo logEvent, IDictionary? combinedProperties) { combinedProperties = combinedProperties ?? CreateNewDictionary(ContextProperties.Count); for (int i = 0; i < ContextProperties.Count; ++i) { var contextProperty = ContextProperties[i]; - if (string.IsNullOrEmpty(contextProperty?.Name)) + if (contextProperty is null || string.IsNullOrEmpty(contextProperty.Name)) continue; try @@ -404,7 +405,7 @@ private IDictionary CaptureContextProperties(LogEventInfo logEve return combinedProperties; } - private static bool TryGetContextPropertyValue(LogEventInfo logEvent, TargetPropertyWithContext contextProperty, out object propertyValue) + private static bool TryGetContextPropertyValue(LogEventInfo logEvent, TargetPropertyWithContext contextProperty, out object? propertyValue) { propertyValue = contextProperty.RenderValue(logEvent); if (!contextProperty.IncludeEmptyValue && StringHelpers.IsNullOrEmptyString(propertyValue)) @@ -421,7 +422,7 @@ private static bool TryGetContextPropertyValue(LogEventInfo logEvent, TargetProp /// /// Optional pre-allocated dictionary for the snapshot /// Dictionary with GDC context if any, else null - protected virtual IDictionary CaptureContextGdc(LogEventInfo logEvent, IDictionary contextProperties) + protected virtual IDictionary? CaptureContextGdc(LogEventInfo logEvent, IDictionary? contextProperties) { var globalNames = GlobalDiagnosticsContext.GetNames(); if (globalNames.Count == 0) @@ -429,13 +430,13 @@ protected virtual IDictionary CaptureContextGdc(LogEventInfo log contextProperties = contextProperties ?? CreateNewDictionary(globalNames.Count); bool checkForDuplicates = contextProperties.Count > 0; - bool checkExcludeProperties = ExcludeProperties?.Count > 0; + var excludeProperties = (ExcludeProperties is null || ExcludeProperties.Count == 0) ? null : ExcludeProperties; foreach (string propertyName in globalNames) { if (string.IsNullOrEmpty(propertyName)) continue; - if (checkExcludeProperties && ExcludeProperties.Contains(propertyName)) + if (excludeProperties?.Contains(propertyName) == true) continue; var propertyValue = GlobalDiagnosticsContext.GetObject(propertyName); @@ -454,12 +455,12 @@ protected virtual IDictionary CaptureContextGdc(LogEventInfo log /// /// Optional pre-allocated dictionary for the snapshot /// Dictionary with ScopeContext properties if any, else null - protected virtual IDictionary CaptureScopeContextProperties(LogEventInfo logEvent, IDictionary contextProperties) + protected virtual IDictionary? CaptureScopeContextProperties(LogEventInfo logEvent, IDictionary? contextProperties) { using (var scopeEnumerator = ScopeContext.GetAllPropertiesEnumerator()) { bool checkForDuplicates = contextProperties?.Count > 0; - bool checkExcludeProperties = ExcludeProperties?.Count > 0; + var excludeProperties = (ExcludeProperties is null || ExcludeProperties.Count == 0) ? null : ExcludeProperties; while (scopeEnumerator.MoveNext()) { var scopeProperty = scopeEnumerator.Current; @@ -467,12 +468,12 @@ protected virtual IDictionary CaptureScopeContextProperties(LogE if (string.IsNullOrEmpty(propertyName)) continue; - if (checkExcludeProperties && ExcludeProperties.Contains(propertyName)) + if (excludeProperties?.Contains(propertyName) == true) continue; contextProperties = contextProperties ?? CreateNewDictionary(0); - object propertyValue = scopeProperty.Value; + var propertyValue = scopeProperty.Value; if (SerializeScopeContextProperty(logEvent, propertyName, propertyValue, out var serializedValue)) { AddContextProperty(logEvent, propertyName, serializedValue, checkForDuplicates, contextProperties); @@ -491,7 +492,7 @@ protected virtual IDictionary CaptureScopeContextProperties(LogE /// ScopeContext Dictionary value /// Snapshot of ScopeContext property-value /// Include object value in snapshot - protected virtual bool SerializeScopeContextProperty(LogEventInfo logEvent, string name, object value, out object serializedValue) + protected virtual bool SerializeScopeContextProperty(LogEventInfo logEvent, string name, object? value, out object? serializedValue) { if (string.IsNullOrEmpty(name)) { @@ -513,11 +514,11 @@ protected virtual IList CaptureScopeContextNested(LogEventInfo logEvent) if (stack.Count == 0) return stack; - IList filteredStack = null; + IList? filteredStack = null; for (int i = 0; i < stack.Count; ++i) { var ndcValue = stack[i]; - if (SerializeScopeContextNestedState(logEvent, ndcValue, out var serializedValue)) + if (SerializeScopeContextNestedState(logEvent, ndcValue, out var serializedValue) && serializedValue != null) { if (filteredStack != null) filteredStack.Add(serializedValue); @@ -544,9 +545,9 @@ protected virtual IList CaptureScopeContextNested(LogEventInfo logEvent) /// nested state value /// Snapshot of stack item value /// Include object value in snapshot - protected virtual bool SerializeScopeContextNestedState(LogEventInfo logEvent, object value, out object serializedValue) + protected virtual bool SerializeScopeContextNestedState(LogEventInfo logEvent, object value, out object? serializedValue) { - return SerializeItemValue(logEvent, null, value, out serializedValue); + return SerializeItemValue(logEvent, string.Empty, value, out serializedValue); } /// @@ -557,7 +558,7 @@ protected virtual bool SerializeScopeContextNestedState(LogEventInfo logEvent, o /// Object Value /// Snapshot of value /// Include object value in snapshot - protected virtual bool SerializeItemValue(LogEventInfo logEvent, string name, object value, out object serializedValue) + protected virtual bool SerializeItemValue(LogEventInfo logEvent, string name, object? value, out object? serializedValue) { if (value is null) { @@ -583,7 +584,7 @@ protected virtual bool SerializeItemValue(LogEventInfo logEvent, string name, ob /// Collection with NDLC context if any, else null [Obsolete("Replaced by GetScopeContextNested. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] - protected IList GetContextNdlc(LogEventInfo logEvent) => GetScopeContextNested(logEvent); + protected IList? GetContextNdlc(LogEventInfo logEvent) => GetScopeContextNested(logEvent); /// /// Returns the captured snapshot of for the @@ -592,7 +593,7 @@ protected virtual bool SerializeItemValue(LogEventInfo logEvent, string name, ob /// Dictionary with MDLC context if any, else null [Obsolete("Replaced by GetScopeContextProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] - protected IDictionary GetContextMdlc(LogEventInfo logEvent) => GetScopeContextProperties(logEvent); + protected IDictionary? GetContextMdlc(LogEventInfo logEvent) => GetScopeContextProperties(logEvent); [ThreadAgnostic] internal sealed class TargetWithContextLayout : Layout, IIncludeContext, IUsesStackTrace, IStringValueRenderer @@ -606,8 +607,8 @@ public Layout TargetLayout _targetStringLayout = _targetLayout as IStringValueRenderer; } } - private Layout _targetLayout; - private IStringValueRenderer _targetStringLayout; + private Layout _targetLayout = Layout.Empty; + private IStringValueRenderer? _targetStringLayout; /// Internal Layout that allows capture of properties-dictionary internal LayoutScopeContextProperties ScopeContextPropertiesLayout { get; } @@ -768,7 +769,7 @@ protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuil TargetLayout?.Render(logEvent, target); } - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) + string? IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) { return _targetStringLayout?.GetFormattedString(logEvent); } diff --git a/src/NLog/Targets/TargetWithLayout.cs b/src/NLog/Targets/TargetWithLayout.cs index 36d32da835..fad88f50e9 100644 --- a/src/NLog/Targets/TargetWithLayout.cs +++ b/src/NLog/Targets/TargetWithLayout.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets { using NLog.Config; diff --git a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs index 7036f5af5a..043a247619 100644 --- a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs +++ b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets { using NLog.Config; @@ -86,7 +88,7 @@ public override Layout Layout /// Gets or sets the footer. /// /// - public Layout Footer + public Layout? Footer { get => LHF.Footer; set => LHF.Footer = value; @@ -96,7 +98,7 @@ public Layout Footer /// Gets or sets the header. /// /// - public Layout Header + public Layout? Header { get => LHF.Header; set => LHF.Header = value; diff --git a/src/NLog/Targets/Wrappers/WrapperTargetBase.cs b/src/NLog/Targets/Wrappers/WrapperTargetBase.cs index 9a1e4ab952..b4fa920504 100644 --- a/src/NLog/Targets/Wrappers/WrapperTargetBase.cs +++ b/src/NLog/Targets/Wrappers/WrapperTargetBase.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using System; @@ -45,7 +47,7 @@ public abstract class WrapperTargetBase : Target /// Gets or sets the target that is wrapped by this target. /// /// - public Target WrappedTarget + public Target? WrappedTarget { get => _wrappedTarget; set @@ -54,7 +56,7 @@ public Target WrappedTarget _tostring = null; } } - private Target _wrappedTarget; + private Target? _wrappedTarget; /// public override string ToString() diff --git a/src/NLog/Time/AccurateLocalTimeSource.cs b/src/NLog/Time/AccurateLocalTimeSource.cs index 20798109e4..babe41ef11 100644 --- a/src/NLog/Time/AccurateLocalTimeSource.cs +++ b/src/NLog/Time/AccurateLocalTimeSource.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Time { using System; diff --git a/src/NLog/Time/AccurateUtcTimeSource.cs b/src/NLog/Time/AccurateUtcTimeSource.cs index 9f45c12cc5..15dede17a6 100644 --- a/src/NLog/Time/AccurateUtcTimeSource.cs +++ b/src/NLog/Time/AccurateUtcTimeSource.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Time { using System; diff --git a/src/NLog/Time/CachedTimeSource.cs b/src/NLog/Time/CachedTimeSource.cs index 04156d176e..6c75e05db1 100644 --- a/src/NLog/Time/CachedTimeSource.cs +++ b/src/NLog/Time/CachedTimeSource.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Time { using System; diff --git a/src/NLog/Time/FastLocalTimeSource.cs b/src/NLog/Time/FastLocalTimeSource.cs index 07bfc29f1f..5122fd04e7 100644 --- a/src/NLog/Time/FastLocalTimeSource.cs +++ b/src/NLog/Time/FastLocalTimeSource.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Time { using System; diff --git a/src/NLog/Time/FastUtcTimeSource.cs b/src/NLog/Time/FastUtcTimeSource.cs index d3bd786d0e..986e6d3c17 100644 --- a/src/NLog/Time/FastUtcTimeSource.cs +++ b/src/NLog/Time/FastUtcTimeSource.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Time { using System; diff --git a/src/NLog/Time/TimeSource.cs b/src/NLog/Time/TimeSource.cs index de54719fbc..167ba3d9f3 100644 --- a/src/NLog/Time/TimeSource.cs +++ b/src/NLog/Time/TimeSource.cs @@ -31,11 +31,13 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Time { using System; - using Config; - using Internal; + using NLog.Config; + using NLog.Internal; /// /// Defines source of current time. diff --git a/src/NLog/Time/TimeSourceAttribute.cs b/src/NLog/Time/TimeSourceAttribute.cs index ca9d19ffda..95999cb529 100644 --- a/src/NLog/Time/TimeSourceAttribute.cs +++ b/src/NLog/Time/TimeSourceAttribute.cs @@ -31,10 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Time { using System; - using Config; + using NLog.Config; /// /// Marks class as a time source and assigns a name to it. diff --git a/tests/NLog.UnitTests/Config/TargetConfigurationTests.cs b/tests/NLog.UnitTests/Config/TargetConfigurationTests.cs index db3cb92f55..4cc3d7025b 100644 --- a/tests/NLog.UnitTests/Config/TargetConfigurationTests.cs +++ b/tests/NLog.UnitTests/Config/TargetConfigurationTests.cs @@ -498,7 +498,7 @@ public void DefaultWrapperTest() var retryingTargetWrapper = bufferingTargetWrapper.WrappedTarget as RetryingTargetWrapper; Assert.NotNull(retryingTargetWrapper); - Assert.Null(retryingTargetWrapper.Name); + Assert.Empty(retryingTargetWrapper.Name); var debugTarget = retryingTargetWrapper.WrappedTarget as DebugTarget; Assert.NotNull(debugTarget); diff --git a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs index 69aa158826..e5b485e550 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs @@ -73,10 +73,10 @@ public void WhenEmpty_MissingInner_ShouldNotThrow() { using (new NoThrowNLogExceptions()) { - SimpleLayout l = @"${whenEmpty:whenEmpty=${literal:text=c:\logs\}:inner=${environment:LOG_DIR_XXX}}api.log"; + SimpleLayout l = @"${whenEmpty:whenEmpty=${literal:text=c:\logs\}:inner=${environment:LOG_DIR_XXX}}_api.log"; var le = LogEventInfo.Create(LogLevel.Info, "logger", "message"); LogManager.ThrowExceptions = true; - Assert.Equal("api.log", l.Render(le)); + Assert.Equal("LOG_DIR_XXX_api.log", l.Render(le)); } } From 6f04acbd0cfd4c7f56cffbafed1701f82d96b310 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 18 May 2025 16:20:59 +0200 Subject: [PATCH 117/224] NLog Target Wrapper with nullable references (#5820) --- src/NLog/Filters/ConditionBasedFilter.cs | 2 + src/NLog/Layouts/Typed/Layout.cs | 4 +- src/NLog/Targets/AsyncTaskTarget.cs | 4 +- .../Targets/Wrappers/AsyncRequestQueue-T.cs | 14 +++--- .../Targets/Wrappers/AsyncRequestQueueBase.cs | 15 +++--- .../Targets/Wrappers/AsyncTargetWrapper.cs | 47 +++++++++---------- .../Wrappers/AutoFlushTargetWrapper.cs | 28 ++++++----- .../Wrappers/BufferingTargetWrapper.cs | 19 ++++---- .../Targets/Wrappers/CompoundTargetBase.cs | 2 + .../Wrappers/ConcurrentRequestQueue.cs | 20 ++++---- .../Targets/Wrappers/FallbackGroupTarget.cs | 6 ++- src/NLog/Targets/Wrappers/FilteringRule.cs | 6 ++- .../Wrappers/FilteringTargetWrapper.cs | 24 +++++----- .../Targets/Wrappers/GroupByTargetWrapper.cs | 22 ++++----- .../Targets/Wrappers/LimitingTargetWrapper.cs | 11 +++-- .../Wrappers/LogEventDroppedEventArgs.cs | 4 +- .../Wrappers/LogEventQueueGrowEventArgs.cs | 2 + .../Wrappers/PostFilteringTargetWrapper.cs | 25 +++++----- .../Targets/Wrappers/RandomizeGroupTarget.cs | 2 + .../Wrappers/RepeatingTargetWrapper.cs | 8 ++-- .../Targets/Wrappers/RetryingTargetWrapper.cs | 15 +++--- .../Targets/Wrappers/RoundRobinGroupTarget.cs | 2 + src/NLog/Targets/Wrappers/SplitGroupTarget.cs | 2 + tests/NLog.UnitTests/ApiTests.cs | 1 + .../Layouts/LayoutTypedTests.cs | 2 +- tests/NLog.UnitTests/Targets/TargetTests.cs | 7 ++- .../Wrappers/AsyncRequestQueueTests.cs | 12 ++--- .../Wrappers/ConcurrentRequestQueueTests.cs | 2 +- 28 files changed, 171 insertions(+), 137 deletions(-) diff --git a/src/NLog/Filters/ConditionBasedFilter.cs b/src/NLog/Filters/ConditionBasedFilter.cs index 6369107b17..92f83a9013 100644 --- a/src/NLog/Filters/ConditionBasedFilter.cs +++ b/src/NLog/Filters/ConditionBasedFilter.cs @@ -47,6 +47,8 @@ namespace NLog.Filters [Filter("when")] public class ConditionBasedFilter : Filter { + internal static readonly ConditionBasedFilter Empty = new ConditionBasedFilter(); + /// /// Gets or sets the condition expression. /// diff --git a/src/NLog/Layouts/Typed/Layout.cs b/src/NLog/Layouts/Typed/Layout.cs index 0cc2b3aa92..16040ba95b 100644 --- a/src/NLog/Layouts/Typed/Layout.cs +++ b/src/NLog/Layouts/Typed/Layout.cs @@ -420,10 +420,8 @@ public override int GetHashCode() /// Converts a given value to a . /// /// Text to be converted. - public static implicit operator Layout?(T value) + public static implicit operator Layout(T value) { - if (object.Equals(value, default(T)) && !typeof(T).IsValueType) return null; - return new Layout(value); } diff --git a/src/NLog/Targets/AsyncTaskTarget.cs b/src/NLog/Targets/AsyncTaskTarget.cs index e3de69da52..9e6768e400 100644 --- a/src/NLog/Targets/AsyncTaskTarget.cs +++ b/src/NLog/Targets/AsyncTaskTarget.cs @@ -138,8 +138,8 @@ public AsyncTargetWrapperOverflowAction OverflowAction /// public int QueueLimit { - get => _requestQueue.RequestLimit; - set => _requestQueue.RequestLimit = value; + get => _requestQueue.QueueLimit; + set => _requestQueue.QueueLimit = value; } /// diff --git a/src/NLog/Targets/Wrappers/AsyncRequestQueue-T.cs b/src/NLog/Targets/Wrappers/AsyncRequestQueue-T.cs index 74df5d231a..a58fb77937 100644 --- a/src/NLog/Targets/Wrappers/AsyncRequestQueue-T.cs +++ b/src/NLog/Targets/Wrappers/AsyncRequestQueue-T.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using System.Collections.Generic; @@ -46,11 +48,11 @@ internal sealed class AsyncRequestQueue : AsyncRequestQueueBase /// /// Initializes a new instance of the AsyncRequestQueue class. /// - /// Request limit. + /// Queue max size. /// The overflow action. - public AsyncRequestQueue(int requestLimit, AsyncTargetWrapperOverflowAction overflowAction) + public AsyncRequestQueue(int queueLimit, AsyncTargetWrapperOverflowAction overflowAction) { - RequestLimit = requestLimit; + QueueLimit = queueLimit; OnOverflow = overflowAction; } @@ -81,7 +83,7 @@ public override bool Enqueue(AsyncLogEventInfo logEventInfo) lock (_logEventInfoQueue) { var currentCount = _logEventInfoQueue.Count; - if (currentCount >= RequestLimit) + if (currentCount >= QueueLimit) { switch (OnOverflow) { @@ -94,11 +96,11 @@ public override bool Enqueue(AsyncLogEventInfo logEventInfo) case AsyncTargetWrapperOverflowAction.Grow: InternalLogger.Debug("AsyncQueue - Growing the size of queue, because queue is full"); OnLogEventQueueGrows(currentCount + 1); - RequestLimit *= 2; + QueueLimit *= 2; break; case AsyncTargetWrapperOverflowAction.Block: - while (currentCount >= RequestLimit) + while (currentCount >= QueueLimit) { InternalLogger.Debug("AsyncQueue - Blocking until ready, because queue is full"); System.Threading.Monitor.Wait(_logEventInfoQueue); diff --git a/src/NLog/Targets/Wrappers/AsyncRequestQueueBase.cs b/src/NLog/Targets/Wrappers/AsyncRequestQueueBase.cs index f0cc71855d..42bd7768c2 100644 --- a/src/NLog/Targets/Wrappers/AsyncRequestQueueBase.cs +++ b/src/NLog/Targets/Wrappers/AsyncRequestQueueBase.cs @@ -31,10 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; +#nullable enable namespace NLog.Targets.Wrappers { + using System; using System.Collections.Generic; using NLog.Common; @@ -43,9 +44,9 @@ internal abstract class AsyncRequestQueueBase public abstract bool IsEmpty { get; } /// - /// Gets or sets the request limit. + /// Gets or sets the queue max-size /// - public int RequestLimit { get; set; } + public int QueueLimit { get; set; } /// /// Gets or sets the action to be taken when there's no more room in @@ -56,12 +57,12 @@ internal abstract class AsyncRequestQueueBase /// /// Occurs when LogEvent has been dropped, because internal queue is full and set to /// - public event EventHandler LogEventDropped; + public event EventHandler? LogEventDropped; /// /// Occurs when internal queue size is growing, because internal queue is full and set to /// - public event EventHandler LogEventQueueGrow; + public event EventHandler? LogEventQueueGrow; public abstract bool Enqueue(AsyncLogEventInfo logEventInfo); @@ -78,9 +79,9 @@ internal abstract class AsyncRequestQueueBase protected void OnLogEventDropped(LogEventInfo logEventInfo) => LogEventDropped?.Invoke(this, new LogEventDroppedEventArgs(logEventInfo)); /// - /// Raise event when RequestCount overflow + /// Raise event when RequestCount overflow /// /// current requests count - protected void OnLogEventQueueGrows(long requestsCount) => LogEventQueueGrow?.Invoke(this, new LogEventQueueGrowEventArgs(RequestLimit, requestsCount)); + protected void OnLogEventQueueGrows(long requestsCount) => LogEventQueueGrow?.Invoke(this, new LogEventQueueGrowEventArgs(QueueLimit, requestsCount)); } } diff --git a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs index 74a5d4ba74..4d123ed173 100644 --- a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using System; @@ -81,18 +83,29 @@ public class AsyncTargetWrapper : WrapperTargetBase { private readonly object _writeLockObject = new object(); private readonly object _timerLockObject = new object(); - private Timer _lazyWriterTimer; + private Timer? _lazyWriterTimer; private readonly ReusableAsyncLogEventList _reusableAsyncLogEventList = new ReusableAsyncLogEventList(200); - private event EventHandler _logEventDroppedEvent; - private event EventHandler _eventQueueGrowEvent; + private readonly WaitCallback _flushEventsInQueueDelegate; + private event EventHandler? _logEventDroppedEvent; + private event EventHandler? _eventQueueGrowEvent; private bool _missingServiceTypes; /// /// Initializes a new instance of the class. /// public AsyncTargetWrapper() - : this(null) { +#if NETFRAMEWORK + _requestQueue = new AsyncRequestQueue(10000, AsyncTargetWrapperOverflowAction.Discard); +#else + // NetStandard20 includes many optimizations for ConcurrentQueue: + // - See: https://blogs.msdn.microsoft.com/dotnet/2017/06/07/performance-improvements-in-net-core/ + // Net40 ConcurrencyQueue can seem to leak, because it doesn't clear properly on dequeue + // - See: https://blogs.msdn.microsoft.com/pfxteam/2012/05/08/concurrentqueuet-holding-on-to-a-few-dequeued-elements/ + _requestQueue = new ConcurrentRequestQueue(10000, AsyncTargetWrapperOverflowAction.Discard); +#endif + + _flushEventsInQueueDelegate = FlushEventsInQueue; } /// @@ -122,20 +135,10 @@ public AsyncTargetWrapper(Target wrappedTarget) /// Maximum number of requests in the queue. /// The action to be taken when the queue overflows. public AsyncTargetWrapper(Target wrappedTarget, int queueLimit, AsyncTargetWrapperOverflowAction overflowAction) + : this() { - Name = string.IsNullOrEmpty(wrappedTarget?.Name) ? Name : (wrappedTarget.Name + "_wrapper"); + Name = (wrappedTarget is null || string.IsNullOrEmpty(wrappedTarget.Name)) ? Name : (wrappedTarget.Name + "_wrapper"); WrappedTarget = wrappedTarget; - -#if NETFRAMEWORK - _requestQueue = new AsyncRequestQueue(10000, AsyncTargetWrapperOverflowAction.Discard); -#else - // NetStandard20 includes many optimizations for ConcurrentQueue: - // - See: https://blogs.msdn.microsoft.com/dotnet/2017/06/07/performance-improvements-in-net-core/ - // Net40 ConcurrencyQueue can seem to leak, because it doesn't clear properly on dequeue - // - See: https://blogs.msdn.microsoft.com/pfxteam/2012/05/08/concurrentqueuet-holding-on-to-a-few-dequeued-elements/ - _requestQueue = new ConcurrentRequestQueue(10000, AsyncTargetWrapperOverflowAction.Discard); -#endif - QueueLimit = queueLimit; OverflowAction = overflowAction; } @@ -220,8 +223,8 @@ public AsyncTargetWrapperOverflowAction OverflowAction /// public int QueueLimit { - get => _requestQueue.RequestLimit; - set => _requestQueue.RequestLimit = value; + get => _requestQueue.QueueLimit; + set => _requestQueue.QueueLimit = value; } /// @@ -254,13 +257,9 @@ public int QueueLimit /// The asynchronous continuation. protected override void FlushAsync(AsyncContinuation asyncContinuation) { - if (_flushEventsInQueueDelegate is null) - _flushEventsInQueueDelegate = FlushEventsInQueue; AsyncHelpers.StartAsyncTask(_flushEventsInQueueDelegate, asyncContinuation); } - private WaitCallback _flushEventsInQueueDelegate; - /// /// Initializes the target by starting the lazy writer timer. /// @@ -555,7 +554,7 @@ private void FlushEventsInQueue(object state) } else { - asyncContinuation(new NLogRuntimeException($"Target {this} failed to flush after lock timeout.")); + asyncContinuation?.Invoke(new NLogRuntimeException($"Target {this} failed to flush after lock timeout.")); } } @@ -611,7 +610,7 @@ private int WriteLogEventsToTarget(IList logEvents, string re { if (reason != null) InternalLogger.Trace("{0}: Writing {1} events ({2})", this, batchSize, reason); - WrappedTarget.WriteAsyncLogEvents(logEvents); + WrappedTarget?.WriteAsyncLogEvents(logEvents); } return batchSize; diff --git a/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs b/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs index 02f30bcf2e..1e845f28c5 100644 --- a/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using NLog.Common; @@ -64,7 +66,7 @@ public class AutoFlushTargetWrapper : WrapperTargetBase /// a flush on the wrapped target. /// /// - public ConditionExpression Condition { get; set; } + public ConditionExpression Condition { get; set; } = ConditionExpression.Empty; /// /// Delay the flush until the LogEvent has been confirmed as written @@ -92,8 +94,8 @@ public bool AsyncFlush /// Initializes a new instance of the class. /// public AutoFlushTargetWrapper() - : this(null) { + _flushCompletedContinuation = (ex) => _pendingManualFlushList.CompleteOperation(ex); } /// @@ -112,29 +114,29 @@ public AutoFlushTargetWrapper(string name, Target wrappedTarget) /// /// The wrapped target. public AutoFlushTargetWrapper(Target wrappedTarget) + : this() { - Name = string.IsNullOrEmpty(wrappedTarget?.Name) ? Name : (wrappedTarget.Name + "_wrapper"); + Name = (wrappedTarget is null || string.IsNullOrEmpty(wrappedTarget.Name)) ? Name : (wrappedTarget.Name + "_wrapper"); WrappedTarget = wrappedTarget; - _flushCompletedContinuation = (ex) => _pendingManualFlushList.CompleteOperation(ex); } /// protected override void InitializeTarget() { base.InitializeTarget(); - if (!_asyncFlush.HasValue && !TargetSupportsAsyncFlush(WrappedTarget)) + if (!_asyncFlush.HasValue && !TargetSupportsAsyncFlush()) { AsyncFlush = false; // Disable AsyncFlush, so the intended trigger works } } - private static bool TargetSupportsAsyncFlush(Target wrappedTarget) + private bool TargetSupportsAsyncFlush() { - if (wrappedTarget is BufferingTargetWrapper) + if (WrappedTarget is BufferingTargetWrapper) return false; #if !NET35 - if (wrappedTarget is AsyncTaskTarget) + if (WrappedTarget is AsyncTaskTarget) return false; #endif @@ -149,7 +151,7 @@ private static bool TargetSupportsAsyncFlush(Target wrappedTarget) /// Logging event to be written out. protected override void Write(AsyncLogEventInfo logEvent) { - if (Condition is null || ConditionExpression.BoxedTrue.Equals(Condition.Evaluate(logEvent.LogEvent))) + if (Condition is null || ReferenceEquals(Condition, ConditionExpression.Empty) || ConditionExpression.BoxedTrue.Equals(Condition.Evaluate(logEvent.LogEvent))) { if (AsyncFlush) { @@ -165,18 +167,18 @@ protected override void Write(AsyncLogEventInfo logEvent) currentContinuation(ex); }; _pendingManualFlushList.BeginOperation(); - WrappedTarget.WriteAsyncLogEvent(logEvent.LogEvent.WithContinuation(wrappedContinuation)); + WrappedTarget?.WriteAsyncLogEvent(logEvent.LogEvent.WithContinuation(wrappedContinuation)); } else { _pendingManualFlushList.BeginOperation(); - WrappedTarget.WriteAsyncLogEvent(logEvent); + WrappedTarget?.WriteAsyncLogEvent(logEvent); FlushWrappedTarget(_flushCompletedContinuation); } } else { - WrappedTarget.WriteAsyncLogEvent(logEvent); + WrappedTarget?.WriteAsyncLogEvent(logEvent); } } @@ -199,7 +201,7 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation) private void FlushWrappedTarget(AsyncContinuation asyncContinuation) { - WrappedTarget.Flush(asyncContinuation); + WrappedTarget?.Flush(asyncContinuation); } /// diff --git a/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs b/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs index f3f21bd793..33f3d78b35 100644 --- a/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using System; @@ -49,16 +51,17 @@ namespace NLog.Targets.Wrappers [Target("BufferingWrapper", IsWrapper = true)] public class BufferingTargetWrapper : WrapperTargetBase { - private AsyncRequestQueue _buffer; - private Timer _flushTimer; + private AsyncRequestQueue _buffer = new AsyncRequestQueue(100, AsyncTargetWrapperOverflowAction.Discard); + private Timer? _flushTimer; private readonly object _lockObject = new object(); /// /// Initializes a new instance of the class. /// public BufferingTargetWrapper() - : this(null) { + BufferSize = 100; + FlushTimeout = -1; } /// @@ -111,7 +114,7 @@ public BufferingTargetWrapper(Target wrappedTarget, int bufferSize, int flushTim /// The action to take when the buffer overflows. public BufferingTargetWrapper(Target wrappedTarget, int bufferSize, int flushTimeout, BufferingTargetWrapperOverflowAction overflowAction) { - Name = string.IsNullOrEmpty(wrappedTarget?.Name) ? Name : (wrappedTarget.Name + "_wrapper"); + Name = (wrappedTarget is null || string.IsNullOrEmpty(wrappedTarget.Name)) ? Name : (wrappedTarget.Name + "_wrapper"); WrappedTarget = wrappedTarget; BufferSize = bufferSize; FlushTimeout = flushTimeout; @@ -242,7 +245,7 @@ protected override void Write(AsyncLogEventInfo logEvent) PrecalculateVolatileLayouts(logEvent.LogEvent); var firstEventInQueue = _buffer.Enqueue(logEvent); - if (_buffer.RequestCount >= _buffer.RequestLimit) + if (_buffer.RequestCount >= _buffer.QueueLimit) { // If the OverflowAction action is set to "Discard", the buffer will automatically // roll over the oldest item. @@ -262,7 +265,7 @@ protected override void Write(AsyncLogEventInfo logEvent) if (flushTimeout > 0) { // reset the timer on first item added to the buffer or whenever SlidingTimeout is set to true - _flushTimer.Change(flushTimeout, -1); + _flushTimer?.Change(flushTimeout, -1); } } } @@ -282,7 +285,7 @@ private void FlushCallback(object state) if (_flushTimer is null) return; - WriteEventsInBuffer(null); + WriteEventsInBuffer(string.Empty); } else { @@ -320,7 +323,7 @@ private void WriteEventsInBuffer(string reason) AsyncLogEventInfo[] logEvents = _buffer.DequeueBatch(int.MaxValue); if (logEvents.Length > 0) { - if (reason != null) + if (!string.IsNullOrEmpty(reason)) InternalLogger.Trace("{0}: Writing {1} events ({2})", this, logEvents.Length, reason); WrappedTarget.WriteAsyncLogEvents(logEvents); } diff --git a/src/NLog/Targets/Wrappers/CompoundTargetBase.cs b/src/NLog/Targets/Wrappers/CompoundTargetBase.cs index 684898b2c5..a882b57245 100644 --- a/src/NLog/Targets/Wrappers/CompoundTargetBase.cs +++ b/src/NLog/Targets/Wrappers/CompoundTargetBase.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/ConcurrentRequestQueue.cs b/src/NLog/Targets/Wrappers/ConcurrentRequestQueue.cs index d7b1b245a5..9029f2bc1a 100644 --- a/src/NLog/Targets/Wrappers/ConcurrentRequestQueue.cs +++ b/src/NLog/Targets/Wrappers/ConcurrentRequestQueue.cs @@ -33,6 +33,8 @@ #if !NET35 +#nullable enable + namespace NLog.Targets.Wrappers { using System; @@ -51,11 +53,11 @@ internal sealed class ConcurrentRequestQueue : AsyncRequestQueueBase /// /// Initializes a new instance of the AsyncRequestQueue class. /// - /// Request limit. + /// Queue max size. /// The overflow action. - public ConcurrentRequestQueue(int requestLimit, AsyncTargetWrapperOverflowAction overflowAction) + public ConcurrentRequestQueue(int queueLimit, AsyncTargetWrapperOverflowAction overflowAction) { - RequestLimit = requestLimit; + QueueLimit = queueLimit; OnOverflow = overflowAction; } @@ -80,7 +82,7 @@ public override bool Enqueue(AsyncLogEventInfo logEventInfo) { long currentCount = Interlocked.Increment(ref _count); bool queueWasEmpty = currentCount == 1; // Inserted first item in empty queue - if (currentCount > RequestLimit) + if (currentCount > QueueLimit) { switch (OnOverflow) { @@ -98,7 +100,7 @@ public override bool Enqueue(AsyncLogEventInfo logEventInfo) { InternalLogger.Debug("AsyncQueue - Growing the size of queue, because queue is full"); OnLogEventQueueGrows(currentCount); - RequestLimit *= 2; + QueueLimit *= 2; } break; } @@ -123,7 +125,7 @@ private bool DequeueUntilBelowRequestLimit() } currentCount = Interlocked.Read(ref _count); queueWasEmpty = true; - } while (currentCount > RequestLimit); + } while (currentCount > QueueLimit); return queueWasEmpty; } @@ -134,14 +136,14 @@ private bool WaitForBelowRequestLimit() long currentCount = TrySpinWaitForLowerCount(); // If yield did not help, then wait on a lock - if (currentCount > RequestLimit) + if (currentCount > QueueLimit) { InternalLogger.Debug("AsyncQueue - Blocking until ready, because queue is full"); lock (_logEventInfoQueue) { InternalLogger.Trace("AsyncQueue - Entered critical section."); currentCount = Interlocked.Read(ref _count); - while (currentCount > RequestLimit) + while (currentCount > QueueLimit) { Interlocked.Decrement(ref _count); Monitor.Wait(_logEventInfoQueue); @@ -171,7 +173,7 @@ private long TrySpinWaitForLowerCount() spinWait.SpinOnce(); currentCount = Interlocked.Read(ref _count); - if (currentCount <= RequestLimit) + if (currentCount <= QueueLimit) break; } diff --git a/src/NLog/Targets/Wrappers/FallbackGroupTarget.cs b/src/NLog/Targets/Wrappers/FallbackGroupTarget.cs index e3a27e6cfa..4ac995d596 100644 --- a/src/NLog/Targets/Wrappers/FallbackGroupTarget.cs +++ b/src/NLog/Targets/Wrappers/FallbackGroupTarget.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using System.Collections.Generic; @@ -158,7 +160,7 @@ private AsyncLogEventInfo WrapWithFallback(AsyncLogEventInfo logEvent, int targe } } - AsyncContinuation continuation = null; + AsyncContinuation? continuation = null; int tryCounter = 0; continuation = ex => { @@ -183,7 +185,7 @@ private AsyncLogEventInfo WrapWithFallback(AsyncLogEventInfo logEvent, int targe { InternalLogger.Warn(ex, "{0}: Target '{1}' failed. Fallback to next: `{2}`", this, Targets[targetToInvoke], Targets[nextTarget]); targetToInvoke = nextTarget; - Targets[targetToInvoke].WriteAsyncLogEvent(logEvent.LogEvent.WithContinuation(continuation)); + Targets[targetToInvoke].WriteAsyncLogEvent(continuation is null ? logEvent : logEvent.LogEvent.WithContinuation(continuation)); } else { diff --git a/src/NLog/Targets/Wrappers/FilteringRule.cs b/src/NLog/Targets/Wrappers/FilteringRule.cs index 131c740ae0..5afc1c00af 100644 --- a/src/NLog/Targets/Wrappers/FilteringRule.cs +++ b/src/NLog/Targets/Wrappers/FilteringRule.cs @@ -31,10 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { - using Conditions; - using Config; + using NLog.Conditions; + using NLog.Config; /// /// Filtering rule for . diff --git a/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs b/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs index 79026ac54b..f651c9082f 100644 --- a/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs @@ -31,12 +31,13 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using System.Collections.Generic; using NLog.Common; using NLog.Conditions; - using NLog.Config; using NLog.Filters; using NLog.Internal; @@ -66,8 +67,8 @@ public class FilteringTargetWrapper : WrapperTargetBase /// Initializes a new instance of the class. /// public FilteringTargetWrapper() - : this(null, null) { + Filter = ConditionBasedFilter.Empty; } /// @@ -89,9 +90,9 @@ public FilteringTargetWrapper(string name, Target wrappedTarget, ConditionExpres /// The condition. public FilteringTargetWrapper(Target wrappedTarget, ConditionExpression condition) { - Name = string.IsNullOrEmpty(wrappedTarget?.Name) ? Name : (wrappedTarget.Name + "_wrapper"); + Name = (wrappedTarget is null || string.IsNullOrEmpty(wrappedTarget.Name)) ? Name : (wrappedTarget.Name + "_wrapper"); WrappedTarget = wrappedTarget; - Condition = condition; + Filter = CreateFilter(condition); } /// @@ -99,7 +100,7 @@ public FilteringTargetWrapper(Target wrappedTarget, ConditionExpression conditio /// to the wrapped target. /// /// - public ConditionExpression Condition { get => (Filter as ConditionBasedFilter)?.Condition; set => Filter = CreateFilter(value); } + public ConditionExpression? Condition { get => (Filter as ConditionBasedFilter)?.Condition; set => Filter = CreateFilter(value ?? ConditionExpression.Empty); } /// /// Gets or sets the filter. Log events who evaluates to will be discarded @@ -110,7 +111,7 @@ public FilteringTargetWrapper(Target wrappedTarget, ConditionExpression conditio /// protected override void InitializeTarget() { - if (Filter is null) + if (Filter is null || ReferenceEquals(Condition, ConditionExpression.Empty)) throw new NLogConfigurationException($"FilteringTargetWrapper Filter-property must be assigned. Filter LogEvents using blank Filter not supported."); base.InitializeTarget(); @@ -126,7 +127,7 @@ protected override void Write(AsyncLogEventInfo logEvent) { if (ShouldLogEvent(logEvent, Filter)) { - WrappedTarget.WriteAsyncLogEvent(logEvent); + WrappedTarget?.WriteAsyncLogEvent(logEvent); } } @@ -136,7 +137,7 @@ protected override void Write(IList logEvents) var filterLogEvents = logEvents.Filter(Filter, (logEvent, filter) => ShouldLogEvent(logEvent, filter)); if (filterLogEvents.Count > 0) { - WrappedTarget.WriteAsyncLogEvents(filterLogEvents); + WrappedTarget?.WriteAsyncLogEvents(filterLogEvents); } } @@ -156,10 +157,9 @@ private static bool ShouldLogEvent(AsyncLogEventInfo logEvent, Filter filter) private static ConditionBasedFilter CreateFilter(ConditionExpression value) { - if (value is null) - { - return null; - } + if (value is null || ReferenceEquals(value, ConditionExpression.Empty)) + return ConditionBasedFilter.Empty; + return new ConditionBasedFilter { Condition = value, FilterDefaultAction = FilterResult.Ignore }; } } diff --git a/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs b/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs index bd1a66c61c..1877006d37 100644 --- a/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs @@ -31,11 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using System.Collections.Generic; using NLog.Common; - using NLog.Config; using NLog.Internal; using NLog.Layouts; @@ -49,18 +50,19 @@ namespace NLog.Targets.Wrappers [Target("GroupByWrapper", IsWrapper = true)] public class GroupByTargetWrapper : WrapperTargetBase { - SortHelpers.KeySelector _buildKeyStringDelegate; + private readonly SortHelpers.KeySelector _buildKeyStringDelegate; /// /// Identifier to perform group-by /// - public Layout Key { get; set; } + public Layout Key { get; set; } = Layout.Empty; /// /// Initializes a new instance of the class. /// - public GroupByTargetWrapper() : this(null) + public GroupByTargetWrapper() { + _buildKeyStringDelegate = logEvent => RenderLogEvent(Key, logEvent.LogEvent); } /// @@ -68,7 +70,7 @@ public GroupByTargetWrapper() : this(null) /// /// The wrapped target. public GroupByTargetWrapper(Target wrappedTarget) - : this(string.IsNullOrEmpty(wrappedTarget?.Name) ? null : (wrappedTarget.Name + "_wrapper"), wrappedTarget) + : this(string.Empty, wrappedTarget) { } @@ -89,8 +91,9 @@ public GroupByTargetWrapper(string name, Target wrappedTarget) /// The wrapped target. /// Group by identifier. public GroupByTargetWrapper(string name, Target wrappedTarget, Layout key) + : this() { - Name = name; + Name = (!string.IsNullOrEmpty(name) || wrappedTarget is null || string.IsNullOrEmpty(wrappedTarget.Name)) ? name : (wrappedTarget.Name + "_wrapper"); WrappedTarget = wrappedTarget; Key = key; } @@ -107,19 +110,16 @@ protected override void InitializeTarget() /// protected override void Write(AsyncLogEventInfo logEvent) { - WrappedTarget.WriteAsyncLogEvent(logEvent); + WrappedTarget?.WriteAsyncLogEvent(logEvent); } /// protected override void Write(IList logEvents) { - if (_buildKeyStringDelegate is null) - _buildKeyStringDelegate = logEvent => RenderLogEvent(Key, logEvent.LogEvent); - var buckets = logEvents.BucketSort(_buildKeyStringDelegate); foreach (var bucket in buckets) { - WrappedTarget.WriteAsyncLogEvents(bucket.Value); + WrappedTarget?.WriteAsyncLogEvents(bucket.Value); } } } diff --git a/src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs b/src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs index 12908ac918..1143b818ed 100644 --- a/src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using System; @@ -52,11 +54,12 @@ public class LimitingTargetWrapper : WrapperTargetBase /// /// Initializes a new instance of the class. /// - public LimitingTargetWrapper() : this(null) + public LimitingTargetWrapper() { + MessageLimit = 1000; + Interval = TimeSpan.FromHours(1); } - /// /// Initializes a new instance of the class. /// @@ -85,7 +88,7 @@ public LimitingTargetWrapper(Target wrappedTarget) /// Interval in which the maximum number of messages can be written. public LimitingTargetWrapper(Target wrappedTarget, int messageLimit, TimeSpan interval) { - Name = string.IsNullOrEmpty(wrappedTarget?.Name) ? Name : (wrappedTarget.Name + "_wrapper"); + Name = (wrappedTarget is null || string.IsNullOrEmpty(wrappedTarget.Name)) ? Name : (wrappedTarget.Name + "_wrapper"); WrappedTarget = wrappedTarget; MessageLimit = messageLimit; Interval = interval; @@ -149,7 +152,7 @@ protected override void Write(AsyncLogEventInfo logEvent) if (messageLimit <= 0 || MessagesWrittenCount < messageLimit) { - WrappedTarget.WriteAsyncLogEvent(logEvent); + WrappedTarget?.WriteAsyncLogEvent(logEvent); MessagesWrittenCount++; } else diff --git a/src/NLog/Targets/Wrappers/LogEventDroppedEventArgs.cs b/src/NLog/Targets/Wrappers/LogEventDroppedEventArgs.cs index f914c5fd28..c61f1df88d 100644 --- a/src/NLog/Targets/Wrappers/LogEventDroppedEventArgs.cs +++ b/src/NLog/Targets/Wrappers/LogEventDroppedEventArgs.cs @@ -31,10 +31,12 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; +#nullable enable namespace NLog.Targets.Wrappers { + using System; + /// /// Arguments for events. /// diff --git a/src/NLog/Targets/Wrappers/LogEventQueueGrowEventArgs.cs b/src/NLog/Targets/Wrappers/LogEventQueueGrowEventArgs.cs index 329ffbf403..e319d14d8e 100644 --- a/src/NLog/Targets/Wrappers/LogEventQueueGrowEventArgs.cs +++ b/src/NLog/Targets/Wrappers/LogEventQueueGrowEventArgs.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs b/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs index 611920f795..c893a61c65 100644 --- a/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using System.Collections.Generic; @@ -69,7 +71,6 @@ public class PostFilteringTargetWrapper : WrapperTargetBase /// Initializes a new instance of the class. /// public PostFilteringTargetWrapper() - : this(null) { } @@ -77,7 +78,7 @@ public PostFilteringTargetWrapper() /// Initializes a new instance of the class. /// public PostFilteringTargetWrapper(Target wrappedTarget) - : this(string.IsNullOrEmpty(wrappedTarget?.Name) ? null : (wrappedTarget.Name + "_wrapper"), wrappedTarget) + : this(string.Empty, wrappedTarget) { } @@ -88,7 +89,7 @@ public PostFilteringTargetWrapper(Target wrappedTarget) /// The wrapped target. public PostFilteringTargetWrapper(string name, Target wrappedTarget) { - Name = name ?? Name; + Name = (!string.IsNullOrEmpty(name) || wrappedTarget is null || string.IsNullOrEmpty(wrappedTarget.Name)) ? name : (wrappedTarget.Name + "_wrapper"); WrappedTarget = wrappedTarget; } @@ -96,7 +97,7 @@ public PostFilteringTargetWrapper(string name, Target wrappedTarget) /// Gets or sets the default filter to be applied when no specific rule matches. /// /// - public ConditionExpression DefaultFilter { get; set; } + public ConditionExpression DefaultFilter { get; set; } = ConditionExpression.Empty; /// /// Gets the collection of filtering rules. The rules are processed top-down @@ -139,10 +140,10 @@ protected override void Write(IList logEvents) { InternalLogger.Trace("{0}: Running on {1} events", this, logEvents.Count); - var resultFilter = EvaluateAllRules(logEvents) ?? DefaultFilter; - if (resultFilter is null) + var resultFilter = EvaluateAllRules(logEvents); + if (resultFilter is null || ReferenceEquals(resultFilter, ConditionExpression.Empty)) { - WrappedTarget.WriteAsyncLogEvents(logEvents); + WrappedTarget?.WriteAsyncLogEvents(logEvents); } else { @@ -152,14 +153,14 @@ protected override void Write(IList logEvents) if (resultBuffer.Count > 0) { InternalLogger.Trace("{0}: Sending to {1}", this, WrappedTarget); - WrappedTarget.WriteAsyncLogEvents(resultBuffer); + WrappedTarget?.WriteAsyncLogEvents(resultBuffer); } } } private static bool ShouldLogEvent(AsyncLogEventInfo logEvent, ConditionExpression resultFilter) { - object v = resultFilter.Evaluate(logEvent.LogEvent); + var v = resultFilter.Evaluate(logEvent.LogEvent); if (ConditionExpression.BoxedTrue.Equals(v)) { return true; @@ -179,14 +180,14 @@ private static bool ShouldLogEvent(AsyncLogEventInfo logEvent, ConditionExpressi private ConditionExpression EvaluateAllRules(IList logEvents) { if (Rules.Count == 0) - return null; + return DefaultFilter; for (int i = 0; i < logEvents.Count; ++i) { for (int j = 0; j < Rules.Count; ++j) { var rule = Rules[j]; - object v = rule.Exists.Evaluate(logEvents[i].LogEvent); + var v = rule.Exists.Evaluate(logEvents[i].LogEvent); if (ConditionExpression.BoxedTrue.Equals(v)) { InternalLogger.Trace("{0}: Rule matched: {1}", this, rule.Exists); @@ -195,7 +196,7 @@ private ConditionExpression EvaluateAllRules(IList logEvents) } } - return null; + return DefaultFilter; } } } diff --git a/src/NLog/Targets/Wrappers/RandomizeGroupTarget.cs b/src/NLog/Targets/Wrappers/RandomizeGroupTarget.cs index f8b2f64dfd..f79d908afc 100644 --- a/src/NLog/Targets/Wrappers/RandomizeGroupTarget.cs +++ b/src/NLog/Targets/Wrappers/RandomizeGroupTarget.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/RepeatingTargetWrapper.cs b/src/NLog/Targets/Wrappers/RepeatingTargetWrapper.cs index 8da51aa6bb..b6a7eff04a 100644 --- a/src/NLog/Targets/Wrappers/RepeatingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/RepeatingTargetWrapper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using NLog.Common; @@ -61,7 +63,6 @@ public class RepeatingTargetWrapper : WrapperTargetBase /// Initializes a new instance of the class. /// public RepeatingTargetWrapper() - : this(null, 3) { } @@ -84,6 +85,7 @@ public RepeatingTargetWrapper(string name, Target wrappedTarget, int repeatCount /// The repeat count. public RepeatingTargetWrapper(Target wrappedTarget, int repeatCount) { + Name = (wrappedTarget is null || string.IsNullOrEmpty(wrappedTarget.Name)) ? Name : (wrappedTarget.Name + "_wrapper"); WrappedTarget = wrappedTarget; RepeatCount = repeatCount; } @@ -92,7 +94,7 @@ public RepeatingTargetWrapper(Target wrappedTarget, int repeatCount) /// Gets or sets the number of times to repeat each log message. /// /// - public int RepeatCount { get; set; } + public int RepeatCount { get; set; } = 3; /// /// Forwards the log message to the by calling the method times. @@ -100,7 +102,7 @@ public RepeatingTargetWrapper(Target wrappedTarget, int repeatCount) /// The log event. protected override void Write(AsyncLogEventInfo logEvent) { - AsyncHelpers.Repeat(RepeatCount, logEvent.Continuation, cont => WrappedTarget.WriteAsyncLogEvent(logEvent.LogEvent.WithContinuation(cont))); + AsyncHelpers.Repeat(RepeatCount, logEvent.Continuation, cont => WrappedTarget?.WriteAsyncLogEvent(logEvent.LogEvent.WithContinuation(cont))); } } } diff --git a/src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs b/src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs index f8dcfae821..55a67c70d9 100644 --- a/src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs @@ -31,6 +31,8 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +#nullable enable + namespace NLog.Targets.Wrappers { using System; @@ -66,8 +68,9 @@ public class RetryingTargetWrapper : WrapperTargetBase /// Initializes a new instance of the class. /// public RetryingTargetWrapper() - : this(null, 3, 100) { + RetryCount = 3; + RetryDelayMilliseconds = 100; } /// @@ -91,7 +94,7 @@ public RetryingTargetWrapper(string name, Target wrappedTarget, int retryCount, /// The retry delay milliseconds. public RetryingTargetWrapper(Target wrappedTarget, int retryCount, int retryDelayMilliseconds) { - Name = string.IsNullOrEmpty(wrappedTarget?.Name) ? Name : (wrappedTarget.Name + "_wrapper"); + Name = (wrappedTarget is null || string.IsNullOrEmpty(wrappedTarget.Name)) ? Name : (wrappedTarget.Name + "_wrapper"); WrappedTarget = wrappedTarget; RetryCount = retryCount; RetryDelayMilliseconds = retryDelayMilliseconds; @@ -141,7 +144,7 @@ protected override void WriteAsyncThreadSafe(IList logEvents) lock (_retrySyncObject) { - WrappedTarget.WriteAsyncLogEvents(logEvents); + WrappedTarget?.WriteAsyncLogEvents(logEvents); } } else @@ -175,12 +178,12 @@ protected override void WriteAsyncThreadSafe(AsyncLogEventInfo logEvent) /// The log event. protected override void Write(AsyncLogEventInfo logEvent) { - WrappedTarget.WriteAsyncLogEvent(WrapWithRetry(logEvent, (retryNumber) => true)); + WrappedTarget?.WriteAsyncLogEvent(WrapWithRetry(logEvent, (retryNumber) => true)); } private AsyncLogEventInfo WrapWithRetry(AsyncLogEventInfo logEvent, Func sleepBeforeRetry) { - AsyncContinuation continuation = null; + AsyncContinuation? continuation = null; int counter = 0; continuation = ex => @@ -221,7 +224,7 @@ private AsyncLogEventInfo WrapWithRetry(AsyncLogEventInfo logEvent, Func layout1 = url; + Layout layout1 = null; Layout layout2 = new Layout(url); // Act + Assert diff --git a/tests/NLog.UnitTests/Targets/TargetTests.cs b/tests/NLog.UnitTests/Targets/TargetTests.cs index c5534c6a4e..4bdf93436c 100644 --- a/tests/NLog.UnitTests/Targets/TargetTests.cs +++ b/tests/NLog.UnitTests/Targets/TargetTests.cs @@ -168,8 +168,6 @@ public void TargetContructorWithNameTest() CheckEquals(targetType, targetConstructedTarget, namedConstructedTarget, ref lastPropertyName, ref checkCount); } - - } catch (Exception ex) { @@ -179,6 +177,7 @@ public void TargetContructorWithNameTest() Assert.False(constructionFailed, failureMessage); } } + Assert.Equal(neededCheckCount, checkCount); } @@ -274,7 +273,7 @@ public override bool Equals(AsyncRequestQueue x, AsyncRequestQueue y) { if (x is null) return y is null; - return x.RequestLimit == y.RequestLimit && x.OnOverflow == y.OnOverflow; + return x.QueueLimit == y.QueueLimit && x.OnOverflow == y.OnOverflow; } /// @@ -288,7 +287,7 @@ public override int GetHashCode(AsyncRequestQueue obj) { unchecked { - return (obj.RequestLimit * 397) ^ (int)obj.OnOverflow; + return (obj.QueueLimit * 397) ^ (int)obj.OnOverflow; } } } diff --git a/tests/NLog.UnitTests/Targets/Wrappers/AsyncRequestQueueTests.cs b/tests/NLog.UnitTests/Targets/Wrappers/AsyncRequestQueueTests.cs index f5d20959b0..7c1c3be3f6 100644 --- a/tests/NLog.UnitTests/Targets/Wrappers/AsyncRequestQueueTests.cs +++ b/tests/NLog.UnitTests/Targets/Wrappers/AsyncRequestQueueTests.cs @@ -49,7 +49,7 @@ public void AsyncRequestQueueWithDiscardBehaviorTest() var ev4 = LogEventInfo.CreateNullEvent().WithContinuation(ex => { }); var queue = new AsyncRequestQueue(3, AsyncTargetWrapperOverflowAction.Discard); - Assert.Equal(3, queue.RequestLimit); + Assert.Equal(3, queue.QueueLimit); Assert.Equal(AsyncTargetWrapperOverflowAction.Discard, queue.OnOverflow); Assert.Equal(0, queue.RequestCount); queue.Enqueue(ev1); @@ -82,7 +82,7 @@ public void AsyncRequestQueueWithGrowBehaviorTest() var ev4 = LogEventInfo.CreateNullEvent().WithContinuation(ex => { }); var queue = new AsyncRequestQueue(3, AsyncTargetWrapperOverflowAction.Grow); - Assert.Equal(3, queue.RequestLimit); + Assert.Equal(3, queue.QueueLimit); Assert.Equal(AsyncTargetWrapperOverflowAction.Grow, queue.OnOverflow); Assert.Equal(0, queue.RequestCount); queue.Enqueue(ev1); @@ -147,7 +147,7 @@ public void AsyncRequestQueueWithBlockBehavior() logEventInfos = queue.DequeueBatch(left); int got = logEventInfos.Length; - Assert.True(got <= queue.RequestLimit); + Assert.True(got <= queue.QueueLimit); total += got; } @@ -168,7 +168,7 @@ public void AsyncRequestQueueWithBlockBehavior() logEventInfos = queue.DequeueBatch(left); int got = logEventInfos.Length; - Assert.True(got <= queue.RequestLimit); + Assert.True(got <= queue.QueueLimit); total += got; } @@ -185,7 +185,7 @@ public void AsyncRequestQueueClearTest() var ev4 = LogEventInfo.CreateNullEvent().WithContinuation(ex => { }); var queue = new AsyncRequestQueue(3, AsyncTargetWrapperOverflowAction.Grow); - Assert.Equal(3, queue.RequestLimit); + Assert.Equal(3, queue.QueueLimit); Assert.Equal(AsyncTargetWrapperOverflowAction.Grow, queue.OnOverflow); Assert.Equal(0, queue.RequestCount); queue.Enqueue(ev1); @@ -226,7 +226,7 @@ public void RaiseEventLogEventQueueGrow_OnLogItems() } Assert.Equal(ExpectedCountOfGrovingTimes, grovingItemsCount); - Assert.Equal(ExpectedFinalSize, requestQueue.RequestLimit); + Assert.Equal(ExpectedFinalSize, requestQueue.QueueLimit); } [Fact] diff --git a/tests/NLog.UnitTests/Targets/Wrappers/ConcurrentRequestQueueTests.cs b/tests/NLog.UnitTests/Targets/Wrappers/ConcurrentRequestQueueTests.cs index 7ca818cbf8..fb634f8c7e 100644 --- a/tests/NLog.UnitTests/Targets/Wrappers/ConcurrentRequestQueueTests.cs +++ b/tests/NLog.UnitTests/Targets/Wrappers/ConcurrentRequestQueueTests.cs @@ -84,7 +84,7 @@ public void RaiseEventLogEventQueueGrow_OnLogItems() } Assert.Equal(ExpectedCountOfGrovingTimes, grovingItemsCount); - Assert.Equal(ExpectedFinalSize, requestQueue.RequestLimit); + Assert.Equal(ExpectedFinalSize, requestQueue.QueueLimit); } [Fact] From a04e9ab559d3201f67a0db39b5bac068703cbdbb Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 19 May 2025 22:26:19 +0200 Subject: [PATCH 118/224] NLog Target with nullable references (#5822) --- .../OutputDebugStringTarget.cs | 2 +- src/NLog.Targets.Trace/TraceTarget.cs | 2 +- .../WebServiceTarget.cs | 14 +- .../NLog.WindowsEventLog.csproj | 19 +- src/NLog/Config/ServiceRepository.cs | 5 +- .../Config/ServiceRepositoryExtensions.cs | 4 +- src/NLog/Config/ServiceRepositoryInternal.cs | 5 +- src/NLog/IObjectTypeTransformer.cs | 4 +- src/NLog/Internal/MemberNotNullAttribute.cs | 171 ------------------ src/NLog/Internal/PathHelpers.cs | 6 +- src/NLog/Internal/PropertyHelper.cs | 10 +- src/NLog/Internal/ReflectionHelpers.cs | 8 +- .../Internal/SetupLoadConfigurationBuilder.cs | 6 +- src/NLog/Internal/ThreadSafeDictionary.cs | 2 - src/NLog/Internal/TimeoutContinuation.cs | 1 - src/NLog/Internal/XmlParser.cs | 12 +- .../AllEventPropertiesLayoutRenderer.cs | 28 +-- .../AppDomainLayoutRenderer.cs | 2 +- .../AppSettingLayoutRenderer.cs | 12 +- .../AssemblyVersionLayoutRenderer.cs | 29 ++- .../LayoutRenderers/BaseDirLayoutRenderer.cs | 4 +- .../CallSiteLineNumberLayoutRenderer.cs | 2 +- .../LayoutRenderers/CounterLayoutRenderer.cs | 2 +- .../CurrentDirLayoutRenderer.cs | 4 +- .../LayoutRenderers/DateLayoutRenderer.cs | 6 +- .../EnvironmentLayoutRenderer.cs | 8 +- .../EventPropertiesLayoutRenderer.cs | 46 +++-- .../ExceptionDataLayoutRenderer.cs | 8 +- .../ExceptionLayoutRenderer.cs | 73 ++++---- .../LayoutRenderers/FuncLayoutRenderer.cs | 24 ++- .../FuncThreadAgnosticLayoutRenderer.cs | 4 +- src/NLog/LayoutRenderers/GdcLayoutRenderer.cs | 22 +-- .../LayoutRenderers/HostNameLayoutRenderer.cs | 9 +- .../LayoutRenderers/IdentityLayoutRenderer.cs | 5 +- .../InstallContextLayoutRenderer.cs | 4 +- src/NLog/LayoutRenderers/LayoutRenderer.cs | 6 +- .../LayoutRendererAttribute.cs | 2 - .../LayoutRenderers/LevelLayoutRenderer.cs | 2 +- .../LayoutRenderers/LiteralLayoutRenderer.cs | 2 +- .../LiteralWithRawValueLayoutRenderer.cs | 6 +- .../MachineNameLayoutRenderer.cs | 2 +- .../LayoutRenderers/MessageLayoutRenderer.cs | 2 +- .../LayoutRenderers/NLogDirLayoutRenderer.cs | 14 +- .../ProcessDirLayoutRenderer.cs | 14 +- .../ProcessInfoLayoutRenderer.cs | 16 +- .../ScopeContextIndentLayoutRenderer.cs | 2 +- .../ScopeContextNestedStatesLayoutRenderer.cs | 24 ++- .../ScopeContextPropertyLayoutRenderer.cs | 12 +- .../ScopeContextTimingLayoutRenderer.cs | 2 +- .../SpecialFolderLayoutRenderer.cs | 4 +- .../StackTraceLayoutRenderer.cs | 4 +- .../LayoutRenderers/TempDirLayoutRenderer.cs | 16 +- .../LayoutRenderers/VariableLayoutRenderer.cs | 8 +- .../Wrappers/CachedLayoutRendererWrapper.cs | 10 +- .../Wrappers/ObjectPathRendererWrapper.cs | 16 +- .../OnExceptionLayoutRendererWrapper.cs | 2 +- .../OnHasPropertiesLayoutRendererWrapper.cs | 2 +- .../Wrappers/ReplaceLayoutRendererWrapper.cs | 4 +- .../WhenEmptyLayoutRendererWrapper.cs | 54 ++---- .../Wrappers/WhenLayoutRendererWrapper.cs | 6 +- .../Wrappers/WrapperLayoutRendererBase.cs | 3 - .../WrapperLayoutRendererBuilderBase.cs | 2 - src/NLog/Layouts/Layout.cs | 11 +- src/NLog/Layouts/Typed/Layout.cs | 9 +- src/NLog/LogFactory.cs | 8 +- src/NLog/NLog.csproj | 1 + src/NLog/SetupBuilderExtensions.cs | 2 +- src/NLog/SetupExtensionsBuilderExtensions.cs | 30 +-- .../SetupInternalLoggerBuilderExtensions.cs | 10 +- src/NLog/SetupLoadConfigurationExtensions.cs | 29 +-- .../SetupSerializationBuilderExtensions.cs | 4 +- src/NLog/Targets/AsyncTaskTarget.cs | 76 ++++---- src/NLog/Targets/ColoredConsoleTarget.cs | 8 +- .../Targets/ConsoleRowHighlightingRule.cs | 18 +- src/NLog/Targets/ConsoleTarget.cs | 6 +- src/NLog/Targets/ConsoleTargetHelper.cs | 2 +- .../Targets/ConsoleWordHighlightingRule.cs | 18 +- src/NLog/Targets/DebugSystemTarget.cs | 2 +- src/NLog/Targets/DebuggerTarget.cs | 2 +- src/NLog/Targets/EventLogTarget.cs | 48 ++--- .../ExclusiveFileLockingAppender.cs | 7 +- .../BaseFileArchiveHandler.cs | 8 +- .../LegacyArchiveFileNameHandler.cs | 2 +- src/NLog/Targets/FileTarget.cs | 29 +-- src/NLog/Targets/JsonSerializeOptions.cs | 4 +- src/NLog/Targets/LineEndingMode.cs | 6 +- src/NLog/Targets/MethodCallParameter.cs | 6 +- src/NLog/Targets/MethodCallTarget.cs | 48 +++-- src/NLog/Targets/MethodCallTargetBase.cs | 20 +- src/NLog/Targets/NullTarget.cs | 2 +- src/NLog/Targets/TargetPropertyWithContext.cs | 2 - src/NLog/Targets/TargetWithLayout.cs | 2 - .../TargetWithLayoutHeaderAndFooter.cs | 1 - .../LayoutRenderers/ExceptionTests.cs | 13 +- .../Wrappers/WhenEmptyTests.cs | 5 +- 95 files changed, 493 insertions(+), 746 deletions(-) delete mode 100644 src/NLog/Internal/MemberNotNullAttribute.cs diff --git a/src/NLog.OutputDebugString/OutputDebugStringTarget.cs b/src/NLog.OutputDebugString/OutputDebugStringTarget.cs index 6b4b664dc2..55748a9648 100644 --- a/src/NLog.OutputDebugString/OutputDebugStringTarget.cs +++ b/src/NLog.OutputDebugString/OutputDebugStringTarget.cs @@ -59,7 +59,7 @@ public sealed class OutputDebugStringTarget : TargetWithLayoutHeaderAndFooter /// /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// - public OutputDebugStringTarget() : base() + public OutputDebugStringTarget() { } diff --git a/src/NLog.Targets.Trace/TraceTarget.cs b/src/NLog.Targets.Trace/TraceTarget.cs index 5fc91e9c81..4d12ab45ee 100644 --- a/src/NLog.Targets.Trace/TraceTarget.cs +++ b/src/NLog.Targets.Trace/TraceTarget.cs @@ -80,7 +80,7 @@ public sealed class TraceTarget : TargetWithLayoutHeaderAndFooter /// /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// - public TraceTarget() : base() + public TraceTarget() { } diff --git a/src/NLog.Targets.WebService/WebServiceTarget.cs b/src/NLog.Targets.WebService/WebServiceTarget.cs index eb107be71e..c9c682e6f1 100644 --- a/src/NLog.Targets.WebService/WebServiceTarget.cs +++ b/src/NLog.Targets.WebService/WebServiceTarget.cs @@ -242,19 +242,7 @@ public WebServiceProxyType ProxyType protected override void DoInvoke(object[] parameters) { // method is not used, instead asynchronous overload will be used - throw new NotImplementedException(); - } - - /// - /// Calls the target DoInvoke method, and handles AsyncContinuation callback - /// - /// Method call parameters. - /// The continuation. - protected override void DoInvoke(object[] parameters, AsyncContinuation continuation) - { - var url = BuildWebServiceUrl(LogEventInfo.CreateNullEvent(), parameters); - var webRequest = CreateHttpWebRequest(url); - DoInvoke(parameters, webRequest, continuation); + throw new NotSupportedException(); } /// diff --git a/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj b/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj index a8ea939549..f8951348bc 100644 --- a/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj +++ b/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj @@ -1,7 +1,7 @@  - netstandard2.0 + netstandard2.0;netstandard2.1 NLog.WindowsEventLog for .NET Standard NLog @@ -32,8 +32,10 @@ true true true - true - true + enable + 9 + true + true copyused @@ -42,8 +44,17 @@ $(DefineConstants);WindowsEventLogPackage + + NLog.WindowsEventLog for NetStandard 2.1 + $(DefineConstants);WindowsEventLogPackage + + - + + + + + diff --git a/src/NLog/Config/ServiceRepository.cs b/src/NLog/Config/ServiceRepository.cs index 93436c8472..470ad8bb6d 100644 --- a/src/NLog/Config/ServiceRepository.cs +++ b/src/NLog/Config/ServiceRepository.cs @@ -31,12 +31,9 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; - using System.Diagnostics.CodeAnalysis; /// /// Interface to register available configuration objects type @@ -56,7 +53,7 @@ public abstract class ServiceRepository : IServiceProvider /// Avoid calling this while handling a LogEvent, since random deadlocks can occur. public abstract object GetService(Type serviceType); - internal abstract bool TryGetService([NotNullWhen(returnValue: true)] out T? serviceInstance) where T : class; + internal abstract bool TryGetService(out T? serviceInstance) where T : class; internal ServiceRepository() { diff --git a/src/NLog/Config/ServiceRepositoryExtensions.cs b/src/NLog/Config/ServiceRepositoryExtensions.cs index 99fd2bbd01..06d415af93 100644 --- a/src/NLog/Config/ServiceRepositoryExtensions.cs +++ b/src/NLog/Config/ServiceRepositoryExtensions.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; @@ -59,7 +57,7 @@ internal static T ResolveService(this ServiceRepository serviceProvider, bool try { - if (serviceProvider.TryGetService(out var service)) + if (serviceProvider.TryGetService(out var service) && service != null) { return service; } diff --git a/src/NLog/Config/ServiceRepositoryInternal.cs b/src/NLog/Config/ServiceRepositoryInternal.cs index 58172dc12c..b64b882cac 100644 --- a/src/NLog/Config/ServiceRepositoryInternal.cs +++ b/src/NLog/Config/ServiceRepositoryInternal.cs @@ -31,13 +31,10 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; using NLog.Internal; /// @@ -92,7 +89,7 @@ public override object GetService(Type serviceType) return objectResolver?.Invoke(); } - internal override bool TryGetService([NotNullWhen(returnValue: true)] out T? serviceInstance) where T : class + internal override bool TryGetService(out T? serviceInstance) where T : class { if (TryGetService(typeof(T)) is T service) { diff --git a/src/NLog/IObjectTypeTransformer.cs b/src/NLog/IObjectTypeTransformer.cs index 6b3f26c4e9..a2c859c931 100644 --- a/src/NLog/IObjectTypeTransformer.cs +++ b/src/NLog/IObjectTypeTransformer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { /// @@ -46,6 +44,6 @@ internal interface IObjectTypeTransformer /// /// Null if unknown object, or object cannot be handled /// - object? TryTransformObject(object? obj); + object? TryTransformObject(object obj); } } diff --git a/src/NLog/Internal/MemberNotNullAttribute.cs b/src/NLog/Internal/MemberNotNullAttribute.cs deleted file mode 100644 index d53d30a104..0000000000 --- a/src/NLog/Internal/MemberNotNullAttribute.cs +++ /dev/null @@ -1,171 +0,0 @@ -// -// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * Neither the name of Jaroslaw Kowalski nor the names of its -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -// - -#if !NETSTANDARD2_1_OR_GREATER && !NETCOREAPP3_0_OR_GREATER -namespace System.Diagnostics.CodeAnalysis -{ - /// Specifies that null is allowed as an input even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - internal sealed class AllowNullAttribute : Attribute { } - - /// Specifies that null is disallowed as an input even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - internal sealed class DisallowNullAttribute : Attribute { } - - /// Specifies that an output may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class MaybeNullAttribute : Attribute { } - - /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class NotNullAttribute : Attribute { } - - /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class MaybeNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter may be null. - /// - public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } - } - - /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class NotNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } - } - - /// Specifies that the output will be non-null if the named parameter is non-null. - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] - internal sealed class NotNullIfNotNullAttribute : Attribute - { - /// Initializes the attribute with the associated parameter name. - /// - /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. - /// - public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; - - /// Gets the associated parameter name. - public string ParameterName { get; } - } - - /// Applied to a method that will never return under any circumstance. - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - internal sealed class DoesNotReturnAttribute : Attribute { } - - /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class DoesNotReturnIfAttribute : Attribute - { - /// Initializes the attribute with the specified parameter value. - /// - /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to - /// the associated parameter matches this value. - /// - public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; - - /// Gets the condition parameter value. - public bool ParameterValue { get; } - } - - /// Specifies that the method or property will ensure that the listed field and property members have not-null values. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullAttribute : Attribute - { - /// Initializes the attribute with a field or property member. - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullAttribute(string member) => Members = new[] { member }; - - /// Initializes the attribute with the list of field and property members. - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullAttribute(params string[] members) => Members = members; - - /// Gets field or property member names. - public string[] Members { get; } - } - - /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - internal sealed class MemberNotNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition and a field or property member. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, string member) - { - ReturnValue = returnValue; - Members = new[] { member }; - } - - /// Initializes the attribute with the specified return value condition and list of field and property members. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, params string[] members) - { - ReturnValue = returnValue; - Members = members; - } - - /// Gets the return value condition. - public bool ReturnValue { get; } - - /// Gets field or property member names. - public string[] Members { get; } - } -} -#endif diff --git a/src/NLog/Internal/PathHelpers.cs b/src/NLog/Internal/PathHelpers.cs index 967b54c15d..237c23ee7c 100644 --- a/src/NLog/Internal/PathHelpers.cs +++ b/src/NLog/Internal/PathHelpers.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System.IO; @@ -48,12 +46,12 @@ internal static class PathHelpers /// internal static string CombinePaths(string path, string dir, string file) { - if (dir != null) + if (!string.IsNullOrEmpty(dir)) { path = Path.Combine(path, dir); } - if (file != null) + if (!string.IsNullOrEmpty(file)) { path = Path.Combine(path, file); } diff --git a/src/NLog/Internal/PropertyHelper.cs b/src/NLog/Internal/PropertyHelper.cs index 5959c66dd0..079df40118 100644 --- a/src/NLog/Internal/PropertyHelper.cs +++ b/src/NLog/Internal/PropertyHelper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; @@ -364,7 +362,7 @@ private static bool TryFlatListConversion(object obj, PropertyInfo propInfo, str try { - if (TryCreateCollectionObject(obj, propInfo, out var newList, out var collectionAddMethod, out var propertyType)) + if (TryCreateCollectionObject(obj, propInfo, out var newList, out var collectionAddMethod, out var propertyType) && collectionAddMethod != null && propertyType != null) { var values = valueRaw.SplitQuoted(',', '\'', '\\'); foreach (var value in values) @@ -395,7 +393,7 @@ private static bool TryFlatListConversion(object obj, PropertyInfo propInfo, str [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2072")] [UnconditionalSuppressMessage("Trimming - Allow converting option-values from config", "IL2075")] - private static bool TryCreateCollectionObject(object obj, PropertyInfo propInfo, out object? collectionObject, [NotNullWhen(returnValue: true)] out MethodInfo? collectionAddMethod, [NotNullWhen(returnValue: true)] out Type? collectionItemType) + private static bool TryCreateCollectionObject(object obj, PropertyInfo propInfo, out object? collectionObject, out MethodInfo? collectionAddMethod, out Type? collectionItemType) { collectionObject = null; collectionAddMethod = null; @@ -481,7 +479,7 @@ private static bool TryCreateCollectionObject(object obj, PropertyInfo propInfo, return TryCreateTypeCollection(collectionType, out collectionObject, out collectionAddMethod, out collectionItemType); } - private static bool TryCreateListCollection(Type collectionType, [NotNullWhen(returnValue: true)] out object? collectionObject, [NotNullWhen(returnValue: true)] out MethodInfo? collectionAddMethod, [NotNullWhen(returnValue: true)] out Type? collectionItemType) + private static bool TryCreateListCollection(Type collectionType, out object? collectionObject, out MethodInfo? collectionAddMethod, out Type? collectionItemType) { if (collectionType.IsAssignableFrom(typeof(List))) { @@ -497,7 +495,7 @@ private static bool TryCreateListCollection(Type collectionType, [NotNullWhen return false; } - private static bool TryCreateHashSetCollection(Type collectionType, [NotNullWhen(returnValue: true)] out object? collectionObject, [NotNullWhen(returnValue: true)] out MethodInfo? collectionAddMethod, [NotNullWhen(returnValue: true)] out Type? collectionItemType) + private static bool TryCreateHashSetCollection(Type collectionType, out object? collectionObject, out MethodInfo? collectionAddMethod, out Type? collectionItemType) { if (collectionType.IsAssignableFrom(typeof(HashSet))) { diff --git a/src/NLog/Internal/ReflectionHelpers.cs b/src/NLog/Internal/ReflectionHelpers.cs index a1d7b55f28..9dcf5c7379 100644 --- a/src/NLog/Internal/ReflectionHelpers.cs +++ b/src/NLog/Internal/ReflectionHelpers.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; @@ -65,7 +63,7 @@ public static bool IsStaticClass(this Type type) /// /// Object instance, use null for static methods. /// Complete list of parameters that matches the method, including optional/default parameters. - public delegate object? LateBoundMethod(object target, object[] arguments); + public delegate object? LateBoundMethod(object target, object?[] arguments); /// /// Creates an optimized delegate for calling the MethodInfo using Expression-Trees @@ -105,10 +103,10 @@ private static LateBoundMethod CompileLateBoundMethod(MethodInfo methodInfo) // ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...) if (methodCall.Type == typeof(void)) { - var lambda = Expression.Lambda>( + var lambda = Expression.Lambda>( methodCall, instanceParameter, parametersParameter); - Action execute = lambda.Compile(); + Action execute = lambda.Compile(); return (instance, parameters) => { execute(instance, parameters); diff --git a/src/NLog/Internal/SetupLoadConfigurationBuilder.cs b/src/NLog/Internal/SetupLoadConfigurationBuilder.cs index a9b872845d..fcbcfc2b46 100644 --- a/src/NLog/Internal/SetupLoadConfigurationBuilder.cs +++ b/src/NLog/Internal/SetupLoadConfigurationBuilder.cs @@ -31,18 +31,16 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using NLog.Config; internal sealed class SetupLoadConfigurationBuilder : ISetupLoadConfigurationBuilder { - internal SetupLoadConfigurationBuilder(LogFactory logFactory, LoggingConfiguration configuration) + internal SetupLoadConfigurationBuilder(LogFactory logFactory, LoggingConfiguration? configuration) { LogFactory = logFactory; - Configuration = configuration; + _configuration = configuration; } public LogFactory LogFactory { get; } diff --git a/src/NLog/Internal/ThreadSafeDictionary.cs b/src/NLog/Internal/ThreadSafeDictionary.cs index 1c3f55bfdc..cc1ec1a514 100644 --- a/src/NLog/Internal/ThreadSafeDictionary.cs +++ b/src/NLog/Internal/ThreadSafeDictionary.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System.Collections; diff --git a/src/NLog/Internal/TimeoutContinuation.cs b/src/NLog/Internal/TimeoutContinuation.cs index 15db0ef325..4d8a174b7e 100644 --- a/src/NLog/Internal/TimeoutContinuation.cs +++ b/src/NLog/Internal/TimeoutContinuation.cs @@ -36,7 +36,6 @@ namespace NLog.Internal { using System; - using System.ComponentModel; using System.Threading; using NLog.Common; diff --git a/src/NLog/Internal/XmlParser.cs b/src/NLog/Internal/XmlParser.cs index 0a2ee2ba28..a4a240272d 100644 --- a/src/NLog/Internal/XmlParser.cs +++ b/src/NLog/Internal/XmlParser.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; @@ -73,7 +71,7 @@ public XmlParserElement LoadDocument(out IList? processingInst Stack stack = new Stack(); - var currentRoot = new XmlParserElement(rootName, rootAttributes); + var currentRoot = new XmlParserElement(rootName ?? string.Empty, rootAttributes); stack.Push(currentRoot); bool stillReading = true; @@ -103,7 +101,7 @@ public XmlParserElement LoadDocument(out IList? processingInst if (TryReadStartElement(out var elementName, out var elementAttributes)) { stillReading = true; - currentRoot = new XmlParserElement(elementName, elementAttributes); + currentRoot = new XmlParserElement(elementName ?? string.Empty, elementAttributes); stack.Peek().AddChild(currentRoot); stack.Push(currentRoot); } @@ -140,7 +138,7 @@ public bool TryReadProcessingInstructions(out IList? processin if (!TryBeginReadStartElement(out var instructionName, processingInstruction: true)) throw new XmlParserException("Invalid XML document. Cannot parse XML processing instruction"); - if (string.IsNullOrEmpty(instructionName) || instructionName.Length == 1 || instructionName[0] != '?') + if (instructionName is null || string.IsNullOrEmpty(instructionName) || instructionName.Length == 1 || instructionName[0] != '?') throw new XmlParserException("Invalid XML document. Cannot parse XML processing instruction"); instructionName = instructionName.Substring(1); @@ -167,7 +165,7 @@ public bool TryReadProcessingInstructions(out IList? processin /// /// True if start element was found. /// Something unexpected has failed. - public bool TryReadStartElement([NotNullWhen(returnValue: true)] out string? name, out List>? attributes) + public bool TryReadStartElement(out string? name, out List>? attributes) { SkipWhiteSpaces(); @@ -372,7 +370,7 @@ private bool TryReadAttributes(out List>? attribute /// Consumer of this method should handle safe position. /// /// Something unexpected has failed. - private bool TryBeginReadStartElement([NotNullWhen(returnValue: true)] out string? name, bool processingInstruction = false) + private bool TryBeginReadStartElement(out string? name, bool processingInstruction = false) { if (_xmlSource.Current != '<' || _xmlSource.Peek() == '/' || _xmlSource.Peek() == '!') { diff --git a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs index f7c404e21d..de23d65b7e 100644 --- a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs @@ -53,16 +53,16 @@ namespace NLog.LayoutRenderers public class AllEventPropertiesLayoutRenderer : LayoutRenderer { private string _format; - private string _beforeKey; - private string _afterKey; - private string _afterValue; + private string? _beforeKey; + private string? _afterKey; + private string? _afterValue; /// /// Initializes a new instance of the class. /// public AllEventPropertiesLayoutRenderer() { - Format = "[key]=[value]"; + _format = Format = "[key]=[value]"; Exclude = new HashSet(StringComparer.OrdinalIgnoreCase); } @@ -80,7 +80,7 @@ public string Separator } } private string _separator = ", "; - private string _separatorOriginal; + private string _separatorOriginal = ", "; /// /// Get or set if empty values should be included. @@ -109,7 +109,7 @@ public string Separator /// /// Disables to capture ScopeContext-properties from active thread context /// - public LayoutRenderer DisableThreadAgnostic => IncludeScopeProperties ? _disableThreadAgnostic : null; + public LayoutRenderer? DisableThreadAgnostic => IncludeScopeProperties ? _disableThreadAgnostic : null; private static readonly LayoutRenderer _disableThreadAgnostic = new FuncLayoutRenderer(string.Empty, (evt, cfg) => string.Empty); /// @@ -172,7 +172,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) bool includeSeparator = false; if (logEvent.HasProperties) { - using (var propertyEnumerator = logEvent.TryCreatePropertiesInternal().GetPropertyEnumerator()) + using (var propertyEnumerator = logEvent.CreatePropertiesInternal().GetPropertyEnumerator()) { while (propertyEnumerator.MoveNext()) { @@ -201,9 +201,9 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) } } - private bool AppendProperty(StringBuilder builder, object propertyKey, object propertyValue, string propertyFormat, IFormatProvider formatProvider, bool includeSeparator, bool checkForExclude, bool nonStandardFormat) + private bool AppendProperty(StringBuilder builder, object propertyKey, object? propertyValue, string? propertyFormat, IFormatProvider? formatProvider, bool includeSeparator, bool checkForExclude, bool nonStandardFormat) { - if (!IncludeEmptyValues && IsEmptyPropertyValue(propertyValue)) + if (!IncludeEmptyValues && StringHelpers.IsNullOrEmptyString(propertyValue)) return false; if (checkForExclude && Exclude.Contains(propertyKey as string ?? string.Empty)) @@ -233,15 +233,5 @@ private bool AppendProperty(StringBuilder builder, object propertyKey, object pr return true; } - - private static bool IsEmptyPropertyValue(object value) - { - if (value is string s) - { - return string.IsNullOrEmpty(s); - } - - return value is null; - } } } diff --git a/src/NLog/LayoutRenderers/AppDomainLayoutRenderer.cs b/src/NLog/LayoutRenderers/AppDomainLayoutRenderer.cs index 0ebff4b138..40b9778675 100644 --- a/src/NLog/LayoutRenderers/AppDomainLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AppDomainLayoutRenderer.cs @@ -98,7 +98,7 @@ protected override void CloseLayoutRenderer() base.CloseLayoutRenderer(); } - private string _assemblyName; + private string? _assemblyName; /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/LayoutRenderers/AppSettingLayoutRenderer.cs b/src/NLog/LayoutRenderers/AppSettingLayoutRenderer.cs index 8f0348f092..69a8bd3424 100644 --- a/src/NLog/LayoutRenderers/AppSettingLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AppSettingLayoutRenderer.cs @@ -55,14 +55,14 @@ namespace NLog.LayoutRenderers [ThreadAgnostic] public sealed class AppSettingLayoutRenderer : LayoutRenderer, IStringValueRenderer { - private string _connectionStringName = null; + private string? _connectionStringName = null; /// /// The AppSetting item-name /// /// [DefaultParameter] - public string Item { get; set; } + public string Item { get; set; } = string.Empty; /// /// Obsolete and replaced by with NLog v4.6. @@ -77,7 +77,7 @@ public sealed class AppSettingLayoutRenderer : LayoutRenderer, IStringValueRende /// The default value to render if the AppSetting value is null. /// /// - public string Default { get; set; } + public string Default { get; set; } = string.Empty; internal IConfigurationManager ConfigurationManager { get; set; } = new ConfigurationManager(); @@ -107,9 +107,9 @@ private string GetStringValue() if (string.IsNullOrEmpty(Item)) return Default; - string value = _connectionStringName != null ? - ConfigurationManager.LookupConnectionString(_connectionStringName)?.ConnectionString : - ConfigurationManager.AppSettings[Item]; + var value = _connectionStringName is null ? + ConfigurationManager.AppSettings[Item] : + ConfigurationManager.LookupConnectionString(_connectionStringName)?.ConnectionString; return value ?? Default ?? string.Empty; } diff --git a/src/NLog/LayoutRenderers/AssemblyVersionLayoutRenderer.cs b/src/NLog/LayoutRenderers/AssemblyVersionLayoutRenderer.cs index ffa885b2fe..5281804314 100644 --- a/src/NLog/LayoutRenderers/AssemblyVersionLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AssemblyVersionLayoutRenderer.cs @@ -58,7 +58,7 @@ public class AssemblyVersionLayoutRenderer : LayoutRenderer /// /// [DefaultParameter] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; /// /// Gets or sets the type of assembly version to retrieve. @@ -75,7 +75,7 @@ public class AssemblyVersionLayoutRenderer : LayoutRenderer /// /// public string Default { get => _default ?? GenerateDefaultValue(); set => _default = value; } - private string _default; + private string? _default; /// /// Gets or sets the custom format of the assembly version output. @@ -103,14 +103,7 @@ protected override void InitializeLayoutRenderer() base.InitializeLayoutRenderer(); } - /// - protected override void CloseLayoutRenderer() - { - _assemblyVersion = null; - base.CloseLayoutRenderer(); - } - - private string _assemblyVersion; + private string? _assemblyVersion; /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) @@ -125,18 +118,18 @@ private string ApplyFormatToVersion(string version) { if (version is null) { - return _default; + return Default; } else if (StringHelpers.IsNullOrWhiteSpace(version)) { - return _default ?? GenerateDefaultValue(); + return Default; } else if (version == "0.0.0.0" && _default != null) { - return _default; + return Default; } - if (Format.Equals(DefaultFormat, StringComparison.OrdinalIgnoreCase) || string.IsNullOrEmpty(version)) + if (Format.Equals(DefaultFormat, StringComparison.OrdinalIgnoreCase)) { return version; } @@ -164,13 +157,13 @@ private string GetVersion() switch (Type) { case AssemblyVersionType.File: - return assembly?.GetFirstCustomAttribute()?.Version; + return assembly?.GetFirstCustomAttribute()?.Version ?? string.Empty; case AssemblyVersionType.Informational: - return assembly?.GetFirstCustomAttribute()?.InformationalVersion; + return assembly?.GetFirstCustomAttribute()?.InformationalVersion ?? string.Empty; default: - return assembly?.GetName().Version?.ToString(); + return assembly?.GetName()?.Version?.ToString() ?? string.Empty; } } catch (Exception ex) @@ -178,7 +171,7 @@ private string GetVersion() NLog.Common.InternalLogger.Warn(ex, "${assembly-version} - Failed to load assembly {0}", Name); if (ex.MustBeRethrown()) throw; - return null; + return string.Empty; } } diff --git a/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs index 9d2d3a32a3..4108a3bbbb 100644 --- a/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs @@ -89,13 +89,13 @@ internal BaseDirLayoutRenderer(IAppEnvironment appEnvironment) /// Gets or sets the name of the file to be Path.Combine()'d with the base directory. /// /// - public string File { get; set; } + public string File { get; set; } = string.Empty; /// /// Gets or sets the name of the directory to be Path.Combine()'d with the base directory. /// /// - public string Dir { get; set; } + public string Dir { get; set; } = string.Empty; /// protected override void InitializeLayoutRenderer() diff --git a/src/NLog/LayoutRenderers/CallSiteLineNumberLayoutRenderer.cs b/src/NLog/LayoutRenderers/CallSiteLineNumberLayoutRenderer.cs index bbded1ad8e..1654938d38 100644 --- a/src/NLog/LayoutRenderers/CallSiteLineNumberLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CallSiteLineNumberLayoutRenderer.cs @@ -71,7 +71,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) builder.AppendInvariant(lineNumber.Value); } - bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) + bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object? value) { value = GetLineNumber(logEvent); return true; diff --git a/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs b/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs index 28d3be6bf9..a79991933e 100644 --- a/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs @@ -69,7 +69,7 @@ public class CounterLayoutRenderer : LayoutRenderer, IRawValue /// Gets or sets the name of the sequence. Different named sequences can have individual values. /// /// - public Layout Sequence { get; set; } + public Layout? Sequence { get; set; } /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/LayoutRenderers/CurrentDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/CurrentDirLayoutRenderer.cs index e009d26f55..4e84d16011 100644 --- a/src/NLog/LayoutRenderers/CurrentDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CurrentDirLayoutRenderer.cs @@ -53,13 +53,13 @@ public class CurrentDirLayoutRenderer : LayoutRenderer, IStringValueRenderer /// Gets or sets the name of the file to be Path.Combine()'d with the current directory. /// /// - public string File { get; set; } + public string File { get; set; } = string.Empty; /// /// Gets or sets the name of the directory to be Path.Combine()'d with the current directory. /// /// - public string Dir { get; set; } + public string Dir { get; set; } = string.Empty; /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/LayoutRenderers/DateLayoutRenderer.cs b/src/NLog/LayoutRenderers/DateLayoutRenderer.cs index d286cedc2d..49761c23bb 100644 --- a/src/NLog/LayoutRenderers/DateLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/DateLayoutRenderer.cs @@ -55,7 +55,7 @@ public class DateLayoutRenderer : LayoutRenderer, IRawValue, IStringValueRendere /// public DateLayoutRenderer() { - Format = "yyyy/MM/dd HH:mm:ss.fff"; + _format = Format = "yyyy/MM/dd HH:mm:ss.fff"; } /// @@ -85,7 +85,7 @@ public string Format private string _format; private const string _lowTimeResolutionChars = "YyMDdHh"; - private CachedDateFormatted _cachedDateFormatted = null; + private CachedDateFormatted? _cachedDateFormatted = null; private bool _utcRoundRoundTrip; /// @@ -126,7 +126,7 @@ bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) => GetStringValue(GetValue(logEvent), GetFormatProvider(logEvent, Culture)); - private string GetStringValue(DateTime timestamp, IFormatProvider formatProvider) + private string GetStringValue(DateTime timestamp, IFormatProvider? formatProvider) { var cachedDateFormatted = _cachedDateFormatted; if (!ReferenceEquals(formatProvider, CultureInfo.InvariantCulture)) diff --git a/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs b/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs index f36a0c5a75..bb6eaed0d3 100644 --- a/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs @@ -54,13 +54,13 @@ public class EnvironmentLayoutRenderer : LayoutRenderer, IStringValueRenderer /// /// [DefaultParameter] - public string Variable { get; set; } + public string Variable { get; set; } = string.Empty; /// /// Gets or sets the default value to be used when the environment variable is not set. /// /// - public string Default { get; set; } + public string Default { get; set; } = string.Empty; private System.Collections.Generic.KeyValuePair _cachedValue; @@ -79,7 +79,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) GetSimpleLayout()?.Render(logEvent, builder); } - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) + string? IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) { var simpleLayout = GetSimpleLayout(); if (simpleLayout is null) @@ -89,7 +89,7 @@ string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) return null; } - private SimpleLayout GetSimpleLayout() + private SimpleLayout? GetSimpleLayout() { if (Variable != null) { diff --git a/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs b/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs index 08a57de95a..95b97d043c 100644 --- a/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs @@ -53,7 +53,7 @@ namespace NLog.LayoutRenderers public class EventPropertiesLayoutRenderer : LayoutRenderer, IRawValue, IStringValueRenderer { private ObjectReflectionCache ObjectReflectionCache => _objectReflectionCache ?? (_objectReflectionCache = new ObjectReflectionCache(LoggingConfiguration.GetServiceProvider())); - private ObjectReflectionCache _objectReflectionCache; + private ObjectReflectionCache? _objectReflectionCache; private ObjectPropertyPath _objectPropertyPath; /// @@ -61,14 +61,14 @@ public class EventPropertiesLayoutRenderer : LayoutRenderer, IRawValue, IStringV /// /// [DefaultParameter] - public string Item { get => _item?.ToString(); set => _item = (value != null && IgnoreCase) ? new PropertiesDictionary.IgnoreCasePropertyKey(value) : (object)value; } - private object _item; + public string Item { get => _item?.ToString() ?? string.Empty; set => _item = (value is null || !IgnoreCase) ? (value ?? string.Empty) : new PropertiesDictionary.IgnoreCasePropertyKey(value); } + private object _item = string.Empty; /// /// Format string for conversion from object to string. /// /// - public string Format { get; set; } + public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. @@ -82,7 +82,7 @@ public class EventPropertiesLayoutRenderer : LayoutRenderer, IRawValue, IStringV /// public string ObjectPath { - get => _objectPropertyPath.Value; + get => _objectPropertyPath.Value ?? string.Empty; set => _objectPropertyPath.Value = value; } @@ -98,7 +98,7 @@ public bool IgnoreCase if (value != _ignoreCase) { _ignoreCase = value; - Item = _item?.ToString(); + Item = _item?.ToString() ?? string.Empty; } } } @@ -109,7 +109,7 @@ protected override void InitializeLayoutRenderer() { base.InitializeLayoutRenderer(); - if (string.IsNullOrEmpty(Item)) + if (StringHelpers.IsNullOrEmptyString(_item)) throw new NLogConfigurationException("EventProperty-LayoutRenderer Item-property must be assigned. Lookup blank value not supported."); } @@ -122,15 +122,27 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) } } - bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) + bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object? value) { TryGetValue(logEvent, out value); return true; } - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) => GetStringValue(logEvent); + string? IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) + { + if (!MessageTemplates.ValueFormatter.FormatAsJson.Equals(Format)) + { + if (TryGetValue(logEvent, out var value)) + { + string stringValue = FormatHelper.TryFormatToString(value, Format, GetFormatProvider(logEvent, Culture)); + return stringValue; + } + return string.Empty; + } + return null; + } - private bool TryGetValue(LogEventInfo logEvent, out object value) + private bool TryGetValue(LogEventInfo logEvent, out object? value) { value = null; @@ -154,19 +166,5 @@ private bool TryGetValue(LogEventInfo logEvent, out object value) return true; } - - private string GetStringValue(LogEventInfo logEvent) - { - if (!MessageTemplates.ValueFormatter.FormatAsJson.Equals(Format)) - { - if (TryGetValue(logEvent, out var value)) - { - string stringValue = FormatHelper.TryFormatToString(value, Format, GetFormatProvider(logEvent, Culture)); - return stringValue; - } - return string.Empty; - } - return null; - } } } diff --git a/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs b/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs index 62f0b475bc..a4cd964f3b 100644 --- a/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs @@ -58,7 +58,7 @@ public class ExceptionDataLayoutRenderer : LayoutRenderer /// /// [DefaultParameter] - public string Item { get; set; } + public string Item { get; set; } = string.Empty; /// /// Gets or sets whether to render innermost Exception from @@ -70,7 +70,7 @@ public class ExceptionDataLayoutRenderer : LayoutRenderer /// Format string for conversion from object to string. /// /// - public string Format { get; set; } + public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. @@ -87,7 +87,7 @@ protected override void InitializeLayoutRenderer() throw new NLogConfigurationException("ExceptionData-LayoutRenderer Item-property must be assigned. Lookup blank value not supported."); } - private Exception GetTopException(LogEventInfo logEvent) + private Exception? GetTopException(LogEventInfo logEvent) { return BaseException ? logEvent.Exception?.GetBaseException() : logEvent.Exception; } @@ -95,7 +95,7 @@ private Exception GetTopException(LogEventInfo logEvent) /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { - Exception primaryException = GetTopException(logEvent); + var primaryException = GetTopException(logEvent); if (primaryException != null) { var value = primaryException.Data[Item]; diff --git a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs index 02f966b76d..77b6ae5ba2 100644 --- a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs @@ -52,9 +52,6 @@ namespace NLog.LayoutRenderers [ThreadAgnostic] public class ExceptionLayoutRenderer : LayoutRenderer, IRawValue { - private string _format; - private string _innerFormat = string.Empty; - private static readonly Dictionary _formatsMapping = new Dictionary(StringComparer.OrdinalIgnoreCase) { {"MESSAGE",ExceptionRenderingFormat.Message}, @@ -71,7 +68,7 @@ public class ExceptionLayoutRenderer : LayoutRenderer, IRawValue {"PROPERTIES",ExceptionRenderingFormat.Properties}, }; - private static readonly Dictionary> _renderingfunctions = new Dictionary>() + private static readonly Dictionary> _renderingfunctions = new Dictionary>() { { ExceptionRenderingFormat.Message, (layout, sb, ex, aggex) => layout.AppendMessage(sb, ex)}, { ExceptionRenderingFormat.Type, (layout, sb, ex, aggex) => layout.AppendType(sb, ex)}, @@ -99,35 +96,36 @@ public class ExceptionLayoutRenderer : LayoutRenderer, IRawValue }, StringComparer.Ordinal); private ObjectReflectionCache ObjectReflectionCache => _objectReflectionCache ?? (_objectReflectionCache = new ObjectReflectionCache(LoggingConfiguration.GetServiceProvider())); - private ObjectReflectionCache _objectReflectionCache; + private ObjectReflectionCache? _objectReflectionCache; + + private List _formats = new List(); + private List? _innerFormats; /// /// Initializes a new instance of the class. /// public ExceptionLayoutRenderer() { - Format = "TOSTRING,DATA"; - } + _format = "TOSTRING,DATA"; + } /// /// Gets or sets the format of the output. Must be a comma-separated list of exception /// properties: Message, Type, ShortType, ToString, Method, StackTrace. /// This parameter value is case-insensitive. /// - /// - /// /// [DefaultParameter] public string Format { get => _format; - set { _format = value; - Formats = CompileFormat(value, nameof(Format)); + _formats.Clear(); } } + private string _format; /// /// Gets or sets the format of the output of inner exceptions. Must be a comma-separated list of exception @@ -135,15 +133,16 @@ public string Format /// This parameter value is case-insensitive. /// /// - public string InnerFormat + public string? InnerFormat { get => _innerFormat; set { _innerFormat = value; - InnerFormats = CompileFormat(value, nameof(InnerFormat)); + _innerFormats = null; } } + private string? _innerFormat; /// /// Gets or sets the separator used to concatenate parts specified in the Format. @@ -159,7 +158,7 @@ public string Separator } } private string _separator = " "; - private string _separatorOriginal; + private string _separatorOriginal = " "; /// /// Gets or sets the separator used to concatenate exception data specified in the Format. @@ -175,7 +174,7 @@ public string ExceptionDataSeparator } } private string _exceptionDataSeparator = ";"; - private string _exceptionDataSeparatorOriginal; + private string _exceptionDataSeparatorOriginal = ";"; /// /// Gets or sets the maximum number of inner exceptions to include in the output. @@ -213,29 +212,15 @@ public string ExceptionDataSeparator /// Gets the formats of the output of inner exceptions to be rendered in target. /// /// - public List Formats - { - get; - private set; - } - - /// - /// Gets the formats of the output to be rendered in target. - /// - /// - public List InnerFormats - { - get; - private set; - } + public IEnumerable Formats => _formats; - bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) + bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object? value) { value = GetTopException(logEvent); return true; } - private Exception GetTopException(LogEventInfo logEvent) + private Exception? GetTopException(LogEventInfo logEvent) { return BaseException ? logEvent.Exception?.GetBaseException() : logEvent.Exception; } @@ -244,6 +229,10 @@ private Exception GetTopException(LogEventInfo logEvent) protected override void InitializeLayoutRenderer() { base.InitializeLayoutRenderer(); + + _formats = CompileFormat(Format, nameof(Format)); + _innerFormats = InnerFormat is null ? null : CompileFormat(InnerFormat, nameof(InnerFormat)); + if (_separatorOriginal != null) _separator = Layouts.SimpleLayout.Evaluate(_separatorOriginal, LoggingConfiguration); if (_exceptionDataSeparatorOriginal != null) @@ -253,7 +242,7 @@ protected override void InitializeLayoutRenderer() /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { - Exception primaryException = GetTopException(logEvent); + var primaryException = GetTopException(logEvent); if (primaryException != null) { int currentLevel = 0; @@ -262,7 +251,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) if (logEvent.Exception is AggregateException aggregateException) { primaryException = FlattenException ? GetPrimaryException(aggregateException) : aggregateException; - AppendException(primaryException, Formats, builder, aggregateException); + AppendException(primaryException, _formats, builder, aggregateException); if (currentLevel < MaxInnerExceptionLevel) { currentLevel = AppendInnerExceptionTree(primaryException, currentLevel, builder); @@ -275,7 +264,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) else #endif { - AppendException(primaryException, Formats, builder); + AppendException(primaryException, _formats, builder); if (currentLevel < MaxInnerExceptionLevel) { AppendInnerExceptionTree(primaryException, currentLevel, builder); @@ -346,10 +335,10 @@ private void AppendInnerException(Exception currentException, StringBuilder buil { // separate inner exceptions builder.Append(InnerExceptionSeparator); - AppendException(currentException, InnerFormats ?? Formats, builder); + AppendException(currentException, _innerFormats ?? _formats, builder); } - private void AppendException(Exception currentException, List renderFormats, StringBuilder builder, Exception aggregateException = null) + private void AppendException(Exception currentException, List renderFormats, StringBuilder builder, Exception? aggregateException = null) { int currentLength = builder.Length; foreach (ExceptionRenderingFormat renderingFormat in renderFormats) @@ -438,7 +427,7 @@ protected virtual void AppendStackTrace(StringBuilder sb, Exception ex) protected virtual void AppendToString(StringBuilder sb, Exception ex) { string exceptionMessage = string.Empty; - Exception innerException = null; + Exception? innerException = null; try { @@ -515,7 +504,7 @@ protected virtual void AppendHResult(StringBuilder sb, Exception ex) #endif } - private void AppendData(StringBuilder builder, Exception ex, Exception aggregateException) + private void AppendData(StringBuilder builder, Exception ex, Exception? aggregateException) { if (aggregateException?.Data?.Count > 0 && !ReferenceEquals(ex, aggregateException)) { @@ -598,7 +587,7 @@ protected virtual void AppendProperties(StringBuilder sb, Exception ex) /// /// Split the string and then compile into list of Rendering formats. /// - private static List CompileFormat(string formatSpecifier, string propertyName) + private List CompileFormat(string formatSpecifier, string propertyName) { List formats = new List(); string[] parts = formatSpecifier.SplitAndTrimTokens(','); @@ -612,7 +601,9 @@ private static List CompileFormat(string formatSpecifi } else { - InternalLogger.Warn("Exception-LayoutRenderer assigned unknown {0}: {1}", propertyName, s); // TODO Delay parsing to Initialize and check ThrowConfigExceptions + NLogConfigurationException ex = new NLogConfigurationException($"Exception-LayoutRenderer assigned unknown {propertyName}: {s}"); + if (ex.MustBeRethrown() || (LoggingConfiguration?.LogFactory?.ThrowConfigExceptions ?? LoggingConfiguration?.LogFactory?.ThrowExceptions) == true) + throw ex; } } return formats; diff --git a/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs b/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs index eb7a72d902..0de6528118 100644 --- a/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs @@ -44,7 +44,7 @@ namespace NLog.LayoutRenderers /// public class FuncLayoutRenderer : LayoutRenderer, IStringValueRenderer { - private readonly Func _renderMethod; + private readonly Func _renderMethod; /// /// Initializes a new instance of the class. @@ -53,6 +53,7 @@ public class FuncLayoutRenderer : LayoutRenderer, IStringValueRenderer protected FuncLayoutRenderer(string layoutRendererName) { LayoutRendererName = layoutRendererName; + _renderMethod = (evt, cfg) => string.Empty; } /// @@ -60,7 +61,7 @@ protected FuncLayoutRenderer(string layoutRendererName) /// /// Name without ${}. /// Method that renders the layout. - public FuncLayoutRenderer(string layoutRendererName, Func renderMethod) + public FuncLayoutRenderer(string layoutRendererName, Func renderMethod) { _renderMethod = Guard.ThrowIfNull(renderMethod); LayoutRendererName = layoutRendererName; @@ -69,20 +70,19 @@ public FuncLayoutRenderer(string layoutRendererName, Func /// Name used in config without ${}. E.g. "test" could be used as "${test}". /// - public string LayoutRendererName { get; set; } + public string LayoutRendererName { get; } /// /// Method that renders the layout. - /// - /// This public property will be removed in NLog 5. /// - public Func RenderMethod => _renderMethod; + [Obsolete("Public API-property was a mistake. Marked obsolete with NLog v6.0")] + public Func RenderMethod => _renderMethod; /// /// Format string for conversion from object to string. /// /// - public string Format { get; set; } + public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. @@ -97,13 +97,11 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) AppendFormattedValue(builder, logEvent, value, Format, Culture); } - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) => GetStringValue(logEvent); - - private string GetStringValue(LogEventInfo logEvent) + string? IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) { if (!MessageTemplates.ValueFormatter.FormatAsJson.Equals(Format)) { - object value = RenderValue(logEvent); + var value = RenderValue(logEvent); string stringValue = FormatHelper.TryFormatToString(value, Format, GetFormatProvider(logEvent, Culture)); return stringValue; } @@ -115,9 +113,9 @@ private string GetStringValue(LogEventInfo logEvent) /// /// The logging event. /// The value. - protected virtual object RenderValue(LogEventInfo logEvent) + protected virtual object? RenderValue(LogEventInfo logEvent) { - return _renderMethod?.Invoke(logEvent, LoggingConfiguration); + return _renderMethod.Invoke(logEvent, LoggingConfiguration); } } } diff --git a/src/NLog/LayoutRenderers/FuncThreadAgnosticLayoutRenderer.cs b/src/NLog/LayoutRenderers/FuncThreadAgnosticLayoutRenderer.cs index 1bddc0d911..82a8b5b6e4 100644 --- a/src/NLog/LayoutRenderers/FuncThreadAgnosticLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/FuncThreadAgnosticLayoutRenderer.cs @@ -48,12 +48,12 @@ internal sealed class FuncThreadAgnosticLayoutRenderer : FuncLayoutRenderer, IRa /// /// Name without ${}. /// Method that renders the layout. - public FuncThreadAgnosticLayoutRenderer(string layoutRendererName, Func renderMethod) + public FuncThreadAgnosticLayoutRenderer(string layoutRendererName, Func renderMethod) : base(layoutRendererName, renderMethod) { } - bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) + bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object? value) { value = RenderValue(logEvent); return true; diff --git a/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs b/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs index d9a0586194..d3952b7840 100644 --- a/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs @@ -49,20 +49,20 @@ namespace NLog.LayoutRenderers [ThreadAgnostic] public class GdcLayoutRenderer : LayoutRenderer, IRawValue, IStringValueRenderer { - private CachedLookup _cachedLookup = new CachedLookup(null, null); + private CachedLookup _cachedLookup = new CachedLookup(string.Empty, null); /// /// Gets or sets the name of the item. /// /// [DefaultParameter] - public string Item { get; set; } + public string Item { get; set; } = string.Empty; /// /// Format string for conversion from object to string. /// /// - public string Format { get; set; } + public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. @@ -82,33 +82,31 @@ protected override void InitializeLayoutRenderer() /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { - object value = GetValue(); + var value = GetValue(); if (value != null || !string.IsNullOrEmpty(Format)) { AppendFormattedValue(builder, logEvent, value, Format, Culture); } } - bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) + bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object? value) { value = GetValue(); return true; } - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) => GetStringValue(logEvent); - - private string GetStringValue(LogEventInfo logEvent) + string? IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) { if (!MessageTemplates.ValueFormatter.FormatAsJson.Equals(Format)) { - object value = GetValue(); + var value = GetValue(); string stringValue = FormatHelper.TryFormatToString(value, Format, GetFormatProvider(logEvent, Culture)); return stringValue; } return null; } - private object GetValue() + private object? GetValue() { var cachedLookup = _cachedLookup; var cachedDictionary = GlobalDiagnosticsContext.GetReadOnlyDict(); @@ -125,9 +123,9 @@ private object GetValue() private sealed class CachedLookup { internal readonly object CachedDictionary; - internal readonly object CachedItemValue; + internal readonly object? CachedItemValue; - public CachedLookup(object cachedDictionary, object cachedItemValue) + public CachedLookup(object cachedDictionary, object? cachedItemValue) { CachedDictionary = cachedDictionary; CachedItemValue = cachedItemValue; diff --git a/src/NLog/LayoutRenderers/HostNameLayoutRenderer.cs b/src/NLog/LayoutRenderers/HostNameLayoutRenderer.cs index 7e965310ff..28caf85f2b 100644 --- a/src/NLog/LayoutRenderers/HostNameLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/HostNameLayoutRenderer.cs @@ -51,7 +51,7 @@ namespace NLog.LayoutRenderers [ThreadAgnostic] public class HostNameLayoutRenderer : LayoutRenderer { - private string _hostName; + private string? _hostName; /// protected override void InitializeLayoutRenderer() @@ -85,7 +85,8 @@ private static string GetHostName() return TryLookupValue(() => Environment.GetEnvironmentVariable("HOSTNAME"), "HOSTNAME") ?? TryLookupValue(() => Environment.GetEnvironmentVariable("COMPUTERNAME"), "COMPUTERNAME") ?? TryLookupValue(() => Environment.GetEnvironmentVariable("MACHINENAME"), "MACHINENAME") - ?? TryLookupValue(() => Environment.MachineName, "MachineName"); + ?? TryLookupValue(() => Environment.MachineName, "MachineName") + ?? string.Empty; } /// @@ -94,11 +95,11 @@ private static string GetHostName() /// The lookup function. /// Type of the lookup. /// - private static string TryLookupValue(Func lookupFunc, string lookupType) + private static string? TryLookupValue(Func lookupFunc, string lookupType) { try { - string lookupValue = lookupFunc()?.Trim(); + var lookupValue = lookupFunc()?.Trim(); return string.IsNullOrEmpty(lookupValue) ? null : lookupValue; } catch (Exception ex) diff --git a/src/NLog/LayoutRenderers/IdentityLayoutRenderer.cs b/src/NLog/LayoutRenderers/IdentityLayoutRenderer.cs index f57888c6da..34af58b3ee 100644 --- a/src/NLog/LayoutRenderers/IdentityLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/IdentityLayoutRenderer.cs @@ -74,7 +74,7 @@ public class IdentityLayoutRenderer : LayoutRenderer /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { - IIdentity identity = GetValue(); + var identity = GetValue(); if (identity != null) { string separator = string.Empty; @@ -100,10 +100,9 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) builder.Append(identity.Name); } } - } - private static IIdentity GetValue() + private static IIdentity? GetValue() { var currentPrincipal = System.Threading.Thread.CurrentPrincipal; return currentPrincipal?.Identity; diff --git a/src/NLog/LayoutRenderers/InstallContextLayoutRenderer.cs b/src/NLog/LayoutRenderers/InstallContextLayoutRenderer.cs index 2667c43436..7cf73beb1d 100644 --- a/src/NLog/LayoutRenderers/InstallContextLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/InstallContextLayoutRenderer.cs @@ -52,7 +52,7 @@ public class InstallContextLayoutRenderer : LayoutRenderer /// /// [DefaultParameter] - public string Parameter { get; set; } + public string Parameter { get; set; } = string.Empty; /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) @@ -65,7 +65,7 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) } } - private object GetValue(LogEventInfo logEvent) + private object? GetValue(LogEventInfo logEvent) { return !logEvent.Properties.TryGetValue(Parameter, out var value) ? null : value; } diff --git a/src/NLog/LayoutRenderers/LayoutRenderer.cs b/src/NLog/LayoutRenderers/LayoutRenderer.cs index 0b05cd9c03..d0aaf82f3c 100644 --- a/src/NLog/LayoutRenderers/LayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LayoutRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.LayoutRenderers { using System; @@ -184,7 +182,7 @@ internal void RenderAppendBuilder(LogEventInfo logEvent, StringBuilder builder) /// Logging event. protected abstract void Append(StringBuilder builder, LogEventInfo logEvent); - internal void AppendFormattedValue(StringBuilder builder, LogEventInfo logEvent, object? value, string? format, CultureInfo culture) + internal void AppendFormattedValue(StringBuilder builder, LogEventInfo logEvent, object? value, string? format, CultureInfo? culture) { if (format is null && value is string stringValue) { @@ -294,7 +292,7 @@ public static void Register(string name, Func func) /// Callback that returns the value for the layout renderer. [Obsolete("Instead use LogManager.Setup().SetupExtensions(). Marked obsolete with NLog v5.2")] [EditorBrowsable(EditorBrowsableState.Never)] - public static void Register(string name, Func func) + public static void Register(string name, Func func) { Guard.ThrowIfNull(func); var layoutRenderer = new FuncLayoutRenderer(name, func); diff --git a/src/NLog/LayoutRenderers/LayoutRendererAttribute.cs b/src/NLog/LayoutRenderers/LayoutRendererAttribute.cs index 4e90b8e6be..b8cf0ea91e 100644 --- a/src/NLog/LayoutRenderers/LayoutRendererAttribute.cs +++ b/src/NLog/LayoutRenderers/LayoutRendererAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.LayoutRenderers { using System; diff --git a/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs b/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs index eb65c8fcb2..fcd5357244 100644 --- a/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs @@ -141,7 +141,7 @@ bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) return true; } - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) + string? IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) { if (Format == LevelFormat.Name) { diff --git a/src/NLog/LayoutRenderers/LiteralLayoutRenderer.cs b/src/NLog/LayoutRenderers/LiteralLayoutRenderer.cs index 9f11c85937..1156fee2a0 100644 --- a/src/NLog/LayoutRenderers/LiteralLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LiteralLayoutRenderer.cs @@ -73,7 +73,7 @@ public LiteralLayoutRenderer(string text) /// /// [DefaultParameter] - public string Text { get; set; } + public string Text { get; set; } = string.Empty; /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/LayoutRenderers/LiteralWithRawValueLayoutRenderer.cs b/src/NLog/LayoutRenderers/LiteralWithRawValueLayoutRenderer.cs index 11beba35c9..52eaaabc6b 100644 --- a/src/NLog/LayoutRenderers/LiteralWithRawValueLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LiteralWithRawValueLayoutRenderer.cs @@ -43,7 +43,7 @@ namespace NLog.LayoutRenderers internal sealed class LiteralWithRawValueLayoutRenderer : LiteralLayoutRenderer, IRawValue { private readonly bool _rawValueSuccess; - private readonly object _rawValue; + private readonly object? _rawValue; /// /// Initializes a new instance of the class. @@ -52,14 +52,14 @@ internal sealed class LiteralWithRawValueLayoutRenderer : LiteralLayoutRenderer, /// /// Fixed raw value /// This is used by the layout compiler. - public LiteralWithRawValueLayoutRenderer(string text, bool rawValueSuccess, object rawValue) + public LiteralWithRawValueLayoutRenderer(string text, bool rawValueSuccess, object? rawValue) { _rawValueSuccess = rawValueSuccess; _rawValue = rawValue; Text = text; } - bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) + bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object? value) { value = _rawValue; return _rawValueSuccess; diff --git a/src/NLog/LayoutRenderers/MachineNameLayoutRenderer.cs b/src/NLog/LayoutRenderers/MachineNameLayoutRenderer.cs index 4e9ecef707..1363ece185 100644 --- a/src/NLog/LayoutRenderers/MachineNameLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/MachineNameLayoutRenderer.cs @@ -51,7 +51,7 @@ namespace NLog.LayoutRenderers [ThreadAgnostic] public class MachineNameLayoutRenderer : LayoutRenderer { - private string _machineName; + private string? _machineName; /// protected override void InitializeLayoutRenderer() diff --git a/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs b/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs index 04281f9463..7b8379175b 100644 --- a/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs @@ -129,7 +129,7 @@ private static Exception GetPrimaryException(Exception exception) return exception; } - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) + string? IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) { if (WithException) return null; diff --git a/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs index facd70f9f7..490c3bed9c 100644 --- a/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs @@ -55,17 +55,17 @@ public class NLogDirLayoutRenderer : LayoutRenderer /// Gets or sets the name of the file to be Path.Combine()'d with the directory name. /// /// - public string File { get; set; } + public string File { get; set; } = string.Empty; /// /// Gets or sets the name of the directory to be Path.Combine()'d with the directory name. /// /// - public string Dir { get; set; } + public string Dir { get; set; } = string.Empty; private static string NLogDir => _nlogDir ?? (_nlogDir = ResolveNLogDir()); - private static string _nlogDir; - private string _nlogCombinedPath; + private static string? _nlogDir; + private string? _nlogCombinedPath; /// protected override void InitializeLayoutRenderer() @@ -74,12 +74,6 @@ protected override void InitializeLayoutRenderer() base.InitializeLayoutRenderer(); } - /// - protected override void CloseLayoutRenderer() - { - _nlogCombinedPath = null; - base.CloseLayoutRenderer(); - } /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/LayoutRenderers/ProcessDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessDirLayoutRenderer.cs index b1ec790108..9a7c74cbff 100644 --- a/src/NLog/LayoutRenderers/ProcessDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessDirLayoutRenderer.cs @@ -54,18 +54,19 @@ namespace NLog.LayoutRenderers public class ProcessDirLayoutRenderer : LayoutRenderer { private readonly string _processDir; + private string? _nlogCombinedPath; /// /// Gets or sets the name of the file to be Path.Combine()'d with the process directory. /// /// - public string File { get; set; } + public string File { get; set; } = string.Empty; /// /// Gets or sets the name of the directory to be Path.Combine()'d with the process directory. /// /// - public string Dir { get; set; } + public string Dir { get; set; } = string.Empty; /// /// Initializes a new instance of the class. @@ -83,10 +84,17 @@ internal ProcessDirLayoutRenderer(IAppEnvironment appEnvironment) _processDir = Path.GetDirectoryName(appEnvironment.CurrentProcessFilePath); } + /// + protected override void InitializeLayoutRenderer() + { + _nlogCombinedPath = null; + base.InitializeLayoutRenderer(); + } + /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { - var path = PathHelpers.CombinePaths(_processDir, Dir, File); + var path = _nlogCombinedPath ?? (_nlogCombinedPath = PathHelpers.CombinePaths(_processDir, Dir, File)); builder.Append(path); } } diff --git a/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs index da5e7e780b..caad3fb23b 100644 --- a/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs @@ -50,8 +50,8 @@ namespace NLog.LayoutRenderers [LayoutRenderer("processinfo")] public class ProcessInfoLayoutRenderer : LayoutRenderer { - private Process _process; - private ReflectionHelpers.LateBoundMethod _lateBoundPropertyGet; + private Process? _process; + private ReflectionHelpers.LateBoundMethod? _lateBoundPropertyGet; /// /// Gets or sets the property to retrieve. @@ -64,7 +64,7 @@ public class ProcessInfoLayoutRenderer : LayoutRenderer /// Gets or sets the format-string to use if the property supports it (Ex. DateTime / TimeSpan / Enum) /// /// - public string Format { get; set; } + public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. @@ -98,16 +98,14 @@ protected override void CloseLayoutRenderer() /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { - var value = GetValue(); + if (_process is null || _lateBoundPropertyGet is null) + return; + + var value = _lateBoundPropertyGet.Invoke(_process, ArrayHelper.Empty()); if (value != null) { AppendFormattedValue(builder, logEvent, value, Format, Culture); } } - - private object GetValue() - { - return _lateBoundPropertyGet?.Invoke(_process, null); - } } } diff --git a/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs index a1098657b8..a98a5de750 100644 --- a/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs @@ -57,7 +57,7 @@ public sealed class ScopeContextIndentLayoutRenderer : LayoutRenderer /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { - string indent = null; + string? indent = null; var messages = ScopeContext.GetAllNestedStateList(); for (int i = 0; i < messages.Count; ++i) diff --git a/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs index 701d5cd0d3..3ef2037f58 100644 --- a/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs @@ -77,13 +77,13 @@ public string Separator } } private string _separator = " "; - private string _separatorOriginal; + private string _separatorOriginal = " "; /// /// Gets or sets how to format each nested state. Ex. like JSON = @ /// /// - public string Format { get; set; } + public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. @@ -134,19 +134,23 @@ private void AppendNestedStates(StringBuilder builder, IList messages, i { bool formatAsJson = MessageTemplates.ValueFormatter.FormatAsJson.Equals(Format); - string separator = null; - string itemSeparator = null; + string separator = _separator ?? string.Empty; + string itemSeparator = separator; if (formatAsJson) { - separator = _separator ?? string.Empty; + if (itemSeparator == " ") + itemSeparator = ", "; + else if (string.IsNullOrEmpty(itemSeparator)) + itemSeparator = ","; + else + itemSeparator = "," + itemSeparator; builder.Append('['); builder.Append(separator); - itemSeparator = "," + separator; } try { - string currentSeparator = null; + string? currentSeparator = null; for (int i = endPos - 1; i >= startPos; --i) { builder.Append(currentSeparator); @@ -156,7 +160,7 @@ private void AppendNestedStates(StringBuilder builder, IList messages, i builder.Append(Convert.ToString(messages[i])); // Special support for Microsoft Extension Logging ILogger.BeginScope else AppendFormattedValue(builder, logEvent, messages[i], Format, Culture); - currentSeparator = itemSeparator ?? _separator ?? string.Empty; + currentSeparator = itemSeparator; } } finally @@ -205,7 +209,7 @@ private void AppendJsonFormattedValue(object nestedState, IFormatProvider format } } - bool AppendJsonProperty(string propertyName, object propertyValue, StringBuilder builder, string itemSeparator) + bool AppendJsonProperty(string propertyName, object? propertyValue, StringBuilder builder, string itemSeparator) { if (string.IsNullOrEmpty(propertyName)) return false; @@ -226,7 +230,7 @@ bool AppendJsonProperty(string propertyName, object propertyValue, StringBuilder return true; } - bool HasUniqueCollectionKeys(IEnumerable> propertyList) + private static bool HasUniqueCollectionKeys(IEnumerable> propertyList) { if (propertyList is IDictionary) { diff --git a/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs index 2151b340d4..ffa22155a4 100644 --- a/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs @@ -55,13 +55,13 @@ public sealed class ScopeContextPropertyLayoutRenderer : LayoutRenderer, IString /// /// [DefaultParameter] - public string Item { get; set; } + public string Item { get; set; } = string.Empty; /// /// Format string for conversion from object to string. /// /// - public string Format { get; set; } + public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. @@ -85,20 +85,18 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) AppendFormattedValue(builder, logEvent, value, Format, Culture); } - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) => GetStringValue(logEvent); - - private string GetStringValue(LogEventInfo logEvent) + string? IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) { if (!MessageTemplates.ValueFormatter.FormatAsJson.Equals(Format)) { - object value = GetValue(); + var value = GetValue(); string stringValue = FormatHelper.TryFormatToString(value, Format, GetFormatProvider(logEvent, Culture)); return stringValue; } return null; } - private object GetValue() + private object? GetValue() { ScopeContext.TryGetProperty(Item, out var value); return value; diff --git a/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs index 17bf73a235..d47d1f7194 100644 --- a/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs @@ -74,7 +74,7 @@ public sealed class ScopeContextTimingLayoutRenderer : LayoutRenderer /// When Format has not been specified, then it will render TimeSpan.TotalMilliseconds /// /// - public string Format { get; set; } + public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. diff --git a/src/NLog/LayoutRenderers/SpecialFolderLayoutRenderer.cs b/src/NLog/LayoutRenderers/SpecialFolderLayoutRenderer.cs index 777ac6d3da..04552905b8 100644 --- a/src/NLog/LayoutRenderers/SpecialFolderLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/SpecialFolderLayoutRenderer.cs @@ -74,13 +74,13 @@ public class SpecialFolderLayoutRenderer : LayoutRenderer /// Gets or sets the name of the file to be Path.Combine()'d with the directory name. /// /// - public string File { get; set; } + public string File { get; set; } = string.Empty; /// /// Gets or sets the name of the directory to be Path.Combine()'d with the directory name. /// /// - public string Dir { get; set; } + public string Dir { get; set; } = string.Empty; /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) diff --git a/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs b/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs index 2fec0d112f..1eb5cd8c78 100644 --- a/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs @@ -81,7 +81,7 @@ public string Separator } } private string _separator = " => "; - private string _separatorOriginal; + private string _separatorOriginal = " => "; /// /// Logger should capture StackTrace, if it was not provided manually @@ -180,7 +180,7 @@ public StackFrameList(StackTrace stackTrace, int startingFrame, int endingFrame, private void AppendRaw(StringBuilder builder, StackFrameList stackFrameList) { - string separator = null; + string? separator = null; for (int i = 0; i < stackFrameList.Count; ++i) { builder.Append(separator); diff --git a/src/NLog/LayoutRenderers/TempDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/TempDirLayoutRenderer.cs index c552acd8b5..de23cd6756 100644 --- a/src/NLog/LayoutRenderers/TempDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/TempDirLayoutRenderer.cs @@ -51,35 +51,33 @@ namespace NLog.LayoutRenderers [ThreadAgnostic] public class TempDirLayoutRenderer : LayoutRenderer { - private static string tempDir; + private static string TempDir => _tempDir ?? (_tempDir = Path.GetTempPath()); + private static string? _tempDir; + private string? _nlogCombinedPath; /// /// Gets or sets the name of the file to be Path.Combine()'d with the directory name. /// /// - public string File { get; set; } + public string File { get; set; } = string.Empty; /// /// Gets or sets the name of the directory to be Path.Combine()'d with the directory name. /// /// - public string Dir { get; set; } + public string Dir { get; set; } = string.Empty; /// protected override void InitializeLayoutRenderer() { - if (tempDir is null) - { - tempDir = Path.GetTempPath(); // Can throw exception - } - + _nlogCombinedPath = null; base.InitializeLayoutRenderer(); } /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { - var path = PathHelpers.CombinePaths(tempDir, Dir, File); + var path = _nlogCombinedPath ?? (_nlogCombinedPath = PathHelpers.CombinePaths(TempDir, Dir, File)); builder.Append(path); } } diff --git a/src/NLog/LayoutRenderers/VariableLayoutRenderer.cs b/src/NLog/LayoutRenderers/VariableLayoutRenderer.cs index aa5de0e0d6..2fc9c0cabd 100644 --- a/src/NLog/LayoutRenderers/VariableLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/VariableLayoutRenderer.cs @@ -52,20 +52,20 @@ public class VariableLayoutRenderer : LayoutRenderer /// /// [DefaultParameter] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; /// /// Gets or sets the default value to be used when the variable is not set. /// /// Not used if Name is null /// - public string Default { get; set; } + public string Default { get; set; } = string.Empty; /// /// Gets the configuration variable layout matching the configured Name /// /// Mostly relevant for the scanning of active NLog Layouts (Ex. CallSite capture) - public Layout ActiveLayout => TryGetLayout(out var layout) ? layout : null; + public Layout? ActiveLayout => TryGetLayout(out var layout) ? layout : null; /// protected override void InitializeLayoutRenderer() @@ -85,7 +85,7 @@ protected override void InitializeLayoutRenderer() /// /// Try lookup the configuration variable layout matching the configured Name /// - private bool TryGetLayout(out Layout layout) + private bool TryGetLayout(out Layout? layout) { layout = null; //Note: don't use LogManager (locking, recursion) diff --git a/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs index 60b063bd50..467d348228 100644 --- a/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs @@ -69,8 +69,8 @@ public enum ClearCacheOption } private readonly object _lockObject = new object(); - private string _cachedValue; - private string _renderedCacheKey; + private string? _cachedValue; + private string? _renderedCacheKey; private DateTime _cachedValueExpires; private TimeSpan? _cachedValueTimeout; @@ -90,7 +90,7 @@ public enum ClearCacheOption /// Cachekey. If the cachekey changes, resets the value. For example, the cachekey would be the current day.s /// /// - public Layout CacheKey { get; set; } + public Layout? CacheKey { get; set; } /// /// Gets or sets a value indicating how many seconds the value should stay cached until it expires @@ -160,7 +160,7 @@ protected override string RenderInner(LogEventInfo logEvent) } } - string LookupValidCachedValue(LogEventInfo logEvent, string newCacheKey) + string? LookupValidCachedValue(LogEventInfo logEvent, string newCacheKey) { if (_renderedCacheKey != newCacheKey) return null; @@ -171,6 +171,6 @@ string LookupValidCachedValue(LogEventInfo logEvent, string newCacheKey) return _cachedValue; } - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) => Cached ? RenderInner(logEvent) : null; + string? IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) => Cached ? RenderInner(logEvent) : null; } } diff --git a/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs index 6221a8e67c..a2e05745bb 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs @@ -52,7 +52,7 @@ namespace NLog.LayoutRenderers.Wrappers public sealed class ObjectPathRendererWrapper : WrapperLayoutRendererBase, IRawValue { private ObjectReflectionCache ObjectReflectionCache => _objectReflectionCache ?? (_objectReflectionCache = new ObjectReflectionCache(LoggingConfiguration.GetServiceProvider())); - private ObjectReflectionCache _objectReflectionCache; + private ObjectReflectionCache? _objectReflectionCache; private ObjectPropertyPath _objectPropertyPath; /// @@ -73,7 +73,7 @@ public string Path /// public string ObjectPath { - get => _objectPropertyPath.Value; + get => _objectPropertyPath.Value ?? string.Empty; set => _objectPropertyPath.Value = value; } @@ -81,7 +81,7 @@ public string ObjectPath /// Format string for conversion from object to string. /// /// - public string Format { get; set; } + public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. @@ -98,15 +98,15 @@ protected override string Transform(string text) /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) { - if (TryGetRawPropertyValue(logEvent, out object propertyValue)) + if (TryGetRawPropertyValue(logEvent, out var propertyValue)) { AppendFormattedValue(builder, logEvent, propertyValue, Format, Culture); } } - private bool TryGetRawPropertyValue(LogEventInfo logEvent, out object propertyValue) + private bool TryGetRawPropertyValue(LogEventInfo logEvent, out object? propertyValue) { - if (Inner?.TryGetRawValue(logEvent, out var rawValue) == true && TryGetPropertyValue(rawValue, out propertyValue)) + if (Inner?.TryGetRawValue(logEvent, out var rawValue) == true && rawValue != null && TryGetPropertyValue(rawValue, out propertyValue)) return true; propertyValue = null; @@ -117,11 +117,11 @@ private bool TryGetRawPropertyValue(LogEventInfo logEvent, out object propertyVa /// Lookup property-value from source object based on /// /// Could resolve property-value? - public bool TryGetPropertyValue(object sourceObject, out object propertyValue) + public bool TryGetPropertyValue(object sourceObject, out object? propertyValue) { return ObjectReflectionCache.TryGetObjectProperty(sourceObject, _objectPropertyPath.PathNames, out propertyValue); } - bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) => TryGetRawPropertyValue(logEvent, out value); + bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object? value) => TryGetRawPropertyValue(logEvent, out value); } } diff --git a/src/NLog/LayoutRenderers/Wrappers/OnExceptionLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/OnExceptionLayoutRendererWrapper.cs index 97f1f4f84d..9b7d3fae8f 100644 --- a/src/NLog/LayoutRenderers/Wrappers/OnExceptionLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/OnExceptionLayoutRendererWrapper.cs @@ -52,7 +52,7 @@ public sealed class OnExceptionLayoutRendererWrapper : WrapperLayoutRendererBase /// If is not found, print this layout. /// /// - public Layout Else { get; set; } + public Layout Else { get; set; } = Layout.Empty; /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) diff --git a/src/NLog/LayoutRenderers/Wrappers/OnHasPropertiesLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/OnHasPropertiesLayoutRendererWrapper.cs index 8ab425142b..07cc1c2fb0 100644 --- a/src/NLog/LayoutRenderers/Wrappers/OnHasPropertiesLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/OnHasPropertiesLayoutRendererWrapper.cs @@ -55,7 +55,7 @@ public sealed class OnHasPropertiesLayoutRendererWrapper : WrapperLayoutRenderer /// If is not found, print this layout. /// /// - public Layout Else { get; set; } + public Layout Else { get; set; } = Layout.Empty; /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) diff --git a/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs index 5131d31551..39c7af0c9e 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs @@ -67,7 +67,7 @@ public string SearchFor } } private string _searchFor = string.Empty; - private string _searchForOriginal; + private string _searchForOriginal = string.Empty; /// /// Gets or sets the replacement string. @@ -84,7 +84,7 @@ public string ReplaceWith } } private string _replaceWith = string.Empty; - private string _replaceWithOriginal; + private string _replaceWithOriginal = string.Empty; /// /// Gets or sets a value indicating whether to ignore case. diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs index 8e8fdd76e0..21b1dddf94 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs @@ -47,7 +47,7 @@ namespace NLog.LayoutRenderers.Wrappers [ThreadAgnostic] public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBase, IRawValue, IStringValueRenderer { - private bool _skipStringValueRenderer; + private Func? _stringValueRenderer; /// /// Gets or sets the layout to be rendered when original layout produced empty result. @@ -58,12 +58,25 @@ public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBase, /// protected override void InitializeLayoutRenderer() { + _stringValueRenderer = null; + if (WhenEmpty is null) throw new NLogConfigurationException("WhenEmpty-LayoutRenderer WhenEmpty-property must be assigned."); base.InitializeLayoutRenderer(); WhenEmpty.Initialize(LoggingConfiguration); - _skipStringValueRenderer = !TryGetStringValue(out _, out _); + + if (Inner is SimpleLayout innerLayout && WhenEmpty is SimpleLayout whenEmptyLayout) + { + if ((innerLayout.IsFixedText || innerLayout.IsSimpleStringText) && (whenEmptyLayout.IsFixedText || whenEmptyLayout.IsSimpleStringText)) + { + _stringValueRenderer = (logEvent) => + { + var innerValue = innerLayout.Render(logEvent); + return string.IsNullOrEmpty(innerValue) ? whenEmptyLayout.Render(logEvent) : innerValue; + }; + } + } } /// @@ -83,43 +96,12 @@ protected override string Transform(string text) throw new NotSupportedException(); } - string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) - { - if (_skipStringValueRenderer) - { - return null; - } - - if (TryGetStringValue(out var innerLayout, out var whenEmptyLayout)) - { - var innerValue = innerLayout.Render(logEvent); - if (!string.IsNullOrEmpty(innerValue)) - { - return innerValue; - } - - // render WhenEmpty when the inner layout was empty - return whenEmptyLayout.Render(logEvent); - } - - _skipStringValueRenderer = true; - return null; - } - - private bool TryGetStringValue(out SimpleLayout innerLayout, out SimpleLayout whenEmptyLayout) - { - whenEmptyLayout = WhenEmpty as SimpleLayout; - innerLayout = Inner as SimpleLayout; - - return IsStringLayout(innerLayout) && IsStringLayout(whenEmptyLayout); - } - - private static bool IsStringLayout(SimpleLayout innerLayout) + string? IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) { - return innerLayout != null && (innerLayout.IsFixedText || innerLayout.IsSimpleStringText); + return _stringValueRenderer?.Invoke(logEvent); } - bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) + bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object? value) { if (Inner?.TryGetRawValue(logEvent, out var innerValue) == true) { diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs index 7c5fb23b32..7d7183d546 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs @@ -62,7 +62,7 @@ public sealed class WhenLayoutRendererWrapper : WrapperLayoutRendererBase, IRawV /// If is not met, print this layout. /// /// - public Layout Else { get; set; } + public Layout Else { get; set; } = Layout.Empty; /// protected override void InitializeLayoutRenderer() @@ -106,7 +106,7 @@ private bool ShouldRenderInner(LogEventInfo logEvent) return When is null || ReferenceEquals(When, ConditionExpression.Empty) || true.Equals(When.Evaluate(logEvent)); } - bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) + bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object? value) { if (ShouldRenderInner(logEvent)) { @@ -116,7 +116,7 @@ bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) return TryGetRawValueFromLayout(logEvent, Else, out value); } - private static bool TryGetRawValueFromLayout(LogEventInfo logEvent, Layout layout, out object value) + private static bool TryGetRawValueFromLayout(LogEventInfo logEvent, Layout layout, out object? value) { if (layout is null) { diff --git a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs index fbd4917279..41533eae6b 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs @@ -31,12 +31,9 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.LayoutRenderers.Wrappers { using System.Text; - using NLog.Common; using NLog.Config; using NLog.Layouts; diff --git a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs index 88d4d5a476..232762b499 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.LayoutRenderers.Wrappers { using System; diff --git a/src/NLog/Layouts/Layout.cs b/src/NLog/Layouts/Layout.cs index c90039c81f..09aa86ecaa 100644 --- a/src/NLog/Layouts/Layout.cs +++ b/src/NLog/Layouts/Layout.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using System; @@ -93,9 +91,12 @@ public abstract class Layout : ISupportsInitialize, IRenderable /// /// Text to be converted. /// object represented by the text. - public static implicit operator Layout?([Localizable(false)] string text) + public static implicit operator Layout([Localizable(false)] string text) { - return text is null ? null : FromString(text, ConfigurationItemFactory.Default); + if (text is null) + return new Layout(null); + else + return FromString(text, ConfigurationItemFactory.Default); } /// @@ -170,7 +171,7 @@ public static Layout FromMethod(Func layoutMethod, LayoutR return new SimpleLayout(new[] { layoutRenderer }, layoutRenderer.LayoutRendererName); } - internal static LayoutRenderers.FuncLayoutRenderer CreateFuncLayoutRenderer(Func layoutMethod, LayoutRenderOptions options, string name) + internal static LayoutRenderers.FuncLayoutRenderer CreateFuncLayoutRenderer(Func layoutMethod, LayoutRenderOptions options, string name) { if ((options & LayoutRenderOptions.ThreadAgnostic) == LayoutRenderOptions.ThreadAgnostic) return new LayoutRenderers.FuncThreadAgnosticLayoutRenderer(name, layoutMethod); diff --git a/src/NLog/Layouts/Typed/Layout.cs b/src/NLog/Layouts/Typed/Layout.cs index 16040ba95b..c0f0556432 100644 --- a/src/NLog/Layouts/Typed/Layout.cs +++ b/src/NLog/Layouts/Typed/Layout.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using System; @@ -429,12 +427,9 @@ public static implicit operator Layout(T value) /// Converts a given text to a . /// /// Text to be converted. - public static implicit operator Layout?([Localizable(false)] string layout) + public static implicit operator Layout([Localizable(false)] string layout) { - Layout? innerLayout = layout; - if (innerLayout is null) return null; - - return new Layout(innerLayout); + return (layout is null && !typeof(T).IsValueType) ? new Layout(default(T)) : new Layout(layout ?? string.Empty); } /// diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 4ce47ba7ec..415948cfa0 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; @@ -1041,9 +1039,9 @@ public LogFactory LoadConfiguration(string configFile) return LoadConfiguration(configFile, optional: false); } - internal LogFactory LoadConfiguration(string configFile, bool optional) + internal LogFactory LoadConfiguration(string? configFile, bool optional) { - var actualConfigFile = string.IsNullOrEmpty(configFile) ? "NLog.config" : configFile; + var actualConfigFile = (configFile is null || string.IsNullOrEmpty(configFile)) ? "NLog.config" : configFile; if (optional && string.Equals(actualConfigFile.Trim(), "NLog.config", StringComparison.OrdinalIgnoreCase) && _config != null) { return this; // Skip optional loading of default config, when config is already loaded @@ -1067,7 +1065,7 @@ internal LogFactory LoadConfiguration(string configFile, bool optional) return this; } - private string CreateFileNotFoundMessage(string configFile) + private string CreateFileNotFoundMessage(string? configFile) { var messageBuilder = new StringBuilder("Failed to load NLog LoggingConfiguration."); try diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index 0941ddd353..bad971a25e 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -64,6 +64,7 @@ For all config options and platform support, check https://nlog-project.org/conf true true + enable true true 9 diff --git a/src/NLog/SetupBuilderExtensions.cs b/src/NLog/SetupBuilderExtensions.cs index 6875025945..b941dcc8d2 100644 --- a/src/NLog/SetupBuilderExtensions.cs +++ b/src/NLog/SetupBuilderExtensions.cs @@ -143,7 +143,7 @@ public static ISetupBuilder LoadConfiguration(this ISetupBuilder setupBuilder, L /// Fluent interface parameter. /// Explicit configuration file to be read (Default NLog.config from candidates paths) /// Whether to allow application to run when NLog config is not available - public static ISetupBuilder LoadConfigurationFromFile(this ISetupBuilder setupBuilder, string configFile = null, bool optional = true) + public static ISetupBuilder LoadConfigurationFromFile(this ISetupBuilder setupBuilder, string? configFile = null, bool optional = true) { setupBuilder.LogFactory.LoadConfiguration(configFile, optional); return setupBuilder; diff --git a/src/NLog/SetupExtensionsBuilderExtensions.cs b/src/NLog/SetupExtensionsBuilderExtensions.cs index dbb490c263..b2d32d6b61 100644 --- a/src/NLog/SetupExtensionsBuilderExtensions.cs +++ b/src/NLog/SetupExtensionsBuilderExtensions.cs @@ -111,7 +111,7 @@ public static ISetupExtensionsBuilder RegisterAssembly(this ISetupExtensionsBuil /// Type of the Target. /// Fluent interface parameter. /// The target type-alias for use in NLog configuration. Will extract from class-attribute when unassigned. - public static ISetupExtensionsBuilder RegisterTarget<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] T>(this ISetupExtensionsBuilder setupBuilder, string name = null) + public static ISetupExtensionsBuilder RegisterTarget<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] T>(this ISetupExtensionsBuilder setupBuilder, string? name = null) where T : Target, new() { return RegisterTarget(setupBuilder, () => new T(), name); @@ -124,11 +124,11 @@ public static ISetupExtensionsBuilder RegisterAssembly(this ISetupExtensionsBuil /// Fluent interface parameter. /// The factory method for creating instance of NLog Target /// The target type-alias for use in NLog configuration. Will extract from class-attribute when unassigned. - public static ISetupExtensionsBuilder RegisterTarget<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] T>(this ISetupExtensionsBuilder setupBuilder, Func factory, string typeAlias = null) + public static ISetupExtensionsBuilder RegisterTarget<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] T>(this ISetupExtensionsBuilder setupBuilder, Func factory, string? typeAlias = null) where T : Target { typeAlias = string.IsNullOrEmpty(typeAlias) ? typeof(T).GetFirstCustomAttribute()?.Name : typeAlias; - if (string.IsNullOrEmpty(typeAlias)) + if (typeAlias is null || string.IsNullOrEmpty(typeAlias)) { typeAlias = ResolveTypeAlias("TargetWrapper", "Target"); if (typeof(NLog.Targets.Wrappers.WrapperTargetBase).IsAssignableFrom(typeof(T)) && !typeAlias.EndsWith("Wrapper", StringComparison.OrdinalIgnoreCase)) @@ -163,7 +163,7 @@ public static ISetupExtensionsBuilder RegisterTarget(this ISetupExtensionsBuilde /// Type of the layout renderer. /// Fluent interface parameter. /// The layout type-alias for use in NLog configuration. Will extract from class-attribute when unassigned. - public static ISetupExtensionsBuilder RegisterLayout<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] T>(this ISetupExtensionsBuilder setupBuilder, string typeAlias = null) + public static ISetupExtensionsBuilder RegisterLayout<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] T>(this ISetupExtensionsBuilder setupBuilder, string? typeAlias = null) where T : Layout, new() { return RegisterLayout(setupBuilder, () => new T(), typeAlias); @@ -176,10 +176,10 @@ public static ISetupExtensionsBuilder RegisterTarget(this ISetupExtensionsBuilde /// Fluent interface parameter. /// The factory method for creating instance of NLog Layout /// The layout type-alias for use in NLog configuration. Will extract from class-attribute when unassigned. - public static ISetupExtensionsBuilder RegisterLayout<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] T>(this ISetupExtensionsBuilder setupBuilder, Func factory, string typeAlias = null) + public static ISetupExtensionsBuilder RegisterLayout<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] T>(this ISetupExtensionsBuilder setupBuilder, Func factory, string? typeAlias = null) where T : Layout { - typeAlias = string.IsNullOrEmpty(typeAlias) ? ResolveTypeAlias(ArrayHelper.Empty()) : typeAlias; + typeAlias = (typeAlias is null || string.IsNullOrEmpty(typeAlias)) ? ResolveTypeAlias(ArrayHelper.Empty()) : typeAlias; ConfigurationItemFactory.Default.GetLayoutFactory().RegisterType(typeAlias, factory); return setupBuilder; } @@ -207,7 +207,7 @@ public static ISetupExtensionsBuilder RegisterLayout(this ISetupExtensionsBuilde /// Type of the layout renderer. /// Fluent interface parameter. /// The layout-renderer type-alias for use in NLog configuration - without '${ }'. Will extract from class-attribute when unassigned. - public static ISetupExtensionsBuilder RegisterLayoutRenderer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] T>(this ISetupExtensionsBuilder setupBuilder, string name = null) + public static ISetupExtensionsBuilder RegisterLayoutRenderer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] T>(this ISetupExtensionsBuilder setupBuilder, string? name = null) where T : LayoutRenderer, new() { return RegisterLayoutRenderer(setupBuilder, () => new T(), name); @@ -220,10 +220,10 @@ public static ISetupExtensionsBuilder RegisterLayout(this ISetupExtensionsBuilde /// Fluent interface parameter. /// The factory method for creating instance of NLog LayoutRenderer /// The layout-renderer type-alias for use in NLog configuration - without '${ }'. Will extract from class-attribute when unassigned. - public static ISetupExtensionsBuilder RegisterLayoutRenderer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] T>(this ISetupExtensionsBuilder setupBuilder, Func factory, string typeAlias = null) + public static ISetupExtensionsBuilder RegisterLayoutRenderer<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] T>(this ISetupExtensionsBuilder setupBuilder, Func factory, string? typeAlias = null) where T : LayoutRenderer { - typeAlias = string.IsNullOrEmpty(typeAlias) ? ResolveTypeAlias("LayoutRendererWrapper", "LayoutRenderer") : typeAlias; + typeAlias = (typeAlias is null || string.IsNullOrEmpty(typeAlias)) ? ResolveTypeAlias("LayoutRendererWrapper", "LayoutRenderer") : typeAlias; ConfigurationItemFactory.Default.GetLayoutRendererFactory().RegisterType(typeAlias, factory); return setupBuilder; } @@ -231,10 +231,10 @@ public static ISetupExtensionsBuilder RegisterLayout(this ISetupExtensionsBuilde private static string ResolveTypeAlias(params string[] trimEndings) where TNameAttribute : NameBaseAttribute { var typeAlias = typeof(T).GetFirstCustomAttribute()?.Name; - if (!string.IsNullOrEmpty(typeAlias)) + if (typeAlias is null || string.IsNullOrEmpty(typeAlias)) + return ResolveTypeAlias(trimEndings); + else return typeAlias; - - return ResolveTypeAlias(trimEndings); } private static string ResolveTypeAlias(params string[] trimEndings) @@ -246,7 +246,7 @@ private static string ResolveTypeAlias(params string[] trimEndings) int endingPosition = typeAlias.IndexOf(ending, StringComparison.OrdinalIgnoreCase); if (endingPosition > 0) { - return typeAlias.Substring(endingPosition); + return typeAlias.Substring(0, endingPosition); } } @@ -287,7 +287,7 @@ public static ISetupExtensionsBuilder RegisterLayoutRenderer(this ISetupExtensio /// Fluent interface parameter. /// The layout-renderer type-alias for use in NLog configuration - without '${ }' /// Callback that returns the value for the layout renderer. - public static ISetupExtensionsBuilder RegisterLayoutRenderer(this ISetupExtensionsBuilder setupBuilder, string name, Func layoutMethod) + public static ISetupExtensionsBuilder RegisterLayoutRenderer(this ISetupExtensionsBuilder setupBuilder, string name, Func layoutMethod) { return RegisterLayoutRenderer(setupBuilder, name, layoutMethod, LayoutRenderOptions.None); } @@ -311,7 +311,7 @@ public static ISetupExtensionsBuilder RegisterLayoutRenderer(this ISetupExtensio /// The layout-renderer type-alias for use in NLog configuration - without '${ }' /// Callback that returns the value for the layout renderer. /// Whether method is ThreadAgnostic and doesn't depend on context of the logging application thread. - public static ISetupExtensionsBuilder RegisterLayoutRenderer(this ISetupExtensionsBuilder setupBuilder, string name, Func layoutMethod, LayoutRenderOptions options) + public static ISetupExtensionsBuilder RegisterLayoutRenderer(this ISetupExtensionsBuilder setupBuilder, string name, Func layoutMethod, LayoutRenderOptions options) { FuncLayoutRenderer layoutRenderer = Layout.CreateFuncLayoutRenderer(layoutMethod, options, name); return setupBuilder.RegisterLayoutRenderer(layoutRenderer); diff --git a/src/NLog/SetupInternalLoggerBuilderExtensions.cs b/src/NLog/SetupInternalLoggerBuilderExtensions.cs index 6755d0279d..2491d312f0 100644 --- a/src/NLog/SetupInternalLoggerBuilderExtensions.cs +++ b/src/NLog/SetupInternalLoggerBuilderExtensions.cs @@ -147,7 +147,7 @@ public static ISetupInternalLoggerBuilder SetupFromEnvironmentVariables(this ISe return setupBuilder; } - private static string GetAppSettings(string configName) + private static string? GetAppSettings(string configName) { #if NETFRAMEWORK try @@ -165,11 +165,11 @@ private static string GetAppSettings(string configName) return null; } - private static string GetSettingString(string configName, string envName) + private static string? GetSettingString(string configName, string envName) { try { - string settingValue = GetAppSettings(configName); + var settingValue = GetAppSettings(configName); if (settingValue != null) return settingValue; } @@ -200,7 +200,7 @@ private static string GetSettingString(string configName, string envName) private static LogLevel GetSetting(string configName, string envName, LogLevel defaultValue) { - string value = GetSettingString(configName, envName); + var value = GetSettingString(configName, envName); if (value is null) { return defaultValue; @@ -223,7 +223,7 @@ private static LogLevel GetSetting(string configName, string envName, LogLevel d private static T GetSetting(string configName, string envName, T defaultValue) { - string value = GetSettingString(configName, envName); + var value = GetSettingString(configName, envName); if (value is null) { return defaultValue; diff --git a/src/NLog/SetupLoadConfigurationExtensions.cs b/src/NLog/SetupLoadConfigurationExtensions.cs index f31054a520..d413462bb6 100644 --- a/src/NLog/SetupLoadConfigurationExtensions.cs +++ b/src/NLog/SetupLoadConfigurationExtensions.cs @@ -75,7 +75,7 @@ public static ISetupLoadConfigurationBuilder SetGlobalContextProperty(this ISetu /// Fluent interface parameter. /// Logger name pattern to check which names matches this rule /// Rule identifier to allow rule lookup - public static ISetupConfigurationLoggingRuleBuilder ForLogger(this ISetupLoadConfigurationBuilder configBuilder, string loggerNamePattern = "*", string ruleName = null) + public static ISetupConfigurationLoggingRuleBuilder ForLogger(this ISetupLoadConfigurationBuilder configBuilder, string loggerNamePattern = "*", string? ruleName = null) { var ruleBuilder = new SetupConfigurationLoggingRuleBuilder(configBuilder.LogFactory, configBuilder.Configuration, loggerNamePattern, ruleName); ruleBuilder.LoggingRule.EnableLoggingForLevels(LogLevel.MinLevel, LogLevel.MaxLevel); @@ -89,7 +89,7 @@ public static ISetupConfigurationLoggingRuleBuilder ForLogger(this ISetupLoadCon /// Restrict minimum LogLevel for names that matches this rule /// Logger name pattern to check which names matches this rule /// Rule identifier to allow rule lookup - public static ISetupConfigurationLoggingRuleBuilder ForLogger(this ISetupLoadConfigurationBuilder configBuilder, LogLevel finalMinLevel, string loggerNamePattern = "*", string ruleName = null) + public static ISetupConfigurationLoggingRuleBuilder ForLogger(this ISetupLoadConfigurationBuilder configBuilder, LogLevel finalMinLevel, string loggerNamePattern = "*", string? ruleName = null) { var ruleBuilder = new SetupConfigurationLoggingRuleBuilder(configBuilder.LogFactory, configBuilder.Configuration, loggerNamePattern, ruleName); ruleBuilder.LoggingRule.EnableLoggingForLevels(finalMinLevel ?? LogLevel.MinLevel, LogLevel.MaxLevel); @@ -101,7 +101,7 @@ public static ISetupConfigurationLoggingRuleBuilder ForLogger(this ISetupLoadCon /// /// Fluent interface parameter. /// Override the name for the target created - public static ISetupConfigurationTargetBuilder ForTarget(this ISetupLoadConfigurationBuilder configBuilder, string targetName = null) + public static ISetupConfigurationTargetBuilder ForTarget(this ISetupLoadConfigurationBuilder configBuilder, string? targetName = null) { var ruleBuilder = new SetupConfigurationTargetBuilder(configBuilder.LogFactory, configBuilder.Configuration, targetName); return ruleBuilder; @@ -318,7 +318,7 @@ public static ISetupConfigurationTargetBuilder WriteTo(this ISetupConfigurationT /// /// Fluent interface parameter. /// Only discard output from matching Logger when below minimum LogLevel - public static void WriteToNil(this ISetupConfigurationLoggingRuleBuilder configBuilder, LogLevel finalMinLevel = null) + public static void WriteToNil(this ISetupConfigurationLoggingRuleBuilder configBuilder, LogLevel? finalMinLevel = null) { var loggingRule = configBuilder.LoggingRule; if (finalMinLevel != null) @@ -398,9 +398,10 @@ public static T FirstTarget(this ISetupConfigurationTargetBuilder configBuild throw new InvalidCastException($"Unable to cast object of type '{target.GetType()}' to type '{typeof(T)}'"); } - internal static IEnumerable YieldAllTargets(Target target) + internal static IEnumerable YieldAllTargets(Target? target) { - yield return target; + if (target is not null) + yield return target; if (target is WrapperTargetBase wrapperTarget) { @@ -423,7 +424,7 @@ internal static IEnumerable YieldAllTargets(Target target) /// Fluent interface parameter. /// Method to call on logevent /// Layouts to render object[]-args before calling - public static ISetupConfigurationTargetBuilder WriteToMethodCall(this ISetupConfigurationTargetBuilder configBuilder, Action logEventAction, Layout[] layouts = null) + public static ISetupConfigurationTargetBuilder WriteToMethodCall(this ISetupConfigurationTargetBuilder configBuilder, Action logEventAction, Layout[]? layouts = null) { Guard.ThrowIfNull(logEventAction); @@ -446,7 +447,7 @@ public static ISetupConfigurationTargetBuilder WriteToMethodCall(this ISetupConf /// Write to stderr instead of standard output (stdout) /// Skip overhead from writing to console, when not available (Ex. running as Windows Service) /// Enable batch writing of logevents, instead of Console.WriteLine for each logevent (Requires ) - public static ISetupConfigurationTargetBuilder WriteToConsole(this ISetupConfigurationTargetBuilder configBuilder, Layout layout = null, System.Text.Encoding encoding = null, bool stderr = false, bool detectConsoleAvailable = false, bool writeBuffered = false) + public static ISetupConfigurationTargetBuilder WriteToConsole(this ISetupConfigurationTargetBuilder configBuilder, Layout? layout = null, System.Text.Encoding? encoding = null, bool stderr = false, bool detectConsoleAvailable = false, bool writeBuffered = false) { var consoleTarget = new ConsoleTarget(); if (layout != null) @@ -469,7 +470,7 @@ public static ISetupConfigurationTargetBuilder WriteToConsole(this ISetupConfigu /// Write to stderr instead of standard output (stdout) /// Skip overhead from writing to console, when not available (Ex. running as Windows Service) /// Enables output using ANSI Color Codes (Windows console does not support this by default) - public static ISetupConfigurationTargetBuilder WriteToColoredConsole(this ISetupConfigurationTargetBuilder configBuilder, Layout layout = null, bool highlightWordLevel = false, System.Text.Encoding encoding = null, bool stderr = false, bool detectConsoleAvailable = false, bool enableAnsiOutput = false) + public static ISetupConfigurationTargetBuilder WriteToColoredConsole(this ISetupConfigurationTargetBuilder configBuilder, Layout? layout = null, bool highlightWordLevel = false, System.Text.Encoding? encoding = null, bool stderr = false, bool detectConsoleAvailable = false, bool enableAnsiOutput = false) { var consoleTarget = new ColoredConsoleTarget(); if (layout != null) @@ -522,7 +523,7 @@ public static ISetupConfigurationTargetBuilder WriteToColoredConsole(this ISetup /// /// /// Override the default Layout for output - public static ISetupConfigurationTargetBuilder WriteToDebug(this ISetupConfigurationTargetBuilder configBuilder, Layout layout = null) + public static ISetupConfigurationTargetBuilder WriteToDebug(this ISetupConfigurationTargetBuilder configBuilder, Layout? layout = null) { var debugTarget = new DebugSystemTarget(); if (layout != null) @@ -536,7 +537,7 @@ public static ISetupConfigurationTargetBuilder WriteToDebug(this ISetupConfigura /// /// Override the default Layout for output [System.Diagnostics.Conditional("DEBUG")] - public static void WriteToDebugConditional(this ISetupConfigurationTargetBuilder configBuilder, Layout layout = null) + public static void WriteToDebugConditional(this ISetupConfigurationTargetBuilder configBuilder, Layout? layout = null) { configBuilder.WriteToDebug(layout); } @@ -553,7 +554,7 @@ public static void WriteToDebugConditional(this ISetupConfigurationTargetBuilder /// Size in bytes where log files will be automatically archived. /// Maximum number of archive files that should be kept. /// Maximum days of archive files that should be kept. - public static ISetupConfigurationTargetBuilder WriteToFile(this ISetupConfigurationTargetBuilder configBuilder, Layout fileName, Layout layout = null, System.Text.Encoding encoding = null, LineEndingMode lineEnding = null, bool keepFileOpen = true, long archiveAboveSize = -1, int maxArchiveFiles = -1, int maxArchiveDays = -1) + public static ISetupConfigurationTargetBuilder WriteToFile(this ISetupConfigurationTargetBuilder configBuilder, Layout fileName, Layout? layout = null, System.Text.Encoding? encoding = null, LineEndingMode? lineEnding = null, bool keepFileOpen = true, long archiveAboveSize = -1, int maxArchiveFiles = -1, int maxArchiveDays = -1) { Guard.ThrowIfNull(fileName); @@ -564,7 +565,7 @@ public static ISetupConfigurationTargetBuilder WriteToFile(this ISetupConfigurat fileTarget.Layout = layout; if (encoding != null) fileTarget.Encoding = encoding; - if (lineEnding != null) + if (lineEnding is not null) fileTarget.LineEnding = lineEnding; if (archiveAboveSize > 0) fileTarget.ArchiveAboveSize = archiveAboveSize; @@ -580,7 +581,7 @@ public static ISetupConfigurationTargetBuilder WriteToFile(this ISetupConfigurat /// /// Fluent interface parameter. /// Factory method for creating target-wrapper - public static ISetupConfigurationTargetBuilder WithWrapper(this ISetupConfigurationTargetBuilder configBuilder, Func wrapperFactory) + public static ISetupConfigurationTargetBuilder WithWrapper(this ISetupConfigurationTargetBuilder configBuilder, Func wrapperFactory) { Guard.ThrowIfNull(wrapperFactory); diff --git a/src/NLog/SetupSerializationBuilderExtensions.cs b/src/NLog/SetupSerializationBuilderExtensions.cs index 9f15efbad7..01269e556a 100644 --- a/src/NLog/SetupSerializationBuilderExtensions.cs +++ b/src/NLog/SetupSerializationBuilderExtensions.cs @@ -117,7 +117,7 @@ public ObjectTypeTransformation(Func transformer, IObjectTypeTransfor _transformer = transformer; } - public object TryTransformObject(object obj) + public object? TryTransformObject(object obj) { if (obj is T rawObject) { @@ -142,7 +142,7 @@ public ObjectTypeTransformation(Type objecType, Func transformer _objectType = objecType; } - public object TryTransformObject(object obj) + public object? TryTransformObject(object obj) { if (_objectType.IsAssignableFrom(obj.GetType())) { diff --git a/src/NLog/Targets/AsyncTaskTarget.cs b/src/NLog/Targets/AsyncTaskTarget.cs index 9e6768e400..fa54723c44 100644 --- a/src/NLog/Targets/AsyncTaskTarget.cs +++ b/src/NLog/Targets/AsyncTaskTarget.cs @@ -81,11 +81,12 @@ public abstract class AsyncTaskTarget : TargetWithContext AsyncRequestQueueBase _requestQueue; private readonly Action _taskCancelledTokenReInit; private readonly Action _taskCompletion; - private Task _previousTask; + private Task? _previousTask; private readonly Timer _lazyWriterTimer; + private readonly LogEventInfo _flushEvent = LogEventInfo.Create(LogLevel.Off, null, "NLog Async Task Flush Event"); private readonly ReusableAsyncLogEventList _reusableAsyncLogEventList = new ReusableAsyncLogEventList(200); - private Tuple, List> _reusableLogEvents; - private WaitCallback _flushEventsInQueueDelegate; + private Tuple, List>? _reusableLogEvents; + private WaitCallback? _flushEventsInQueueDelegate; private bool _missingServiceTypes; /// @@ -160,7 +161,7 @@ public int QueueLimit protected AsyncTaskTarget() { _taskCompletion = TaskCompletion; - _taskCancelledTokenReInit = TaskCancelledTokenReInit; + _taskCancelledTokenReInit = () => TaskCancelledTokenReInit(out _); _taskTimeoutTimer = new Timer(TaskTimeout, null, Timeout.Infinite, Timeout.Infinite); #if NETFRAMEWORK @@ -175,7 +176,7 @@ protected AsyncTaskTarget() _lazyWriterTimer = new Timer((s) => TaskStartNext(null, false), null, Timeout.Infinite, Timeout.Infinite); - TaskCancelledTokenReInit(); + TaskCancelledTokenReInit(out _cancelTokenSource); } /// @@ -183,7 +184,7 @@ protected override void InitializeTarget() { _missingServiceTypes = false; - TaskCancelledTokenReInit(); + TaskCancelledTokenReInit(out var _); base.InitializeTarget(); @@ -246,7 +247,7 @@ protected virtual Task WriteAsyncTask(IList logEvents, Cancellatio else { // Should never come here. Only here if someone by mistake configured BatchSize > 1 for target that only handles single LogEventInfo - Task taskChain = null; + Task? taskChain = null; for (int i = 0; i < logEvents.Count; ++i) { LogEventInfo logEvent = logEvents[i]; @@ -255,7 +256,12 @@ protected virtual Task WriteAsyncTask(IList logEvents, Cancellatio else taskChain = taskChain.ContinueWith(t => WriteAsyncTask(logEvent, cancellationToken), cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler).Unwrap(); } - return taskChain; + return taskChain ?? +#if NET45 + System.Threading.Tasks.Task.FromResult(null); +#else + System.Threading.Tasks.Task.CompletedTask; +#endif } } @@ -388,7 +394,7 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation) InternalLogger.Debug("{0}: Flushing {1}", this, _requestQueue.IsEmpty ? "empty queue" : "pending queue items"); if (_requestQueue.OnOverflow != AsyncTargetWrapperOverflowAction.Block) { - _requestQueue.Enqueue(new AsyncLogEventInfo(null, asyncContinuation)); + _requestQueue.Enqueue(new AsyncLogEventInfo(_flushEvent, asyncContinuation)); _lazyWriterTimer.Change(0, Timeout.Infinite); // Schedule timer to empty queue, and execute asyncContinuation } else @@ -398,7 +404,7 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation) { _flushEventsInQueueDelegate = (cont) => { - _requestQueue.Enqueue(new AsyncLogEventInfo(null, (AsyncContinuation)cont)); + _requestQueue.Enqueue(new AsyncLogEventInfo(_flushEvent, (AsyncContinuation)cont)); lock (SyncRoot) _lazyWriterTimer.Change(0, Timeout.Infinite); // Schedule timer to empty queue, and execute asyncContinuation }; @@ -446,7 +452,7 @@ protected override void Dispose(bool disposing) /// /// Used for race-condition validation between task-completion and timeout /// Signals whether previousTask completed an almost full BatchSize - private void TaskStartNext(object previousTask, bool fullBatchCompleted) + private void TaskStartNext(object? previousTask, bool fullBatchCompleted) { do { @@ -497,18 +503,18 @@ private void TaskStartNext(object previousTask, bool fullBatchCompleted) } while (!_requestQueue.IsEmpty || previousTask != null); } - private bool CheckOtherTask(object previousTask) + private bool CheckOtherTask(object? previousTask) { - if (previousTask != null) + if (previousTask is null) { - // Task Completed - if (_previousTask != null && !ReferenceEquals(previousTask, _previousTask)) + // Task Queue Timer + if (_previousTask?.IsCompleted == false) return true; } else { - // Task Queue Timer - if (_previousTask?.IsCompleted == false) + // Task Completed + if (_previousTask != null && !ReferenceEquals(previousTask, _previousTask)) return true; } @@ -522,9 +528,9 @@ internal Task WriteAsyncTaskWithRetry(Task firstTask, IList logEve { var tcs = #if !NET45 - new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); #else - new TaskCompletionSource(); + new TaskCompletionSource(); #endif return firstTask.ContinueWith(t => { @@ -533,7 +539,7 @@ internal Task WriteAsyncTaskWithRetry(Task firstTask, IList logEve if (t.Exception != null) tcs.TrySetException(t.Exception); - Exception actualException = ExtractActualException(t.Exception); + var actualException = ExtractActualException(t.Exception) ?? new TaskCanceledException("Task failed without exception"); if (RetryFailedAsyncTask(actualException, cancellationToken, retryCount - 1, out var retryDelay)) { @@ -571,7 +577,7 @@ internal Task WriteAsyncTaskWithRetry(Task firstTask, IList logEve /// New Task created [true / false] private bool TaskCreation(IList logEvents) { - System.Tuple, List> reusableLogEvents = null; + System.Tuple, List>? reusableLogEvents = null; try { @@ -586,7 +592,7 @@ private bool TaskCreation(IList logEvents) for (int i = 0; i < logEvents.Count; ++i) { - if (logEvents[i].LogEvent is null) + if (ReferenceEquals(logEvents[i].LogEvent, _flushEvent)) { // Flush Request reusableLogEvents.Item2.Add(logEvents[i].Continuation); @@ -631,7 +637,7 @@ private bool TaskCreation(IList logEvents) { _previousTask = null; InternalLogger.Error(ex, "{0}: WriteAsyncTask failed on creation", this); - NotifyTaskCompletion(reusableLogEvents?.Item2, ex); + NotifyTaskCompletion(reusableLogEvents?.Item2 ?? (IList)ArrayHelper.Empty(), ex); } return false; } @@ -642,7 +648,15 @@ private Task StartWriteAsyncTask(IList logEvents, CancellationToke { InternalLogger.Trace("{0}: Writing {1} events", this, logEvents.Count); var newTask = WriteAsyncTask(logEvents, cancellationToken); - if (newTask?.Status == TaskStatus.Created) + if (newTask is null) + { +#if NET45 + return System.Threading.Tasks.Task.FromResult(null); +#else + return System.Threading.Tasks.Task.CompletedTask; +#endif + } + if (newTask.Status == TaskStatus.Created) newTask.Start(TaskScheduler); return newTask; } @@ -663,11 +677,11 @@ private Task StartWriteAsyncTask(IList logEvents, CancellationToke } } - private static void NotifyTaskCompletion(IList reusableContinuations, Exception ex) + private static void NotifyTaskCompletion(IList reusableContinuations, Exception? ex) { try { - for (int i = 0; i < reusableContinuations?.Count; ++i) + for (int i = 0; i < reusableContinuations.Count; ++i) reusableContinuations[i](ex); } catch @@ -738,7 +752,7 @@ private void TaskCompletion(Task completedTask, object continuation) else success = false; - if (success) + if (success && reusableLogEvents != null) { // The expected Task completed with success, allow buffer reuse fullBatchCompleted = reusableLogEvents.Item2.Count * 2 > BatchSize; @@ -794,7 +808,7 @@ private void TaskTimeout(object state) InternalLogger.Debug("{0}: WriteAsyncTask had timeout. Task did not cancel properly: {1}.", this, previousTask.Status); } - Exception actualException = ExtractActualException(previousTask.Exception); + var actualException = ExtractActualException(previousTask.Exception); RetryFailedAsyncTask(actualException ?? new TimeoutException("WriteAsyncTask had timeout"), CancellationToken.None, 0, out var retryDelay); } } @@ -822,7 +836,7 @@ private static bool WaitTaskIsCompleted(Task task, TimeSpan timeout) return task.IsCompleted; } - private static Exception ExtractActualException(AggregateException taskException) + private static Exception? ExtractActualException(AggregateException? taskException) { if (taskException?.InnerExceptions?.Count == 1 && !(taskException.InnerExceptions[0] is AggregateException)) return taskException.InnerExceptions[0]; // Skip Flatten() @@ -831,9 +845,9 @@ private static Exception ExtractActualException(AggregateException taskException return flattenExceptions?.Count == 1 ? flattenExceptions[0] : taskException; } - private void TaskCancelledTokenReInit() + private void TaskCancelledTokenReInit(out CancellationTokenSource cancelTokenSource) { - _cancelTokenSource = new CancellationTokenSource(); + _cancelTokenSource = cancelTokenSource = new CancellationTokenSource(); _cancelTokenSource.Token.Register(_taskCancelledTokenReInit); } } diff --git a/src/NLog/Targets/ColoredConsoleTarget.cs b/src/NLog/Targets/ColoredConsoleTarget.cs index 1d43c96c7f..35adc76197 100644 --- a/src/NLog/Targets/ColoredConsoleTarget.cs +++ b/src/NLog/Targets/ColoredConsoleTarget.cs @@ -121,7 +121,7 @@ public ColoredConsoleTarget(string name) : this() /// Gets or sets a value indicating whether to send the log messages to the standard error instead of the standard output. /// /// - public Layout StdErr { get; set; } + public Layout? StdErr { get; set; } /// /// Gets or sets a value indicating whether to use default row highlighting rules. @@ -183,7 +183,7 @@ public Encoding Encoding _encoding = value; } } - private Encoding _encoding; + private Encoding? _encoding; /// /// Gets or sets a value indicating whether to auto-check if the console is available. @@ -465,7 +465,7 @@ private ConsoleRowHighlightingRule GetMatchingRowHighlightingRule(LogEventInfo l return matchingRule ?? ConsoleRowHighlightingRule.Default; } - private static ConsoleRowHighlightingRule GetMatchingRowHighlightingRule(IList rules, LogEventInfo logEvent) + private static ConsoleRowHighlightingRule? GetMatchingRowHighlightingRule(IList rules, LogEventInfo logEvent) { for (int i = 0; i < rules.Count; ++i) { @@ -490,7 +490,7 @@ private string GenerateColorEscapeSequences(LogEventInfo logEvent, string messag for (int i = 0; i < WordHighlightingRules.Count; ++i) { var hl = WordHighlightingRules[i]; - if (hl.Condition != null && false.Equals(hl.Condition.Evaluate(logEvent))) + if (!hl.CheckCondition(logEvent)) continue; var matches = hl.GetWordsForHighlighting(message); diff --git a/src/NLog/Targets/ConsoleRowHighlightingRule.cs b/src/NLog/Targets/ConsoleRowHighlightingRule.cs index 676c71b043..3567383a92 100644 --- a/src/NLog/Targets/ConsoleRowHighlightingRule.cs +++ b/src/NLog/Targets/ConsoleRowHighlightingRule.cs @@ -46,7 +46,6 @@ public class ConsoleRowHighlightingRule /// Initializes a new instance of the class. /// public ConsoleRowHighlightingRule() - : this(null, ConsoleOutputColor.NoChange, ConsoleOutputColor.NoChange) { } @@ -66,39 +65,32 @@ public ConsoleRowHighlightingRule(ConditionExpression condition, ConsoleOutputCo /// /// Gets the default highlighting rule. Doesn't change the color. /// - public static ConsoleRowHighlightingRule Default { get; } = new ConsoleRowHighlightingRule(null, ConsoleOutputColor.NoChange, ConsoleOutputColor.NoChange); + public static ConsoleRowHighlightingRule Default { get; } = new ConsoleRowHighlightingRule(ConditionExpression.Empty, ConsoleOutputColor.NoChange, ConsoleOutputColor.NoChange); /// /// Gets or sets the condition that must be met in order to set the specified foreground and background color. /// /// - public ConditionExpression Condition { get; set; } + public ConditionExpression Condition { get; set; } = ConditionExpression.Empty; /// /// Gets or sets the foreground color. /// /// - public ConsoleOutputColor ForegroundColor { get; set; } + public ConsoleOutputColor ForegroundColor { get; set; } = ConsoleOutputColor.NoChange; /// /// Gets or sets the background color. /// /// - public ConsoleOutputColor BackgroundColor { get; set; } + public ConsoleOutputColor BackgroundColor { get; set; } = ConsoleOutputColor.NoChange; /// /// Checks whether the specified log event matches the condition (if any). /// - /// - /// Log event. - /// - /// - /// A value of if the condition is not defined or - /// if it matches, otherwise. - /// public bool CheckCondition(LogEventInfo logEvent) { - return Condition is null || true.Equals(Condition.Evaluate(logEvent)); + return Condition is null || ReferenceEquals(Condition, ConditionExpression.Empty) || true.Equals(Condition.Evaluate(logEvent)); } } } diff --git a/src/NLog/Targets/ConsoleTarget.cs b/src/NLog/Targets/ConsoleTarget.cs index b813d86a21..7a27586490 100644 --- a/src/NLog/Targets/ConsoleTarget.cs +++ b/src/NLog/Targets/ConsoleTarget.cs @@ -95,7 +95,7 @@ public sealed class ConsoleTarget : TargetWithLayoutHeaderAndFooter /// Gets or sets a value indicating whether to send the log messages to the standard error instead of the standard output. /// /// - public Layout StdErr { get; set; } + public Layout? StdErr { get; set; } /// /// The encoding for writing messages to the . @@ -111,7 +111,7 @@ public Encoding Encoding _encoding = value; } } - private Encoding _encoding; + private Encoding? _encoding; /// /// Gets or sets a value indicating whether to auto-check if the console is available @@ -142,7 +142,7 @@ public Encoding Encoding /// /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// - public ConsoleTarget() : base() + public ConsoleTarget() { } diff --git a/src/NLog/Targets/ConsoleTargetHelper.cs b/src/NLog/Targets/ConsoleTargetHelper.cs index ce677d6ee7..e3e6a6c5bb 100644 --- a/src/NLog/Targets/ConsoleTargetHelper.cs +++ b/src/NLog/Targets/ConsoleTargetHelper.cs @@ -74,7 +74,7 @@ public static bool IsConsoleAvailable(out string reason) return true; } - public static Encoding GetConsoleOutputEncoding(Encoding currentEncoding, bool isInitialized, bool pauseLogging) + public static Encoding GetConsoleOutputEncoding(Encoding? currentEncoding, bool isInitialized, bool pauseLogging) { if (currentEncoding != null) return currentEncoding; diff --git a/src/NLog/Targets/ConsoleWordHighlightingRule.cs b/src/NLog/Targets/ConsoleWordHighlightingRule.cs index 7d04e2f5c0..e96f976cc1 100644 --- a/src/NLog/Targets/ConsoleWordHighlightingRule.cs +++ b/src/NLog/Targets/ConsoleWordHighlightingRule.cs @@ -49,8 +49,6 @@ public class ConsoleWordHighlightingRule /// public ConsoleWordHighlightingRule() { - BackgroundColor = ConsoleOutputColor.NoChange; - ForegroundColor = ConsoleOutputColor.NoChange; } /// @@ -70,7 +68,7 @@ public ConsoleWordHighlightingRule(string text, ConsoleOutputColor foregroundCol /// Gets or sets the condition that must be met before scanning the row for highlight of words /// /// - public ConditionExpression Condition { get; set; } + public ConditionExpression Condition { get; set; } = ConditionExpression.Empty; /// /// Gets or sets the text to be matched. You must specify either text or regex. @@ -95,19 +93,19 @@ public ConsoleWordHighlightingRule(string text, ConsoleOutputColor foregroundCol /// Gets or sets the foreground color. /// /// - public ConsoleOutputColor ForegroundColor { get; set; } + public ConsoleOutputColor ForegroundColor { get; set; } = ConsoleOutputColor.NoChange; /// /// Gets or sets the background color. /// /// - public ConsoleOutputColor BackgroundColor { get; set; } + public ConsoleOutputColor BackgroundColor { get; set; } = ConsoleOutputColor.NoChange; /// /// Scans the for words that should be highlighted. /// /// List of words with start-position (Key) and word-length (Value) - internal protected virtual IEnumerable> GetWordsForHighlighting(string haystack) + internal protected virtual IEnumerable>? GetWordsForHighlighting(string haystack) { if (ReferenceEquals(_text, string.Empty)) return null; @@ -151,5 +149,13 @@ private int FindNextWordForHighlighting(string haystack, int? prevIndex) } return index; } + + /// + /// Checks whether the specified log event matches the condition (if any). + /// + internal bool CheckCondition(LogEventInfo logEvent) + { + return Condition is null || ReferenceEquals(Condition, ConditionExpression.Empty) || true.Equals(Condition.Evaluate(logEvent)); + } } } diff --git a/src/NLog/Targets/DebugSystemTarget.cs b/src/NLog/Targets/DebugSystemTarget.cs index 5ccd611620..f3c025a164 100644 --- a/src/NLog/Targets/DebugSystemTarget.cs +++ b/src/NLog/Targets/DebugSystemTarget.cs @@ -51,7 +51,7 @@ public sealed class DebugSystemTarget : TargetWithLayoutHeaderAndFooter /// /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// - public DebugSystemTarget() : base() + public DebugSystemTarget() { } diff --git a/src/NLog/Targets/DebuggerTarget.cs b/src/NLog/Targets/DebuggerTarget.cs index 0b8942427c..f269322eab 100644 --- a/src/NLog/Targets/DebuggerTarget.cs +++ b/src/NLog/Targets/DebuggerTarget.cs @@ -63,7 +63,7 @@ public sealed class DebuggerTarget : TargetWithLayoutHeaderAndFooter /// /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// - public DebuggerTarget() : base() + public DebuggerTarget() { } diff --git a/src/NLog/Targets/EventLogTarget.cs b/src/NLog/Targets/EventLogTarget.cs index 6959723f52..294716b532 100644 --- a/src/NLog/Targets/EventLogTarget.cs +++ b/src/NLog/Targets/EventLogTarget.cs @@ -74,8 +74,9 @@ public class EventLogTarget : TargetWithLayout, IInstallable /// Initializes a new instance of the class. /// public EventLogTarget() - : this(null, null) { + Source = AppDomain.CurrentDomain.FriendlyName; + _eventLogWrapper = new EventLogWrapper(); } /// @@ -83,7 +84,7 @@ public EventLogTarget() /// /// Name of the target. public EventLogTarget(string name) - : this(null, null) + : this() { Name = name; } @@ -93,15 +94,15 @@ public EventLogTarget(string name) /// internal EventLogTarget(IEventLogWrapper eventLogWrapper, string sourceName) { - _eventLogWrapper = eventLogWrapper ?? new EventLogWrapper(); - Source = sourceName ?? AppDomain.CurrentDomain.FriendlyName; + _eventLogWrapper = eventLogWrapper; + Source = string.IsNullOrEmpty(sourceName) ? AppDomain.CurrentDomain.FriendlyName : sourceName; } /// /// Gets or sets the name of the machine on which Event Log service is running. /// /// - public Layout MachineName { get; set; } + public Layout MachineName { get; set; } = Layout.Empty; /// /// Gets or sets the layout that renders event ID. @@ -113,13 +114,13 @@ internal EventLogTarget(IEventLogWrapper eventLogWrapper, string sourceName) /// Gets or sets the layout that renders event Category. /// /// - public Layout Category { get; set; } + public Layout? Category { get; set; } /// /// Optional entry type. When not set, or when not convertible to then determined by /// /// - public Layout EntryType { get; set; } + public Layout? EntryType { get; set; } /// /// Gets or sets the value to be used as the event Source. @@ -128,7 +129,7 @@ internal EventLogTarget(IEventLogWrapper eventLogWrapper, string sourceName) /// By default this is the friendly name of the current AppDomain. /// /// - public Layout Source { get; set; } + public Layout Source { get; set; } = Layout.Empty; /// /// Gets or sets the name of the Event Log to write to. This can be System, Application or any user-defined name. @@ -150,7 +151,7 @@ internal EventLogTarget(IEventLogWrapper eventLogWrapper, string sourceName) /// If null, the value will not be specified while creating the Event log. /// /// - public Layout MaxKilobytes { get; set; } + public Layout? MaxKilobytes { get; set; } /// /// Gets or sets the action to take if the message is larger than the option. @@ -349,17 +350,14 @@ private EventLogEntryType GetEntryType(LogEventInfo logEvent) /// Internal for unit tests internal string GetFixedSource() { - if (Source is SimpleLayout simpleLayout && simpleLayout.IsFixedText) + if (Source is SimpleLayout simpleLayout && simpleLayout.IsFixedText && Log is SimpleLayout logNameLayout && logNameLayout.IsFixedText) { - if (MachineName is null || (MachineName is SimpleLayout machineLayout && machineLayout.IsFixedText && ".".Equals(machineLayout.FixedText))) + if (MachineName is null || (MachineName is SimpleLayout machineLayout && machineLayout.IsFixedText && (".".Equals(machineLayout.FixedText) || string.IsNullOrEmpty(machineLayout.FixedText)))) { - if (Log is SimpleLayout logNameLayout && logNameLayout.IsFixedText) - { - return simpleLayout.FixedText; - } + return simpleLayout.FixedText ?? string.Empty; } } - return null; + return string.Empty; } /// @@ -503,23 +501,27 @@ internal interface IEventLogWrapper /// private sealed class EventLogWrapper : IEventLogWrapper, IDisposable { - private EventLog _windowsEventLog; + private EventLog? _windowsEventLog; - public string Source { get; private set; } + public string Source { get; private set; } = string.Empty; - public string Log { get; private set; } + public string Log { get; private set; } = string.Empty; - public string MachineName { get; private set; } + public string MachineName { get; private set; } = string.Empty; public long MaximumKilobytes { - get => _windowsEventLog.MaximumKilobytes; - set => _windowsEventLog.MaximumKilobytes = value; + get => _windowsEventLog?.MaximumKilobytes ?? 0; + set + { + if (_windowsEventLog != null) + _windowsEventLog.MaximumKilobytes = value; + } } public bool IsEventLogAssociated => _windowsEventLog != null; public void WriteEntry(string message, EventLogEntryType entryType, int eventId, short category) => - _windowsEventLog.WriteEntry(message, entryType, eventId, category); + _windowsEventLog?.WriteEntry(message, entryType, eventId, category); /// /// Creates a new association with an instance of Windows . diff --git a/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs index fa4a3178d1..b512759c0b 100644 --- a/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs +++ b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs @@ -147,7 +147,7 @@ public void Flush() public void Dispose() { - SafeCloseFile(_filePath, ref _fileStream); + SafeCloseFile(_filePath, _fileStream); } public bool VerifyFileExists() @@ -160,7 +160,7 @@ private void MonitorFileHasBeenDeleted() if (!SafeFileExists(_filePath)) { InternalLogger.Debug("{0}: Recreating FileStream because no longer File.Exists: '{1}'", _fileTarget, _filePath); - SafeCloseFile(_filePath, ref _fileStream); + SafeCloseFile(_filePath, _fileStream); _fileStream = _fileTarget.CreateFileStreamWithRetry(this, _fileTarget.BufferSize, initialFileOpen: false); _countedFileSize = RefreshCountedFileSize(); RefreshFileBirthTimeUtc(false); @@ -172,12 +172,11 @@ private void MonitorFileHasBeenDeleted() return (_fileTarget.ArchiveAboveSize > 0 && _fileTarget.GetType().Equals(typeof(FileTarget))) ? _fileStream.Length : default(long?); } - private void SafeCloseFile(string filepath, ref Stream fileStream) + private void SafeCloseFile(string filepath, Stream? fileStream) { try { var stream = fileStream; - fileStream = null; stream?.Dispose(); } catch (Exception ex) diff --git a/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs b/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs index 99d0691d7c..e28aae0e3e 100644 --- a/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs +++ b/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs @@ -49,7 +49,7 @@ public BaseFileArchiveHandler(FileTarget fileTarget) _fileTarget = fileTarget; } - protected bool DeleteOldFilesBeforeArchive(string filePath, bool initialFileOpen, string excludeFileName = null) + protected bool DeleteOldFilesBeforeArchive(string filePath, bool initialFileOpen, string? excludeFileName = null) { // Get all files matching the filename, order by timestamp, and when same timestamp then order by filename // - First start with removing the oldest files @@ -59,7 +59,7 @@ protected bool DeleteOldFilesBeforeArchive(string filePath, bool initialFileOpen return DeleteOldFilesBeforeArchive(fileDirectory, fileWildcard, initialFileOpen, excludeFileName); } - protected bool DeleteOldFilesBeforeArchive(string fileDirectory, string fileWildcard, bool initialFileOpen, string excludeFileName = null, bool wildCardContainsSeqNo = false) + protected bool DeleteOldFilesBeforeArchive(string fileDirectory, string fileWildcard, bool initialFileOpen, string? excludeFileName = null, bool wildCardContainsSeqNo = false) { try { @@ -171,7 +171,7 @@ public override string ToString() /// - Trim away sequencer-number, so not part of sorting /// - Use DateTime part from FileSystem for ordering by Date-only, and sort by FileName /// - public static IEnumerable CleanupFiles(FileInfo[] fileInfos, int maxArchiveFiles, int maxArchiveDays, int fileWildcardStartIndex, int fileWildcardEndIndex, string excludeFileName = null, bool wildCardContainsSeqNo = false) + public static IEnumerable CleanupFiles(FileInfo[] fileInfos, int maxArchiveFiles, int maxArchiveDays, int fileWildcardStartIndex, int fileWildcardEndIndex, string? excludeFileName = null, bool wildCardContainsSeqNo = false) { if (fileInfos.Length <= 1) { @@ -211,7 +211,7 @@ public static IEnumerable CleanupFiles(FileInfo[] fileInfos, int maxAr } } - private static bool ExcludeFileName(string fileName, int fileWildcardStartIndex, int fileWildcardEndIndex, string excludeFileName) + private static bool ExcludeFileName(string fileName, int fileWildcardStartIndex, int fileWildcardEndIndex, string? excludeFileName) { if (fileWildcardStartIndex >= 0 && fileWildcardEndIndex >= 0 && fileName.Length > fileWildcardEndIndex) { diff --git a/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs b/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs index af0e18dee4..2cd1254a6e 100644 --- a/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs +++ b/src/NLog/Targets/FileArchiveHandlers/LegacyArchiveFileNameHandler.cs @@ -57,7 +57,7 @@ public LegacyArchiveFileNameHandler(FileTarget fileTarget) public override int ArchiveBeforeOpenFile(string newFileName, LogEventInfo firstLogEvent, DateTime? previousFileLastModified, int newSequenceNumber) { var archiveFileName = _fileTarget.ArchiveFileName?.Render(firstLogEvent); - if (StringHelpers.IsNullOrWhiteSpace(archiveFileName)) + if (archiveFileName is null || StringHelpers.IsNullOrWhiteSpace(archiveFileName)) { return base.ArchiveBeforeOpenFile(newFileName, firstLogEvent, previousFileLastModified, newSequenceNumber); } diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index e53b5c1f7a..43f5362673 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -82,7 +82,7 @@ public Layout FileName } } private Layout _fileName = Layout.Empty; - private string _fixedFileName = string.Empty; + private string? _fixedFileName; /// /// Gets or sets a value indicating whether to create directories if they do not exist. @@ -231,7 +231,7 @@ public bool WriteBom /// /// [Obsolete("Instead use ArchiveSuffixFormat. Marked obsolete with NLog 6.0")] - public string ArchiveDateFormat + public string? ArchiveDateFormat { get => _archiveDateFormat; set @@ -239,11 +239,12 @@ public string ArchiveDateFormat if (string.Equals(value, _archiveDateFormat)) return; - ArchiveSuffixFormat = string.IsNullOrEmpty(value) ? _archiveSuffixFormat : @"_{1:" + value + @"}_{0:00}"; + if (!string.IsNullOrEmpty(value)) + ArchiveSuffixFormat = @"_{1:" + value + @"}_{0:00}"; _archiveDateFormat = value; } } - private string _archiveDateFormat = string.Empty; + private string? _archiveDateFormat; /// /// Gets or sets the size in bytes above which log files will be automatically archived. @@ -293,7 +294,7 @@ public FileArchivePeriod ArchiveEvery /// Avoid using when possible, and instead rely on only using and . /// /// - public Layout ArchiveFileName + public Layout? ArchiveFileName { get => _archiveFileName; set @@ -318,14 +319,14 @@ public Layout ArchiveFileName } _archiveFileName = value; - if (!ReferenceEquals(_archiveSuffixFormat, archiveSuffixFormat)) + if (!ReferenceEquals(_archiveSuffixFormat, archiveSuffixFormat) && archiveSuffixFormat != null) { ArchiveSuffixFormat = archiveSuffixFormat; } _fileArchiveHandler = null; } } - private Layout _archiveFileName; + private Layout? _archiveFileName; private static readonly string _legacyDateArchiveSuffixFormat = "_{1:yyyyMMdd}_{0:00}"; // Cater for ArchiveNumbering.DateAndSequence private static readonly string _legacySequenceArchiveSuffixFormat = "_{0:00}"; // Cater for ArchiveNumbering.Sequence @@ -374,7 +375,7 @@ public string ArchiveNumbering return; _archiveNumbering = string.IsNullOrEmpty(value) ? null : value.Trim(); - if (string.IsNullOrEmpty(_archiveNumbering)) + if (_archiveNumbering is null || string.IsNullOrEmpty(_archiveNumbering)) return; if (_archiveSuffixFormat is null || ReferenceEquals(_archiveSuffixFormat, _legacyDateArchiveSuffixFormat) || ReferenceEquals(_archiveSuffixFormat, _legacySequenceArchiveSuffixFormat)) @@ -383,7 +384,7 @@ public string ArchiveNumbering } } } - private string _archiveNumbering; + private string? _archiveNumbering; /// /// Gets or sets the format-string to convert archive sequence-number by using string.Format @@ -431,7 +432,7 @@ public string ArchiveSuffixFormat _archiveSuffixFormat = value; } } - private string _archiveSuffixFormat; + private string? _archiveSuffixFormat; /// /// Gets or sets a value indicating whether the footer should be written only when the file is archived. @@ -453,7 +454,7 @@ private int OpenFileMonitorTimerInterval } private IFileArchiveHandler FileAchiveHandler => _fileArchiveHandler ?? (_fileArchiveHandler = CreateFileArchiveHandler()); - private IFileArchiveHandler _fileArchiveHandler; + private IFileArchiveHandler? _fileArchiveHandler; struct OpenFileAppender { @@ -476,7 +477,7 @@ public OpenFileAppender(IFileAppender fileAppender, int sequenceNumber) private readonly SortHelpers.KeySelector _getFileNameFromLayout; private DateTime _lastWriteTime; - private Timer _openFileMonitorTimer; + private Timer? _openFileMonitorTimer; /// /// Initializes a new instance of the class. @@ -698,7 +699,7 @@ protected virtual void RenderFormattedMessage(LogEventInfo logEvent, StringBuild Layout.Render(logEvent, target); } - private void WriteToFile(string filename, LogEventInfo firstLogEvent, MemoryStream ms, out Exception lastException) + private void WriteToFile(string filename, LogEventInfo firstLogEvent, MemoryStream ms, out Exception? lastException) { try { @@ -1093,7 +1094,7 @@ internal static string CleanFullFilePath(string filename) { var lastDirSeparator = filename.LastIndexOfAny(DirectorySeparatorChars); - char[] fileNameChars = null; // defer char[] memory-allocation until detecting invalid char + char[]? fileNameChars = null; // defer char[] memory-allocation until detecting invalid char for (int i = lastDirSeparator + 1; i < filename.Length; i++) { diff --git a/src/NLog/Targets/JsonSerializeOptions.cs b/src/NLog/Targets/JsonSerializeOptions.cs index b6a7593f96..1a01f66b3e 100644 --- a/src/NLog/Targets/JsonSerializeOptions.cs +++ b/src/NLog/Targets/JsonSerializeOptions.cs @@ -53,14 +53,14 @@ public class JsonSerializeOptions /// [Obsolete("Marked obsolete with NLog 5.0. Should always be InvariantCulture.")] [EditorBrowsable(EditorBrowsableState.Never)] - public IFormatProvider FormatProvider { get; set; } + public IFormatProvider? FormatProvider { get; set; } /// /// Format string for value /// [Obsolete("Marked obsolete with NLog 5.0. Should always be InvariantCulture.")] [EditorBrowsable(EditorBrowsableState.Never)] - public string Format { get; set; } + public string? Format { get; set; } /// /// Should non-ascii characters be encoded. Default: false diff --git a/src/NLog/Targets/LineEndingMode.cs b/src/NLog/Targets/LineEndingMode.cs index 5e1fa84c7e..e96227bfc1 100644 --- a/src/NLog/Targets/LineEndingMode.cs +++ b/src/NLog/Targets/LineEndingMode.cs @@ -88,7 +88,11 @@ public sealed class LineEndingMode : IEquatable /// public string NewLineCharacters => _newLineCharacters; - private LineEndingMode() { } + private LineEndingMode() + { + _name = string.Empty; + _newLineCharacters = string.Empty; + } /// /// Initializes a new instance of . diff --git a/src/NLog/Targets/MethodCallParameter.cs b/src/NLog/Targets/MethodCallParameter.cs index 43f66ce18f..ccd66bea72 100644 --- a/src/NLog/Targets/MethodCallParameter.cs +++ b/src/NLog/Targets/MethodCallParameter.cs @@ -93,7 +93,7 @@ public MethodCallParameter(string name, Layout layout, Type type) /// Gets or sets the name of the parameter. /// /// - public string Name { get; set; } + public string Name { get; set; } = string.Empty; /// /// Gets or sets the layout that should be use to calculate the value for the parameter. @@ -120,13 +120,13 @@ public MethodCallParameter(string name, Layout layout, Type type) /// Gets or sets the fallback value when result value is not available /// /// - public Layout DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } + public Layout? DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } /// /// Render Result Value /// /// Log event for rendering /// Result value when available, else fallback to defaultValue - public object RenderValue(LogEventInfo logEvent) => _layoutInfo.RenderValue(logEvent); + public object? RenderValue(LogEventInfo logEvent) => _layoutInfo.RenderValue(logEvent); } } diff --git a/src/NLog/Targets/MethodCallTarget.cs b/src/NLog/Targets/MethodCallTarget.cs index 72d3ab6cf7..d19c8918dc 100644 --- a/src/NLog/Targets/MethodCallTarget.cs +++ b/src/NLog/Targets/MethodCallTarget.cs @@ -34,6 +34,7 @@ namespace NLog.Targets { using System; + using System.Diagnostics; using System.Reflection; using NLog.Common; using NLog.Config; @@ -64,7 +65,7 @@ public sealed class MethodCallTarget : MethodCallTargetBase /// Gets or sets the class name. /// /// - public string ClassName { get; set; } + public string ClassName { get; set; } = string.Empty; /// /// Gets or sets the method name. The method must be public and static. @@ -73,14 +74,14 @@ public sealed class MethodCallTarget : MethodCallTargetBase /// e.g. /// /// - public string MethodName { get; set; } + public string MethodName { get; set; } = string.Empty; - Action _logEventAction; + Action? _logEventAction; /// /// Initializes a new instance of the class. /// - public MethodCallTarget() : base() + public MethodCallTarget() { } @@ -88,8 +89,9 @@ public MethodCallTarget() : base() /// Initializes a new instance of the class. /// /// Name of the target. - public MethodCallTarget(string name) : this(name, null) + public MethodCallTarget(string name) { + Name = name; } /// @@ -97,7 +99,7 @@ public MethodCallTarget(string name) : this(name, null) /// /// Name of the target. /// Method to call on logevent. - public MethodCallTarget(string name, Action logEventAction) : this() + public MethodCallTarget(string name, Action logEventAction) { Name = name; _logEventAction = logEventAction; @@ -119,7 +121,7 @@ protected override void InitializeTarget() } [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow method lookup from config", "IL2075")] - private static Action BuildLogEventAction(string className, string methodName) + private static Action BuildLogEventAction(string className, string methodName) { var targetType = PropertyTypeConverter.ConvertToType(className.Trim(), false); if (targetType is null) @@ -144,18 +146,18 @@ private static Action BuildLogEventAction(string classNa } } - private static Action BuildLogEventAction(MethodInfo methodInfo) + private static Action BuildLogEventAction(MethodInfo methodInfo) { var neededParameters = methodInfo.GetParameters().Length; - ReflectionHelpers.LateBoundMethod lateBoundMethod = null; + ReflectionHelpers.LateBoundMethod? lateBoundMethod = null; return (logEvent, parameters) => { var missingParameters = neededParameters - parameters.Length; if (missingParameters > 0) { //fill missing parameters with Type.Missing - var newParams = new object[neededParameters]; + var newParams = new object?[neededParameters]; for (int i = 0; i < parameters.Length; ++i) newParams[i] = parameters[i]; for (int i = parameters.Length; i < neededParameters; ++i) @@ -173,17 +175,17 @@ private static Action BuildLogEventAction(MethodInfo met if (lateBoundMethod is null) lateBoundMethod = CreateFastInvoke(methodInfo, parameters) ?? CreateNormalInvoke(methodInfo, parameters); else - lateBoundMethod.Invoke(null, parameters); + CallMethod(lateBoundMethod, parameters); } }; } - private static ReflectionHelpers.LateBoundMethod CreateFastInvoke(MethodInfo methodInfo, object[] parameters) + private static ReflectionHelpers.LateBoundMethod? CreateFastInvoke(MethodInfo methodInfo, object?[] parameters) { try { var lateBoundMethod = ReflectionHelpers.CreateLateBoundMethod(methodInfo); - lateBoundMethod.Invoke(null, parameters); + CallMethod(lateBoundMethod, parameters); return lateBoundMethod; } catch (Exception ex) @@ -193,13 +195,20 @@ private static ReflectionHelpers.LateBoundMethod CreateFastInvoke(MethodInfo met } } - private static ReflectionHelpers.LateBoundMethod CreateNormalInvoke(MethodInfo methodInfo, object[] parameters) + private static void CallMethod(ReflectionHelpers.LateBoundMethod lateBoundMethod, object?[] parameters) + { +#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. + lateBoundMethod.Invoke(null, parameters); +#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. + } + + private static ReflectionHelpers.LateBoundMethod CreateNormalInvoke(MethodInfo methodInfo, object?[] parameters) { ReflectionHelpers.LateBoundMethod reflectionMethod = (target, args) => methodInfo.Invoke(null, args); try { - reflectionMethod.Invoke(null, parameters); + CallMethod(reflectionMethod, parameters); return reflectionMethod; } catch (Exception ex) @@ -214,7 +223,7 @@ private static ReflectionHelpers.LateBoundMethod CreateNormalInvoke(MethodInfo m /// /// Method parameters. /// The logging event. - protected override void DoInvoke(object[] parameters, AsyncLogEventInfo logEvent) + protected override void DoInvoke(object?[] parameters, AsyncLogEventInfo logEvent) { try { @@ -236,12 +245,13 @@ protected override void DoInvoke(object[] parameters, AsyncLogEventInfo logEvent /// Calls the specified Method. /// /// Method parameters. - protected override void DoInvoke(object[] parameters) + protected override void DoInvoke(object?[] parameters) { - ExecuteLogMethod(parameters, null); + // method is not used, instead asynchronous overload will be used + throw new NotSupportedException(); } - private void ExecuteLogMethod(object[] parameters, LogEventInfo logEvent) + private void ExecuteLogMethod(object?[] parameters, LogEventInfo logEvent) { if (_logEventAction is null) { diff --git a/src/NLog/Targets/MethodCallTargetBase.cs b/src/NLog/Targets/MethodCallTargetBase.cs index 863bf44ccd..ff4970820a 100644 --- a/src/NLog/Targets/MethodCallTargetBase.cs +++ b/src/NLog/Targets/MethodCallTargetBase.cs @@ -66,7 +66,7 @@ protected MethodCallTargetBase() /// The logging event. protected override void Write(AsyncLogEventInfo logEvent) { - object[] parameters = Parameters.Count > 0 ? new object[Parameters.Count] : ArrayHelper.Empty(); + object?[] parameters = Parameters.Count > 0 ? new object?[Parameters.Count] : ArrayHelper.Empty(); for (int i = 0; i < parameters.Length; ++i) { try @@ -92,22 +92,12 @@ protected override void Write(AsyncLogEventInfo logEvent) /// /// Method call parameters. /// The logging event. - protected virtual void DoInvoke(object[] parameters, AsyncLogEventInfo logEvent) - { - DoInvoke(parameters, logEvent.Continuation); - } - - /// - /// Calls the target DoInvoke method, and handles AsyncContinuation callback - /// - /// Method call parameters. - /// The continuation. - protected virtual void DoInvoke(object[] parameters, AsyncContinuation continuation) + protected virtual void DoInvoke(object?[] parameters, AsyncLogEventInfo logEvent) { try { DoInvoke(parameters); - continuation(null); + logEvent.Continuation(null); } catch (Exception ex) { @@ -116,7 +106,7 @@ protected virtual void DoInvoke(object[] parameters, AsyncContinuation continuat throw; } - continuation(ex); + logEvent.Continuation(ex); } } @@ -124,6 +114,6 @@ protected virtual void DoInvoke(object[] parameters, AsyncContinuation continuat /// Calls the target method. Must be implemented in concrete classes. /// /// Method call parameters. - protected abstract void DoInvoke(object[] parameters); + protected abstract void DoInvoke(object?[] parameters); } } diff --git a/src/NLog/Targets/NullTarget.cs b/src/NLog/Targets/NullTarget.cs index 573cf08962..a08aab8ad3 100644 --- a/src/NLog/Targets/NullTarget.cs +++ b/src/NLog/Targets/NullTarget.cs @@ -63,7 +63,7 @@ public sealed class NullTarget : TargetWithLayout /// /// Initializes a new instance of the class. /// - public NullTarget() : base() + public NullTarget() { } diff --git a/src/NLog/Targets/TargetPropertyWithContext.cs b/src/NLog/Targets/TargetPropertyWithContext.cs index aa79502c46..f8bf9ad2e5 100644 --- a/src/NLog/Targets/TargetPropertyWithContext.cs +++ b/src/NLog/Targets/TargetPropertyWithContext.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets { using System; diff --git a/src/NLog/Targets/TargetWithLayout.cs b/src/NLog/Targets/TargetWithLayout.cs index fad88f50e9..36d32da835 100644 --- a/src/NLog/Targets/TargetWithLayout.cs +++ b/src/NLog/Targets/TargetWithLayout.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets { using NLog.Config; diff --git a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs index 043a247619..32b91ba84d 100644 --- a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs +++ b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs @@ -35,7 +35,6 @@ namespace NLog.Targets { - using NLog.Config; using NLog.Layouts; /// diff --git a/tests/NLog.UnitTests/LayoutRenderers/ExceptionTests.cs b/tests/NLog.UnitTests/LayoutRenderers/ExceptionTests.cs index 1b316e6a8a..e697e5fca8 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/ExceptionTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/ExceptionTests.cs @@ -35,6 +35,7 @@ namespace NLog.UnitTests.LayoutRenderers { using System; using System.Collections.Generic; + using System.Linq; using NLog.Config; using NLog.Internal; using NLog.LayoutRenderers; @@ -859,8 +860,8 @@ public void ExcpetionTestAPI() var t = (DebugTarget)logFactory.Configuration.AllTargets[0]; var elr = ((SimpleLayout)t.Layout).Renderers[0] as ExceptionLayoutRenderer; - Assert.Equal(ExceptionRenderingFormat.ShortType, elr.Formats[0]); - Assert.Equal(ExceptionRenderingFormat.Message, elr.Formats[1]); + Assert.Equal(ExceptionRenderingFormat.ShortType, elr.Formats.First()); + Assert.Equal(ExceptionRenderingFormat.Message, elr.Formats.Last()); } [Fact] @@ -883,14 +884,6 @@ public void InnerExceptionTestAPI() logFactory.AssertDebugLastMessage("ApplicationException Wrapper2" + EnvironmentHelper.NewLine + "Wrapper1" + EnvironmentHelper.NewLine + "Test exception"); - - var t = (DebugTarget)logFactory.Configuration.AllTargets[0]; - var elr = ((SimpleLayout)t.Layout).Renderers[0] as ExceptionLayoutRenderer; - - Assert.Equal(ExceptionRenderingFormat.ShortType, elr.Formats[0]); - Assert.Equal(ExceptionRenderingFormat.Message, elr.Formats[1]); - - Assert.Equal(ExceptionRenderingFormat.Message, elr.InnerFormats[0]); } [Fact] diff --git a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs index e5b485e550..fc201c6363 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/WhenEmptyTests.cs @@ -111,16 +111,15 @@ public void GetStringValueShouldWork(string message, string expected) { // Arrange SimpleLayout layout = @"${message:whenEmpty=default}"; + layout.Initialize(null); var stringValueRenderer = (IStringValueRenderer)layout.Renderers[0]; - var logEvent = LogEventInfo.Create(LogLevel.Info, "logger", message); // Act + var logEvent = LogEventInfo.Create(LogLevel.Info, "logger", message); var result = stringValueRenderer.GetFormattedString(logEvent); // Assert Assert.Equal(expected, result); - - } } } From 6f8a8532e51481580386dbf6ddab6729e40f8d0b Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 20 May 2025 21:33:59 +0200 Subject: [PATCH 119/224] Removed explicit nullable enable since on by default (#5824) --- src/NLog/Common/AsyncContinuation.cs | 2 -- src/NLog/Common/AsyncHelpers.cs | 2 -- src/NLog/Common/AsyncLogEventInfo.cs | 2 -- src/NLog/Common/AsynchronousAction.cs | 2 -- src/NLog/Common/ConversionHelpers.cs | 2 -- src/NLog/Common/IInternalLoggerContext.cs | 2 -- src/NLog/Common/InternalEventOccurredHandler.cs | 2 -- src/NLog/Common/InternalLogEventArgs.cs | 2 -- src/NLog/Common/InternalLogger-generated.cs | 2 -- src/NLog/Common/InternalLogger-generated.tt | 2 -- src/NLog/Common/InternalLogger.cs | 2 -- src/NLog/Common/LogEventInfoBuffer.cs | 2 -- src/NLog/Conditions/ConditionAndExpression.cs | 2 -- src/NLog/Conditions/ConditionEvaluationException.cs | 2 -- src/NLog/Conditions/ConditionExceptionExpression.cs | 2 -- src/NLog/Conditions/ConditionExpression.cs | 2 -- src/NLog/Conditions/ConditionLayoutExpression.cs | 2 -- src/NLog/Conditions/ConditionLevelExpression.cs | 2 -- src/NLog/Conditions/ConditionLiteralExpression.cs | 2 -- src/NLog/Conditions/ConditionLoggerNameExpression.cs | 2 -- src/NLog/Conditions/ConditionMessageExpression.cs | 2 -- src/NLog/Conditions/ConditionMethodAttribute.cs | 2 -- src/NLog/Conditions/ConditionMethodExpression.cs | 2 -- src/NLog/Conditions/ConditionMethods.cs | 2 -- src/NLog/Conditions/ConditionMethodsAttribute.cs | 2 -- src/NLog/Conditions/ConditionNotExpression.cs | 2 -- src/NLog/Conditions/ConditionOrExpression.cs | 2 -- src/NLog/Conditions/ConditionParseException.cs | 2 -- src/NLog/Conditions/ConditionParser.cs | 2 -- src/NLog/Conditions/ConditionRelationalExpression.cs | 2 -- src/NLog/Conditions/ConditionRelationalOperator.cs | 2 -- src/NLog/Conditions/ConditionTokenizer.cs | 2 -- src/NLog/Config/AdvancedAttribute.cs | 2 -- src/NLog/Config/AppDomainFixedOutputAttribute.cs | 2 -- src/NLog/Config/ArrayParameterAttribute.cs | 2 -- src/NLog/Config/AssemblyExtensionLoader.cs | 2 -- src/NLog/Config/AssemblyExtensionTypes.cs | 2 -- src/NLog/Config/AssemblyExtensionTypes.tt | 2 -- src/NLog/Config/ConfigSectionHandler.cs | 2 -- src/NLog/Config/ConfigurationItemFactory.cs | 2 -- src/NLog/Config/DefaultParameterAttribute.cs | 2 -- src/NLog/Config/DynamicLogLevelFilter.cs | 2 -- src/NLog/Config/DynamicRangeLevelFilter.cs | 2 -- src/NLog/Config/Factory.cs | 2 -- src/NLog/Config/IFactory.cs | 2 -- src/NLog/Config/IIncludeContext.cs | 2 -- src/NLog/Config/IInstallable.cs | 2 -- src/NLog/Config/ILoggingConfigurationElement.cs | 2 -- src/NLog/Config/ILoggingConfigurationLoader.cs | 2 -- src/NLog/Config/ILoggingRuleLevelFilter.cs | 2 -- src/NLog/Config/IPropertyTypeConverter.cs | 2 -- src/NLog/Config/ISetupBuilder.cs | 2 -- src/NLog/Config/ISetupConfigurationLoggingRuleBuilder.cs | 2 -- src/NLog/Config/ISetupConfigurationTargetBuilder.cs | 2 -- src/NLog/Config/ISetupExtensionsBuilder.cs | 2 -- src/NLog/Config/ISetupInternalLoggerBuilder.cs | 2 -- src/NLog/Config/ISetupLoadConfigurationBuilder.cs | 2 -- src/NLog/Config/ISetupLogFactoryBuilder.cs | 2 -- src/NLog/Config/ISetupSerializationBuilder.cs | 2 -- src/NLog/Config/IUsesStackTrace.cs | 2 -- src/NLog/Config/InstallationContext.cs | 2 -- src/NLog/Config/LoggerNameMatcher.cs | 2 -- src/NLog/Config/LoggingConfiguration.cs | 2 -- src/NLog/Config/LoggingConfigurationChangedEventArgs.cs | 2 -- src/NLog/Config/LoggingConfigurationElementExtensions.cs | 2 -- src/NLog/Config/LoggingConfigurationFileLoader.cs | 2 -- src/NLog/Config/LoggingConfigurationParser.cs | 2 -- src/NLog/Config/LoggingRule.cs | 2 -- src/NLog/Config/LoggingRuleLevelFilter.cs | 2 -- src/NLog/Config/MethodFactory.cs | 2 -- src/NLog/Config/MutableUnsafeAttribute.cs | 2 -- src/NLog/Config/NLogConfigurationIgnorePropertyAttribute.cs | 2 -- src/NLog/Config/NLogConfigurationItemAttribute.cs | 2 -- src/NLog/Config/NLogDependencyResolveException.cs | 2 -- src/NLog/Config/NameBaseAttribute.cs | 2 -- src/NLog/Config/PropertyTypeConverter.cs | 2 -- src/NLog/Config/RequiredParameterAttribute.cs | 2 -- src/NLog/Config/ServiceRepositoryUpdateEventArgs.cs | 2 -- src/NLog/Config/SimpleConfigurator.cs | 2 -- src/NLog/Config/ThreadAgnosticAttribute.cs | 2 -- src/NLog/Config/ThreadAgnosticImmutableAttribute.cs | 2 -- src/NLog/Config/ThreadSafeAttribute.cs | 2 -- src/NLog/Config/XmlLoggingConfiguration.cs | 2 -- src/NLog/Config/XmlLoggingConfigurationElement.cs | 2 -- src/NLog/Config/XmlParserConfigurationElement.cs | 2 -- src/NLog/Config/XmlParserException.cs | 2 -- src/NLog/Filters/ConditionBasedFilter.cs | 2 -- src/NLog/Filters/Filter.cs | 2 -- src/NLog/Filters/FilterAttribute.cs | 2 -- src/NLog/Filters/LayoutBasedFilter.cs | 2 -- src/NLog/Filters/WhenContainsFilter.cs | 2 -- src/NLog/Filters/WhenEqualFilter.cs | 2 -- src/NLog/Filters/WhenMethodFilter.cs | 2 -- src/NLog/Filters/WhenNotContainsFilter.cs | 2 -- src/NLog/Filters/WhenNotEqualFilter.cs | 2 -- src/NLog/Filters/WhenRepeatedFilter.cs | 2 -- src/NLog/GlobalDiagnosticsContext.cs | 2 -- src/NLog/IJsonConverter.cs | 2 -- src/NLog/ILogger-V1.cs | 2 -- src/NLog/ILogger.cs | 2 -- src/NLog/ILoggerBase-V1.cs | 2 -- src/NLog/ILoggerBase.cs | 2 -- src/NLog/ILoggerExtensions.cs | 2 -- src/NLog/ISuppress.cs | 2 -- src/NLog/IValueFormatter.cs | 2 -- src/NLog/Internal/AppEnvironmentWrapper.cs | 2 -- src/NLog/Internal/AppendBuilderCreator.cs | 2 -- src/NLog/Internal/ArrayHelper.cs | 2 -- src/NLog/Internal/AssemblyHelpers.cs | 2 -- src/NLog/Internal/AssemblyMetadataAttribute.cs | 2 -- src/NLog/Internal/AsyncOperationCounter.cs | 2 -- src/NLog/Internal/CallSiteInformation.cs | 2 -- src/NLog/Internal/CollectionExtensions.cs | 2 -- src/NLog/Internal/ConfigVariablesDictionary.cs | 2 -- src/NLog/Internal/ConfigurationManager.cs | 2 -- src/NLog/Internal/DictionaryEntryEnumerable.cs | 2 -- src/NLog/Internal/DynamicallyAccessedMemberTypes.cs | 2 -- src/NLog/Internal/EnvironmentHelper.cs | 2 -- src/NLog/Internal/ExceptionHelper.cs | 2 -- src/NLog/Internal/ExceptionMessageFormatProvider.cs | 2 -- src/NLog/Internal/FileInfoHelper.cs | 2 -- src/NLog/Internal/FormatHelper.cs | 2 -- src/NLog/Internal/Guard.cs | 2 -- src/NLog/Internal/IAppEnvironment.cs | 2 -- src/NLog/Internal/IConfigurationManager.cs | 2 -- src/NLog/Internal/IFileSystem.cs | 2 -- src/NLog/Internal/ILogMessageFormatter.cs | 2 -- src/NLog/Internal/IRawValue.cs | 2 -- src/NLog/Internal/IRenderable.cs | 2 -- src/NLog/Internal/IStringValueRenderer.cs | 2 -- src/NLog/Internal/ISupportsInitialize.cs | 2 -- src/NLog/Internal/ITargetWithFilterChain.cs | 2 -- src/NLog/Internal/LogMessageStringFormatter.cs | 2 -- src/NLog/Internal/LogMessageTemplateFormatter.cs | 2 -- src/NLog/Internal/MruCache.cs | 2 -- src/NLog/Internal/MultiFileWatcher.cs | 2 -- src/NLog/Internal/NativeMethods.cs | 2 -- src/NLog/Internal/ObjectGraphScanner.cs | 2 -- src/NLog/Internal/ObjectHandleSerializer.cs | 2 -- src/NLog/Internal/ObjectPropertyPath.cs | 2 -- src/NLog/Internal/ObjectReflectionCache.cs | 2 -- src/NLog/Internal/OverloadResolutionPriorityAttribute.cs | 2 -- src/NLog/Internal/PlatformDetector.cs | 2 -- src/NLog/Internal/PropertiesDictionary.cs | 2 -- src/NLog/Internal/ReusableAsyncLogEventList.cs | 2 -- src/NLog/Internal/ReusableBufferCreator.cs | 2 -- src/NLog/Internal/ReusableBuilderCreator.cs | 2 -- src/NLog/Internal/ReusableObjectCreator.cs | 2 -- src/NLog/Internal/ReusableStreamCreator.cs | 2 -- src/NLog/Internal/RuntimeOS.cs | 2 -- src/NLog/Internal/ScopeContextAsyncState.cs | 2 -- src/NLog/Internal/ScopeContextPropertyEnumerator.cs | 2 -- src/NLog/Internal/SetupBuilder.cs | 2 -- src/NLog/Internal/SetupConfigurationLoggingRuleBuilder.cs | 2 -- src/NLog/Internal/SetupConfigurationTargetBuilder.cs | 2 -- src/NLog/Internal/SetupExtensionsBuilder.cs | 2 -- src/NLog/Internal/SetupInternalLoggerBuilder.cs | 2 -- src/NLog/Internal/SetupLogFactoryBuilder.cs | 2 -- src/NLog/Internal/SetupSerializationBuilder.cs | 2 -- src/NLog/Internal/SimpleStringReader.cs | 2 -- src/NLog/Internal/SingleCallContinuation.cs | 2 -- src/NLog/Internal/SingleItemOptimizedHashSet.cs | 2 -- src/NLog/Internal/SortHelpers.cs | 2 -- src/NLog/Internal/StackTraceUsageUtils.cs | 2 -- src/NLog/Internal/StringBuilderExt.cs | 2 -- src/NLog/Internal/StringBuilderPool.cs | 2 -- src/NLog/Internal/StringHelpers.cs | 2 -- src/NLog/Internal/StringSplitter.cs | 2 -- src/NLog/Internal/TargetWithFilterChain.cs | 2 -- src/NLog/Internal/TimeoutContinuation.cs | 2 -- src/NLog/Internal/UrlHelper.cs | 2 -- src/NLog/Internal/XmlHelper.cs | 2 -- src/NLog/Layouts/CSV/CsvColumn.cs | 2 -- src/NLog/Layouts/CSV/CsvLayout.cs | 2 -- src/NLog/Layouts/CompoundLayout.cs | 2 -- src/NLog/Layouts/JSON/JsonArrayLayout.cs | 2 -- src/NLog/Layouts/JSON/JsonAttribute.cs | 2 -- src/NLog/Layouts/JSON/JsonLayout.cs | 2 -- src/NLog/Layouts/LayoutAttribute.cs | 2 -- src/NLog/Layouts/LayoutParser.cs | 2 -- src/NLog/Layouts/LayoutWithHeaderAndFooter.cs | 2 -- src/NLog/Layouts/SimpleLayout.cs | 2 -- src/NLog/Layouts/Typed/LayoutTypedExtensions.cs | 2 -- src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs | 2 -- src/NLog/Layouts/XML/XmlAttribute.cs | 2 -- src/NLog/Layouts/XML/XmlElement.cs | 2 -- src/NLog/Layouts/XML/XmlElementBase.cs | 2 -- src/NLog/Layouts/XML/XmlLayout.cs | 2 -- src/NLog/LogEventBuilder.cs | 2 -- src/NLog/LogEventInfo.cs | 2 -- src/NLog/LogFactory-T.cs | 2 -- src/NLog/LogLevel.cs | 2 -- src/NLog/LogLevelTypeConverter.cs | 2 -- src/NLog/LogManager.cs | 2 -- src/NLog/LogMessageFormatter.cs | 2 -- src/NLog/LogMessageGenerator.cs | 2 -- src/NLog/Logger-Conditional.cs | 2 -- src/NLog/Logger-V1Compat.cs | 2 -- src/NLog/Logger-generated.cs | 2 -- src/NLog/Logger-generated.tt | 2 -- src/NLog/Logger.cs | 2 -- src/NLog/LoggerImpl.cs | 2 -- src/NLog/MappedDiagnosticsContext.cs | 2 -- src/NLog/MappedDiagnosticsLogicalContext.cs | 2 -- src/NLog/MessageTemplates/Hole.cs | 2 -- src/NLog/MessageTemplates/Literal.cs | 2 -- src/NLog/MessageTemplates/LiteralHole.cs | 2 -- src/NLog/MessageTemplates/MessageTemplateParameter.cs | 2 -- src/NLog/MessageTemplates/MessageTemplateParameters.cs | 2 -- src/NLog/MessageTemplates/TemplateEnumerator.cs | 2 -- src/NLog/MessageTemplates/TemplateParserException.cs | 2 -- src/NLog/MessageTemplates/ValueFormatter.cs | 2 -- src/NLog/NLogConfigurationException.cs | 2 -- src/NLog/NLogRuntimeException.cs | 2 -- src/NLog/NestedDiagnosticsContext.cs | 2 -- src/NLog/NestedDiagnosticsLogicalContext.cs | 2 -- src/NLog/NullLogger.cs | 2 -- src/NLog/ScopeContext.cs | 2 -- src/NLog/Targets/DefaultJsonSerializer.cs | 2 -- src/NLog/Targets/Target.cs | 2 -- src/NLog/Targets/TargetAttribute.cs | 2 -- src/NLog/Targets/TargetWithContext.cs | 2 -- src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs | 2 -- src/NLog/Targets/Wrappers/AsyncRequestQueue-T.cs | 2 -- src/NLog/Targets/Wrappers/AsyncRequestQueueBase.cs | 2 -- src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs | 2 -- src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs | 2 -- src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs | 2 -- src/NLog/Targets/Wrappers/CompoundTargetBase.cs | 2 -- src/NLog/Targets/Wrappers/ConcurrentRequestQueue.cs | 2 -- src/NLog/Targets/Wrappers/FallbackGroupTarget.cs | 2 -- src/NLog/Targets/Wrappers/FilteringRule.cs | 2 -- src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs | 2 -- src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs | 2 -- src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs | 2 -- src/NLog/Targets/Wrappers/LogEventDroppedEventArgs.cs | 2 -- src/NLog/Targets/Wrappers/LogEventQueueGrowEventArgs.cs | 2 -- src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs | 2 -- src/NLog/Targets/Wrappers/RandomizeGroupTarget.cs | 2 -- src/NLog/Targets/Wrappers/RepeatingTargetWrapper.cs | 2 -- src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs | 2 -- src/NLog/Targets/Wrappers/RoundRobinGroupTarget.cs | 2 -- src/NLog/Targets/Wrappers/SplitGroupTarget.cs | 2 -- src/NLog/Targets/Wrappers/WrapperTargetBase.cs | 2 -- src/NLog/Time/AccurateLocalTimeSource.cs | 2 -- src/NLog/Time/AccurateUtcTimeSource.cs | 2 -- src/NLog/Time/CachedTimeSource.cs | 2 -- src/NLog/Time/FastLocalTimeSource.cs | 2 -- src/NLog/Time/FastUtcTimeSource.cs | 2 -- src/NLog/Time/TimeSource.cs | 2 -- src/NLog/Time/TimeSourceAttribute.cs | 2 -- 251 files changed, 502 deletions(-) diff --git a/src/NLog/Common/AsyncContinuation.cs b/src/NLog/Common/AsyncContinuation.cs index cb759240a1..c546a92ad5 100644 --- a/src/NLog/Common/AsyncContinuation.cs +++ b/src/NLog/Common/AsyncContinuation.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Common { using System; diff --git a/src/NLog/Common/AsyncHelpers.cs b/src/NLog/Common/AsyncHelpers.cs index cf42f45b9e..8a1a70d32c 100644 --- a/src/NLog/Common/AsyncHelpers.cs +++ b/src/NLog/Common/AsyncHelpers.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Common { using System; diff --git a/src/NLog/Common/AsyncLogEventInfo.cs b/src/NLog/Common/AsyncLogEventInfo.cs index a5be70ca43..454b8c234a 100644 --- a/src/NLog/Common/AsyncLogEventInfo.cs +++ b/src/NLog/Common/AsyncLogEventInfo.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Common { /// diff --git a/src/NLog/Common/AsynchronousAction.cs b/src/NLog/Common/AsynchronousAction.cs index 3ac7febde5..0397832be4 100644 --- a/src/NLog/Common/AsynchronousAction.cs +++ b/src/NLog/Common/AsynchronousAction.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Common { /// diff --git a/src/NLog/Common/ConversionHelpers.cs b/src/NLog/Common/ConversionHelpers.cs index b72011c73c..624ab4629c 100644 --- a/src/NLog/Common/ConversionHelpers.cs +++ b/src/NLog/Common/ConversionHelpers.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Common { using System; diff --git a/src/NLog/Common/IInternalLoggerContext.cs b/src/NLog/Common/IInternalLoggerContext.cs index 211f375e31..4186ce4955 100644 --- a/src/NLog/Common/IInternalLoggerContext.cs +++ b/src/NLog/Common/IInternalLoggerContext.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Common { using JetBrains.Annotations; diff --git a/src/NLog/Common/InternalEventOccurredHandler.cs b/src/NLog/Common/InternalEventOccurredHandler.cs index 914048f64f..94ed6c8116 100644 --- a/src/NLog/Common/InternalEventOccurredHandler.cs +++ b/src/NLog/Common/InternalEventOccurredHandler.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Common { /// diff --git a/src/NLog/Common/InternalLogEventArgs.cs b/src/NLog/Common/InternalLogEventArgs.cs index 928e8f4a97..ec852cfa1f 100644 --- a/src/NLog/Common/InternalLogEventArgs.cs +++ b/src/NLog/Common/InternalLogEventArgs.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Common { using System; diff --git a/src/NLog/Common/InternalLogger-generated.cs b/src/NLog/Common/InternalLogger-generated.cs index cffe363f10..7e877c095e 100644 --- a/src/NLog/Common/InternalLogger-generated.cs +++ b/src/NLog/Common/InternalLogger-generated.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Common { using System; diff --git a/src/NLog/Common/InternalLogger-generated.tt b/src/NLog/Common/InternalLogger-generated.tt index 3439149877..7b22142d23 100644 --- a/src/NLog/Common/InternalLogger-generated.tt +++ b/src/NLog/Common/InternalLogger-generated.tt @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Common { using System; diff --git a/src/NLog/Common/InternalLogger.cs b/src/NLog/Common/InternalLogger.cs index 4f5e1906f7..8a1adba4e2 100644 --- a/src/NLog/Common/InternalLogger.cs +++ b/src/NLog/Common/InternalLogger.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Common { using System; diff --git a/src/NLog/Common/LogEventInfoBuffer.cs b/src/NLog/Common/LogEventInfoBuffer.cs index b7d8a13e97..e5e420f08d 100644 --- a/src/NLog/Common/LogEventInfoBuffer.cs +++ b/src/NLog/Common/LogEventInfoBuffer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Common { using System; diff --git a/src/NLog/Conditions/ConditionAndExpression.cs b/src/NLog/Conditions/ConditionAndExpression.cs index 0b8a263cc6..379364ab35 100644 --- a/src/NLog/Conditions/ConditionAndExpression.cs +++ b/src/NLog/Conditions/ConditionAndExpression.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { /// diff --git a/src/NLog/Conditions/ConditionEvaluationException.cs b/src/NLog/Conditions/ConditionEvaluationException.cs index 42c5c65857..1ba94d8091 100644 --- a/src/NLog/Conditions/ConditionEvaluationException.cs +++ b/src/NLog/Conditions/ConditionEvaluationException.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { using System; diff --git a/src/NLog/Conditions/ConditionExceptionExpression.cs b/src/NLog/Conditions/ConditionExceptionExpression.cs index 4ceac63bb4..a261065e05 100644 --- a/src/NLog/Conditions/ConditionExceptionExpression.cs +++ b/src/NLog/Conditions/ConditionExceptionExpression.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { /// diff --git a/src/NLog/Conditions/ConditionExpression.cs b/src/NLog/Conditions/ConditionExpression.cs index 36526ac4dc..3b34453615 100644 --- a/src/NLog/Conditions/ConditionExpression.cs +++ b/src/NLog/Conditions/ConditionExpression.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { using System; diff --git a/src/NLog/Conditions/ConditionLayoutExpression.cs b/src/NLog/Conditions/ConditionLayoutExpression.cs index 143429d891..d5fcd55bd1 100644 --- a/src/NLog/Conditions/ConditionLayoutExpression.cs +++ b/src/NLog/Conditions/ConditionLayoutExpression.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { using System.Text; diff --git a/src/NLog/Conditions/ConditionLevelExpression.cs b/src/NLog/Conditions/ConditionLevelExpression.cs index 6e38531664..b243df6b23 100644 --- a/src/NLog/Conditions/ConditionLevelExpression.cs +++ b/src/NLog/Conditions/ConditionLevelExpression.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { /// diff --git a/src/NLog/Conditions/ConditionLiteralExpression.cs b/src/NLog/Conditions/ConditionLiteralExpression.cs index a46b8091df..c93a6d1813 100644 --- a/src/NLog/Conditions/ConditionLiteralExpression.cs +++ b/src/NLog/Conditions/ConditionLiteralExpression.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { using System; diff --git a/src/NLog/Conditions/ConditionLoggerNameExpression.cs b/src/NLog/Conditions/ConditionLoggerNameExpression.cs index dc88c2a93e..fd4a7d0120 100644 --- a/src/NLog/Conditions/ConditionLoggerNameExpression.cs +++ b/src/NLog/Conditions/ConditionLoggerNameExpression.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { /// diff --git a/src/NLog/Conditions/ConditionMessageExpression.cs b/src/NLog/Conditions/ConditionMessageExpression.cs index 73baa989b2..a7c35b239e 100644 --- a/src/NLog/Conditions/ConditionMessageExpression.cs +++ b/src/NLog/Conditions/ConditionMessageExpression.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { /// diff --git a/src/NLog/Conditions/ConditionMethodAttribute.cs b/src/NLog/Conditions/ConditionMethodAttribute.cs index 62686425f1..c95d96028c 100644 --- a/src/NLog/Conditions/ConditionMethodAttribute.cs +++ b/src/NLog/Conditions/ConditionMethodAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { using System; diff --git a/src/NLog/Conditions/ConditionMethodExpression.cs b/src/NLog/Conditions/ConditionMethodExpression.cs index 403391aa96..308103cf0e 100644 --- a/src/NLog/Conditions/ConditionMethodExpression.cs +++ b/src/NLog/Conditions/ConditionMethodExpression.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { using System; diff --git a/src/NLog/Conditions/ConditionMethods.cs b/src/NLog/Conditions/ConditionMethods.cs index bebe6eea9a..846086739f 100644 --- a/src/NLog/Conditions/ConditionMethods.cs +++ b/src/NLog/Conditions/ConditionMethods.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { using System; diff --git a/src/NLog/Conditions/ConditionMethodsAttribute.cs b/src/NLog/Conditions/ConditionMethodsAttribute.cs index ae76082de3..1adb01681f 100644 --- a/src/NLog/Conditions/ConditionMethodsAttribute.cs +++ b/src/NLog/Conditions/ConditionMethodsAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { using System; diff --git a/src/NLog/Conditions/ConditionNotExpression.cs b/src/NLog/Conditions/ConditionNotExpression.cs index 5fb79cf5d5..9a7bd21cf3 100644 --- a/src/NLog/Conditions/ConditionNotExpression.cs +++ b/src/NLog/Conditions/ConditionNotExpression.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { /// diff --git a/src/NLog/Conditions/ConditionOrExpression.cs b/src/NLog/Conditions/ConditionOrExpression.cs index 946daaa2ed..4fbc4261f0 100644 --- a/src/NLog/Conditions/ConditionOrExpression.cs +++ b/src/NLog/Conditions/ConditionOrExpression.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { /// diff --git a/src/NLog/Conditions/ConditionParseException.cs b/src/NLog/Conditions/ConditionParseException.cs index ba5a17c2e1..5b6c1d73d1 100644 --- a/src/NLog/Conditions/ConditionParseException.cs +++ b/src/NLog/Conditions/ConditionParseException.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { using System; diff --git a/src/NLog/Conditions/ConditionParser.cs b/src/NLog/Conditions/ConditionParser.cs index 3de397b5c5..1c1b4bd6e5 100644 --- a/src/NLog/Conditions/ConditionParser.cs +++ b/src/NLog/Conditions/ConditionParser.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { using System; diff --git a/src/NLog/Conditions/ConditionRelationalExpression.cs b/src/NLog/Conditions/ConditionRelationalExpression.cs index 3f7e5bc9f5..3b31764a5c 100644 --- a/src/NLog/Conditions/ConditionRelationalExpression.cs +++ b/src/NLog/Conditions/ConditionRelationalExpression.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { using System; diff --git a/src/NLog/Conditions/ConditionRelationalOperator.cs b/src/NLog/Conditions/ConditionRelationalOperator.cs index 5fa2afd346..55d9f92922 100644 --- a/src/NLog/Conditions/ConditionRelationalOperator.cs +++ b/src/NLog/Conditions/ConditionRelationalOperator.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { /// diff --git a/src/NLog/Conditions/ConditionTokenizer.cs b/src/NLog/Conditions/ConditionTokenizer.cs index 1fb5b35e09..546aa7d223 100644 --- a/src/NLog/Conditions/ConditionTokenizer.cs +++ b/src/NLog/Conditions/ConditionTokenizer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Conditions { using System; diff --git a/src/NLog/Config/AdvancedAttribute.cs b/src/NLog/Config/AdvancedAttribute.cs index c2bbcd45b8..b0a632efed 100644 --- a/src/NLog/Config/AdvancedAttribute.cs +++ b/src/NLog/Config/AdvancedAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/AppDomainFixedOutputAttribute.cs b/src/NLog/Config/AppDomainFixedOutputAttribute.cs index 3face60b14..9cfc5634d2 100644 --- a/src/NLog/Config/AppDomainFixedOutputAttribute.cs +++ b/src/NLog/Config/AppDomainFixedOutputAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/ArrayParameterAttribute.cs b/src/NLog/Config/ArrayParameterAttribute.cs index b4bea5c056..596a902f94 100644 --- a/src/NLog/Config/ArrayParameterAttribute.cs +++ b/src/NLog/Config/ArrayParameterAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/AssemblyExtensionLoader.cs b/src/NLog/Config/AssemblyExtensionLoader.cs index 90d02ce160..7e34620324 100644 --- a/src/NLog/Config/AssemblyExtensionLoader.cs +++ b/src/NLog/Config/AssemblyExtensionLoader.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index 526353f9c5..5fb32260c3 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { /// diff --git a/src/NLog/Config/AssemblyExtensionTypes.tt b/src/NLog/Config/AssemblyExtensionTypes.tt index 0c83ff3574..6a1d70d35a 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.tt +++ b/src/NLog/Config/AssemblyExtensionTypes.tt @@ -41,8 +41,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { /// diff --git a/src/NLog/Config/ConfigSectionHandler.cs b/src/NLog/Config/ConfigSectionHandler.cs index 6d8778f020..50368264ae 100644 --- a/src/NLog/Config/ConfigSectionHandler.cs +++ b/src/NLog/Config/ConfigSectionHandler.cs @@ -33,8 +33,6 @@ #if NETFRAMEWORK -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index 860a891324..ff92edb6f5 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/DefaultParameterAttribute.cs b/src/NLog/Config/DefaultParameterAttribute.cs index cad2d7c8a6..2358261460 100644 --- a/src/NLog/Config/DefaultParameterAttribute.cs +++ b/src/NLog/Config/DefaultParameterAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/DynamicLogLevelFilter.cs b/src/NLog/Config/DynamicLogLevelFilter.cs index 588219104e..a21b363bc9 100644 --- a/src/NLog/Config/DynamicLogLevelFilter.cs +++ b/src/NLog/Config/DynamicLogLevelFilter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/DynamicRangeLevelFilter.cs b/src/NLog/Config/DynamicRangeLevelFilter.cs index 9c818856da..8f4aa9e073 100644 --- a/src/NLog/Config/DynamicRangeLevelFilter.cs +++ b/src/NLog/Config/DynamicRangeLevelFilter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/Factory.cs b/src/NLog/Config/Factory.cs index 1647fbbe2c..8891cc44f6 100644 --- a/src/NLog/Config/Factory.cs +++ b/src/NLog/Config/Factory.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/IFactory.cs b/src/NLog/Config/IFactory.cs index 458cadbd30..820cc7c8c4 100644 --- a/src/NLog/Config/IFactory.cs +++ b/src/NLog/Config/IFactory.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/IIncludeContext.cs b/src/NLog/Config/IIncludeContext.cs index b9bd8ad570..f42bc94049 100644 --- a/src/NLog/Config/IIncludeContext.cs +++ b/src/NLog/Config/IIncludeContext.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { /// diff --git a/src/NLog/Config/IInstallable.cs b/src/NLog/Config/IInstallable.cs index a949428f36..b6d0635201 100644 --- a/src/NLog/Config/IInstallable.cs +++ b/src/NLog/Config/IInstallable.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { /// diff --git a/src/NLog/Config/ILoggingConfigurationElement.cs b/src/NLog/Config/ILoggingConfigurationElement.cs index 220bcdc494..b0088a8c50 100644 --- a/src/NLog/Config/ILoggingConfigurationElement.cs +++ b/src/NLog/Config/ILoggingConfigurationElement.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System.Collections.Generic; diff --git a/src/NLog/Config/ILoggingConfigurationLoader.cs b/src/NLog/Config/ILoggingConfigurationLoader.cs index 70ba3af5db..4c6b7e64b4 100644 --- a/src/NLog/Config/ILoggingConfigurationLoader.cs +++ b/src/NLog/Config/ILoggingConfigurationLoader.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/ILoggingRuleLevelFilter.cs b/src/NLog/Config/ILoggingRuleLevelFilter.cs index 43324f7bb4..3c6036d4c5 100644 --- a/src/NLog/Config/ILoggingRuleLevelFilter.cs +++ b/src/NLog/Config/ILoggingRuleLevelFilter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { internal interface ILoggingRuleLevelFilter diff --git a/src/NLog/Config/IPropertyTypeConverter.cs b/src/NLog/Config/IPropertyTypeConverter.cs index 0387e28c97..a7dbc9070e 100644 --- a/src/NLog/Config/IPropertyTypeConverter.cs +++ b/src/NLog/Config/IPropertyTypeConverter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/ISetupBuilder.cs b/src/NLog/Config/ISetupBuilder.cs index 90be76f421..fa5001a0a6 100644 --- a/src/NLog/Config/ISetupBuilder.cs +++ b/src/NLog/Config/ISetupBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { /// diff --git a/src/NLog/Config/ISetupConfigurationLoggingRuleBuilder.cs b/src/NLog/Config/ISetupConfigurationLoggingRuleBuilder.cs index 44c85f4461..c8e95c0e61 100644 --- a/src/NLog/Config/ISetupConfigurationLoggingRuleBuilder.cs +++ b/src/NLog/Config/ISetupConfigurationLoggingRuleBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { /// diff --git a/src/NLog/Config/ISetupConfigurationTargetBuilder.cs b/src/NLog/Config/ISetupConfigurationTargetBuilder.cs index 4f2914a4c9..9eac334bc9 100644 --- a/src/NLog/Config/ISetupConfigurationTargetBuilder.cs +++ b/src/NLog/Config/ISetupConfigurationTargetBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System.Collections.Generic; diff --git a/src/NLog/Config/ISetupExtensionsBuilder.cs b/src/NLog/Config/ISetupExtensionsBuilder.cs index 8bfbf74e43..18000173c9 100644 --- a/src/NLog/Config/ISetupExtensionsBuilder.cs +++ b/src/NLog/Config/ISetupExtensionsBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { /// diff --git a/src/NLog/Config/ISetupInternalLoggerBuilder.cs b/src/NLog/Config/ISetupInternalLoggerBuilder.cs index a0ff7e6523..9216062465 100644 --- a/src/NLog/Config/ISetupInternalLoggerBuilder.cs +++ b/src/NLog/Config/ISetupInternalLoggerBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { /// diff --git a/src/NLog/Config/ISetupLoadConfigurationBuilder.cs b/src/NLog/Config/ISetupLoadConfigurationBuilder.cs index bb32feea82..8f6cbcd68c 100644 --- a/src/NLog/Config/ISetupLoadConfigurationBuilder.cs +++ b/src/NLog/Config/ISetupLoadConfigurationBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { /// diff --git a/src/NLog/Config/ISetupLogFactoryBuilder.cs b/src/NLog/Config/ISetupLogFactoryBuilder.cs index 4f00aa6db6..ae7f00950b 100644 --- a/src/NLog/Config/ISetupLogFactoryBuilder.cs +++ b/src/NLog/Config/ISetupLogFactoryBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { /// diff --git a/src/NLog/Config/ISetupSerializationBuilder.cs b/src/NLog/Config/ISetupSerializationBuilder.cs index 4d49e4e638..a86d83e36f 100644 --- a/src/NLog/Config/ISetupSerializationBuilder.cs +++ b/src/NLog/Config/ISetupSerializationBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { /// diff --git a/src/NLog/Config/IUsesStackTrace.cs b/src/NLog/Config/IUsesStackTrace.cs index a22bf53196..39e7590656 100644 --- a/src/NLog/Config/IUsesStackTrace.cs +++ b/src/NLog/Config/IUsesStackTrace.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { /// diff --git a/src/NLog/Config/InstallationContext.cs b/src/NLog/Config/InstallationContext.cs index afcc3f338e..6893e09371 100644 --- a/src/NLog/Config/InstallationContext.cs +++ b/src/NLog/Config/InstallationContext.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/LoggerNameMatcher.cs b/src/NLog/Config/LoggerNameMatcher.cs index 7c286f0a64..236ae65fb2 100644 --- a/src/NLog/Config/LoggerNameMatcher.cs +++ b/src/NLog/Config/LoggerNameMatcher.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/LoggingConfiguration.cs b/src/NLog/Config/LoggingConfiguration.cs index f1154cb7f9..56724e37d5 100644 --- a/src/NLog/Config/LoggingConfiguration.cs +++ b/src/NLog/Config/LoggingConfiguration.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/LoggingConfigurationChangedEventArgs.cs b/src/NLog/Config/LoggingConfigurationChangedEventArgs.cs index 63d9ee1bd7..042ad3fd18 100644 --- a/src/NLog/Config/LoggingConfigurationChangedEventArgs.cs +++ b/src/NLog/Config/LoggingConfigurationChangedEventArgs.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/LoggingConfigurationElementExtensions.cs b/src/NLog/Config/LoggingConfigurationElementExtensions.cs index d2c5f12fd2..0570ed7602 100644 --- a/src/NLog/Config/LoggingConfigurationElementExtensions.cs +++ b/src/NLog/Config/LoggingConfigurationElementExtensions.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/LoggingConfigurationFileLoader.cs b/src/NLog/Config/LoggingConfigurationFileLoader.cs index b6ba2998fc..92df616217 100644 --- a/src/NLog/Config/LoggingConfigurationFileLoader.cs +++ b/src/NLog/Config/LoggingConfigurationFileLoader.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index 4fd4b57364..4505c2593b 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/LoggingRule.cs b/src/NLog/Config/LoggingRule.cs index 3ca8aea3c7..65558696ff 100644 --- a/src/NLog/Config/LoggingRule.cs +++ b/src/NLog/Config/LoggingRule.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/LoggingRuleLevelFilter.cs b/src/NLog/Config/LoggingRuleLevelFilter.cs index 0392185a73..a5b83ca853 100644 --- a/src/NLog/Config/LoggingRuleLevelFilter.cs +++ b/src/NLog/Config/LoggingRuleLevelFilter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/MethodFactory.cs b/src/NLog/Config/MethodFactory.cs index 265860b24d..da97e8f349 100644 --- a/src/NLog/Config/MethodFactory.cs +++ b/src/NLog/Config/MethodFactory.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/MutableUnsafeAttribute.cs b/src/NLog/Config/MutableUnsafeAttribute.cs index 5740fa6533..e6842ae39d 100644 --- a/src/NLog/Config/MutableUnsafeAttribute.cs +++ b/src/NLog/Config/MutableUnsafeAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/NLogConfigurationIgnorePropertyAttribute.cs b/src/NLog/Config/NLogConfigurationIgnorePropertyAttribute.cs index e04df33255..36f6a43b81 100644 --- a/src/NLog/Config/NLogConfigurationIgnorePropertyAttribute.cs +++ b/src/NLog/Config/NLogConfigurationIgnorePropertyAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/NLogConfigurationItemAttribute.cs b/src/NLog/Config/NLogConfigurationItemAttribute.cs index e3f20b8781..e4c1db0dca 100644 --- a/src/NLog/Config/NLogConfigurationItemAttribute.cs +++ b/src/NLog/Config/NLogConfigurationItemAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/NLogDependencyResolveException.cs b/src/NLog/Config/NLogDependencyResolveException.cs index caac0be9f2..7922812bd8 100644 --- a/src/NLog/Config/NLogDependencyResolveException.cs +++ b/src/NLog/Config/NLogDependencyResolveException.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/NameBaseAttribute.cs b/src/NLog/Config/NameBaseAttribute.cs index 6a8821fa18..848d3880aa 100644 --- a/src/NLog/Config/NameBaseAttribute.cs +++ b/src/NLog/Config/NameBaseAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/PropertyTypeConverter.cs b/src/NLog/Config/PropertyTypeConverter.cs index 9025385a10..d30d830585 100644 --- a/src/NLog/Config/PropertyTypeConverter.cs +++ b/src/NLog/Config/PropertyTypeConverter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/RequiredParameterAttribute.cs b/src/NLog/Config/RequiredParameterAttribute.cs index 4e83ad8b09..73c2e24a67 100644 --- a/src/NLog/Config/RequiredParameterAttribute.cs +++ b/src/NLog/Config/RequiredParameterAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/ServiceRepositoryUpdateEventArgs.cs b/src/NLog/Config/ServiceRepositoryUpdateEventArgs.cs index ca1ec68e1e..b6d37666f7 100644 --- a/src/NLog/Config/ServiceRepositoryUpdateEventArgs.cs +++ b/src/NLog/Config/ServiceRepositoryUpdateEventArgs.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/SimpleConfigurator.cs b/src/NLog/Config/SimpleConfigurator.cs index 8e0325e3d3..4f17ee8bc7 100644 --- a/src/NLog/Config/SimpleConfigurator.cs +++ b/src/NLog/Config/SimpleConfigurator.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/ThreadAgnosticAttribute.cs b/src/NLog/Config/ThreadAgnosticAttribute.cs index 65ecaf3058..b262c597a1 100644 --- a/src/NLog/Config/ThreadAgnosticAttribute.cs +++ b/src/NLog/Config/ThreadAgnosticAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/ThreadAgnosticImmutableAttribute.cs b/src/NLog/Config/ThreadAgnosticImmutableAttribute.cs index 1c5e72b4d7..26f399afa6 100644 --- a/src/NLog/Config/ThreadAgnosticImmutableAttribute.cs +++ b/src/NLog/Config/ThreadAgnosticImmutableAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/ThreadSafeAttribute.cs b/src/NLog/Config/ThreadSafeAttribute.cs index c60af0b1f1..9b95a7f7ec 100644 --- a/src/NLog/Config/ThreadSafeAttribute.cs +++ b/src/NLog/Config/ThreadSafeAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/XmlLoggingConfiguration.cs b/src/NLog/Config/XmlLoggingConfiguration.cs index 5e61501968..0d2a69b65e 100644 --- a/src/NLog/Config/XmlLoggingConfiguration.cs +++ b/src/NLog/Config/XmlLoggingConfiguration.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/XmlLoggingConfigurationElement.cs b/src/NLog/Config/XmlLoggingConfigurationElement.cs index a14c3e6ec2..93347050e6 100644 --- a/src/NLog/Config/XmlLoggingConfigurationElement.cs +++ b/src/NLog/Config/XmlLoggingConfigurationElement.cs @@ -33,8 +33,6 @@ #if NETFRAMEWORK -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/XmlParserConfigurationElement.cs b/src/NLog/Config/XmlParserConfigurationElement.cs index 9bc1051654..34f53f0d3a 100644 --- a/src/NLog/Config/XmlParserConfigurationElement.cs +++ b/src/NLog/Config/XmlParserConfigurationElement.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Config/XmlParserException.cs b/src/NLog/Config/XmlParserException.cs index 8f234fbbce..10c2384542 100644 --- a/src/NLog/Config/XmlParserException.cs +++ b/src/NLog/Config/XmlParserException.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Config { using System; diff --git a/src/NLog/Filters/ConditionBasedFilter.cs b/src/NLog/Filters/ConditionBasedFilter.cs index 92f83a9013..f3c1af6601 100644 --- a/src/NLog/Filters/ConditionBasedFilter.cs +++ b/src/NLog/Filters/ConditionBasedFilter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Filters { using NLog.Conditions; diff --git a/src/NLog/Filters/Filter.cs b/src/NLog/Filters/Filter.cs index 68165e9942..779f4b7b0e 100644 --- a/src/NLog/Filters/Filter.cs +++ b/src/NLog/Filters/Filter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Filters { using NLog.Config; diff --git a/src/NLog/Filters/FilterAttribute.cs b/src/NLog/Filters/FilterAttribute.cs index e93c1175b0..c0e3b74e1e 100644 --- a/src/NLog/Filters/FilterAttribute.cs +++ b/src/NLog/Filters/FilterAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Filters { using System; diff --git a/src/NLog/Filters/LayoutBasedFilter.cs b/src/NLog/Filters/LayoutBasedFilter.cs index 4ed6b3e0d4..bc047f5952 100644 --- a/src/NLog/Filters/LayoutBasedFilter.cs +++ b/src/NLog/Filters/LayoutBasedFilter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Filters { using NLog.Layouts; diff --git a/src/NLog/Filters/WhenContainsFilter.cs b/src/NLog/Filters/WhenContainsFilter.cs index f2a901322d..e5135c6a66 100644 --- a/src/NLog/Filters/WhenContainsFilter.cs +++ b/src/NLog/Filters/WhenContainsFilter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Filters { using System; diff --git a/src/NLog/Filters/WhenEqualFilter.cs b/src/NLog/Filters/WhenEqualFilter.cs index 799d2a4e9e..e8a66cb08c 100644 --- a/src/NLog/Filters/WhenEqualFilter.cs +++ b/src/NLog/Filters/WhenEqualFilter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Filters { using System; diff --git a/src/NLog/Filters/WhenMethodFilter.cs b/src/NLog/Filters/WhenMethodFilter.cs index 111d0baae0..237ae1fa68 100644 --- a/src/NLog/Filters/WhenMethodFilter.cs +++ b/src/NLog/Filters/WhenMethodFilter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Filters { using System; diff --git a/src/NLog/Filters/WhenNotContainsFilter.cs b/src/NLog/Filters/WhenNotContainsFilter.cs index 0754d88b5f..53a4d54d85 100644 --- a/src/NLog/Filters/WhenNotContainsFilter.cs +++ b/src/NLog/Filters/WhenNotContainsFilter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Filters { using System; diff --git a/src/NLog/Filters/WhenNotEqualFilter.cs b/src/NLog/Filters/WhenNotEqualFilter.cs index 41c9b9492e..3abd7818dc 100644 --- a/src/NLog/Filters/WhenNotEqualFilter.cs +++ b/src/NLog/Filters/WhenNotEqualFilter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Filters { using System; diff --git a/src/NLog/Filters/WhenRepeatedFilter.cs b/src/NLog/Filters/WhenRepeatedFilter.cs index b97b13818e..5df47aa61e 100644 --- a/src/NLog/Filters/WhenRepeatedFilter.cs +++ b/src/NLog/Filters/WhenRepeatedFilter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Filters { using System; diff --git a/src/NLog/GlobalDiagnosticsContext.cs b/src/NLog/GlobalDiagnosticsContext.cs index 883f314532..db3e3db4ed 100644 --- a/src/NLog/GlobalDiagnosticsContext.cs +++ b/src/NLog/GlobalDiagnosticsContext.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/IJsonConverter.cs b/src/NLog/IJsonConverter.cs index a330a7d45a..5d80f2ce30 100644 --- a/src/NLog/IJsonConverter.cs +++ b/src/NLog/IJsonConverter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { /// diff --git a/src/NLog/ILogger-V1.cs b/src/NLog/ILogger-V1.cs index 17a7095514..37d53ae708 100644 --- a/src/NLog/ILogger-V1.cs +++ b/src/NLog/ILogger-V1.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/ILogger.cs b/src/NLog/ILogger.cs index 1f555bbbb1..8f8cae04c4 100644 --- a/src/NLog/ILogger.cs +++ b/src/NLog/ILogger.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/ILoggerBase-V1.cs b/src/NLog/ILoggerBase-V1.cs index cc9c0038b6..ff231cf7b2 100644 --- a/src/NLog/ILoggerBase-V1.cs +++ b/src/NLog/ILoggerBase-V1.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/ILoggerBase.cs b/src/NLog/ILoggerBase.cs index 18b63f09d9..3caa6142a4 100644 --- a/src/NLog/ILoggerBase.cs +++ b/src/NLog/ILoggerBase.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/ILoggerExtensions.cs b/src/NLog/ILoggerExtensions.cs index 5745803841..1e465e11f5 100644 --- a/src/NLog/ILoggerExtensions.cs +++ b/src/NLog/ILoggerExtensions.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/ISuppress.cs b/src/NLog/ISuppress.cs index aab961ee17..c402f3be62 100644 --- a/src/NLog/ISuppress.cs +++ b/src/NLog/ISuppress.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/IValueFormatter.cs b/src/NLog/IValueFormatter.cs index bc8b81906c..7bb47372a8 100644 --- a/src/NLog/IValueFormatter.cs +++ b/src/NLog/IValueFormatter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/Internal/AppEnvironmentWrapper.cs b/src/NLog/Internal/AppEnvironmentWrapper.cs index 2519364c7f..24e78a23e4 100644 --- a/src/NLog/Internal/AppEnvironmentWrapper.cs +++ b/src/NLog/Internal/AppEnvironmentWrapper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal.Fakeables { using System; diff --git a/src/NLog/Internal/AppendBuilderCreator.cs b/src/NLog/Internal/AppendBuilderCreator.cs index 99af5f7067..1a0f25a8fb 100644 --- a/src/NLog/Internal/AppendBuilderCreator.cs +++ b/src/NLog/Internal/AppendBuilderCreator.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/ArrayHelper.cs b/src/NLog/Internal/ArrayHelper.cs index f214150411..26c9d8ca01 100644 --- a/src/NLog/Internal/ArrayHelper.cs +++ b/src/NLog/Internal/ArrayHelper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { internal static class ArrayHelper diff --git a/src/NLog/Internal/AssemblyHelpers.cs b/src/NLog/Internal/AssemblyHelpers.cs index 20ff0954a5..e7dc092c30 100644 --- a/src/NLog/Internal/AssemblyHelpers.cs +++ b/src/NLog/Internal/AssemblyHelpers.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/AssemblyMetadataAttribute.cs b/src/NLog/Internal/AssemblyMetadataAttribute.cs index fec40782df..6856c4fd30 100644 --- a/src/NLog/Internal/AssemblyMetadataAttribute.cs +++ b/src/NLog/Internal/AssemblyMetadataAttribute.cs @@ -33,8 +33,6 @@ #if NET35 -#nullable enable - namespace System.Reflection { [AttributeUsage(AttributeTargets.Assembly)] diff --git a/src/NLog/Internal/AsyncOperationCounter.cs b/src/NLog/Internal/AsyncOperationCounter.cs index 12236a0ace..6b6689cbfb 100644 --- a/src/NLog/Internal/AsyncOperationCounter.cs +++ b/src/NLog/Internal/AsyncOperationCounter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/CallSiteInformation.cs b/src/NLog/Internal/CallSiteInformation.cs index 7ab9e5f294..169b480bae 100644 --- a/src/NLog/Internal/CallSiteInformation.cs +++ b/src/NLog/Internal/CallSiteInformation.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/CollectionExtensions.cs b/src/NLog/Internal/CollectionExtensions.cs index 01b32d84b8..61e00798de 100644 --- a/src/NLog/Internal/CollectionExtensions.cs +++ b/src/NLog/Internal/CollectionExtensions.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/ConfigVariablesDictionary.cs b/src/NLog/Internal/ConfigVariablesDictionary.cs index 841d695578..ea941c56ef 100644 --- a/src/NLog/Internal/ConfigVariablesDictionary.cs +++ b/src/NLog/Internal/ConfigVariablesDictionary.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/ConfigurationManager.cs b/src/NLog/Internal/ConfigurationManager.cs index d5fffaf813..31447a29ab 100644 --- a/src/NLog/Internal/ConfigurationManager.cs +++ b/src/NLog/Internal/ConfigurationManager.cs @@ -33,8 +33,6 @@ #if NETFRAMEWORK -#nullable enable - namespace NLog.Internal { using System.Collections.Specialized; diff --git a/src/NLog/Internal/DictionaryEntryEnumerable.cs b/src/NLog/Internal/DictionaryEntryEnumerable.cs index 9182d4dd1f..75fd0cb437 100644 --- a/src/NLog/Internal/DictionaryEntryEnumerable.cs +++ b/src/NLog/Internal/DictionaryEntryEnumerable.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/DynamicallyAccessedMemberTypes.cs b/src/NLog/Internal/DynamicallyAccessedMemberTypes.cs index 7f5b0210ec..d9f6a1990e 100644 --- a/src/NLog/Internal/DynamicallyAccessedMemberTypes.cs +++ b/src/NLog/Internal/DynamicallyAccessedMemberTypes.cs @@ -33,8 +33,6 @@ #if !NET5_0_OR_GREATER -#nullable enable - namespace System.Diagnostics.CodeAnalysis { [AttributeUsage( diff --git a/src/NLog/Internal/EnvironmentHelper.cs b/src/NLog/Internal/EnvironmentHelper.cs index 0de106123b..e1aa5004e5 100644 --- a/src/NLog/Internal/EnvironmentHelper.cs +++ b/src/NLog/Internal/EnvironmentHelper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/ExceptionHelper.cs b/src/NLog/Internal/ExceptionHelper.cs index c8df26f564..f6955758c5 100644 --- a/src/NLog/Internal/ExceptionHelper.cs +++ b/src/NLog/Internal/ExceptionHelper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/ExceptionMessageFormatProvider.cs b/src/NLog/Internal/ExceptionMessageFormatProvider.cs index 0b7a6d02eb..c0b596ddcd 100644 --- a/src/NLog/Internal/ExceptionMessageFormatProvider.cs +++ b/src/NLog/Internal/ExceptionMessageFormatProvider.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/FileInfoHelper.cs b/src/NLog/Internal/FileInfoHelper.cs index 6b9cecccec..9db8ab3dd7 100644 --- a/src/NLog/Internal/FileInfoHelper.cs +++ b/src/NLog/Internal/FileInfoHelper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/FormatHelper.cs b/src/NLog/Internal/FormatHelper.cs index d28aed87df..d8f37550dd 100644 --- a/src/NLog/Internal/FormatHelper.cs +++ b/src/NLog/Internal/FormatHelper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/Guard.cs b/src/NLog/Internal/Guard.cs index 3a85b573de..a7c1b52d43 100644 --- a/src/NLog/Internal/Guard.cs +++ b/src/NLog/Internal/Guard.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - #if !NETCOREAPP3_1_OR_GREATER namespace System.Runtime.CompilerServices diff --git a/src/NLog/Internal/IAppEnvironment.cs b/src/NLog/Internal/IAppEnvironment.cs index d8b40624a4..ff2d09191e 100644 --- a/src/NLog/Internal/IAppEnvironment.cs +++ b/src/NLog/Internal/IAppEnvironment.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal.Fakeables { using System; diff --git a/src/NLog/Internal/IConfigurationManager.cs b/src/NLog/Internal/IConfigurationManager.cs index 938bfacb13..8415fd06b6 100644 --- a/src/NLog/Internal/IConfigurationManager.cs +++ b/src/NLog/Internal/IConfigurationManager.cs @@ -33,8 +33,6 @@ #if NETFRAMEWORK -#nullable enable - namespace NLog.Internal { using System.Collections.Specialized; diff --git a/src/NLog/Internal/IFileSystem.cs b/src/NLog/Internal/IFileSystem.cs index 44da88ec58..0f04118cce 100644 --- a/src/NLog/Internal/IFileSystem.cs +++ b/src/NLog/Internal/IFileSystem.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal.Fakeables { using System.IO; diff --git a/src/NLog/Internal/ILogMessageFormatter.cs b/src/NLog/Internal/ILogMessageFormatter.cs index 09666e360e..53981628f4 100644 --- a/src/NLog/Internal/ILogMessageFormatter.cs +++ b/src/NLog/Internal/ILogMessageFormatter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System.Text; diff --git a/src/NLog/Internal/IRawValue.cs b/src/NLog/Internal/IRawValue.cs index 63cd9a95a8..3f1c503b3a 100644 --- a/src/NLog/Internal/IRawValue.cs +++ b/src/NLog/Internal/IRawValue.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { /// diff --git a/src/NLog/Internal/IRenderable.cs b/src/NLog/Internal/IRenderable.cs index 241b81443c..cfba216bb8 100644 --- a/src/NLog/Internal/IRenderable.cs +++ b/src/NLog/Internal/IRenderable.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { /// diff --git a/src/NLog/Internal/IStringValueRenderer.cs b/src/NLog/Internal/IStringValueRenderer.cs index 33bbf5cc94..c186de99cf 100644 --- a/src/NLog/Internal/IStringValueRenderer.cs +++ b/src/NLog/Internal/IStringValueRenderer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { /// diff --git a/src/NLog/Internal/ISupportsInitialize.cs b/src/NLog/Internal/ISupportsInitialize.cs index 4fe1111f0f..c73d30cac9 100644 --- a/src/NLog/Internal/ISupportsInitialize.cs +++ b/src/NLog/Internal/ISupportsInitialize.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using NLog.Config; diff --git a/src/NLog/Internal/ITargetWithFilterChain.cs b/src/NLog/Internal/ITargetWithFilterChain.cs index 5b687f5be0..2b7714ba8f 100644 --- a/src/NLog/Internal/ITargetWithFilterChain.cs +++ b/src/NLog/Internal/ITargetWithFilterChain.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/LogMessageStringFormatter.cs b/src/NLog/Internal/LogMessageStringFormatter.cs index 92af25fb14..e3246a8092 100644 --- a/src/NLog/Internal/LogMessageStringFormatter.cs +++ b/src/NLog/Internal/LogMessageStringFormatter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System.Globalization; diff --git a/src/NLog/Internal/LogMessageTemplateFormatter.cs b/src/NLog/Internal/LogMessageTemplateFormatter.cs index f5537e066b..7e24d07485 100644 --- a/src/NLog/Internal/LogMessageTemplateFormatter.cs +++ b/src/NLog/Internal/LogMessageTemplateFormatter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/MruCache.cs b/src/NLog/Internal/MruCache.cs index 33e6f06acc..fa5d7bf173 100644 --- a/src/NLog/Internal/MruCache.cs +++ b/src/NLog/Internal/MruCache.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System.Collections.Generic; diff --git a/src/NLog/Internal/MultiFileWatcher.cs b/src/NLog/Internal/MultiFileWatcher.cs index fc77e8ff14..55112b9a25 100644 --- a/src/NLog/Internal/MultiFileWatcher.cs +++ b/src/NLog/Internal/MultiFileWatcher.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/NativeMethods.cs b/src/NLog/Internal/NativeMethods.cs index 272e4598f0..f9c185653b 100644 --- a/src/NLog/Internal/NativeMethods.cs +++ b/src/NLog/Internal/NativeMethods.cs @@ -33,8 +33,6 @@ #if NETFRAMEWORK -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/ObjectGraphScanner.cs b/src/NLog/Internal/ObjectGraphScanner.cs index 7b3e6d998e..daf87b011e 100644 --- a/src/NLog/Internal/ObjectGraphScanner.cs +++ b/src/NLog/Internal/ObjectGraphScanner.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/ObjectHandleSerializer.cs b/src/NLog/Internal/ObjectHandleSerializer.cs index 835332bfed..698fb631f6 100644 --- a/src/NLog/Internal/ObjectHandleSerializer.cs +++ b/src/NLog/Internal/ObjectHandleSerializer.cs @@ -33,8 +33,6 @@ #if NET35 || NET40 || NET45 -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/ObjectPropertyPath.cs b/src/NLog/Internal/ObjectPropertyPath.cs index 76188f29c2..72819e4eba 100644 --- a/src/NLog/Internal/ObjectPropertyPath.cs +++ b/src/NLog/Internal/ObjectPropertyPath.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { internal struct ObjectPropertyPath diff --git a/src/NLog/Internal/ObjectReflectionCache.cs b/src/NLog/Internal/ObjectReflectionCache.cs index fe704b214c..e7aacdd773 100644 --- a/src/NLog/Internal/ObjectReflectionCache.cs +++ b/src/NLog/Internal/ObjectReflectionCache.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/OverloadResolutionPriorityAttribute.cs b/src/NLog/Internal/OverloadResolutionPriorityAttribute.cs index f09fd630ca..bea34420d8 100644 --- a/src/NLog/Internal/OverloadResolutionPriorityAttribute.cs +++ b/src/NLog/Internal/OverloadResolutionPriorityAttribute.cs @@ -33,8 +33,6 @@ #if NETSTANDARD2_1_OR_GREATER && !NET9_0_OR_GREATER -#nullable enable - namespace System.Runtime.CompilerServices { /// diff --git a/src/NLog/Internal/PlatformDetector.cs b/src/NLog/Internal/PlatformDetector.cs index ab5aa07ec1..61c5a85d35 100644 --- a/src/NLog/Internal/PlatformDetector.cs +++ b/src/NLog/Internal/PlatformDetector.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/PropertiesDictionary.cs b/src/NLog/Internal/PropertiesDictionary.cs index b1fb4d9bd6..ad3ac0be66 100644 --- a/src/NLog/Internal/PropertiesDictionary.cs +++ b/src/NLog/Internal/PropertiesDictionary.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/ReusableAsyncLogEventList.cs b/src/NLog/Internal/ReusableAsyncLogEventList.cs index 01bbb95165..c614816232 100644 --- a/src/NLog/Internal/ReusableAsyncLogEventList.cs +++ b/src/NLog/Internal/ReusableAsyncLogEventList.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System.Collections.Generic; diff --git a/src/NLog/Internal/ReusableBufferCreator.cs b/src/NLog/Internal/ReusableBufferCreator.cs index e784ba5d79..85a851eac8 100644 --- a/src/NLog/Internal/ReusableBufferCreator.cs +++ b/src/NLog/Internal/ReusableBufferCreator.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { /// diff --git a/src/NLog/Internal/ReusableBuilderCreator.cs b/src/NLog/Internal/ReusableBuilderCreator.cs index d43754ddba..bfedaae321 100644 --- a/src/NLog/Internal/ReusableBuilderCreator.cs +++ b/src/NLog/Internal/ReusableBuilderCreator.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System.Text; diff --git a/src/NLog/Internal/ReusableObjectCreator.cs b/src/NLog/Internal/ReusableObjectCreator.cs index 29eb119d5c..43c8778d6d 100644 --- a/src/NLog/Internal/ReusableObjectCreator.cs +++ b/src/NLog/Internal/ReusableObjectCreator.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/ReusableStreamCreator.cs b/src/NLog/Internal/ReusableStreamCreator.cs index f9d9d6c56d..bf6d482e1c 100644 --- a/src/NLog/Internal/ReusableStreamCreator.cs +++ b/src/NLog/Internal/ReusableStreamCreator.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/RuntimeOS.cs b/src/NLog/Internal/RuntimeOS.cs index 8eccbf8362..79da0a101d 100644 --- a/src/NLog/Internal/RuntimeOS.cs +++ b/src/NLog/Internal/RuntimeOS.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { /// diff --git a/src/NLog/Internal/ScopeContextAsyncState.cs b/src/NLog/Internal/ScopeContextAsyncState.cs index 2af2994d67..4b29d50c18 100644 --- a/src/NLog/Internal/ScopeContextAsyncState.cs +++ b/src/NLog/Internal/ScopeContextAsyncState.cs @@ -33,8 +33,6 @@ #if !NET35 && !NET40 && !NET45 -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/ScopeContextPropertyEnumerator.cs b/src/NLog/Internal/ScopeContextPropertyEnumerator.cs index c99cb4f862..47cd1cd15c 100644 --- a/src/NLog/Internal/ScopeContextPropertyEnumerator.cs +++ b/src/NLog/Internal/ScopeContextPropertyEnumerator.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System.Collections.Generic; diff --git a/src/NLog/Internal/SetupBuilder.cs b/src/NLog/Internal/SetupBuilder.cs index 8372da8871..b5b6a9b165 100644 --- a/src/NLog/Internal/SetupBuilder.cs +++ b/src/NLog/Internal/SetupBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using NLog.Config; diff --git a/src/NLog/Internal/SetupConfigurationLoggingRuleBuilder.cs b/src/NLog/Internal/SetupConfigurationLoggingRuleBuilder.cs index 9d635ceb7b..f62ba7c108 100644 --- a/src/NLog/Internal/SetupConfigurationLoggingRuleBuilder.cs +++ b/src/NLog/Internal/SetupConfigurationLoggingRuleBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System.Collections; diff --git a/src/NLog/Internal/SetupConfigurationTargetBuilder.cs b/src/NLog/Internal/SetupConfigurationTargetBuilder.cs index 30e56ba6d0..a7a8a25889 100644 --- a/src/NLog/Internal/SetupConfigurationTargetBuilder.cs +++ b/src/NLog/Internal/SetupConfigurationTargetBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/SetupExtensionsBuilder.cs b/src/NLog/Internal/SetupExtensionsBuilder.cs index e3a3cef6e8..ce09dd721d 100644 --- a/src/NLog/Internal/SetupExtensionsBuilder.cs +++ b/src/NLog/Internal/SetupExtensionsBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using NLog.Config; diff --git a/src/NLog/Internal/SetupInternalLoggerBuilder.cs b/src/NLog/Internal/SetupInternalLoggerBuilder.cs index d615b6e71a..8f16c4629f 100644 --- a/src/NLog/Internal/SetupInternalLoggerBuilder.cs +++ b/src/NLog/Internal/SetupInternalLoggerBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using NLog.Config; diff --git a/src/NLog/Internal/SetupLogFactoryBuilder.cs b/src/NLog/Internal/SetupLogFactoryBuilder.cs index 80b0ea1844..172e21a93a 100644 --- a/src/NLog/Internal/SetupLogFactoryBuilder.cs +++ b/src/NLog/Internal/SetupLogFactoryBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using NLog.Config; diff --git a/src/NLog/Internal/SetupSerializationBuilder.cs b/src/NLog/Internal/SetupSerializationBuilder.cs index e8ed3c82ec..84e1061665 100644 --- a/src/NLog/Internal/SetupSerializationBuilder.cs +++ b/src/NLog/Internal/SetupSerializationBuilder.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using NLog.Config; diff --git a/src/NLog/Internal/SimpleStringReader.cs b/src/NLog/Internal/SimpleStringReader.cs index c9bb3fef78..a30522619f 100644 --- a/src/NLog/Internal/SimpleStringReader.cs +++ b/src/NLog/Internal/SimpleStringReader.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/SingleCallContinuation.cs b/src/NLog/Internal/SingleCallContinuation.cs index 61c6fcd402..6dcd3948b1 100644 --- a/src/NLog/Internal/SingleCallContinuation.cs +++ b/src/NLog/Internal/SingleCallContinuation.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/SingleItemOptimizedHashSet.cs b/src/NLog/Internal/SingleItemOptimizedHashSet.cs index 8520ab3cee..ded0e5fe44 100644 --- a/src/NLog/Internal/SingleItemOptimizedHashSet.cs +++ b/src/NLog/Internal/SingleItemOptimizedHashSet.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/SortHelpers.cs b/src/NLog/Internal/SortHelpers.cs index 3afd5c2665..18c932a003 100644 --- a/src/NLog/Internal/SortHelpers.cs +++ b/src/NLog/Internal/SortHelpers.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/StackTraceUsageUtils.cs b/src/NLog/Internal/StackTraceUsageUtils.cs index cb6ab19119..0783ff1c73 100644 --- a/src/NLog/Internal/StackTraceUsageUtils.cs +++ b/src/NLog/Internal/StackTraceUsageUtils.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/StringBuilderExt.cs b/src/NLog/Internal/StringBuilderExt.cs index 3830786249..31e7e5b567 100644 --- a/src/NLog/Internal/StringBuilderExt.cs +++ b/src/NLog/Internal/StringBuilderExt.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/StringBuilderPool.cs b/src/NLog/Internal/StringBuilderPool.cs index 627fd51d97..1bde3bba1b 100644 --- a/src/NLog/Internal/StringBuilderPool.cs +++ b/src/NLog/Internal/StringBuilderPool.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/StringHelpers.cs b/src/NLog/Internal/StringHelpers.cs index cf410c8aa2..1fb88efb61 100644 --- a/src/NLog/Internal/StringHelpers.cs +++ b/src/NLog/Internal/StringHelpers.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/StringSplitter.cs b/src/NLog/Internal/StringSplitter.cs index 605556b770..70e56348d9 100644 --- a/src/NLog/Internal/StringSplitter.cs +++ b/src/NLog/Internal/StringSplitter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/TargetWithFilterChain.cs b/src/NLog/Internal/TargetWithFilterChain.cs index 34876da7fe..806b1ab904 100644 --- a/src/NLog/Internal/TargetWithFilterChain.cs +++ b/src/NLog/Internal/TargetWithFilterChain.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/TimeoutContinuation.cs b/src/NLog/Internal/TimeoutContinuation.cs index 4d8a174b7e..3b14ffee9a 100644 --- a/src/NLog/Internal/TimeoutContinuation.cs +++ b/src/NLog/Internal/TimeoutContinuation.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/UrlHelper.cs b/src/NLog/Internal/UrlHelper.cs index d37058b5f7..6206b66fc8 100644 --- a/src/NLog/Internal/UrlHelper.cs +++ b/src/NLog/Internal/UrlHelper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Internal/XmlHelper.cs b/src/NLog/Internal/XmlHelper.cs index 39a4364274..34e3469a8f 100644 --- a/src/NLog/Internal/XmlHelper.cs +++ b/src/NLog/Internal/XmlHelper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Internal { using System; diff --git a/src/NLog/Layouts/CSV/CsvColumn.cs b/src/NLog/Layouts/CSV/CsvColumn.cs index 96f7be1ee1..6ad56fc9e8 100644 --- a/src/NLog/Layouts/CSV/CsvColumn.cs +++ b/src/NLog/Layouts/CSV/CsvColumn.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using NLog.Config; diff --git a/src/NLog/Layouts/CSV/CsvLayout.cs b/src/NLog/Layouts/CSV/CsvLayout.cs index 0c2f690574..9599513edb 100644 --- a/src/NLog/Layouts/CSV/CsvLayout.cs +++ b/src/NLog/Layouts/CSV/CsvLayout.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using System.Collections.Generic; diff --git a/src/NLog/Layouts/CompoundLayout.cs b/src/NLog/Layouts/CompoundLayout.cs index f7677960a9..20cf03f1f4 100644 --- a/src/NLog/Layouts/CompoundLayout.cs +++ b/src/NLog/Layouts/CompoundLayout.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using System.Collections.Generic; diff --git a/src/NLog/Layouts/JSON/JsonArrayLayout.cs b/src/NLog/Layouts/JSON/JsonArrayLayout.cs index 0343e12fee..7d494b92ee 100644 --- a/src/NLog/Layouts/JSON/JsonArrayLayout.cs +++ b/src/NLog/Layouts/JSON/JsonArrayLayout.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using System.Collections.Generic; diff --git a/src/NLog/Layouts/JSON/JsonAttribute.cs b/src/NLog/Layouts/JSON/JsonAttribute.cs index 69c77e873f..560e920b1f 100644 --- a/src/NLog/Layouts/JSON/JsonAttribute.cs +++ b/src/NLog/Layouts/JSON/JsonAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using System; diff --git a/src/NLog/Layouts/JSON/JsonLayout.cs b/src/NLog/Layouts/JSON/JsonLayout.cs index 40600c75a0..00588b1e88 100644 --- a/src/NLog/Layouts/JSON/JsonLayout.cs +++ b/src/NLog/Layouts/JSON/JsonLayout.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using System; diff --git a/src/NLog/Layouts/LayoutAttribute.cs b/src/NLog/Layouts/LayoutAttribute.cs index ffba558791..bc44fe134c 100644 --- a/src/NLog/Layouts/LayoutAttribute.cs +++ b/src/NLog/Layouts/LayoutAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using System; diff --git a/src/NLog/Layouts/LayoutParser.cs b/src/NLog/Layouts/LayoutParser.cs index c4cfef5cb2..b65ed14584 100644 --- a/src/NLog/Layouts/LayoutParser.cs +++ b/src/NLog/Layouts/LayoutParser.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using System; diff --git a/src/NLog/Layouts/LayoutWithHeaderAndFooter.cs b/src/NLog/Layouts/LayoutWithHeaderAndFooter.cs index 9813f3b1eb..a7cd3226ef 100644 --- a/src/NLog/Layouts/LayoutWithHeaderAndFooter.cs +++ b/src/NLog/Layouts/LayoutWithHeaderAndFooter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using NLog.Config; diff --git a/src/NLog/Layouts/SimpleLayout.cs b/src/NLog/Layouts/SimpleLayout.cs index 11400aaf83..27df45988d 100644 --- a/src/NLog/Layouts/SimpleLayout.cs +++ b/src/NLog/Layouts/SimpleLayout.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using System; diff --git a/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs b/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs index 126e5c2fe3..c3e330d343 100644 --- a/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs +++ b/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using JetBrains.Annotations; diff --git a/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs b/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs index 5282b8e77e..7799c587d6 100644 --- a/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs +++ b/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using System; diff --git a/src/NLog/Layouts/XML/XmlAttribute.cs b/src/NLog/Layouts/XML/XmlAttribute.cs index c60b215477..55bf5a7e08 100644 --- a/src/NLog/Layouts/XML/XmlAttribute.cs +++ b/src/NLog/Layouts/XML/XmlAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using System; diff --git a/src/NLog/Layouts/XML/XmlElement.cs b/src/NLog/Layouts/XML/XmlElement.cs index 60ff70d821..8d1415a880 100644 --- a/src/NLog/Layouts/XML/XmlElement.cs +++ b/src/NLog/Layouts/XML/XmlElement.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using NLog.Config; diff --git a/src/NLog/Layouts/XML/XmlElementBase.cs b/src/NLog/Layouts/XML/XmlElementBase.cs index 191bf5fa69..328a6b8956 100644 --- a/src/NLog/Layouts/XML/XmlElementBase.cs +++ b/src/NLog/Layouts/XML/XmlElementBase.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using System; diff --git a/src/NLog/Layouts/XML/XmlLayout.cs b/src/NLog/Layouts/XML/XmlLayout.cs index 98286bbbae..27cd237aeb 100644 --- a/src/NLog/Layouts/XML/XmlLayout.cs +++ b/src/NLog/Layouts/XML/XmlLayout.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Layouts { using NLog.Config; diff --git a/src/NLog/LogEventBuilder.cs b/src/NLog/LogEventBuilder.cs index 895a844762..e8722dfc91 100644 --- a/src/NLog/LogEventBuilder.cs +++ b/src/NLog/LogEventBuilder.cs @@ -38,8 +38,6 @@ using JetBrains.Annotations; using NLog.Internal; -#nullable enable - namespace NLog { /// diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index 8ca1885a65..1a7d5c42ca 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/LogFactory-T.cs b/src/NLog/LogFactory-T.cs index e4af9d5601..dbd2649b5a 100644 --- a/src/NLog/LogFactory-T.cs +++ b/src/NLog/LogFactory-T.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System.Diagnostics; diff --git a/src/NLog/LogLevel.cs b/src/NLog/LogLevel.cs index ff58cc4fac..cb46634d66 100644 --- a/src/NLog/LogLevel.cs +++ b/src/NLog/LogLevel.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/LogLevelTypeConverter.cs b/src/NLog/LogLevelTypeConverter.cs index 329ddb8546..0d6b01fe1e 100644 --- a/src/NLog/LogLevelTypeConverter.cs +++ b/src/NLog/LogLevelTypeConverter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Attributes { using System; diff --git a/src/NLog/LogManager.cs b/src/NLog/LogManager.cs index d92230afd2..545b61a74a 100644 --- a/src/NLog/LogManager.cs +++ b/src/NLog/LogManager.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/LogMessageFormatter.cs b/src/NLog/LogMessageFormatter.cs index 94fd333033..b115dd4f62 100644 --- a/src/NLog/LogMessageFormatter.cs +++ b/src/NLog/LogMessageFormatter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { /// diff --git a/src/NLog/LogMessageGenerator.cs b/src/NLog/LogMessageGenerator.cs index fd037011ed..2c3d253a53 100644 --- a/src/NLog/LogMessageGenerator.cs +++ b/src/NLog/LogMessageGenerator.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { /// diff --git a/src/NLog/Logger-Conditional.cs b/src/NLog/Logger-Conditional.cs index 3daa0da1eb..7aa4135693 100644 --- a/src/NLog/Logger-Conditional.cs +++ b/src/NLog/Logger-Conditional.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/Logger-V1Compat.cs b/src/NLog/Logger-V1Compat.cs index 58bf83f161..67eb3bf3c0 100644 --- a/src/NLog/Logger-V1Compat.cs +++ b/src/NLog/Logger-V1Compat.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/Logger-generated.cs b/src/NLog/Logger-generated.cs index 3d3b26ed44..606177a26c 100644 --- a/src/NLog/Logger-generated.cs +++ b/src/NLog/Logger-generated.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/Logger-generated.tt b/src/NLog/Logger-generated.tt index 4edbc42116..18ac5a1eb2 100644 --- a/src/NLog/Logger-generated.tt +++ b/src/NLog/Logger-generated.tt @@ -40,8 +40,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/Logger.cs b/src/NLog/Logger.cs index 45cef9d9a2..5a7b180cfc 100644 --- a/src/NLog/Logger.cs +++ b/src/NLog/Logger.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/LoggerImpl.cs b/src/NLog/LoggerImpl.cs index 5410554d99..d28ea2183f 100644 --- a/src/NLog/LoggerImpl.cs +++ b/src/NLog/LoggerImpl.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/MappedDiagnosticsContext.cs b/src/NLog/MappedDiagnosticsContext.cs index 58820fbdf1..51950cf100 100644 --- a/src/NLog/MappedDiagnosticsContext.cs +++ b/src/NLog/MappedDiagnosticsContext.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/MappedDiagnosticsLogicalContext.cs b/src/NLog/MappedDiagnosticsLogicalContext.cs index 8b5cb2930c..77232ca825 100644 --- a/src/NLog/MappedDiagnosticsLogicalContext.cs +++ b/src/NLog/MappedDiagnosticsLogicalContext.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/MessageTemplates/Hole.cs b/src/NLog/MessageTemplates/Hole.cs index 8b0b67045e..80c7568959 100644 --- a/src/NLog/MessageTemplates/Hole.cs +++ b/src/NLog/MessageTemplates/Hole.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.MessageTemplates { /// diff --git a/src/NLog/MessageTemplates/Literal.cs b/src/NLog/MessageTemplates/Literal.cs index 19bd5a9039..c14380df55 100644 --- a/src/NLog/MessageTemplates/Literal.cs +++ b/src/NLog/MessageTemplates/Literal.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.MessageTemplates { /// diff --git a/src/NLog/MessageTemplates/LiteralHole.cs b/src/NLog/MessageTemplates/LiteralHole.cs index 880502c4ae..7a86bb5bc1 100644 --- a/src/NLog/MessageTemplates/LiteralHole.cs +++ b/src/NLog/MessageTemplates/LiteralHole.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.MessageTemplates { /// diff --git a/src/NLog/MessageTemplates/MessageTemplateParameter.cs b/src/NLog/MessageTemplates/MessageTemplateParameter.cs index 1ff4c285e7..b682e35740 100644 --- a/src/NLog/MessageTemplates/MessageTemplateParameter.cs +++ b/src/NLog/MessageTemplates/MessageTemplateParameter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.MessageTemplates { using JetBrains.Annotations; diff --git a/src/NLog/MessageTemplates/MessageTemplateParameters.cs b/src/NLog/MessageTemplates/MessageTemplateParameters.cs index 71090c1559..6a85dc5b4d 100644 --- a/src/NLog/MessageTemplates/MessageTemplateParameters.cs +++ b/src/NLog/MessageTemplates/MessageTemplateParameters.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.MessageTemplates { using System; diff --git a/src/NLog/MessageTemplates/TemplateEnumerator.cs b/src/NLog/MessageTemplates/TemplateEnumerator.cs index 9c5d1ccd90..31166f8575 100644 --- a/src/NLog/MessageTemplates/TemplateEnumerator.cs +++ b/src/NLog/MessageTemplates/TemplateEnumerator.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.MessageTemplates { using System; diff --git a/src/NLog/MessageTemplates/TemplateParserException.cs b/src/NLog/MessageTemplates/TemplateParserException.cs index 040adc8ab6..4ef99e9d3a 100644 --- a/src/NLog/MessageTemplates/TemplateParserException.cs +++ b/src/NLog/MessageTemplates/TemplateParserException.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.MessageTemplates { using System; diff --git a/src/NLog/MessageTemplates/ValueFormatter.cs b/src/NLog/MessageTemplates/ValueFormatter.cs index fb64c3955d..e643733447 100644 --- a/src/NLog/MessageTemplates/ValueFormatter.cs +++ b/src/NLog/MessageTemplates/ValueFormatter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.MessageTemplates { using System; diff --git a/src/NLog/NLogConfigurationException.cs b/src/NLog/NLogConfigurationException.cs index 50a6242a88..c3e3f8be73 100644 --- a/src/NLog/NLogConfigurationException.cs +++ b/src/NLog/NLogConfigurationException.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/NLogRuntimeException.cs b/src/NLog/NLogRuntimeException.cs index b123545534..3656c65871 100644 --- a/src/NLog/NLogRuntimeException.cs +++ b/src/NLog/NLogRuntimeException.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/NestedDiagnosticsContext.cs b/src/NLog/NestedDiagnosticsContext.cs index cdabd3fa98..c8d64b6d75 100644 --- a/src/NLog/NestedDiagnosticsContext.cs +++ b/src/NLog/NestedDiagnosticsContext.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/NestedDiagnosticsLogicalContext.cs b/src/NLog/NestedDiagnosticsLogicalContext.cs index ecefc627d2..eed302d16a 100644 --- a/src/NLog/NestedDiagnosticsLogicalContext.cs +++ b/src/NLog/NestedDiagnosticsLogicalContext.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/NullLogger.cs b/src/NLog/NullLogger.cs index 538d6a2a4a..8a30ddc704 100644 --- a/src/NLog/NullLogger.cs +++ b/src/NLog/NullLogger.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using Internal; diff --git a/src/NLog/ScopeContext.cs b/src/NLog/ScopeContext.cs index 1c48028edd..32d326c298 100644 --- a/src/NLog/ScopeContext.cs +++ b/src/NLog/ScopeContext.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog { using System; diff --git a/src/NLog/Targets/DefaultJsonSerializer.cs b/src/NLog/Targets/DefaultJsonSerializer.cs index a1f268bf4b..a7b3221acb 100644 --- a/src/NLog/Targets/DefaultJsonSerializer.cs +++ b/src/NLog/Targets/DefaultJsonSerializer.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets { using System; diff --git a/src/NLog/Targets/Target.cs b/src/NLog/Targets/Target.cs index 078e3f6b6f..3b51a04fba 100644 --- a/src/NLog/Targets/Target.cs +++ b/src/NLog/Targets/Target.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets { using System; diff --git a/src/NLog/Targets/TargetAttribute.cs b/src/NLog/Targets/TargetAttribute.cs index 7a99cdb598..065becfa95 100644 --- a/src/NLog/Targets/TargetAttribute.cs +++ b/src/NLog/Targets/TargetAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets { using System; diff --git a/src/NLog/Targets/TargetWithContext.cs b/src/NLog/Targets/TargetWithContext.cs index df1aab1951..742fd3e4cf 100644 --- a/src/NLog/Targets/TargetWithContext.cs +++ b/src/NLog/Targets/TargetWithContext.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets { using System; diff --git a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs index 32b91ba84d..dce2e0e4f9 100644 --- a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs +++ b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets { using NLog.Layouts; diff --git a/src/NLog/Targets/Wrappers/AsyncRequestQueue-T.cs b/src/NLog/Targets/Wrappers/AsyncRequestQueue-T.cs index a58fb77937..bb0a1b892d 100644 --- a/src/NLog/Targets/Wrappers/AsyncRequestQueue-T.cs +++ b/src/NLog/Targets/Wrappers/AsyncRequestQueue-T.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System.Collections.Generic; diff --git a/src/NLog/Targets/Wrappers/AsyncRequestQueueBase.cs b/src/NLog/Targets/Wrappers/AsyncRequestQueueBase.cs index 42bd7768c2..e958df0410 100644 --- a/src/NLog/Targets/Wrappers/AsyncRequestQueueBase.cs +++ b/src/NLog/Targets/Wrappers/AsyncRequestQueueBase.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs index 4d123ed173..30cdd866f3 100644 --- a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs b/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs index 1e845f28c5..b4652ca15a 100644 --- a/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using NLog.Common; diff --git a/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs b/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs index 33f3d78b35..698ec57c57 100644 --- a/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/CompoundTargetBase.cs b/src/NLog/Targets/Wrappers/CompoundTargetBase.cs index a882b57245..684898b2c5 100644 --- a/src/NLog/Targets/Wrappers/CompoundTargetBase.cs +++ b/src/NLog/Targets/Wrappers/CompoundTargetBase.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/ConcurrentRequestQueue.cs b/src/NLog/Targets/Wrappers/ConcurrentRequestQueue.cs index 9029f2bc1a..3e3ffd5d62 100644 --- a/src/NLog/Targets/Wrappers/ConcurrentRequestQueue.cs +++ b/src/NLog/Targets/Wrappers/ConcurrentRequestQueue.cs @@ -33,8 +33,6 @@ #if !NET35 -#nullable enable - namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/FallbackGroupTarget.cs b/src/NLog/Targets/Wrappers/FallbackGroupTarget.cs index 4ac995d596..26fd3fd2b9 100644 --- a/src/NLog/Targets/Wrappers/FallbackGroupTarget.cs +++ b/src/NLog/Targets/Wrappers/FallbackGroupTarget.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System.Collections.Generic; diff --git a/src/NLog/Targets/Wrappers/FilteringRule.cs b/src/NLog/Targets/Wrappers/FilteringRule.cs index 5afc1c00af..083fd199fe 100644 --- a/src/NLog/Targets/Wrappers/FilteringRule.cs +++ b/src/NLog/Targets/Wrappers/FilteringRule.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using NLog.Conditions; diff --git a/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs b/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs index f651c9082f..56db1bfa40 100644 --- a/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System.Collections.Generic; diff --git a/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs b/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs index 1877006d37..e0106f4930 100644 --- a/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System.Collections.Generic; diff --git a/src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs b/src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs index 1143b818ed..4fc3e06274 100644 --- a/src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/LogEventDroppedEventArgs.cs b/src/NLog/Targets/Wrappers/LogEventDroppedEventArgs.cs index c61f1df88d..9312402ef7 100644 --- a/src/NLog/Targets/Wrappers/LogEventDroppedEventArgs.cs +++ b/src/NLog/Targets/Wrappers/LogEventDroppedEventArgs.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/LogEventQueueGrowEventArgs.cs b/src/NLog/Targets/Wrappers/LogEventQueueGrowEventArgs.cs index e319d14d8e..329ffbf403 100644 --- a/src/NLog/Targets/Wrappers/LogEventQueueGrowEventArgs.cs +++ b/src/NLog/Targets/Wrappers/LogEventQueueGrowEventArgs.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs b/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs index c893a61c65..2870389a14 100644 --- a/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System.Collections.Generic; diff --git a/src/NLog/Targets/Wrappers/RandomizeGroupTarget.cs b/src/NLog/Targets/Wrappers/RandomizeGroupTarget.cs index f79d908afc..f8b2f64dfd 100644 --- a/src/NLog/Targets/Wrappers/RandomizeGroupTarget.cs +++ b/src/NLog/Targets/Wrappers/RandomizeGroupTarget.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/RepeatingTargetWrapper.cs b/src/NLog/Targets/Wrappers/RepeatingTargetWrapper.cs index b6a7eff04a..32220b5399 100644 --- a/src/NLog/Targets/Wrappers/RepeatingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/RepeatingTargetWrapper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using NLog.Common; diff --git a/src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs b/src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs index 55a67c70d9..e5eb8c64d5 100644 --- a/src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/RoundRobinGroupTarget.cs b/src/NLog/Targets/Wrappers/RoundRobinGroupTarget.cs index c55c2dcded..1a118b59bb 100644 --- a/src/NLog/Targets/Wrappers/RoundRobinGroupTarget.cs +++ b/src/NLog/Targets/Wrappers/RoundRobinGroupTarget.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System.Threading; diff --git a/src/NLog/Targets/Wrappers/SplitGroupTarget.cs b/src/NLog/Targets/Wrappers/SplitGroupTarget.cs index 3bd159de5f..c66e5fe72f 100644 --- a/src/NLog/Targets/Wrappers/SplitGroupTarget.cs +++ b/src/NLog/Targets/Wrappers/SplitGroupTarget.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Targets/Wrappers/WrapperTargetBase.cs b/src/NLog/Targets/Wrappers/WrapperTargetBase.cs index b4fa920504..0aa6ce97ab 100644 --- a/src/NLog/Targets/Wrappers/WrapperTargetBase.cs +++ b/src/NLog/Targets/Wrappers/WrapperTargetBase.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Targets.Wrappers { using System; diff --git a/src/NLog/Time/AccurateLocalTimeSource.cs b/src/NLog/Time/AccurateLocalTimeSource.cs index babe41ef11..20798109e4 100644 --- a/src/NLog/Time/AccurateLocalTimeSource.cs +++ b/src/NLog/Time/AccurateLocalTimeSource.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Time { using System; diff --git a/src/NLog/Time/AccurateUtcTimeSource.cs b/src/NLog/Time/AccurateUtcTimeSource.cs index 15dede17a6..9f45c12cc5 100644 --- a/src/NLog/Time/AccurateUtcTimeSource.cs +++ b/src/NLog/Time/AccurateUtcTimeSource.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Time { using System; diff --git a/src/NLog/Time/CachedTimeSource.cs b/src/NLog/Time/CachedTimeSource.cs index 6c75e05db1..04156d176e 100644 --- a/src/NLog/Time/CachedTimeSource.cs +++ b/src/NLog/Time/CachedTimeSource.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Time { using System; diff --git a/src/NLog/Time/FastLocalTimeSource.cs b/src/NLog/Time/FastLocalTimeSource.cs index 5122fd04e7..07bfc29f1f 100644 --- a/src/NLog/Time/FastLocalTimeSource.cs +++ b/src/NLog/Time/FastLocalTimeSource.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Time { using System; diff --git a/src/NLog/Time/FastUtcTimeSource.cs b/src/NLog/Time/FastUtcTimeSource.cs index 986e6d3c17..d3bd786d0e 100644 --- a/src/NLog/Time/FastUtcTimeSource.cs +++ b/src/NLog/Time/FastUtcTimeSource.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Time { using System; diff --git a/src/NLog/Time/TimeSource.cs b/src/NLog/Time/TimeSource.cs index 167ba3d9f3..c2246ac10d 100644 --- a/src/NLog/Time/TimeSource.cs +++ b/src/NLog/Time/TimeSource.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Time { using System; diff --git a/src/NLog/Time/TimeSourceAttribute.cs b/src/NLog/Time/TimeSourceAttribute.cs index 95999cb529..c4ac5901f5 100644 --- a/src/NLog/Time/TimeSourceAttribute.cs +++ b/src/NLog/Time/TimeSourceAttribute.cs @@ -31,8 +31,6 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -#nullable enable - namespace NLog.Time { using System; From f52937f155ad4fc9dff6637f68d62c2e4bf047b6 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 21 May 2025 21:42:40 +0200 Subject: [PATCH 120/224] NetStandard2.1 for all nuget-packages (#5826) --- build.ps1 | 20 ++++----- src/NLog.Database/NLog.Database.csproj | 15 +++++-- .../NLog.OutputDebugString.csproj | 13 ++++-- src/NLog.RegEx/NLog.RegEx.csproj | 13 ++++-- src/NLog.RegEx/Properties/AssemblyInfo.cs | 45 +++++++++++++++++++ .../NLog.Targets.AtomicFile.csproj | 1 - .../Properties/AssemblyInfo.cs | 45 +++++++++++++++++++ .../NLog.Targets.ConcurrentFile.csproj | 2 +- .../NLog.Targets.GZipFile.csproj | 18 ++++---- .../Properties/AssemblyInfo.cs | 45 +++++++++++++++++++ .../NLog.Targets.Mail.csproj | 13 ++++-- .../NLog.Targets.Network.csproj | 13 ++++-- .../NetworkSenders/SslSocketProxy.cs | 2 + .../NLog.Targets.Trace.csproj | 13 ++++-- .../NLog.Targets.WebService.csproj | 13 ++++-- .../NLog.WindowsEventLog.csproj | 13 +++--- .../NLog.WindowsRegistry.csproj | 19 +++++--- src/NLog/Logger-V1Compat.cs | 2 +- src/NLog/NLog.csproj | 21 ++------- ...rocessRunner.cs => AtomicProcessRunner.cs} | 23 +++++----- ...ts.cs => AtomicWritesMultiProcessTests.cs} | 9 ++-- .../ConcurrentWritesMultiProcessTests.cs | 2 - 22 files changed, 262 insertions(+), 98 deletions(-) create mode 100644 src/NLog.RegEx/Properties/AssemblyInfo.cs create mode 100644 src/NLog.Targets.AtomicFile/Properties/AssemblyInfo.cs create mode 100644 src/NLog.Targets.GZipFile/Properties/AssemblyInfo.cs rename tests/NLog.Targets.AtomicFile.Tests/{ProcessRunner.cs => AtomicProcessRunner.cs} (88%) rename tests/NLog.Targets.AtomicFile.Tests/{ConcurrentWritesMultiProcessTests.cs => AtomicWritesMultiProcessTests.cs} (98%) diff --git a/build.ps1 b/build.ps1 index b8102a30a1..a17bb2ad43 100644 --- a/build.ps1 +++ b/build.ps1 @@ -35,18 +35,18 @@ function create-package($packageName, $targetFrameworks) { exit $LastExitCode } } -create-package 'NLog.Database' '"net35;net45;net46;netstandard2.0"' -create-package 'NLog.Targets.Mail' '"net35;net45;net46;netstandard2.0"' -create-package 'NLog.Targets.Network' '"net45;net46;netstandard2.0"' -create-package 'NLog.Targets.Trace' '"net35;net45;net46;netstandard2.0"' -create-package 'NLog.Targets.WebService' '"net45;net46;netstandard2.0"' -create-package 'NLog.Targets.GZipFile' '"net45;net46;netstandard2.0"' -create-package 'NLog.OutputDebugString' '"net35;net45;net46;netstandard2.0"' -create-package 'NLog.RegEx' '"net35;net45;net46;netstandard2.0"' -create-package 'NLog.WindowsRegistry' '"net35;net45;net46;netstandard2.0"' +create-package 'NLog.Database' '"net35;net45;net46;netstandard2.0;netstandard2.1"' +create-package 'NLog.Targets.Mail' '"net35;net45;net46;netstandard2.0;netstandard2.1"' +create-package 'NLog.Targets.Network' '"net45;net46;netstandard2.0;netstandard2.1"' +create-package 'NLog.Targets.Trace' '"net35;net45;net46;netstandard2.0;netstandard2.1"' +create-package 'NLog.Targets.WebService' '"net45;net46;netstandard2.0;netstandard2.1"' +create-package 'NLog.Targets.GZipFile' '"net45;net46;netstandard2.0;netstandard2.1"' +create-package 'NLog.OutputDebugString' '"net35;net45;net46;netstandard2.0;netstandard2.1"' +create-package 'NLog.RegEx' '"net35;net45;net46;netstandard2.0;netstandard2.1"' +create-package 'NLog.WindowsRegistry' '"net35;net45;net46;netstandard2.0;netstandard2.1"' create-package 'NLog.Targets.ConcurrentFile' '"net35;net45;net46;netstandard2.0"' msbuild /t:Restore,Pack ./src/NLog.Targets.AtomicFile/ /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount -create-package 'NLog.WindowsEventLog' '"netstandard2.0"' +create-package 'NLog.WindowsEventLog' '"netstandard2.0;netstandard2.1"' msbuild /t:xsd /t:NuGetSchemaPackage ./src/NLog.proj /p:Configuration=Release /p:BuildNetFX45=true /p:BuildVersion=$versionProduct /p:Configuration=Release /p:BuildLabelOverride=NONE /verbosity:minimal diff --git a/src/NLog.Database/NLog.Database.csproj b/src/NLog.Database/NLog.Database.csproj index 049eb100ef..acb25206d8 100644 --- a/src/NLog.Database/NLog.Database.csproj +++ b/src/NLog.Database/NLog.Database.csproj @@ -1,10 +1,10 @@  - PackageReference - - net35;net46;netstandard2.0 - + 17.0 + net35;net46;netstandard2.0 + net35;net46;netstandard2.0;netstandard2.1 + NLog Database Target NLog NLog.Database includes DatabaseTarget for writing to any database with DbProvider support @@ -31,8 +31,11 @@ ..\NLog.snk true + true true true + true + true @@ -54,6 +57,10 @@ NLog.Database for NetStandard 2.0 + + NLog.Database for NetStandard 2.1 + + diff --git a/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj b/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj index 4fa364f993..31c4ac5080 100644 --- a/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj +++ b/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj @@ -1,7 +1,9 @@  - net35;net46;netstandard2.0 + 17.0 + net35;net46;netstandard2.0 + net35;net46;netstandard2.0;netstandard2.1 NLog.OutputDebugString NLog @@ -32,9 +34,12 @@ true true true - true - true - copyused + true + true + + + + NLog.OutputDebugString for .NET Standard 2.1 diff --git a/src/NLog.RegEx/NLog.RegEx.csproj b/src/NLog.RegEx/NLog.RegEx.csproj index 67cf230beb..4f9064419f 100644 --- a/src/NLog.RegEx/NLog.RegEx.csproj +++ b/src/NLog.RegEx/NLog.RegEx.csproj @@ -1,7 +1,9 @@  - net35;net46;netstandard2.0 + 17.0 + net35;net46;netstandard2.0 + net35;net46;netstandard2.0;netstandard2.1 NLog.RegEx NLog @@ -32,9 +34,8 @@ true true true - true - true - copyused + true + true @@ -56,6 +57,10 @@ NLog.RegEx for NetStandard 2.0 + + NLog.RegEx for NetStandard 2.1 + + diff --git a/src/NLog.RegEx/Properties/AssemblyInfo.cs b/src/NLog.RegEx/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..3c9d9e1f4a --- /dev/null +++ b/src/NLog.RegEx/Properties/AssemblyInfo.cs @@ -0,0 +1,45 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security; + +[assembly: AssemblyCulture("")] +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] +[assembly: AllowPartiallyTrustedCallers] +#if !NET35 +[assembly: SecurityRules(SecurityRuleSet.Level1)] +#endif diff --git a/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj index 19d3bfc66a..9c80e474fa 100644 --- a/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj +++ b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj @@ -33,7 +33,6 @@ true true true - copyused true diff --git a/src/NLog.Targets.AtomicFile/Properties/AssemblyInfo.cs b/src/NLog.Targets.AtomicFile/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..3c9d9e1f4a --- /dev/null +++ b/src/NLog.Targets.AtomicFile/Properties/AssemblyInfo.cs @@ -0,0 +1,45 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security; + +[assembly: AssemblyCulture("")] +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] +[assembly: AllowPartiallyTrustedCallers] +#if !NET35 +[assembly: SecurityRules(SecurityRuleSet.Level1)] +#endif diff --git a/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj b/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj index ac9189378e..a3daed04bc 100644 --- a/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj +++ b/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj @@ -5,7 +5,7 @@ NLog.Targets.ConcurrentFile NLog - FileTarget with support for ConcurrentWrites where multiple processes can write to the same file + FileTarget with support for ConcurrentWrites with Global Mutex and EnableArchiveFileCompression with ZIP NLog.Targets.ConcurrentFile v$(ProductVersion) $(ProductVersion) Jarek Kowalski,Kim Christensen,Julian Verdurmen diff --git a/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj b/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj index 785f8515cb..9be6293195 100644 --- a/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj +++ b/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj @@ -1,7 +1,9 @@  - netstandard2.0;net46 + 17.0 + net46;netstandard2.0 + net46;netstandard2.0;netstandard2.1 NLog.Targets.GZipFile NLog @@ -31,9 +33,8 @@ true true true - true - true - copyused + true + true true @@ -51,12 +52,11 @@ NLog.Targets.GZipFile for NetStandard 2.0 - - - - + + NLog.Targets.GZipFile for NetStandard 2.1 + - + diff --git a/src/NLog.Targets.GZipFile/Properties/AssemblyInfo.cs b/src/NLog.Targets.GZipFile/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..3c9d9e1f4a --- /dev/null +++ b/src/NLog.Targets.GZipFile/Properties/AssemblyInfo.cs @@ -0,0 +1,45 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security; + +[assembly: AssemblyCulture("")] +[assembly: CLSCompliant(true)] +[assembly: ComVisible(false)] +[assembly: AllowPartiallyTrustedCallers] +#if !NET35 +[assembly: SecurityRules(SecurityRuleSet.Level1)] +#endif diff --git a/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj b/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj index c3a6dc75d5..9ffb129b5e 100644 --- a/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj +++ b/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj @@ -1,7 +1,9 @@  - net35;net46;netstandard2.0 + 17.0 + net35;net46;netstandard2.0 + net35;net46;netstandard2.0;netstandard2.1 NLog.Targets.Mail NLog @@ -32,9 +34,8 @@ true true true - true - true - copyused + true + true @@ -56,6 +57,10 @@ NLog.Targets.Mail for NetStandard 2.0 + + NLog.Targets.Mail for NetStandard 2.1 + + diff --git a/src/NLog.Targets.Network/NLog.Targets.Network.csproj b/src/NLog.Targets.Network/NLog.Targets.Network.csproj index 2a959766be..674d263f5b 100644 --- a/src/NLog.Targets.Network/NLog.Targets.Network.csproj +++ b/src/NLog.Targets.Network/NLog.Targets.Network.csproj @@ -1,7 +1,9 @@  - net46;netstandard2.0 + 17.0 + net46;netstandard2.0 + net46;netstandard2.0;netstandard2.1 NLog.Targets.Network NLog @@ -32,9 +34,12 @@ true true true - true - true - copyused + true + true + + + + NLog.Targets.Network for .NET Standard 2.1 diff --git a/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs b/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs index 3658d12969..c314efa228 100644 --- a/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs +++ b/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs @@ -166,7 +166,9 @@ private static SocketError GetSocketError(Exception ex) private static void AuthenticateAsClient(SslStream sslStream, string targetHost, SslProtocols enabledSslProtocols, X509Certificate2Collection sslCertificateOverride) { +#pragma warning disable CS0618 // Type or member is obsolete if (enabledSslProtocols != SslProtocols.Default || sslCertificateOverride != null) +#pragma warning restore CS0618 // Type or member is obsolete { sslStream.AuthenticateAsClient(targetHost, sslCertificateOverride?.Count > 0 ? sslCertificateOverride : null, enabledSslProtocols, false); } diff --git a/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj b/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj index 89ab57c41c..e76df1be10 100644 --- a/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj +++ b/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj @@ -1,7 +1,9 @@  - net35;net46;netstandard2.0 + 17.0 + net35;net46;netstandard2.0 + net35;net46;netstandard2.0;netstandard2.1 NLog.Targets.Trace NLog @@ -32,9 +34,12 @@ true true true - true - true - copyused + true + true + + + + NLog.Targets.Trace for .NET Standard 2.1 diff --git a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj index 1a9b37d6f8..b0bafaf8cf 100644 --- a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj +++ b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj @@ -1,7 +1,9 @@  - net46;netstandard2.0 + 17.0 + net46;netstandard2.0 + net46;netstandard2.0;netstandard2.1 NLog.Targets.WebService NLog @@ -32,9 +34,12 @@ true true true - true - true - copyused + true + true + + + + NLog.Targets.WebService for .NET Standard 2.1 diff --git a/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj b/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj index f8951348bc..888c3e3357 100644 --- a/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj +++ b/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj @@ -1,7 +1,9 @@  - netstandard2.0;netstandard2.1 + 17.0 + netstandard2.0 + netstandard2.0;netstandard2.1 NLog.WindowsEventLog for .NET Standard NLog @@ -36,16 +38,15 @@ 9 true true - copyused - - NLog.WindowsEventLog for NetStandard 2.0 + + NLog.WindowsEventLog for NetStandard 2.1 $(DefineConstants);WindowsEventLogPackage - - NLog.WindowsEventLog for NetStandard 2.1 + + NLog.WindowsEventLog for NetStandard 2.0 $(DefineConstants);WindowsEventLogPackage diff --git a/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj b/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj index 154e8acd2b..794529f637 100644 --- a/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj +++ b/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj @@ -1,7 +1,9 @@  - net35;net46;netstandard2.0 + 17.0 + net35;net46;netstandard2.0 + net35;net46;netstandard2.0;netstandard2.1 NLog Windows Registry NLog @@ -32,9 +34,8 @@ true true true - true - true - copyused + true + true @@ -56,13 +57,21 @@ NLog.WindowsRegistry for NetStandard 2.0 + + NLog.WindowsRegistry for NetStandard 2.1 + + - + + + + + diff --git a/src/NLog/Logger-V1Compat.cs b/src/NLog/Logger-V1Compat.cs index 67eb3bf3c0..c38b7c0f8c 100644 --- a/src/NLog/Logger-V1Compat.cs +++ b/src/NLog/Logger-V1Compat.cs @@ -71,7 +71,7 @@ public void Log(LogLevel level, object? value) /// A to be written. [EditorBrowsable(EditorBrowsableState.Never)] #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER - [OverloadResolutionPriority(-1)] + [OverloadResolutionPriority(-1)] #endif public void Log(LogLevel level, IFormatProvider? formatProvider, object? value) { diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index bad971a25e..ca14107f55 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -62,13 +62,14 @@ For all config options and platform support, check https://nlog-project.org/conf ..\NLog.snk true + true true true enable - true - true 9 13 + true + true @@ -106,21 +107,7 @@ For all config options and platform support, check https://nlog-project.org/conf - - - - - - - - - - - - - - - + diff --git a/tests/NLog.Targets.AtomicFile.Tests/ProcessRunner.cs b/tests/NLog.Targets.AtomicFile.Tests/AtomicProcessRunner.cs similarity index 88% rename from tests/NLog.Targets.AtomicFile.Tests/ProcessRunner.cs rename to tests/NLog.Targets.AtomicFile.Tests/AtomicProcessRunner.cs index 797aa43e0e..f5df651510 100644 --- a/tests/NLog.Targets.AtomicFile.Tests/ProcessRunner.cs +++ b/tests/NLog.Targets.AtomicFile.Tests/AtomicProcessRunner.cs @@ -34,18 +34,15 @@ namespace NLog.Targets.AtomicFile.Tests { using System; - using System.CodeDom.Compiler; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; - using Microsoft.CodeAnalysis.CSharp; - using Microsoft.CSharp; using Xunit; - static class ProcessRunner + static class AtomicProcessRunner { - static ProcessRunner() + static AtomicProcessRunner() { string sourceCode = @" using System; @@ -90,23 +87,23 @@ public static int Main(string[] args) }"; #if NETFRAMEWORK - CSharpCodeProvider provider = new CSharpCodeProvider(); - var options = new CompilerParameters(); + var provider = new Microsoft.CSharp.CSharpCodeProvider(); + var options = new System.CodeDom.Compiler.CompilerParameters(); options.OutputAssembly = "Runner.exe"; options.GenerateExecutable = true; options.IncludeDebugInformation = true; // To allow debugging the generated Runner.exe we need to keep files. // See https://stackoverflow.com/questions/875723/how-to-debug-break-in-codedom-compiled-code - options.TempFiles = new TempFileCollection(Environment.GetEnvironmentVariable("TEMP"), true); + options.TempFiles = new System.CodeDom.Compiler.TempFileCollection(Environment.GetEnvironmentVariable("TEMP"), true); var results = provider.CompileAssemblyFromSource(options, sourceCode); #else - var compilation = CSharpCompilation + var compilation = Microsoft.CodeAnalysis.CSharp.CSharpCompilation .Create( "Runner.dll", - new[] { CSharpSyntaxTree.ParseText(sourceCode) }, + new[] { Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(sourceCode) }, references: Basic.Reference.Assemblies.ReferenceAssemblies.NetStandard20); - var outputAssembly = Path.Combine(Path.GetDirectoryName(typeof(ProcessRunner).Assembly.Location), "Runner.dll"); + var outputAssembly = Path.Combine(Path.GetDirectoryName(typeof(AtomicProcessRunner).Assembly.Location), "Runner.dll"); if (System.IO.File.Exists(outputAssembly)) System.IO.File.Delete(outputAssembly); using (var stream = new FileStream(outputAssembly, FileMode.CreateNew)) @@ -117,10 +114,10 @@ public static int Main(string[] args) Assert.True(failures.Count == 0, string.Join(Environment.NewLine, failures)); Assert.True(result.Success); } - var outputRuntimeConfig = Path.Combine(Path.GetDirectoryName(typeof(ProcessRunner).Assembly.Location), "Runner.runtimeconfig.json"); + var outputRuntimeConfig = Path.Combine(Path.GetDirectoryName(typeof(AtomicProcessRunner).Assembly.Location), "Runner.runtimeconfig.json"); if (System.IO.File.Exists(outputRuntimeConfig)) System.IO.File.Delete(outputRuntimeConfig); - using (var streamReader = new StreamReader(Path.Combine(Path.GetDirectoryName(typeof(ProcessRunner).Assembly.Location), typeof(ProcessRunner).Assembly.GetName().Name) + ".runtimeconfig.json")) + using (var streamReader = new StreamReader(Path.Combine(Path.GetDirectoryName(typeof(AtomicProcessRunner).Assembly.Location), typeof(AtomicProcessRunner).Assembly.GetName().Name) + ".runtimeconfig.json")) using (var streamWriter = new StreamWriter(outputRuntimeConfig)) { streamWriter.Write(streamReader.ReadToEnd()); diff --git a/tests/NLog.Targets.AtomicFile.Tests/ConcurrentWritesMultiProcessTests.cs b/tests/NLog.Targets.AtomicFile.Tests/AtomicWritesMultiProcessTests.cs similarity index 98% rename from tests/NLog.Targets.AtomicFile.Tests/ConcurrentWritesMultiProcessTests.cs rename to tests/NLog.Targets.AtomicFile.Tests/AtomicWritesMultiProcessTests.cs index 77a5f43940..2cb57207b2 100644 --- a/tests/NLog.Targets.AtomicFile.Tests/ConcurrentWritesMultiProcessTests.cs +++ b/tests/NLog.Targets.AtomicFile.Tests/AtomicWritesMultiProcessTests.cs @@ -40,18 +40,17 @@ namespace NLog.Targets.AtomicFile.Tests using System.Diagnostics; using System.IO; using System.Linq; - using System.Text; using System.Threading; using NLog.Common; using NLog.Targets; using NLog.Targets.Wrappers; using Xunit; - public class ConcurrentWritesMultiProcessTests + public class AtomicWritesMultiProcessTests { - private static readonly Logger _logger = LogManager.GetLogger(nameof(ConcurrentWritesMultiProcessTests)); + private static readonly Logger _logger = LogManager.GetLogger(nameof(AtomicWritesMultiProcessTests)); - public ConcurrentWritesMultiProcessTests() + public AtomicWritesMultiProcessTests() { InternalLogger.Reset(); LogManager.Configuration = null; @@ -190,7 +189,7 @@ private void DoConcurrentTest(int numProcesses, int numLogs, string mode) for (int i = 0; i < numProcesses; ++i) { - processes[i] = ProcessRunner.SpawnMethod( + processes[i] = AtomicProcessRunner.SpawnMethod( GetType(), nameof(MultiProcessExecutor), i.ToString(), diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentWritesMultiProcessTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentWritesMultiProcessTests.cs index 4e6a58b057..c20daf694a 100644 --- a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentWritesMultiProcessTests.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentWritesMultiProcessTests.cs @@ -44,11 +44,9 @@ namespace NLog.Targets.ConcurrentFile.Tests using System.Linq; using System.Threading; using NLog.Common; - using NLog.Config; using NLog.Targets; using NLog.Targets.Wrappers; using Xunit; - using Xunit.Extensions; public class ConcurrentWritesMultiProcessTests { From 9901db79bdaea45a67a1cd75716af3633dccb356 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 22 May 2025 21:18:09 +0200 Subject: [PATCH 121/224] NLog.Database with nullable references (#5827) --- src/NLog.Database/DatabaseCommandInfo.cs | 2 +- .../DatabaseObjectPropertyInfo.cs | 14 ++-- src/NLog.Database/DatabaseParameterInfo.cs | 34 +++++----- src/NLog.Database/DatabaseTarget.cs | 67 ++++++++++--------- .../Internal/ReflectionHelpers.cs | 4 +- src/NLog.Database/NLog.Database.csproj | 2 + src/NLog/Internal/PropertyHelper.cs | 2 +- src/NLog/Internal/XmlParser.cs | 24 ++++++- .../Wrappers/LeftLayoutRendererWrapper.cs | 2 +- .../LowercaseLayoutRendererWrapper.cs | 2 +- .../Wrappers/PaddingLayoutRendererWrapper.cs | 2 +- .../ReplaceNewLinesLayoutRendererWrapper.cs | 2 +- .../SubstringLayoutRendererWrapper.cs | 2 +- .../WhenEmptyLayoutRendererWrapper.cs | 2 +- .../WrapperLayoutRendererBuilderBase.cs | 2 +- .../XmlEncodeLayoutRendererWrapper.cs | 2 +- src/NLog/Layouts/Layout.cs | 8 +-- src/NLog/Layouts/SimpleLayout.cs | 2 +- src/NLog/Layouts/Typed/Layout.cs | 6 +- .../Layouts/Typed/LayoutTypedExtensions.cs | 2 +- src/NLog/Targets/Target.cs | 4 +- tests/NLog.UnitTests/ApiTests.cs | 4 +- 22 files changed, 110 insertions(+), 81 deletions(-) diff --git a/src/NLog.Database/DatabaseCommandInfo.cs b/src/NLog.Database/DatabaseCommandInfo.cs index d28cc9774b..5d7c23fc41 100644 --- a/src/NLog.Database/DatabaseCommandInfo.cs +++ b/src/NLog.Database/DatabaseCommandInfo.cs @@ -55,7 +55,7 @@ public class DatabaseCommandInfo /// Gets or sets the connection string to run the command against. If not provided, connection string from the target is used. /// /// - public Layout ConnectionString { get; set; } + public Layout ConnectionString { get; set; } = Layout.Empty; /// /// Gets or sets the command text. diff --git a/src/NLog.Database/DatabaseObjectPropertyInfo.cs b/src/NLog.Database/DatabaseObjectPropertyInfo.cs index 432bd36e16..197d12c563 100644 --- a/src/NLog.Database/DatabaseObjectPropertyInfo.cs +++ b/src/NLog.Database/DatabaseObjectPropertyInfo.cs @@ -58,7 +58,7 @@ public DatabaseObjectPropertyInfo() /// Gets or sets the name for the object-property /// /// - public string Name { get; set; } + public string Name { get; set; } = string.Empty; /// /// Gets or sets the value to assign on the object-property @@ -76,22 +76,22 @@ public DatabaseObjectPropertyInfo() /// Gets or sets convert format of the property value /// /// - public string Format { get => _layoutInfo.ValueParseFormat; set => _layoutInfo.ValueParseFormat = value; } + public string? Format { get => _layoutInfo.ValueParseFormat; set => _layoutInfo.ValueParseFormat = value; } /// /// Gets or sets the culture used for parsing property string-value for type-conversion /// /// - public CultureInfo Culture { get => _layoutInfo.ValueParseCulture; set => _layoutInfo.ValueParseCulture = value; } + public CultureInfo? Culture { get => _layoutInfo.ValueParseCulture; set => _layoutInfo.ValueParseCulture = value; } /// /// Render Result Value /// /// Log event for rendering /// Result value when available, else fallback to defaultValue - public object RenderValue(LogEventInfo logEvent) => _layoutInfo.RenderValue(logEvent); + public object? RenderValue(LogEventInfo logEvent) => _layoutInfo.RenderValue(logEvent); - internal bool SetPropertyValue(object dbObject, object propertyValue) + internal bool SetPropertyValue(object dbObject, object? propertyValue) { var dbConnectionType = dbObject.GetType(); var propertySetterCache = _propertySetter; @@ -116,9 +116,9 @@ private struct PropertySetterCacheItem { public string PropertyName { get; } public Type ObjectType { get; } - public Action PropertySetter { get; } + public Action PropertySetter { get; } - public PropertySetterCacheItem(string propertyName, Type objectType, Action propertySetter) + public PropertySetterCacheItem(string propertyName, Type objectType, Action propertySetter) { PropertyName = propertyName; ObjectType = objectType; diff --git a/src/NLog.Database/DatabaseParameterInfo.cs b/src/NLog.Database/DatabaseParameterInfo.cs index 7cb808e2a2..a2bc999980 100644 --- a/src/NLog.Database/DatabaseParameterInfo.cs +++ b/src/NLog.Database/DatabaseParameterInfo.cs @@ -87,7 +87,7 @@ public class DatabaseParameterInfo /// Initializes a new instance of the class. /// public DatabaseParameterInfo() - : this(null, null) + : this(string.Empty, Layout.Empty) { } @@ -142,8 +142,8 @@ public string DbType } } } - private string _dbType; - private Type _dbParameterType; + private string _dbType = string.Empty; + private Type? _dbParameterType; /// /// Gets or sets the database parameter size. @@ -167,7 +167,7 @@ public string DbType /// Gets or sets the type of the parameter. /// /// - public Type ParameterType + public Type? ParameterType { get => _layoutInfo.ValueType; set @@ -181,19 +181,19 @@ public Type ParameterType /// Gets or sets the fallback value when result value is not available /// /// - public Layout DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } + public Layout? DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } /// /// Gets or sets convert format of the database parameter value. /// /// - public string Format { get => _layoutInfo.ValueParseFormat; set => _layoutInfo.ValueParseFormat = value; } + public string? Format { get => _layoutInfo.ValueParseFormat; set => _layoutInfo.ValueParseFormat = value; } /// /// Gets or sets the culture used for parsing parameter string-value for type-conversion /// /// - public CultureInfo Culture { get => _layoutInfo.ValueParseCulture; set => _layoutInfo.ValueParseCulture = value; } + public CultureInfo? Culture { get => _layoutInfo.ValueParseCulture; set => _layoutInfo.ValueParseCulture = value; } /// /// Gets or sets whether empty value should translate into DbNull. Requires database column to allow NULL values. @@ -218,7 +218,7 @@ public bool AllowDbNull /// /// Log event for rendering /// Result value when available, else fallback to defaultValue - public object RenderValue(LogEventInfo logEvent) => _layoutInfo.RenderValue(logEvent); + public object? RenderValue(LogEventInfo logEvent) => _layoutInfo.RenderValue(logEvent); internal bool SetDbType(IDbDataParameter dbParameter) { @@ -235,10 +235,10 @@ internal bool SetDbType(IDbDataParameter dbParameter) return true; // DbType not in use } - private static Type TryParseDbType(string dbTypeName) + private static Type? TryParseDbType(string dbTypeName) { // retrieve the type name if a full name is given - dbTypeName = dbTypeName?.Substring(dbTypeName.LastIndexOf('.') + 1).Trim(); + dbTypeName = dbTypeName is null ? string.Empty : dbTypeName.Substring(dbTypeName.LastIndexOf('.') + 1).Trim(); if (string.IsNullOrEmpty(dbTypeName)) return null; @@ -246,27 +246,27 @@ private static Type TryParseDbType(string dbTypeName) return _typesByDbTypeName.TryGetValue(dbTypeName, out var type) ? type : null; } - DbTypeSetter _cachedDbTypeSetter; + DbTypeSetter? _cachedDbTypeSetter; private sealed class DbTypeSetter { private readonly Type _dbPropertyInfoType; private readonly string _dbTypeName; - private readonly PropertyInfo _dbTypeSetter; - private readonly Enum _dbTypeValue; - private Action _dbTypeSetterFast; + private readonly PropertyInfo? _dbTypeSetter; + private readonly Enum? _dbTypeValue; + private Action? _dbTypeSetterFast; public DbTypeSetter(Type dbParameterType, string dbTypeName) { _dbPropertyInfoType = dbParameterType; - _dbTypeName = dbTypeName?.Trim(); + _dbTypeName = dbTypeName is null ? string.Empty : dbTypeName.Trim(); if (!string.IsNullOrEmpty(_dbTypeName)) { string[] dbTypeNames = _dbTypeName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); if (dbTypeNames.Length > 1 && !string.Equals(dbTypeNames[0], nameof(System.Data.DbType), StringComparison.OrdinalIgnoreCase)) { PropertyInfo propInfo = dbParameterType.GetProperty(dbTypeNames[0], BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); - if (propInfo != null && TryParseEnum(dbTypeNames[1], propInfo.PropertyType, out Enum enumType)) + if (propInfo != null && TryParseEnum(dbTypeNames[1], propInfo.PropertyType, out var enumType) && enumType != null) { _dbTypeSetter = propInfo; _dbTypeValue = enumType; @@ -313,7 +313,7 @@ public bool SetDbType(IDbDataParameter dbParameter) return false; } - private static bool TryParseEnum(string value, Type enumType, out Enum enumValue) + private static bool TryParseEnum(string value, Type enumType, out Enum? enumValue) { if (!string.IsNullOrEmpty(value)) { diff --git a/src/NLog.Database/DatabaseTarget.cs b/src/NLog.Database/DatabaseTarget.cs index 20daa639f7..d505dfa942 100644 --- a/src/NLog.Database/DatabaseTarget.cs +++ b/src/NLog.Database/DatabaseTarget.cs @@ -80,8 +80,8 @@ namespace NLog.Targets [Target("Database")] public class DatabaseTarget : Target, IInstallable { - private IDbConnection _activeConnection; - private string _activeConnectionString; + private IDbConnection? _activeConnection; + private string? _activeConnectionString; /// /// Initializes a new instance of the class. @@ -139,7 +139,7 @@ public DatabaseTarget(string name) : this() /// Gets or sets the name of the connection string (as specified in <connectionStrings> configuration section. /// /// - public string ConnectionStringName { get; set; } + public string ConnectionStringName { get; set; } = string.Empty; #endif /// @@ -147,13 +147,13 @@ public DatabaseTarget(string name) : this() /// specified in DBHost, DBUserName, DBPassword, DBDatabase. /// /// - public Layout ConnectionString { get; set; } + public Layout ConnectionString { get; set; } = Layout.Empty; /// /// Gets or sets the connection string using for installation and uninstallation. If not provided, regular ConnectionString is being used. /// /// - public Layout InstallConnectionString { get; set; } + public Layout InstallConnectionString { get; set; } = Layout.Empty; /// /// Gets the installation DDL commands. @@ -191,7 +191,7 @@ public DatabaseTarget(string name) : this() /// connection string. /// /// - public Layout DBUserName { get; set; } + public Layout? DBUserName { get; set; } /// /// Gets or sets the database password. If the ConnectionString is not provided @@ -199,7 +199,7 @@ public DatabaseTarget(string name) : this() /// connection string. /// /// - public Layout DBPassword + public Layout? DBPassword { get => _dbPassword; set @@ -211,8 +211,8 @@ public Layout DBPassword _dbPasswordFixed = null; } } - private Layout _dbPassword; - private string _dbPasswordFixed; + private Layout? _dbPassword; + private string? _dbPasswordFixed; /// /// Gets or sets the database name. If the ConnectionString is not provided @@ -220,7 +220,7 @@ public Layout DBPassword /// connection string. /// /// - public Layout DBDatabase { get; set; } + public Layout? DBDatabase { get; set; } /// /// Gets or sets the text of the SQL command to be run on each log level. @@ -279,13 +279,13 @@ public Layout DBPassword public System.Data.IsolationLevel? IsolationLevel { get; set; } #if NETFRAMEWORK - internal DbProviderFactory ProviderFactory { get; set; } + internal DbProviderFactory? ProviderFactory { get; set; } // this is so we can mock the connection string without creating sub-processes internal ConnectionStringSettingsCollection ConnectionStringsSettings { get; set; } #endif - internal Type ConnectionType { get; private set; } + internal Type? ConnectionType { get; private set; } /// /// Performs installation which requires administrative permissions. @@ -390,9 +390,10 @@ protected override void InitializeTarget() throw new NLogConfigurationException($"Connection string '{ConnectionStringName}' is not declared in section."); } - if (!string.IsNullOrEmpty(cs.ConnectionString?.Trim())) + var connectionString = cs.ConnectionString ?? string.Empty; + if (!string.IsNullOrEmpty(connectionString.Trim())) { - ConnectionString = Layout.FromLiteral(cs.ConnectionString); + ConnectionString = Layout.FromLiteral(connectionString); } providerName = cs.ProviderName?.Trim() ?? string.Empty; } @@ -667,7 +668,7 @@ private ICollection>> GroupInBucke } string firstConnectionString = BuildConnectionString(logEvents[0].LogEvent); - IDictionary> dictionary = null; + IDictionary>? dictionary = null; for (int i = 1; i < logEvents.Count; ++i) { var connectionString = BuildConnectionString(logEvents[i].LogEvent); @@ -689,7 +690,10 @@ private ICollection>> GroupInBucke bucket.Add(logEvents[i]); } - return (ICollection>>)dictionary ?? new KeyValuePair>[] { new KeyValuePair>(firstConnectionString, logEvents) }; + if (dictionary is null) + return new KeyValuePair>[] { new KeyValuePair>(firstConnectionString, logEvents) }; + else + return dictionary; } private void WriteLogEventsToDatabase(IList logEvents, string connectionString) @@ -758,14 +762,14 @@ private void WriteLogEventsWithTransactionScope(IList logEven //Always suppress transaction so that the caller does not rollback logging if they are rolling back their transaction. using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Suppress)) { - EnsureConnectionOpen(connectionString, logEvents.Count > 0 ? logEvents[0].LogEvent : null); + var activeConnection = EnsureConnectionOpen(connectionString, logEvents[0].LogEvent); - var dbTransaction = _activeConnection.BeginTransaction(IsolationLevel.Value); + var dbTransaction = activeConnection.BeginTransaction(IsolationLevel ?? System.Data.IsolationLevel.Snapshot); try { for (int i = 0; i < logEvents.Count; ++i) { - ExecuteDbCommandWithParameters(logEvents[i].LogEvent, _activeConnection, dbTransaction); + ExecuteDbCommandWithParameters(logEvents[i].LogEvent, activeConnection, dbTransaction); } dbTransaction?.Commit(); @@ -814,9 +818,9 @@ private void WriteLogEventSuppressTransactionScope(LogEventInfo logEvent, string //Always suppress transaction so that the caller does not rollback logging if they are rolling back their transaction. using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Suppress)) { - EnsureConnectionOpen(connectionString, logEvent); + var activeConnection = EnsureConnectionOpen(connectionString, logEvent); - ExecuteDbCommandWithParameters(logEvent, _activeConnection, null); + ExecuteDbCommandWithParameters(logEvent, activeConnection, null); transactionScope.Complete(); //not really needed as there is no transaction at all. } @@ -836,7 +840,7 @@ private void WriteLogEventSuppressTransactionScope(LogEventInfo logEvent, string /// /// Write logEvent to database /// - private void ExecuteDbCommandWithParameters(LogEventInfo logEvent, IDbConnection dbConnection, IDbTransaction dbTransaction) + private void ExecuteDbCommandWithParameters(LogEventInfo logEvent, IDbConnection dbConnection, IDbTransaction? dbTransaction) { using (IDbCommand command = CreateDbCommand(logEvent, dbConnection)) { @@ -889,7 +893,7 @@ private IDbCommand CreateDbCommandWithParameters(LogEventInfo logEvent, IDbConne /// protected string BuildConnectionString(LogEventInfo logEvent) { - if (ConnectionString != null) + if (ConnectionString != null && !ReferenceEquals(ConnectionString, Layout.Empty)) { return RenderLogEvent(ConnectionString, logEvent); } @@ -909,7 +913,7 @@ protected string BuildConnectionString(LogEventInfo logEvent) sb.Append("User id="); sb.Append(dbUserName); sb.Append(";Password="); - var password = _dbPasswordFixed ?? EscapeValueForConnectionString(_dbPassword.Render(logEvent)); + var password = _dbPasswordFixed ?? EscapeValueForConnectionString(_dbPassword?.Render(logEvent) ?? string.Empty); sb.Append(password); sb.Append(";"); } @@ -967,7 +971,7 @@ private static string EscapeValueForConnectionString(string value) return value; } - private void EnsureConnectionOpen(string connectionString, LogEventInfo logEventInfo) + private IDbConnection EnsureConnectionOpen(string connectionString, LogEventInfo logEventInfo) { if (_activeConnection != null && _activeConnectionString != connectionString) { @@ -977,12 +981,13 @@ private void EnsureConnectionOpen(string connectionString, LogEventInfo logEvent if (_activeConnection != null) { - return; + return _activeConnection; } InternalLogger.Trace("{0}: Open connection.", this); _activeConnection = OpenConnection(connectionString, logEventInfo); _activeConnectionString = connectionString; + return _activeConnection; } private void CloseConnection() @@ -1024,13 +1029,13 @@ private void RunInstallCommands(InstallationContext installationContext, IEnumer SetConnectionType(); } - EnsureConnectionOpen(connectionString, logEvent); + var activeConnection = EnsureConnectionOpen(connectionString, logEvent); string commandText = RenderLogEvent(commandInfo.Text, logEvent); installationContext.Trace("DatabaseTarget(Name={0}) - Executing {1} '{2}'", Name, commandInfo.CommandType, commandText); - using (IDbCommand command = CreateDbCommandWithParameters(logEvent, _activeConnection, commandInfo.CommandType, commandText, commandInfo.Parameters)) + using (IDbCommand command = CreateDbCommandWithParameters(logEvent, activeConnection, commandInfo.CommandType, commandText, commandInfo.Parameters)) { try { @@ -1064,12 +1069,12 @@ private void RunInstallCommands(InstallationContext installationContext, IEnumer private string GetConnectionStringFromCommand(DatabaseCommandInfo commandInfo, LogEventInfo logEvent) { string connectionString; - if (commandInfo.ConnectionString != null) + if (commandInfo.ConnectionString != null && !ReferenceEquals(commandInfo.ConnectionString, Layout.Empty)) { // if there is connection string specified on the command info, use it connectionString = RenderLogEvent(commandInfo.ConnectionString, logEvent); } - else if (InstallConnectionString != null) + else if (InstallConnectionString != null && !ReferenceEquals(InstallConnectionString, Layout.Empty)) { // next, try InstallConnectionString connectionString = RenderLogEvent(InstallConnectionString, logEvent); @@ -1135,7 +1140,7 @@ protected virtual IDbDataParameter CreateDatabaseParameter(IDbCommand command, D /// /// Current logevent. /// Parameter configuration info. - protected internal virtual object GetDatabaseParameterValue(LogEventInfo logEvent, DatabaseParameterInfo parameterInfo) + protected internal virtual object? GetDatabaseParameterValue(LogEventInfo logEvent, DatabaseParameterInfo parameterInfo) { var value = parameterInfo.RenderValue(logEvent); if (parameterInfo.AllowDbNull && (value is null || string.Empty.Equals(value))) diff --git a/src/NLog.Database/Internal/ReflectionHelpers.cs b/src/NLog.Database/Internal/ReflectionHelpers.cs index 2eac56106b..e5c2439b19 100644 --- a/src/NLog.Database/Internal/ReflectionHelpers.cs +++ b/src/NLog.Database/Internal/ReflectionHelpers.cs @@ -41,12 +41,12 @@ namespace NLog.Internal /// internal static class ReflectionHelpers { - public static Action CreatePropertySetter(this PropertyInfo propertyInfo) + public static Action CreatePropertySetter(this PropertyInfo propertyInfo) { var target = System.Linq.Expressions.Expression.Parameter(typeof(object), "target"); var value = System.Linq.Expressions.Expression.Parameter(typeof(object), "parameter"); var body = System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression.Convert(target, propertyInfo.DeclaringType), propertyInfo.GetSetMethod(true), System.Linq.Expressions.Expression.Convert(value, propertyInfo.PropertyType)); - return System.Linq.Expressions.Expression.Lambda>(body, target, value).Compile(); + return System.Linq.Expressions.Expression.Lambda>(body, target, value).Compile(); } } } diff --git a/src/NLog.Database/NLog.Database.csproj b/src/NLog.Database/NLog.Database.csproj index acb25206d8..baae61c20b 100644 --- a/src/NLog.Database/NLog.Database.csproj +++ b/src/NLog.Database/NLog.Database.csproj @@ -34,6 +34,8 @@ true true true + enable + 9 true true diff --git a/src/NLog/Internal/PropertyHelper.cs b/src/NLog/Internal/PropertyHelper.cs index 079df40118..90e1efd654 100644 --- a/src/NLog/Internal/PropertyHelper.cs +++ b/src/NLog/Internal/PropertyHelper.cs @@ -330,7 +330,7 @@ private static bool TryGetEnumValue(Type resultType, string value, out object? r private static object TryParseLayoutValue(string stringValue, ConfigurationItemFactory configurationItemFactory) { - return string.IsNullOrEmpty(stringValue) ? SimpleLayout.Default : new SimpleLayout(stringValue, configurationItemFactory); + return string.IsNullOrEmpty(stringValue) ? Layout.Empty : new SimpleLayout(stringValue, configurationItemFactory); } private static object TryParseConditionValue(string stringValue, ConfigurationItemFactory configurationItemFactory) diff --git a/src/NLog/Internal/XmlParser.cs b/src/NLog/Internal/XmlParser.cs index a4a240272d..d76e6b4b13 100644 --- a/src/NLog/Internal/XmlParser.cs +++ b/src/NLog/Internal/XmlParser.cs @@ -36,7 +36,6 @@ namespace NLog.Internal using System; using System.Collections; using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Text; @@ -270,7 +269,7 @@ public bool TryReadInnerText(out string innerText) private string ReadCDATA() { string contentValue; - if (!SkipChar('!') || !SkipChar('[') || !SkipChar('C') || !SkipChar('D') || !SkipChar('A') || !SkipChar('T') || !SkipChar('A') || !SkipChar('[')) + if (!SkipCDATA()) throw new XmlParserException("Invalid XML document. Cannot parse XML CDATA"); _stringBuilder.ClearBuilder(); @@ -298,6 +297,27 @@ private string ReadCDATA() return contentValue; } + private bool SkipCDATA() + { + if (!SkipChar('!')) + return false; + if (!SkipChar('[')) + return false; + if (!SkipChar('C')) + return false; + if (!SkipChar('D')) + return false; + if (!SkipChar('A')) + return false; + if (!SkipChar('T')) + return false; + if (!SkipChar('A')) + return false; + if (!SkipChar('[')) + return false; + return true; + } + private void SkipXmlComment() { if (!SkipChar('!') || !SkipChar('-') || !SkipChar('-')) diff --git a/src/NLog/LayoutRenderers/Wrappers/LeftLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/LeftLayoutRendererWrapper.cs index abb0569626..452fd5e9bc 100644 --- a/src/NLog/LayoutRenderers/Wrappers/LeftLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/LeftLayoutRendererWrapper.cs @@ -73,7 +73,7 @@ protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBui return; } - Inner.Render(logEvent, builder); + Inner?.Render(logEvent, builder); var renderedLength = builder.Length - orgLength; if (renderedLength > Length) { diff --git a/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs index ce258f58be..702af64400 100644 --- a/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs @@ -77,7 +77,7 @@ public sealed class LowercaseLayoutRendererWrapper : WrapperLayoutRendererBase /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) { - Inner.Render(logEvent, builder); + Inner?.Render(logEvent, builder); if (Lowercase && builder.Length > orgLength) { TransformToLowerCase(builder, logEvent, orgLength); diff --git a/src/NLog/LayoutRenderers/Wrappers/PaddingLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/PaddingLayoutRendererWrapper.cs index f81d198799..f146226594 100644 --- a/src/NLog/LayoutRenderers/Wrappers/PaddingLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/PaddingLayoutRendererWrapper.cs @@ -89,7 +89,7 @@ public sealed class PaddingLayoutRendererWrapper : WrapperLayoutRendererBase /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) { - Inner.Render(logEvent, builder); + Inner?.Render(logEvent, builder); if (Padding != 0) { int absolutePadding = Padding; diff --git a/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs index 81de2043a5..f32dc623ba 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs @@ -79,7 +79,7 @@ public string Replacement /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) { - Inner.Render(logEvent, builder); + Inner?.Render(logEvent, builder); if (builder.Length > orgLength) { var containsNewLines = builder.IndexOf('\n', orgLength) >= 0; diff --git a/src/NLog/LayoutRenderers/Wrappers/SubstringLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/SubstringLayoutRendererWrapper.cs index b018470a82..adaf1954ae 100644 --- a/src/NLog/LayoutRenderers/Wrappers/SubstringLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/SubstringLayoutRendererWrapper.cs @@ -76,7 +76,7 @@ protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBui return; } - Inner.Render(logEvent, builder); + Inner?.Render(logEvent, builder); var renderedLength = builder.Length - orgLength; if (renderedLength > 0) diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs index 21b1dddf94..5f0986dbc8 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs @@ -82,7 +82,7 @@ protected override void InitializeLayoutRenderer() /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) { - Inner.Render(logEvent, builder); + Inner?.Render(logEvent, builder); if (builder.Length > orgLength) return; diff --git a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs index 232762b499..607e193a4b 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs @@ -78,7 +78,7 @@ protected virtual void TransformFormattedMesssage(LogEventInfo logEvent, StringB [Obsolete("Inherit from WrapperLayoutRendererBase and override RenderInnerAndTransform() instead. Marked obsolete in NLog 4.6")] protected virtual void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target) { - Inner.Render(logEvent, target); + Inner?.Render(logEvent, target); } /// diff --git a/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs index 5d6f81ac58..7ee5e71fe3 100644 --- a/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs @@ -77,7 +77,7 @@ protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBui orgLength = builder.Length; } - Inner.Render(logEvent, builder); + Inner?.Render(logEvent, builder); XmlHelper.RemoveInvalidXmlIfNeeded(builder, orgLength); diff --git a/src/NLog/Layouts/Layout.cs b/src/NLog/Layouts/Layout.cs index 09aa86ecaa..a0c8c518d7 100644 --- a/src/NLog/Layouts/Layout.cs +++ b/src/NLog/Layouts/Layout.cs @@ -54,7 +54,7 @@ public abstract class Layout : ISupportsInitialize, IRenderable /// /// Default Layout-value that renders string.Empty /// - public static Layout Empty => SimpleLayout.Default; + public static readonly Layout Empty = new SimpleLayout(); /// /// Is this layout initialized? See @@ -117,7 +117,7 @@ public static Layout FromString([Localizable(false)] string layoutText) /// Instance of . public static Layout FromString([Localizable(false)] string layoutText, ConfigurationItemFactory configurationItemFactory) { - return string.IsNullOrEmpty(layoutText) ? SimpleLayout.Default : new SimpleLayout(layoutText, configurationItemFactory); + return string.IsNullOrEmpty(layoutText) ? Layout.Empty : new SimpleLayout(layoutText, configurationItemFactory); } /// @@ -130,7 +130,7 @@ public static Layout FromString([Localizable(false)] string layoutText, bool thr { try { - return string.IsNullOrEmpty(layoutText) ? SimpleLayout.Default : new SimpleLayout(layoutText, ConfigurationItemFactory.Default, throwConfigExceptions); + return string.IsNullOrEmpty(layoutText) ? Layout.Empty : new SimpleLayout(layoutText, ConfigurationItemFactory.Default, throwConfigExceptions); } catch (NLogConfigurationException) { @@ -151,7 +151,7 @@ public static Layout FromString([Localizable(false)] string layoutText, bool thr public static Layout FromLiteral([Localizable(false)] string literalText) { if (string.IsNullOrEmpty(literalText)) - return SimpleLayout.Default; + return Layout.Empty; else return new SimpleLayout(new[] { new NLog.LayoutRenderers.LiteralLayoutRenderer(literalText) }, literalText); } diff --git a/src/NLog/Layouts/SimpleLayout.cs b/src/NLog/Layouts/SimpleLayout.cs index 27df45988d..369534e226 100644 --- a/src/NLog/Layouts/SimpleLayout.cs +++ b/src/NLog/Layouts/SimpleLayout.cs @@ -62,7 +62,7 @@ public sealed class SimpleLayout : Layout, IUsesStackTrace, IStringValueRenderer private readonly IRawValue? _rawValueRenderer; private IStringValueRenderer? _stringValueRenderer; - internal static readonly SimpleLayout Default = new SimpleLayout(); + internal static SimpleLayout Default => (SimpleLayout)Layout.Empty; /// /// Initializes a new instance of the class. diff --git a/src/NLog/Layouts/Typed/Layout.cs b/src/NLog/Layouts/Typed/Layout.cs index c0f0556432..997195c160 100644 --- a/src/NLog/Layouts/Typed/Layout.cs +++ b/src/NLog/Layouts/Typed/Layout.cs @@ -144,12 +144,12 @@ private Layout(Func layoutMethod, LayoutRenderOptions options) /// Log event for rendering /// Fallback value when no value available /// Result value when available, else fallback to defaultValue - internal T? RenderTypedValue([CanBeNull] LogEventInfo? logEvent, T? defaultValue = default(T)) + internal T? RenderTypedValue(LogEventInfo logEvent, T? defaultValue = default(T)) { return RenderTypedValue(logEvent, null, defaultValue); } - internal T? RenderTypedValue([CanBeNull] LogEventInfo? logEvent, [CanBeNull] StringBuilder? stringBuilder, T? defaultValue) + internal T? RenderTypedValue(LogEventInfo logEvent, [CanBeNull] StringBuilder? stringBuilder, T? defaultValue) { if (IsFixed) return _fixedValue; @@ -173,7 +173,7 @@ private Layout(Func layoutMethod, LayoutRenderOptions options) return defaultValue; } - private object? RenderObjectValue([CanBeNull] LogEventInfo logEvent, [CanBeNull] StringBuilder? stringBuilder) + private object? RenderObjectValue(LogEventInfo logEvent, [CanBeNull] StringBuilder? stringBuilder) { if (logEvent is null) return null; diff --git a/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs b/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs index c3e330d343..bfe3650d8c 100644 --- a/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs +++ b/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs @@ -51,7 +51,7 @@ public static class LayoutTypedExtensions /// The logevent info. /// Fallback value when no value available /// Result value when available, else fallback to defaultValue - public static T? RenderValue([CanBeNull] this Layout? layout, [CanBeNull] LogEventInfo? logEvent, T? defaultValue = default(T)) + public static T? RenderValue([CanBeNull] this Layout? layout, LogEventInfo logEvent, T? defaultValue = default(T)) { return layout is null ? defaultValue : layout.RenderTypedValue(logEvent, defaultValue); } diff --git a/src/NLog/Targets/Target.cs b/src/NLog/Targets/Target.cs index 3b51a04fba..066959d450 100644 --- a/src/NLog/Targets/Target.cs +++ b/src/NLog/Targets/Target.cs @@ -698,7 +698,7 @@ protected void MergeEventProperties(LogEventInfo logEvent) /// The layout. /// The logevent info. /// String representing log event. - protected string RenderLogEvent([CanBeNull] Layout layout, [CanBeNull] LogEventInfo logEvent) + protected string RenderLogEvent([CanBeNull] Layout? layout, LogEventInfo logEvent) { if (layout is null || logEvent is null) return string.Empty; // Signal that input was wrong @@ -729,7 +729,7 @@ protected string RenderLogEvent([CanBeNull] Layout layout, [CanBeNull] LogEventI /// The logevent info. /// Fallback value when no value available /// Result value when available, else fallback to defaultValue - protected T? RenderLogEvent([CanBeNull] Layout? layout, [CanBeNull] LogEventInfo? logEvent, T? defaultValue = default(T)) + protected T? RenderLogEvent([CanBeNull] Layout? layout, LogEventInfo logEvent, T? defaultValue = default(T)) { if (layout is null) return defaultValue; diff --git a/tests/NLog.UnitTests/ApiTests.cs b/tests/NLog.UnitTests/ApiTests.cs index 579d9cd64b..00ec8df654 100644 --- a/tests/NLog.UnitTests/ApiTests.cs +++ b/tests/NLog.UnitTests/ApiTests.cs @@ -477,8 +477,8 @@ public void ShouldNotHaveExplicitStaticConstructors() "NLog.MessageTemplates.MessageTemplateParameters", "NLog.MessageTemplates.TemplateEnumerator", "NLog.MessageTemplates.ValueFormatter", + "NLog.Layouts.Layout", "NLog.Layouts.LayoutParser", - "NLog.Layouts.SimpleLayout", "NLog.Layouts.ValueTypeLayoutInfo", "NLog.Layouts.XmlElementBase", "NLog.LayoutRenderers.AllEventPropertiesLayoutRenderer", @@ -524,6 +524,8 @@ public void ShouldNotHaveExplicitStaticConstructors() "NLog.Config.LoggingConfigurationParser+ValidatedConfigurationElement" }; + knownStaticConstructors.Remove("NLog.Layouts.SimpleLayout"); // Prevent random initialization order of static contructors, because NLog.Layouts.Layout has static-constructor that depends on NLog.Layouts.SimpleLayout + var typesWithStaticConstructors = allTypes // Exclude compiler-generated types (e.g., lambdas, nested <>c classes) .Where(type => !type.IsDefined(typeof(CompilerGeneratedAttribute), inherit: false)) From 88f7584c36924aedd6664ebe0f6346da0307bf9b Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 23 May 2025 06:20:55 +0200 Subject: [PATCH 122/224] NLog.OutputDebugString with nullable references (#5828) --- .../NLog.OutputDebugString.csproj | 2 + src/NLog.RegEx/Internal/RegexHelper.cs | 14 +++--- .../RegexReplaceLayoutRendererWrapper.cs | 17 +++---- src/NLog.RegEx/NLog.RegEx.csproj | 2 + .../ConsoleWordHighlightingRuleRegex.cs | 6 +-- .../NLog.WindowsRegistry.csproj | 2 + .../RegistryLayoutRenderer.cs | 44 ++++++++++--------- 7 files changed, 47 insertions(+), 40 deletions(-) diff --git a/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj b/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj index 31c4ac5080..dbd5a86483 100644 --- a/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj +++ b/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj @@ -34,6 +34,8 @@ true true true + enable + 9 true true diff --git a/src/NLog.RegEx/Internal/RegexHelper.cs b/src/NLog.RegEx/Internal/RegexHelper.cs index a7fae2a717..d803a7e999 100644 --- a/src/NLog.RegEx/Internal/RegexHelper.cs +++ b/src/NLog.RegEx/Internal/RegexHelper.cs @@ -38,15 +38,15 @@ namespace NLog.RegEx.Internal internal sealed class RegexHelper { - private Regex _regex; - private string _searchText; - private string _regexPattern; + private Regex? _regex; + private string? _searchText; + private string? _regexPattern; private bool _wholeWords; private bool _ignoreCase; public string SearchText { - get => _searchText; + get => _searchText ?? string.Empty; set { _searchText = value; @@ -55,7 +55,7 @@ public string SearchText } } - public string RegexPattern + public string? RegexPattern { get => _regexPattern; set @@ -103,7 +103,7 @@ public bool IgnoreCase } } - public Regex Regex + public Regex? Regex { get { @@ -159,7 +159,7 @@ public string Replace(string input, string replacement) } } - public MatchCollection Matches(string input) + public MatchCollection? Matches(string input) { if (CompileRegex) { diff --git a/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs b/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs index 1f36c70456..dd24867e6d 100644 --- a/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs +++ b/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs @@ -56,8 +56,8 @@ namespace NLog.LayoutRenderers.Wrappers [ThreadAgnostic] public sealed class RegexReplaceLayoutRendererWrapper : WrapperLayoutRendererBase { - private RegexHelper _regexHelper; - private MatchEvaluator _groupMatchEvaluator; + private readonly RegexHelper _regexHelper = new RegexHelper(); + private MatchEvaluator? _groupMatchEvaluator; /// /// Gets or sets the text to search for. @@ -79,7 +79,7 @@ public sealed class RegexReplaceLayoutRendererWrapper : WrapperLayoutRendererBas /// /// The group name. /// - public string ReplaceGroupName { get; set; } + public string? ReplaceGroupName { get; set; } /// /// Gets or sets a value indicating whether to ignore case. @@ -109,12 +109,9 @@ protected override void InitializeLayoutRenderer() if (string.IsNullOrEmpty(SearchFor)) throw new NLogConfigurationException("RegEx-Replace-LayoutRenderer SearchFor-property must be assigned. Searching for blank value not supported."); - _regexHelper = new RegexHelper() - { - IgnoreCase = IgnoreCase, - WholeWords = WholeWords, - CompileRegex = CompileRegex, - }; + _regexHelper.IgnoreCase = IgnoreCase; + _regexHelper.WholeWords = WholeWords; + _regexHelper.CompileRegex = CompileRegex; _regexHelper.RegexPattern = SearchFor; if (!string.IsNullOrEmpty(ReplaceGroupName) && _regexHelper.Regex?.GetGroupNames()?.Contains(ReplaceGroupName) == false) @@ -126,7 +123,7 @@ protected override void InitializeLayoutRenderer() /// protected override string Transform(string text) { - if (string.IsNullOrEmpty(ReplaceGroupName)) + if (ReplaceGroupName is null || string.IsNullOrEmpty(ReplaceGroupName)) { return _regexHelper.Replace(text, ReplaceWith); } diff --git a/src/NLog.RegEx/NLog.RegEx.csproj b/src/NLog.RegEx/NLog.RegEx.csproj index 4f9064419f..1cf37d26ad 100644 --- a/src/NLog.RegEx/NLog.RegEx.csproj +++ b/src/NLog.RegEx/NLog.RegEx.csproj @@ -34,6 +34,8 @@ true true true + enable + 9 true true diff --git a/src/NLog.RegEx/Targets/ConsoleWordHighlightingRuleRegex.cs b/src/NLog.RegEx/Targets/ConsoleWordHighlightingRuleRegex.cs index 544fc4a9be..8972385149 100644 --- a/src/NLog.RegEx/Targets/ConsoleWordHighlightingRuleRegex.cs +++ b/src/NLog.RegEx/Targets/ConsoleWordHighlightingRuleRegex.cs @@ -49,7 +49,7 @@ public class ConsoleWordHighlightingRuleRegex : ConsoleWordHighlightingRule /// Gets or sets the regular expression to be matched. You must specify either text or regex. /// /// - public string Regex + public string? Regex { get => _regexHelper.RegexPattern; set => _regexHelper.RegexPattern = value; @@ -65,10 +65,10 @@ public bool CompileRegex set => _regexHelper.CompileRegex = value; } - private string _searchText; + private string _searchText = string.Empty; /// - protected override IEnumerable> GetWordsForHighlighting(string haystack) + protected override IEnumerable>? GetWordsForHighlighting(string haystack) { if (!ReferenceEquals(_searchText, Text)) { diff --git a/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj b/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj index 794529f637..f3e5ada621 100644 --- a/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj +++ b/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj @@ -34,6 +34,8 @@ true true true + enable + 9 true true diff --git a/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs b/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs index 38aa9aa7ac..fd49d7d491 100644 --- a/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs +++ b/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs @@ -38,7 +38,6 @@ namespace NLog.LayoutRenderers using System.Text; using Microsoft.Win32; using NLog.Common; - using NLog.Config; using NLog.Layouts; /// @@ -51,13 +50,13 @@ public class RegistryLayoutRenderer : LayoutRenderer /// Gets or sets the registry value name. /// /// - public Layout Value { get; set; } + public Layout Value { get; set; } = Layout.Empty; /// /// Gets or sets the value to be output when the specified registry key or value is not found. /// /// - public Layout DefaultValue { get; set; } + public Layout? DefaultValue { get; set; } /// /// Require escaping backward slashes in . Need to be backwards-compatible. @@ -114,9 +113,17 @@ protected override void InitializeLayoutRenderer() /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { - object registryValue = null; + var value = LookupRegistryValue(logEvent); + builder.Append(value); + } + + private string LookupRegistryValue(LogEventInfo logEvent) + { + object? registryValue = null; // Value = null is necessary for querying "unnamed values" - string renderedValue = Value?.Render(logEvent); + var registryName = Value?.Render(logEvent); + if (string.IsNullOrEmpty(registryName)) + registryName = null; var parseResult = ParseKey(Key.Render(logEvent)); try @@ -131,12 +138,12 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) { using (RegistryKey registryKey = rootKey.OpenSubKey(parseResult.SubKey)) { - if (registryKey != null) registryValue = registryKey.GetValue(renderedValue); + if (registryKey != null) registryValue = registryKey.GetValue(registryName); } } else { - registryValue = rootKey.GetValue(renderedValue); + registryValue = rootKey.GetValue(registryName); } } } @@ -148,27 +155,24 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) InternalLogger.Error(ex, "Error when reading from registry"); } - string value = null; if (registryValue != null) // valid value returned from registry will never be null { - value = Convert.ToString(registryValue, System.Globalization.CultureInfo.InvariantCulture); + return Convert.ToString(registryValue, System.Globalization.CultureInfo.InvariantCulture); } - else if (DefaultValue != null) - { - value = DefaultValue.Render(logEvent); - if (RequireEscapingSlashesInDefaultValue) - { - //remove escape slash - value = value.Replace("\\\\", "\\"); - } + var defaultValue = DefaultValue?.Render(logEvent); + if (defaultValue != null && RequireEscapingSlashesInDefaultValue) + { + //remove escape slash + defaultValue = defaultValue.Replace("\\\\", "\\"); } - builder.Append(value); + + return defaultValue ?? string.Empty; } private sealed class ParseResult { - public string SubKey { get; set; } + public string SubKey { get; set; } = string.Empty; public RegistryHive Hive { get; set; } @@ -188,7 +192,7 @@ private static ParseResult ParseKey(string key) string hiveName; int pos = key.IndexOfAny(new char[] { '\\', '/' }); - string subkey = null; + string subkey = string.Empty; if (pos >= 0) { hiveName = key.Substring(0, pos); From 6c6fd5301444824568874d7d0dd7c5af0820afa6 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 23 May 2025 07:58:57 +0200 Subject: [PATCH 123/224] NLog.Targets.WebService with nullable references (#5829) --- .../AtomicFileTarget.cs | 4 +- .../NLog.Targets.AtomicFile.csproj | 2 + .../NLog.Targets.GZipFile.csproj | 2 + src/NLog.Targets.Mail/MailTarget.cs | 10 +-- .../NLog.Targets.Mail.csproj | 2 + .../NLog.Targets.Trace.csproj | 2 + src/NLog.Targets.Trace/NLogTraceListener.cs | 21 ++--- .../StackTraceUsageUtils.cs | 13 +-- .../NLog.Targets.WebService.csproj | 2 + .../WebServiceTarget.cs | 88 ++++++++++--------- src/NLog/Internal/StackTraceUsageUtils.cs | 13 +-- 11 files changed, 90 insertions(+), 69 deletions(-) diff --git a/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs b/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs index be01af39fa..f83220df44 100644 --- a/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs +++ b/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs @@ -132,8 +132,8 @@ private Stream CreateUnixStream(string filePath) int fd = Mono.Unix.Native.Syscall.open(filePath, openFlags, permissions); if (fd == -1 && Mono.Unix.Native.Stdlib.GetLastError() == Mono.Unix.Native.Errno.ENOENT && CreateDirs) { - string dirName = Path.GetDirectoryName(filePath); - if (!Directory.Exists(dirName)) + var dirName = Path.GetDirectoryName(filePath); + if (dirName != null && !Directory.Exists(dirName)) Directory.CreateDirectory(dirName); fd = Mono.Unix.Native.Syscall.open(filePath, openFlags, permissions); diff --git a/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj index 9c80e474fa..319a52ecdf 100644 --- a/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj +++ b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj @@ -31,6 +31,8 @@ true true true + enable + 9 true true true diff --git a/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj b/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj index 9be6293195..1946050f5f 100644 --- a/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj +++ b/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj @@ -33,6 +33,8 @@ true true true + enable + 9 true true true diff --git a/src/NLog.Targets.Mail/MailTarget.cs b/src/NLog.Targets.Mail/MailTarget.cs index d7809b0acd..03c74f103a 100644 --- a/src/NLog.Targets.Mail/MailTarget.cs +++ b/src/NLog.Targets.Mail/MailTarget.cs @@ -112,14 +112,14 @@ public MailTarget(string name) : this() } #if NETFRAMEWORK - private SmtpSection _currentailSettings; + private SmtpSection? _currentailSettings; /// /// Gets the mailSettings/smtp configuration from app.config in cases when we need those configuration. /// E.g when UseSystemNetMailSettings is enabled and we need to read the From attribute from system.net/mailSettings/smtp /// /// Internal for mocking - internal SmtpSection SmtpSection + internal SmtpSection? SmtpSection { get { @@ -163,8 +163,8 @@ public Layout From //only use from config when not set in current if (UseSystemNetMailSettings && (_from is null || ReferenceEquals(_from, Layout.Empty))) { - var from = SmtpSection.From; - if (string.IsNullOrEmpty(from)) + var from = SmtpSection?.From; + if (from is null || string.IsNullOrEmpty(from)) return Layout.Empty; _from = from; } @@ -287,7 +287,7 @@ public Layout Body /// Gets or sets the priority used for sending mails. /// /// - public Layout Priority { get; set; } + public Layout Priority { get; set; } = MailPriority.Normal; /// /// Gets or sets a value indicating whether NewLine characters in the body should be replaced with
tags. diff --git a/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj b/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj index 9ffb129b5e..5344b23725 100644 --- a/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj +++ b/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj @@ -34,6 +34,8 @@ true true true + enable + 9 true true diff --git a/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj b/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj index e76df1be10..3052064f51 100644 --- a/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj +++ b/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj @@ -34,6 +34,8 @@ true true true + enable + 9 true true diff --git a/src/NLog.Targets.Trace/NLogTraceListener.cs b/src/NLog.Targets.Trace/NLogTraceListener.cs index 7b4cfe2b80..8946648506 100644 --- a/src/NLog.Targets.Trace/NLogTraceListener.cs +++ b/src/NLog.Targets.Trace/NLogTraceListener.cs @@ -55,7 +55,7 @@ public class NLogTraceListener : TraceListener private LogLevel _defaultLogLevel = LogLevel.Debug; private bool _attributesLoaded; private bool _autoLoggerName; - private LogLevel _forceLogLevel; + private LogLevel? _forceLogLevel; private bool _disableFlush; /// @@ -63,6 +63,7 @@ public class NLogTraceListener : TraceListener /// public NLogTraceListener() { + _logFactory = LogManager.LogFactory; } /// @@ -104,7 +105,7 @@ public LogLevel DefaultLogLevel /// /// Gets or sets the log which should be always used regardless of source level. /// - public LogLevel ForceLogLevel + public LogLevel? ForceLogLevel { get { @@ -204,7 +205,7 @@ private void WriteInternal(string message) if (Filter != null && !Filter.ShouldTrace(null, String.Empty, TraceEventType.Verbose, 0, message, null, null, null)) return; - ProcessLogEventInfo(DefaultLogLevel, null, message, null, null, TraceEventType.Verbose, null); + ProcessLogEventInfo(DefaultLogLevel, string.Empty, message, null, null, TraceEventType.Verbose, null); } private void WriteInternal(object o) @@ -214,11 +215,11 @@ private void WriteInternal(object o) if (o is null || o is IConvertible) { - ProcessLogEventInfo(DefaultLogLevel, null, o?.ToString() ?? string.Empty, null, null, TraceEventType.Verbose, null); + ProcessLogEventInfo(DefaultLogLevel, string.Empty, o?.ToString() ?? string.Empty, null, null, TraceEventType.Verbose, null); } else { - ProcessLogEventInfo(DefaultLogLevel, null, "{0}", new[] { o }, null, TraceEventType.Verbose, null); + ProcessLogEventInfo(DefaultLogLevel, string.Empty, "{0}", new[] { o }, null, TraceEventType.Verbose, null); } } @@ -236,7 +237,7 @@ public override void Close() /// A message to emit. public override void Fail(string message) { - ProcessLogEventInfo(LogLevel.Error, null, message, null, null, TraceEventType.Error, null); + ProcessLogEventInfo(LogLevel.Error, string.Empty, message, null, null, TraceEventType.Error, null); } /// @@ -246,7 +247,7 @@ public override void Fail(string message) /// A detailed message to emit. public override void Fail(string message, string detailMessage) { - ProcessLogEventInfo(LogLevel.Error, null, string.Concat(message, " ", detailMessage), null, null, TraceEventType.Error, null); + ProcessLogEventInfo(LogLevel.Error, string.Empty, string.Concat(message, " ", detailMessage), null, null, TraceEventType.Error, null); } /// @@ -435,7 +436,7 @@ private static LogLevel TranslateLogLevel(TraceEventType eventType) /// The event type. /// The related activity id. /// - protected virtual void ProcessLogEventInfo(LogLevel logLevel, string loggerName, [Localizable(false)] string message, object[] arguments, int? eventId, TraceEventType? eventType, Guid? relatedActivityId) + protected virtual void ProcessLogEventInfo(LogLevel logLevel, string loggerName, [Localizable(false)] string message, object?[]? arguments, int? eventId, TraceEventType? eventType, Guid? relatedActivityId) { var globalLogLevel = (LogFactory ?? LogManager.LogFactory).GlobalThreshold; if (logLevel < globalLogLevel) @@ -443,7 +444,7 @@ protected virtual void ProcessLogEventInfo(LogLevel logLevel, string loggerName, return; // We are done } - StackTrace stackTrace = AutoLoggerName ? new StackTrace() : null; + var stackTrace = AutoLoggerName ? new StackTrace() : null; var logger = GetLogger(loggerName, stackTrace, out int userFrameIndex); logLevel = _forceLogLevel ?? logLevel; @@ -508,7 +509,7 @@ private static object ResolvedBoxedEventId(int eventId) return eventId; } - private Logger GetLogger(string loggerName, StackTrace stackTrace, out int userFrameIndex) + private Logger GetLogger(string loggerName, StackTrace? stackTrace, out int userFrameIndex) { if (string.IsNullOrEmpty(loggerName)) { diff --git a/src/NLog.Targets.Trace/StackTraceUsageUtils.cs b/src/NLog.Targets.Trace/StackTraceUsageUtils.cs index 5e494522f1..8482dd7c01 100644 --- a/src/NLog.Targets.Trace/StackTraceUsageUtils.cs +++ b/src/NLog.Targets.Trace/StackTraceUsageUtils.cs @@ -77,7 +77,7 @@ public static string LookupClassNameFromStackFrame(StackFrame stackFrame) private static string GetStackFrameMethodClassName(MethodBase method, bool includeNameSpace, bool cleanAsyncMoveNext, bool cleanAnonymousDelegates) { if (method is null) - return null; + return string.Empty; var callerClassType = method.DeclaringType; if (cleanAsyncMoveNext @@ -90,7 +90,10 @@ private static string GetStackFrameMethodClassName(MethodBase method, bool inclu callerClassType = callerClassType.DeclaringType; } - string className = includeNameSpace ? callerClassType?.FullName : callerClassType?.Name; + if (callerClassType is null) + return string.Empty; + + var className = includeNameSpace ? callerClassType.FullName : callerClassType.Name; if (cleanAnonymousDelegates && className?.IndexOf("<>", StringComparison.Ordinal) >= 0) { if (!includeNameSpace && callerClassType.DeclaringType != null && callerClassType.IsNested) @@ -114,7 +117,7 @@ private static string GetStackFrameMethodClassName(MethodBase method, bool inclu className = string.IsNullOrEmpty(typeNamespace) ? className : string.Concat(typeNamespace, ".", className); } - return className; + return className ?? string.Empty; } private static string GetNamespaceFromTypeAssembly(Type callerClassType) @@ -129,14 +132,14 @@ private static string GetNamespaceFromTypeAssembly(Type callerClassType) } } - return null; + return string.Empty; } /// /// Returns the assembly from the provided StackFrame (If not internal assembly) /// /// Valid assembly, or null if assembly was internal - private static Assembly LookupAssemblyFromMethod(MethodBase method) + private static Assembly? LookupAssemblyFromMethod(MethodBase method) { var assembly = method?.DeclaringType?.Assembly ?? method?.Module?.Assembly; diff --git a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj index b0bafaf8cf..db21fa2b56 100644 --- a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj +++ b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj @@ -34,6 +34,8 @@ true true true + enable + 9 true true diff --git a/src/NLog.Targets.WebService/WebServiceTarget.cs b/src/NLog.Targets.WebService/WebServiceTarget.cs index c9c682e6f1..ede89be93c 100644 --- a/src/NLog.Targets.WebService/WebServiceTarget.cs +++ b/src/NLog.Targets.WebService/WebServiceTarget.cs @@ -117,25 +117,25 @@ public WebServiceTarget(string name) : this() /// Gets or sets the web service URL. /// /// - public Layout Url { get; set; } = new Layout((Uri)null); + public Layout Url { get; set; } = new Layout(default(Uri)); /// /// Gets or sets the value of the User-agent HTTP header. /// /// - public Layout UserAgent { get; set; } + public Layout? UserAgent { get; set; } /// /// Gets or sets the Web service method name. Only used with Soap. /// /// - public string MethodName { get; set; } + public string MethodName { get; set; } = string.Empty; /// /// Gets or sets the Web service namespace. Only used with Soap. /// /// - public string Namespace { get; set; } + public string Namespace { get; set; } = string.Empty; /// /// Gets or sets the protocol to be used when calling web service. @@ -144,9 +144,9 @@ public WebServiceTarget(string name) : this() public WebServiceProtocol Protocol { get => _activeProtocol.Key; - set => _activeProtocol = new KeyValuePair(value, null); + set => _activeProtocol = new KeyValuePair(value, null); } - private KeyValuePair _activeProtocol = new KeyValuePair(WebServiceProtocol.Soap11, null); + private KeyValuePair _activeProtocol = new KeyValuePair(WebServiceProtocol.Soap11, null); /// /// Gets or sets the proxy configuration when calling web service @@ -158,15 +158,15 @@ public WebServiceProtocol Protocol public WebServiceProxyType ProxyType { get => _activeProxy.Key; - set => _activeProxy = new KeyValuePair(value, null); + set => _activeProxy = new KeyValuePair(value, null); } - private KeyValuePair _activeProxy = new KeyValuePair(WebServiceProxyType.DefaultWebProxy, null); + private KeyValuePair _activeProxy = new KeyValuePair(WebServiceProxyType.DefaultWebProxy, null); /// /// Gets or sets the custom proxy address, include port separated by a colon /// /// - public Layout ProxyAddress { get; set; } + public Layout? ProxyAddress { get; set; } /// /// Should we include the BOM (Byte-order-mark) for UTF? Influences the property. @@ -206,7 +206,7 @@ public WebServiceProxyType ProxyType /// (see and ). /// /// - public string XmlRoot { get; set; } + public string XmlRoot { get; set; } = string.Empty; /// /// Gets or sets the (optional) root namespace of the XML document, @@ -214,7 +214,7 @@ public WebServiceProxyType ProxyType /// (see and ). /// /// - public string XmlRootNamespace { get; set; } + public string XmlRootNamespace { get; set; } = string.Empty; /// /// Gets the array of parameters to be passed. @@ -230,16 +230,16 @@ public WebServiceProxyType ProxyType public bool PreAuthenticate { get; set; } private IJsonConverter JsonConverter => _jsonConverter ?? (_jsonConverter = ResolveService()); - private IJsonConverter _jsonConverter; + private IJsonConverter? _jsonConverter; private long _pendingWriteOperations; - private Action _pendingFlushOperation; + private Action? _pendingFlushOperation; /// /// Calls the target method. Must be implemented in concrete classes. /// /// Method call parameters. - protected override void DoInvoke(object[] parameters) + protected override void DoInvoke(object?[] parameters) { // method is not used, instead asynchronous overload will be used throw new NotSupportedException(); @@ -250,10 +250,10 @@ protected override void DoInvoke(object[] parameters) /// /// Parameters to be passed. /// The logging event. - protected override void DoInvoke(object[] parameters, AsyncLogEventInfo logEvent) + protected override void DoInvoke(object?[] parameters, AsyncLogEventInfo logEvent) { - Uri url = null; - HttpWebRequest webRequest = null; + Uri? url = null; + HttpWebRequest? webRequest = null; try { @@ -307,7 +307,7 @@ private HttpWebRequest CreateHttpWebRequest(Uri url) { IWebProxy proxy = WebRequest.GetSystemWebProxy(); proxy.Credentials = CredentialCache.DefaultCredentials; - _activeProxy = new KeyValuePair(ProxyType, proxy); + _activeProxy = new KeyValuePair(ProxyType, proxy); } webRequest.Proxy = _activeProxy.Value; break; @@ -317,7 +317,7 @@ private HttpWebRequest CreateHttpWebRequest(Uri url) if (_activeProxy.Value is null) { IWebProxy proxy = new WebProxy(RenderLogEvent(ProxyAddress, LogEventInfo.CreateNullEvent()), true); - _activeProxy = new KeyValuePair(ProxyType, proxy); + _activeProxy = new KeyValuePair(ProxyType, proxy); } webRequest.Proxy = _activeProxy.Value; } @@ -335,7 +335,7 @@ private HttpWebRequest CreateHttpWebRequest(Uri url) return webRequest; } - private void DoInvoke(object[] parameters, HttpWebRequest webRequest, AsyncContinuation continuation) + private void DoInvoke(object?[] parameters, HttpWebRequest webRequest, AsyncContinuation continuation) { Func beginGetRequest = (request, result) => request.BeginGetRequestStream(result, null); Func getRequestStream = (request, result) => request.EndGetRequestStream(result); @@ -343,10 +343,10 @@ private void DoInvoke(object[] parameters, HttpWebRequest webRequest, AsyncConti DoInvoke(parameters, continuation, webRequest, beginGetRequest, getRequestStream); } - internal void DoInvoke(object[] parameters, AsyncContinuation continuation, HttpWebRequest webRequest, Func beginGetRequest, + internal void DoInvoke(object?[] parameters, AsyncContinuation continuation, HttpWebRequest webRequest, Func beginGetRequest, Func getRequestStream) { - MemoryStream postPayload = null; + MemoryStream? postPayload = null; if (Protocol == WebServiceProtocol.HttpGet) { @@ -354,9 +354,13 @@ internal void DoInvoke(object[] parameters, AsyncContinuation continuation, Http } else { - if (_activeProtocol.Value is null) - _activeProtocol = new KeyValuePair(Protocol, _postFormatterFactories[Protocol](this)); - postPayload = _activeProtocol.Value.PrepareRequest(webRequest, parameters); + var activeProtocol = _activeProtocol.Value; + if (activeProtocol is null) + { + activeProtocol = _postFormatterFactories[Protocol](this); + _activeProtocol = new KeyValuePair(Protocol, activeProtocol); + } + postPayload = activeProtocol.PrepareRequest(webRequest, parameters); } System.Threading.Interlocked.Increment(ref _pendingWriteOperations); @@ -444,7 +448,7 @@ private void PostPayload(AsyncContinuation continuation, HttpWebRequest webReque }); } - private void DoInvokeCompleted(AsyncContinuation continuation, Exception ex) + private void DoInvokeCompleted(AsyncContinuation continuation, Exception? ex) { System.Threading.Interlocked.Decrement(ref _pendingWriteOperations); _pendingFlushOperation?.Invoke(); @@ -471,7 +475,7 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation) else { var pendingFlushOperation = _pendingFlushOperation; - Action newPendingFlushOperation = null; + Action? newPendingFlushOperation = null; newPendingFlushOperation = () => { pendingFlushOperation?.Invoke(); @@ -497,7 +501,7 @@ protected override void CloseTarget() /// /// Builds the URL to use when calling the web service for a message, depending on the WebServiceProtocol. /// - private Uri BuildWebServiceUrl(LogEventInfo logEvent, object[] parameterValues) + private Uri? BuildWebServiceUrl(LogEventInfo logEvent, object?[] parameterValues) { var uri = RenderLogEvent(Url, logEvent); if (Protocol != WebServiceProtocol.HttpGet) @@ -525,7 +529,7 @@ private Uri BuildWebServiceUrl(LogEventInfo logEvent, object[] parameterValues) return builder.Uri; } - private void BuildWebServiceQueryParameters(object[] parameterValues, StringBuilder sb) + private void BuildWebServiceQueryParameters(object?[] parameterValues, StringBuilder sb) { string separator = string.Empty; for (int i = 0; i < Parameters.Count; i++) @@ -538,7 +542,7 @@ private void BuildWebServiceQueryParameters(object[] parameterValues, StringBuil } } - private void AppendParameterAsString(object parameterValue, StringBuilder sb) + private void AppendParameterAsString(object? parameterValue, StringBuilder sb) { var parameterObject = parameterValue as IConvertible; if (parameterObject != null) @@ -579,10 +583,10 @@ private static void WriteStreamAndFixPreamble(MemoryStream postPayload, Stream o var hasBomInEncoding = encoding.GetPreamble().Length == preambleSize; //BOM already in Encoding. - nothingToDo = writeUtf8BOM.Value && hasBomInEncoding; + nothingToDo = writeUtf8BOM == true && hasBomInEncoding; //Bom already not in Encoding - nothingToDo = nothingToDo || !writeUtf8BOM.Value && !hasBomInEncoding; + nothingToDo = nothingToDo || (writeUtf8BOM != true && !hasBomInEncoding); } var byteArray = postPayload.GetBuffer(); @@ -604,7 +608,7 @@ protected HttpPostFormatterBase(WebServiceTarget target) } protected string ContentType => _contentType ?? (_contentType = GetContentType(Target)); - private string _contentType; + private string? _contentType; protected WebServiceTarget Target { get; } @@ -613,7 +617,7 @@ protected virtual string GetContentType(WebServiceTarget target) return string.Concat("charset=", target.Encoding.WebName); } - public MemoryStream PrepareRequest(HttpWebRequest request, object[] parameterValues) + public MemoryStream PrepareRequest(HttpWebRequest request, object?[] parameterValues) { InitRequest(request); @@ -628,7 +632,7 @@ protected virtual void InitRequest(HttpWebRequest request) request.ContentType = ContentType; } - protected abstract void WriteContent(MemoryStream ms, object[] parameterValues); + protected abstract void WriteContent(MemoryStream ms, object?[] parameterValues); } private sealed class HttpPostFormEncodedFormatter : HttpPostTextFormatterBase @@ -642,7 +646,7 @@ protected override string GetContentType(WebServiceTarget target) return string.Concat("application/x-www-form-urlencoded", "; ", base.GetContentType(target)); } - protected override void WriteStringContent(StringBuilder builder, object[] parameterValues) + protected override void WriteStringContent(StringBuilder builder, object?[] parameterValues) { Target.BuildWebServiceQueryParameters(parameterValues, builder); } @@ -663,7 +667,7 @@ protected override string GetContentType(WebServiceTarget target) return string.Concat("application/json", "; ", base.GetContentType(target)); } - protected override void WriteStringContent(StringBuilder builder, object[] parameterValues) + protected override void WriteStringContent(StringBuilder builder, object?[] parameterValues) { if (Target.Parameters.Count == 1 && string.IsNullOrEmpty(Target.Parameters[0].Name) && parameterValues[0] is string s) { @@ -756,7 +760,7 @@ protected HttpPostSoapFormatterBase(WebServiceTarget target) : base(target) protected abstract string SoapEnvelopeNamespace { get; } protected abstract string SoapName { get; } - protected override void WriteContent(MemoryStream ms, object[] parameterValues) + protected override void WriteContent(MemoryStream ms, object?[] parameterValues) { using (var xtw = XmlWriter.Create(ms, _xmlWriterSettings)) { @@ -792,7 +796,7 @@ protected HttpPostTextFormatterBase(WebServiceTarget target) : base(target) _encodingPreamble = target.Encoding.GetPreamble(); } - protected override void WriteContent(MemoryStream ms, object[] parameterValues) + protected override void WriteContent(MemoryStream ms, object?[] parameterValues) { lock (_reusableStringBuilder) { @@ -827,7 +831,7 @@ protected override void WriteContent(MemoryStream ms, object[] parameterValues) } } - protected abstract void WriteStringContent(StringBuilder builder, object[] parameterValues); + protected abstract void WriteStringContent(StringBuilder builder, object?[] parameterValues); } private sealed class HttpPostXmlDocumentFormatter : HttpPostXmlFormatterBase @@ -847,7 +851,7 @@ protected override string GetContentType(WebServiceTarget target) return string.Concat("application/xml", "; ", base.GetContentType(target)); } - protected override void WriteContent(MemoryStream ms, object[] parameterValues) + protected override void WriteContent(MemoryStream ms, object?[] parameterValues) { using (var xtw = XmlWriter.Create(ms, _xmlWriterSettings)) { @@ -867,7 +871,7 @@ protected HttpPostXmlFormatterBase(WebServiceTarget target) : base(target) { } - protected void WriteAllParametersToCurrenElement(XmlWriter currentXmlWriter, object[] parameterValues) + protected void WriteAllParametersToCurrenElement(XmlWriter currentXmlWriter, object?[] parameterValues) { for (int i = 0; i < Target.Parameters.Count; i++) { diff --git a/src/NLog/Internal/StackTraceUsageUtils.cs b/src/NLog/Internal/StackTraceUsageUtils.cs index 0783ff1c73..dfa44911fa 100644 --- a/src/NLog/Internal/StackTraceUsageUtils.cs +++ b/src/NLog/Internal/StackTraceUsageUtils.cs @@ -126,10 +126,13 @@ public static string GetStackFrameMethodClassName(MethodBase method, bool includ callerClassType = callerClassType.DeclaringType; } - var className = (includeNameSpace ? callerClassType?.FullName : callerClassType?.Name) ?? string.Empty; - if (cleanAnonymousDelegates && className.IndexOf("<>", StringComparison.Ordinal) >= 0) + if (callerClassType is null) + return string.Empty; + + var className = includeNameSpace ? callerClassType.FullName : callerClassType.Name; + if (cleanAnonymousDelegates && className?.IndexOf("<>", StringComparison.Ordinal) >= 0) { - if (!includeNameSpace && callerClassType != null && callerClassType.DeclaringType != null && callerClassType.IsNested) + if (!includeNameSpace && callerClassType.DeclaringType != null && callerClassType.IsNested) { className = callerClassType.DeclaringType.Name; } @@ -144,13 +147,13 @@ public static string GetStackFrameMethodClassName(MethodBase method, bool includ } } - if (includeNameSpace && className.IndexOf('.') == -1) + if (includeNameSpace && className?.IndexOf('.') == -1) { var typeNamespace = GetNamespaceFromTypeAssembly(callerClassType); className = string.IsNullOrEmpty(typeNamespace) ? className : string.Concat(typeNamespace, ".", className); } - return className; + return className ?? string.Empty; } private static string GetNamespaceFromTypeAssembly(Type? callerClassType) From dd993da94bd5af0834a99e567ef0dafabf43b996 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 23 May 2025 21:07:15 +0200 Subject: [PATCH 124/224] NLog.Targets.Network with nullable references (#5830) --- .../Layouts/GelfLayout.cs | 53 +++++++++---------- .../Layouts/Log4JXmlEventLayout.cs | 16 +++--- .../Layouts/SyslogLayout.cs | 35 ++++++------ .../NLog.Targets.Network.csproj | 2 + .../NetworkSenders/HttpNetworkSender.cs | 2 +- .../NetworkSenders/INetworkSenderFactory.cs | 2 +- .../NetworkSenders/NetworkSender.cs | 2 +- .../NetworkSenders/NetworkSenderFactory.cs | 2 +- .../NetworkSenders/QueuedNetworkSender.cs | 16 +++--- .../NetworkSenders/SslSocketProxy.cs | 12 ++--- .../NetworkSenders/TcpNetworkSender.cs | 24 +++++---- .../NetworkSenders/UdpNetworkSender.cs | 22 ++++---- .../Targets/ChainsawTarget.cs | 10 +--- .../Targets/GelfTarget.cs | 10 +--- .../Targets/NetworkTarget.cs | 41 ++++++++------ .../Targets/SyslogTarget.cs | 14 ++--- 16 files changed, 128 insertions(+), 135 deletions(-) diff --git a/src/NLog.Targets.Network/Layouts/GelfLayout.cs b/src/NLog.Targets.Network/Layouts/GelfLayout.cs index e6a6896f44..c808ca8c9c 100644 --- a/src/NLog.Targets.Network/Layouts/GelfLayout.cs +++ b/src/NLog.Targets.Network/Layouts/GelfLayout.cs @@ -29,12 +29,8 @@ namespace NLog.Layouts [AppDomainFixedOutput] public class GelfLayout : CompoundLayout { - private IJsonConverter JsonConverter - { - get => _jsonConverter ?? (_jsonConverter = ResolveService()); - set => _jsonConverter = value; - } - private IJsonConverter _jsonConverter; + private IJsonConverter JsonConverter => _jsonConverter ?? (_jsonConverter = ResolveService()); + private IJsonConverter? _jsonConverter; /// /// Graylog Message Host-field @@ -49,7 +45,7 @@ public Layout GelfHostName } } private Layout _gelfHostName; - private string _gelfHostNameString; + private string? _gelfHostNameString; /// /// Graylog Message Short-Message-field @@ -78,8 +74,8 @@ public Layout GelfFacility _gelfFacilityString = null; } } - private Layout _gelfFacility; - private string _gelfFacilityString; + private Layout _gelfFacility = Layout.Empty; + private string? _gelfFacilityString; /// /// Gets or sets GELF additional fields @@ -115,7 +111,7 @@ public Layout GelfFacility /// /// Disables to capture ScopeContext-properties from active thread context /// - public LayoutRenderer DisableThreadAgnostic => IncludeScopeProperties ? _disableThreadAgnostic : (IncludeEventProperties ? _enableThreadAgnosticImmutable : null); + public LayoutRenderer? DisableThreadAgnostic => IncludeScopeProperties ? _disableThreadAgnostic : (IncludeEventProperties ? _enableThreadAgnosticImmutable : null); private static readonly LayoutRenderer _disableThreadAgnostic = new FuncLayoutRenderer(string.Empty, (evt, cfg) => string.Empty); private static readonly LayoutRenderer _enableThreadAgnosticImmutable = new ExceptionDataLayoutRenderer() { Item = " " }; @@ -124,7 +120,7 @@ public Layout GelfFacility /// public GelfLayout() { - GelfHostName = "${hostname}"; + _gelfHostName = GelfHostName = "${hostname}"; GelfShortMessage = GelfFullMessage = "${message}"; } @@ -168,12 +164,12 @@ protected override void InitializeLayout() protected override void CloseLayout() { base.CloseLayout(); - JsonConverter = null; + _jsonConverter = null; } - private string ResolveJsonFixedString(Layout layout) + private string? ResolveJsonFixedString(Layout layout) { - if (layout is SimpleLayout simpleLayout && simpleLayout.IsFixedText) + if (layout is SimpleLayout simpleLayout && simpleLayout.IsFixedText && !ReferenceEquals(layout, Layout.Empty)) { var stringBuilder = new StringBuilder(); JsonConverter.SerializeObject(simpleLayout.FixedText, stringBuilder); @@ -218,7 +214,7 @@ protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuil var fullMessage = GelfFullMessage?.Render(logEvent) ?? string.Empty; if (string.IsNullOrEmpty(fullMessage) && shortMessage.Length > ShortMessageMaxLength) fullMessage = shortMessage; - else if (fullMessage?.Length < ShortMessageMaxLength && string.Equals(fullMessage, shortMessage, StringComparison.Ordinal)) + else if (fullMessage.Length < ShortMessageMaxLength && string.Equals(fullMessage, shortMessage, StringComparison.Ordinal)) fullMessage = string.Empty; if (!string.IsNullOrEmpty(fullMessage)) { @@ -240,7 +236,7 @@ protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuil target.Append(_completeJsonPropertyName); target.Append((int)gelfSeverity); - if (_gelfFacility != null) + if (_gelfFacility != null && !ReferenceEquals(_gelfFacility, Layout.Empty)) { target.Append(_beginJsonPropertyName); target.Append("facility"); @@ -307,14 +303,14 @@ protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuil if (eventProperties?.Count > 0) { - var filterEventProperties = ExcludeProperties?.Count > 0; + var excludeProperties = ExcludeProperties?.Count > 0 ? ExcludeProperties : null; foreach (var eventProperty in eventProperties) { if (ExcludeEmptyProperties && (eventProperty.Value is null || ReferenceEquals(eventProperty.Value, string.Empty))) continue; var eventPropertyName = eventProperty.Key?.ToString() ?? string.Empty; - if (filterEventProperties && ExcludeProperties.Contains(eventPropertyName)) + if (excludeProperties?.Contains(eventPropertyName) == true) continue; BeginJsonProperty(target, eventPropertyName, eventProperty.Value); @@ -324,7 +320,7 @@ protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuil target.Append(_completeJsonMessage); } - private static bool ExcludeScopeProperty(string propertyName, IDictionary eventProperties, ISet excludeProperties) + private static bool ExcludeScopeProperty(string propertyName, IDictionary? eventProperties, ISet? excludeProperties) { if (excludeProperties?.Contains(propertyName) == true) return true; @@ -333,7 +329,7 @@ private static bool ExcludeScopeProperty(string propertyName, IDictionary eventProperties, IList> scopeProperties) + private static bool ExcludeGelfField(string gelfFieldName, IDictionary? eventProperties, IList>? scopeProperties) { if (string.IsNullOrEmpty(gelfFieldName)) return true; @@ -359,7 +355,7 @@ private static bool ExcludeGelfField(string gelfFieldName, IDictionary ResolveEventProperties(LogEventInfo logEvent) + private IDictionary? ResolveEventProperties(LogEventInfo logEvent) { if (!logEvent.HasProperties) return null; @@ -387,10 +383,10 @@ private IDictionary ResolveEventProperties(LogEventInfo logEvent return eventProperties; } - private IList> ResolveScopePropertyList() + private IList>? ResolveScopePropertyList() { var scopeProperties = ScopeContext.GetAllProperties(); - if (scopeProperties is IList> scopePropertyList) + if (scopeProperties is IList> scopePropertyList) { if (scopePropertyList.Count == 0) return null; @@ -417,14 +413,13 @@ private IList> ResolveScopePropertyList() return scopePropertyList; } - if (IncludeProperties?.Count > 0) - scopePropertyList = scopeProperties?.Where(p => IncludeProperties.Contains(p.Key)).ToList(); - else - scopePropertyList = scopeProperties?.ToList(); - return scopePropertyList?.Count > 0 ? scopePropertyList : null; + var scopePropertyCollection = IncludeProperties?.Count > 0 + ? scopeProperties?.Where(p => IncludeProperties.Contains(p.Key)).ToList() + : scopeProperties?.ToList(); + return scopePropertyCollection?.Count > 0 ? scopePropertyCollection : null; } - private void BeginJsonProperty(StringBuilder sb, string propName, object propertyValue) + private void BeginJsonProperty(StringBuilder sb, string propName, object? propertyValue) { if (ExcludeEmptyProperties && (propertyValue is null || ReferenceEquals(propertyValue, string.Empty))) return; diff --git a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs index 671c74fce1..08bc9c2741 100644 --- a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs @@ -103,7 +103,7 @@ public class Log4JXmlEventLayout : CompoundLayout /// Gets or sets the stack separator for log4j:NDC in output from nested context. /// /// - public string ScopeNestedSeparator { get; set; } + public string ScopeNestedSeparator { get; set; } = string.Empty; /// /// Gets or sets the stack separator for log4j:NDC in output from nested context. @@ -186,7 +186,7 @@ public class Log4JXmlEventLayout : CompoundLayout /// /// public Layout AppInfo { get => _log4jAppName.Attributes[1].Layout; set => _log4jAppName.Attributes[1].Layout = value; } - private readonly XmlElement _log4jAppName = new XmlElement("log4j:data", null) + private readonly XmlElement _log4jAppName = new XmlElement("log4j:data", Layout.Empty) { Attributes = { @@ -195,7 +195,7 @@ public class Log4JXmlEventLayout : CompoundLayout } }; - private readonly XmlElement _log4jMachineName = new XmlElement("log4j:data", null) + private readonly XmlElement _log4jMachineName = new XmlElement("log4j:data", Layout.Empty) { Attributes = { @@ -239,7 +239,7 @@ protected override void InitializeLayout() if (IncludeCallSite || IncludeSourceInfo) { - var locationInfo = new XmlElement("log4j:locationInfo", null) + var locationInfo = new XmlElement("log4j:locationInfo", Layout.Empty) { Attributes = { @@ -261,14 +261,14 @@ protected override void InitializeLayout() if (IncludeNLogData) { _innerXml.Elements.Add(new XmlElement("nlog:eventSequenceNumber", "${sequenceid}")); - _innerXml.Elements.Add(new XmlElement("nlog:locationInfo", null) + _innerXml.Elements.Add(new XmlElement("nlog:locationInfo", Layout.Empty) { Attributes = { new XmlAttribute("assembly", "${callsite:fileName=true:className=false:methodName=false}") } }); - _innerXml.Elements.Add(new XmlElement("nlog:properties", null) + _innerXml.Elements.Add(new XmlElement("nlog:properties", Layout.Empty) { PropertiesElementName = "nlog:data", PropertiesElementKeyAttribute = "name", @@ -285,7 +285,7 @@ protected override void InitializeLayout() _innerXml.Elements.Add(new XmlElement("log4j:NDC", scopeNested)); } - var dataProperties = new XmlElement("log4j:properties", null) + var dataProperties = new XmlElement("log4j:properties", Layout.Empty) { PropertiesElementName = "log4j:data", PropertiesElementKeyAttribute = "name", @@ -296,7 +296,7 @@ protected override void InitializeLayout() foreach (var parameter in Parameters) { - var propertyElement = new XmlElement("log4j:data", null) + var propertyElement = new XmlElement("log4j:data", Layout.Empty) { IncludeEmptyValue = parameter.IncludeEmptyValue, Attributes = diff --git a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs index bfe20263b3..fae7922124 100644 --- a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs +++ b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs @@ -23,12 +23,8 @@ public class SyslogLayout : CompoundLayout private const int AppNameMaxLength = 48; private const int ProcessIdMaxLength = 128; - private IValueFormatter ValueFormatter - { - get => _valueFormatter ?? (_valueFormatter = ResolveService()); - set => _valueFormatter = value; - } - private IValueFormatter _valueFormatter; + private IValueFormatter ValueFormatter => _valueFormatter ?? (_valueFormatter = ResolveService()); + private IValueFormatter? _valueFormatter; /// /// Gets or sets a value indicating whether to use RFC 3164 for Syslog Format @@ -59,11 +55,11 @@ public Layout SyslogHostName set { _hostName = value; - _hostNameString = _hostName is SimpleLayout simpleLayout && simpleLayout.IsFixedText ? EscapePropertyName(simpleLayout.FixedText, HostNameMaxLength) : null; + _hostNameString = _hostName is SimpleLayout simpleLayout && simpleLayout.FixedText is not null ? EscapePropertyName(simpleLayout.FixedText, HostNameMaxLength) : null; } } private Layout _hostName; - private string _hostNameString; + private string? _hostNameString; /// /// Name of the device / application / process sending the Syslog-message (Optional) @@ -78,11 +74,11 @@ public Layout SyslogAppName set { _appName = value; - _appNameString = _appName is SimpleLayout simpleLayout && simpleLayout.IsFixedText ? EscapePropertyName(simpleLayout.FixedText, AppNameMaxLength) : null; + _appNameString = _appName is SimpleLayout simpleLayout && simpleLayout.FixedText is not null ? EscapePropertyName(simpleLayout.FixedText, AppNameMaxLength) : null; } } private Layout _appName; - private string _appNameString; + private string? _appNameString; /// /// Process Id or Process Name or Logger Name (Optional) @@ -96,11 +92,11 @@ public Layout SyslogProcessId set { _processId = value; - _processIdString = _processId is SimpleLayout simpleLayout && simpleLayout.IsFixedText ? EscapePropertyName(simpleLayout.FixedText, ProcessIdMaxLength) : null; + _processIdString = _processId is SimpleLayout simpleLayout && simpleLayout.FixedText is not null ? EscapePropertyName(simpleLayout.FixedText, ProcessIdMaxLength) : null; } } private Layout _processId; - private string _processIdString; + private string? _processIdString; /// /// The type of message that should be the same for events with the same semantics. Ex ${event-properties:EventId} (Optional) @@ -108,7 +104,7 @@ public Layout SyslogProcessId /// /// RFC 5424 - NILVALUE or 1 to 32 PRINTUSASCII /// - public Layout SyslogMessageId { get; set; } + public Layout? SyslogMessageId { get; set; } /// /// Mesage Payload @@ -146,7 +142,7 @@ public Layout SyslogProcessId /// /// Disables to capture volatile LogEvent-properties from active thread context /// - public LayoutRenderer DisableThreadAgnostic => IncludeEventProperties ? _enableThreadAgnosticImmutable : null; + public LayoutRenderer? DisableThreadAgnostic => IncludeEventProperties ? _enableThreadAgnosticImmutable : null; private static readonly LayoutRenderer _enableThreadAgnosticImmutable = new ExceptionDataLayoutRenderer() { Item = " " }; /// @@ -154,9 +150,9 @@ public Layout SyslogProcessId /// public SyslogLayout() { - SyslogHostName = "${hostname}"; - SyslogAppName = "${processname}"; - SyslogProcessId = "${processid}"; + _hostName = SyslogHostName = "${hostname}"; + _appName = SyslogAppName = "${processname}"; + _processId = SyslogProcessId = "${processid}"; } /// @@ -192,7 +188,8 @@ protected override void InitializeLayout() /// protected override void CloseLayout() { - ValueFormatter = null; + _valueFormatter = null; + base.CloseLayout(); } /// @@ -354,7 +351,7 @@ private static string AppendPropertyName(StringBuilder target, string structured return structuredDataId; } - private void AppendPropertyValue(StringBuilder target, object propertyValue) + private void AppendPropertyValue(StringBuilder target, object? propertyValue) { target.Append('"'); diff --git a/src/NLog.Targets.Network/NLog.Targets.Network.csproj b/src/NLog.Targets.Network/NLog.Targets.Network.csproj index 674d263f5b..c4c90730a2 100644 --- a/src/NLog.Targets.Network/NLog.Targets.Network.csproj +++ b/src/NLog.Targets.Network/NLog.Targets.Network.csproj @@ -34,6 +34,8 @@ true true true + enable + 9 true true diff --git a/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs index 382832a0d8..8a50effd68 100644 --- a/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/HttpNetworkSender.cs @@ -49,7 +49,7 @@ internal sealed class HttpNetworkSender : QueuedNetworkSender internal TimeSpan SendTimeout { get; set; } - internal X509Certificate2Collection SslCertificateOverride { get; set; } + internal X509Certificate2Collection? SslCertificateOverride { get; set; } /// /// Initializes a new instance of the class. diff --git a/src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs b/src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs index 3edbc6c782..78486d55c9 100644 --- a/src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs +++ b/src/NLog.Targets.Network/NetworkSenders/INetworkSenderFactory.cs @@ -51,6 +51,6 @@ internal interface INetworkSenderFactory /// /// A newly created network sender. /// - QueuedNetworkSender Create(string url, X509Certificate2Collection sslCertificateOverride, NetworkTarget networkTarget); + QueuedNetworkSender Create(string url, X509Certificate2Collection? sslCertificateOverride, NetworkTarget networkTarget); } } diff --git a/src/NLog.Targets.Network/NetworkSenders/NetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/NetworkSender.cs index 0d21620c61..eb939d39ae 100644 --- a/src/NLog.Targets.Network/NetworkSenders/NetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/NetworkSender.cs @@ -193,7 +193,7 @@ protected virtual IPAddress ResolveIpAddress(Uri uri, AddressFamily addressFamil } } - public virtual ISocket CheckSocket() + public virtual ISocket? CheckSocket() { return null; } diff --git a/src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs b/src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs index 6a7699cdb4..bfdb4e2e47 100644 --- a/src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs +++ b/src/NLog.Targets.Network/NetworkSenders/NetworkSenderFactory.cs @@ -46,7 +46,7 @@ internal sealed class NetworkSenderFactory : INetworkSenderFactory public static readonly INetworkSenderFactory Default = new NetworkSenderFactory(); /// - public QueuedNetworkSender Create(string url, X509Certificate2Collection sslCertificateOverride, NetworkTarget networkTarget) + public QueuedNetworkSender Create(string url, X509Certificate2Collection? sslCertificateOverride, NetworkTarget networkTarget) { if (url.StartsWith("tcp://", StringComparison.OrdinalIgnoreCase)) { diff --git a/src/NLog.Targets.Network/NetworkSenders/QueuedNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/QueuedNetworkSender.cs index c6f9c1866d..cfba1a5c41 100644 --- a/src/NLog.Targets.Network/NetworkSenders/QueuedNetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/QueuedNetworkSender.cs @@ -61,10 +61,10 @@ public NetworkRequestArgs(byte[] buffer, int offset, int length, AsyncContinuati private readonly Queue _pendingRequests = new Queue(); private readonly Queue _activeRequests = new Queue(); - private Exception _pendingError; + private Exception? _pendingError; private bool _asyncOperationInProgress; - private AsyncContinuation _closeContinuation; - private AsyncContinuation _flushContinuation; + private AsyncContinuation? _closeContinuation; + private AsyncContinuation? _flushContinuation; /// /// Initializes a new instance of the class. @@ -79,12 +79,12 @@ protected QueuedNetworkSender(string url) public NetworkTargetQueueOverflowAction OnQueueOverflow { get; set; } - public event EventHandler LogEventDropped; + public event EventHandler? LogEventDropped; protected override void DoSend(byte[] bytes, int offset, int length, AsyncContinuation asyncContinuation) { NetworkRequestArgs? eventArgs = new NetworkRequestArgs(bytes, offset, length, asyncContinuation); - AsyncContinuation failedContinuation = null; + AsyncContinuation? failedContinuation = null; lock (_pendingRequests) { @@ -195,7 +195,7 @@ protected void BeginInitialize() } } - protected NetworkRequestArgs? EndRequest(AsyncContinuation asyncContinuation, Exception pendingException) + protected NetworkRequestArgs? EndRequest(AsyncContinuation? asyncContinuation, Exception? pendingException) { if (pendingException != null) { @@ -231,8 +231,8 @@ protected void BeginInitialize() private NetworkRequestArgs? DequeueNextItem() { - AsyncContinuation closeContinuation; - AsyncContinuation flushContinuation; + AsyncContinuation? closeContinuation; + AsyncContinuation? flushContinuation; lock (_activeRequests) { diff --git a/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs b/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs index c314efa228..5839a61fbd 100644 --- a/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs +++ b/src/NLog.Targets.Network/NetworkSenders/SslSocketProxy.cs @@ -45,10 +45,10 @@ internal sealed class SslSocketProxy : ISocket, IDisposable private readonly SocketProxy _socketProxy; private readonly string _host; private readonly SslProtocols _sslProtocol; - private readonly X509Certificate2Collection _sslCertificateOverride; - SslStream _sslStream; + private readonly X509Certificate2Collection? _sslCertificateOverride; + SslStream? _sslStream; - public SslSocketProxy(string host, SslProtocols sslProtocol, SocketProxy socketProxy, X509Certificate2Collection sslCertificateOverride) + public SslSocketProxy(string host, SslProtocols sslProtocol, SocketProxy socketProxy, X509Certificate2Collection? sslCertificateOverride) { _socketProxy = socketProxy; _host = host; @@ -84,7 +84,7 @@ private void SocketProxySendCompleted(IAsyncResult asyncResult) var proxyArgs = asyncResult.AsyncState as TcpNetworkSender.MySocketAsyncEventArgs; try { - _sslStream.EndWrite(asyncResult); + _sslStream?.EndWrite(asyncResult); } catch (SocketException ex) { @@ -164,7 +164,7 @@ private static SocketError GetSocketError(Exception ex) return socketError; } - private static void AuthenticateAsClient(SslStream sslStream, string targetHost, SslProtocols enabledSslProtocols, X509Certificate2Collection sslCertificateOverride) + private static void AuthenticateAsClient(SslStream sslStream, string targetHost, SslProtocols enabledSslProtocols, X509Certificate2Collection? sslCertificateOverride) { #pragma warning disable CS0618 // Type or member is obsolete if (enabledSslProtocols != SslProtocols.Default || sslCertificateOverride != null) @@ -192,7 +192,7 @@ private bool UserCertificateValidationCallback(object sender, object certificate public bool SendAsync(SocketAsyncEventArgs args) { - _sslStream.BeginWrite(args.Buffer, args.Offset, args.Count, _sendCompleted, args); + _sslStream?.BeginWrite(args.Buffer, args.Offset, args.Count, _sendCompleted, args); return true; } diff --git a/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs index dfbca0a53a..8ccd23713b 100644 --- a/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/TcpNetworkSender.cs @@ -46,9 +46,9 @@ namespace NLog.Internal.NetworkSenders internal class TcpNetworkSender : QueuedNetworkSender { private static bool? EnableKeepAliveSuccessful; - private ISocket _socket; + private ISocket? _socket; private readonly EventHandler _socketOperationCompletedAsync; - System.Threading.WaitCallback _asyncBeginRequest; + System.Threading.WaitCallback? _asyncBeginRequest; /// /// Initializes a new instance of the class. @@ -66,7 +66,7 @@ public TcpNetworkSender(string url, AddressFamily addressFamily) internal System.Security.Authentication.SslProtocols SslProtocols { get; set; } - internal X509Certificate2Collection SslCertificateOverride { get; set; } + internal X509Certificate2Collection? SslCertificateOverride { get; set; } internal TimeSpan KeepAliveTime { get; set; } @@ -217,7 +217,7 @@ protected override void DoClose(AsyncContinuation continuation) base.DoClose(ex => CloseSocket(continuation, ex)); } - private void CloseSocket(AsyncContinuation continuation, Exception pendingException) + private void CloseSocket(AsyncContinuation continuation, Exception? pendingException) { try { @@ -263,7 +263,7 @@ private void BeginSocketRequest(SocketAsyncEventArgs args) { try { - asyncOperation = _socket.SendAsync(args); + asyncOperation = _socket?.SendAsync(args) ?? false; } catch (SocketException ex) { @@ -279,12 +279,16 @@ private void BeginSocketRequest(SocketAsyncEventArgs args) args.SocketError = SocketError.OperationAborted; } - args = asyncOperation ? null : SocketOperationCompleted(args); + var nextArgs = asyncOperation ? null : SocketOperationCompleted(args); + if (nextArgs is null) + break; + + args = nextArgs; } while (args != null); } - private void SetSocketNetworkRequest(SocketAsyncEventArgs socketEventArgs, NetworkRequestArgs networkRequest) + private static void SetSocketNetworkRequest(SocketAsyncEventArgs socketEventArgs, NetworkRequestArgs networkRequest) { socketEventArgs.SetBuffer(networkRequest.RequestBuffer, networkRequest.RequestBufferOffset, networkRequest.RequestBufferLength); socketEventArgs.UserToken = networkRequest.AsyncContinuation; @@ -299,9 +303,9 @@ private void SocketOperationCompletedAsync(object sender, SocketAsyncEventArgs a } } - private SocketAsyncEventArgs SocketOperationCompleted(SocketAsyncEventArgs args) + private SocketAsyncEventArgs? SocketOperationCompleted(SocketAsyncEventArgs args) { - Exception socketException = null; + Exception? socketException = null; if (args.SocketError != SocketError.Success) { socketException = new IOException($"Error: {args.SocketError.ToString()}, Address: {Address}"); @@ -322,7 +326,7 @@ private SocketAsyncEventArgs SocketOperationCompleted(SocketAsyncEventArgs args) } } - public override ISocket CheckSocket() + public override ISocket? CheckSocket() { if (_socket is null) { diff --git a/src/NLog.Targets.Network/NetworkSenders/UdpNetworkSender.cs b/src/NLog.Targets.Network/NetworkSenders/UdpNetworkSender.cs index 97c2f26559..4822afd1b2 100644 --- a/src/NLog.Targets.Network/NetworkSenders/UdpNetworkSender.cs +++ b/src/NLog.Targets.Network/NetworkSenders/UdpNetworkSender.cs @@ -44,10 +44,10 @@ namespace NLog.Internal.NetworkSenders /// internal class UdpNetworkSender : QueuedNetworkSender { - private ISocket _socket; - private EndPoint _endpoint; + private ISocket? _socket; + private EndPoint? _endpoint; private readonly EventHandler _socketOperationCompletedAsync; - System.Threading.WaitCallback _asyncBeginRequest; + System.Threading.WaitCallback? _asyncBeginRequest; /// /// Initializes a new instance of the class. @@ -93,7 +93,7 @@ protected override void DoClose(AsyncContinuation continuation) base.DoClose(ex => CloseSocket(continuation, ex)); } - private void CloseSocket(AsyncContinuation continuation, Exception pendingException) + private void CloseSocket(AsyncContinuation continuation, Exception? pendingException) { try { @@ -140,7 +140,7 @@ private void BeginSocketRequest(SocketAsyncEventArgs args) { try { - asyncOperation = _socket.SendToAsync(args); + asyncOperation = _socket?.SendToAsync(args) ?? false; } catch (SocketException ex) { @@ -156,7 +156,11 @@ private void BeginSocketRequest(SocketAsyncEventArgs args) args.SocketError = SocketError.OperationAborted; } - args = asyncOperation ? null : SocketOperationCompleted(args); + var nextArgs = asyncOperation ? null : SocketOperationCompleted(args); + if (nextArgs is null) + break; + + args = nextArgs; } while (args != null); } @@ -177,9 +181,9 @@ private void SocketOperationCompletedAsync(object sender, SocketAsyncEventArgs a } } - private SocketAsyncEventArgs SocketOperationCompleted(SocketAsyncEventArgs args) + private SocketAsyncEventArgs? SocketOperationCompleted(SocketAsyncEventArgs args) { - Exception socketException = null; + Exception? socketException = null; if (args.SocketError != SocketError.Success) { socketException = new IOException($"Error: {args.SocketError.ToString()}, Address: {Address}"); @@ -207,7 +211,7 @@ private SocketAsyncEventArgs SocketOperationCompleted(SocketAsyncEventArgs args) } } - public override ISocket CheckSocket() + public override ISocket? CheckSocket() { if (_socket is null) { diff --git a/src/NLog.Targets.Network/Targets/ChainsawTarget.cs b/src/NLog.Targets.Network/Targets/ChainsawTarget.cs index fa62ae2cbd..76c8012014 100644 --- a/src/NLog.Targets.Network/Targets/ChainsawTarget.cs +++ b/src/NLog.Targets.Network/Targets/ChainsawTarget.cs @@ -254,14 +254,8 @@ public string NdcItemSeparator /// public override Layout Layout { - get - { - return _log4JLayout; - } - set - { - // Fixed Log4JXmlEventLayout - } + get => _log4JLayout; + set { /* Fixed Log4JXmlEventLayout */ } // NOSONAR } } } diff --git a/src/NLog.Targets.Network/Targets/GelfTarget.cs b/src/NLog.Targets.Network/Targets/GelfTarget.cs index 04b28c392c..21c97bd83b 100644 --- a/src/NLog.Targets.Network/Targets/GelfTarget.cs +++ b/src/NLog.Targets.Network/Targets/GelfTarget.cs @@ -79,14 +79,8 @@ public class GelfTarget : NetworkTarget /// public override Layout Layout { - get - { - return _gelfLayout; - } - set - { - // Fixed GelfLayout - } + get => _gelfLayout; + set { /* Fixed GelfLayout */ } // NOSONAR } /// diff --git a/src/NLog.Targets.Network/Targets/NetworkTarget.cs b/src/NLog.Targets.Network/Targets/NetworkTarget.cs index 59a429d604..cd0543b77f 100644 --- a/src/NLog.Targets.Network/Targets/NetworkTarget.cs +++ b/src/NLog.Targets.Network/Targets/NetworkTarget.cs @@ -82,7 +82,7 @@ public class NetworkTarget : TargetWithLayout private readonly StringBuilder _reusableStringBuilder = new StringBuilder(); private readonly object _certificateCacheLock = new object(); - private Dictionary _certificateCache; + private Dictionary? _certificateCache; /// /// Initializes a new instance of the class. @@ -125,7 +125,7 @@ public NetworkTarget(string name) : this() /// For SOAP-based webservice support over HTTP use WebService target. /// /// - public Layout Address { get; set; } + public Layout Address { get; set; } = Layout.Empty; /// /// Gets or sets a value indicating whether to keep connection open whenever possible. @@ -198,7 +198,7 @@ public LineEndingMode LineEnding /// - When connection-list is full and set to
/// - When message is too big and set to
/// - public event EventHandler LogEventDropped; + public event EventHandler? LogEventDropped; /// /// Gets or sets the size of the connection cache (number of connections which are kept alive). Requires = true @@ -236,13 +236,13 @@ public LineEndingMode LineEnding /// Gets or sets the file path to custom SSL certificate for TCP Socket SSL connections /// /// - public Layout SslCertificateFile { get; set; } + public Layout? SslCertificateFile { get; set; } /// /// Gets or sets the password for the custom SSL certificate specified by /// /// - public Layout SslCertificatePassword { get; set; } + public Layout? SslCertificatePassword { get; set; } /// /// The number of seconds a connection will remain idle before the first keep-alive probe is sent @@ -273,6 +273,15 @@ public LineEndingMode LineEnding internal INetworkSenderFactory SenderFactory { get; set; } + /// + protected override void InitializeTarget() + { + base.InitializeTarget(); + + if (Address is null || ReferenceEquals(Address, Layout.Empty)) + throw new NLogConfigurationException($"{GetType()} Address-property must be assigned. Address is needed for network destination."); + } + /// /// Flush any pending log messages asynchronously (in case of asynchronous targets). /// @@ -281,7 +290,7 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation) { int remainingCount; - void Continuation(Exception ex) + void Continuation(Exception? ex) { // ignore exception if (Interlocked.Decrement(ref remainingCount) == 0) @@ -346,7 +355,7 @@ protected override void Write(AsyncLogEventInfo logEvent) { string address = RenderLogEvent(Address, logEvent.LogEvent); byte[] payload = GetBytesToWrite(logEvent.LogEvent); - byte[] header = GetHeaderToWrite(logEvent.LogEvent, address, payload); + byte[]? header = GetHeaderToWrite(logEvent.LogEvent, address, payload); int messageSize = payload.Length; InternalLogger.Trace("{0}: Sending {1} bytes to address: '{2}'", this, messageSize, address); @@ -386,7 +395,7 @@ protected override void Write(AsyncLogEventInfo logEvent) } } - private void WriteBytesToCachedNetworkSender(string address, byte[] header, byte[] payload, AsyncLogEventInfo logEvent) + private void WriteBytesToCachedNetworkSender(string address, byte[]? header, byte[] payload, AsyncLogEventInfo logEvent) { LinkedListNode senderNode; try @@ -417,7 +426,7 @@ private void WriteBytesToCachedNetworkSender(string address, byte[] header, byte }); } - private void WriteBytesToNewNetworkSender(string address, byte[] header, byte[] payload, AsyncLogEventInfo logEvent) + private void WriteBytesToNewNetworkSender(string address, byte[]? header, byte[] payload, AsyncLogEventInfo logEvent) { NetworkSender sender; LinkedListNode linkedListNode; @@ -541,7 +550,7 @@ protected virtual byte[] GetBytesToWrite(LogEventInfo logEvent) /// network address. /// Payload buffer. /// Byte array. - protected virtual byte[] GetHeaderToWrite(LogEventInfo logEvent, string address, byte[] payload) + protected virtual byte[]? GetHeaderToWrite(LogEventInfo logEvent, string address, byte[] payload) { return null; } @@ -614,7 +623,7 @@ private LinkedListNode GetCachedNetworkSender(string address, Log { // make room in the cache by closing the least recently used connection int minAccessTime = int.MaxValue; - LinkedListNode leastRecentlyUsed = null; + LinkedListNode? leastRecentlyUsed = null; foreach (var pair in _currentSenderCache) { @@ -646,8 +655,8 @@ private LinkedListNode GetCachedNetworkSender(string address, Log private NetworkSender CreateNetworkSender(string address, LogEventInfo logEventInfo) { - var sslCertificateFile = SslCertificateFile?.Render(logEventInfo); - var sslCertificatePassword = SslCertificatePassword?.Render(logEventInfo); + var sslCertificateFile = SslCertificateFile?.Render(logEventInfo) ?? string.Empty; + var sslCertificatePassword = SslCertificatePassword?.Render(logEventInfo) ?? string.Empty; var sslCertificateOverride = LoadSslCertificateFromFile(sslCertificateFile, sslCertificatePassword); var sender = SenderFactory.Create(address, sslCertificateOverride, this); @@ -681,16 +690,16 @@ private void ReleaseCachedConnection(LinkedListNode senderNode) } } - private static void WriteBytesToNetworkSender(NetworkSender sender, byte[] payload, byte[] header, AsyncContinuation continuation) + private static void WriteBytesToNetworkSender(NetworkSender sender, byte[] payload, byte[]? header, AsyncContinuation continuation) { if (header?.Length > 0) sender.Send(header, 0, header.Length, continuation); sender.Send(payload, 0, payload.Length, continuation); } - internal X509Certificate2Collection LoadSslCertificateFromFile(string sslCertificateFile, string sslCertificatePassword) + internal X509Certificate2Collection? LoadSslCertificateFromFile(string sslCertificateFile, string sslCertificatePassword) { - if (sslCertificateFile is null) + if (string.IsNullOrEmpty(sslCertificateFile)) return null; // NOSONAR if (_certificateCache != null && _certificateCache.TryGetValue(sslCertificateFile, out var clientCertificates)) diff --git a/src/NLog.Targets.Network/Targets/SyslogTarget.cs b/src/NLog.Targets.Network/Targets/SyslogTarget.cs index 3341da7096..e654064bfb 100644 --- a/src/NLog.Targets.Network/Targets/SyslogTarget.cs +++ b/src/NLog.Targets.Network/Targets/SyslogTarget.cs @@ -72,7 +72,7 @@ public class SyslogTarget : NetworkTarget public Layout SyslogProcessId { get => _syslogLayout.SyslogProcessId; set => _syslogLayout.SyslogProcessId = value; } /// - public Layout SyslogMessageId { get => _syslogLayout.SyslogMessageId; set => _syslogLayout.SyslogMessageId = value; } + public Layout? SyslogMessageId { get => _syslogLayout.SyslogMessageId; set => _syslogLayout.SyslogMessageId = value; } /// public Layout SyslogMessage { get => _syslogLayout.SyslogMessage; set => _syslogLayout.SyslogMessage = value; } @@ -103,7 +103,7 @@ public SyslogTarget() } /// - protected override byte[] GetHeaderToWrite(LogEventInfo logEvent, string address, byte[] payload) + protected override byte[]? GetHeaderToWrite(LogEventInfo logEvent, string address, byte[] payload) { if (LineEnding?.NewLineCharacters?.Length > 0) return null; @@ -135,14 +135,8 @@ private static byte[] GenerateOctetHeader(int octetCount) /// public override Layout Layout { - get - { - return _syslogLayout; - } - set - { - // Fixed SyslogLayout - } + get => _syslogLayout; + set { /* Fixed SyslogLayout */ } // NOSONAR } } } From 85cbe63ce0ab235591231fc3f4dc7465726e45f2 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 24 May 2025 08:10:51 +0200 Subject: [PATCH 125/224] DumpApiXml - Scan multiple assemblies (#5831) --- src/NLog.proj | 3 +- tools/DumpApiXml/DumpApiXml.csproj | 63 ++++---------- tools/DumpApiXml/DumpApiXml.sln | 15 +++- tools/DumpApiXml/Program.cs | 32 ++++++++ .../NLogNugetPackages.csproj | 33 ++++++++ .../dll_to_doc/dll_to_doc.csproj | 82 ++++--------------- .../SandcastleDocs/dll_to_doc/dll_to_doc.sln | 6 ++ 7 files changed, 115 insertions(+), 119 deletions(-) create mode 100644 tools/NLogNugetPackages/NLogNugetPackages.csproj diff --git a/src/NLog.proj b/src/NLog.proj index c5fe5c71b1..327172ab34 100644 --- a/src/NLog.proj +++ b/src/NLog.proj @@ -103,6 +103,7 @@ + @@ -117,7 +118,7 @@ Inputs="@(TargetFramework -> '$(BaseOutputDirectory)\bin\$(Configuration)\%(Identity)\NLog.dll');$(DumpApiXml);$(MergeApiXml)" Outputs="$(BaseOutputDirectory)\bin\$(Configuration)\NLogMerged.api.xml" > - diff --git a/tools/DumpApiXml/DumpApiXml.csproj b/tools/DumpApiXml/DumpApiXml.csproj index 596b1da527..010fd9b504 100644 --- a/tools/DumpApiXml/DumpApiXml.csproj +++ b/tools/DumpApiXml/DumpApiXml.csproj @@ -1,56 +1,21 @@ - - + + - Debug - AnyCPU - 9.0.30729 - 2.0 - {773C477E-2626-432D-B959-325B3B146744} + net472 Exe - Properties - DumpApiXml - DumpApiXml - v4.7.2 + false + true ..\..\build\bin\Tools\ - ..\..\build\obj\Tools\ - 512 - - - true - full - false - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - false - - - pdbonly - true - TRACE - prompt - 4 - AllRules.ruleset - false - - - - - - - - - - - - - + - - Designer - + - + + + + + + + \ No newline at end of file diff --git a/tools/DumpApiXml/DumpApiXml.sln b/tools/DumpApiXml/DumpApiXml.sln index d5244595d0..7d0b4cce80 100644 --- a/tools/DumpApiXml/DumpApiXml.sln +++ b/tools/DumpApiXml/DumpApiXml.sln @@ -1,8 +1,12 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35931.197 d17.13 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DumpApiXml", "DumpApiXml.csproj", "{773C477E-2626-432D-B959-325B3B146744}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLogNugetPackages", "..\NLogNugetPackages\NLogNugetPackages.csproj", "{6C7CF943-03E5-FBA1-7103-FAFF78DEAE0F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -13,8 +17,15 @@ Global {773C477E-2626-432D-B959-325B3B146744}.Debug|Any CPU.Build.0 = Debug|Any CPU {773C477E-2626-432D-B959-325B3B146744}.Release|Any CPU.ActiveCfg = Release|Any CPU {773C477E-2626-432D-B959-325B3B146744}.Release|Any CPU.Build.0 = Release|Any CPU + {6C7CF943-03E5-FBA1-7103-FAFF78DEAE0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6C7CF943-03E5-FBA1-7103-FAFF78DEAE0F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6C7CF943-03E5-FBA1-7103-FAFF78DEAE0F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6C7CF943-03E5-FBA1-7103-FAFF78DEAE0F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {003EDB45-0CA0-43AD-95CA-7C0389D2E186} + EndGlobalSection EndGlobal diff --git a/tools/DumpApiXml/Program.cs b/tools/DumpApiXml/Program.cs index 889c42c184..83d02c1fb6 100644 --- a/tools/DumpApiXml/Program.cs +++ b/tools/DumpApiXml/Program.cs @@ -63,6 +63,38 @@ private static int Main(string[] args) return 1; } + var outputDir = Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); + if (!string.IsNullOrEmpty(outputDir)) + { + var assemblyFiles = Directory.GetFiles(outputDir, "NLog*.dll"); + Console.WriteLine("Detected {0} assemblies in folder: {1}", assemblyFiles.Length, outputDir); + + foreach (var assembly in assemblyFiles) + { + if (assembly.EndsWith("NLog.dll", StringComparison.OrdinalIgnoreCase)) + continue; + + try + { + builder.LoadAssembly(assembly); + string docpath = Path.ChangeExtension(assembly, ".xml"); + if (File.Exists(docpath)) + { + builder.LoadComments(docpath); + Console.WriteLine("Loaded assembly with XML-docs: {0}", Path.GetFileName(assembly)); + } + else + { + Console.WriteLine("Loaded assembly without XML-docs: {0}", Path.GetFileName(assembly)); + } + } + catch (Exception ex) + { + Console.WriteLine("Failed loading assembly {0} - {1}", Path.GetFullPath(assembly), ex); + } + } + } + outputFile = Path.GetFullPath(outputFile); Console.WriteLine("Generating '{0}'...", outputFile); Directory.CreateDirectory(Path.GetDirectoryName(outputFile)); diff --git a/tools/NLogNugetPackages/NLogNugetPackages.csproj b/tools/NLogNugetPackages/NLogNugetPackages.csproj new file mode 100644 index 0000000000..db1ded419d --- /dev/null +++ b/tools/NLogNugetPackages/NLogNugetPackages.csproj @@ -0,0 +1,33 @@ + + + + net472 + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb;.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/SandcastleDocs/dll_to_doc/dll_to_doc.csproj b/tools/SandcastleDocs/dll_to_doc/dll_to_doc.csproj index a564157cad..33c3f739bf 100644 --- a/tools/SandcastleDocs/dll_to_doc/dll_to_doc.csproj +++ b/tools/SandcastleDocs/dll_to_doc/dll_to_doc.csproj @@ -3,76 +3,24 @@ Exe net472 - false - + false - - - - Always - - - - Always - - - - Always - - - - Always - - - - Always - - - - Always - - - - Always - - - - Always - - - - Always - - - - Always - - - - Always - - - - Always - - - - Always - - - - Always - - - - Always - - - - Always - + + + + + + + true + + + + + + + \ No newline at end of file diff --git a/tools/SandcastleDocs/dll_to_doc/dll_to_doc.sln b/tools/SandcastleDocs/dll_to_doc/dll_to_doc.sln index 0e3de54e95..e7e4667c25 100644 --- a/tools/SandcastleDocs/dll_to_doc/dll_to_doc.sln +++ b/tools/SandcastleDocs/dll_to_doc/dll_to_doc.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.2.32516.85 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dll_to_doc", "dll_to_doc.csproj", "{AA900992-9A6B-44F4-B891-193531554B55}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLogNugetPackages", "..\..\NLogNugetPackages\NLogNugetPackages.csproj", "{97E582D4-2DC1-0776-FDE6-BBD03EFD8375}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {AA900992-9A6B-44F4-B891-193531554B55}.Debug|Any CPU.Build.0 = Debug|Any CPU {AA900992-9A6B-44F4-B891-193531554B55}.Release|Any CPU.ActiveCfg = Release|Any CPU {AA900992-9A6B-44F4-B891-193531554B55}.Release|Any CPU.Build.0 = Release|Any CPU + {97E582D4-2DC1-0776-FDE6-BBD03EFD8375}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {97E582D4-2DC1-0776-FDE6-BBD03EFD8375}.Debug|Any CPU.Build.0 = Debug|Any CPU + {97E582D4-2DC1-0776-FDE6-BBD03EFD8375}.Release|Any CPU.ActiveCfg = Release|Any CPU + {97E582D4-2DC1-0776-FDE6-BBD03EFD8375}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 8673d5806a3eae343fd0427fcfd4575d5a51fd4e Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 24 May 2025 09:27:18 +0200 Subject: [PATCH 126/224] Updated XML docs to say Layout render instead of calculate (#5832) --- src/NLog.Database/DatabaseParameterInfo.cs | 2 +- src/NLog.Targets.Network/Layouts/GelfLayout.cs | 8 ++++---- .../Layouts/Log4JXmlEventParameter.cs | 7 ++++--- src/NLog.Targets.Network/Targets/NetworkTarget.cs | 4 ++-- .../LayoutRenderers/AllEventPropertiesLayoutRenderer.cs | 5 ++--- src/NLog/Layouts/CSV/CsvColumn.cs | 2 +- src/NLog/Layouts/JSON/JsonAttribute.cs | 6 +++--- src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs | 2 +- src/NLog/Layouts/XML/XmlAttribute.cs | 5 +++-- src/NLog/Layouts/XML/XmlElement.cs | 2 +- src/NLog/Layouts/XML/XmlElementBase.cs | 3 +-- src/NLog/Targets/MethodCallParameter.cs | 2 +- src/NLog/Targets/TargetPropertyWithContext.cs | 8 ++++---- 13 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/NLog.Database/DatabaseParameterInfo.cs b/src/NLog.Database/DatabaseParameterInfo.cs index a2bc999980..a56653920f 100644 --- a/src/NLog.Database/DatabaseParameterInfo.cs +++ b/src/NLog.Database/DatabaseParameterInfo.cs @@ -109,7 +109,7 @@ public DatabaseParameterInfo(string parameterName, Layout parameterLayout) public string Name { get; set; } /// - /// Gets or sets the layout that should be use to calculate the value for the parameter. + /// Gets or sets the layout used for rendering the database-parameter value. /// /// public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } diff --git a/src/NLog.Targets.Network/Layouts/GelfLayout.cs b/src/NLog.Targets.Network/Layouts/GelfLayout.cs index c808ca8c9c..547e3eee6d 100644 --- a/src/NLog.Targets.Network/Layouts/GelfLayout.cs +++ b/src/NLog.Targets.Network/Layouts/GelfLayout.cs @@ -33,7 +33,7 @@ public class GelfLayout : CompoundLayout private IJsonConverter? _jsonConverter; /// - /// Graylog Message Host-field + /// Gets or sets Graylog Message Host-field /// public Layout GelfHostName { @@ -48,19 +48,19 @@ public Layout GelfHostName private string? _gelfHostNameString; /// - /// Graylog Message Short-Message-field + /// Gets or sets the Graylog Message Short-Message-field /// /// Will truncate when longer than 250 chars public Layout GelfShortMessage { get; set; } /// - /// Graylog Message Full-Message-field + /// Gets or sets the Graylog Message Full-Message-field /// /// Will truncate when longer than 250 chars public Layout GelfFullMessage { get; set; } /// - /// Graylog Message Facility-field + /// Gets or sets whether to activate the legacy Graylog Message Facility-field /// /// /// Activated legacy GELF v1.0 format diff --git a/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs index ddf2ea04e7..a5efe62f63 100644 --- a/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs @@ -50,20 +50,21 @@ public Log4JXmlEventParameter() } /// - /// Gets or sets viewer parameter name. + /// Gets or sets log4j:data property-name. /// /// public string Name { get; set; } = string.Empty; /// - /// Gets or sets the layout that should be use to calculate the value for the parameter. + /// Gets or sets the layout used for rendering the log4j:data property-value. /// /// public Layout Layout { get; set; } = Layout.Empty; /// - /// Gets or sets whether an attribute with empty value should be included in the output + /// Gets or sets whether empty property-value should be included in the output. Default = false /// + /// Empty value is either null or empty string /// public bool IncludeEmptyValue { get; set; } } diff --git a/src/NLog.Targets.Network/Targets/NetworkTarget.cs b/src/NLog.Targets.Network/Targets/NetworkTarget.cs index cd0543b77f..dde812dddd 100644 --- a/src/NLog.Targets.Network/Targets/NetworkTarget.cs +++ b/src/NLog.Targets.Network/Targets/NetworkTarget.cs @@ -108,10 +108,10 @@ public NetworkTarget(string name) : this() } /// - /// Gets or sets the network address. + /// Gets or sets the network destination address. /// /// - /// The network address can be: + /// The network destination address can be: ///
    ///
  • tcp://host:port - TCP (auto select IPv4/IPv6)
  • ///
  • tcp4://host:port - force TCP/IPv4
  • diff --git a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs index de23d65b7e..c8e0a952e6 100644 --- a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs @@ -83,10 +83,9 @@ public string Separator private string _separatorOriginal = ", "; /// - /// Get or set if empty values should be included. - /// - /// A value is empty when null or in case of a string, null or empty string. + /// Gets or sets whether empty property-values should be included in the output. Default = false /// + /// Empty value is either null or empty string /// public bool IncludeEmptyValues { get; set; } diff --git a/src/NLog/Layouts/CSV/CsvColumn.cs b/src/NLog/Layouts/CSV/CsvColumn.cs index 6ad56fc9e8..26d9bd6f1c 100644 --- a/src/NLog/Layouts/CSV/CsvColumn.cs +++ b/src/NLog/Layouts/CSV/CsvColumn.cs @@ -67,7 +67,7 @@ public CsvColumn(string name, Layout layout) public string Name { get; set; } /// - /// Gets or sets the layout of the column. + /// Gets or sets the layout used for rendering the column value. /// /// public Layout Layout { get; set; } diff --git a/src/NLog/Layouts/JSON/JsonAttribute.cs b/src/NLog/Layouts/JSON/JsonAttribute.cs index 560e920b1f..1dea55e97a 100644 --- a/src/NLog/Layouts/JSON/JsonAttribute.cs +++ b/src/NLog/Layouts/JSON/JsonAttribute.cs @@ -70,7 +70,6 @@ public JsonAttribute(string name, Layout layout, bool encode) _name = Name; Layout = layout; Encode = encode; - IncludeEmptyValue = false; } /// @@ -97,7 +96,7 @@ public string Name private string _name; /// - /// Gets or sets the layout that will be rendered as the attribute's value. + /// Gets or sets the layout used for rendering the attribute value. /// /// public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } @@ -138,8 +137,9 @@ public string Name public bool EscapeForwardSlash { get; set; } /// - /// Gets or sets whether an attribute with empty value should be included in the output + /// Gets or sets whether empty attribute value should be included in the output. Default = false /// + /// Empty value is either null or empty string /// public bool IncludeEmptyValue { diff --git a/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs b/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs index 7799c587d6..1a511facba 100644 --- a/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs +++ b/src/NLog/Layouts/Typed/ValueTypeLayoutInfo.cs @@ -55,7 +55,7 @@ public ValueTypeLayoutInfo() } /// - /// Gets or sets the layout that will render the result value + /// Gets or sets the layout used for rendering the value. /// /// public Layout Layout diff --git a/src/NLog/Layouts/XML/XmlAttribute.cs b/src/NLog/Layouts/XML/XmlAttribute.cs index 55bf5a7e08..dde02dac44 100644 --- a/src/NLog/Layouts/XML/XmlAttribute.cs +++ b/src/NLog/Layouts/XML/XmlAttribute.cs @@ -80,7 +80,7 @@ public XmlAttribute(string name, Layout layout, bool encode) private string _name = string.Empty; /// - /// Gets or sets the layout that will be rendered as the attribute's value. + /// Gets or sets the layout used for rendering the attribute value. /// /// public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } @@ -104,8 +104,9 @@ public XmlAttribute(string name, Layout layout, bool encode) public bool Encode { get; set; } /// - /// Gets or sets whether an attribute with empty value should be included in the output + /// Gets or sets whether empty attribute value should be included in the output. Default = false /// + /// Empty value is either null or empty string /// public bool IncludeEmptyValue { diff --git a/src/NLog/Layouts/XML/XmlElement.cs b/src/NLog/Layouts/XML/XmlElement.cs index 8d1415a880..c179d671b6 100644 --- a/src/NLog/Layouts/XML/XmlElement.cs +++ b/src/NLog/Layouts/XML/XmlElement.cs @@ -81,7 +81,7 @@ public Layout Value } /// - /// Value inside the element + /// Gets or sets the layout used for rendering the XML-element InnerText. /// /// public Layout Layout { get => Value; set => Value = value; } diff --git a/src/NLog/Layouts/XML/XmlElementBase.cs b/src/NLog/Layouts/XML/XmlElementBase.cs index 328a6b8956..22ef271a61 100644 --- a/src/NLog/Layouts/XML/XmlElementBase.cs +++ b/src/NLog/Layouts/XML/XmlElementBase.cs @@ -96,9 +96,8 @@ protected XmlElementBase(string elementName, Layout elementValue) public IList Attributes { get; } = new List(); /// - /// Gets or sets whether a ElementValue with empty value should be included in the output + /// Gets or sets whether empty XML-element should be included in the output. Default = false /// - /// public bool IncludeEmptyValue { get; set; } /// diff --git a/src/NLog/Targets/MethodCallParameter.cs b/src/NLog/Targets/MethodCallParameter.cs index ccd66bea72..cbe1e6c6ee 100644 --- a/src/NLog/Targets/MethodCallParameter.cs +++ b/src/NLog/Targets/MethodCallParameter.cs @@ -96,7 +96,7 @@ public MethodCallParameter(string name, Layout layout, Type type) public string Name { get; set; } = string.Empty; /// - /// Gets or sets the layout that should be use to calculate the value for the parameter. + /// Gets or sets the layout used for rendering the method-parameter value. /// /// public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } diff --git a/src/NLog/Targets/TargetPropertyWithContext.cs b/src/NLog/Targets/TargetPropertyWithContext.cs index f8bf9ad2e5..298df99d04 100644 --- a/src/NLog/Targets/TargetPropertyWithContext.cs +++ b/src/NLog/Targets/TargetPropertyWithContext.cs @@ -59,17 +59,16 @@ public TargetPropertyWithContext(string name, Layout layout) { Name = name; Layout = layout; - IncludeEmptyValue = false; } /// - /// Gets or sets the name of the attribute. + /// Gets or sets the name of the property. /// /// public string Name { get; set; } /// - /// Gets or sets the layout that will be rendered as the attribute's value. + /// Gets or sets the layout used for rendering the property value. /// /// public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } @@ -87,8 +86,9 @@ public TargetPropertyWithContext(string name, Layout layout) public Layout? DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } /// - /// Gets or sets when an empty value should cause the property to be included + /// Gets or sets whether empty property value should be included in the output. Default = false /// + /// Empty value is either null or empty string /// public bool IncludeEmptyValue { From 38d731552ca3e58d33af88a812ada9d59713d78b Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 24 May 2025 22:55:06 +0200 Subject: [PATCH 127/224] LogEventInfo SequenceID marked obsolete, instead use CounterLayoutRenderer (#5834) --- src/NLog/Config/AssemblyExtensionTypes.cs | 2 + src/NLog/Config/AssemblyExtensionTypes.tt | 2 + src/NLog/Internal/StringBuilderExt.cs | 4 ++ .../LayoutRenderers/CounterLayoutRenderer.cs | 70 +++++++++++++++---- .../LayoutRenderers/GuidLayoutRenderer.cs | 2 + .../SequenceIdLayoutRenderer.cs | 6 ++ src/NLog/LogEventInfo.cs | 7 +- .../LayoutRenderers/CounterTests.cs | 8 +-- .../SequenceIdLayoutRendererTest.cs | 3 +- tests/NLog.UnitTests/Targets/TargetTests.cs | 2 + .../Wrappers/AsyncTargetWrapperTests.cs | 2 + 11 files changed, 87 insertions(+), 21 deletions(-) diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index 5fb32260c3..d12dd21cbd 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -38,6 +38,7 @@ namespace NLog.Config /// internal static class AssemblyExtensionTypes { +#pragma warning disable CS0618 // Type or member is obsolete public static void RegisterTargetTypes(ConfigurationItemFactory factory, bool skipCheckExists) { factory.RegisterTypeProperties(() => null); @@ -398,5 +399,6 @@ public static void RegisterConditionTypes(ConfigurationItemFactory factory, bool factory.GetConditionMethodFactory().RegisterThreeParameters("ends-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); } } +#pragma warning restore CS0618 // Type or member is obsolete } } diff --git a/src/NLog/Config/AssemblyExtensionTypes.tt b/src/NLog/Config/AssemblyExtensionTypes.tt index 6a1d70d35a..c192f6ddc9 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.tt +++ b/src/NLog/Config/AssemblyExtensionTypes.tt @@ -48,6 +48,7 @@ namespace NLog.Config /// internal static class AssemblyExtensionTypes { +#pragma warning disable CS0618 // Type or member is obsolete <# string[] NetFrameworkOnly = new [] { "NLog.LayoutRenderers.AppSettingLayoutRenderer", @@ -259,5 +260,6 @@ namespace NLog.Config factory.GetConditionMethodFactory().RegisterThreeParameters("ends-with", (logEvent, arg1, arg2, arg3) => NLog.Conditions.ConditionMethods.EndsWith(arg1?.ToString(), arg2?.ToString(), arg3 is bool ignoreCase ? ignoreCase : true)); } } +#pragma warning restore CS0618 // Type or member is obsolete } } diff --git a/src/NLog/Internal/StringBuilderExt.cs b/src/NLog/Internal/StringBuilderExt.cs index 31e7e5b567..10f3d71e97 100644 --- a/src/NLog/Internal/StringBuilderExt.cs +++ b/src/NLog/Internal/StringBuilderExt.cs @@ -417,9 +417,11 @@ internal static void AppendNumericInvariant(this StringBuilder sb, IConvertible case TypeCode.Int64: { long int64 = value.ToInt64(CultureInfo.InvariantCulture); +#if NETFRAMEWORK if (int64 < int.MaxValue && int64 > int.MinValue) sb.AppendInvariant((int)int64); else +#endif sb.Append(int64); } break; @@ -428,9 +430,11 @@ internal static void AppendNumericInvariant(this StringBuilder sb, IConvertible case TypeCode.UInt64: { ulong uint64 = value.ToUInt64(CultureInfo.InvariantCulture); +#if NETFRAMEWORK if (uint64 < uint.MaxValue) sb.AppendInvariant((uint)uint64); else +#endif sb.Append(uint64); } break; diff --git a/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs b/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs index a79991933e..351fdfe55a 100644 --- a/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs @@ -34,8 +34,8 @@ namespace NLog.LayoutRenderers { using System; - using System.Collections.Generic; using System.Text; + using System.Threading; using NLog.Config; using NLog.Internal; using NLog.Layouts; @@ -51,16 +51,35 @@ namespace NLog.LayoutRenderers [ThreadAgnostic] public class CounterLayoutRenderer : LayoutRenderer, IRawValue { - private static readonly Dictionary Sequences = new Dictionary(StringComparer.Ordinal); + private sealed class GlobalSequence + { + private long _value; + public string Name { get; } + + public GlobalSequence(string sequenceName, long initialValue) + { + Name = sequenceName; + _value = initialValue; + } + + public long NextValue(int increment) => Interlocked.Add(ref _value, increment); + }; +#if NET35 + private static readonly System.Collections.Generic.Dictionary Sequences = new System.Collections.Generic.Dictionary(StringComparer.Ordinal); +#else + private static readonly System.Collections.Concurrent.ConcurrentDictionary Sequences = new System.Collections.Concurrent.ConcurrentDictionary(StringComparer.Ordinal); +#endif + private static GlobalSequence? _firstSequence; /// /// Gets or sets the initial value of the counter. /// /// - public long Value { get; set; } = 1; + public long Value { get => _value; set => _value = value; } + private long _value; /// - /// Gets or sets the value to be added to the counter after each layout rendering. + /// Gets or sets the value for incrementing the counter for every layout rendering. /// /// public int Increment { get; set; } = 1; @@ -75,9 +94,11 @@ public class CounterLayoutRenderer : LayoutRenderer, IRawValue protected override void Append(StringBuilder builder, LogEventInfo logEvent) { var v = GetNextValue(logEvent); +#if NETFRAMEWORK if (v < int.MaxValue && v > int.MinValue) builder.AppendInvariant((int)v); else +#endif builder.Append(v); } @@ -85,24 +106,45 @@ private long GetNextValue(LogEventInfo logEvent) { if (Sequence is null) { - long currentValue = Value; - Value += Increment; - return currentValue; + return Interlocked.Add(ref _value, Increment); } var sequenceName = Sequence.Render(logEvent); + return GetNextGlobalValue(sequenceName); + } + + private long GetNextGlobalValue(string sequenceName) + { + var globalSequence = _firstSequence; + if (globalSequence is null) + { + globalSequence = new GlobalSequence(sequenceName, Value); + Interlocked.CompareExchange(ref _firstSequence, globalSequence, null); + globalSequence = _firstSequence; + } + if (globalSequence.Name.Equals(sequenceName)) + return globalSequence.NextValue(Increment); + +#if NET35 lock (Sequences) { - if (!Sequences.TryGetValue(sequenceName, out var nextValue)) + if (!Sequences.TryGetValue(sequenceName, out globalSequence)) { - nextValue = Value; + globalSequence = new GlobalSequence(sequenceName, Value); + Sequences[sequenceName] = globalSequence; + } + } +#else + if (!Sequences.TryGetValue(sequenceName, out globalSequence)) + { + globalSequence = new GlobalSequence(sequenceName, Value); + if (!Sequences.TryAdd(sequenceName, globalSequence)) + { + Sequences.TryGetValue(sequenceName, out globalSequence); } - - var currentValue = nextValue; - nextValue += Increment; - Sequences[sequenceName] = nextValue; - return currentValue; } +#endif + return globalSequence.NextValue(Increment); } bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object value) diff --git a/src/NLog/LayoutRenderers/GuidLayoutRenderer.cs b/src/NLog/LayoutRenderers/GuidLayoutRenderer.cs index 572ce164c8..c95c11520d 100644 --- a/src/NLog/LayoutRenderers/GuidLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/GuidLayoutRenderer.cs @@ -97,7 +97,9 @@ private Guid GetValue(LogEventInfo logEvent) byte i = (byte)((zeroDateTicks >> 16) & 0xFF); byte j = (byte)((zeroDateTicks >> 8) & 0xFF); byte k = (byte)(zeroDateTicks & 0XFF); +#pragma warning disable CS0618 // Type or member is obsolete guid = new Guid(logEvent.SequenceID, b, c, d, e, f, g, h, i, j, k); +#pragma warning restore CS0618 // Type or member is obsolete } else { diff --git a/src/NLog/LayoutRenderers/SequenceIdLayoutRenderer.cs b/src/NLog/LayoutRenderers/SequenceIdLayoutRenderer.cs index d922847e9f..42741388c3 100644 --- a/src/NLog/LayoutRenderers/SequenceIdLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/SequenceIdLayoutRenderer.cs @@ -33,12 +33,16 @@ namespace NLog.LayoutRenderers { + using System; + using System.ComponentModel; using System.Text; using NLog.Config; using NLog.Internal; /// /// The sequence ID + /// + /// Marked obsolete with NLog 6.0, instead use ${counter} /// /// /// See NLog Wiki @@ -46,6 +50,8 @@ namespace NLog.LayoutRenderers /// Documentation on NLog Wiki [LayoutRenderer("sequenceid")] [ThreadAgnostic] + [Obsolete("Use ${counter:sequence=global} instead of ${sequenceid}. Marked obsolete with NLog 6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public class SequenceIdLayoutRenderer : LayoutRenderer, IRawValue { /// diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index 1a7d5c42ca..6a5b8561c5 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -185,10 +185,13 @@ public LogEventInfo(LogLevel level, string? loggerName, IFormatProvider? formatP } /// - /// Gets the unique identifier of log event which is automatically generated - /// and monotonously increasing. + /// Gets the sequence number for this LogEvent, which monotonously increasing for each LogEvent until int-overflow + /// + /// Marked obsolete with NLog 6.0, instead use ${counter:sequence=global} or ${guid:GeneratedFromLogEvent=true} /// // ReSharper disable once InconsistentNaming + [Obsolete("Use ${counter:sequence=global} instead of ${sequenceid}. Marked obsolete with NLog 6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public int SequenceID { get diff --git a/tests/NLog.UnitTests/LayoutRenderers/CounterTests.cs b/tests/NLog.UnitTests/LayoutRenderers/CounterTests.cs index 73101266d9..d8330e2c87 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/CounterTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/CounterTests.cs @@ -100,13 +100,13 @@ public void PresetCounterTest() var logger = LogManager.GetLogger("A"); logger.Debug("a"); logger.Info("a"); - AssertDebugLastMessage("debug", "a 1 1"); + AssertDebugLastMessage("debug", "a 4 1"); logger.Warn("a"); - AssertDebugLastMessage("debug", "a 4 2"); + AssertDebugLastMessage("debug", "a 7 2"); logger.Error("a"); - AssertDebugLastMessage("debug", "a 7 3"); + AssertDebugLastMessage("debug", "a 10 3"); logger.Fatal("a"); - AssertDebugLastMessage("debug", "a 10 4"); + AssertDebugLastMessage("debug", "a 13 4"); } [Fact] diff --git a/tests/NLog.UnitTests/LayoutRenderers/SequenceIdLayoutRendererTest.cs b/tests/NLog.UnitTests/LayoutRenderers/SequenceIdLayoutRendererTest.cs index 163b568b07..066e93d524 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/SequenceIdLayoutRendererTest.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/SequenceIdLayoutRendererTest.cs @@ -33,12 +33,13 @@ namespace NLog.UnitTests.LayoutRenderers { + using System; using System.Globalization; using Xunit; + [Obsolete("Use ${counter:sequence=global} instead of ${sequenceid}. Marked obsolete with NLog 6.0")] public class SequenceIdLayoutRendererTest : NLogTestBase { - [Fact] public void RenderSequenceIdLayoutRenderer() { diff --git a/tests/NLog.UnitTests/Targets/TargetTests.cs b/tests/NLog.UnitTests/Targets/TargetTests.cs index 4bdf93436c..b477b07f57 100644 --- a/tests/NLog.UnitTests/Targets/TargetTests.cs +++ b/tests/NLog.UnitTests/Targets/TargetTests.cs @@ -873,7 +873,9 @@ public void TypedLayoutTargetAsyncTest() Assert.Equal((byte)42, logEvent.Properties["ByteProperty"]); Assert.Equal((short)43, logEvent.Properties["Int16Property"]); Assert.Equal(CurrentManagedThreadId, logEvent.Properties["Int32Property"]); +#pragma warning disable CS0618 // Type or member is obsolete Assert.Equal((long)logEvent.SequenceID, logEvent.Properties["Int64Property"]); +#pragma warning restore CS0618 // Type or member is obsolete Assert.Equal(AppDomain.CurrentDomain.FriendlyName, logEvent.Properties["StringProperty"]); Assert.Equal(true, logEvent.Properties["BoolProperty"]); Assert.Equal(3.14159, logEvent.Properties["DoubleProperty"]); diff --git a/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs b/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs index 951a9d3435..01c4874c89 100644 --- a/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs +++ b/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs @@ -121,7 +121,9 @@ private static void AsyncTargetWrapperSyncTest_WhenTimeToSleepBetweenBatchesIsEq for (int i = 0; i < itemPrepareList.Capacity; ++i) { var logEvent = new LogEventInfo(); +#pragma warning disable CS0618 // Type or member is obsolete int sequenceID = logEvent.SequenceID; +#pragma warning restore CS0618 // Type or member is obsolete bool blockConsumer = (itemPrepareList.Capacity / 2) == i; // Force producers to get into blocking-mode itemPrepareList.Add(logEvent.WithContinuation((ex) => { if (blockConsumer) Thread.Sleep(125); itemWrittenList.Add(sequenceID); })); } From 1f4520bc613404a44a163b72b153dd0568907df7 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 25 May 2025 00:02:25 +0200 Subject: [PATCH 128/224] Logger WithProperties to support params ReadOnlySpan (#5835) --- src/NLog/LogEventBuilder.cs | 2 +- src/NLog/Logger.cs | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/NLog/LogEventBuilder.cs b/src/NLog/LogEventBuilder.cs index e8722dfc91..016d28d2ed 100644 --- a/src/NLog/LogEventBuilder.cs +++ b/src/NLog/LogEventBuilder.cs @@ -129,7 +129,7 @@ public LogEventBuilder Properties([NotNull] IEnumerable /// The properties to set. - public LogEventBuilder Properties(params ReadOnlySpan<(string, object)> properties) + public LogEventBuilder Properties(params ReadOnlySpan<(string, object?)> properties) { if (_logEvent is null) return this; diff --git a/src/NLog/Logger.cs b/src/NLog/Logger.cs index 5a7b180cfc..46becde5fd 100644 --- a/src/NLog/Logger.cs +++ b/src/NLog/Logger.cs @@ -128,19 +128,39 @@ public Logger WithProperty(string propertyKey, object? propertyValue) ///
/// Collection of key-value pair properties /// New Logger object that automatically appends specified properties - public Logger WithProperties(IEnumerable> properties) + public Logger WithProperties(IEnumerable> properties) { Guard.ThrowIfNull(properties); Logger newLogger = CreateChildLogger(); var newProperties = newLogger.Properties; - foreach (KeyValuePair property in properties) + foreach (var property in properties) { newProperties[property.Key] = property.Value; } return newLogger; } +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER + /// + /// Creates new logger that automatically appends the specified properties to all log events (without changing current logger) + /// + /// With property, all properties can be enumerated. + /// + /// Collection of key-value pair properties + /// New Logger object that automatically appends specified properties + public Logger WithProperties(params ReadOnlySpan<(string, object?)> properties) + { + Logger newLogger = CreateChildLogger(); + var newProperties = newLogger.Properties; + foreach (var property in properties) + { + newProperties[property.Item1] = property.Item2; + } + return newLogger; + } +#endif + /// /// Obsolete and replaced by that prevents unexpected side-effects in Logger-state. /// From e33da8cbe7af2953b458fd53e49c97a1ab535d95 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 25 May 2025 00:32:43 +0200 Subject: [PATCH 129/224] Removed NotNull + CanBeNull since now using nullable annotations (#5836) --- .../FileAppenders/BaseMutexFileAppender.cs | 1 - src/NLog/Common/IInternalLoggerContext.cs | 3 - src/NLog/Common/InternalLogEventArgs.cs | 6 +- src/NLog/Common/InternalLogger.cs | 4 +- src/NLog/Config/LoggingConfiguration.cs | 9 +-- src/NLog/Config/LoggingConfigurationParser.cs | 5 +- src/NLog/Config/LoggingRule.cs | 15 ++-- .../Config/NLogDependencyResolveException.cs | 7 +- .../Config/ServiceRepositoryExtensions.cs | 11 ++- src/NLog/Config/XmlLoggingConfiguration.cs | 29 ++++---- src/NLog/ILoggerExtensions.cs | 74 +++++++++---------- src/NLog/Internal/CollectionExtensions.cs | 4 +- .../Internal/LogMessageTemplateFormatter.cs | 3 +- src/NLog/Internal/ReflectionHelpers.cs | 4 - src/NLog/Internal/StringHelpers.cs | 2 +- src/NLog/Layouts/Layout.cs | 2 - src/NLog/Layouts/Typed/Layout.cs | 7 +- .../Layouts/Typed/LayoutTypedExtensions.cs | 3 +- src/NLog/LogEventBuilder.cs | 10 +-- src/NLog/LogEventInfo.cs | 5 +- src/NLog/LogFactory.cs | 2 +- src/NLog/LogManager.cs | 2 + src/NLog/Logger.cs | 4 +- src/NLog/LoggerImpl.cs | 3 +- .../MessageTemplateParameter.cs | 8 +- src/NLog/MessageTemplates/ValueFormatter.cs | 3 +- src/NLog/Targets/LineEndingMode.cs | 3 +- src/NLog/Targets/Target.cs | 5 +- .../Config/ServiceRepositoryTests.cs | 2 - tests/NLog.UnitTests/LogManagerTests.cs | 6 -- 30 files changed, 99 insertions(+), 143 deletions(-) diff --git a/src/NLog.Targets.ConcurrentFile/FileAppenders/BaseMutexFileAppender.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/BaseMutexFileAppender.cs index 3e035d4b18..0d56453e5b 100644 --- a/src/NLog.Targets.ConcurrentFile/FileAppenders/BaseMutexFileAppender.cs +++ b/src/NLog.Targets.ConcurrentFile/FileAppenders/BaseMutexFileAppender.cs @@ -38,7 +38,6 @@ namespace NLog.Internal.FileAppenders using System.Security; using System.Threading; using System.Text; - using JetBrains.Annotations; #if NETFRAMEWORK using System.Security.AccessControl; diff --git a/src/NLog/Common/IInternalLoggerContext.cs b/src/NLog/Common/IInternalLoggerContext.cs index 4186ce4955..d485aab18e 100644 --- a/src/NLog/Common/IInternalLoggerContext.cs +++ b/src/NLog/Common/IInternalLoggerContext.cs @@ -33,8 +33,6 @@ namespace NLog.Common { - using JetBrains.Annotations; - /// /// Enables to extract extra context details for /// @@ -48,7 +46,6 @@ internal interface IInternalLoggerContext /// /// The current LogFactory next to LogManager /// - [CanBeNull] LogFactory? LogFactory { get; } } } diff --git a/src/NLog/Common/InternalLogEventArgs.cs b/src/NLog/Common/InternalLogEventArgs.cs index ec852cfa1f..a35c714c89 100644 --- a/src/NLog/Common/InternalLogEventArgs.cs +++ b/src/NLog/Common/InternalLogEventArgs.cs @@ -34,7 +34,6 @@ namespace NLog.Common { using System; - using JetBrains.Annotations; /// /// Internal LogEvent details from @@ -54,24 +53,21 @@ public readonly struct InternalLogEventArgs /// /// The exception. Could be null. /// - [CanBeNull] public Exception? Exception { get; } /// /// The type that triggered this internal log event, for example the FileTarget. /// This property is not always populated. /// - [CanBeNull] public Type? SenderType { get; } /// /// The context name that triggered this internal log event, for example the name of the Target. /// This property is not always populated. /// - [CanBeNull] public string? SenderName { get; } - internal InternalLogEventArgs(string message, LogLevel level, [CanBeNull] Exception? exception, [CanBeNull] Type? senderType, [CanBeNull] string? senderName) + internal InternalLogEventArgs(string message, LogLevel level, Exception? exception, Type? senderType, string? senderName) { Message = message; Level = level; diff --git a/src/NLog/Common/InternalLogger.cs b/src/NLog/Common/InternalLogger.cs index 8a1adba4e2..937e15b26e 100644 --- a/src/NLog/Common/InternalLogger.cs +++ b/src/NLog/Common/InternalLogger.cs @@ -281,7 +281,7 @@ public static void Log(Exception? ex, LogLevel level, [Localizable(false)] strin /// level /// message /// optional args for - private static void Write([CanBeNull] Exception? ex, LogLevel level, string message, [CanBeNull] object?[]? args) + private static void Write(Exception? ex, LogLevel level, string message, object?[]? args) { if (!IsLogLevelEnabled(level)) { @@ -354,7 +354,7 @@ private static void WriteToLog(LogLevel level, Exception? ex, string fullMessage /// /// Create log line with timestamp, exception message etc (if configured) /// - private static string CreateLogLine([CanBeNull] Exception? ex, LogLevel level, string fullMessage) + private static string CreateLogLine(Exception? ex, LogLevel level, string fullMessage) { const string timeStampFormat = "yyyy-MM-dd HH:mm:ss.ffff"; const string fieldSeparator = " "; diff --git a/src/NLog/Config/LoggingConfiguration.cs b/src/NLog/Config/LoggingConfiguration.cs index 56724e37d5..b582e6d9e5 100644 --- a/src/NLog/Config/LoggingConfiguration.cs +++ b/src/NLog/Config/LoggingConfiguration.cs @@ -40,8 +40,6 @@ namespace NLog.Config using System.Globalization; using System.Linq; using System.Threading; - using JetBrains.Annotations; - using NLog.Common; using NLog.Internal; using NLog.Layouts; @@ -172,7 +170,6 @@ private void AddTargetThreadSafe(Target target, string? targetAlias = null) /// /// Specific culture info or null to use /// - [CanBeNull] public CultureInfo? DefaultCultureInfo { get; set; } /// @@ -210,7 +207,7 @@ internal bool TryLookupDynamicVariable(string key, out Layout value) /// The target object with a non /// /// when is - public void AddTarget([NotNull] Target target) + public void AddTarget(Target target) { Guard.ThrowIfNull(target); @@ -228,7 +225,7 @@ public void AddTarget([NotNull] Target target) /// The target object. /// when is /// when is - public void AddTarget(string name, [NotNull] Target target) + public void AddTarget(string name, Target target) { Guard.ThrowIfNull(name); Guard.ThrowIfNull(target); @@ -866,13 +863,11 @@ private List GetSupportsInitializes(bool reverse = false) /// /// /// - [NotNull] internal string ExpandSimpleVariables(string? input) { return ExpandSimpleVariables(input, out var _); } - [NotNull] internal string ExpandSimpleVariables(string? input, out string? matchingVariableName) { var output = input; diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index 4505c2593b..756561c485 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -56,8 +56,6 @@ namespace NLog.Config /// public abstract class LoggingConfigurationParser : LoggingConfiguration { - private readonly ServiceRepository _serviceRepository; - /// /// Constructor /// @@ -65,7 +63,6 @@ public abstract class LoggingConfigurationParser : LoggingConfiguration protected LoggingConfigurationParser(LogFactory logFactory) : base(logFactory) { - _serviceRepository = logFactory.ServiceRepository; } /// @@ -204,7 +201,7 @@ private void SetNLogElementSettings(ILoggingConfigurationElement nlogConfig) ConfigurationItemFactory.Default.AssemblyLoader.ScanForAutoLoadExtensions(ConfigurationItemFactory.Default); } - _serviceRepository.ParseMessageTemplates(LogFactory, parseMessageTemplates); + LogFactory.ServiceRepository.ParseMessageTemplates(LogFactory, parseMessageTemplates); } /// diff --git a/src/NLog/Config/LoggingRule.cs b/src/NLog/Config/LoggingRule.cs index 65558696ff..aaf38672bd 100644 --- a/src/NLog/Config/LoggingRule.cs +++ b/src/NLog/Config/LoggingRule.cs @@ -38,9 +38,9 @@ namespace NLog.Config using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; - using System.Linq; using System.Text; using NLog.Filters; + using NLog.Internal; using NLog.Targets; /// @@ -57,7 +57,6 @@ public class LoggingRule /// Create an empty . /// public LoggingRule() - : this(null) { } @@ -77,8 +76,8 @@ public LoggingRule(string? ruleName) /// Maximum log level needed to trigger this rule. /// Target to be written to when the rule matches. public LoggingRule(string loggerNamePattern, LogLevel minLevel, LogLevel maxLevel, Target target) - : this() { + Guard.ThrowIfNull(target); LoggerNamePattern = loggerNamePattern; _targets.Add(target); EnableLoggingForLevels(minLevel, maxLevel); @@ -91,8 +90,8 @@ public LoggingRule(string loggerNamePattern, LogLevel minLevel, LogLevel maxLeve /// Minimum log level needed to trigger this rule. /// Target to be written to when the rule matches. public LoggingRule(string loggerNamePattern, LogLevel minLevel, Target target) - : this() { + Guard.ThrowIfNull(target); LoggerNamePattern = loggerNamePattern; _targets.Add(target); EnableLoggingForLevels(minLevel, LogLevel.MaxLevel); @@ -104,8 +103,8 @@ public LoggingRule(string loggerNamePattern, LogLevel minLevel, Target target) /// Logger name pattern used for . It may include one or more '*' or '?' wildcards at any position. /// Target to be written to when the rule matches. public LoggingRule(string loggerNamePattern, Target target) - : this() { + Guard.ThrowIfNull(target); LoggerNamePattern = loggerNamePattern; _targets.Add(target); } @@ -132,7 +131,7 @@ public LoggingRule(string loggerNamePattern, Target target) private readonly List _childRules = new List(); [Obsolete("Very exotic feature without any unit-tests, not sure if it works. Marked obsolete with NLog v5.3")] internal LoggingRule[] GetChildRulesThreadSafe() { lock (_childRules) return _childRules.ToArray(); } - internal Target[] GetTargetsThreadSafe() { lock (_targets) return _targets.Count == 0 ? NLog.Internal.ArrayHelper.Empty() : _targets.ToArray(); } + internal Target[] GetTargetsThreadSafe() { lock (_targets) return _targets.Count == 0 ? ArrayHelper.Empty() : _targets.ToArray(); } internal bool RemoveTargetThreadSafe(Target target) { lock (_targets) return _targets.Remove(target); } /// @@ -274,6 +273,8 @@ public void DisableLoggingForLevel(LogLevel level) /// Maximum log level to be disabled. public void DisableLoggingForLevels(LogLevel minLevel, LogLevel maxLevel) { + Guard.ThrowIfNull(minLevel); + Guard.ThrowIfNull(maxLevel); _logLevelFilter = _logLevelFilter.GetSimpleFilterForUpdate().SetLoggingLevels(minLevel, maxLevel, false); } @@ -284,6 +285,8 @@ public void DisableLoggingForLevels(LogLevel minLevel, LogLevel maxLevel) /// Maximum log level needed to trigger this rule. public void SetLoggingLevels(LogLevel minLevel, LogLevel maxLevel) { + Guard.ThrowIfNull(minLevel); + Guard.ThrowIfNull(maxLevel); _logLevelFilter = _logLevelFilter.GetSimpleFilterForUpdate().SetLoggingLevels(LogLevel.MinLevel, LogLevel.MaxLevel, false).SetLoggingLevels(minLevel, maxLevel, true); } diff --git a/src/NLog/Config/NLogDependencyResolveException.cs b/src/NLog/Config/NLogDependencyResolveException.cs index 7922812bd8..a5fd2eeaed 100644 --- a/src/NLog/Config/NLogDependencyResolveException.cs +++ b/src/NLog/Config/NLogDependencyResolveException.cs @@ -34,7 +34,6 @@ namespace NLog.Config { using System; - using JetBrains.Annotations; using NLog.Internal; /// @@ -45,12 +44,12 @@ public sealed class NLogDependencyResolveException : Exception /// /// Typed we tried to resolve /// - [NotNull] public Type ServiceType { get; } + public Type ServiceType { get; } /// /// Initializes a new instance of the class. /// - public NLogDependencyResolveException(string message, [NotNull] Type serviceType) : base(CreateFullMessage(serviceType, message)) + public NLogDependencyResolveException(string message, Type serviceType) : base(CreateFullMessage(serviceType, message)) { ServiceType = Guard.ThrowIfNull(serviceType); } @@ -58,7 +57,7 @@ public NLogDependencyResolveException(string message, [NotNull] Type serviceType /// /// Initializes a new instance of the class. /// - public NLogDependencyResolveException(string message, Exception innerException, [NotNull] Type serviceType) : base(CreateFullMessage(serviceType, message), innerException) + public NLogDependencyResolveException(string message, Exception innerException, Type serviceType) : base(CreateFullMessage(serviceType, message), innerException) { ServiceType = Guard.ThrowIfNull(serviceType); } diff --git a/src/NLog/Config/ServiceRepositoryExtensions.cs b/src/NLog/Config/ServiceRepositoryExtensions.cs index 06d415af93..6787be879b 100644 --- a/src/NLog/Config/ServiceRepositoryExtensions.cs +++ b/src/NLog/Config/ServiceRepositoryExtensions.cs @@ -34,13 +34,12 @@ namespace NLog.Config { using System; - using JetBrains.Annotations; using NLog.Internal; using NLog.Targets; internal static class ServiceRepositoryExtensions { - internal static ServiceRepository GetServiceProvider([CanBeNull] this LoggingConfiguration? loggingConfiguration) + internal static ServiceRepository GetServiceProvider(this LoggingConfiguration? loggingConfiguration) { return loggingConfiguration?.LogFactory?.ServiceRepository ?? LogManager.LogFactory.ServiceRepository; } @@ -138,7 +137,7 @@ internal static ServiceRepository RegisterSingleton(this ServiceRepository se /// /// Registers the string serializer to use with /// - internal static ServiceRepository RegisterValueFormatter(this ServiceRepository serviceRepository, [NotNull] IValueFormatter valueFormatter) + internal static ServiceRepository RegisterValueFormatter(this ServiceRepository serviceRepository, IValueFormatter valueFormatter) { Guard.ThrowIfNull(valueFormatter); @@ -146,7 +145,7 @@ internal static ServiceRepository RegisterValueFormatter(this ServiceRepository return serviceRepository; } - internal static ServiceRepository RegisterJsonConverter(this ServiceRepository serviceRepository, [NotNull] IJsonConverter jsonConverter) + internal static ServiceRepository RegisterJsonConverter(this ServiceRepository serviceRepository, IJsonConverter jsonConverter) { Guard.ThrowIfNull(jsonConverter); @@ -154,7 +153,7 @@ internal static ServiceRepository RegisterJsonConverter(this ServiceRepository s return serviceRepository; } - internal static ServiceRepository RegisterPropertyTypeConverter(this ServiceRepository serviceRepository, [NotNull] IPropertyTypeConverter converter) + internal static ServiceRepository RegisterPropertyTypeConverter(this ServiceRepository serviceRepository, IPropertyTypeConverter converter) { Guard.ThrowIfNull(converter); @@ -162,7 +161,7 @@ internal static ServiceRepository RegisterPropertyTypeConverter(this ServiceRepo return serviceRepository; } - internal static ServiceRepository RegisterObjectTypeTransformer(this ServiceRepository serviceRepository, [NotNull] IObjectTypeTransformer transformer) + internal static ServiceRepository RegisterObjectTypeTransformer(this ServiceRepository serviceRepository, IObjectTypeTransformer transformer) { Guard.ThrowIfNull(transformer); diff --git a/src/NLog/Config/XmlLoggingConfiguration.cs b/src/NLog/Config/XmlLoggingConfiguration.cs index 0d2a69b65e..42a14738b1 100644 --- a/src/NLog/Config/XmlLoggingConfiguration.cs +++ b/src/NLog/Config/XmlLoggingConfiguration.cs @@ -39,7 +39,6 @@ namespace NLog.Config using System.IO; using System.Linq; using System.Threading; - using JetBrains.Annotations; using NLog.Common; using NLog.Internal; using NLog.Layouts; @@ -68,7 +67,7 @@ internal XmlLoggingConfiguration(LogFactory logFactory) /// Initializes a new instance of the class. /// /// Path to the config-file to read. - public XmlLoggingConfiguration([NotNull] string fileName) + public XmlLoggingConfiguration(string fileName) : this(fileName, LogManager.LogFactory) { } @@ -77,7 +76,7 @@ public XmlLoggingConfiguration([NotNull] string fileName) /// /// Path to the config-file to read. /// The to which to apply any applicable configuration values. - public XmlLoggingConfiguration([NotNull] string fileName, LogFactory logFactory) + public XmlLoggingConfiguration(string fileName, LogFactory logFactory) : base(logFactory) { Guard.ThrowIfNullOrEmpty(fileName); @@ -88,7 +87,7 @@ public XmlLoggingConfiguration([NotNull] string fileName, LogFactory logFactory) /// Initializes a new instance of the class. /// /// Configuration file to be read. - public XmlLoggingConfiguration([NotNull] TextReader xmlSource) + public XmlLoggingConfiguration(TextReader xmlSource) : this(xmlSource, null) { } @@ -98,7 +97,7 @@ public XmlLoggingConfiguration([NotNull] TextReader xmlSource) /// /// Configuration file to be read. /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. - public XmlLoggingConfiguration([NotNull] TextReader xmlSource, string? filePath) + public XmlLoggingConfiguration(TextReader xmlSource, string? filePath) : this(xmlSource, filePath, LogManager.LogFactory) { } @@ -109,7 +108,7 @@ public XmlLoggingConfiguration([NotNull] TextReader xmlSource, string? filePath) /// Configuration file to be read. /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The to which to apply any applicable configuration values. - public XmlLoggingConfiguration([NotNull] TextReader xmlSource, string? filePath, LogFactory logFactory) + public XmlLoggingConfiguration(TextReader xmlSource, string? filePath, LogFactory logFactory) : base(logFactory) { Guard.ThrowIfNull(xmlSource); @@ -122,7 +121,7 @@ public XmlLoggingConfiguration([NotNull] TextReader xmlSource, string? filePath, /// /// XML reader to read from. [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] - public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader) + public XmlLoggingConfiguration(System.Xml.XmlReader reader) : this(reader, null) { } /// @@ -131,7 +130,7 @@ public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader) /// XmlReader containing the configuration section. /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] - public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader, [CanBeNull] string? fileName) + public XmlLoggingConfiguration(System.Xml.XmlReader reader, string? fileName) : this(reader, fileName, LogManager.LogFactory) { } @@ -142,7 +141,7 @@ public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader, [CanBeNull /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The to which to apply any applicable configuration values. [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] - public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader, [CanBeNull] string? fileName, LogFactory logFactory) + public XmlLoggingConfiguration(System.Xml.XmlReader reader, string? fileName, LogFactory logFactory) : base(logFactory) { Guard.ThrowIfNull(reader); @@ -156,7 +155,7 @@ public XmlLoggingConfiguration([NotNull] System.Xml.XmlReader reader, [CanBeNull /// NLog configuration as XML string. /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The to which to apply any applicable configuration values. - internal XmlLoggingConfiguration([NotNull] string xmlContents, [CanBeNull] string filePath, LogFactory logFactory) + internal XmlLoggingConfiguration(string xmlContents, string filePath, LogFactory logFactory) : base(logFactory) { Guard.ThrowIfNullOrEmpty(xmlContents); @@ -338,7 +337,7 @@ internal void LoadFromXmlContent(string xmlContents, string filePath) #if NETFRAMEWORK [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] - private void ParseFromXmlReader([NotNull] System.Xml.XmlReader reader, [CanBeNull] string? filePath) + private void ParseFromXmlReader(System.Xml.XmlReader reader, string? filePath) { try { @@ -391,7 +390,7 @@ private void ParseFromTextReader(TextReader textReader, string? filePath) /// /// Include new file into the configuration. Check if not already included. /// - private void IncludeNewConfigFile([NotNull] string filePath, bool autoReloadDefault) + private void IncludeNewConfigFile(string filePath, bool autoReloadDefault) { if (!_fileMustAutoReloadLookup.ContainsKey(GetFileLookupKey(filePath))) { @@ -409,7 +408,7 @@ private void IncludeNewConfigFile([NotNull] string filePath, bool autoReloadDefa /// /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The default value for the autoReload option. - private void ParseTopLevel(ILoggingConfigurationElement content, [CanBeNull] string? filePath, bool autoReloadDefault) + private void ParseTopLevel(ILoggingConfigurationElement content, string? filePath, bool autoReloadDefault) { content.AssertName("nlog", "configuration"); @@ -431,7 +430,7 @@ private void ParseTopLevel(ILoggingConfigurationElement content, [CanBeNull] str /// /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The default value for the autoReload option. - private void ParseConfigurationElement(ILoggingConfigurationElement configurationElement, [CanBeNull] string? filePath, bool autoReloadDefault) + private void ParseConfigurationElement(ILoggingConfigurationElement configurationElement, string? filePath, bool autoReloadDefault) { InternalLogger.Trace("ParseConfigurationElement"); configurationElement.AssertName("configuration"); @@ -449,7 +448,7 @@ private void ParseConfigurationElement(ILoggingConfigurationElement configuratio /// /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The default value for the autoReload option. - private void ParseNLogElement(ILoggingConfigurationElement nlogElement, [CanBeNull] string? filePath, bool autoReloadDefault) + private void ParseNLogElement(ILoggingConfigurationElement nlogElement, string? filePath, bool autoReloadDefault) { InternalLogger.Trace("ParseNLogElement"); nlogElement.AssertName("nlog"); diff --git a/src/NLog/ILoggerExtensions.cs b/src/NLog/ILoggerExtensions.cs index 1e465e11f5..b731b1ee85 100644 --- a/src/NLog/ILoggerExtensions.cs +++ b/src/NLog/ILoggerExtensions.cs @@ -54,7 +54,7 @@ public static class ILoggerExtensions /// The logger to write the log event to. /// The log level. When not /// for chaining calls. - public static LogEventBuilder ForLogEvent([NotNull] this ILogger logger, LogLevel? logLevel = null) + public static LogEventBuilder ForLogEvent(this ILogger logger, LogLevel? logLevel = null) { return logLevel is null ? new LogEventBuilder(logger) : new LogEventBuilder(logger, logLevel); } @@ -64,7 +64,7 @@ public static LogEventBuilder ForLogEvent([NotNull] this ILogger logger, LogLeve /// /// The logger to write the log event to. /// for chaining calls. - public static LogEventBuilder ForTraceEvent([NotNull] this ILogger logger) + public static LogEventBuilder ForTraceEvent(this ILogger logger) { return new LogEventBuilder(logger, LogLevel.Trace); } @@ -74,7 +74,7 @@ public static LogEventBuilder ForTraceEvent([NotNull] this ILogger logger) /// /// The logger to write the log event to. /// for chaining calls. - public static LogEventBuilder ForDebugEvent([NotNull] this ILogger logger) + public static LogEventBuilder ForDebugEvent(this ILogger logger) { return new LogEventBuilder(logger, LogLevel.Debug); } @@ -84,7 +84,7 @@ public static LogEventBuilder ForDebugEvent([NotNull] this ILogger logger) ///
/// The logger to write the log event to. /// for chaining calls. - public static LogEventBuilder ForInfoEvent([NotNull] this ILogger logger) + public static LogEventBuilder ForInfoEvent(this ILogger logger) { return new LogEventBuilder(logger, LogLevel.Info); } @@ -94,7 +94,7 @@ public static LogEventBuilder ForInfoEvent([NotNull] this ILogger logger) ///
/// The logger to write the log event to. /// for chaining calls. - public static LogEventBuilder ForWarnEvent([NotNull] this ILogger logger) + public static LogEventBuilder ForWarnEvent(this ILogger logger) { return new LogEventBuilder(logger, LogLevel.Warn); } @@ -104,7 +104,7 @@ public static LogEventBuilder ForWarnEvent([NotNull] this ILogger logger) ///
/// The logger to write the log event to. /// for chaining calls. - public static LogEventBuilder ForErrorEvent([NotNull] this ILogger logger) + public static LogEventBuilder ForErrorEvent(this ILogger logger) { return new LogEventBuilder(logger, LogLevel.Error); } @@ -114,7 +114,7 @@ public static LogEventBuilder ForErrorEvent([NotNull] this ILogger logger) ///
/// The logger to write the log event to. /// for chaining calls. - public static LogEventBuilder ForFatalEvent([NotNull] this ILogger logger) + public static LogEventBuilder ForFatalEvent(this ILogger logger) { return new LogEventBuilder(logger, LogLevel.Fatal); } @@ -126,7 +126,7 @@ public static LogEventBuilder ForFatalEvent([NotNull] this ILogger logger) /// The exception information of the logging event. /// The for the log event. Defaults to when not specified. /// for chaining calls. - public static LogEventBuilder ForExceptionEvent([NotNull] this ILogger logger, Exception? exception, LogLevel? logLevel = null) + public static LogEventBuilder ForExceptionEvent(this ILogger logger, Exception? exception, LogLevel? logLevel = null) { return ForLogEvent(logger, logLevel ?? LogLevel.Error).Exception(exception); } @@ -143,7 +143,7 @@ public static LogEventBuilder ForExceptionEvent([NotNull] this ILogger logger, E /// A logger implementation that will handle the message. /// The value to be written. [Conditional("DEBUG")] - public static void ConditionalDebug([NotNull] this ILogger logger, T? value) + public static void ConditionalDebug(this ILogger logger, T? value) { logger.Debug(value); } @@ -156,7 +156,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, T? value) /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. [Conditional("DEBUG")] - public static void ConditionalDebug([NotNull] this ILogger logger, IFormatProvider? formatProvider, T? value) + public static void ConditionalDebug(this ILogger logger, IFormatProvider? formatProvider, T? value) { logger.Debug(formatProvider, value); } @@ -167,7 +167,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, IFormatPro /// A logger implementation that will handle the message. /// A function returning message to be written. Function is not evaluated if logging is not enabled. [Conditional("DEBUG")] - public static void ConditionalDebug([NotNull] this ILogger logger, LogMessageGenerator messageFunc) + public static void ConditionalDebug(this ILogger logger, LogMessageGenerator messageFunc) { logger.Debug(messageFunc); } @@ -181,7 +181,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, LogMessageGen /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) + public static void ConditionalDebug(this ILogger logger, Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Debug(exception, message, args); } @@ -196,7 +196,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, Exception? ex /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) + public static void ConditionalDebug(this ILogger logger, Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Debug(exception, formatProvider, message, args); } @@ -207,7 +207,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, Exception? ex /// A logger implementation that will handle the message. /// Log message. [Conditional("DEBUG")] - public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)] string message) + public static void ConditionalDebug(this ILogger logger, [Localizable(false)] string message) { logger.Debug(message); } @@ -220,7 +220,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable( /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) + public static void ConditionalDebug(this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Debug(message, args); } @@ -234,7 +234,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable( /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) + public static void ConditionalDebug(this ILogger logger, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Debug(formatProvider, message, args); } @@ -248,7 +248,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, IFormatProvid /// The argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) + public static void ConditionalDebug(this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (logger.IsDebugEnabled) { @@ -267,7 +267,7 @@ public static void ConditionalDebug([NotNull] this ILogger logger, [L /// The second argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) + public static void ConditionalDebug(this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (logger.IsDebugEnabled) { @@ -288,7 +288,7 @@ public static void ConditionalDebug([NotNull] this ILogg /// The third argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalDebug([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) + public static void ConditionalDebug(this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (logger.IsDebugEnabled) { @@ -310,7 +310,7 @@ public static void ConditionalDebug([NotNull /// A logger implementation that will handle the message. /// The value to be written. [Conditional("DEBUG")] - public static void ConditionalTrace([NotNull] this ILogger logger, T? value) + public static void ConditionalTrace(this ILogger logger, T? value) { logger.Trace(value); } @@ -323,7 +323,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, T? value) /// An IFormatProvider that supplies culture-specific formatting information. /// The value to be written. [Conditional("DEBUG")] - public static void ConditionalTrace([NotNull] this ILogger logger, IFormatProvider? formatProvider, T? value) + public static void ConditionalTrace(this ILogger logger, IFormatProvider? formatProvider, T? value) { logger.Trace(formatProvider, value); } @@ -334,7 +334,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, IFormatPro /// A logger implementation that will handle the message. /// A function returning message to be written. Function is not evaluated if logging is not enabled. [Conditional("DEBUG")] - public static void ConditionalTrace([NotNull] this ILogger logger, LogMessageGenerator messageFunc) + public static void ConditionalTrace(this ILogger logger, LogMessageGenerator messageFunc) { logger.Trace(messageFunc); } @@ -348,7 +348,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, LogMessageGen /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) + public static void ConditionalTrace(this ILogger logger, Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Trace(exception, message, args); } @@ -363,7 +363,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, Exception? ex /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) + public static void ConditionalTrace(this ILogger logger, Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Trace(exception, formatProvider, message, args); } @@ -374,7 +374,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, Exception? ex /// A logger implementation that will handle the message. /// Log message. [Conditional("DEBUG")] - public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)] string message) + public static void ConditionalTrace(this ILogger logger, [Localizable(false)] string message) { logger.Trace(message); } @@ -387,7 +387,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable( /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) + public static void ConditionalTrace(this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Trace(message, args); } @@ -401,7 +401,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable( /// Arguments to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) + public static void ConditionalTrace(this ILogger logger, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { logger.Trace(formatProvider, message, args); } @@ -415,7 +415,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, IFormatProvid /// The argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) + public static void ConditionalTrace(this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { if (logger.IsTraceEnabled) { @@ -434,7 +434,7 @@ public static void ConditionalTrace([NotNull] this ILogger logger, [L /// The second argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) + public static void ConditionalTrace(this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { if (logger.IsTraceEnabled) { @@ -455,7 +455,7 @@ public static void ConditionalTrace([NotNull] this ILogg /// The third argument to format. [Conditional("DEBUG")] [MessageTemplateFormatMethod("message")] - public static void ConditionalTrace([NotNull] this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) + public static void ConditionalTrace(this ILogger logger, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { if (logger.IsTraceEnabled) { @@ -472,7 +472,7 @@ public static void ConditionalTrace([NotNull /// The log level. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Log([NotNull] this ILogger logger, LogLevel level, Exception? exception, LogMessageGenerator messageFunc) + public static void Log(this ILogger logger, LogLevel level, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsEnabled(level)) { @@ -486,7 +486,7 @@ public static void Log([NotNull] this ILogger logger, LogLevel level, Exception? /// A logger implementation that will handle the message. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Trace([NotNull] this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) + public static void Trace(this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsTraceEnabled) { @@ -502,7 +502,7 @@ public static void Trace([NotNull] this ILogger logger, Exception? exception, Lo /// A logger implementation that will handle the message. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Debug([NotNull] this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) + public static void Debug(this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsDebugEnabled) { @@ -518,7 +518,7 @@ public static void Debug([NotNull] this ILogger logger, Exception? exception, Lo /// A logger implementation that will handle the message. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Info([NotNull] this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) + public static void Info(this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsInfoEnabled) { @@ -534,7 +534,7 @@ public static void Info([NotNull] this ILogger logger, Exception? exception, Log /// A logger implementation that will handle the message. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Warn([NotNull] this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) + public static void Warn(this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsWarnEnabled) { @@ -550,7 +550,7 @@ public static void Warn([NotNull] this ILogger logger, Exception? exception, Log /// A logger implementation that will handle the message. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Error([NotNull] this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) + public static void Error(this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsErrorEnabled) { @@ -566,7 +566,7 @@ public static void Error([NotNull] this ILogger logger, Exception? exception, Lo /// A logger implementation that will handle the message. /// An exception to be logged. /// A function returning message to be written. Function is not evaluated if logging is not enabled. - public static void Fatal([NotNull] this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) + public static void Fatal(this ILogger logger, Exception? exception, LogMessageGenerator messageFunc) { if (logger.IsFatalEnabled) { diff --git a/src/NLog/Internal/CollectionExtensions.cs b/src/NLog/Internal/CollectionExtensions.cs index 61e00798de..15442d35b2 100644 --- a/src/NLog/Internal/CollectionExtensions.cs +++ b/src/NLog/Internal/CollectionExtensions.cs @@ -35,7 +35,6 @@ namespace NLog.Internal { using System; using System.Collections.Generic; - using JetBrains.Annotations; internal static class CollectionExtensions { @@ -43,8 +42,7 @@ internal static class CollectionExtensions /// Memory optimized filtering ///
/// Passing state too avoid delegate capture and memory-allocations. - [NotNull] - public static IList Filter([NotNull] this IList items, TState state, Func filter) + public static IList Filter(this IList items, TState state, Func filter) { var hasIgnoredLogEvents = false; IList? filterLogEvents = null; diff --git a/src/NLog/Internal/LogMessageTemplateFormatter.cs b/src/NLog/Internal/LogMessageTemplateFormatter.cs index 7e24d07485..437176fba6 100644 --- a/src/NLog/Internal/LogMessageTemplateFormatter.cs +++ b/src/NLog/Internal/LogMessageTemplateFormatter.cs @@ -37,7 +37,6 @@ namespace NLog.Internal using System.Collections.Generic; using System.Globalization; using System.Text; - using JetBrains.Annotations; using MessageTemplates; using NLog.Config; @@ -62,7 +61,7 @@ internal sealed class LogMessageTemplateFormatter : ILogMessageFormatter /// When true: Do not fallback to StringBuilder.Format for positional templates /// /// - public LogMessageTemplateFormatter([NotNull] LogFactory logFactory, bool forceMessageTemplateRenderer, bool singleTargetOnly) + public LogMessageTemplateFormatter(LogFactory logFactory, bool forceMessageTemplateRenderer, bool singleTargetOnly) { _logFactory = logFactory; _forceMessageTemplateRenderer = forceMessageTemplateRenderer; diff --git a/src/NLog/Internal/ReflectionHelpers.cs b/src/NLog/Internal/ReflectionHelpers.cs index 9dcf5c7379..ffcf88e432 100644 --- a/src/NLog/Internal/ReflectionHelpers.cs +++ b/src/NLog/Internal/ReflectionHelpers.cs @@ -38,7 +38,6 @@ namespace NLog.Internal using System.Linq; using System.Linq.Expressions; using System.Reflection; - using JetBrains.Annotations; /// /// Reflection helpers. @@ -159,20 +158,17 @@ private static UnaryExpression CreateParameterExpression(ParameterInfo parameter return valueCast; } - [CanBeNull] public static TAttr? GetFirstCustomAttribute(this Type type) where TAttr : Attribute { return Attribute.GetCustomAttributes(type, typeof(TAttr)).FirstOrDefault() as TAttr; } - [CanBeNull] public static TAttr? GetFirstCustomAttribute(this PropertyInfo info) where TAttr : Attribute { return Attribute.GetCustomAttributes(info, typeof(TAttr)).FirstOrDefault() as TAttr; } - [CanBeNull] public static TAttr? GetFirstCustomAttribute(this Assembly assembly) where TAttr : Attribute { diff --git a/src/NLog/Internal/StringHelpers.cs b/src/NLog/Internal/StringHelpers.cs index 1fb88efb61..73ca308b62 100644 --- a/src/NLog/Internal/StringHelpers.cs +++ b/src/NLog/Internal/StringHelpers.cs @@ -85,7 +85,7 @@ internal static string[] SplitAndTrimTokens(this string value, char delimiter) /// Replace string with /// /// The same reference of nothing has been replaced. - public static string Replace([NotNull] string str, [NotNull] string oldValue, string newValue, StringComparison comparison, bool wholeWords = false) + public static string Replace(string str, string oldValue, string newValue, StringComparison comparison, bool wholeWords = false) { Guard.ThrowIfNull(str); diff --git a/src/NLog/Layouts/Layout.cs b/src/NLog/Layouts/Layout.cs index a0c8c518d7..ce07e633c5 100644 --- a/src/NLog/Layouts/Layout.cs +++ b/src/NLog/Layouts/Layout.cs @@ -39,7 +39,6 @@ namespace NLog.Layouts using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; - using JetBrains.Annotations; using NLog.Common; using NLog.Config; using NLog.Internal; @@ -83,7 +82,6 @@ public abstract class Layout : ISupportsInitialize, IRenderable /// /// Gets the logging configuration this target is part of. /// - [CanBeNull] protected internal LoggingConfiguration? LoggingConfiguration { get; private set; } /// diff --git a/src/NLog/Layouts/Typed/Layout.cs b/src/NLog/Layouts/Typed/Layout.cs index 997195c160..4fcf9e1209 100644 --- a/src/NLog/Layouts/Typed/Layout.cs +++ b/src/NLog/Layouts/Typed/Layout.cs @@ -37,7 +37,6 @@ namespace NLog.Layouts using System.ComponentModel; using System.Globalization; using System.Text; - using JetBrains.Annotations; using NLog.Common; using NLog.Config; using NLog.Internal; @@ -149,7 +148,7 @@ private Layout(Func layoutMethod, LayoutRenderOptions options) return RenderTypedValue(logEvent, null, defaultValue); } - internal T? RenderTypedValue(LogEventInfo logEvent, [CanBeNull] StringBuilder? stringBuilder, T? defaultValue) + internal T? RenderTypedValue(LogEventInfo logEvent, StringBuilder? stringBuilder, T? defaultValue) { if (IsFixed) return _fixedValue; @@ -173,7 +172,7 @@ private Layout(Func layoutMethod, LayoutRenderOptions options) return defaultValue; } - private object? RenderObjectValue(LogEventInfo logEvent, [CanBeNull] StringBuilder? stringBuilder) + private object? RenderObjectValue(LogEventInfo logEvent, StringBuilder? stringBuilder) { if (logEvent is null) return null; @@ -238,7 +237,7 @@ public static Layout FromMethod(Func layoutMethod, LayoutRen return new Layout(layoutMethod, options); } - private void PrecalculateInnerLayout(LogEventInfo logEvent, [CanBeNull] StringBuilder? target) + private void PrecalculateInnerLayout(LogEventInfo logEvent, StringBuilder? target) { if (IsFixed || (_layoutValue.ThreadAgnostic && !_layoutValue.ThreadAgnosticImmutable)) return; diff --git a/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs b/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs index bfe3650d8c..bd2c4e064b 100644 --- a/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs +++ b/src/NLog/Layouts/Typed/LayoutTypedExtensions.cs @@ -33,7 +33,6 @@ namespace NLog { - using JetBrains.Annotations; using NLog.Layouts; using NLog.Targets; @@ -51,7 +50,7 @@ public static class LayoutTypedExtensions /// The logevent info. /// Fallback value when no value available /// Result value when available, else fallback to defaultValue - public static T? RenderValue([CanBeNull] this Layout? layout, LogEventInfo logEvent, T? defaultValue = default(T)) + public static T? RenderValue(this Layout? layout, LogEventInfo logEvent, T? defaultValue = default(T)) { return layout is null ? defaultValue : layout.RenderTypedValue(logEvent, defaultValue); } diff --git a/src/NLog/LogEventBuilder.cs b/src/NLog/LogEventBuilder.cs index 016d28d2ed..9ed3e56108 100644 --- a/src/NLog/LogEventBuilder.cs +++ b/src/NLog/LogEventBuilder.cs @@ -53,7 +53,7 @@ public struct LogEventBuilder /// Initializes a new instance of the class. /// /// The to send the log event. - public LogEventBuilder([NotNull] ILogger logger) + public LogEventBuilder(ILogger logger) { _logger = Guard.ThrowIfNull(logger); _logEvent = new LogEventInfo() { LoggerName = _logger.Name }; @@ -64,7 +64,7 @@ public LogEventBuilder([NotNull] ILogger logger) ///
/// The to send the log event. /// The log level. LogEvent is only created when is enabled for - public LogEventBuilder([NotNull] ILogger logger, [NotNull] LogLevel logLevel) + public LogEventBuilder(ILogger logger, LogLevel logLevel) { _logger = Guard.ThrowIfNull(logger); @@ -83,13 +83,11 @@ public LogEventBuilder([NotNull] ILogger logger, [NotNull] LogLevel logLevel) /// /// The logger to write the log event to /// - [NotNull] public ILogger Logger => _logger; /// /// Logging event that will be written /// - [CanBeNull] public LogEventInfo? LogEvent => _logEvent is null ? null : ResolveLogEvent(_logEvent); /// @@ -97,7 +95,7 @@ public LogEventBuilder([NotNull] ILogger logger, [NotNull] LogLevel logLevel) /// /// The name of the context property. /// The value of the context property. - public LogEventBuilder Property([NotNull] string propertyName, T? propertyValue) + public LogEventBuilder Property(string propertyName, T? propertyValue) { Guard.ThrowIfNull(propertyName); @@ -112,7 +110,7 @@ public LogEventBuilder Property([NotNull] string propertyName, T? propertyVal /// Sets multiple per-event context properties on the logging event. ///
/// The properties to set. - public LogEventBuilder Properties([NotNull] IEnumerable> properties) + public LogEventBuilder Properties(IEnumerable> properties) { Guard.ThrowIfNull(properties); diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index 6a5b8561c5..ea8502b164 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -212,9 +212,8 @@ public int SequenceID ///
public LogLevel Level { get; set; } - [CanBeNull] internal CallSiteInformation? CallSiteInformation { get; private set; } + internal CallSiteInformation? CallSiteInformation { get; private set; } - [NotNull] internal CallSiteInformation GetCallSiteInformationInternal() { return CallSiteInformation ?? (CallSiteInformation = new CallSiteInformation()); } /// @@ -709,7 +708,7 @@ private static bool HasImmutableProperties(PropertiesDictionary properties) return true; } - internal void SetMessageFormatter([NotNull] LogMessageFormatter messageFormatter, [CanBeNull] LogMessageFormatter? singleTargetMessageFormatter) + internal void SetMessageFormatter(LogMessageFormatter messageFormatter, LogMessageFormatter? singleTargetMessageFormatter) { bool hasCustomMessageFormatter = _messageFormatter != null; if (!hasCustomMessageFormatter) diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 415948cfa0..78db73b48a 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -219,6 +219,7 @@ public bool AutoShutdown /// /// Setter will re-configure all -objects, so no need to also call /// + [CanBeNull] public LoggingConfiguration? Configuration { get @@ -339,7 +340,6 @@ public LogLevel GlobalThreshold /// /// Specific culture info or null to use /// - [CanBeNull] public CultureInfo? DefaultCultureInfo { get => _config is null ? _defaultCultureInfo : _config.DefaultCultureInfo; diff --git a/src/NLog/LogManager.cs b/src/NLog/LogManager.cs index 545b61a74a..58e98bc089 100644 --- a/src/NLog/LogManager.cs +++ b/src/NLog/LogManager.cs @@ -38,6 +38,7 @@ namespace NLog using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; + using JetBrains.Annotations; using NLog.Common; using NLog.Config; using NLog.Internal; @@ -126,6 +127,7 @@ public static bool AutoShutdown /// /// Setter will re-configure all -objects, so no need to also call /// + [CanBeNull] public static LoggingConfiguration? Configuration { get => LogFactory.Configuration; diff --git a/src/NLog/Logger.cs b/src/NLog/Logger.cs index 46becde5fd..9a776052d0 100644 --- a/src/NLog/Logger.cs +++ b/src/NLog/Logger.cs @@ -872,7 +872,7 @@ private void WriteToTargets(LogLevel level, Exception? ex, IFormatProvider? form } } - private void WriteLogEventToTargets([NotNull] LogEventInfo logEvent, [NotNull] ITargetWithFilterChain targetsForLevel) + private void WriteLogEventToTargets(LogEventInfo logEvent, ITargetWithFilterChain targetsForLevel) { try { @@ -892,7 +892,7 @@ private void WriteLogEventToTargets([NotNull] LogEventInfo logEvent, [NotNull] I } } - private void WriteLogEventToTargets(Type wrapperType, [NotNull] LogEventInfo logEvent, [NotNull] ITargetWithFilterChain targetsForLevel) + private void WriteLogEventToTargets(Type wrapperType, LogEventInfo logEvent, ITargetWithFilterChain targetsForLevel) { try { diff --git a/src/NLog/LoggerImpl.cs b/src/NLog/LoggerImpl.cs index d28ea2183f..5a585b3ef8 100644 --- a/src/NLog/LoggerImpl.cs +++ b/src/NLog/LoggerImpl.cs @@ -36,7 +36,6 @@ namespace NLog using System; using System.Collections.Generic; using System.Diagnostics; - using JetBrains.Annotations; using NLog.Common; using NLog.Config; using NLog.Filters; @@ -49,7 +48,7 @@ internal static class LoggerImpl { private const int StackTraceSkipMethods = 0; - internal static void Write([NotNull] Type loggerType, [NotNull] TargetWithFilterChain targetsForLevel, LogEventInfo logEvent, LogFactory logFactory) + internal static void Write(Type loggerType, TargetWithFilterChain targetsForLevel, LogEventInfo logEvent, LogFactory logFactory) { logEvent.SetMessageFormatter(logFactory.ActiveMessageFormatter, targetsForLevel.NextInChain is null ? logFactory.SingleTargetMessageFormatter : null); diff --git a/src/NLog/MessageTemplates/MessageTemplateParameter.cs b/src/NLog/MessageTemplates/MessageTemplateParameter.cs index b682e35740..29e306d054 100644 --- a/src/NLog/MessageTemplates/MessageTemplateParameter.cs +++ b/src/NLog/MessageTemplates/MessageTemplateParameter.cs @@ -33,7 +33,6 @@ namespace NLog.MessageTemplates { - using JetBrains.Annotations; using NLog.Internal; /// @@ -45,20 +44,17 @@ public struct MessageTemplateParameter /// Parameter Name extracted from /// This is everything between "{" and the first of ",:}". /// - [NotNull] public string Name { get; } /// /// Parameter Value extracted from the -array /// - [CanBeNull] public object? Value { get; } /// /// Format to render the parameter. /// This is everything between ":" and the first unescaped "}" /// - [CanBeNull] public string? Format { get; } /// @@ -102,7 +98,7 @@ public int? PositionalIndex /// Parameter Name /// Parameter Value /// Parameter Format - internal MessageTemplateParameter([NotNull] string name, object? value, string? format) + internal MessageTemplateParameter(string name, object? value, string? format) { Name = Guard.ThrowIfNull(name); Value = value; @@ -117,7 +113,7 @@ internal MessageTemplateParameter([NotNull] string name, object? value, string? /// Parameter Value /// Parameter Format /// Parameter CaptureType - public MessageTemplateParameter([NotNull] string name, object? value, string? format, CaptureType captureType) + public MessageTemplateParameter(string name, object? value, string? format, CaptureType captureType) { Name = Guard.ThrowIfNull(name); Value = value; diff --git a/src/NLog/MessageTemplates/ValueFormatter.cs b/src/NLog/MessageTemplates/ValueFormatter.cs index e643733447..87e39c6637 100644 --- a/src/NLog/MessageTemplates/ValueFormatter.cs +++ b/src/NLog/MessageTemplates/ValueFormatter.cs @@ -38,7 +38,6 @@ namespace NLog.MessageTemplates using System.Collections.Generic; using System.Globalization; using System.Text; - using JetBrains.Annotations; using NLog.Config; using NLog.Internal; @@ -88,7 +87,7 @@ public bool SerializeObject(object? value, StringBuilder builder) } } - public ValueFormatter([NotNull] IServiceProvider serviceProvider, bool legacyStringQuotes) + public ValueFormatter(IServiceProvider serviceProvider, bool legacyStringQuotes) { _serviceProvider = serviceProvider; _legacyStringQuotes = legacyStringQuotes; diff --git a/src/NLog/Targets/LineEndingMode.cs b/src/NLog/Targets/LineEndingMode.cs index e96227bfc1..1d8e8625cf 100644 --- a/src/NLog/Targets/LineEndingMode.cs +++ b/src/NLog/Targets/LineEndingMode.cs @@ -34,7 +34,6 @@ using System; using System.ComponentModel; using System.Globalization; -using JetBrains.Annotations; using NLog.Internal; namespace NLog.Targets @@ -115,7 +114,7 @@ private LineEndingMode(string name, string newLineCharacters) /// /// The value, that corresponds to the . /// There is no line ending mode with the specified name. - public static LineEndingMode FromString([NotNull] string name) + public static LineEndingMode FromString(string name) { Guard.ThrowIfNull(name); diff --git a/src/NLog/Targets/Target.cs b/src/NLog/Targets/Target.cs index 066959d450..f8117aad0d 100644 --- a/src/NLog/Targets/Target.cs +++ b/src/NLog/Targets/Target.cs @@ -38,7 +38,6 @@ namespace NLog.Targets using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; - using JetBrains.Annotations; using NLog.Common; using NLog.Config; using NLog.Internal; @@ -698,7 +697,7 @@ protected void MergeEventProperties(LogEventInfo logEvent) /// The layout. /// The logevent info. /// String representing log event. - protected string RenderLogEvent([CanBeNull] Layout? layout, LogEventInfo logEvent) + protected string RenderLogEvent(Layout? layout, LogEventInfo logEvent) { if (layout is null || logEvent is null) return string.Empty; // Signal that input was wrong @@ -729,7 +728,7 @@ protected string RenderLogEvent([CanBeNull] Layout? layout, LogEventInfo logEven /// The logevent info. /// Fallback value when no value available /// Result value when available, else fallback to defaultValue - protected T? RenderLogEvent([CanBeNull] Layout? layout, LogEventInfo logEvent, T? defaultValue = default(T)) + protected T? RenderLogEvent(Layout? layout, LogEventInfo logEvent, T? defaultValue = default(T)) { if (layout is null) return defaultValue; diff --git a/tests/NLog.UnitTests/Config/ServiceRepositoryTests.cs b/tests/NLog.UnitTests/Config/ServiceRepositoryTests.cs index 6374c22dd5..17c7a12f94 100644 --- a/tests/NLog.UnitTests/Config/ServiceRepositoryTests.cs +++ b/tests/NLog.UnitTests/Config/ServiceRepositoryTests.cs @@ -36,9 +36,7 @@ namespace NLog.UnitTests.Config { using System; using System.Text; - using JetBrains.Annotations; using NLog.Config; - using NLog.Internal; using NLog.Targets; using Xunit; diff --git a/tests/NLog.UnitTests/LogManagerTests.cs b/tests/NLog.UnitTests/LogManagerTests.cs index 73b0cd3ca4..4bc7abd460 100644 --- a/tests/NLog.UnitTests/LogManagerTests.cs +++ b/tests/NLog.UnitTests/LogManagerTests.cs @@ -31,15 +31,11 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using JetBrains.Annotations; - namespace NLog.UnitTests { using System; - using System.IO; using System.Linq; using System.Threading.Tasks; - using NLog.Common; using NLog.Config; using NLog.Targets; using Xunit; @@ -242,13 +238,11 @@ public void GivenCurrentClass_WhenGetCurrentClassLogger_ThenLoggerShouldBeCurren private static class ImAStaticClass { - [UsedImplicitly] public static readonly Logger Logger = LogManager.GetCurrentClassLogger(); static ImAStaticClass() { } public static void DummyToInvokeInitializers() { } - } [Fact] From 9564763e9f04385afdd87dcc70acf0fac58dda18 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 25 May 2025 09:28:30 +0200 Subject: [PATCH 130/224] Fixing code smells reported by Sonar (#5837) --- .../OutputDebugStringTarget.cs | 2 +- src/NLog/Config/Factory.cs | 44 +++++++++---------- src/NLog/Config/LoggingConfigurationParser.cs | 6 +-- src/NLog/ILogger.cs | 20 ++++----- .../EnvironmentUserLayoutRenderer.cs | 2 +- .../LoggerNameLayoutRenderer.cs | 2 +- 6 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/NLog.OutputDebugString/OutputDebugStringTarget.cs b/src/NLog.OutputDebugString/OutputDebugStringTarget.cs index 55748a9648..bd99acd3bb 100644 --- a/src/NLog.OutputDebugString/OutputDebugStringTarget.cs +++ b/src/NLog.OutputDebugString/OutputDebugStringTarget.cs @@ -106,7 +106,7 @@ protected override void Write(LogEventInfo logEvent) WriteDebugString(RenderLogEvent(Layout, logEvent)); } - private void WriteDebugString(string message) + private static void WriteDebugString(string message) { NativeMethods.OutputDebugString(message); } diff --git a/src/NLog/Config/Factory.cs b/src/NLog/Config/Factory.cs index 8891cc44f6..8a1f328dab 100644 --- a/src/NLog/Config/Factory.cs +++ b/src/NLog/Config/Factory.cs @@ -84,6 +84,28 @@ public void Initialize(Action itemRegistration) public bool CheckTypeAliasExists(string typeAlias) => _items.ContainsKey(FactoryExtensions.NormalizeName(typeAlias)); + public void RegisterType<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] TType>(string typeAlias) where TType : TBaseType, new() + { + typeAlias = FactoryExtensions.NormalizeName(typeAlias); + + lock (ConfigurationItemFactory.SyncRoot) + { + _parentFactory.RegisterTypeProperties(() => new TType()); + _items[typeAlias] = () => new TType(); + } + } + + public void RegisterType<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] TType>(string typeAlias, Func itemCreator) where TType : TBaseType + { + typeAlias = FactoryExtensions.NormalizeName(typeAlias); + + lock (ConfigurationItemFactory.SyncRoot) + { + _parentFactory.RegisterTypeProperties(() => itemCreator()); + _items[typeAlias] = () => itemCreator(); + } + } + /// /// Registers the type. /// @@ -174,28 +196,6 @@ private void RegisterDefinition([DynamicallyAccessedMembers(DynamicallyAccessedM } } - public void RegisterType<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] TType>(string typeAlias) where TType : TBaseType, new() - { - typeAlias = FactoryExtensions.NormalizeName(typeAlias); - - lock (ConfigurationItemFactory.SyncRoot) - { - _parentFactory.RegisterTypeProperties(() => new TType()); - _items[typeAlias] = () => new TType(); - } - } - - public void RegisterType<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicProperties)] TType>(string typeAlias, Func itemCreator) where TType : TBaseType - { - typeAlias = FactoryExtensions.NormalizeName(typeAlias); - - lock (ConfigurationItemFactory.SyncRoot) - { - _parentFactory.RegisterTypeProperties(() => itemCreator()); - _items[typeAlias] = () => itemCreator(); - } - } - private bool TryGetItemFactory(string typeAlias, out Func? itemFactory) { lock (ConfigurationItemFactory.SyncRoot) diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index 756561c485..be03a28584 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -1095,14 +1095,10 @@ private void SetPropertyValueFromString(T targetObject, string propertyName, try { if (targetObject is null) - { throw new NLogConfigurationException($"'{typeof(T).Name}' is null, and cannot assign property '{propertyName}'='{propertyValue}'"); - } if (!PropertyHelper.TryGetPropertyInfo(ConfigurationItemFactory.Default, targetObject, propertyName, out var propertyInfo) || propertyInfo is null) - { throw new NLogConfigurationException($"'{targetObject.GetType()?.Name}' cannot assign unknown property '{propertyName}'='{propertyValue}'"); - } var propertyValueExpanded = ExpandSimpleVariables(propertyValue, out var matchingVariableName); if (matchingVariableName != null && TryLookupDynamicVariable(matchingVariableName, out var matchingLayout)) @@ -1126,7 +1122,7 @@ private void SetPropertyValueFromString(T targetObject, string propertyName, if (ex.MustBeRethrownImmediately()) throw; - var configException = new NLogConfigurationException($"'{targetObject?.GetType()?.Name}' cannot assign property '{propertyName}'='{propertyValue}' in section '{element.Name}'. Error: {ex.Message}", ex); + var configException = new NLogConfigurationException($"'{targetObject.GetType()?.Name}' cannot assign property '{propertyName}'='{propertyValue}' in section '{element.Name}'. Error: {ex.Message}", ex); if (MustThrowConfigException(configException)) throw; } diff --git a/src/NLog/ILogger.cs b/src/NLog/ILogger.cs index 8f8cae04c4..123047642c 100644 --- a/src/NLog/ILogger.cs +++ b/src/NLog/ILogger.cs @@ -126,16 +126,6 @@ bool IsFatalEnabled /// A function returning message to be written. Function is not evaluated if logging is not enabled. void Trace(LogMessageGenerator messageFunc); - /// - /// Obsolete and replaced by - Writes the diagnostic message and exception at the Trace level. - /// - /// A to be written. - /// An exception to be logged. - /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. - [Obsolete("Use Trace(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] - [EditorBrowsable(EditorBrowsableState.Never)] - void TraceException([Localizable(false)] string message, Exception? exception); - /// /// Writes the diagnostic message and exception at the Trace level. /// @@ -264,6 +254,16 @@ bool IsFatalEnabled [MessageTemplateFormatMethod("message")] void Trace([Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3); + /// + /// Obsolete and replaced by - Writes the diagnostic message and exception at the Trace level. + /// + /// A to be written. + /// An exception to be logged. + /// This method was marked as obsolete before NLog 4.3.11 and it may be removed in a future release. + [Obsolete("Use Trace(Exception exception, string message) method instead. Marked obsolete with v4.3.11 (Only here because of LibLog)")] + [EditorBrowsable(EditorBrowsableState.Never)] + void TraceException([Localizable(false)] string message, Exception? exception); + #endregion #region Debug() overloads diff --git a/src/NLog/LayoutRenderers/EnvironmentUserLayoutRenderer.cs b/src/NLog/LayoutRenderers/EnvironmentUserLayoutRenderer.cs index 114392eca5..be8285dc57 100644 --- a/src/NLog/LayoutRenderers/EnvironmentUserLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/EnvironmentUserLayoutRenderer.cs @@ -102,7 +102,7 @@ string GetDomainName() return GetValueSafe(() => Environment.UserDomainName, DefaultDomain); } - private string GetValueSafe(Func getValue, string defaultValue) + private static string GetValueSafe(Func getValue, string defaultValue) { try { diff --git a/src/NLog/LayoutRenderers/LoggerNameLayoutRenderer.cs b/src/NLog/LayoutRenderers/LoggerNameLayoutRenderer.cs index 3d83967825..58ccf7b9e1 100644 --- a/src/NLog/LayoutRenderers/LoggerNameLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LoggerNameLayoutRenderer.cs @@ -106,7 +106,7 @@ string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) return logEvent.LoggerName ?? string.Empty; } - private int TryGetLastDotForShortName(LogEventInfo logEvent) + private static int TryGetLastDotForShortName(LogEventInfo logEvent) { return logEvent.LoggerName?.LastIndexOf('.') ?? -1; } From 1df06d4c94e08e418b52c222186de40f07c85fa5 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 25 May 2025 13:50:26 +0200 Subject: [PATCH 131/224] Renamed ChainsawTarget to Log4JXmlTarget (#5838) --- Test-XmlFile.ps1 | 2 +- .../Log4JXml/Simple/Example.cs | 26 ++++++++++ .../Log4JXml/Simple/Log4JXml.csproj | 48 +++++++++++++++++++ .../Log4JXml/Simple/Log4JXml.sln | 20 ++++++++ .../Configuration File/Log4JXml/NLog.config | 11 +++++ .../OutputDebugStringTarget.cs | 3 ++ .../AtomicFileTarget.cs | 4 ++ src/NLog.Targets.GZipFile/GZipFileTarget.cs | 4 ++ .../Layouts/GelfLayout.cs | 5 +- .../Layouts/Log4JXmlEventParameter.cs | 5 +- .../Layouts/SyslogLayout.cs | 4 ++ src/NLog.Targets.Network/README.md | 31 ++++++++++++ .../Targets/GelfTarget.cs | 4 ++ .../{ChainsawTarget.cs => Log4JXmlTarget.cs} | 17 +++---- .../Targets/NetworkTarget.cs | 5 -- src/NLog/Config/ConfigurationItemFactory.cs | 5 +- src/NLog/Layouts/CSV/CsvColumn.cs | 4 ++ src/NLog/Layouts/JSON/JsonAttribute.cs | 4 ++ src/NLog/Layouts/XML/XmlAttribute.cs | 4 ++ src/NLog/Layouts/XML/XmlElement.cs | 4 ++ src/NLog/Layouts/XML/XmlElementBase.cs | 4 ++ .../NetworkTargetTests.cs | 4 +- 22 files changed, 198 insertions(+), 20 deletions(-) create mode 100644 examples/targets/Configuration API/Log4JXml/Simple/Example.cs create mode 100644 examples/targets/Configuration API/Log4JXml/Simple/Log4JXml.csproj create mode 100644 examples/targets/Configuration API/Log4JXml/Simple/Log4JXml.sln create mode 100644 examples/targets/Configuration File/Log4JXml/NLog.config rename src/NLog.Targets.Network/Targets/{ChainsawTarget.cs => Log4JXmlTarget.cs} (95%) diff --git a/Test-XmlFile.ps1 b/Test-XmlFile.ps1 index bac2425902..1d79706ad4 100644 --- a/Test-XmlFile.ps1 +++ b/Test-XmlFile.ps1 @@ -54,7 +54,7 @@ function Test-XmlFile $pwd = get-location; $testFilesDir = "$pwd\examples\targets\Configuration File" $xsdFilePath = "$pwd\src\NLog\bin\Release\NLog.xsd" -$excludedTests = ("MessageBox","RichTextBox","FormControl","PerfCounter","OutputDebugString","MSMQ","Database","Mail","Network","Chainsaw","NLogViewer","WebService","Trace") +$excludedTests = ("MessageBox","RichTextBox","FormControl","Database","Log4JXml","Chainsaw","NLogViewer") # Returns true if all selected tests in examples directory are valid $ret = $true Get-ChildItem -Path $testFilesDir -Directory -Exclude $excludedTests | Get-ChildItem -Recurse -File -Filter NLog.config | % { diff --git a/examples/targets/Configuration API/Log4JXml/Simple/Example.cs b/examples/targets/Configuration API/Log4JXml/Simple/Example.cs new file mode 100644 index 0000000000..f186f828a4 --- /dev/null +++ b/examples/targets/Configuration API/Log4JXml/Simple/Example.cs @@ -0,0 +1,26 @@ +using NLog; +using NLog.Config; +using NLog.Targets; + +class Example +{ + static void Main(string[] args) + { + Log4JXmlTarget target = new Log4JXmlTarget(); + target.Address = "udp://localhost:4000"; + + LoggingConfiguration nlogConfig = new LoggingConfiguration(); + nlogConfig.AddRuleForAllLevels(target); + LogManager.Configuration = nlogConfig; + + Logger logger = LogManager.GetLogger("Example"); + logger.Trace("log message 1"); + logger.Debug("log message 2"); + logger.Info("log message 3"); + logger.Warn("log message 4"); + logger.Error("log message 5"); + logger.Fatal("log message 6"); + + LogManager.Flush(); + } +} diff --git a/examples/targets/Configuration API/Log4JXml/Simple/Log4JXml.csproj b/examples/targets/Configuration API/Log4JXml/Simple/Log4JXml.csproj new file mode 100644 index 0000000000..d0020fa664 --- /dev/null +++ b/examples/targets/Configuration API/Log4JXml/Simple/Log4JXml.csproj @@ -0,0 +1,48 @@ + + + Debug + AnyCPU + {D7B11859-2A1B-49F7-A7C7-83B72495BF31} + Exe + false + Log4JXml + + + + + + + 2.0 + + + true + full + false + .\bin\Debug\ + DEBUG;TRACE + + + pdbonly + true + .\bin\Release\ + TRACE + + + + + + + + + + + + {020354EE-5073-4BB5-9AA2-A7EADA8CAD09} + NLog + + + + + + + \ No newline at end of file diff --git a/examples/targets/Configuration API/Log4JXml/Simple/Log4JXml.sln b/examples/targets/Configuration API/Log4JXml/Simple/Log4JXml.sln new file mode 100644 index 0000000000..2d387ac9f5 --- /dev/null +++ b/examples/targets/Configuration API/Log4JXml/Simple/Log4JXml.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Log4JXml", "Log4JXml.csproj", "{D7B11859-2A1B-49F7-A7C7-83B72495BF31}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D7B11859-2A1B-49F7-A7C7-83B72495BF31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7B11859-2A1B-49F7-A7C7-83B72495BF31}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7B11859-2A1B-49F7-A7C7-83B72495BF31}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7B11859-2A1B-49F7-A7C7-83B72495BF31}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/examples/targets/Configuration File/Log4JXml/NLog.config b/examples/targets/Configuration File/Log4JXml/NLog.config new file mode 100644 index 0000000000..227e34b1fa --- /dev/null +++ b/examples/targets/Configuration File/Log4JXml/NLog.config @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/NLog.OutputDebugString/OutputDebugStringTarget.cs b/src/NLog.OutputDebugString/OutputDebugStringTarget.cs index bd99acd3bb..b5c8629156 100644 --- a/src/NLog.OutputDebugString/OutputDebugStringTarget.cs +++ b/src/NLog.OutputDebugString/OutputDebugStringTarget.cs @@ -38,6 +38,9 @@ namespace NLog.Targets /// /// Outputs log messages through the OutputDebugString() Win32 API. /// + /// + /// See NLog Wiki + /// /// Documentation on NLog Wiki /// ///

diff --git a/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs b/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs index f83220df44..7da85e1c9e 100644 --- a/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs +++ b/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs @@ -40,6 +40,10 @@ namespace NLog.Targets ///

/// Extended standard FileTarget with atomic file append for multi-process logging to the same file /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [Target("AtomFile")] [Target("AtomicFile")] public class AtomicFileTarget : FileTarget diff --git a/src/NLog.Targets.GZipFile/GZipFileTarget.cs b/src/NLog.Targets.GZipFile/GZipFileTarget.cs index a40cba051f..519d9ccfa5 100644 --- a/src/NLog.Targets.GZipFile/GZipFileTarget.cs +++ b/src/NLog.Targets.GZipFile/GZipFileTarget.cs @@ -39,6 +39,10 @@ namespace NLog.Targets /// /// Extended standard FileTarget with GZip compression as part of file-logging /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [Target("GZipFile")] public class GZipFileTarget : FileTarget { diff --git a/src/NLog.Targets.Network/Layouts/GelfLayout.cs b/src/NLog.Targets.Network/Layouts/GelfLayout.cs index 547e3eee6d..c031eff245 100644 --- a/src/NLog.Targets.Network/Layouts/GelfLayout.cs +++ b/src/NLog.Targets.Network/Layouts/GelfLayout.cs @@ -11,6 +11,10 @@ namespace NLog.Layouts /// /// GELF (Graylog Extended Log Format) is a JSON-based, structured log format for Graylog Log Management. /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki /// /// { /// "version": "1.1", @@ -56,7 +60,6 @@ public Layout GelfHostName /// /// Gets or sets the Graylog Message Full-Message-field /// - /// Will truncate when longer than 250 chars public Layout GelfFullMessage { get; set; } /// diff --git a/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs index a5efe62f63..9586298a1a 100644 --- a/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventParameter.cs @@ -34,11 +34,14 @@ namespace NLog.Layouts { using NLog.Config; - using NLog.Targets.Internal; /// /// Represents a parameter for the /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [NLogConfigurationItem] public class Log4JXmlEventParameter { diff --git a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs index fae7922124..98568c14a7 100644 --- a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs +++ b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs @@ -11,6 +11,10 @@ namespace NLog.Layouts /// /// A specialized layout that renders Syslog-formatted events in format Rfc3164 / Rfc5424 /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [Layout("SyslogLayout")] [ThreadAgnostic] [AppDomainFixedOutput] diff --git a/src/NLog.Targets.Network/README.md b/src/NLog.Targets.Network/README.md index 9408943585..81e9bd590a 100644 --- a/src/NLog.Targets.Network/README.md +++ b/src/NLog.Targets.Network/README.md @@ -6,6 +6,30 @@ If having trouble with output, then check [NLog InternalLogger](https://github.c See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Network-target) for available options and examples. +## NLog SysLog Target + +NLog Syslog Target combines the NLog NetworkTarget with NLog SyslogLayout + +If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) + +See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Syslog-target) for available options and examples. + +## NLog GELF Target + +NLog Gelf Target combines the NLog NetworkTarget with NLog GelfLayout for Graylog Extended Logging Format (GELF) + +If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) + +See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Gelf-target) for available options and examples. + +## NLog Log4JXml Target + +NLog Log4JXml Target combines the NLog NetworkTarget with NLog Log4JXmlEventLayout for NLogViewer / Chainsaw. + +If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) + +See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Chainsaw-target) for available options and examples. + ## Register Extension NLog will only recognize type-alias `Network` when loading from `NLog.config`-file, if having added extension to `NLog.config`-file: @@ -21,5 +45,12 @@ Alternative register from code using [fluent configuration API](https://github.c ```csharp LogManager.Setup().SetupExtensions(ext => { ext.RegisterTarget(); + ext.RegisterTarget(); + ext.RegisterTarget(); + ext.RegisterTarget(); + ext.RegisterTarget(); + ext.RegisterLayout(); + ext.RegisterLayout(); + ext.RegisterLayout(); }); ``` diff --git a/src/NLog.Targets.Network/Targets/GelfTarget.cs b/src/NLog.Targets.Network/Targets/GelfTarget.cs index 21c97bd83b..19d424a8c2 100644 --- a/src/NLog.Targets.Network/Targets/GelfTarget.cs +++ b/src/NLog.Targets.Network/Targets/GelfTarget.cs @@ -40,6 +40,10 @@ namespace NLog.Targets /// /// Sends log messages to GrayLog server using either TCP or UDP with GELF-format /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [Target("Gelf")] public class GelfTarget : NetworkTarget { diff --git a/src/NLog.Targets.Network/Targets/ChainsawTarget.cs b/src/NLog.Targets.Network/Targets/Log4JXmlTarget.cs similarity index 95% rename from src/NLog.Targets.Network/Targets/ChainsawTarget.cs rename to src/NLog.Targets.Network/Targets/Log4JXmlTarget.cs index 76c8012014..a49b3adb31 100644 --- a/src/NLog.Targets.Network/Targets/ChainsawTarget.cs +++ b/src/NLog.Targets.Network/Targets/Log4JXmlTarget.cs @@ -40,7 +40,7 @@ namespace NLog.Targets using NLog.Layouts; /// - /// Sends log messages to the remote instance of Chainsaw application from log4j. + /// Sends log messages to the remote instance of Chainsaw / NLogViewer application using Log4J Xml /// /// /// See NLog Wiki @@ -51,36 +51,37 @@ namespace NLog.Targets /// To set up the target in the configuration file, /// use the following syntax: ///

- /// + /// ///

/// To set up the log target programmatically use code like this: ///

- /// + /// /// + [Target("Log4JXml")] [Target("Chainsaw")] [Target("NLogViewer")] - public class ChainsawTarget : NetworkTarget + public class Log4JXmlTarget : NetworkTarget { private readonly Log4JXmlEventLayout _log4JLayout = new Log4JXmlEventLayout(); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// - public ChainsawTarget() + public Log4JXmlTarget() { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// /// Name of the target. - public ChainsawTarget(string name) : this() + public Log4JXmlTarget(string name) : this() { Name = name; } diff --git a/src/NLog.Targets.Network/Targets/NetworkTarget.cs b/src/NLog.Targets.Network/Targets/NetworkTarget.cs index dde812dddd..ed4ead7691 100644 --- a/src/NLog.Targets.Network/Targets/NetworkTarget.cs +++ b/src/NLog.Targets.Network/Targets/NetworkTarget.cs @@ -66,11 +66,6 @@ namespace NLog.Targets /// demonstrates the NetCat tool receiving log messages from Network target. ///

/// - ///

- /// There are two specialized versions of the Network target: Chainsaw - /// and NLogViewer which write to instances of Chainsaw log4j viewer - /// or NLogViewer application respectively. - ///

/// [Target("Network")] public class NetworkTarget : TargetWithLayout diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index ff92edb6f5..577c14f642 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -491,8 +491,9 @@ private void RegisterAllTargets(bool skipCheckExists) SafeRegisterNamedType(_targets, "logreceiverservice", "NLog.Targets.LogReceiverWebServiceTarget, NLog.Wcf", skipCheckExists); SafeRegisterNamedType(_targets, "outputdebugstring", "NLog.Targets.OutputDebugStringTarget, NLog.OutputDebugString", skipCheckExists); SafeRegisterNamedType(_targets, "network", "NLog.Targets.NetworkTarget, NLog.Targets.Network", skipCheckExists); - SafeRegisterNamedType(_targets, "chainsaw", "NLog.Targets.ChainsawTarget, NLog.Targets.Network", skipCheckExists); - SafeRegisterNamedType(_targets, "nlogviewer", "NLog.Targets.ChainsawTarget, NLog.Targets.Network", skipCheckExists); + SafeRegisterNamedType(_targets, "log4jxml", "NLog.Targets.Log4JXmlTarget, NLog.Targets.Network", skipCheckExists); + SafeRegisterNamedType(_targets, "chainsaw", "NLog.Targets.Log4JXmlTarget, NLog.Targets.Network", skipCheckExists); + SafeRegisterNamedType(_targets, "nlogviewer", "NLog.Targets.Log4JXmlTarget, NLog.Targets.Network", skipCheckExists); SafeRegisterNamedType(_targets, "syslog", "NLog.Targets.SyslogTarget, NLog.Targets.Network", skipCheckExists); SafeRegisterNamedType(_targets, "gelf", "NLog.Targets.GelfTarget, NLog.Targets.Network", skipCheckExists); SafeRegisterNamedType(_targets, "mail", "NLog.Targets.MailTarget, NLog.Targets.Mail", skipCheckExists); diff --git a/src/NLog/Layouts/CSV/CsvColumn.cs b/src/NLog/Layouts/CSV/CsvColumn.cs index 26d9bd6f1c..dcac013e6a 100644 --- a/src/NLog/Layouts/CSV/CsvColumn.cs +++ b/src/NLog/Layouts/CSV/CsvColumn.cs @@ -38,6 +38,10 @@ namespace NLog.Layouts /// /// A column in the CSV. /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [NLogConfigurationItem] public class CsvColumn { diff --git a/src/NLog/Layouts/JSON/JsonAttribute.cs b/src/NLog/Layouts/JSON/JsonAttribute.cs index 1dea55e97a..61214bc898 100644 --- a/src/NLog/Layouts/JSON/JsonAttribute.cs +++ b/src/NLog/Layouts/JSON/JsonAttribute.cs @@ -41,6 +41,10 @@ namespace NLog.Layouts /// /// JSON attribute. /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [NLogConfigurationItem] public class JsonAttribute { diff --git a/src/NLog/Layouts/XML/XmlAttribute.cs b/src/NLog/Layouts/XML/XmlAttribute.cs index dde02dac44..9f3d4a2870 100644 --- a/src/NLog/Layouts/XML/XmlAttribute.cs +++ b/src/NLog/Layouts/XML/XmlAttribute.cs @@ -41,6 +41,10 @@ namespace NLog.Layouts /// /// XML attribute. /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [NLogConfigurationItem] public class XmlAttribute { diff --git a/src/NLog/Layouts/XML/XmlElement.cs b/src/NLog/Layouts/XML/XmlElement.cs index c179d671b6..af11b7daf3 100644 --- a/src/NLog/Layouts/XML/XmlElement.cs +++ b/src/NLog/Layouts/XML/XmlElement.cs @@ -38,6 +38,10 @@ namespace NLog.Layouts /// /// A XML Element /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [ThreadAgnostic] public class XmlElement : XmlElementBase { diff --git a/src/NLog/Layouts/XML/XmlElementBase.cs b/src/NLog/Layouts/XML/XmlElementBase.cs index 22ef271a61..7b0d0238d8 100644 --- a/src/NLog/Layouts/XML/XmlElementBase.cs +++ b/src/NLog/Layouts/XML/XmlElementBase.cs @@ -44,6 +44,10 @@ namespace NLog.Layouts /// /// A specialized layout that renders XML-formatted events. /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki public abstract class XmlElementBase : Layout { private Layout[]? _precalculateLayouts; diff --git a/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs index 399dac72fc..8bfc017267 100644 --- a/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs @@ -1302,7 +1302,7 @@ public void Bug3990StackOverflowWhenUsingNLogViewerTarget() { // this would fail because of stack overflow in the // constructor of NLogViewerTarget - var logFactory = new LogFactory().Setup().SetupExtensions(ext => ext.RegisterTarget("NLogViewer")) + var logFactory = new LogFactory().Setup().SetupExtensions(ext => ext.RegisterTarget("NLogViewer")) .LoadConfigurationFromXml(@" @@ -1313,7 +1313,7 @@ public void Bug3990StackOverflowWhenUsingNLogViewerTarget() ").LogFactory; - var target = logFactory.Configuration.LoggingRules[0].Targets[0] as ChainsawTarget; + var target = logFactory.Configuration.LoggingRules[0].Targets[0] as Log4JXmlTarget; Assert.NotNull(target); } From 5fed3f0aaf633efe265e779f5e134cdb0042b611 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 25 May 2025 15:19:02 +0200 Subject: [PATCH 132/224] Version 6.0 RC1 (#5839) --- CHANGELOG.md | 12 ++++++++++++ build.ps1 | 2 +- src/NLog/NLog.csproj | 10 +++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4ab487680..5ff3993c03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ Date format: (year/month/day) ## Change Log +### Version 6.0 RC1 (2025/04/25) + +**Major changes** +- Updated NLog API with `enable` and introduced `Layout.Empty` +- Marked `[RequiredParameter]` as obsolete, and replaced with explicit option validation during initialization. +- Marked `LogEventInfo.SequenceID` and `${sequenceid}` as obsolete, and instead use `${counter:sequence=global}`. +- Added support for params ReadOnlySpan when using C# 13 +- Prioritize generic Logger-methods by marking legacy methods with `[OverloadResolutionPriority(-1)]` when using C# 13 +- Skip LogEventInfo.Parameters-array-allocation when unable to defer message-template formatting. +- Renamed ChainsawTarget to Log4JXmlTarget to match Log4JXmlEventLayout +- Updated NLog.Schema to include intellisense for multiple NLog-assemblies. + ### Version 6.0 Preview 1 (2025/04/27) **Major Changes** diff --git a/build.ps1 b/build.ps1 index a17bb2ad43..13df3ad97c 100644 --- a/build.ps1 +++ b/build.ps1 @@ -3,7 +3,7 @@ dotnet --version $versionPrefix = "6.0.0" -$versionSuffix = "preview1" +$versionSuffix = "rc1" $versionFile = $versionPrefix + "." + ${env:APPVEYOR_BUILD_NUMBER} $versionProduct = $versionPrefix; if (-Not $versionSuffix.Equals("")) diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index ca14107f55..f7509b0011 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -28,7 +28,7 @@ For ASP.NET Core, check: https://www.nuget.org/packages/NLog.Web.AspNetCore Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ -NLog v6.0 Preview1 with the following major changes: +NLog v6.0 RC1 with the following major changes: - Support AOT builds without build warnings. - New FileTarget without ConcurrentWrites support, but still support KeepFileOpen true / false. @@ -43,6 +43,14 @@ NLog v6.0 Preview1 with the following major changes: - Moved WebServiceTarget into the new nuget-package NLog.Targets.WebService - Removed dependency on System.Text.RegularExpressions and introduced new nuget-package NLog.RegEx. - Removed dependency on System.Xml.XmlReader by implementing own internal basic XML-Parser. +- Updated NLog API with Nullable-support and introduced Layout.Empty +- Marked [RequiredParameter] as obsolete, and replaced with explicit option validation during initialization. +- Marked LogEventInfo.SequenceID and ${sequenceid} as obsolete, and instead use ${counter:sequence=global}. +- Added support for params ReadOnlySpan when using C# 13 +- Prioritize generic Logger-methods by marking legacy methods with [OverloadResolutionPriority(-1)] when using C# 13 +- Skip LogEventInfo.Parameters-array-allocation when unable to defer message-template formatting. +- Renamed ChainsawTarget to Log4JXmlTarget to match Log4JXmlEventLayout +- Updated NLog.Schema to include intellisense for multiple NLog-assemblies. NLog v6.0 release notes: https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html From e5f0fb206e5cad087f577d2245ae4f13defbf482 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 25 May 2025 15:57:00 +0200 Subject: [PATCH 133/224] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3165b548fc..d85bdddb27 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Having troubles? Check the [troubleshooting guide](https://github.com/NLog/NLog/ ℹ️ NLog 6.0 will support AOT -[NLog 6.0 Preview1](https://www.nuget.org/packages/NLog/6.0.0-preview1#releasenotes-body-tab) now available. See also [List of major changes in NLog 6.0](https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html) +[NLog 6.0 RC1](https://www.nuget.org/packages/NLog/6.0.0-rc1#releasenotes-body-tab) now available. See also [List of major changes in NLog 6.0](https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html) NLog Packages --- From 95e61443b7eb924b1627983a541c10effe90db4e Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 25 May 2025 17:01:47 +0200 Subject: [PATCH 134/224] NLog.Schema - Updated to include NLog v6-rc1 assemblies (#5840) --- Test-XmlFile.ps1 | 2 +- .../NLogNugetPackages.csproj | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Test-XmlFile.ps1 b/Test-XmlFile.ps1 index 1d79706ad4..ddc44c7ed1 100644 --- a/Test-XmlFile.ps1 +++ b/Test-XmlFile.ps1 @@ -54,7 +54,7 @@ function Test-XmlFile $pwd = get-location; $testFilesDir = "$pwd\examples\targets\Configuration File" $xsdFilePath = "$pwd\src\NLog\bin\Release\NLog.xsd" -$excludedTests = ("MessageBox","RichTextBox","FormControl","Database","Log4JXml","Chainsaw","NLogViewer") +$excludedTests = ("MessageBox","RichTextBox","FormControl","Database","Chainsaw","NLogViewer") # Returns true if all selected tests in examples directory are valid $ret = $true Get-ChildItem -Path $testFilesDir -Directory -Exclude $excludedTests | Get-ChildItem -Recurse -File -Filter NLog.config | % { diff --git a/tools/NLogNugetPackages/NLogNugetPackages.csproj b/tools/NLogNugetPackages/NLogNugetPackages.csproj index db1ded419d..1bde7c837e 100644 --- a/tools/NLogNugetPackages/NLogNugetPackages.csproj +++ b/tools/NLogNugetPackages/NLogNugetPackages.csproj @@ -7,27 +7,27 @@ - + - + - - - - - - - + + + + + + + - + \ No newline at end of file From 433c2125b8e93d1704f3349ab824167e90d6fae0 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 25 May 2025 17:34:24 +0200 Subject: [PATCH 135/224] NLog.Targets.Network - Support NET35 (#5841) --- build.ps1 | 2 +- src/NLog.Targets.Network/Layouts/GelfLayout.cs | 18 ++++++++++++++---- .../NLog.Targets.Network.csproj | 4 ++-- src/NLog.Targets.Network/Targets/GelfTarget.cs | 8 ++++++++ 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/build.ps1 b/build.ps1 index 13df3ad97c..10b09094f0 100644 --- a/build.ps1 +++ b/build.ps1 @@ -45,7 +45,7 @@ create-package 'NLog.OutputDebugString' '"net35;net45;net46;netstandard2.0;netst create-package 'NLog.RegEx' '"net35;net45;net46;netstandard2.0;netstandard2.1"' create-package 'NLog.WindowsRegistry' '"net35;net45;net46;netstandard2.0;netstandard2.1"' create-package 'NLog.Targets.ConcurrentFile' '"net35;net45;net46;netstandard2.0"' -msbuild /t:Restore,Pack ./src/NLog.Targets.AtomicFile/ /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount +msbuild /t:Restore,Pack ./src/NLog.Targets.AtomicFile/ /p:targetFrameworks="net35;net45;net46;net8.0" /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount create-package 'NLog.WindowsEventLog' '"netstandard2.0;netstandard2.1"' msbuild /t:xsd /t:NuGetSchemaPackage ./src/NLog.proj /p:Configuration=Release /p:BuildNetFX45=true /p:BuildVersion=$versionProduct /p:Configuration=Release /p:BuildLabelOverride=NONE /verbosity:minimal diff --git a/src/NLog.Targets.Network/Layouts/GelfLayout.cs b/src/NLog.Targets.Network/Layouts/GelfLayout.cs index c031eff245..35cb4309c2 100644 --- a/src/NLog.Targets.Network/Layouts/GelfLayout.cs +++ b/src/NLog.Targets.Network/Layouts/GelfLayout.cs @@ -104,12 +104,20 @@ public Layout GelfFacility /// /// List of property names to exclude when is true /// +#if NET35 + public HashSet ExcludeProperties { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); +#else public ISet ExcludeProperties { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); +#endif /// /// List of property names to include when is true /// +#if NET35 + public HashSet IncludeProperties { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); +#else public ISet IncludeProperties { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); +#endif /// /// Disables to capture ScopeContext-properties from active thread context @@ -159,8 +167,6 @@ protected override void InitializeLayout() _gelfHostNameString = ResolveJsonFixedString(_gelfHostName); _gelfFacilityString = ResolveJsonFixedString(_gelfFacility); - if (_gelfFacilityString != null && string.IsNullOrWhiteSpace(_gelfFacilityString)) - _gelfFacilityString = "GELF"; } /// @@ -323,7 +329,11 @@ protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuil target.Append(_completeJsonMessage); } +#if NET35 + private static bool ExcludeScopeProperty(string propertyName, IDictionary? eventProperties, HashSet? excludeProperties) +#else private static bool ExcludeScopeProperty(string propertyName, IDictionary? eventProperties, ISet? excludeProperties) +#endif { if (excludeProperties?.Contains(propertyName) == true) return true; @@ -380,7 +390,7 @@ private static bool ExcludeGelfField(string gelfFieldName, IDictionary IncludeProperties.Contains(p.Key)).ToDictionary(p => p.Key, p => p.Value) : null; + return foundProperty ? eventProperties.Where(p => IncludeProperties.Contains(p.Key?.ToString() ?? string.Empty)).ToDictionary(p => p.Key, p => p.Value) : null; } return eventProperties; @@ -485,7 +495,7 @@ internal static SyslogLevel ToSyslogLevel(LogLevel logLevel) private static string EscapePropertyName(string propertyName) { - if (string.IsNullOrWhiteSpace(propertyName)) + if (string.IsNullOrEmpty(propertyName)) return string.Empty; foreach (var chr in propertyName) diff --git a/src/NLog.Targets.Network/NLog.Targets.Network.csproj b/src/NLog.Targets.Network/NLog.Targets.Network.csproj index c4c90730a2..5c2d6fffd8 100644 --- a/src/NLog.Targets.Network/NLog.Targets.Network.csproj +++ b/src/NLog.Targets.Network/NLog.Targets.Network.csproj @@ -2,8 +2,8 @@ 17.0 - net46;netstandard2.0 - net46;netstandard2.0;netstandard2.1 + net35;net46;netstandard2.0 + net35;net46;netstandard2.0;netstandard2.1 NLog.Targets.Network NLog diff --git a/src/NLog.Targets.Network/Targets/GelfTarget.cs b/src/NLog.Targets.Network/Targets/GelfTarget.cs index 19d424a8c2..27d71279a5 100644 --- a/src/NLog.Targets.Network/Targets/GelfTarget.cs +++ b/src/NLog.Targets.Network/Targets/GelfTarget.cs @@ -75,10 +75,18 @@ public class GelfTarget : NetworkTarget public bool ExcludeEmptyProperties { get => _gelfLayout.ExcludeEmptyProperties; set => _gelfLayout.ExcludeEmptyProperties = value; } /// +#if NET35 + public HashSet ExcludeProperties { get => _gelfLayout.ExcludeProperties; } +#else public ISet ExcludeProperties { get => _gelfLayout.ExcludeProperties; } +#endif /// +#if NET35 + public HashSet IncludeProperties { get => _gelfLayout.IncludeProperties; } +#else public ISet IncludeProperties { get => _gelfLayout.IncludeProperties; } +#endif /// public override Layout Layout From d4c61563850c6d432da26938f55f1eb3a1906dfc Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 25 May 2025 20:01:40 +0200 Subject: [PATCH 136/224] Mark struct as readonly to allow compiler optimization (#5842) --- .../Internal/ReusableObjectCreator.cs | 6 +++++- src/NLog/Common/AsyncLogEventInfo.cs | 6 +++++- src/NLog/Common/InternalLogEventArgs.cs | 6 +++++- src/NLog/Conditions/ConditionTokenizer.cs | 6 +++++- src/NLog/Config/ConfigurationItemFactory.cs | 6 +++++- src/NLog/Config/DynamicRangeLevelFilter.cs | 6 +++++- src/NLog/Config/MethodFactory.cs | 4 ++++ src/NLog/Filters/WhenRepeatedFilter.cs | 6 +++++- src/NLog/Internal/MruCache.cs | 4 ++++ src/NLog/Internal/ObjectReflectionCache.cs | 8 ++++++-- src/NLog/Internal/PropertiesDictionary.cs | 6 +++++- src/NLog/MessageTemplates/MessageTemplateParameter.cs | 6 +++++- .../Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs | 4 ++++ src/NLog/Targets/FileTarget.cs | 4 ++++ 14 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/NLog.Targets.ConcurrentFile/Internal/ReusableObjectCreator.cs b/src/NLog.Targets.ConcurrentFile/Internal/ReusableObjectCreator.cs index 4e02c4fe61..c4db88d976 100644 --- a/src/NLog.Targets.ConcurrentFile/Internal/ReusableObjectCreator.cs +++ b/src/NLog.Targets.ConcurrentFile/Internal/ReusableObjectCreator.cs @@ -69,7 +69,11 @@ private void Deallocate(T reusableObject) _reusableObject = reusableObject; } - public struct LockOject : IDisposable + public +#if !NETFRAMEWORK + readonly +#endif + struct LockOject : IDisposable { /// /// Access the acquired reusable object diff --git a/src/NLog/Common/AsyncLogEventInfo.cs b/src/NLog/Common/AsyncLogEventInfo.cs index 454b8c234a..17fa3da21b 100644 --- a/src/NLog/Common/AsyncLogEventInfo.cs +++ b/src/NLog/Common/AsyncLogEventInfo.cs @@ -36,7 +36,11 @@ namespace NLog.Common /// /// Represents the logging event with asynchronous continuation. /// - public struct AsyncLogEventInfo : System.IEquatable + public +#if !NETFRAMEWORK + readonly +#endif + struct AsyncLogEventInfo : System.IEquatable { /// /// Initializes a new instance of the struct. diff --git a/src/NLog/Common/InternalLogEventArgs.cs b/src/NLog/Common/InternalLogEventArgs.cs index a35c714c89..215862dcff 100644 --- a/src/NLog/Common/InternalLogEventArgs.cs +++ b/src/NLog/Common/InternalLogEventArgs.cs @@ -38,7 +38,11 @@ namespace NLog.Common /// /// Internal LogEvent details from /// - public readonly struct InternalLogEventArgs + public +#if !NETFRAMEWORK + readonly +#endif + struct InternalLogEventArgs { /// /// The rendered message diff --git a/src/NLog/Conditions/ConditionTokenizer.cs b/src/NLog/Conditions/ConditionTokenizer.cs index 546aa7d223..4b9e648027 100644 --- a/src/NLog/Conditions/ConditionTokenizer.cs +++ b/src/NLog/Conditions/ConditionTokenizer.cs @@ -488,7 +488,11 @@ private int ReadChar() /// /// Mapping between characters and token types for punctuations. /// - private struct CharToTokenType + private +#if !NETFRAMEWORK + readonly +#endif + struct CharToTokenType { public readonly char Character; public readonly ConditionTokenType TokenType; diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index 577c14f642..dd4b11554a 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -72,7 +72,11 @@ public sealed class ConfigurationItemFactory private readonly Factory _timeSources; private readonly Dictionary _itemFactories = new Dictionary(256); - private struct ItemFactory + private +#if !NETFRAMEWORK + readonly +#endif + struct ItemFactory { public readonly Func> ItemProperties; public readonly Func ItemCreator; diff --git a/src/NLog/Config/DynamicRangeLevelFilter.cs b/src/NLog/Config/DynamicRangeLevelFilter.cs index 8f4aa9e073..13120abeb5 100644 --- a/src/NLog/Config/DynamicRangeLevelFilter.cs +++ b/src/NLog/Config/DynamicRangeLevelFilter.cs @@ -122,7 +122,11 @@ private bool[] ParseLevelRange(string minLevelFilter, string maxLevelFilter) } } - private struct MinMaxLevels : IEquatable + private +#if !NETFRAMEWORK + readonly +#endif + struct MinMaxLevels : IEquatable { private readonly string _minLevel; private readonly string _maxLevel; diff --git a/src/NLog/Config/MethodFactory.cs b/src/NLog/Config/MethodFactory.cs index da97e8f349..60520da6af 100644 --- a/src/NLog/Config/MethodFactory.cs +++ b/src/NLog/Config/MethodFactory.cs @@ -47,6 +47,10 @@ internal sealed class MethodFactory : IFactory { private readonly Dictionary _nameToMethodDetails = new Dictionary(StringComparer.OrdinalIgnoreCase); + private +#if !NETFRAMEWORK + readonly +#endif struct MethodDetails { public readonly MethodInfo MethodInfo; diff --git a/src/NLog/Filters/WhenRepeatedFilter.cs b/src/NLog/Filters/WhenRepeatedFilter.cs index 5df47aa61e..7247a1d3f3 100644 --- a/src/NLog/Filters/WhenRepeatedFilter.cs +++ b/src/NLog/Filters/WhenRepeatedFilter.cs @@ -351,7 +351,11 @@ public bool HasExpired(DateTime logEventTime, int timeoutSeconds) /// /// Filter Lookup Key (immutable) /// - private struct FilterInfoKey : IEquatable + private +#if !NETFRAMEWORK + readonly +#endif + struct FilterInfoKey : IEquatable { private readonly StringBuilder? _stringBuffer; public readonly string? StringValue; diff --git a/src/NLog/Internal/MruCache.cs b/src/NLog/Internal/MruCache.cs index fa5d7bf173..550ea2eebd 100644 --- a/src/NLog/Internal/MruCache.cs +++ b/src/NLog/Internal/MruCache.cs @@ -197,6 +197,10 @@ public bool TryGetValue(TKey key, out TValue? value) return true; } + private +#if !NETFRAMEWORK + readonly +#endif struct MruCacheItem { public readonly TValue Value; diff --git a/src/NLog/Internal/ObjectReflectionCache.cs b/src/NLog/Internal/ObjectReflectionCache.cs index e7aacdd773..8c1f91bd91 100644 --- a/src/NLog/Internal/ObjectReflectionCache.cs +++ b/src/NLog/Internal/ObjectReflectionCache.cs @@ -287,7 +287,7 @@ struct ObjectPropertyList : IEnumerable #if !NETFRAMEWORK readonly #endif - struct PropertyValue + struct PropertyValue { public readonly string Name; public readonly object? Value; @@ -530,7 +530,11 @@ public FastPropertyLookup(string name, TypeCode typeCode, ReflectionHelpers.Late } } - private struct ObjectPropertyInfos : IEquatable + private +#if !NETFRAMEWORK + readonly +#endif + struct ObjectPropertyInfos : IEquatable { public readonly PropertyInfo[] Properties; public readonly FastPropertyLookup[]? FastLookup; diff --git a/src/NLog/Internal/PropertiesDictionary.cs b/src/NLog/Internal/PropertiesDictionary.cs index ad3ac0be66..74a07b04cb 100644 --- a/src/NLog/Internal/PropertiesDictionary.cs +++ b/src/NLog/Internal/PropertiesDictionary.cs @@ -49,7 +49,11 @@ namespace NLog.Internal [DebuggerDisplay("Count = {Count}")] internal sealed class PropertiesDictionary : IDictionary { - private struct PropertyValue + private +#if !NETFRAMEWORK + readonly +#endif + struct PropertyValue { /// /// Value of the property diff --git a/src/NLog/MessageTemplates/MessageTemplateParameter.cs b/src/NLog/MessageTemplates/MessageTemplateParameter.cs index 29e306d054..13683d438d 100644 --- a/src/NLog/MessageTemplates/MessageTemplateParameter.cs +++ b/src/NLog/MessageTemplates/MessageTemplateParameter.cs @@ -38,7 +38,11 @@ namespace NLog.MessageTemplates /// /// Description of a single parameter extracted from a MessageTemplate /// - public struct MessageTemplateParameter + public +#if !NETFRAMEWORK + readonly +#endif + struct MessageTemplateParameter { /// /// Parameter Name extracted from diff --git a/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs b/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs index e28aae0e3e..5032135918 100644 --- a/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs +++ b/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs @@ -110,6 +110,10 @@ protected bool DeleteOldFilesBeforeArchive(string fileDirectory, string fileWild return FileInfoDateTime.ScanFileNamesForMaxSequenceNo(fileInfos, fileWildcardStartIndex, fileWildcardEndIndex); } + private +#if !NETFRAMEWORK + readonly +#endif struct FileInfoDateTime : IComparer { public FileInfo FileInfo { get; } diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index 43f5362673..bde3dbaeec 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -456,6 +456,10 @@ private int OpenFileMonitorTimerInterval private IFileArchiveHandler FileAchiveHandler => _fileArchiveHandler ?? (_fileArchiveHandler = CreateFileArchiveHandler()); private IFileArchiveHandler? _fileArchiveHandler; + private +#if !NETFRAMEWORK + readonly +#endif struct OpenFileAppender { public IFileAppender FileAppender { get; } From 0078f30d08069f3afc1874da2f35a56f953442fb Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 25 May 2025 22:18:24 +0200 Subject: [PATCH 137/224] ILoggingConfigurationElement - Values with nullable reference (#5843) --- .../Config/ILoggingConfigurationElement.cs | 2 +- src/NLog/Config/LoggingConfigurationParser.cs | 30 +++++++++---------- .../Config/XmlLoggingConfigurationElement.cs | 16 +++++----- .../Config/XmlParserConfigurationElement.cs | 8 +++-- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/NLog/Config/ILoggingConfigurationElement.cs b/src/NLog/Config/ILoggingConfigurationElement.cs index b0088a8c50..591b8b86e6 100644 --- a/src/NLog/Config/ILoggingConfigurationElement.cs +++ b/src/NLog/Config/ILoggingConfigurationElement.cs @@ -47,7 +47,7 @@ public interface ILoggingConfigurationElement /// /// Configuration Key/Value Pairs /// - IEnumerable> Values { get; } + IEnumerable> Values { get; } /// /// Child configuration elements /// diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index be03a28584..7ea37392d8 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -209,14 +209,14 @@ private void SetNLogElementSettings(ILoggingConfigurationElement nlogConfig) /// /// /// - private ICollection> CreateUniqueSortedListFromConfig(ILoggingConfigurationElement nlogConfig) + private ICollection> CreateUniqueSortedListFromConfig(ILoggingConfigurationElement nlogConfig) { var configurationElement = ValidatedConfigurationElement.Create(nlogConfig, LogFactory); var dict = configurationElement.Values; if (dict.Count <= 1) return dict; - var sortedList = new List>(dict.Count); + var sortedList = new List>(dict.Count); var highPriorityList = new HashSet(StringComparer.OrdinalIgnoreCase) { "ThrowExceptions", @@ -230,7 +230,7 @@ private ICollection> CreateUniqueSortedListFromConf var settingValue = configurationElement.GetOptionalValue(highPrioritySetting, null); if (settingValue != null) { - sortedList.Add(new KeyValuePair(highPrioritySetting, settingValue)); + sortedList.Add(new KeyValuePair(highPrioritySetting, settingValue)); } } foreach (var configItem in configurationElement.Values) @@ -582,7 +582,7 @@ private void ParseRulesElement(ValidatedConfigurationElement rulesElement, IList namePattern = childProperty.Value; break; case "ENABLED": - enabled = ParseBooleanValue(childProperty.Key, childProperty.Value, true); + enabled = ParseBooleanValue(childProperty.Key, childProperty.Value ?? string.Empty, true); break; case "APPENDTO": writeTargets = childProperty.Value; @@ -591,7 +591,7 @@ private void ParseRulesElement(ValidatedConfigurationElement rulesElement, IList writeTargets = childProperty.Value; break; case "FINAL": - final = ParseBooleanValue(childProperty.Key, childProperty.Value, false); + final = ParseBooleanValue(childProperty.Key, childProperty.Value ?? string.Empty, false); break; case "LEVEL": case "LEVELS": @@ -1078,8 +1078,8 @@ private void ConfigureObjectFromAttributes(T targetObject, ValidatedConfigura { foreach (var kvp in element.Values) { - string childName = kvp.Key; - string childValue = kvp.Value; + var childName = kvp.Key; + var childValue = kvp.Value; if (ignoreType && MatchesName(childName, "type")) { @@ -1090,7 +1090,7 @@ private void ConfigureObjectFromAttributes(T targetObject, ValidatedConfigura } } - private void SetPropertyValueFromString(T targetObject, string propertyName, string propertyValue, ValidatedConfigurationElement element) where T : class + private void SetPropertyValueFromString(T targetObject, string propertyName, string? propertyValue, ValidatedConfigurationElement element) where T : class { try { @@ -1568,8 +1568,8 @@ public ValidatedConfigurationElement(ILoggingConfigurationElement element, bool public string Name { get; } - public ICollection> Values => _valueLookup ?? (ICollection>)ArrayHelper.Empty>(); - private readonly IDictionary? _valueLookup; + public ICollection> Values => _valueLookup ?? (ICollection>)ArrayHelper.Empty>(); + private readonly IDictionary? _valueLookup; public IEnumerable ValidChildren { @@ -1600,7 +1600,7 @@ IEnumerable YieldAndCacheValidChildren() /// IEnumerable ILoggingConfigurationElement.Children => ValidChildren.Cast(); - IEnumerable> ILoggingConfigurationElement.Values => Values; + IEnumerable> ILoggingConfigurationElement.Values => Values; public string GetRequiredValue(string attributeName, string section) { @@ -1624,18 +1624,18 @@ public string GetRequiredValue(string attributeName, string section) if (_valueLookup is null) return defaultValue; - _valueLookup.TryGetValue(attributeName, out string value); + _valueLookup.TryGetValue(attributeName, out var value); return value ?? defaultValue; } - private static IDictionary? CreateValueLookup(ILoggingConfigurationElement element, bool throwConfigExceptions) + private static IDictionary? CreateValueLookup(ILoggingConfigurationElement element, bool throwConfigExceptions) { - IDictionary? valueLookup = null; + IDictionary? valueLookup = null; List? warnings = null; foreach (var attribute in element.Values) { var attributeKey = attribute.Key?.Trim() ?? string.Empty; - valueLookup = valueLookup ?? new Dictionary(StringComparer.OrdinalIgnoreCase); + valueLookup = valueLookup ?? new Dictionary(StringComparer.OrdinalIgnoreCase); if (!string.IsNullOrEmpty(attributeKey) && !valueLookup.ContainsKey(attributeKey)) { valueLookup[attributeKey] = attribute.Value; diff --git a/src/NLog/Config/XmlLoggingConfigurationElement.cs b/src/NLog/Config/XmlLoggingConfigurationElement.cs index 93347050e6..6a8ef08154 100644 --- a/src/NLog/Config/XmlLoggingConfigurationElement.cs +++ b/src/NLog/Config/XmlLoggingConfigurationElement.cs @@ -72,7 +72,7 @@ public XmlLoggingConfigurationElement(XmlReader reader, bool nestedElement) /// /// Gets the dictionary of attribute values. /// - public IList> AttributeValues { get; } + public IList> AttributeValues { get; } /// /// Gets the collection of child elements. @@ -86,7 +86,7 @@ public XmlLoggingConfigurationElement(XmlReader reader, bool nestedElement) public string Name => LocalName; - public IEnumerable> Values + public IEnumerable> Values { get { @@ -96,7 +96,7 @@ public IEnumerable> Values if (SingleValueElement(child)) { // Values assigned using nested node-elements. Maybe in combination with attributes - return AttributeValues.Concat(Children.Where(item => SingleValueElement(item)).Select(item => new KeyValuePair(item.Name, item.Value ?? string.Empty))); + return AttributeValues.Concat(Children.Where(item => SingleValueElement(item)).Select(item => new KeyValuePair(item.Name, item.Value ?? string.Empty))); } } return AttributeValues; @@ -174,9 +174,9 @@ private static IList ParseChildren(XmlReader rea return children ?? ArrayHelper.Empty(); } - private static IList> ParseAttributes(XmlReader reader, bool nestedElement) + private static IList> ParseAttributes(XmlReader reader, bool nestedElement) { - IList>? attributes = null; + IList>? attributes = null; if (reader.MoveToFirstAttribute()) { do @@ -186,14 +186,14 @@ private static IList> ParseAttributes(XmlReader rea continue; } - attributes = attributes ?? new List>(); - attributes.Add(new KeyValuePair(reader.LocalName, reader.Value)); + attributes = attributes ?? new List>(); + attributes.Add(new KeyValuePair(reader.LocalName, reader.Value)); } while (reader.MoveToNextAttribute()); reader.MoveToElement(); } - return attributes ?? ArrayHelper.Empty>(); + return attributes ?? ArrayHelper.Empty>(); } /// diff --git a/src/NLog/Config/XmlParserConfigurationElement.cs b/src/NLog/Config/XmlParserConfigurationElement.cs index 34f53f0d3a..1f69e0a9b8 100644 --- a/src/NLog/Config/XmlParserConfigurationElement.cs +++ b/src/NLog/Config/XmlParserConfigurationElement.cs @@ -53,14 +53,14 @@ internal sealed class XmlParserConfigurationElement : ILoggingConfigurationEleme /// /// Gets the dictionary of attribute values. /// - public IList> AttributeValues { get; } + public IList> AttributeValues { get; } /// /// Gets the collection of child elements. /// public IList Children { get; } - public IEnumerable> Values + public IEnumerable> Values { get { @@ -70,7 +70,7 @@ public IEnumerable> Values if (SingleValueElement(child)) { // Values assigned using nested node-elements. Maybe in combination with attributes - return AttributeValues.Concat(Children.Where(item => SingleValueElement(item)).Select(item => new KeyValuePair(item.Name, item.Value ?? string.Empty))); + return AttributeValues.Concat(Children.Where(item => SingleValueElement(item)).Select(item => new KeyValuePair(item.Name, item.Value ?? string.Empty))); } } return AttributeValues; @@ -102,7 +102,9 @@ public XmlParserConfigurationElement(XmlParser.XmlParserElement xmlElement, bool var namePrefixIndex = xmlElement.Name.IndexOf(':'); Name = namePrefixIndex >= 0 ? xmlElement.Name.Substring(namePrefixIndex + 1) : xmlElement.Name; Value = xmlElement.InnerText; +#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type. AttributeValues = ParseAttributes(xmlElement, nestedElement); +#pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type. Children = ParseChildren(xmlElement, nestedElement); } From 2a055522e88b3eb8db04940eb64951f3aea73dd6 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 26 May 2025 07:48:18 +0200 Subject: [PATCH 138/224] XmlParser - Handle XML comments between processing instructions (#5844) --- src/NLog/Internal/XmlParser.cs | 11 ++++++++++- tests/NLog.UnitTests/Internal/XmlParserTests.cs | 3 +++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/NLog/Internal/XmlParser.cs b/src/NLog/Internal/XmlParser.cs index d76e6b4b13..ee6cd13205 100644 --- a/src/NLog/Internal/XmlParser.cs +++ b/src/NLog/Internal/XmlParser.cs @@ -132,8 +132,17 @@ public bool TryReadProcessingInstructions(out IList? processin processingInstructions = null; - while (_xmlSource.Current == '<' && _xmlSource.Peek() == '?') + while (_xmlSource.Current == '<') { + if (_xmlSource.Peek() == '!') + { + _xmlSource.MoveNext(); + SkipXmlComment(); + continue; + } + if (_xmlSource.Peek() != '?') + break; + if (!TryBeginReadStartElement(out var instructionName, processingInstruction: true)) throw new XmlParserException("Invalid XML document. Cannot parse XML processing instruction"); diff --git a/tests/NLog.UnitTests/Internal/XmlParserTests.cs b/tests/NLog.UnitTests/Internal/XmlParserTests.cs index 5e35c4afdc..b8cf4bc708 100644 --- a/tests/NLog.UnitTests/Internal/XmlParserTests.cs +++ b/tests/NLog.UnitTests/Internal/XmlParserTests.cs @@ -176,8 +176,11 @@ public void XmlParse_InvalidDocument(string xmlSource) [InlineData(" ")] [InlineData("\n\n")] [InlineData("\n\n")] + [InlineData("")] [InlineData("")] [InlineData("\n")] + [InlineData("")] + [InlineData("")] public void XmlParse_EmptyDocument(string xmlSource) { var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); From de17b535701d83709bd0e82903b41932a1d9dfdf Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 26 May 2025 20:27:25 +0200 Subject: [PATCH 139/224] NLog.Schema with NLog.Windows.Forms-nuget-package (#5845) --- Test-XmlFile.ps1 | 2 +- .../Configuration File/RichTextBox/RowColoring/NLog.config | 2 +- tests/NLog.UnitTests/Internal/XmlParserTests.cs | 2 ++ tools/NLogNugetPackages/NLogNugetPackages.csproj | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Test-XmlFile.ps1 b/Test-XmlFile.ps1 index ddc44c7ed1..c194310fd7 100644 --- a/Test-XmlFile.ps1 +++ b/Test-XmlFile.ps1 @@ -54,7 +54,7 @@ function Test-XmlFile $pwd = get-location; $testFilesDir = "$pwd\examples\targets\Configuration File" $xsdFilePath = "$pwd\src\NLog\bin\Release\NLog.xsd" -$excludedTests = ("MessageBox","RichTextBox","FormControl","Database","Chainsaw","NLogViewer") +$excludedTests = ("Database","Chainsaw","NLogViewer") # Returns true if all selected tests in examples directory are valid $ret = $true Get-ChildItem -Path $testFilesDir -Directory -Exclude $excludedTests | Get-ChildItem -Recurse -File -Filter NLog.config | % { diff --git a/examples/targets/Configuration File/RichTextBox/RowColoring/NLog.config b/examples/targets/Configuration File/RichTextBox/RowColoring/NLog.config index 162e0f5f10..a5f3b04a18 100644 --- a/examples/targets/Configuration File/RichTextBox/RowColoring/NLog.config +++ b/examples/targets/Configuration File/RichTextBox/RowColoring/NLog.config @@ -4,7 +4,7 @@ - + diff --git a/tests/NLog.UnitTests/Internal/XmlParserTests.cs b/tests/NLog.UnitTests/Internal/XmlParserTests.cs index b8cf4bc708..e5f7905857 100644 --- a/tests/NLog.UnitTests/Internal/XmlParserTests.cs +++ b/tests/NLog.UnitTests/Internal/XmlParserTests.cs @@ -152,6 +152,8 @@ public void XmlConvertIsXmlCharTest() [InlineData("")] [InlineData("&quop;")] [InlineData(""")] + [InlineData("")] + [InlineData("")] public void XmlParse_InvalidDocument(string xmlSource) { Assert.Throws(() => new XmlParser(xmlSource).LoadDocument(out var _)); diff --git a/tools/NLogNugetPackages/NLogNugetPackages.csproj b/tools/NLogNugetPackages/NLogNugetPackages.csproj index 1bde7c837e..7d5b494127 100644 --- a/tools/NLogNugetPackages/NLogNugetPackages.csproj +++ b/tools/NLogNugetPackages/NLogNugetPackages.csproj @@ -26,6 +26,7 @@ + From 031c0c6c687cfd0cda60e6e136db22bac879bc03 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 27 May 2025 19:32:13 +0200 Subject: [PATCH 140/224] Enable IsAotCompatible = true (#5846) --- src/NLog.Database/NLog.Database.csproj | 1 + src/NLog.OutputDebugString/NLog.OutputDebugString.csproj | 1 + src/NLog.RegEx/NLog.RegEx.csproj | 1 + src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj | 1 + .../NLog.Targets.ConcurrentFile.csproj | 2 +- src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj | 1 + src/NLog.Targets.Mail/NLog.Targets.Mail.csproj | 1 + src/NLog.Targets.Network/NLog.Targets.Network.csproj | 1 + src/NLog.Targets.Trace/NLog.Targets.Trace.csproj | 1 + src/NLog.Targets.WebService/NLog.Targets.WebService.csproj | 1 + src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj | 1 + src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj | 1 + src/NLog/NLog.csproj | 1 + 13 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/NLog.Database/NLog.Database.csproj b/src/NLog.Database/NLog.Database.csproj index baae61c20b..c0c4b45948 100644 --- a/src/NLog.Database/NLog.Database.csproj +++ b/src/NLog.Database/NLog.Database.csproj @@ -38,6 +38,7 @@ 9 true true + true diff --git a/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj b/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj index dbd5a86483..740347ab91 100644 --- a/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj +++ b/src/NLog.OutputDebugString/NLog.OutputDebugString.csproj @@ -38,6 +38,7 @@ 9 true true + true diff --git a/src/NLog.RegEx/NLog.RegEx.csproj b/src/NLog.RegEx/NLog.RegEx.csproj index 1cf37d26ad..edb3fd05af 100644 --- a/src/NLog.RegEx/NLog.RegEx.csproj +++ b/src/NLog.RegEx/NLog.RegEx.csproj @@ -38,6 +38,7 @@ 9 true true + true diff --git a/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj index 319a52ecdf..4e3c0c91d1 100644 --- a/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj +++ b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj @@ -35,6 +35,7 @@ 9 true true + true true diff --git a/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj b/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj index a3daed04bc..81c4870d4b 100644 --- a/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj +++ b/src/NLog.Targets.ConcurrentFile/NLog.Targets.ConcurrentFile.csproj @@ -34,7 +34,7 @@ true true true - copyused + true true diff --git a/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj b/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj index 1946050f5f..bcdf545e86 100644 --- a/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj +++ b/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj @@ -37,6 +37,7 @@ 9 true true + true true diff --git a/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj b/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj index 5344b23725..3b9616eccf 100644 --- a/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj +++ b/src/NLog.Targets.Mail/NLog.Targets.Mail.csproj @@ -38,6 +38,7 @@ 9 true true + true diff --git a/src/NLog.Targets.Network/NLog.Targets.Network.csproj b/src/NLog.Targets.Network/NLog.Targets.Network.csproj index 5c2d6fffd8..2a1f6e764d 100644 --- a/src/NLog.Targets.Network/NLog.Targets.Network.csproj +++ b/src/NLog.Targets.Network/NLog.Targets.Network.csproj @@ -38,6 +38,7 @@ 9 true true + true diff --git a/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj b/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj index 3052064f51..26578145ec 100644 --- a/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj +++ b/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj @@ -38,6 +38,7 @@ 9 true true + true diff --git a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj index db21fa2b56..3b43b1363d 100644 --- a/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj +++ b/src/NLog.Targets.WebService/NLog.Targets.WebService.csproj @@ -38,6 +38,7 @@ 9 true true + true diff --git a/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj b/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj index 888c3e3357..e0add104f6 100644 --- a/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj +++ b/src/NLog.WindowsEventLog/NLog.WindowsEventLog.csproj @@ -38,6 +38,7 @@ 9 true true + true diff --git a/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj b/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj index f3e5ada621..7cb120b40e 100644 --- a/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj +++ b/src/NLog.WindowsRegistry/NLog.WindowsRegistry.csproj @@ -38,6 +38,7 @@ 9 true true + true diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index f7509b0011..a5fa313545 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -78,6 +78,7 @@ For all config options and platform support, check https://nlog-project.org/conf 13 true true + true From 95df2edc41db5740dc6d5cd3818df476c60049f3 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 29 May 2025 02:09:56 +0200 Subject: [PATCH 141/224] ConsoleTarget - EnableBatchWrite = true by default (#5847) --- src/NLog/SetupLoadConfigurationExtensions.cs | 9 +++++---- src/NLog/Targets/ConsoleTarget.cs | 15 +++++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/NLog/SetupLoadConfigurationExtensions.cs b/src/NLog/SetupLoadConfigurationExtensions.cs index d413462bb6..020679dbd5 100644 --- a/src/NLog/SetupLoadConfigurationExtensions.cs +++ b/src/NLog/SetupLoadConfigurationExtensions.cs @@ -446,17 +446,18 @@ public static ISetupConfigurationTargetBuilder WriteToMethodCall(this ISetupConf /// Override the default Encoding for output (Ex. UTF8) /// Write to stderr instead of standard output (stdout) /// Skip overhead from writing to console, when not available (Ex. running as Windows Service) - /// Enable batch writing of logevents, instead of Console.WriteLine for each logevent (Requires ) - public static ISetupConfigurationTargetBuilder WriteToConsole(this ISetupConfigurationTargetBuilder configBuilder, Layout? layout = null, System.Text.Encoding? encoding = null, bool stderr = false, bool detectConsoleAvailable = false, bool writeBuffered = false) + /// Enable batch writing of logevents, instead of Console.WriteLine for each logevent (For optimal performance use ) + public static ISetupConfigurationTargetBuilder WriteToConsole(this ISetupConfigurationTargetBuilder configBuilder, Layout? layout = null, System.Text.Encoding? encoding = null, bool stderr = false, bool detectConsoleAvailable = false, bool enableBatchWrite = true) { var consoleTarget = new ConsoleTarget(); if (layout != null) consoleTarget.Layout = layout; if (encoding != null) consoleTarget.Encoding = encoding; - consoleTarget.StdErr = stderr; + if (stderr) + consoleTarget.StdErr = stderr; consoleTarget.DetectConsoleAvailable = detectConsoleAvailable; - consoleTarget.WriteBuffer = writeBuffered; + consoleTarget.EnableBatchWrite = enableBatchWrite; return configBuilder.WriteTo(consoleTarget); } diff --git a/src/NLog/Targets/ConsoleTarget.cs b/src/NLog/Targets/ConsoleTarget.cs index 7a27586490..5f7541c8fc 100644 --- a/src/NLog/Targets/ConsoleTarget.cs +++ b/src/NLog/Targets/ConsoleTarget.cs @@ -131,10 +131,17 @@ public Encoding Encoding public bool AutoFlush { get; set; } /// - /// Gets or sets whether to activate internal buffering to allow batch writing, instead of using + /// Gets or sets whether to activate internal buffering to support batch writing, instead of using /// /// - public bool WriteBuffer { get; set; } + public bool EnableBatchWrite { get; set; } = true; + + /// + /// Gets or sets whether to activate internal buffering to allow batch writing, instead of using + /// + /// + [Obsolete("Replaced by EnableBatchWrite. Marked obsolete with NLog v6.0")] + public bool WriteBuffer { get => EnableBatchWrite; set => EnableBatchWrite = value; } /// /// Initializes a new instance of the class. @@ -248,7 +255,7 @@ protected override void Write(IList logEvents) return; } - if (WriteBuffer) + if (EnableBatchWrite) { WriteBufferToOutput(logEvents); } @@ -267,7 +274,7 @@ private void RenderToOutput(Layout layout, LogEventInfo logEvent) var stdErr = RenderLogEvent(StdErr, logEvent); var output = GetOutput(stdErr); - if (WriteBuffer) + if (EnableBatchWrite) { WriteBufferToOutput(output, layout, logEvent); } From 5ace9a3c28e93e8f002439aae77e58eee409b0ed Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 29 May 2025 14:17:16 +0200 Subject: [PATCH 142/224] ConsoleTarget - ForceWriteLine = false by default (#5849) --- src/NLog/SetupLoadConfigurationExtensions.cs | 6 +++--- src/NLog/Targets/ConsoleTarget.cs | 20 +++++++++---------- .../Targets/ConsoleTargetTests.cs | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/NLog/SetupLoadConfigurationExtensions.cs b/src/NLog/SetupLoadConfigurationExtensions.cs index 020679dbd5..a69e5c8d64 100644 --- a/src/NLog/SetupLoadConfigurationExtensions.cs +++ b/src/NLog/SetupLoadConfigurationExtensions.cs @@ -446,8 +446,8 @@ public static ISetupConfigurationTargetBuilder WriteToMethodCall(this ISetupConf /// Override the default Encoding for output (Ex. UTF8) /// Write to stderr instead of standard output (stdout) /// Skip overhead from writing to console, when not available (Ex. running as Windows Service) - /// Enable batch writing of logevents, instead of Console.WriteLine for each logevent (For optimal performance use ) - public static ISetupConfigurationTargetBuilder WriteToConsole(this ISetupConfigurationTargetBuilder configBuilder, Layout? layout = null, System.Text.Encoding? encoding = null, bool stderr = false, bool detectConsoleAvailable = false, bool enableBatchWrite = true) + /// Force Console.WriteLine (slower) instead of Console.WriteBuffer (faster) + public static ISetupConfigurationTargetBuilder WriteToConsole(this ISetupConfigurationTargetBuilder configBuilder, Layout? layout = null, System.Text.Encoding? encoding = null, bool stderr = false, bool detectConsoleAvailable = false, bool forceWriteLine = false) { var consoleTarget = new ConsoleTarget(); if (layout != null) @@ -457,7 +457,7 @@ public static ISetupConfigurationTargetBuilder WriteToConsole(this ISetupConfigu if (stderr) consoleTarget.StdErr = stderr; consoleTarget.DetectConsoleAvailable = detectConsoleAvailable; - consoleTarget.EnableBatchWrite = enableBatchWrite; + consoleTarget.ForceWriteLine = forceWriteLine; return configBuilder.WriteTo(consoleTarget); } diff --git a/src/NLog/Targets/ConsoleTarget.cs b/src/NLog/Targets/ConsoleTarget.cs index 5f7541c8fc..59482bce98 100644 --- a/src/NLog/Targets/ConsoleTarget.cs +++ b/src/NLog/Targets/ConsoleTarget.cs @@ -131,17 +131,17 @@ public Encoding Encoding public bool AutoFlush { get; set; } /// - /// Gets or sets whether to activate internal buffering to support batch writing, instead of using + /// Gets or sets whether to force (slower) instead of the faster internal buffering. /// /// - public bool EnableBatchWrite { get; set; } = true; + public bool ForceWriteLine { get; set; } /// /// Gets or sets whether to activate internal buffering to allow batch writing, instead of using /// /// - [Obsolete("Replaced by EnableBatchWrite. Marked obsolete with NLog v6.0")] - public bool WriteBuffer { get => EnableBatchWrite; set => EnableBatchWrite = value; } + [Obsolete("Replaced by ForceWriteLine. Marked obsolete with NLog v6.0")] + public bool WriteBuffer { get => !ForceWriteLine; set => ForceWriteLine = !value; } /// /// Initializes a new instance of the class. @@ -255,13 +255,13 @@ protected override void Write(IList logEvents) return; } - if (EnableBatchWrite) + if (ForceWriteLine) { - WriteBufferToOutput(logEvents); + base.Write(logEvents); // Console.WriteLine } else { - base.Write(logEvents); // Console.WriteLine + WriteBufferToOutput(logEvents); } } @@ -274,13 +274,13 @@ private void RenderToOutput(Layout layout, LogEventInfo logEvent) var stdErr = RenderLogEvent(StdErr, logEvent); var output = GetOutput(stdErr); - if (EnableBatchWrite) + if (ForceWriteLine) { - WriteBufferToOutput(output, layout, logEvent); + WriteLineToOutput(output, RenderLogEvent(layout, logEvent)); } else { - WriteLineToOutput(output, RenderLogEvent(layout, logEvent)); + WriteBufferToOutput(output, layout, logEvent); } } diff --git a/tests/NLog.UnitTests/Targets/ConsoleTargetTests.cs b/tests/NLog.UnitTests/Targets/ConsoleTargetTests.cs index a5dc3c20d9..9eefd07427 100644 --- a/tests/NLog.UnitTests/Targets/ConsoleTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/ConsoleTargetTests.cs @@ -55,14 +55,14 @@ public void ConsoleOutWriteBufferTest() ConsoleOutTest(true); } - private static void ConsoleOutTest(bool writeBuffer) + private static void ConsoleOutTest(bool forceWriteLine) { var target = new ConsoleTarget() { Header = "-- header --", Layout = "${logger} ${message}", Footer = "-- footer --", - WriteBuffer = writeBuffer, + ForceWriteLine = forceWriteLine, }; var consoleOutWriter = new StringWriter(); From 465fb7057d7968225a46869ca5bb8b50d69b0324 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 30 May 2025 13:32:23 +0200 Subject: [PATCH 143/224] LogEventInfo - New constructor with ReadOnlySpan MessageTemplateParameter (#5850) --- src/NLog/Internal/PropertiesDictionary.cs | 2 +- src/NLog/LogEventInfo.cs | 27 ++++++++++++++++++- .../LogMessageFormatterTests.cs | 22 ++++++++++++--- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/NLog/Internal/PropertiesDictionary.cs b/src/NLog/Internal/PropertiesDictionary.cs index 74a07b04cb..a5c64193ad 100644 --- a/src/NLog/Internal/PropertiesDictionary.cs +++ b/src/NLog/Internal/PropertiesDictionary.cs @@ -98,7 +98,7 @@ public PropertiesDictionary(IList? messageParameters = /// Transforms the list of event-properties into IDictionary-interface /// /// Message-template-parameters - public PropertiesDictionary(IReadOnlyList> eventProperties) + public PropertiesDictionary(IReadOnlyList> eventProperties) { var propertyCount = eventProperties.Count; if (propertyCount > 0) diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index ea8502b164..473d8fcfb1 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -133,6 +133,31 @@ public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] str _messageFormatter = (l) => l._formattedMessage ?? l.Message ?? string.Empty; } +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER + /// + /// Initializes a new instance of the class. + /// + /// Log level. + /// Override default Logger name. Default is used when null + /// Pre-formatted log message for ${message}. + /// Log message-template including parameter placeholders for ${message:raw=true}. + /// Already parsed message template parameters. + public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] string formattedMessage, [Localizable(false)] string messageTemplate, ReadOnlySpan messageTemplateParameters) + : this(level, loggerName, messageTemplate) + { + _formattedMessage = formattedMessage; + _messageFormatter = (l) => l._formattedMessage ?? l.Message ?? string.Empty; + + if (messageTemplateParameters.Length > 0) + { + var messageProperties = new MessageTemplateParameter[messageTemplateParameters.Length]; + for (int i = 0; i < messageTemplateParameters.Length; ++i) + messageProperties[i] = messageTemplateParameters[i]; + _properties = new PropertiesDictionary(messageProperties); + } + } +#endif + #if !NET35 /// /// Initializes a new instance of the class. @@ -141,7 +166,7 @@ public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] str /// Override default Logger name. Default is used when null /// Log message. /// List of event-properties - public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] string message, IReadOnlyList>? eventProperties) + public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] string message, IReadOnlyList>? eventProperties) : this(level, loggerName, null, message, null, null) { if (eventProperties?.Count > 0) diff --git a/tests/NLog.UnitTests/LogMessageFormatterTests.cs b/tests/NLog.UnitTests/LogMessageFormatterTests.cs index 3116d2caa8..6799dcd0fc 100644 --- a/tests/NLog.UnitTests/LogMessageFormatterTests.cs +++ b/tests/NLog.UnitTests/LogMessageFormatterTests.cs @@ -33,6 +33,7 @@ namespace NLog.UnitTests { + using System; using System.Text; using NLog.MessageTemplates; using NLog.Targets; @@ -87,18 +88,26 @@ public void ExtensionsLoggingFormatJsonTest() [Fact] public void ExtensionsLoggingPreFormatJsonTest() { - LogEventInfo logEventInfo1 = new LogEventInfo(LogLevel.Info, "MyLogger", "Login request from John for BestApplicationEver", "Login request from {Username} for {Application}", new[] + LogEventInfo logEventInfo1 = new LogEventInfo(LogLevel.Info, "MyLogger", "Login request from John for BestApplicationEver", "Login request from {Username} for {Application}", (System.Collections.Generic.IList)new[] { new MessageTemplateParameter("Username", "John", null, CaptureType.Normal), new MessageTemplateParameter("Application", "BestApplicationEver", null, CaptureType.Normal) }); - LogEventInfo logEventInfo2 = new LogEventInfo(LogLevel.Info, "MyLogger", "Login request from John for BestApplicationEver", "Login request from {Username} for {Application}", new[] -{ + LogEventInfo logEventInfo2 = new LogEventInfo(LogLevel.Info, "MyLogger", "Login request from John for BestApplicationEver", "Login request from {Username} for {Application}", (System.Collections.Generic.IList)new[] + { new MessageTemplateParameter("Username", "John", null, CaptureType.Normal), new MessageTemplateParameter("Application", new StringBuilder("BestApplicationEver", 32), null, CaptureType.Normal) }); +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER + LogEventInfo logEventInfo3 = new LogEventInfo(LogLevel.Info, "MyLogger", "Login request from John for BestApplicationEver", "Login request from {Username} for {Application}", new Span(new[] + { + new MessageTemplateParameter("Username", "John", null, CaptureType.Normal), + new MessageTemplateParameter("Application", new StringBuilder("BestApplicationEver", 32), null, CaptureType.Normal) + })); +#endif + var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@" @@ -123,6 +132,13 @@ public void ExtensionsLoggingPreFormatJsonTest() var result2 = debugTarget.Layout.Render(logEventInfo2); Assert.Same(result2, debugTarget.LastMessage); +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER + logger.Log(logEventInfo3); + logFactory.Flush(); + var result3 = debugTarget.Layout.Render(logEventInfo3); + Assert.Same(result3, debugTarget.LastMessage); +#endif + logger.Log(logEventInfo1); logFactory.Flush(); var result1 = debugTarget.Layout.Render(logEventInfo1); From dd8e11bcbc99a408466abb65fd5868e583cff135 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 31 May 2025 09:41:20 +0200 Subject: [PATCH 144/224] LogEventBuilder - Align Properties from Span with expected behavior (#5853) --- src/NLog/LogEventBuilder.cs | 42 +++++++++++-------------------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/src/NLog/LogEventBuilder.cs b/src/NLog/LogEventBuilder.cs index 9ed3e56108..81b041d599 100644 --- a/src/NLog/LogEventBuilder.cs +++ b/src/NLog/LogEventBuilder.cs @@ -98,11 +98,10 @@ public LogEventBuilder(ILogger logger, LogLevel logLevel) public LogEventBuilder Property(string propertyName, T? propertyValue) { Guard.ThrowIfNull(propertyName); - - if (_logEvent is null) - return this; - - _logEvent.Properties[propertyName] = propertyValue; + if (_logEvent != null) + { + _logEvent.Properties[propertyName] = propertyValue; + } return this; } @@ -113,12 +112,11 @@ public LogEventBuilder Property(string propertyName, T? propertyValue) public LogEventBuilder Properties(IEnumerable> properties) { Guard.ThrowIfNull(properties); - - if (_logEvent is null) - return this; - - foreach (var property in properties) - _logEvent.Properties[property.Key] = property.Value; + if (_logEvent != null) + { + foreach (var property in properties) + _logEvent.Properties[property.Key] = property.Value; + } return this; } @@ -129,27 +127,11 @@ public LogEventBuilder Properties(IEnumerable> pro /// The properties to set. public LogEventBuilder Properties(params ReadOnlySpan<(string, object?)> properties) { - if (_logEvent is null) - return this; - - if (_logEvent.Parameters is null) + if (_logEvent != null) { - var eventProperties = _logEvent.TryCreatePropertiesInternal(); - if (eventProperties is null) - { - // Now allocate PropertiesDictionary and copy from properties - var messageProperties = new MessageTemplates.MessageTemplateParameter[properties.Length]; - for (int i = 0; i < properties.Length; ++i) - { - messageProperties[i] = new MessageTemplates.MessageTemplateParameter(properties[i].Item1, properties[i].Item2, null); - } - _logEvent.TryCreatePropertiesInternal(messageProperties); - return this; - } + foreach (var property in properties) + _logEvent.Properties[property.Item1] = property.Item2; } - - foreach (var property in properties) - _logEvent.Properties[property.Item1] = property.Item2; return this; } #endif From 56b11e955c3ad7962bd1320b147da7f278a06522 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 31 May 2025 10:46:29 +0200 Subject: [PATCH 145/224] TestTrimPublish - Output process filesize (#5854) --- tests/TestTrimPublish/Program.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/TestTrimPublish/Program.cs b/tests/TestTrimPublish/Program.cs index c215cdf3fa..eadc09056b 100644 --- a/tests/TestTrimPublish/Program.cs +++ b/tests/TestTrimPublish/Program.cs @@ -39,6 +39,9 @@ Console.WriteLine(result); +var processPath = Environment.ProcessPath ?? string.Empty; +Console.WriteLine($"ProcessPath: '{processPath}' with FileSize: {new FileInfo(processPath).Length}"); + if (result == $"{Environment.CurrentManagedThreadId}|Success{System.Environment.NewLine}") return 0; else From 85c44175ae263fd7a3a3d437d6fb95ef24d205ad Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 31 May 2025 17:56:14 +0200 Subject: [PATCH 146/224] NLog.Schema nuget-package using targets to copy file (#5855) --- .../ASPNetBufferingWrapper/web.nlog | 4 ++-- .../Configuration File/ASPNetTrace/web.nlog | 4 ++-- .../AsyncWrapper/NLog.config | 4 ++-- .../AutoFlushWrapper/NLog.config | 4 ++-- .../BufferingWrapper/NLog.config | 4 ++-- .../Configuration File/Chainsaw/NLog.config | 4 ++-- .../Row Highlighting/NLog.config | 4 ++-- .../ColoredConsole/Simple/NLog.config | 4 ++-- .../Word Highlighting/NLog.config | 4 ++-- .../Configuration File/Console/NLog.config | 4 ++-- .../Database/MSSQL/NLog.config | 5 ++-- .../Database/Oracle.Native/NLog.config | 4 ++-- .../Database/Oracle.OleDb/NLog.config | 4 ++-- .../Configuration File/Debug/NLog.config | 4 ++-- .../Configuration File/Debugger/NLog.config | 4 ++-- .../Configuration File/EventLog/NLog.config | 4 ++-- .../FallbackGroup/NLog.config | 4 ++-- .../File/Archive1/NLog.config | 4 ++-- .../File/Archive2/NLog.config | 4 ++-- .../File/Archive3/NLog.config | 4 ++-- .../File/Archive4/NLog.config | 4 ++-- .../File/Asynchronous/NLog.config | 4 ++-- .../Configuration File/File/CSV/NLog.config | 4 ++-- .../File/Multiple/NLog.config | 4 ++-- .../File/Multiple2/NLog.config | 4 ++-- .../File/Simple/NLog.config | 4 ++-- .../FilteringWrapper/NLog.config | 5 ++-- .../FormControl/NLog.config | 4 ++-- .../Configuration File/Log4JXml/NLog.config | 5 ++-- .../MSMQ/Multiple/NLog.config | 4 ++-- .../MSMQ/Simple/NLog.config | 4 ++-- .../Mail/Buffered/NLog.config | 4 ++-- .../Mail/Simple/NLog.config | 4 ++-- .../Configuration File/Memory/NLog.config | 5 ++-- .../Configuration File/MessageBox/NLog.config | 5 ++-- .../Configuration File/MethodCall/NLog.config | 5 ++-- .../Configuration File/NLogViewer/NLog.config | 5 ++-- .../Configuration File/Network/NLog.config | 5 ++-- .../Configuration File/Null/NLog.config | 5 ++-- .../OutputDebugString/NLog.config | 5 ++-- .../PerfCounter/NLog.config | 5 ++-- .../PostFilteringWrapper/NLog.config | 5 ++-- .../RandomizeGroup/NLog.config | 5 ++-- .../RepeatingWrapper/NLog.config | 5 ++-- .../RetryingWrapper/NLog.config | 5 ++-- .../RichTextBox/RowColoring/NLog.config | 4 ++-- .../RichTextBox/Simple/NLog.config | 4 ++-- .../RichTextBox/WordColoring/NLog.config | 4 ++-- .../RoundRobinGroup/NLog.config | 5 ++-- .../Configuration File/SplitGroup/NLog.config | 5 ++-- .../Configuration File/Trace/NLog.config | 5 ++-- .../Configuration File/Variables/NLog.config | 4 ++-- .../Configuration File/WebService/NLog.config | 6 ++--- src/NuGet/NLog.Config/content/NLog.config | 3 +-- src/NuGet/NLog.Schema/NLog.Schema.nuspec | 23 ++++++++++++++----- src/NuGet/NLog.Schema/NLog.Schema.targets | 7 ++++++ 56 files changed, 149 insertions(+), 116 deletions(-) create mode 100644 src/NuGet/NLog.Schema/NLog.Schema.targets diff --git a/examples/targets/Configuration File/ASPNetBufferingWrapper/web.nlog b/examples/targets/Configuration File/ASPNetBufferingWrapper/web.nlog index f67132d5ab..204797e30f 100644 --- a/examples/targets/Configuration File/ASPNetBufferingWrapper/web.nlog +++ b/examples/targets/Configuration File/ASPNetBufferingWrapper/web.nlog @@ -1,6 +1,6 @@ - + diff --git a/examples/targets/Configuration File/ASPNetTrace/web.nlog b/examples/targets/Configuration File/ASPNetTrace/web.nlog index 395dbb4b6b..6c24c922b4 100644 --- a/examples/targets/Configuration File/ASPNetTrace/web.nlog +++ b/examples/targets/Configuration File/ASPNetTrace/web.nlog @@ -1,6 +1,6 @@ - + diff --git a/examples/targets/Configuration File/AsyncWrapper/NLog.config b/examples/targets/Configuration File/AsyncWrapper/NLog.config index b26d0555a7..2e6ed0e7ef 100644 --- a/examples/targets/Configuration File/AsyncWrapper/NLog.config +++ b/examples/targets/Configuration File/AsyncWrapper/NLog.config @@ -1,6 +1,6 @@ - + diff --git a/examples/targets/Configuration File/Database/Oracle.OleDb/NLog.config b/examples/targets/Configuration File/Database/Oracle.OleDb/NLog.config index cef7f5a737..ea356d77cc 100644 --- a/examples/targets/Configuration File/Database/Oracle.OleDb/NLog.config +++ b/examples/targets/Configuration File/Database/Oracle.OleDb/NLog.config @@ -1,6 +1,6 @@ - + diff --git a/examples/targets/Configuration File/Debug/NLog.config b/examples/targets/Configuration File/Debug/NLog.config index c1693fa3ce..b2c14b90d9 100644 --- a/examples/targets/Configuration File/Debug/NLog.config +++ b/examples/targets/Configuration File/Debug/NLog.config @@ -1,6 +1,6 @@ - + diff --git a/examples/targets/Configuration File/Debugger/NLog.config b/examples/targets/Configuration File/Debugger/NLog.config index 6dca1efdc8..03be50598b 100644 --- a/examples/targets/Configuration File/Debugger/NLog.config +++ b/examples/targets/Configuration File/Debugger/NLog.config @@ -1,6 +1,6 @@ - + diff --git a/examples/targets/Configuration File/EventLog/NLog.config b/examples/targets/Configuration File/EventLog/NLog.config index 7447ea3b26..58d34063a5 100644 --- a/examples/targets/Configuration File/EventLog/NLog.config +++ b/examples/targets/Configuration File/EventLog/NLog.config @@ -1,6 +1,6 @@ - + diff --git a/examples/targets/Configuration File/FallbackGroup/NLog.config b/examples/targets/Configuration File/FallbackGroup/NLog.config index 02550c5dfb..511b0f2668 100644 --- a/examples/targets/Configuration File/FallbackGroup/NLog.config +++ b/examples/targets/Configuration File/FallbackGroup/NLog.config @@ -1,6 +1,6 @@ - + diff --git a/examples/targets/Configuration File/File/Archive1/NLog.config b/examples/targets/Configuration File/File/Archive1/NLog.config index a7aed6451e..6cae76c416 100644 --- a/examples/targets/Configuration File/File/Archive1/NLog.config +++ b/examples/targets/Configuration File/File/Archive1/NLog.config @@ -1,6 +1,6 @@ - + - + - + - + - + diff --git a/src/NuGet/NLog.Schema/NLog.Schema.targets b/src/NuGet/NLog.Schema/NLog.Schema.targets new file mode 100644 index 0000000000..364205594a --- /dev/null +++ b/src/NuGet/NLog.Schema/NLog.Schema.targets @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file From 91c26d2f879d7ae7f9d5dd87958871a2d5ecc491 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 1 Jun 2025 08:25:23 +0200 Subject: [PATCH 147/224] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d85bdddb27..97ce69d3bb 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ Contributing As the current NLog team is a small team, we cannot fix every bug or implement every feature on our own. So contributions are really appreciated! If you like to start with a small task, then -[up-for-grabs](https://github.com/NLog/NLog/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs+-label%3A%22almost+ready%22+) are nice to start with. +[up-for-grabs](https://github.com/NLog/NLog/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs) are nice to start with. Please note, we have a `dev` and `master` branch From b3b8b8f0720447f9830c6df6a8aa34bd26e7d171 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 1 Jun 2025 09:40:46 +0200 Subject: [PATCH 148/224] Moved details about contribution into CONTRIBUTING.md (#5856) --- .github/CONTRIBUTING.md => CONTRIBUTING.md | 38 ++++++++++++++- README.md | 54 +++------------------- src/NLog.sln | 2 +- 3 files changed, 45 insertions(+), 49 deletions(-) rename .github/CONTRIBUTING.md => CONTRIBUTING.md (68%) diff --git a/.github/CONTRIBUTING.md b/CONTRIBUTING.md similarity index 68% rename from .github/CONTRIBUTING.md rename to CONTRIBUTING.md index d259a6f481..a335932af2 100644 --- a/.github/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ Support & contributing guidelines === -Do you have feature requests, questions or would you like to report a bug? Please follow these guidelines when posting on the [issue list](https://github.com/NLog/NLog/issues). The issues are labeled with the [following guideline](/issue-labeling.md). +Do you have feature requests, questions or would you like to report a bug? Please follow these guidelines when posting on the [issue list](https://github.com/NLog/NLog/issues). Feature requests ---- @@ -77,3 +77,39 @@ git push -f if `rebase` won't work well, use `git merge master` as alternative. It's also possible to send a PR in the opposite direction, but that's not preferred as it will pollute the commit log. + + +Contributing +--- +As the current NLog team is a small team, we cannot fix every bug or implement every feature on our own. So contributions are really appreciated! + +If you like to start with a small task, then +[up-for-grabs](https://github.com/NLog/NLog/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs+-label%3A%22almost+ready%22+) are nice to start with. + +Please note, we have a `dev` and `master` branch + +- `master` is for pure bug fixes and targets NLog 5.x +- `dev` targets NLog 6 + + +A good way to get started (flow) + +1. Fork the NLog repos. +2. Create a new branch in you current repos from the 'dev' branch. (critical bugfixes from 'master') +3. 'Check out' the code with Git or [GitHub Desktop](https://desktop.github.com/) +4. Push commits and create a Pull Request (PR) to NLog + +Please note: bugfixes should target the **master** branch, others the **dev** branch (NLog 6) + + +How to build +--- +Use Visual Studio 2022 and open the solution 'NLog.sln'. + +For building in the cloud we use: +- AppVeyor for Windows- and Linux-builds +- SonarQube for code coverage + +Trying to build your fork in the cloud? Check [this how-to](howto-build-your-fork.md) + +Note: master points to NLog 5.x and dev to NLog 6 diff --git a/README.md b/README.md index 97ce69d3bb..1867883d8c 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=nlog2&metric=coverage&branch=dev)](https://sonarcloud.io/dashboard?id=nlog2&branch=dev) - [![](https://img.shields.io/badge/Docs-GitHub%20wiki-brightgreen)](https://github.com/NLog/NLog/wiki) [![](https://img.shields.io/badge/Troubleshoot-Guide-orange)](https://github.com/nlog/nlog/wiki/Logging-troubleshooting) @@ -42,30 +41,29 @@ For the possible options in the config, check the [Options list](https://nlog-pr Having troubles? Check the [troubleshooting guide](https://github.com/NLog/NLog/wiki/Logging-troubleshooting) - - ----- + ℹ️ NLog 6.0 will support AOT [NLog 6.0 RC1](https://www.nuget.org/packages/NLog/6.0.0-rc1#releasenotes-body-tab) now available. See also [List of major changes in NLog 6.0](https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html) + NLog Packages --- -The NLog-nuget-package provides everything needed for doing file- and console-logging. But there are also multiple NLog extension packages, +The NLog-nuget-package provides everything needed for doing file- and console-logging. But there are many NLog extension packages, that provides additional target- and layout-output. See [targets](https://nlog-project.org/config/?tab=targets) and [layout renderers](https://nlog-project.org/config/?tab=layout-renderers) overview! -See Nuget/build status of all official packages [here](https://github.com/NLog/NLog/blob/dev/packages-and-status.md) +See Nuget/build status of the NLog extension packages maintained by the NLog-project [here](https://github.com/NLog/NLog/blob/dev/packages-and-status.md) Questions, bug reports or feature requests? --- -Issues with getting it working? -Please check the [troubleshooting guide](https://github.com/NLog/NLog/wiki/Logging-troubleshooting) before asking! With a clear error message, it's really easier to solve the issue! +If having issues with getting NLog working? Then please check the [troubleshooting guide](https://github.com/NLog/NLog/wiki/Logging-troubleshooting) before asking! This will often provide you with clear error message when asking, so it is easier to solve the issue! -Unclear how to configure NLog correctly of other questions? Please post questions on [StackOverflow](https://stackoverflow.com/). +If having questions about how to configure NLog correctly? Then please post questions on [StackOverflow](https://stackoverflow.com/questions/tagged/nlog) (using the `nlog` tag) -Do you have feature request or would you like to report a bug? Please post them on the [issue list](https://github.com/NLog/NLog/issues) and follow [these guidelines](.github/CONTRIBUTING.md). +Have you found a bug or issue with NLog functionality? Please post them on the [issue list](https://github.com/NLog/NLog/issues) and follow [these guidelines](/CONTRIBUTING.md). Frequently Asked Questions (FAQ) @@ -73,45 +71,7 @@ Frequently Asked Questions (FAQ) See [FAQ on the Wiki](https://github.com/NLog/NLog/wiki/faq) -Contributing ---- -As the current NLog team is a small team, we cannot fix every bug or implement every feature on our own. So contributions are really appreciated! - -If you like to start with a small task, then -[up-for-grabs](https://github.com/NLog/NLog/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs) are nice to start with. - -Please note, we have a `dev` and `master` branch - -- `master` is for pure bug fixes and targets NLog 5.x -- `dev` targets NLog 6 - - -A good way to get started (flow) - -1. Fork the NLog repos. -1. Create a new branch in you current repos from the 'dev' branch. (critical bugfixes from 'master') -1. 'Check out' the code with Git or [GitHub Desktop](https://desktop.github.com/) -1. Check [contributing.md](.github/CONTRIBUTING.md#sync-projects) -1. Push commits and create a Pull Request (PR) to NLog - -Please note: bugfixes should target the **master** branch, others the **dev** branch (NLog 6) - - License --- NLog is open source software, licensed under the terms of BSD license. See [LICENSE.txt](LICENSE.txt) for details. - - -How to build ---- -Use Visual Studio 2019 and open the solution 'NLog.sln'. - -For building in the cloud we use: -- AppVeyor for Windows- and Linux-builds -- SonarQube for code coverage - -Trying to build your fork in the cloud? Check [this how-to](howto-build-your-fork.md) - -Note: master points to NLog 5.x and dev to NLog 6 - diff --git a/src/NLog.sln b/src/NLog.sln index 15391f8a6e..429b5d4f52 100644 --- a/src/NLog.sln +++ b/src/NLog.sln @@ -28,7 +28,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\appveyor.yml = ..\appveyor.yml ..\build.ps1 = ..\build.ps1 ..\CHANGELOG.md = ..\CHANGELOG.md - ..\.github\CONTRIBUTING.md = ..\.github\CONTRIBUTING.md + ..\CONTRIBUTING.md = ..\CONTRIBUTING.md ..\.github\dependabot.yml = ..\.github\dependabot.yml NLog.proj = NLog.proj ..\README.md = ..\README.md From 7586dbdf607e33be6287cd87862b1a7bb966aa0b Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 1 Jun 2025 09:46:01 +0200 Subject: [PATCH 149/224] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a335932af2..eaf04fc5a9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -84,7 +84,7 @@ Contributing As the current NLog team is a small team, we cannot fix every bug or implement every feature on our own. So contributions are really appreciated! If you like to start with a small task, then -[up-for-grabs](https://github.com/NLog/NLog/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs+-label%3A%22almost+ready%22+) are nice to start with. +[up-for-grabs](https://github.com/NLog/NLog/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs) are nice to start with. Please note, we have a `dev` and `master` branch From 696c82bf7c5e2b4aa7e7cd84eecbeec77512ea3d Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 1 Jun 2025 12:07:34 +0200 Subject: [PATCH 150/224] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index cdaf5f100b..0918c860e4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,9 +8,9 @@ Hi! Thanks for reporting this bug! Please keep / fill in the relevant info from this template so that we can help you as best as possible. -**NLog version**: (e.g. 4.4.13) +**NLog version**: (e.g. 4.7.15) -**Platform**: .Net 3.5 / .Net 4.0 / .Net 4.5 / Mono 4 / Xamarin Android / Xamarin iOS / .NET Core / .NET5 / .NET6 +**Platform**: .NET8 / .NET Framework 4.8 / Linux / Android / iOS / UWP / Unity / etc. **Current NLog config** (xml or C#, if relevant) From 600fade3da11716306b320a0a0cc60e4b8844c99 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 1 Jun 2025 12:08:54 +0200 Subject: [PATCH 151/224] Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index b7bafb5b71..362a620476 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -8,9 +8,9 @@ Hi! Thanks for reporting this feature! Please keep / fill in the relevant info from this template so that we can help you as best as possible. -**NLog version**: (e.g. 4.4.13) +**NLog version**: (e.g. 4.7.15) -**Platform**: .Net 3.5 / .Net 4.0 / .Net 4.5 / Mono 4 / Xamarin Android / Xamarin iOS / .NET Core / .NET5 / .NET6 +**Platform**: .NET8 / .NET Framework 4.8 / Linux / Android / iOS / UWP / Unity / etc. **Current NLog config** (xml or C#, if relevant) From ad1751f9258b64353479a17ba37d590540bc80f1 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 1 Jun 2025 13:33:06 +0200 Subject: [PATCH 152/224] Update CONTRIBUTING.md --- CONTRIBUTING.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eaf04fc5a9..0ba91312d4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,11 +47,9 @@ Keep in mind that multiple versions of .NET are supported. Some methods are not ``` #if NET35 -#if NET45 -#if NET46 +#if NETFRAMEWORK #if NETSTANDARD -#if NETSTANDARD1_3 -#if NETSTANDARD1_5 +#if NETSTANDARD2_1_OR_GREATER ``` Update your fork From 4631ea0c95b282d09bbae992f38b7ab1c2d32f2d Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 1 Jun 2025 14:15:30 +0200 Subject: [PATCH 153/224] Update CONTRIBUTING.md --- CONTRIBUTING.md | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0ba91312d4..0b2fa0edf8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,10 +48,11 @@ Keep in mind that multiple versions of .NET are supported. Some methods are not ``` #if NET35 #if NETFRAMEWORK -#if NETSTANDARD #if NETSTANDARD2_1_OR_GREATER ``` +.NET Framework is now the odd one, so focus should be on using `NETFRAMEWORK` to mark code that doesn't support NetStandard or NetCore. + Update your fork === Is your fork not up-to-date with the NLog code? Most of the time that isn't a problem. But if you like to "sync back" the changes to your repository, execute the following command: @@ -61,9 +62,7 @@ The first time: git remote add upstream https://github.com/NLog/NLog.git ``` - After that you repository will have two remotes. You could update your remote (the fork) in the following way: - ``` git fetch upstream git checkout @@ -76,9 +75,8 @@ if `rebase` won't work well, use `git merge master` as alternative. It's also possible to send a PR in the opposite direction, but that's not preferred as it will pollute the commit log. - Contributing ---- +=== As the current NLog team is a small team, we cannot fix every bug or implement every feature on our own. So contributions are really appreciated! If you like to start with a small task, then @@ -101,7 +99,7 @@ Please note: bugfixes should target the **master** branch, others the **dev** br How to build ---- +=== Use Visual Studio 2022 and open the solution 'NLog.sln'. For building in the cloud we use: @@ -111,3 +109,17 @@ For building in the cloud we use: Trying to build your fork in the cloud? Check [this how-to](howto-build-your-fork.md) Note: master points to NLog 5.x and dev to NLog 6 + +Official list of NLog extensions +=== +NLog Project Homepage provides a list of available [NLog Targets and Layouts](https://nlog-project.org/config/) + +To add a new NLog extension, then just create pull-request here: + +- https://github.com/NLog/NLog.github.io/pulls + +Updating the relevant file: + +- **NLog Targets** - https://github.com/NLog/NLog.github.io/blob/master/config/targets.json +- **NLog Layouts** - https://github.com/NLog/NLog.github.io/blob/master/config/layouts.json +- **NLog LayoutRenderers** - https://github.com/NLog/NLog.github.io/blob/master/config/layout-renderers.json From a8483abe63da8d2eccea488b35ad7d24b2b7177f Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 1 Jun 2025 14:17:36 +0200 Subject: [PATCH 154/224] Update CONTRIBUTING.md --- CONTRIBUTING.md | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0b2fa0edf8..cf75d5f58d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,7 +42,7 @@ Please document any public method and property. Document **why** and not how. At Multiple .NET versions -=== +---- Keep in mind that multiple versions of .NET are supported. Some methods are not available in all .NET versions. The following conditional compilation symbols can be used: ``` @@ -53,30 +53,8 @@ Keep in mind that multiple versions of .NET are supported. Some methods are not .NET Framework is now the odd one, so focus should be on using `NETFRAMEWORK` to mark code that doesn't support NetStandard or NetCore. -Update your fork -=== -Is your fork not up-to-date with the NLog code? Most of the time that isn't a problem. But if you like to "sync back" the changes to your repository, execute the following command: - -The first time: -``` -git remote add upstream https://github.com/NLog/NLog.git -``` - -After that you repository will have two remotes. You could update your remote (the fork) in the following way: -``` -git fetch upstream -git checkout -git rebase upstream/master -..fix if needed and -git push -f -``` - -if `rebase` won't work well, use `git merge master` as alternative. - -It's also possible to send a PR in the opposite direction, but that's not preferred as it will pollute the commit log. - Contributing -=== +---- As the current NLog team is a small team, we cannot fix every bug or implement every feature on our own. So contributions are really appreciated! If you like to start with a small task, then @@ -97,9 +75,31 @@ A good way to get started (flow) Please note: bugfixes should target the **master** branch, others the **dev** branch (NLog 6) +Update your fork +---- +Is your fork not up-to-date with the NLog code? Most of the time that isn't a problem. But if you like to "sync back" the changes to your repository, execute the following command: + +The first time: +``` +git remote add upstream https://github.com/NLog/NLog.git +``` + +After that you repository will have two remotes. You could update your remote (the fork) in the following way: +``` +git fetch upstream +git checkout +git rebase upstream/master +..fix if needed and +git push -f +``` + +if `rebase` won't work well, use `git merge master` as alternative. + +It's also possible to send a PR in the opposite direction, but that's not preferred as it will pollute the commit log. + How to build -=== +---- Use Visual Studio 2022 and open the solution 'NLog.sln'. For building in the cloud we use: From 80c3da6420554dd0e18a05cf633f062e45c8c848 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 1 Jun 2025 20:47:12 +0200 Subject: [PATCH 155/224] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf75d5f58d..a2a223b06e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -110,7 +110,7 @@ Trying to build your fork in the cloud? Check [this how-to](howto-build-your-for Note: master points to NLog 5.x and dev to NLog 6 -Official list of NLog extensions +List of NLog extensions === NLog Project Homepage provides a list of available [NLog Targets and Layouts](https://nlog-project.org/config/) From 216ebe6e1a664793f42ca29e5972c92abef75be1 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 1 Jun 2025 20:54:52 +0200 Subject: [PATCH 156/224] Version 6.0 RC2 (#5851) --- CHANGELOG.md | 39 +++++++++++++- build.ps1 | 2 +- src/NLog/Config/Factory.cs | 52 +++++++++++++++++++ src/NLog/LogEventInfo.cs | 25 --------- src/NLog/NLog.csproj | 10 +++- .../LogMessageFormatterTests.cs | 15 ------ 6 files changed, 100 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ff3993c03..eaf832b632 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,46 @@ Date format: (year/month/day) ## Change Log +### Version 6.0 RC2 (2025/06/01) + +**Improvements** +- Fixed NLog XmlParser to support XML comments within XML processing instructions. +- NLog.Targets.Network now also supports NET35. +- Updated structs to be readonly to allow compiler optimizations. +- Updated interface ILoggingConfigurationElement to support nullable Values. +- Updated all projects to include `` +- Optimized ConsoleTarget to not use Console.WriteLine, and introduced option `ForceWriteLine` +- Updated NLog.Schema nuget-package to include targets-file to copy NLog.xsd to project-folder. +- Improved configuration-file loading to advise about NLog nuget-packages for missing types. + +### Version 5.5 (2025/05/29) + +**Improvements** +- [#5710](https://github.com/NLog/NLog/pull/5710) Restored LogFactory.Setup().SetupFromEnvironmentVariables() as not obsolete (#5710) @snakefoot +- [#5717](https://github.com/NLog/NLog/pull/5717) Avoid using MakeGenericType for Dictionary enumeration when AOT (#5717) @snakefoot +- [#5730](https://github.com/NLog/NLog/pull/5730) Stop using obsolete Assembly.CodeBase for NetStandard (#5730) @snakefoot +- [#5742](https://github.com/NLog/NLog/pull/5742) ExceptionLayoutRenderer - Handle Exception properties like StackTrace can throw with AOT (#5742) @snakefoot +- [#5743](https://github.com/NLog/NLog/pull/5743) ExceptionLayoutRenderer - Handle Data-collection-item ToString can throw with AOT (#5743) @snakefoot +- [#5763](https://github.com/NLog/NLog/pull/5763) ExceptionLayoutRenderer - Handle Exception-properties can throw with AOT (#5763) @snakefoot +- [#5756](https://github.com/NLog/NLog/pull/5756) ServiceRepository - Improve exception-handling when resolving service-types while disposing (#5756) @snakefoot +- [#5759](https://github.com/NLog/NLog/pull/5759) LayoutRenderer - Optimize performance by skipping cache result from render Inner Layout (#5759) @snakefoot +- [#5795](https://github.com/NLog/NLog/pull/5795) ConditionLayoutExpression - Optimize performance by skipping cache result from render Inner Layout (#5795) @snakefoot +- [#5731](https://github.com/NLog/NLog/pull/5731) Mark IFactory RegisterType as obsolete, since it will be removed with NLog v6 (#5731) @snakefoot +- [#5766](https://github.com/NLog/NLog/pull/5766) Mark JsonLayout EscapeForwardSlash as obsolete, since disabled with NLog v6 (#5766) @snakefoot +- [#5823](https://github.com/NLog/NLog/pull/5823) Mark ExceptionLayoutRenderer Formats-List as obsolete, since immutable with NLog v6 (#5823) @snakefoot +- [#5769](https://github.com/NLog/NLog/pull/5769) Updated API-code examples to not depend on obsolete SimpleConfigurator (#5769) @snakefoot +- [#5776](https://github.com/NLog/NLog/pull/5776) ObjectReflectionCache - Handle PropertyValue can throw with AOT (#5776) @snakefoot +- [#5780](https://github.com/NLog/NLog/pull/5780) NetworkTarget - Introduced option NoDelay to disable delayed ACK (#5780) @snakefoot +- [#5788](https://github.com/NLog/NLog/pull/5788) Fix InternalLogger noise about reflection for FuncLayoutRenderer (#5788) @snakefoot +- [#5792](https://github.com/NLog/NLog/pull/5792) TargetWithContext - Reduce allocation for RenderLogEvent when SimpleLayout (#5792) @snakefoot +- [#5810](https://github.com/NLog/NLog/pull/5810) Refactoring to improve null value handling (#5810) @snakefoot +- [#5812](https://github.com/NLog/NLog/pull/5812) Refactoring to improve null value handling (#5812) @snakefoot +- [#5817](https://github.com/NLog/NLog/pull/5817) LoggingConfigurationParser - Prioritize LoggingRules from current config (#5817) @snakefoot +- [#5825](https://github.com/NLog/NLog/pull/5825) WhenEmptyLayoutRendererWrapper - Optimize IStringValueRenderer Logic (#5825) @snakefoot + ### Version 6.0 RC1 (2025/04/25) -**Major changes** +**Improvements** - Updated NLog API with `enable` and introduced `Layout.Empty` - Marked `[RequiredParameter]` as obsolete, and replaced with explicit option validation during initialization. - Marked `LogEventInfo.SequenceID` and `${sequenceid}` as obsolete, and instead use `${counter:sequence=global}`. diff --git a/build.ps1 b/build.ps1 index 10b09094f0..b84ca6bd41 100644 --- a/build.ps1 +++ b/build.ps1 @@ -3,7 +3,7 @@ dotnet --version $versionPrefix = "6.0.0" -$versionSuffix = "rc1" +$versionSuffix = "rc2" $versionFile = $versionPrefix + "." + ${env:APPVEYOR_BUILD_NUMBER} $versionProduct = $versionPrefix; if (-Not $versionSuffix.Equals("")) diff --git a/src/NLog/Config/Factory.cs b/src/NLog/Config/Factory.cs index 8a1f328dab..70b4a2f750 100644 --- a/src/NLog/Config/Factory.cs +++ b/src/NLog/Config/Factory.cs @@ -254,6 +254,54 @@ public static TBaseType CreateInstance(this IFactory facto { message += " - Extension NLog.Database not included?"; } + else if (normalName?.StartsWith("network", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.Targets.Network not included?"; + } + else if (normalName?.StartsWith("nlogviewer", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.Targets.Network not included?"; + } + else if (normalName?.StartsWith("chainsaw", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.Targets.Network not included?"; + } + else if (normalName?.StartsWith("Log4JXml", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.Targets.Network not included?"; + } + else if (normalName?.StartsWith("syslog", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.Targets.Network not included?"; + } + else if (normalName?.StartsWith("gelf", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.Targets.Network not included?"; + } + else if (normalName?.StartsWith("localip", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.Targets.Network not included?"; + } + else if (normalName?.StartsWith("atomFile", StringComparison.OrdinalIgnoreCase) == true || normalName?.StartsWith("atomicFile", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.Targets.AtomicFile not included?"; + } + else if (normalName?.StartsWith("GZipFile", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.Targets.GZipFile not included?"; + } + else if (normalName?.StartsWith("trace", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.Targets.Trace not included?"; + } + else if (normalName?.StartsWith("mailkit", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.MailKit not included?"; + } + else if (normalName?.StartsWith("mail", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.Targets.Mail not included?"; + } else if (normalName?.StartsWith("eventlog", StringComparison.OrdinalIgnoreCase) == true) { message += " - Extension NLog.WindowsEventLog not included?"; @@ -270,6 +318,10 @@ public static TBaseType CreateInstance(this IFactory facto { message += " - Extension NLog.PerformanceCounter not included?"; } + else if (normalName?.StartsWith("regexreplace", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.RegEx not included?"; + } else { message += " - Verify type-alias and check extension is included."; diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index 473d8fcfb1..1e53bf4c3c 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -133,31 +133,6 @@ public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] str _messageFormatter = (l) => l._formattedMessage ?? l.Message ?? string.Empty; } -#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER - /// - /// Initializes a new instance of the class. - /// - /// Log level. - /// Override default Logger name. Default is used when null - /// Pre-formatted log message for ${message}. - /// Log message-template including parameter placeholders for ${message:raw=true}. - /// Already parsed message template parameters. - public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] string formattedMessage, [Localizable(false)] string messageTemplate, ReadOnlySpan messageTemplateParameters) - : this(level, loggerName, messageTemplate) - { - _formattedMessage = formattedMessage; - _messageFormatter = (l) => l._formattedMessage ?? l.Message ?? string.Empty; - - if (messageTemplateParameters.Length > 0) - { - var messageProperties = new MessageTemplateParameter[messageTemplateParameters.Length]; - for (int i = 0; i < messageTemplateParameters.Length; ++i) - messageProperties[i] = messageTemplateParameters[i]; - _properties = new PropertiesDictionary(messageProperties); - } - } -#endif - #if !NET35 /// /// Initializes a new instance of the class. diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index a5fa313545..07027813e5 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -28,7 +28,7 @@ For ASP.NET Core, check: https://www.nuget.org/packages/NLog.Web.AspNetCore Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ -NLog v6.0 RC1 with the following major changes: +NLog v6.0 RC2 with the following major changes: - Support AOT builds without build warnings. - New FileTarget without ConcurrentWrites support, but still support KeepFileOpen true / false. @@ -51,6 +51,14 @@ NLog v6.0 RC1 with the following major changes: - Skip LogEventInfo.Parameters-array-allocation when unable to defer message-template formatting. - Renamed ChainsawTarget to Log4JXmlTarget to match Log4JXmlEventLayout - Updated NLog.Schema to include intellisense for multiple NLog-assemblies. +- Fixed NLog XmlParser to support XML comments within XML processing instructions. +- NLog.Targets.Network now also supports NET35. +- Updated structs to be readonly to allow compiler optimizations. +- Updated interface ILoggingConfigurationElement to support nullable Values. +- Updated all projects to include <IsAotCompatible> +- Optimized ConsoleTarget to not use Console.WriteLine, and introduced option ForceWriteLine +- Updated NLog.Schema nuget-package to include targets-file to copy NLog.xsd to project-folder. +- Improved configuration-file loading to advise about NLog nuget-packages for missing types. NLog v6.0 release notes: https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html diff --git a/tests/NLog.UnitTests/LogMessageFormatterTests.cs b/tests/NLog.UnitTests/LogMessageFormatterTests.cs index 6799dcd0fc..9b26543809 100644 --- a/tests/NLog.UnitTests/LogMessageFormatterTests.cs +++ b/tests/NLog.UnitTests/LogMessageFormatterTests.cs @@ -100,14 +100,6 @@ public void ExtensionsLoggingPreFormatJsonTest() new MessageTemplateParameter("Application", new StringBuilder("BestApplicationEver", 32), null, CaptureType.Normal) }); -#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER - LogEventInfo logEventInfo3 = new LogEventInfo(LogLevel.Info, "MyLogger", "Login request from John for BestApplicationEver", "Login request from {Username} for {Application}", new Span(new[] - { - new MessageTemplateParameter("Username", "John", null, CaptureType.Normal), - new MessageTemplateParameter("Application", new StringBuilder("BestApplicationEver", 32), null, CaptureType.Normal) - })); -#endif - var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@" @@ -132,13 +124,6 @@ public void ExtensionsLoggingPreFormatJsonTest() var result2 = debugTarget.Layout.Render(logEventInfo2); Assert.Same(result2, debugTarget.LastMessage); -#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER - logger.Log(logEventInfo3); - logFactory.Flush(); - var result3 = debugTarget.Layout.Render(logEventInfo3); - Assert.Same(result3, debugTarget.LastMessage); -#endif - logger.Log(logEventInfo1); logFactory.Flush(); var result1 = debugTarget.Layout.Render(logEventInfo1); From 7564b9a8b933a7b28c2d0227d23f10245dd80532 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 1 Jun 2025 21:30:16 +0200 Subject: [PATCH 157/224] LogEventInfo - New constructor with ReadOnlySpan MessageTemplateParameter (#5859) --- CHANGELOG.md | 1 + src/NLog/LogEventInfo.cs | 25 +++++++++++++++++++ src/NLog/NLog.csproj | 1 + .../LogMessageFormatterTests.cs | 15 +++++++++++ 4 files changed, 42 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaf832b632..b20f7706d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Date format: (year/month/day) - Updated interface ILoggingConfigurationElement to support nullable Values. - Updated all projects to include `` - Optimized ConsoleTarget to not use Console.WriteLine, and introduced option `ForceWriteLine` +- Added new LogEventInfo constructor that supports `ReadOnlySpan` - Updated NLog.Schema nuget-package to include targets-file to copy NLog.xsd to project-folder. - Improved configuration-file loading to advise about NLog nuget-packages for missing types. diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index 1e53bf4c3c..473d8fcfb1 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -133,6 +133,31 @@ public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] str _messageFormatter = (l) => l._formattedMessage ?? l.Message ?? string.Empty; } +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER + /// + /// Initializes a new instance of the class. + /// + /// Log level. + /// Override default Logger name. Default is used when null + /// Pre-formatted log message for ${message}. + /// Log message-template including parameter placeholders for ${message:raw=true}. + /// Already parsed message template parameters. + public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] string formattedMessage, [Localizable(false)] string messageTemplate, ReadOnlySpan messageTemplateParameters) + : this(level, loggerName, messageTemplate) + { + _formattedMessage = formattedMessage; + _messageFormatter = (l) => l._formattedMessage ?? l.Message ?? string.Empty; + + if (messageTemplateParameters.Length > 0) + { + var messageProperties = new MessageTemplateParameter[messageTemplateParameters.Length]; + for (int i = 0; i < messageTemplateParameters.Length; ++i) + messageProperties[i] = messageTemplateParameters[i]; + _properties = new PropertiesDictionary(messageProperties); + } + } +#endif + #if !NET35 /// /// Initializes a new instance of the class. diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index 07027813e5..6c7c1ce759 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -57,6 +57,7 @@ NLog v6.0 RC2 with the following major changes: - Updated interface ILoggingConfigurationElement to support nullable Values. - Updated all projects to include <IsAotCompatible> - Optimized ConsoleTarget to not use Console.WriteLine, and introduced option ForceWriteLine +- Added new LogEventInfo constructor that supports ReadOnlySpan - Updated NLog.Schema nuget-package to include targets-file to copy NLog.xsd to project-folder. - Improved configuration-file loading to advise about NLog nuget-packages for missing types. diff --git a/tests/NLog.UnitTests/LogMessageFormatterTests.cs b/tests/NLog.UnitTests/LogMessageFormatterTests.cs index 9b26543809..6799dcd0fc 100644 --- a/tests/NLog.UnitTests/LogMessageFormatterTests.cs +++ b/tests/NLog.UnitTests/LogMessageFormatterTests.cs @@ -100,6 +100,14 @@ public void ExtensionsLoggingPreFormatJsonTest() new MessageTemplateParameter("Application", new StringBuilder("BestApplicationEver", 32), null, CaptureType.Normal) }); +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER + LogEventInfo logEventInfo3 = new LogEventInfo(LogLevel.Info, "MyLogger", "Login request from John for BestApplicationEver", "Login request from {Username} for {Application}", new Span(new[] + { + new MessageTemplateParameter("Username", "John", null, CaptureType.Normal), + new MessageTemplateParameter("Application", new StringBuilder("BestApplicationEver", 32), null, CaptureType.Normal) + })); +#endif + var logFactory = new LogFactory().Setup().LoadConfigurationFromXml(@" @@ -124,6 +132,13 @@ public void ExtensionsLoggingPreFormatJsonTest() var result2 = debugTarget.Layout.Render(logEventInfo2); Assert.Same(result2, debugTarget.LastMessage); +#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER + logger.Log(logEventInfo3); + logFactory.Flush(); + var result3 = debugTarget.Layout.Render(logEventInfo3); + Assert.Same(result3, debugTarget.LastMessage); +#endif + logger.Log(logEventInfo1); logFactory.Flush(); var result1 = debugTarget.Layout.Render(logEventInfo1); From c0e2c439f40c843919295050dcc6abb00c87da41 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 4 Jun 2025 08:19:58 +0200 Subject: [PATCH 158/224] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1867883d8c..bf0f5c4ad6 100644 --- a/README.md +++ b/README.md @@ -49,13 +49,14 @@ Having troubles? Check the [troubleshooting guide](https://github.com/NLog/NLog/ [NLog 6.0 RC1](https://www.nuget.org/packages/NLog/6.0.0-rc1#releasenotes-body-tab) now available. See also [List of major changes in NLog 6.0](https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html) -NLog Packages +NLog Extensions --- -The NLog-nuget-package provides everything needed for doing file- and console-logging. But there are many NLog extension packages, -that provides additional target- and layout-output. See [targets](https://nlog-project.org/config/?tab=targets) and [layout renderers](https://nlog-project.org/config/?tab=layout-renderers) overview! +The NLog-nuget-package provides everything needed for doing file- and console-logging. If you need other output options, then there are many NLog extension packages available (such as databases, email, cloud services, etc.). +See [targets](https://nlog-project.org/config/?tab=targets) and [layout renderers](https://nlog-project.org/config/?tab=layout-renderers) overview! -See Nuget/build status of the NLog extension packages maintained by the NLog-project [here](https://github.com/NLog/NLog/blob/dev/packages-and-status.md) +The NLog extension packages maintained by the NLog-project are [listed here](https://github.com/NLog/NLog/blob/dev/packages-and-status.md) with Nuget/build status. +It is also possible to [Create your own custom NLog extensions](https://github.com/NLog/NLog/wiki/Extending-NLog). Questions, bug reports or feature requests? --- From 75ab20e594943f99801c9271778f54a4e51711fd Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 4 Jun 2025 18:40:41 +0200 Subject: [PATCH 159/224] NLog.Schema nuget-package code-example (#5860) --- README.md | 2 +- src/NuGet/NLog.Schema/NLog.Schema.nuspec | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bf0f5c4ad6..0cd2a5bdc8 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Having troubles? Check the [troubleshooting guide](https://github.com/NLog/NLog/ ℹ️ NLog 6.0 will support AOT -[NLog 6.0 RC1](https://www.nuget.org/packages/NLog/6.0.0-rc1#releasenotes-body-tab) now available. See also [List of major changes in NLog 6.0](https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html) +[NLog 6.0 RC2](https://www.nuget.org/packages/NLog/6.0.0-rc2#releasenotes-body-tab) now available. See also [List of major changes in NLog 6.0](https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html) NLog Extensions diff --git a/src/NuGet/NLog.Schema/NLog.Schema.nuspec b/src/NuGet/NLog.Schema/NLog.Schema.nuspec index 7560d105df..e32f8d45d4 100644 --- a/src/NuGet/NLog.Schema/NLog.Schema.nuspec +++ b/src/NuGet/NLog.Schema/NLog.Schema.nuspec @@ -12,16 +12,18 @@ The nuget-package will try to copy the XSD-file into the project-folder, so one can reference NLog.xsd from NLog.config: - <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"> + <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"> </nlog> If this nuget-package fails to place the XSD-file into the project-folder then one can download latest here: https://nlog-project.org/schemas/NLog.xsd Alternative enable the Visual Studio option "Automatically download DTDs and schemas" and specify the complete URL: - <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd http://www.nlog-project.org/schemas/NLog.xsd"> + <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd http://www.nlog-project.org/schemas/NLog.xsd"> </nlog> NLog $BuildVersion$ From d0e9983ebf676903eac37e58a68c0d02f66715dc Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 6 Jun 2025 16:12:26 +0200 Subject: [PATCH 160/224] Log4JXmlEventLayout - Support IncludeEmptyValue for Parameters (#5863) --- .../Layouts/Log4JXmlEventLayout.cs | 17 +++--- .../WhenEmptyLayoutRendererWrapper.cs | 4 +- src/NLog/Layouts/XML/XmlElementBase.cs | 57 +++++++++++++++---- .../Log4JXmlTests.cs | 22 +++++-- 4 files changed, 71 insertions(+), 29 deletions(-) diff --git a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs index 08bc9c2741..31d21d7288 100644 --- a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs @@ -294,18 +294,15 @@ protected override void InitializeLayout() IncludeScopeProperties = IncludeScopeProperties }; - foreach (var parameter in Parameters) + dataProperties.ContextProperties?.Clear(); + if (Parameters.Count > 0) { - var propertyElement = new XmlElement("log4j:data", Layout.Empty) + if (dataProperties.ContextProperties is null) + dataProperties.ContextProperties = new List(); + foreach (var parameter in Parameters) { - IncludeEmptyValue = parameter.IncludeEmptyValue, - Attributes = - { - new XmlAttribute("name", parameter.Name), - new XmlAttribute("value", parameter.Layout), - } - }; - dataProperties.Elements.Add(propertyElement); + dataProperties.ContextProperties.Add(new Targets.TargetPropertyWithContext(parameter.Name, parameter.Layout) { IncludeEmptyValue = parameter.IncludeEmptyValue }); + } } dataProperties.Elements.Add(_log4jAppName); diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs index 5f0986dbc8..e48d063619 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs @@ -50,7 +50,7 @@ public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBase, private Func? _stringValueRenderer; /// - /// Gets or sets the layout to be rendered when original layout produced empty result. + /// Gets or sets the layout to be rendered when Inner-layout produces empty result. /// /// public Layout WhenEmpty { get; set; } = Layout.Empty; @@ -60,7 +60,7 @@ protected override void InitializeLayoutRenderer() { _stringValueRenderer = null; - if (WhenEmpty is null) + if (WhenEmpty is null || ReferenceEquals(WhenEmpty, Layout.Empty)) throw new NLogConfigurationException("WhenEmpty-LayoutRenderer WhenEmpty-property must be assigned."); base.InitializeLayoutRenderer(); diff --git a/src/NLog/Layouts/XML/XmlElementBase.cs b/src/NLog/Layouts/XML/XmlElementBase.cs index 7b0d0238d8..5270eae7ce 100644 --- a/src/NLog/Layouts/XML/XmlElementBase.cs +++ b/src/NLog/Layouts/XML/XmlElementBase.cs @@ -40,6 +40,7 @@ namespace NLog.Layouts using System.Text; using NLog.Config; using NLog.Internal; + using NLog.Targets; /// /// A specialized layout that renders XML-formatted events. @@ -82,7 +83,7 @@ protected XmlElementBase(string elementName, Layout elementValue) /// /// Auto indent and create new lines /// - /// + /// public bool IndentXml { get; set; } /// @@ -99,9 +100,17 @@ protected XmlElementBase(string elementName, Layout elementValue) [ArrayParameter(typeof(XmlAttribute), "attribute")] public IList Attributes { get; } = new List(); + /// + /// Gets the collection of context properties that should be included with the other properties. + /// + /// + [ArrayParameter(typeof(TargetPropertyWithContext), "contextproperty")] + public List? ContextProperties { get; set; } + /// /// Gets or sets whether empty XML-element should be included in the output. Default = false /// + /// public bool IncludeEmptyValue { get; set; } /// @@ -122,7 +131,7 @@ protected XmlElementBase(string elementName, Layout elementValue) /// /// Gets or sets a value indicating whether to include contents of the dictionary. /// - /// + /// [Obsolete("Replaced by IncludeScopeProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] public bool IncludeMdc { get => _includeMdc ?? false; set => _includeMdc = value; } @@ -133,7 +142,7 @@ protected XmlElementBase(string elementName, Layout elementValue) /// /// Gets or sets a value indicating whether to include contents of the dictionary. /// - /// + /// [Obsolete("Replaced by IncludeScopeProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] public bool IncludeMdlc { get => _includeMdlc ?? false; set => _includeMdlc = value; } @@ -144,15 +153,15 @@ protected XmlElementBase(string elementName, Layout elementValue) /// /// Gets or sets the option to include all properties from the log event (as XML) /// - /// + /// [Obsolete("Replaced by IncludeEventProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] public bool IncludeAllProperties { get => IncludeEventProperties; set => IncludeEventProperties = value; } /// - /// List of property names to exclude when is true + /// List of property names to exclude when is true /// - /// + /// #if !NET35 public ISet ExcludeProperties { get; set; } #else @@ -167,7 +176,7 @@ protected XmlElementBase(string elementName, Layout elementValue) /// /// Skips closing element tag when having configured /// - /// + /// public string PropertiesElementName { get => _propertiesElementName; @@ -190,7 +199,7 @@ public string PropertiesElementName /// /// Will replace newlines in attribute-value with /// - /// + /// public string PropertiesElementKeyAttribute { get; set; } = DefaultPropertyKeyAttribute; /// @@ -204,19 +213,19 @@ public string PropertiesElementName /// /// Will replace newlines in attribute-value with /// - /// + /// public string PropertiesElementValueAttribute { get; set; } = string.Empty; /// /// XML element name to use for rendering IList-collections items /// - /// + /// public string PropertiesCollectionItemName { get; set; } = DefaultCollectionItemName; /// /// How far should the XML serializer follow object references before backing off /// - /// + /// public int MaxRecursionLimit { get; set; } = 1; private ObjectReflectionCache ObjectReflectionCache => _objectReflectionCache ?? (_objectReflectionCache = new ObjectReflectionCache(LoggingConfiguration.GetServiceProvider())); @@ -257,8 +266,17 @@ protected override void InitializeLayout() } } + if (ContextProperties != null && ContextProperties.Count > 0) + { + foreach (var contextProperty in ContextProperties) + { + if (string.IsNullOrEmpty(contextProperty.Name)) + throw new NLogConfigurationException($"XmlElement(Name={ElementNameInternal}): Contains invalid ContextProperty with unassigned Name-property"); + } + } + var innerLayouts = LayoutWrapper.Inner is null ? ArrayHelper.Empty() : new[] { LayoutWrapper.Inner }; - _precalculateLayouts = (IncludeEventProperties || IncludeScopeProperties) ? null : ResolveLayoutPrecalculation(Attributes.Select(atr => atr.Layout).Concat(Elements.Where(elm => elm.Layout != null).Select(elm => elm.Layout)).Concat(innerLayouts)); + _precalculateLayouts = (IncludeEventProperties || IncludeScopeProperties) ? null : ResolveLayoutPrecalculation(Attributes.Select(atr => atr.Layout).Concat(Elements.Where(elm => elm.Layout != null).Select(elm => elm.Layout)).Concat(ContextProperties?.Select(ctx => ctx.Layout) ?? Enumerable.Empty()).Concat(innerLayouts)); } /// @@ -369,6 +387,9 @@ private bool HasNestedXmlElements(LogEventInfo logEvent) if (Elements.Count > 0) return true; + if (ContextProperties?.Count > 0) + return true; + if (IncludeScopeProperties) return true; @@ -380,6 +401,18 @@ private bool HasNestedXmlElements(LogEventInfo logEvent) private void AppendLogEventXmlProperties(LogEventInfo logEventInfo, StringBuilder sb, int orgLength) { + if (ContextProperties != null) + { + foreach (var contextProperty in ContextProperties) + { + var propertyValue = contextProperty.RenderValue(logEventInfo); + if (!contextProperty.IncludeEmptyValue && StringHelpers.IsNullOrEmptyString(propertyValue)) + continue; + + AppendXmlPropertyValue(contextProperty.Name, propertyValue, sb, orgLength); + } + } + if (IncludeScopeProperties) { bool checkExcludeProperties = ExcludeProperties.Count > 0; diff --git a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs index 29306c512a..d493e977c5 100644 --- a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs +++ b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs @@ -225,13 +225,14 @@ public void Log4JXmlEventLayoutParameterTest() { var log4jLayout = new Log4JXmlEventLayout() { + IncludeEventProperties = false, Parameters = { new Log4JXmlEventParameter { Name = "mt", - Layout = "${message:raw=true}", - } + Layout = "${event-properties:planet}", + }, }, }; log4jLayout.AppInfo = "MyApp"; @@ -240,14 +241,25 @@ public void Log4JXmlEventLayoutParameterTest() LoggerName = "MyLOgger", TimeStamp = new DateTime(2010, 01, 01, 12, 34, 56, DateTimeKind.Utc), Level = LogLevel.Info, - Message = "hello, <{0}>", - Parameters = new[] { "world" } + Message = "hello, {planet}", + Parameters = new[] { "" } }; var threadid = Environment.CurrentManagedThreadId; var machinename = Environment.MachineName; var result = log4jLayout.Render(logEventInfo); - Assert.Equal($"hello, <world>", result); + Assert.Equal($"hello, <earth>", result); + + var logEventInfo2 = new LogEventInfo + { + LoggerName = "MyLOgger", + TimeStamp = new DateTime(2010, 01, 01, 12, 34, 56, DateTimeKind.Utc), + Level = LogLevel.Info, + Message = "hello, ", + }; + + var result2 = log4jLayout.Render(logEventInfo2); + Assert.Equal($"hello, <earth>", result2); } [Fact] From 4b934ef2c0a3fe4a4a3a9135686b48063b018785 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 8 Jun 2025 08:01:39 +0200 Subject: [PATCH 161/224] Version 6.0 RC3 (#5869) --- CHANGELOG.md | 5 +++++ README.md | 2 +- build.ps1 | 4 ++-- src/NLog/NLog.csproj | 3 ++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b20f7706d6..a389e83bb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Date format: (year/month/day) ## Change Log +### Version 6.0 RC2 (2025/06/08) + +**Improvements** +- Log4JXmlEventLayout - Fixed IncludeEmptyValue for Parameters + ### Version 6.0 RC2 (2025/06/01) **Improvements** diff --git a/README.md b/README.md index 0cd2a5bdc8..66ca788c99 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Having troubles? Check the [troubleshooting guide](https://github.com/NLog/NLog/ ℹ️ NLog 6.0 will support AOT -[NLog 6.0 RC2](https://www.nuget.org/packages/NLog/6.0.0-rc2#releasenotes-body-tab) now available. See also [List of major changes in NLog 6.0](https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html) +[NLog 6.0 RC3](https://www.nuget.org/packages/NLog/6.0.0-rc3#releasenotes-body-tab) now available. See also [List of major changes in NLog 6.0](https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html) NLog Extensions diff --git a/build.ps1 b/build.ps1 index b84ca6bd41..8bb1e2cbed 100644 --- a/build.ps1 +++ b/build.ps1 @@ -3,7 +3,7 @@ dotnet --version $versionPrefix = "6.0.0" -$versionSuffix = "rc2" +$versionSuffix = "rc3" $versionFile = $versionPrefix + "." + ${env:APPVEYOR_BUILD_NUMBER} $versionProduct = $versionPrefix; if (-Not $versionSuffix.Equals("")) @@ -45,7 +45,7 @@ create-package 'NLog.OutputDebugString' '"net35;net45;net46;netstandard2.0;netst create-package 'NLog.RegEx' '"net35;net45;net46;netstandard2.0;netstandard2.1"' create-package 'NLog.WindowsRegistry' '"net35;net45;net46;netstandard2.0;netstandard2.1"' create-package 'NLog.Targets.ConcurrentFile' '"net35;net45;net46;netstandard2.0"' -msbuild /t:Restore,Pack ./src/NLog.Targets.AtomicFile/ /p:targetFrameworks="net35;net45;net46;net8.0" /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount +msbuild /t:Restore,Pack ./src/NLog.Targets.AtomicFile/ /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount create-package 'NLog.WindowsEventLog' '"netstandard2.0;netstandard2.1"' msbuild /t:xsd /t:NuGetSchemaPackage ./src/NLog.proj /p:Configuration=Release /p:BuildNetFX45=true /p:BuildVersion=$versionProduct /p:Configuration=Release /p:BuildLabelOverride=NONE /verbosity:minimal diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index 6c7c1ce759..81401c8beb 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -28,7 +28,7 @@ For ASP.NET Core, check: https://www.nuget.org/packages/NLog.Web.AspNetCore Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ -NLog v6.0 RC2 with the following major changes: +NLog v6.0 RC3 with the following major changes: - Support AOT builds without build warnings. - New FileTarget without ConcurrentWrites support, but still support KeepFileOpen true / false. @@ -60,6 +60,7 @@ NLog v6.0 RC2 with the following major changes: - Added new LogEventInfo constructor that supports ReadOnlySpan - Updated NLog.Schema nuget-package to include targets-file to copy NLog.xsd to project-folder. - Improved configuration-file loading to advise about NLog nuget-packages for missing types. +- Log4JXmlEventLayout - Fixed IncludeEmptyValue for Parameters. NLog v6.0 release notes: https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html From 4229ab8220cc3747db2cf7e72e3ebabebe84f28a Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 8 Jun 2025 13:29:26 +0200 Subject: [PATCH 162/224] Mark struct as readonly to allow compiler optimization (#5870) --- src/NLog/Internal/ReusableObjectCreator.cs | 6 +++++- src/NLog/Internal/StringBuilderPool.cs | 6 +++++- src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs | 6 +++++- src/NLog/LogFactory.cs | 6 +++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/NLog/Internal/ReusableObjectCreator.cs b/src/NLog/Internal/ReusableObjectCreator.cs index 43c8778d6d..56209a0dde 100644 --- a/src/NLog/Internal/ReusableObjectCreator.cs +++ b/src/NLog/Internal/ReusableObjectCreator.cs @@ -69,7 +69,11 @@ private void Deallocate(T reusableObject) _reusableObject = reusableObject; } - public struct LockOject : IDisposable + public +#if !NETFRAMEWORK + readonly +#endif + struct LockOject : IDisposable { /// /// Access the acquired reusable object diff --git a/src/NLog/Internal/StringBuilderPool.cs b/src/NLog/Internal/StringBuilderPool.cs index 1bde3bba1b..aeae59d677 100644 --- a/src/NLog/Internal/StringBuilderPool.cs +++ b/src/NLog/Internal/StringBuilderPool.cs @@ -120,7 +120,11 @@ private void Release(StringBuilder stringBuilder, int poolIndex) /// /// Keeps track of acquired pool item /// - public struct ItemHolder : IDisposable + public +#if !NETFRAMEWORK + readonly +#endif + struct ItemHolder : IDisposable { public readonly StringBuilder Item; readonly StringBuilderPool? _owner; diff --git a/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs b/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs index 1eb5cd8c78..84d084d37e 100644 --- a/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs @@ -151,7 +151,11 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) } } - private struct StackFrameList + private +#if !NETFRAMEWORK + readonly +#endif + struct StackFrameList { private readonly StackTrace _stackTrace; private readonly int _startingFrame; diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index 78db73b48a..f54043c7ad 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -1090,7 +1090,11 @@ private string CreateFileNotFoundMessage(string? configFile) /// /// Logger cache key. /// - private struct LoggerCacheKey : IEquatable + private +#if !NETFRAMEWORK + readonly +#endif + struct LoggerCacheKey : IEquatable { public readonly string Name; public readonly Type ConcreteType; From fc84a63b4a8672e86500cd0e5267bdcf2e299d40 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 8 Jun 2025 14:01:09 +0200 Subject: [PATCH 163/224] RegisterObjectTransformation so build trimming will keep public properties (#5871) --- src/NLog/SetupSerializationBuilderExtensions.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/NLog/SetupSerializationBuilderExtensions.cs b/src/NLog/SetupSerializationBuilderExtensions.cs index 01269e556a..6cfa9bb65f 100644 --- a/src/NLog/SetupSerializationBuilderExtensions.cs +++ b/src/NLog/SetupSerializationBuilderExtensions.cs @@ -34,6 +34,8 @@ namespace NLog { using System; + using System.Diagnostics.CodeAnalysis; + using NLog.Common; using NLog.Config; using NLog.Internal; @@ -81,12 +83,23 @@ public static ISetupSerializationBuilder RegisterValueFormatterWithStringQuotes( return setupBuilder; } + /// + /// Registers object Type transformation so build trimming will keep public properties. + /// + public static ISetupSerializationBuilder RegisterObjectTransformation<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(this ISetupSerializationBuilder setupBuilder) + { + Guard.ThrowIfNull(setupBuilder); + InternalLogger.Debug("RegisterObjectTransformation for {0} to keep public properties", typeof(T)); + return setupBuilder; + } + /// /// Registers object Type transformation from dangerous (massive) object to safe (reduced) object /// public static ISetupSerializationBuilder RegisterObjectTransformation(this ISetupSerializationBuilder setupBuilder, Func transformer) { Guard.ThrowIfNull(transformer); + InternalLogger.Debug("RegisterObjectTransformation for {0} to convert safely", typeof(T)); var original = setupBuilder.LogFactory.ServiceRepository.GetService(); setupBuilder.LogFactory.ServiceRepository.RegisterObjectTypeTransformer(new ObjectTypeTransformation(transformer, original)); From ec7be9e99fb2c70fed361d70ad52011c17beddfa Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 8 Jun 2025 18:39:38 +0200 Subject: [PATCH 164/224] DatabaseTarget - Added DbConnectionFactory to improve AOT support (#5873) --- src/NLog.Database/DatabaseTarget.cs | 51 +++++++++++-------- src/NLog.Database/NLog.Database.csproj | 1 - .../DatabaseTargetTests.cs | 4 +- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/NLog.Database/DatabaseTarget.cs b/src/NLog.Database/DatabaseTarget.cs index d505dfa942..cfdf2da21d 100644 --- a/src/NLog.Database/DatabaseTarget.cs +++ b/src/NLog.Database/DatabaseTarget.cs @@ -35,7 +35,6 @@ namespace NLog.Targets { using System; using System.Collections.Generic; - using System.ComponentModel; using System.Data; using System.Data.Common; @@ -80,6 +79,7 @@ namespace NLog.Targets [Target("Database")] public class DatabaseTarget : Target, IInstallable { + private readonly Func _createDbConnectionInstance; private IDbConnection? _activeConnection; private string? _activeConnectionString; @@ -92,6 +92,8 @@ public DatabaseTarget() ConnectionStringsSettings = ConfigurationManager.ConnectionStrings; #endif CommandType = CommandType.Text; + _createDbConnectionInstance = CreateDbConnectionFromType; + DbConnectionFactory = _createDbConnectionInstance; } /// @@ -131,7 +133,6 @@ public DatabaseTarget(string name) : this() /// /// /// - [DefaultValue("sqlserver")] public string DBProvider { get; set; } = "sqlserver"; #if NETFRAMEWORK @@ -174,7 +175,6 @@ public DatabaseTarget(string name) : this() /// database connection open between the log events. /// /// - [DefaultValue(false)] public bool KeepConnection { get; set; } /// @@ -245,7 +245,6 @@ public Layout? DBPassword /// normally be the name of the stored procedure. TableDirect method is not supported in this context. /// /// - [DefaultValue(CommandType.Text)] public CommandType CommandType { get; set; } /// @@ -278,6 +277,11 @@ public Layout? DBPassword /// public System.Data.IsolationLevel? IsolationLevel { get; set; } + /// + /// Gets or sets the factory. By default resolved from type-name + /// + public Func DbConnectionFactory { get; set; } + #if NETFRAMEWORK internal DbProviderFactory? ProviderFactory { get; set; } @@ -319,32 +323,35 @@ public void Uninstall(InstallationContext installationContext) internal IDbConnection OpenConnection(string connectionString, LogEventInfo logEventInfo) { - IDbConnection connection; - -#if NETFRAMEWORK - if (ProviderFactory != null) + var dbConnection = DbConnectionFactory?.Invoke(); + if (dbConnection is null) { - connection = ProviderFactory.CreateConnection(); + throw new NLogRuntimeException("Creation of DbConnection failed"); } - else -#endif + + dbConnection.ConnectionString = connectionString; + if (ConnectionProperties?.Count > 0) { - connection = (IDbConnection)Activator.CreateInstance(ConnectionType); + ApplyDatabaseObjectProperties(dbConnection, ConnectionProperties, logEventInfo ?? LogEventInfo.CreateNullEvent()); } - if (connection is null) + dbConnection.Open(); + return dbConnection; + } + + private IDbConnection CreateDbConnectionFromType() + { +#if NETFRAMEWORK + if (ProviderFactory != null) { - throw new NLogRuntimeException("Creation of connection failed"); + return ProviderFactory.CreateConnection(); } - - connection.ConnectionString = connectionString; - if (ConnectionProperties?.Count > 0) +#endif + if (ConnectionType is null) { - ApplyDatabaseObjectProperties(connection, ConnectionProperties, logEventInfo ?? LogEventInfo.CreateNullEvent()); + throw new NLogRuntimeException($"Failed to resolve ConnectionType from DbProvider: {DBProvider}"); } - - connection.Open(); - return connection; + return (IDbConnection)Activator.CreateInstance(ConnectionType); } private void ApplyDatabaseObjectProperties(object databaseObject, IList objectProperties, LogEventInfo logEventInfo) @@ -416,7 +423,7 @@ protected override void InitializeTarget() } #endif - if (!foundProvider) + if (!foundProvider && ReferenceEquals(DbConnectionFactory, _createDbConnectionInstance)) { try { diff --git a/src/NLog.Database/NLog.Database.csproj b/src/NLog.Database/NLog.Database.csproj index c0c4b45948..baae61c20b 100644 --- a/src/NLog.Database/NLog.Database.csproj +++ b/src/NLog.Database/NLog.Database.csproj @@ -38,7 +38,6 @@ 9 true true - true diff --git a/tests/NLog.Database.Tests/DatabaseTargetTests.cs b/tests/NLog.Database.Tests/DatabaseTargetTests.cs index 4aa7d5a206..1bfd5e7c0c 100644 --- a/tests/NLog.Database.Tests/DatabaseTargetTests.cs +++ b/tests/NLog.Database.Tests/DatabaseTargetTests.cs @@ -541,7 +541,7 @@ public void LevelParameterTest(DbType? dbType, bool noRawValue, string expectedV DatabaseTarget dt = new DatabaseTarget() { CommandText = "INSERT INTO FooBar VALUES(@lvl, @msg)", - DBProvider = typeof(MockDbConnection).AssemblyQualifiedName, + DbConnectionFactory = () => new MockDbConnection(), KeepConnection = true, Parameters = { @@ -552,8 +552,6 @@ public void LevelParameterTest(DbType? dbType, bool noRawValue, string expectedV var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.Configuration.AddRuleForAllLevels(dt)).LogFactory; - Assert.Same(typeof(MockDbConnection), dt.ConnectionType); - List exceptions = new List(); var events = new[] { From 4f4892395501af4ff1433df3b5488b2834e3f848 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 9 Jun 2025 09:57:55 +0200 Subject: [PATCH 165/224] DatabaseTarget - Refactor and suppress AOT trimming warnings (#5874) --- .../DatabaseObjectPropertyInfo.cs | 1 + src/NLog.Database/DatabaseParameterInfo.cs | 46 ++-- src/NLog.Database/DatabaseTarget.cs | 5 +- .../DynamicallyAccessedMemberTypes.cs | 233 ++++++++++++++++++ 4 files changed, 260 insertions(+), 25 deletions(-) create mode 100644 src/NLog.Database/Internal/DynamicallyAccessedMemberTypes.cs diff --git a/src/NLog.Database/DatabaseObjectPropertyInfo.cs b/src/NLog.Database/DatabaseObjectPropertyInfo.cs index 197d12c563..77cf3e392c 100644 --- a/src/NLog.Database/DatabaseObjectPropertyInfo.cs +++ b/src/NLog.Database/DatabaseObjectPropertyInfo.cs @@ -91,6 +91,7 @@ public DatabaseObjectPropertyInfo() /// Result value when available, else fallback to defaultValue public object? RenderValue(LogEventInfo logEvent) => _layoutInfo.RenderValue(logEvent); + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Not supported", "IL2075")] internal bool SetPropertyValue(object dbObject, object? propertyValue) { var dbConnectionType = dbObject.GetType(); diff --git a/src/NLog.Database/DatabaseParameterInfo.cs b/src/NLog.Database/DatabaseParameterInfo.cs index a56653920f..4a0c93879f 100644 --- a/src/NLog.Database/DatabaseParameterInfo.cs +++ b/src/NLog.Database/DatabaseParameterInfo.cs @@ -250,27 +250,23 @@ internal bool SetDbType(IDbDataParameter dbParameter) private sealed class DbTypeSetter { - private readonly Type _dbPropertyInfoType; + private readonly Type _dbParameterType; private readonly string _dbTypeName; - private readonly PropertyInfo? _dbTypeSetter; private readonly Enum? _dbTypeValue; - private Action? _dbTypeSetterFast; + private readonly Action? _dbTypeSetter; public DbTypeSetter(Type dbParameterType, string dbTypeName) { - _dbPropertyInfoType = dbParameterType; + _dbParameterType = dbParameterType; _dbTypeName = dbTypeName is null ? string.Empty : dbTypeName.Trim(); if (!string.IsNullOrEmpty(_dbTypeName)) { string[] dbTypeNames = _dbTypeName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); if (dbTypeNames.Length > 1 && !string.Equals(dbTypeNames[0], nameof(System.Data.DbType), StringComparison.OrdinalIgnoreCase)) { - PropertyInfo propInfo = dbParameterType.GetProperty(dbTypeNames[0], BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); - if (propInfo != null && TryParseEnum(dbTypeNames[1], propInfo.PropertyType, out var enumType) && enumType != null) - { - _dbTypeSetter = propInfo; - _dbTypeValue = enumType; - } + var customDbSetter = BuildCustomDbSetter(dbParameterType, dbTypeNames[0], dbTypeNames[1]); + _dbTypeValue = customDbSetter.Key; + _dbTypeSetter = customDbSetter.Value; } else { @@ -278,21 +274,28 @@ public DbTypeSetter(Type dbParameterType, string dbTypeName) if (!string.IsNullOrEmpty(dbTypeName) && ConversionHelpers.TryParseEnum(dbTypeName, out DbType dbType)) { _dbTypeValue = dbType; - _dbTypeSetterFast = (p) => p.DbType = dbType; + _dbTypeSetter = (p) => p.DbType = dbType; } } } } + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Not supported", "IL2070")] + KeyValuePair?> BuildCustomDbSetter(Type dbParameterType, string dbTypePropertyName, string dbTypeEnumValue) + { + PropertyInfo propInfo = dbParameterType.GetProperty(dbTypePropertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); + if (propInfo != null && TryParseEnum(dbTypeEnumValue, propInfo.PropertyType, out var enumType) && enumType != null) + { + var propertySetter = propInfo.CreatePropertySetter(); + return new KeyValuePair?>(enumType, (p) => propertySetter.Invoke(p, _dbTypeValue)); + } + return default; + } + public bool IsValid(Type dbParameterType, string dbTypeName) { - if (ReferenceEquals(_dbPropertyInfoType, dbParameterType) && ReferenceEquals(_dbTypeName, dbTypeName)) + if (ReferenceEquals(_dbParameterType, dbParameterType) && ReferenceEquals(_dbTypeName, dbTypeName)) { - if (_dbTypeSetterFast == null && _dbTypeSetter != null && _dbTypeValue != null) - { - var propertySetter = _dbTypeSetter.CreatePropertySetter(); - _dbTypeSetterFast = (p) => propertySetter.Invoke(p, _dbTypeValue); - } return true; } return false; @@ -300,14 +303,9 @@ public bool IsValid(Type dbParameterType, string dbTypeName) public bool SetDbType(IDbDataParameter dbParameter) { - if (_dbTypeSetterFast != null) - { - _dbTypeSetterFast.Invoke(dbParameter); - return true; - } - else if (_dbTypeSetter != null && _dbTypeValue != null) + if (_dbTypeSetter != null) { - _dbTypeSetter.SetValue(dbParameter, _dbTypeValue, null); + _dbTypeSetter.Invoke(dbParameter); return true; } return false; diff --git a/src/NLog.Database/DatabaseTarget.cs b/src/NLog.Database/DatabaseTarget.cs index cfdf2da21d..51924ebd03 100644 --- a/src/NLog.Database/DatabaseTarget.cs +++ b/src/NLog.Database/DatabaseTarget.cs @@ -339,6 +339,7 @@ internal IDbConnection OpenConnection(string connectionString, LogEventInfo logE return dbConnection; } + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Not supported, instead assign " + nameof(DbConnectionFactory), "IL2072")] private IDbConnection CreateDbConnectionFromType() { #if NETFRAMEWORK @@ -544,6 +545,8 @@ private string GetProviderNameFromDbProviderFactories(string providerName) /// /// Set the to use it for opening connections to the database. /// + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Not supported, instead assign " + nameof(DbConnectionFactory), "IL2026")] + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Not supported, instead assign " + nameof(DbConnectionFactory), "IL2096")] private void SetConnectionType() { switch (DBProvider.ToUpperInvariant()) @@ -1031,7 +1034,7 @@ private void RunInstallCommands(InstallationContext installationContext, IEnumer var connectionString = GetConnectionStringFromCommand(commandInfo, logEvent); // Set ConnectionType if it has not been initialized already - if (ConnectionType is null) + if (ConnectionType is null && ReferenceEquals(DbConnectionFactory, _createDbConnectionInstance)) { SetConnectionType(); } diff --git a/src/NLog.Database/Internal/DynamicallyAccessedMemberTypes.cs b/src/NLog.Database/Internal/DynamicallyAccessedMemberTypes.cs new file mode 100644 index 0000000000..d9f6a1990e --- /dev/null +++ b/src/NLog.Database/Internal/DynamicallyAccessedMemberTypes.cs @@ -0,0 +1,233 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +#if !NET5_0_OR_GREATER + +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter | + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, + Inherited = false)] + internal sealed class DynamicallyAccessedMembersAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified member types. + /// + /// The types of members dynamically accessed. + public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes) + { + MemberTypes = memberTypes; + } + + /// + /// Gets the which specifies the type + /// of members dynamically accessed. + /// + public DynamicallyAccessedMemberTypes MemberTypes { get; } + } + + /// + /// Specifies the types of members that are dynamically accessed. + /// + /// This enumeration has a attribute that allows a + /// bitwise combination of its member values. + /// + [Flags] + internal enum DynamicallyAccessedMemberTypes + { + /// + /// Specifies no members. + /// + None = 0, + + /// + /// Specifies the default, parameterless public constructor. + /// + PublicParameterlessConstructor = 0x0001, + + /// + /// Specifies all public constructors. + /// + PublicConstructors = 0x0002 | PublicParameterlessConstructor, + + /// + /// Specifies all non-public constructors. + /// + NonPublicConstructors = 0x0004, + + /// + /// Specifies all public methods. + /// + PublicMethods = 0x0008, + + /// + /// Specifies all non-public methods. + /// + NonPublicMethods = 0x0010, + + /// + /// Specifies all public fields. + /// + PublicFields = 0x0020, + + /// + /// Specifies all non-public fields. + /// + NonPublicFields = 0x0040, + + /// + /// Specifies all public nested types. + /// + PublicNestedTypes = 0x0080, + + /// + /// Specifies all non-public nested types. + /// + NonPublicNestedTypes = 0x0100, + + /// + /// Specifies all public properties. + /// + PublicProperties = 0x0200, + + /// + /// Specifies all non-public properties. + /// + NonPublicProperties = 0x0400, + + /// + /// Specifies all public events. + /// + PublicEvents = 0x0800, + + /// + /// Specifies all non-public events. + /// + NonPublicEvents = 0x1000, + + /// + /// Specifies all interfaces implemented by the type. + /// + Interfaces = 0x2000, + + /// + /// Specifies all members. + /// + All = ~None + } + + /// + /// Suppresses reporting of a specific rule violation, allowing multiple suppressions on a + /// single code artifact. + /// + /// + /// is different than + /// in that it doesn't have a + /// . So it is always preserved in the compiled assembly. + /// + [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] + internal sealed class UnconditionalSuppressMessageAttribute : Attribute + { + /// + /// Initializes a new instance of the + /// class, specifying the category of the tool and the identifier for an analysis rule. + /// + /// The category for the attribute. + /// The identifier of the analysis rule the attribute applies to. + public UnconditionalSuppressMessageAttribute(string category, string checkId) + { + Category = category; + CheckId = checkId; + } + + /// + /// Gets the category identifying the classification of the attribute. + /// + /// + /// The property describes the tool or tool analysis category + /// for which a message suppression attribute applies. + /// + public string Category { get; } + + /// + /// Gets the identifier of the analysis tool rule to be suppressed. + /// + /// + /// Concatenated together, the and + /// properties form a unique check identifier. + /// + public string CheckId { get; } + + /// + /// Gets or sets the scope of the code that is relevant for the attribute. + /// + /// + /// The Scope property is an optional argument that specifies the metadata scope for which + /// the attribute is relevant. + /// + public string? Scope { get; set; } + + /// + /// Gets or sets a fully qualified path that represents the target of the attribute. + /// + /// + /// The property is an optional argument identifying the analysis target + /// of the attribute. An example value is "System.IO.Stream.ctor():System.Void". + /// Because it is fully qualified, it can be long, particularly for targets such as parameters. + /// The analysis tool user interface should be capable of automatically formatting the parameter. + /// + public string? Target { get; set; } + + /// + /// Gets or sets an optional argument expanding on exclusion criteria. + /// + /// + /// The property is an optional argument that specifies additional + /// exclusion where the literal metadata target is not sufficiently precise. For example, + /// the cannot be applied within a method, + /// and it may be desirable to suppress a violation against a statement in the method that will + /// give a rule violation, but not against all statements in the method. + /// + public string? MessageId { get; set; } + + /// + /// Gets or sets the justification for suppressing the code analysis message. + /// + public string? Justification { get; set; } + } +} + +#endif From 3a59a6b00d69aced4974a6eea7929d32e2b396cf Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 9 Jun 2025 21:11:39 +0200 Subject: [PATCH 166/224] DatabaseTarget - Improve InternalLogger output when failing to parse DbType (#5875) --- src/NLog.Database/DatabaseParameterInfo.cs | 28 ++++++++++++++-------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/NLog.Database/DatabaseParameterInfo.cs b/src/NLog.Database/DatabaseParameterInfo.cs index 4a0c93879f..a63e83e5cd 100644 --- a/src/NLog.Database/DatabaseParameterInfo.cs +++ b/src/NLog.Database/DatabaseParameterInfo.cs @@ -252,7 +252,6 @@ private sealed class DbTypeSetter { private readonly Type _dbParameterType; private readonly string _dbTypeName; - private readonly Enum? _dbTypeValue; private readonly Action? _dbTypeSetter; public DbTypeSetter(Type dbParameterType, string dbTypeName) @@ -264,32 +263,41 @@ public DbTypeSetter(Type dbParameterType, string dbTypeName) string[] dbTypeNames = _dbTypeName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); if (dbTypeNames.Length > 1 && !string.Equals(dbTypeNames[0], nameof(System.Data.DbType), StringComparison.OrdinalIgnoreCase)) { - var customDbSetter = BuildCustomDbSetter(dbParameterType, dbTypeNames[0], dbTypeNames[1]); - _dbTypeValue = customDbSetter.Key; - _dbTypeSetter = customDbSetter.Value; + _dbTypeSetter = BuildCustomDbSetter(dbParameterType, dbTypeNames[0], dbTypeNames[1]); } else { dbTypeName = dbTypeNames[dbTypeNames.Length - 1]; if (!string.IsNullOrEmpty(dbTypeName) && ConversionHelpers.TryParseEnum(dbTypeName, out DbType dbType)) { - _dbTypeValue = dbType; _dbTypeSetter = (p) => p.DbType = dbType; } + else + { + InternalLogger.Error("DatabaseTarget: Failed to resolve enum to assign DbType={0}", dbTypeName); + } } } } [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Not supported", "IL2070")] - KeyValuePair?> BuildCustomDbSetter(Type dbParameterType, string dbTypePropertyName, string dbTypeEnumValue) + Action? BuildCustomDbSetter(Type dbParameterType, string dbTypePropertyName, string dbTypeEnumValue) { PropertyInfo propInfo = dbParameterType.GetProperty(dbTypePropertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); - if (propInfo != null && TryParseEnum(dbTypeEnumValue, propInfo.PropertyType, out var enumType) && enumType != null) + if (propInfo is null) + { + InternalLogger.Error("DatabaseTarget: Failed to resolve type {0} property '{1}' to assign DbType={2}", dbParameterType, dbTypePropertyName, dbTypeEnumValue); + return default; + } + + if (!TryParseEnum(dbTypeEnumValue, propInfo.PropertyType, out var dbType) || dbType is null) { - var propertySetter = propInfo.CreatePropertySetter(); - return new KeyValuePair?>(enumType, (p) => propertySetter.Invoke(p, _dbTypeValue)); + InternalLogger.Error("DatabaseTarget: Failed to resolve enum {0} to assign DbType={1}", propInfo.PropertyType, dbTypeEnumValue); + return default; } - return default; + + var propertySetter = propInfo.CreatePropertySetter(); + return (p) => propertySetter.Invoke(p, dbType); } public bool IsValid(Type dbParameterType, string dbTypeName) From cbef4e574a5126e66e287a926e39312d652674ed Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 10 Jun 2025 19:11:38 +0200 Subject: [PATCH 167/224] Log4JXmlEventLayout - Enforce MaxRecursionLimit = 0 (#5876) --- .../Layouts/Log4JXmlEventLayout.cs | 3 +- src/NLog/Layouts/XML/XmlElementBase.cs | 7 ++++ .../Log4JXmlTests.cs | 40 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs index 31d21d7288..da38e7aba7 100644 --- a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs @@ -291,7 +291,8 @@ protected override void InitializeLayout() PropertiesElementKeyAttribute = "name", PropertiesElementValueAttribute = "value", IncludeEventProperties = IncludeEventProperties, - IncludeScopeProperties = IncludeScopeProperties + IncludeScopeProperties = IncludeScopeProperties, + MaxRecursionLimit = 0, // ToString for everything }; dataProperties.ContextProperties?.Clear(); diff --git a/src/NLog/Layouts/XML/XmlElementBase.cs b/src/NLog/Layouts/XML/XmlElementBase.cs index 5270eae7ce..2d30be008f 100644 --- a/src/NLog/Layouts/XML/XmlElementBase.cs +++ b/src/NLog/Layouts/XML/XmlElementBase.cs @@ -503,6 +503,13 @@ private bool AppendXmlPropertyObjectValue(string propName, object? propertyValue return false; } + if (MaxRecursionLimit == 0 || (nextDepth == MaxRecursionLimit && !(propertyValue is System.Collections.IEnumerable))) + { + string xmlValueString = XmlHelper.XmlConvertToStringSafe(propertyValue); + AppendXmlPropertyStringValue(propName, xmlValueString, sb, orgLength, false, ignorePropertiesElementName); + return true; + } + if (propertyValue is System.Collections.IDictionary dict) { using (StartCollectionScope(ref objectsInPath, dict)) diff --git a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs index d493e977c5..bcd62756a2 100644 --- a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs +++ b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs @@ -286,6 +286,46 @@ public void Log4JXmlEventLayout_ThrowableWrappedInCData_Test() Assert.Equal($"Error occurred&]]>", result); } + + public readonly struct PathString + { + public PathString(string value) + { + Value = value; + } + + public string Value { get; } + public bool HasValue + { + get { return !string.IsNullOrEmpty(Value); } + } + + public override string ToString() + { + return Value ?? string.Empty; + } + } + + [Fact] + public void Log4JXmlEventLayout_IncludeEventProperties_Test() + { + var log4jLayout = new Log4JXmlEventLayout + { + IncludeEventProperties = true, + AppInfo = "MyApp", + }; + + var pathString = new PathString("/test"); + var logEvent = LogEventInfo.Create(LogLevel.Info, "TestLogger", null, "Test Message {Path}", new object[] { pathString }); + logEvent.TimeStamp = new DateTime(2010, 01, 01, 12, 34, 56, DateTimeKind.Utc); + var result = log4jLayout.Render(logEvent); + + var threadid = Environment.CurrentManagedThreadId; + var machinename = Environment.MachineName; + + Assert.Equal($"Test Message /test", result); + } + [Fact] public void Log4JXmlEventLayout_IncludeScopeNested_Test() { From 91e0bbca86602e69394752dac2f9650569ab12e0 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 11 Jun 2025 20:42:33 +0200 Subject: [PATCH 168/224] LogEventBuilder - Optimize Properties to apply initial capacity (#5877) --- src/NLog/Internal/PropertiesDictionary.cs | 15 +++------------ src/NLog/LogEventBuilder.cs | 5 +++-- src/NLog/LogEventInfo.cs | 17 +++++++++++++---- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/NLog/Internal/PropertiesDictionary.cs b/src/NLog/Internal/PropertiesDictionary.cs index a5c64193ad..77eccff135 100644 --- a/src/NLog/Internal/PropertiesDictionary.cs +++ b/src/NLog/Internal/PropertiesDictionary.cs @@ -93,25 +93,16 @@ public PropertiesDictionary(IList? messageParameters = } } -#if !NET35 /// /// Transforms the list of event-properties into IDictionary-interface /// - /// Message-template-parameters - public PropertiesDictionary(IReadOnlyList> eventProperties) + public PropertiesDictionary(int initialCapacity) { - var propertyCount = eventProperties.Count; - if (propertyCount > 0) + if (initialCapacity > 3) { - _eventProperties = new Dictionary(propertyCount, PropertyKeyComparer.Default); - for (int i = 0; i < propertyCount; ++i) - { - var property = eventProperties[i]; - _eventProperties[property.Key] = new PropertyValue(property.Value, false); - } + _eventProperties = new Dictionary(initialCapacity, PropertyKeyComparer.Default); } } -#endif private bool IsEmpty => (_eventProperties is null || _eventProperties.Count == 0) && (_messageProperties is null || _messageProperties.Count == 0); diff --git a/src/NLog/LogEventBuilder.cs b/src/NLog/LogEventBuilder.cs index 81b041d599..09fafaf502 100644 --- a/src/NLog/LogEventBuilder.cs +++ b/src/NLog/LogEventBuilder.cs @@ -125,12 +125,13 @@ public LogEventBuilder Properties(IEnumerable> pro /// Sets multiple per-event context properties on the logging event. /// /// The properties to set. - public LogEventBuilder Properties(params ReadOnlySpan<(string, object?)> properties) + public LogEventBuilder Properties(ReadOnlySpan<(string, object?)> properties) { if (_logEvent != null) { + var dictionary = _logEvent.CreatePropertiesInternal(initialCapacity: properties.Length); foreach (var property in properties) - _logEvent.Properties[property.Item1] = property.Item2; + dictionary[property.Item1] = property.Item2; } return this; } diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index 473d8fcfb1..2e5ac0ef75 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -169,9 +169,18 @@ public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] str public LogEventInfo(LogLevel level, string? loggerName, [Localizable(false)] string message, IReadOnlyList>? eventProperties) : this(level, loggerName, null, message, null, null) { - if (eventProperties?.Count > 0) + if (eventProperties != null) { - _properties = new PropertiesDictionary(eventProperties); + var propertyCount = eventProperties.Count; + if (propertyCount > 0) + { + _properties = new PropertiesDictionary(propertyCount); + for (int i = 0; i < propertyCount; ++i) + { + var property = eventProperties[i]; + _properties[property.Key] = property.Value; + } + } } } #endif @@ -417,11 +426,11 @@ public bool HasProperties return properties; } - internal PropertiesDictionary CreatePropertiesInternal(IList? templateParameters = null) + internal PropertiesDictionary CreatePropertiesInternal(IList? templateParameters = null, int initialCapacity = 0) { if (_properties is null) { - PropertiesDictionary properties = new PropertiesDictionary(templateParameters); + PropertiesDictionary properties = templateParameters is null ? new PropertiesDictionary(initialCapacity) : new PropertiesDictionary(templateParameters); Interlocked.CompareExchange(ref _properties, properties, null); if (templateParameters is null && HasMessageTemplateParameters) { From 295ee76baf83b06dac9b94c54694928bbd651ef3 Mon Sep 17 00:00:00 2001 From: JohnVerheij Date: Sat, 14 Jun 2025 00:05:37 +0200 Subject: [PATCH 169/224] DatabaseTarget with support for AOT without type-reflection (#5878) --------- Co-authored-by: Rolf Kristensen --- run-tests.ps1 | 2 +- .../DatabaseObjectPropertyInfo.cs | 2 +- src/NLog.Database/DatabaseParameterInfo.cs | 110 +++++++++++++----- src/NLog.Database/DatabaseTarget.cs | 77 ++++++------ src/NLog.Database/NLog.Database.csproj | 1 + .../DatabaseTargetTests.cs | 86 +++++++++++++- .../NLog.Database.Tests.csproj | 4 +- 7 files changed, 212 insertions(+), 70 deletions(-) diff --git a/run-tests.ps1 b/run-tests.ps1 index 5424209f61..1ef00f18b9 100644 --- a/run-tests.ps1 +++ b/run-tests.ps1 @@ -79,7 +79,7 @@ if ($isWindows -or $Env:WinDir) } else { - dotnet test ./tests/NLog.Database.Tests/ --framework net6.0 --configuration release + dotnet test ./tests/NLog.Database.Tests/ --framework net8.0 --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } diff --git a/src/NLog.Database/DatabaseObjectPropertyInfo.cs b/src/NLog.Database/DatabaseObjectPropertyInfo.cs index 77cf3e392c..21cf832e0f 100644 --- a/src/NLog.Database/DatabaseObjectPropertyInfo.cs +++ b/src/NLog.Database/DatabaseObjectPropertyInfo.cs @@ -132,6 +132,6 @@ public bool Equals(string propertyName, Type objectType) } } - PropertySetterCacheItem _propertySetter; + private PropertySetterCacheItem _propertySetter; } } diff --git a/src/NLog.Database/DatabaseParameterInfo.cs b/src/NLog.Database/DatabaseParameterInfo.cs index a63e83e5cd..97c8d230cc 100644 --- a/src/NLog.Database/DatabaseParameterInfo.cs +++ b/src/NLog.Database/DatabaseParameterInfo.cs @@ -81,6 +81,8 @@ public class DatabaseParameterInfo { nameof(System.Data.DbType.DateTimeOffset), typeof(DateTimeOffset) } }; + private Func? _dbTypeSetter; + private readonly ValueTypeLayoutInfo _layoutInfo = new ValueTypeLayoutInfo(); /// @@ -102,6 +104,24 @@ public DatabaseParameterInfo(string parameterName, Layout parameterLayout) Layout = parameterLayout; } + /// + /// Initializes a new instance of the class. + /// + /// Name of the parameter. + /// The parameter layout. + /// Method-delegate to perform custom initialization database-parameter. Ex. for AOT to assign custom DbType. + public DatabaseParameterInfo(string parameterName, Layout parameterLayout, Action dbTypeSetter) + : this(parameterName, parameterLayout) + { + if (dbTypeSetter is null) throw new ArgumentNullException(nameof(dbTypeSetter)); + + _dbTypeSetter = (dbParameter) => + { + dbTypeSetter.Invoke(dbParameter); + return true; + }; + } + /// /// Gets or sets the database parameter name. /// @@ -117,13 +137,18 @@ public DatabaseParameterInfo(string parameterName, Layout parameterLayout) /// /// Gets or sets the database parameter DbType. /// + /// + /// Not compatible with AOT since using type-reflection to convert into Enum and assigning value. + /// /// public string DbType { - get => _dbType; + get => _dbTypeEnum.HasValue ? _dbTypeEnum.ToString() : _dbType; set { _dbType = value; + _dbTypeEnum = null; + if (!string.IsNullOrEmpty(_dbType)) { if (ParameterType is null || ParameterType == _dbParameterType) @@ -140,11 +165,43 @@ public string DbType { ParameterType = null; } + + _dbTypeSetter = AssignParameterDbType; } } private string _dbType = string.Empty; private Type? _dbParameterType; + /// + /// Gets or sets the database parameter DbType (without reflection logic) + /// + public DbType DbTypeEnum + { + get => _dbTypeEnum ?? default; + set + { + _dbTypeEnum = value; + _dbType = string.Empty; + + if (ParameterType is null || ParameterType == _dbParameterType) + { + var dbParameterType = TryParseDbType(value.ToString()); + if (dbParameterType != null) + { + ParameterType = dbParameterType; + } + _dbParameterType = dbParameterType; + } + + _dbTypeSetter = (dbParam) => + { + dbParam.DbType = value; + return true; + }; + } + } + private DbType? _dbTypeEnum; + /// /// Gets or sets the database parameter size. /// @@ -222,17 +279,7 @@ public bool AllowDbNull internal bool SetDbType(IDbDataParameter dbParameter) { - if (!string.IsNullOrEmpty(DbType)) - { - if (_cachedDbTypeSetter is null || !_cachedDbTypeSetter.IsValid(dbParameter.GetType(), DbType)) - { - _cachedDbTypeSetter = new DbTypeSetter(dbParameter.GetType(), DbType); - } - - return _cachedDbTypeSetter.SetDbType(dbParameter); - } - - return true; // DbType not in use + return _dbTypeSetter?.Invoke(dbParameter) ?? true; } private static Type? TryParseDbType(string dbTypeName) @@ -246,7 +293,17 @@ internal bool SetDbType(IDbDataParameter dbParameter) return _typesByDbTypeName.TryGetValue(dbTypeName, out var type) ? type : null; } - DbTypeSetter? _cachedDbTypeSetter; + private bool AssignParameterDbType(IDbDataParameter dbParameter) + { + if (_cachedDbTypeSetter is null || !_cachedDbTypeSetter.IsValid(dbParameter.GetType(), DbType)) + { + _cachedDbTypeSetter = new DbTypeSetter(dbParameter.GetType(), _dbType); + } + + return _cachedDbTypeSetter.SetDbType(dbParameter); + } + + private DbTypeSetter? _cachedDbTypeSetter; private sealed class DbTypeSetter { @@ -257,7 +314,7 @@ private sealed class DbTypeSetter public DbTypeSetter(Type dbParameterType, string dbTypeName) { _dbParameterType = dbParameterType; - _dbTypeName = dbTypeName is null ? string.Empty : dbTypeName.Trim(); + _dbTypeName = dbTypeName?.Trim() ?? string.Empty; if (!string.IsNullOrEmpty(_dbTypeName)) { string[] dbTypeNames = _dbTypeName.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); @@ -278,22 +335,25 @@ public DbTypeSetter(Type dbParameterType, string dbTypeName) } } } + else + { + _dbTypeSetter = (p) => { }; + } } - [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Not supported", "IL2070")] - Action? BuildCustomDbSetter(Type dbParameterType, string dbTypePropertyName, string dbTypeEnumValue) + private static Action? BuildCustomDbSetter(Type dbParameterType, string dbTypePropertyName, string dbTypeEnumValue) { PropertyInfo propInfo = dbParameterType.GetProperty(dbTypePropertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); if (propInfo is null) { InternalLogger.Error("DatabaseTarget: Failed to resolve type {0} property '{1}' to assign DbType={2}", dbParameterType, dbTypePropertyName, dbTypeEnumValue); - return default; + return null; } if (!TryParseEnum(dbTypeEnumValue, propInfo.PropertyType, out var dbType) || dbType is null) { InternalLogger.Error("DatabaseTarget: Failed to resolve enum {0} to assign DbType={1}", propInfo.PropertyType, dbTypeEnumValue); - return default; + return null; } var propertySetter = propInfo.CreatePropertySetter(); @@ -302,11 +362,7 @@ public DbTypeSetter(Type dbParameterType, string dbTypeName) public bool IsValid(Type dbParameterType, string dbTypeName) { - if (ReferenceEquals(_dbParameterType, dbParameterType) && ReferenceEquals(_dbTypeName, dbTypeName)) - { - return true; - } - return false; + return ReferenceEquals(_dbParameterType, dbParameterType) && string.Equals(_dbTypeName, dbTypeName, StringComparison.OrdinalIgnoreCase); } public bool SetDbType(IDbDataParameter dbParameter) @@ -335,11 +391,9 @@ private static bool TryParseEnum(string value, Type enumType, out Enum? enumValu return false; } } - else - { - enumValue = null; - return false; - } + + enumValue = null; + return false; } } } diff --git a/src/NLog.Database/DatabaseTarget.cs b/src/NLog.Database/DatabaseTarget.cs index 51924ebd03..5e1ccbcf9b 100644 --- a/src/NLog.Database/DatabaseTarget.cs +++ b/src/NLog.Database/DatabaseTarget.cs @@ -79,32 +79,51 @@ namespace NLog.Targets [Target("Database")] public class DatabaseTarget : Target, IInstallable { - private readonly Func _createDbConnectionInstance; + private readonly Func _dbConnectionFactory; + private readonly Func? _resolveConnectionType; private IDbConnection? _activeConnection; private string? _activeConnectionString; /// /// Initializes a new instance of the class. /// + /// + /// Not compatible with AOT since using type-reflection with to resolve DbConnection-factory. + /// public DatabaseTarget() { #if NETFRAMEWORK ConnectionStringsSettings = ConfigurationManager.ConnectionStrings; #endif CommandType = CommandType.Text; - _createDbConnectionInstance = CreateDbConnectionFromType; - DbConnectionFactory = _createDbConnectionInstance; + _resolveConnectionType = ResolveConnectionType; + _dbConnectionFactory = CreateDbConnectionFromType; } /// /// Initializes a new instance of the class. /// /// Name of the target. + /// + /// Not compatible with AOT since using type-reflection with to resolve DbConnection-factory. + /// public DatabaseTarget(string name) : this() { Name = name; } + /// + /// Initializes a new instance of the class. + /// + /// + /// A factory function that creates instances of for connecting to the database (AOT compatible) + /// + public DatabaseTarget(Func dbConnectionFactory) + { + CommandType = CommandType.Text; + _dbConnectionFactory = dbConnectionFactory ?? throw new ArgumentNullException(nameof(dbConnectionFactory)); + } + /// /// Gets or sets the name of the database provider. /// @@ -277,16 +296,11 @@ public Layout? DBPassword /// public System.Data.IsolationLevel? IsolationLevel { get; set; } - /// - /// Gets or sets the factory. By default resolved from type-name - /// - public Func DbConnectionFactory { get; set; } - #if NETFRAMEWORK internal DbProviderFactory? ProviderFactory { get; set; } // this is so we can mock the connection string without creating sub-processes - internal ConnectionStringSettingsCollection ConnectionStringsSettings { get; set; } + internal ConnectionStringSettingsCollection? ConnectionStringsSettings { get; set; } #endif internal Type? ConnectionType { get; private set; } @@ -323,7 +337,7 @@ public void Uninstall(InstallationContext installationContext) internal IDbConnection OpenConnection(string connectionString, LogEventInfo logEventInfo) { - var dbConnection = DbConnectionFactory?.Invoke(); + var dbConnection = _dbConnectionFactory.Invoke(); if (dbConnection is null) { throw new NLogRuntimeException("Creation of DbConnection failed"); @@ -339,7 +353,6 @@ internal IDbConnection OpenConnection(string connectionString, LogEventInfo logE return dbConnection; } - [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Not supported, instead assign " + nameof(DbConnectionFactory), "IL2072")] private IDbConnection CreateDbConnectionFromType() { #if NETFRAMEWORK @@ -352,6 +365,7 @@ private IDbConnection CreateDbConnectionFromType() { throw new NLogRuntimeException($"Failed to resolve ConnectionType from DbProvider: {DBProvider}"); } + return (IDbConnection)Activator.CreateInstance(ConnectionType); } @@ -392,7 +406,7 @@ protected override void InitializeTarget() if (!string.IsNullOrEmpty(ConnectionStringName)) { // read connection string and provider factory from the configuration file - var cs = ConnectionStringsSettings[ConnectionStringName]; + var cs = ConnectionStringsSettings?[ConnectionStringName]; if (cs is null) { throw new NLogConfigurationException($"Connection string '{ConnectionStringName}' is not declared in section."); @@ -424,11 +438,11 @@ protected override void InitializeTarget() } #endif - if (!foundProvider && ReferenceEquals(DbConnectionFactory, _createDbConnectionInstance)) + if (!foundProvider && _resolveConnectionType != null) { try { - SetConnectionType(); + ConnectionType = _resolveConnectionType.Invoke(); if (ConnectionType is null) { InternalLogger.Warn("{0}: No ConnectionType created from DBProvider={1}", this, DBProvider); @@ -488,7 +502,7 @@ private string InitConnectionString(string providerName) } // ConnectionString was overriden by ConnectionString :) - ConnectionString = Layout.FromLiteral(connectionStringValue.ToString()); + ConnectionString = Layout.FromLiteral(connectionStringValue?.ToString() ?? string.Empty); } } catch (Exception ex) @@ -545,9 +559,7 @@ private string GetProviderNameFromDbProviderFactories(string providerName) /// /// Set the to use it for opening connections to the database. /// - [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Not supported, instead assign " + nameof(DbConnectionFactory), "IL2026")] - [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Not supported, instead assign " + nameof(DbConnectionFactory), "IL2096")] - private void SetConnectionType() + private Type ResolveConnectionType() { switch (DBProvider.ToUpperInvariant()) { @@ -560,42 +572,37 @@ private void SetConnectionType() try { var assembly = Assembly.Load(new AssemblyName("Microsoft.Data.SqlClient")); - ConnectionType = assembly.GetType("Microsoft.Data.SqlClient.SqlConnection", true, true); + return assembly.GetType("Microsoft.Data.SqlClient.SqlConnection", true, true); } catch (Exception ex) { InternalLogger.Warn(ex, "{0}: Failed to load assembly 'Microsoft.Data.SqlClient'. Falling back to 'System.Data.SqlClient'.", this); var assembly = Assembly.Load(new AssemblyName("System.Data.SqlClient")); - ConnectionType = assembly.GetType("System.Data.SqlClient.SqlConnection", true, true); + return assembly.GetType("System.Data.SqlClient.SqlConnection", true, true); } - break; } case "SYSTEM.DATA.SQLCLIENT": { var assembly = Assembly.Load(new AssemblyName("System.Data.SqlClient")); - ConnectionType = assembly.GetType("System.Data.SqlClient.SqlConnection", true, true); - break; + return assembly.GetType("System.Data.SqlClient.SqlConnection", true, true); } #else case "SYSTEM.DATA.SQLCLIENT": { var assembly = typeof(IDbConnection).Assembly; - ConnectionType = assembly.GetType("System.Data.SqlClient.SqlConnection", true, true); - break; + return assembly.GetType("System.Data.SqlClient.SqlConnection", true, true); } #endif case "MICROSOFT.DATA.SQLCLIENT": { var assembly = Assembly.Load(new AssemblyName("Microsoft.Data.SqlClient")); - ConnectionType = assembly.GetType("Microsoft.Data.SqlClient.SqlConnection", true, true); - break; + return assembly.GetType("Microsoft.Data.SqlClient.SqlConnection", true, true); } #if NETFRAMEWORK case "OLEDB": { var assembly = typeof(IDbConnection).Assembly; - ConnectionType = assembly.GetType("System.Data.OleDb.OleDbConnection", true, true); - break; + return assembly.GetType("System.Data.OleDb.OleDbConnection", true, true); } #endif case "ODBC": @@ -606,12 +613,10 @@ private void SetConnectionType() #else var assembly = Assembly.Load(new AssemblyName("System.Data.Odbc")); #endif - ConnectionType = assembly.GetType("System.Data.Odbc.OdbcConnection", true, true); - break; + return assembly.GetType("System.Data.Odbc.OdbcConnection", true, true); } default: - ConnectionType = Type.GetType(DBProvider, true, true); - break; + return Type.GetType(DBProvider, true, true); } } @@ -701,7 +706,7 @@ private ICollection>> GroupInBucke } if (dictionary is null) - return new KeyValuePair>[] { new KeyValuePair>(firstConnectionString, logEvents) }; + return new[] { new KeyValuePair>(firstConnectionString, logEvents) }; else return dictionary; } @@ -1034,9 +1039,9 @@ private void RunInstallCommands(InstallationContext installationContext, IEnumer var connectionString = GetConnectionStringFromCommand(commandInfo, logEvent); // Set ConnectionType if it has not been initialized already - if (ConnectionType is null && ReferenceEquals(DbConnectionFactory, _createDbConnectionInstance)) + if (ConnectionType is null && _resolveConnectionType != null) { - SetConnectionType(); + ConnectionType = _resolveConnectionType.Invoke(); } var activeConnection = EnsureConnectionOpen(connectionString, logEvent); diff --git a/src/NLog.Database/NLog.Database.csproj b/src/NLog.Database/NLog.Database.csproj index baae61c20b..c0c4b45948 100644 --- a/src/NLog.Database/NLog.Database.csproj +++ b/src/NLog.Database/NLog.Database.csproj @@ -38,6 +38,7 @@ 9 true true + true diff --git a/tests/NLog.Database.Tests/DatabaseTargetTests.cs b/tests/NLog.Database.Tests/DatabaseTargetTests.cs index 1bfd5e7c0c..953c695511 100644 --- a/tests/NLog.Database.Tests/DatabaseTargetTests.cs +++ b/tests/NLog.Database.Tests/DatabaseTargetTests.cs @@ -43,6 +43,7 @@ namespace NLog.Database.Tests using System.Data.Common; using System.Globalization; using System.IO; + using System.Linq; using NLog.Config; using NLog.Targets; using Xunit; @@ -538,10 +539,9 @@ public void LevelParameterTest(DbType? dbType, bool noRawValue, string expectedV string lvlLayout = noRawValue ? "${level:format=Ordinal:norawvalue=true}" : "${level:format=Ordinal}"; MockDbConnection.ClearLog(); - DatabaseTarget dt = new DatabaseTarget() + DatabaseTarget dt = new DatabaseTarget(() => new MockDbConnection()) { CommandText = "INSERT INTO FooBar VALUES(@lvl, @msg)", - DbConnectionFactory = () => new MockDbConnection(), KeepConnection = true, Parameters = { @@ -1981,6 +1981,88 @@ public void DatabaseConnectionStringViaVariableTest(string password, string expe Assert.Equal(expected, result); } +#if NET8_0_OR_GREATER + public sealed class DbTypeTestData() : TheoryData(Enum.GetValues()); +#else + public sealed class DbTypeTestData : TheoryData + { + public DbTypeTestData() => AddRange(Enum.GetValues(typeof(DbType)).Cast().ToArray()); + } +#endif + [ClassData(typeof(DbTypeTestData))] + [Theory] + public void DbTypeEnumSetterTest(DbType type) + { + // Clear log + MockDbConnection.ClearLog(); + + // Arrange + var con = new MockDbConnection(); + var dt = new DatabaseTarget(() => con) + { + Parameters = + { + new DatabaseParameterInfo("Test", "AOT") { DbTypeEnum = type }, + } + }; + dt.CreateDbCommand(new LogEventInfo(), con); + + // Assert + var log = MockDbConnection.Log; + Assert.Contains($"DbType={type}", log); + } + + [ClassData(typeof(DbTypeTestData))] + [Theory] + public void DbTypeSetterTest(DbType type) + { + // Clear log + MockDbConnection.ClearLog(); + + // Arrange + var con = new MockDbConnection(); + var dt = new DatabaseTarget(() => con) + { + Parameters = + { + new DatabaseParameterInfo("Test", "AOT", p => p.DbType = type) + } + }; + dt.CreateDbCommand(new LogEventInfo(), con); + + // Assert + var log = MockDbConnection.Log; + Assert.Contains($"DbType={type}", log); + } + + + [Fact] + public void DbTypeSetterExceptionTest() + { + // Arrange + var expectedException = new Exception("Test exception"); + var con = new MockDbConnection(); + var dt = new DatabaseTarget(() => con) + { + Name = "db", + Parameters = + { + new DatabaseParameterInfo("Test", "AOT", (p) => throw expectedException) + } + }; + var actualException = Assert.Throws(() => dt.CreateDbCommand(new LogEventInfo(), con)); + + // Assert + Assert.Equal(expectedException, actualException); + } + + + [Fact] + public void DbTypeSetterNullTest() + { + Assert.Throws(() => new DatabaseParameterInfo("Test", "AOT", null)); + } + private static void AssertLog(string expectedLog) { Assert.Equal(expectedLog.Replace("\r", ""), MockDbConnection.Log.Replace("\r", "")); diff --git a/tests/NLog.Database.Tests/NLog.Database.Tests.csproj b/tests/NLog.Database.Tests/NLog.Database.Tests.csproj index d9a4723461..973edce5b2 100644 --- a/tests/NLog.Database.Tests/NLog.Database.Tests.csproj +++ b/tests/NLog.Database.Tests/NLog.Database.Tests.csproj @@ -3,7 +3,7 @@ 17.0 net462 - net462;net6.0 + net462;net8.0 false @@ -40,7 +40,7 @@ - + From 398a4b02fa282f9a475470a36ec187a1c91c626f Mon Sep 17 00:00:00 2001 From: JohnVerheij Date: Sun, 15 Jun 2025 12:52:36 +0200 Subject: [PATCH 170/224] DatabaseTarget - Only assign ConnectionString when specified (#5880) --- src/NLog.Database/DatabaseTarget.cs | 6 ++++- .../DatabaseTargetTests.cs | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/NLog.Database/DatabaseTarget.cs b/src/NLog.Database/DatabaseTarget.cs index 5e1ccbcf9b..2134a05c65 100644 --- a/src/NLog.Database/DatabaseTarget.cs +++ b/src/NLog.Database/DatabaseTarget.cs @@ -343,7 +343,11 @@ internal IDbConnection OpenConnection(string connectionString, LogEventInfo logE throw new NLogRuntimeException("Creation of DbConnection failed"); } - dbConnection.ConnectionString = connectionString; + if (!string.IsNullOrEmpty(connectionString)) + { + dbConnection.ConnectionString = connectionString; + } + if (ConnectionProperties?.Count > 0) { ApplyDatabaseObjectProperties(dbConnection, ConnectionProperties, logEventInfo ?? LogEventInfo.CreateNullEvent()); diff --git a/tests/NLog.Database.Tests/DatabaseTargetTests.cs b/tests/NLog.Database.Tests/DatabaseTargetTests.cs index 953c695511..c88ed3610d 100644 --- a/tests/NLog.Database.Tests/DatabaseTargetTests.cs +++ b/tests/NLog.Database.Tests/DatabaseTargetTests.cs @@ -2063,6 +2063,29 @@ public void DbTypeSetterNullTest() Assert.Throws(() => new DatabaseParameterInfo("Test", "AOT", null)); } + [Theory] + [InlineData("A", "B", "B")] + [InlineData("A", "", "A")] + [InlineData("A", null, "A")] + public void DbFactoryConnectionStringTest(string csInConn, string csInDbTarget, string csExpected) + { + // Clear log + MockDbConnection.ClearLog(); + + // Arrange + var con = new MockDbConnection(csInConn); + var dt = new DatabaseTarget(() => con) + { + ConnectionString = csInDbTarget + }; + IDbConnection openedConnection = dt.OpenConnection(csInDbTarget, new LogEventInfo()); + + // Assert + var log = MockDbConnection.Log; + Assert.Contains($"Open('{csExpected}')", log); + Assert.Same(con, openedConnection); // Ensure factory-provided connection is used + } + private static void AssertLog(string expectedLog) { Assert.Equal(expectedLog.Replace("\r", ""), MockDbConnection.Log.Replace("\r", "")); From 33b954150f5bd5b62d4607554c286a935cb8e3e2 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 15 Jun 2025 14:00:57 +0200 Subject: [PATCH 171/224] Version 6.0 RC4 (#5879) --- CHANGELOG.md | 11 +++- README.md | 2 +- build.ps1 | 2 +- src/NLog/NLog.csproj | 4 ++ .../Log4JXmlTests.cs | 24 +-------- .../NLog.UnitTests/Layouts/XmlLayoutTests.cs | 26 ++++++++++ .../Targets/TargetWithContextTest.cs | 51 +++++++++++++++++++ 7 files changed, 95 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a389e83bb7..b1a41bce9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,16 @@ Date format: (year/month/day) ## Change Log -### Version 6.0 RC2 (2025/06/08) +### Version 6.0 RC4 (2025/06/15) + +**Improvements** +- Mark struct as readonly to allow compiler optimization +- RegisterObjectTransformation to preserve public properties +- Log4JXmlEventLayout - Enforce MaxRecursionLimit = 0 +- DatabaseTarget with support for AOT (@JohnVerheij) +- DatabaseTarget only assign ConnectionString when specified (@JohnVerheij) + +### Version 6.0 RC3 (2025/06/08) **Improvements** - Log4JXmlEventLayout - Fixed IncludeEmptyValue for Parameters diff --git a/README.md b/README.md index 66ca788c99..f58f5613c2 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Having troubles? Check the [troubleshooting guide](https://github.com/NLog/NLog/ ℹ️ NLog 6.0 will support AOT -[NLog 6.0 RC3](https://www.nuget.org/packages/NLog/6.0.0-rc3#releasenotes-body-tab) now available. See also [List of major changes in NLog 6.0](https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html) +[NLog 6.0 RC4](https://www.nuget.org/packages/NLog/6.0.0-rc4#releasenotes-body-tab) now available. See also [List of major changes in NLog 6.0](https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html) NLog Extensions diff --git a/build.ps1 b/build.ps1 index 8bb1e2cbed..6d0faef62a 100644 --- a/build.ps1 +++ b/build.ps1 @@ -3,7 +3,7 @@ dotnet --version $versionPrefix = "6.0.0" -$versionSuffix = "rc3" +$versionSuffix = "rc4" $versionFile = $versionPrefix + "." + ${env:APPVEYOR_BUILD_NUMBER} $versionProduct = $versionPrefix; if (-Not $versionSuffix.Equals("")) diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index 81401c8beb..ab51d4f60d 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -61,6 +61,10 @@ NLog v6.0 RC3 with the following major changes: - Updated NLog.Schema nuget-package to include targets-file to copy NLog.xsd to project-folder. - Improved configuration-file loading to advise about NLog nuget-packages for missing types. - Log4JXmlEventLayout - Fixed IncludeEmptyValue for Parameters. +- Log4JXmlEventLayout - Enforce MaxRecursionLimit = 0 +- RegisterObjectTransformation to preserve public properties +- DatabaseTarget with support for AOT (@JohnVerheij) +- DatabaseTarget only assign ConnectionString when specified (@JohnVerheij) NLog v6.0 release notes: https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html diff --git a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs index bcd62756a2..ddc0de68f3 100644 --- a/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs +++ b/tests/NLog.Targets.Network.Tests/Log4JXmlTests.cs @@ -286,26 +286,6 @@ public void Log4JXmlEventLayout_ThrowableWrappedInCData_Test() Assert.Equal($"Error occurred&]]>", result); } - - public readonly struct PathString - { - public PathString(string value) - { - Value = value; - } - - public string Value { get; } - public bool HasValue - { - get { return !string.IsNullOrEmpty(Value); } - } - - public override string ToString() - { - return Value ?? string.Empty; - } - } - [Fact] public void Log4JXmlEventLayout_IncludeEventProperties_Test() { @@ -315,7 +295,7 @@ public void Log4JXmlEventLayout_IncludeEventProperties_Test() AppInfo = "MyApp", }; - var pathString = new PathString("/test"); + var pathString = new KeyValuePair("path", "/test"); var logEvent = LogEventInfo.Create(LogLevel.Info, "TestLogger", null, "Test Message {Path}", new object[] { pathString }); logEvent.TimeStamp = new DateTime(2010, 01, 01, 12, 34, 56, DateTimeKind.Utc); var result = log4jLayout.Render(logEvent); @@ -323,7 +303,7 @@ public void Log4JXmlEventLayout_IncludeEventProperties_Test() var threadid = Environment.CurrentManagedThreadId; var machinename = Environment.MachineName; - Assert.Equal($"Test Message /test", result); + Assert.Equal($"Test Message [path, /test]", result); } [Fact] diff --git a/tests/NLog.UnitTests/Layouts/XmlLayoutTests.cs b/tests/NLog.UnitTests/Layouts/XmlLayoutTests.cs index 1ba521f38d..53cff40436 100644 --- a/tests/NLog.UnitTests/Layouts/XmlLayoutTests.cs +++ b/tests/NLog.UnitTests/Layouts/XmlLayoutTests.cs @@ -269,6 +269,32 @@ public void XmlLayout_OnlyLogEventProperties_RenderRootCorrect() Assert.Equal(expected, result); } + [Fact] + public void XmlLayout_LogEventProperties_NoRecursion() + { + // Arrange + var xmlLayout = new XmlLayout() + { + IncludeEventProperties = true, + MaxRecursionLimit = 0, + }; + + var logEventInfo = new LogEventInfo + { + Message = "message 1" + }; + logEventInfo.Properties["prop1"] = "a"; + logEventInfo.Properties["prop2"] = new KeyValuePair("path", "test"); + logEventInfo.Properties["prop3"] = "c"; + + // Act + var result = xmlLayout.Render(logEventInfo); + + // Assert + const string expected = @"a[path, test]c"; + Assert.Equal(expected, result); + } + [Fact] public void XmlLayout_InvalidXmlPropertyName_RenderNameCorrect() { diff --git a/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs b/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs index 971f1d4e98..c12ed53af5 100644 --- a/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs +++ b/tests/NLog.UnitTests/Targets/TargetWithContextTest.cs @@ -401,5 +401,56 @@ public void TargetWithContextPropertyTypeTest() Assert.Contains(new KeyValuePair("object-non-existing", null), lastCombinedProperties); Assert.DoesNotContain("object-non-existing-empty", lastCombinedProperties.Keys); } + + [Theory] + [MemberData(nameof(ConvertFromStringTestCases))] + public void GetPropertyValueFromStringTest(string value, Type propertyType, object expected, bool? includeEmptyValue = null) + { + // Arrange + var targetPropertyWithContext = new TargetPropertyWithContext("@test", value) + { + PropertyType = propertyType, + IncludeEmptyValue = includeEmptyValue ?? false, + }; + + // Act + var result = targetPropertyWithContext.RenderValue(LogEventInfo.CreateNullEvent()); + + // Assert + Assert.Equal(expected, result); + } + + public static IEnumerable ConvertFromStringTestCases() + { + yield return new object[] { "true", typeof(bool), true }; + yield return new object[] { "True", typeof(bool), true }; + yield return new object[] { 1.2.ToString(), typeof(decimal), (decimal)1.2 }; + yield return new object[] { 1.2.ToString(), typeof(double), (double)1.2 }; + yield return new object[] { 1.2.ToString(), typeof(float), (float)1.2 }; + yield return new object[] { "2:30", typeof(TimeSpan), new TimeSpan(0, 2, 30, 0), }; + yield return new object[] { "2018-12-23 22:56", typeof(DateTime), new DateTime(2018, 12, 23, 22, 56, 0), }; + //yield return new object[] { new DateTime(2018, 12, 23, 22, 56, 0).ToString(CultureInfo.InvariantCulture), typeof(DateTime), new DateTime(2018, 12, 23, 22, 56, 0) }; + yield return new object[] { "2018-12-23", typeof(DateTime), new DateTime(2018, 12, 23, 0, 0, 0), }; + yield return new object[] { "2018-12-23 +2:30", typeof(DateTimeOffset), new DateTimeOffset(2018, 12, 23, 0, 0, 0, new TimeSpan(2, 30, 0)) }; + yield return new object[] { "3888CCA3-D11D-45C9-89A5-E6B72185D287", typeof(Guid), Guid.Parse("3888CCA3-D11D-45C9-89A5-E6B72185D287") }; + yield return new object[] { "3888CCA3D11D45C989A5E6B72185D287", typeof(Guid), Guid.Parse("3888CCA3-D11D-45C9-89A5-E6B72185D287") }; + yield return new object[] { "3", typeof(byte), (byte)3 }; + yield return new object[] { "3", typeof(sbyte), (sbyte)3 }; + yield return new object[] { "3", typeof(short), (short)3 }; + yield return new object[] { " 3 ", typeof(short), (short)3 }; + yield return new object[] { "3", typeof(int), 3 }; + yield return new object[] { "3", typeof(long), (long)3 }; + yield return new object[] { "3", typeof(ushort), (ushort)3 }; + yield return new object[] { "3", typeof(uint), (uint)3 }; + yield return new object[] { "3", typeof(ulong), (ulong)3 }; + yield return new object[] { "3", typeof(string), "3" }; + yield return new object[] { "${event-properties:userid}", typeof(int), 0, true }; + yield return new object[] { "${event-properties:userid}", typeof(int), (int?)null, false }; + yield return new object[] { "${date:universalTime=true:format=yyyy-MM:norawvalue=true}", typeof(DateTime), DateTime.SpecifyKind(DateTime.UtcNow.Date.AddDays(-DateTime.UtcNow.Day + 1), DateTimeKind.Unspecified) }; + yield return new object[] { "${shortdate:universalTime=true}", typeof(DateTime), DateTime.UtcNow.Date, true }; + yield return new object[] { "${shortdate:universalTime=true}", typeof(DateTime), DateTime.UtcNow.Date, false }; + yield return new object[] { "${shortdate:universalTime=true}", typeof(string), DateTime.UtcNow.Date.ToString("yyyy-MM-dd"), true }; + yield return new object[] { "${shortdate:universalTime=true}", typeof(string), DateTime.UtcNow.Date.ToString("yyyy-MM-dd"), false }; + } } } From c6bcce332612f2cd821c62b95db627f11464c76f Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 17 Jun 2025 08:04:14 +0200 Subject: [PATCH 172/224] TargetWithLayoutHeaderAndFooter - Layout-property-getter without casting check (#5884) --- src/NLog.Database/DatabaseTarget.cs | 2 +- .../TargetWithLayoutHeaderAndFooter.cs | 59 +++++++++++-------- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/NLog.Database/DatabaseTarget.cs b/src/NLog.Database/DatabaseTarget.cs index 2134a05c65..ad33735961 100644 --- a/src/NLog.Database/DatabaseTarget.cs +++ b/src/NLog.Database/DatabaseTarget.cs @@ -932,7 +932,7 @@ protected string BuildConnectionString(LogEventInfo logEvent) sb.Append("User id="); sb.Append(dbUserName); sb.Append(";Password="); - var password = _dbPasswordFixed ?? EscapeValueForConnectionString(_dbPassword?.Render(logEvent) ?? string.Empty); + var password = _dbPasswordFixed ?? EscapeValueForConnectionString(RenderLogEvent(_dbPassword, logEvent)); sb.Append(password); sb.Append(";"); } diff --git a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs index dce2e0e4f9..e8e621a989 100644 --- a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs +++ b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs @@ -40,6 +40,8 @@ namespace NLog.Targets /// public abstract class TargetWithLayoutHeaderAndFooter : TargetWithLayout { + private Layout _layout; + /// /// Initializes a new instance of the class. /// @@ -48,6 +50,8 @@ public abstract class TargetWithLayoutHeaderAndFooter : TargetWithLayout /// protected TargetWithLayoutHeaderAndFooter() { + var layout = base.Layout; + _layout = layout is LayoutWithHeaderAndFooter headerAndFooter ? headerAndFooter.Layout : layout; } /// @@ -59,24 +63,23 @@ protected TargetWithLayoutHeaderAndFooter() /// public override Layout Layout { - get => LHF.Layout; - + get => _layout; set { - if (value is LayoutWithHeaderAndFooter) + if (value is LayoutWithHeaderAndFooter layout) { base.Layout = value; + _layout = layout.Layout; } - else if (LHF is null) + else if (base.Layout is LayoutWithHeaderAndFooter headerAndFooter) { - LHF = new LayoutWithHeaderAndFooter() - { - Layout = value - }; + headerAndFooter.Layout = value; + _layout = value; } else { - LHF.Layout = value; + base.Layout = new LayoutWithHeaderAndFooter() { Layout = value }; + _layout = value; } } } @@ -87,8 +90,18 @@ public override Layout Layout /// public Layout? Footer { - get => LHF.Footer; - set => LHF.Footer = value; + get => (base.Layout as LayoutWithHeaderAndFooter)?.Footer; + set + { + if (base.Layout is LayoutWithHeaderAndFooter headerAndFooter) + { + headerAndFooter.Footer = value; + } + else if (value is not null) + { + base.Layout = new LayoutWithHeaderAndFooter() { Layout = base.Layout, Footer = value }; + } + } } /// @@ -97,18 +110,18 @@ public Layout? Footer /// public Layout? Header { - get => LHF.Header; - set => LHF.Header = value; - } - - /// - /// Gets or sets the layout with header and footer. - /// - /// The layout with header and footer. - private LayoutWithHeaderAndFooter LHF - { - get => (LayoutWithHeaderAndFooter)base.Layout; - set => base.Layout = value; + get => (base.Layout as LayoutWithHeaderAndFooter)?.Header; + set + { + if (base.Layout is LayoutWithHeaderAndFooter headerAndFooter) + { + headerAndFooter.Header = value; + } + else if (value is not null) + { + base.Layout = new LayoutWithHeaderAndFooter() { Layout = base.Layout, Header = value }; + } + } } } } From ba566b3a543df025c8c9f85f743dc8c33c3e8920 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 17 Jun 2025 09:49:56 +0200 Subject: [PATCH 173/224] AsyncTargetWrapper - LogEventDropped and EventQueueGrow events fixes (#5885) Co-authored-by: Denis --- .../Targets/Wrappers/AsyncRequestQueue-T.cs | 2 +- .../Targets/Wrappers/AsyncTargetWrapper.cs | 8 +- .../Wrappers/ConcurrentRequestQueue.cs | 6 +- .../Wrappers/AsyncRequestQueueTests.cs | 37 +--- .../Wrappers/AsyncTargetWrapperTests.cs | 187 ++++++++++++++++-- .../Wrappers/CommonRequestQueueTests.cs | 128 ++++++++++++ .../Wrappers/ConcurrentRequestQueueTests.cs | 82 +++++--- 7 files changed, 368 insertions(+), 82 deletions(-) create mode 100644 tests/NLog.UnitTests/Targets/Wrappers/CommonRequestQueueTests.cs diff --git a/src/NLog/Targets/Wrappers/AsyncRequestQueue-T.cs b/src/NLog/Targets/Wrappers/AsyncRequestQueue-T.cs index bb0a1b892d..a4a00bb56e 100644 --- a/src/NLog/Targets/Wrappers/AsyncRequestQueue-T.cs +++ b/src/NLog/Targets/Wrappers/AsyncRequestQueue-T.cs @@ -93,8 +93,8 @@ public override bool Enqueue(AsyncLogEventInfo logEventInfo) case AsyncTargetWrapperOverflowAction.Grow: InternalLogger.Debug("AsyncQueue - Growing the size of queue, because queue is full"); - OnLogEventQueueGrows(currentCount + 1); QueueLimit *= 2; + OnLogEventQueueGrows(currentCount + 1); break; case AsyncTargetWrapperOverflowAction.Block: diff --git a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs index 30cdd866f3..698ec2b9f5 100644 --- a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs @@ -161,7 +161,7 @@ public event EventHandler LogEventDropped { add { - if (_eventQueueGrowEvent == null && _requestQueue != null) + if (_logEventDroppedEvent is null) { _requestQueue.LogEventDropped += OnRequestQueueDropItem; } @@ -172,7 +172,7 @@ public event EventHandler LogEventDropped { _logEventDroppedEvent -= value; - if (_eventQueueGrowEvent == null && _requestQueue != null) + if (_logEventDroppedEvent is null) { _requestQueue.LogEventDropped -= OnRequestQueueDropItem; } @@ -186,7 +186,7 @@ public event EventHandler EventQueueGrow { add { - if (_eventQueueGrowEvent == null && _requestQueue != null) + if (_eventQueueGrowEvent is null) { _requestQueue.LogEventQueueGrow += OnRequestQueueGrow; } @@ -197,7 +197,7 @@ public event EventHandler EventQueueGrow { _eventQueueGrowEvent -= value; - if (_eventQueueGrowEvent == null && _requestQueue != null) + if (_eventQueueGrowEvent is null) { _requestQueue.LogEventQueueGrow -= OnRequestQueueGrow; } diff --git a/src/NLog/Targets/Wrappers/ConcurrentRequestQueue.cs b/src/NLog/Targets/Wrappers/ConcurrentRequestQueue.cs index 3e3ffd5d62..24f62f786f 100644 --- a/src/NLog/Targets/Wrappers/ConcurrentRequestQueue.cs +++ b/src/NLog/Targets/Wrappers/ConcurrentRequestQueue.cs @@ -97,8 +97,12 @@ public override bool Enqueue(AsyncLogEventInfo logEventInfo) case AsyncTargetWrapperOverflowAction.Grow: { InternalLogger.Debug("AsyncQueue - Growing the size of queue, because queue is full"); + var currentQueueLimit = QueueLimit; + if (currentCount > currentQueueLimit) + { + QueueLimit = currentQueueLimit * 2; + } OnLogEventQueueGrows(currentCount); - QueueLimit *= 2; } break; } diff --git a/tests/NLog.UnitTests/Targets/Wrappers/AsyncRequestQueueTests.cs b/tests/NLog.UnitTests/Targets/Wrappers/AsyncRequestQueueTests.cs index 7c1c3be3f6..13feb88ca8 100644 --- a/tests/NLog.UnitTests/Targets/Wrappers/AsyncRequestQueueTests.cs +++ b/tests/NLog.UnitTests/Targets/Wrappers/AsyncRequestQueueTests.cs @@ -210,43 +210,16 @@ public void AsyncRequestQueueClearTest() [Fact] public void RaiseEventLogEventQueueGrow_OnLogItems() { - const int RequestsLimit = 2; - const int EventsCount = 5; - const int ExpectedCountOfGrovingTimes = 2; - const int ExpectedFinalSize = 8; - int grovingItemsCount = 0; - - AsyncRequestQueue requestQueue = new AsyncRequestQueue(RequestsLimit, AsyncTargetWrapperOverflowAction.Grow); - - requestQueue.LogEventQueueGrow += (o, e) => { grovingItemsCount++; }; - - for (int i = 0; i < EventsCount; i++) - { - requestQueue.Enqueue(new AsyncLogEventInfo()); - } - - Assert.Equal(ExpectedCountOfGrovingTimes, grovingItemsCount); - Assert.Equal(ExpectedFinalSize, requestQueue.QueueLimit); + CommonRequestQueueTests.RaiseEventLogEventQueueGrow_OnLogItems(GetAsyncRequestQueue); } [Fact] public void RaiseEventLogEventDropped_OnLogItems() { - const int RequestsLimit = 2; - const int EventsCount = 5; - int discardedItemsCount = 0; - - int ExpectedDiscardedItemsCount = EventsCount - RequestsLimit; - AsyncRequestQueue requestQueue = new AsyncRequestQueue(RequestsLimit, AsyncTargetWrapperOverflowAction.Discard); - - requestQueue.LogEventDropped += (o, e) => { discardedItemsCount++; }; - - for (int i = 0; i < EventsCount; i++) - { - requestQueue.Enqueue(new AsyncLogEventInfo()); - } - - Assert.Equal(ExpectedDiscardedItemsCount, discardedItemsCount); + CommonRequestQueueTests.RaiseEventLogEventDropped_OnLogItems(GetAsyncRequestQueue); } + + private static AsyncRequestQueueBase GetAsyncRequestQueue(int size, AsyncTargetWrapperOverflowAction action) => + new AsyncRequestQueue(size, action); } } diff --git a/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs b/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs index 01c4874c89..1feb308f52 100644 --- a/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs +++ b/tests/NLog.UnitTests/Targets/Wrappers/AsyncTargetWrapperTests.cs @@ -84,7 +84,7 @@ public void AsyncTargetWrapperSyncTest_NoLock_WhenTimeToSleepBetweenBatchesIsEqu { RetryingIntegrationTest(3, () => { - AsyncTargetWrapperSyncTest_WhenTimeToSleepBetweenBatchesIsEqualToZero(false); + AsyncTargetWrapperSyncTest_WhenTimeToSleepBetweenBatchesIsEqualToZero(false); }); } @@ -516,11 +516,12 @@ public void FlushingMultipleTimesSimultaneous() } [Fact] - public void LogEventDropped_OnRequestqueueOverflow() + public void LogEventDropped_OnRequestQueueOverflow() { - int queueLimit = 2; - int loggedEventCount = 5; - int eventsCounter = 0; + const int queueLimit = 2; + const int loggedEventCount = 5; + const int expectedEventsDropped = loggedEventCount - queueLimit; + int eventsDroppedCounter = 0; var myTarget = new MyTarget(); var targetWrapper = new AsyncTargetWrapper() @@ -539,18 +540,38 @@ public void LogEventDropped_OnRequestqueueOverflow() try { - targetWrapper.LogEventDropped += (o, e) => { eventsCounter++; }; + // subscribe, log, assert values were reported + targetWrapper.LogEventDropped += OnLogEventDropped; + + LogEvents(); + + Assert.Equal(expectedEventsDropped, eventsDroppedCounter); + + // unsubscribe, log, assert values were not reported + targetWrapper.LogEventDropped -= OnLogEventDropped; + + LogEvents(); + + Assert.Equal(expectedEventsDropped, eventsDroppedCounter); + } + finally + { + logFactory.Configuration = null; + } + return; + + void LogEvents() + { for (int i = 0; i < loggedEventCount; i++) { logger.Info("Hello"); } - - Assert.Equal(loggedEventCount - queueLimit, eventsCounter); } - finally + + void OnLogEventDropped(object _, LogEventDroppedEventArgs logEventDroppedEventArgs) { - logFactory.Configuration = null; + eventsDroppedCounter++; } } @@ -633,12 +654,15 @@ public void LogEventNotDropped_IfOverflowActionGrow() [Fact] public void EventQueueGrow_OnQueueGrow() { - int queueLimit = 2; - int loggedEventCount = 10; - - int expectedGrowingNumber = 3; - - int eventsCounter = 0; + const int queueLimit = 2; + const int loggedEventCount = 10; + const int expectedGrowingNumber = 3; + const int expectedNewQueueSize = 16; + const int expectedRequestsCount = expectedNewQueueSize / 2 + 1; + + int growEventsCounter = 0; + long reportedRequestsCount = 0; + long reportedNewQueueSize = 0; var myTarget = new MyTarget(); var targetWrapper = new AsyncTargetWrapper() @@ -657,19 +681,146 @@ public void EventQueueGrow_OnQueueGrow() try { - targetWrapper.EventQueueGrow += (o, e) => { eventsCounter++; }; + // subscribe, log, assert values were reported + targetWrapper.EventQueueGrow += OnEventQueueGrow; + + LogEvents(); + + Assert.Equal(expectedGrowingNumber, growEventsCounter); + Assert.Equal(expectedRequestsCount, reportedRequestsCount); + Assert.Equal(expectedNewQueueSize, reportedNewQueueSize); + + // unsubscribe, log, assert values were not reported + targetWrapper.EventQueueGrow -= OnEventQueueGrow; + + LogEvents(); + + Assert.Equal(expectedGrowingNumber, growEventsCounter); + Assert.Equal(expectedRequestsCount, reportedRequestsCount); + Assert.Equal(expectedNewQueueSize, reportedNewQueueSize); + } + finally + { + logFactory.Configuration = null; + } + + return; + void LogEvents() + { for (int i = 0; i < loggedEventCount; i++) { logger.Info("Hello"); } + } - Assert.Equal(expectedGrowingNumber, eventsCounter); + void OnEventQueueGrow(object _, LogEventQueueGrowEventArgs e) + { + growEventsCounter++; + reportedRequestsCount = e.RequestsCount; + reportedNewQueueSize = e.NewQueueSize; + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void EnqueueLimitReached_EventsAreCalled_SubscriptionOrderDoesntMatter(bool reverseSubscribeOrder) + { + const int queueLimit = 2; + const int loggedEventCount = 5; + const int expectedEventsDropped = loggedEventCount - queueLimit; + const int expectedGrowingNumber = 2; + const int expectedNewQueueSize = 8; + + int eventsDroppedCounter = 0; + int growEventsCounter = 0; + long reportedRequestsCount = 0; + long reportedNewQueueSize = 0; + var myTarget = new MyTarget(); + + var targetWrapper = new AsyncTargetWrapper() + { + WrappedTarget = myTarget, + QueueLimit = queueLimit, + TimeToSleepBetweenBatches = 500, // Make it slow + OverflowAction = AsyncTargetWrapperOverflowAction.Discard, + }; + + var logFactory = new LogFactory(); + var loggingConfig = new NLog.Config.LoggingConfiguration(logFactory); + loggingConfig.AddRuleForAllLevels(targetWrapper); + logFactory.Configuration = loggingConfig; + var logger = logFactory.GetLogger("Test"); + + try + { + // subscribe, log, assert values were reported - discards at first, then grows + if (reverseSubscribeOrder) + { + targetWrapper.LogEventDropped += OnLogEventDropped; + targetWrapper.EventQueueGrow += OnEventQueueGrow; + } + else + { + targetWrapper.EventQueueGrow += OnEventQueueGrow; + targetWrapper.LogEventDropped += OnLogEventDropped; + } + + LogEvents(); + + Assert.Equal(expectedEventsDropped, eventsDroppedCounter); + Assert.Equal(0, growEventsCounter); + + // and now grow + targetWrapper.OverflowAction = AsyncTargetWrapperOverflowAction.Grow; + + LogEvents(); + + Assert.Equal(expectedGrowingNumber, growEventsCounter); + Assert.Equal(loggedEventCount, reportedRequestsCount); + Assert.Equal(expectedNewQueueSize, reportedNewQueueSize); + Assert.Equal(expectedEventsDropped, eventsDroppedCounter); + + // unsubscribe, log, assert values were not reported + targetWrapper.LogEventDropped -= OnLogEventDropped; + targetWrapper.EventQueueGrow -= OnEventQueueGrow; + + targetWrapper.OverflowAction = AsyncTargetWrapperOverflowAction.Discard; + LogEvents(); + + targetWrapper.OverflowAction = AsyncTargetWrapperOverflowAction.Grow; + LogEvents(); + + Assert.Equal(expectedGrowingNumber, growEventsCounter); + Assert.Equal(expectedEventsDropped, eventsDroppedCounter); } finally { logFactory.Configuration = null; } + + return; + + void LogEvents() + { + for (int i = 0; i < loggedEventCount; i++) + { + logger.Info("Hello"); + } + } + + void OnLogEventDropped(object _, LogEventDroppedEventArgs logEventDroppedEventArgs) + { + eventsDroppedCounter++; + } + + void OnEventQueueGrow(object _, LogEventQueueGrowEventArgs e) + { + growEventsCounter++; + reportedRequestsCount = e.RequestsCount; + reportedNewQueueSize = e.NewQueueSize; + } } [Fact] diff --git a/tests/NLog.UnitTests/Targets/Wrappers/CommonRequestQueueTests.cs b/tests/NLog.UnitTests/Targets/Wrappers/CommonRequestQueueTests.cs new file mode 100644 index 0000000000..10ec7f3e31 --- /dev/null +++ b/tests/NLog.UnitTests/Targets/Wrappers/CommonRequestQueueTests.cs @@ -0,0 +1,128 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.UnitTests.Targets.Wrappers +{ + using System; + using NLog.Common; + using NLog.Targets.Wrappers; + using Xunit; + + internal static class CommonRequestQueueTests + { + internal static void RaiseEventLogEventQueueGrow_OnLogItems(Func getQueue) + { + const int InitialSize = 1; + const int ExpectedFinalGrowingTimes = 3; + const int ExpectedFinalSize = 8; + + var requestQueue = getQueue(InitialSize, AsyncTargetWrapperOverflowAction.Grow); + + int growingTimesCount = 0; + long reportedRequestsCount = 0; + long reportedNewQueueSize = 0; + + requestQueue.LogEventQueueGrow += OnLogEventQueueGrow; + + requestQueue.Enqueue(new AsyncLogEventInfo()); + Assert.Equal(0, growingTimesCount); + + requestQueue.Enqueue(new AsyncLogEventInfo()); + Assert.Equal(1, growingTimesCount); + Assert.Equal(2, reportedRequestsCount); + Assert.Equal(2, reportedNewQueueSize); + + requestQueue.Enqueue(new AsyncLogEventInfo()); + Assert.Equal(2, growingTimesCount); + Assert.Equal(3, reportedRequestsCount); + Assert.Equal(4, reportedNewQueueSize); + + requestQueue.Enqueue(new AsyncLogEventInfo()); + Assert.Equal(2, growingTimesCount); + + requestQueue.Enqueue(new AsyncLogEventInfo()); + Assert.Equal(ExpectedFinalGrowingTimes, growingTimesCount); + Assert.Equal(5, reportedRequestsCount); + Assert.Equal(ExpectedFinalSize, reportedNewQueueSize); + Assert.Equal(ExpectedFinalSize, requestQueue.QueueLimit); + + requestQueue.LogEventQueueGrow -= OnLogEventQueueGrow; + + for (var i = reportedRequestsCount; i < ExpectedFinalSize + 1; i++) + { + requestQueue.Enqueue(new AsyncLogEventInfo()); + } + + Assert.Equal(ExpectedFinalGrowingTimes, growingTimesCount); + Assert.Equal(ExpectedFinalSize * 2, requestQueue.QueueLimit); + + return; + + void OnLogEventQueueGrow(object _, LogEventQueueGrowEventArgs e) + { + growingTimesCount++; + reportedRequestsCount = e.RequestsCount; + reportedNewQueueSize = e.NewQueueSize; + } + } + + internal static void RaiseEventLogEventDropped_OnLogItems(Func getQueue) + { + const int RequestsLimit = 2; + const int EventsCount = 5; + const int ExpectedDiscardedItemsCount = EventsCount - RequestsLimit; + int discardedItemsCount = 0; + + var requestQueue = getQueue(RequestsLimit, AsyncTargetWrapperOverflowAction.Discard); + requestQueue.LogEventDropped += OnLogEventDropped; + + for (int i = 0; i < EventsCount; i++) + { + requestQueue.Enqueue(new AsyncLogEventInfo()); + } + + Assert.Equal(ExpectedDiscardedItemsCount, discardedItemsCount); + + requestQueue.LogEventDropped -= OnLogEventDropped; + requestQueue.Enqueue(new AsyncLogEventInfo()); + Assert.Equal(ExpectedDiscardedItemsCount, discardedItemsCount); + + return; + + void OnLogEventDropped(object o, LogEventDroppedEventArgs e) + { + discardedItemsCount++; + } + } + } +} diff --git a/tests/NLog.UnitTests/Targets/Wrappers/ConcurrentRequestQueueTests.cs b/tests/NLog.UnitTests/Targets/Wrappers/ConcurrentRequestQueueTests.cs index fb634f8c7e..461ba457f8 100644 --- a/tests/NLog.UnitTests/Targets/Wrappers/ConcurrentRequestQueueTests.cs +++ b/tests/NLog.UnitTests/Targets/Wrappers/ConcurrentRequestQueueTests.cs @@ -34,6 +34,8 @@ #if !NET35 using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using NLog.Common; using NLog.Targets.Wrappers; using Xunit; @@ -66,46 +68,74 @@ public void DequeueBatch_WithNonEmptyList_ReturnsValidCount(AsyncTargetWrapperOv } [Fact] - public void RaiseEventLogEventQueueGrow_OnLogItems() + public void Enqueue_WhenGrowBehaviourAndHighlyConcurrent_GrowOnce() { - const int RequestsLimit = 2; - const int EventsCount = 5; - const int ExpectedCountOfGrovingTimes = 2; - const int ExpectedFinalSize = 8; - int grovingItemsCount = 0; - - ConcurrentRequestQueue requestQueue = new ConcurrentRequestQueue(RequestsLimit, AsyncTargetWrapperOverflowAction.Grow); - - requestQueue.LogEventQueueGrow += (o, e) => { grovingItemsCount++; }; + // Arrange + var requestQueue = new ConcurrentRequestQueue(2, AsyncTargetWrapperOverflowAction.Grow); - for (int i = 0; i < EventsCount; i++) + for (int i = 0; i < 4; i++) { requestQueue.Enqueue(new AsyncLogEventInfo()); } - Assert.Equal(ExpectedCountOfGrovingTimes, grovingItemsCount); - Assert.Equal(ExpectedFinalSize, requestQueue.QueueLimit); - } + const int initialQueueCount = 4; + const int initialQueueLimit = 4; + Assert.Equal(initialQueueCount, requestQueue.Count); + Assert.Equal(initialQueueLimit, requestQueue.QueueLimit); - [Fact] - public void RaiseEventLogEventDropped_OnLogItems() - { - const int RequestsLimit = 2; - const int EventsCount = 5; - int discardedItemsCount = 0; + const int threadCount = 4; + var readyToEnqueue = new Barrier(threadCount); + var enqueued = new CountdownEvent(threadCount); - int ExpectedDiscardedItemsCount = EventsCount - RequestsLimit; - ConcurrentRequestQueue requestQueue = new ConcurrentRequestQueue(RequestsLimit, AsyncTargetWrapperOverflowAction.Discard); + // Act + for (int i = 0; i < 100; i++) + { + for (int j = 0; j < threadCount; j++) + { + Task.Run(EnqueueWhenAllThreadsReady); + } + + Assert.True(enqueued.Wait(5000)); + enqueued.Reset(); + + // Assert + const int expectedQueueCount = initialQueueCount + threadCount; + const int expectedQueueLimit = initialQueueLimit * 2; + Assert.Equal(expectedQueueCount, requestQueue.Count); + Assert.Equal(expectedQueueLimit, requestQueue.QueueLimit); + + // rollback requests count and limit + requestQueue.DequeueBatch(threadCount); + requestQueue.QueueLimit = initialQueueLimit; + } - requestQueue.LogEventDropped += (o, e) => { discardedItemsCount++; }; + return; - for (int i = 0; i < EventsCount; i++) + void EnqueueWhenAllThreadsReady() { - requestQueue.Enqueue(new AsyncLogEventInfo()); + var logEvent = new AsyncLogEventInfo(); + readyToEnqueue.SignalAndWait(); + + requestQueue.Enqueue(logEvent); + + enqueued.Signal(); } + } - Assert.Equal(ExpectedDiscardedItemsCount, discardedItemsCount); + [Fact] + public void RaiseEventLogEventQueueGrow_OnLogItems() + { + CommonRequestQueueTests.RaiseEventLogEventQueueGrow_OnLogItems(GetConcurrentRequestQueue); } + + [Fact] + public void RaiseEventLogEventDropped_OnLogItems() + { + CommonRequestQueueTests.RaiseEventLogEventDropped_OnLogItems(GetConcurrentRequestQueue); + } + + private static AsyncRequestQueueBase GetConcurrentRequestQueue(int size, AsyncTargetWrapperOverflowAction action) => + new ConcurrentRequestQueue(size, action); } } From de5f6bffde4611613e3de9cd7e988effd42adf77 Mon Sep 17 00:00:00 2001 From: JohnVerheij Date: Tue, 17 Jun 2025 21:55:03 +0200 Subject: [PATCH 174/224] DatabaseTarget - Constructor with dbConnectionFactory-delegate set empty ConnectionString (#5881) --- src/NLog.Database/DatabaseTarget.cs | 3 +- .../DatabaseTargetTests.cs | 62 ++++++++++++------- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/NLog.Database/DatabaseTarget.cs b/src/NLog.Database/DatabaseTarget.cs index ad33735961..9fe9ae55df 100644 --- a/src/NLog.Database/DatabaseTarget.cs +++ b/src/NLog.Database/DatabaseTarget.cs @@ -120,8 +120,9 @@ public DatabaseTarget(string name) : this() /// public DatabaseTarget(Func dbConnectionFactory) { - CommandType = CommandType.Text; _dbConnectionFactory = dbConnectionFactory ?? throw new ArgumentNullException(nameof(dbConnectionFactory)); + CommandType = CommandType.Text; + ConnectionString = new SimpleLayout(""); } /// diff --git a/tests/NLog.Database.Tests/DatabaseTargetTests.cs b/tests/NLog.Database.Tests/DatabaseTargetTests.cs index c88ed3610d..31007f087c 100644 --- a/tests/NLog.Database.Tests/DatabaseTargetTests.cs +++ b/tests/NLog.Database.Tests/DatabaseTargetTests.cs @@ -539,7 +539,7 @@ public void LevelParameterTest(DbType? dbType, bool noRawValue, string expectedV string lvlLayout = noRawValue ? "${level:format=Ordinal:norawvalue=true}" : "${level:format=Ordinal}"; MockDbConnection.ClearLog(); - DatabaseTarget dt = new DatabaseTarget(() => new MockDbConnection()) + DatabaseTarget dt = new DatabaseTarget(() => new MockDbConnection("A")) { CommandText = "INSERT INTO FooBar VALUES(@lvl, @msg)", KeepConnection = true, @@ -564,7 +564,7 @@ public void LevelParameterTest(DbType? dbType, bool noRawValue, string expectedV Assert.Null(ex); } - string expectedLog = string.Format(@"Open('Server=.;Trusted_Connection=SSPI;'). + string expectedLog = string.Format(@"Open('A'). CreateParameter(0) Parameter #0 Direction=Input Parameter #0 Name=lvl{0} @@ -922,34 +922,38 @@ Add Parameter Parameter #2 [Fact] public void ConnectionStringBuilderTest1() { - DatabaseTarget dt; + var dbProvider = typeof(MockDbConnection).AssemblyQualifiedName; + Assert.NotNull(dbProvider); - dt = new DatabaseTarget(); + DatabaseTarget dt = CreateTargetWithProvider(); Assert.Equal("Server=.;Trusted_Connection=SSPI;", GetConnectionString(dt)); - dt = new DatabaseTarget(); + dt = CreateTargetWithProvider(); dt.DBHost = "${logger}"; Assert.Equal("Server=Logger1;Trusted_Connection=SSPI;", GetConnectionString(dt)); - dt = new DatabaseTarget(); + dt = CreateTargetWithProvider(); dt.DBHost = "HOST1"; dt.DBDatabase = "${logger}"; Assert.Equal("Server=HOST1;Trusted_Connection=SSPI;Database=Logger1", GetConnectionString(dt)); - dt = new DatabaseTarget(); + dt = CreateTargetWithProvider(); dt.DBHost = "HOST1"; dt.DBDatabase = "${logger}"; dt.DBUserName = "user1"; dt.DBPassword = "password1"; Assert.Equal("Server=HOST1;User id=user1;Password=password1;Database=Logger1", GetConnectionString(dt)); - dt = new DatabaseTarget(); + dt = CreateTargetWithProvider(); dt.ConnectionString = "customConnectionString42"; dt.DBHost = "HOST1"; dt.DBDatabase = "${logger}"; dt.DBUserName = "user1"; dt.DBPassword = "password1"; Assert.Equal("customConnectionString42", GetConnectionString(dt)); + + // local function to create a target with the provider set + DatabaseTarget CreateTargetWithProvider() => new DatabaseTarget { DBProvider = dbProvider }; } [Fact] @@ -2064,26 +2068,39 @@ public void DbTypeSetterNullTest() } [Theory] - [InlineData("A", "B", "B")] - [InlineData("A", "", "A")] - [InlineData("A", null, "A")] - public void DbFactoryConnectionStringTest(string csInConn, string csInDbTarget, string csExpected) + [InlineData("A", "A")] + [InlineData("", "")] + [InlineData(null, null)] + public void DbFactoryConnectionStringTest1(string csInConn, string csExpected) { - // Clear log - MockDbConnection.ClearLog(); + // Arrange + var dt = new DatabaseTarget(() => new MockDbConnection(csInConn)); + var cs = GetConnectionString(dt); + // Assert + Assert.Equal(csExpected, cs); + } + + [Theory] + [InlineData("A", "", "Server=.;Trusted_Connection=SSPI;")] + [InlineData(null, "", "Server=.;Trusted_Connection=SSPI;")] + [InlineData("", "", "Server=.;Trusted_Connection=SSPI;")] + [InlineData("A", null, "A")] + [InlineData("", null, "")] + [InlineData("A", "B", "B")] + [InlineData("", "B", "B")] + [InlineData(null, "B", "B")] + public void DbFactoryConnectionStringTest2(string csInConn, string csInDbTarget, string csExpected) + { // Arrange - var con = new MockDbConnection(csInConn); - var dt = new DatabaseTarget(() => con) + var dt = new DatabaseTarget(() => new MockDbConnection(csInConn)) { ConnectionString = csInDbTarget }; - IDbConnection openedConnection = dt.OpenConnection(csInDbTarget, new LogEventInfo()); + var cs = GetConnectionString(dt); // Assert - var log = MockDbConnection.Log; - Assert.Contains($"Open('{csExpected}')", log); - Assert.Same(con, openedConnection); // Ensure factory-provided connection is used + Assert.Equal(csExpected, cs); } private static void AssertLog(string expectedLog) @@ -2091,10 +2108,10 @@ private static void AssertLog(string expectedLog) Assert.Equal(expectedLog.Replace("\r", ""), MockDbConnection.Log.Replace("\r", "")); } - private string GetConnectionString(DatabaseTarget dt) + private static string GetConnectionString(DatabaseTarget dt) { MockDbConnection.ClearLog(); - dt.DBProvider = typeof(MockDbConnection).AssemblyQualifiedName; + dt.CommandText = "NotImportant"; var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.Configuration.AddRuleForAllLevels(dt)).LogFactory; @@ -2173,6 +2190,7 @@ public void Dispose() public static void ClearLog() { Log = string.Empty; + LastConnectionString = null; } public void AddToLog(string message, params object[] args) From 3f301560e3c1afd856be9b782de29acfef54cd21 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 19 Jun 2025 23:30:25 +0200 Subject: [PATCH 175/224] FileTarget - Report on failure when single LogEvent (#5887) --- src/NLog/Targets/FileTarget.cs | 106 +++++++++++++++++---------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index bde3dbaeec..77f184eed6 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -580,15 +580,24 @@ protected override void Write(LogEventInfo logEvent) throw new ArgumentException("The path is not of a legal form."); } - using (var targetStream = _reusableBatchFileWriteStream.Allocate()) + try { - using (var targetBuilder = ReusableLayoutBuilder.Allocate()) - using (var targetBuffer = _reusableEncodingBuffer.Allocate()) + using (var targetStream = _reusableBatchFileWriteStream.Allocate()) { - RenderFormattedMessageToStream(logEvent, targetBuilder.Result, targetBuffer.Result, targetStream.Result); - } + using (var targetBuilder = ReusableLayoutBuilder.Allocate()) + using (var targetBuffer = _reusableEncodingBuffer.Allocate()) + { + RenderFormattedMessageToStream(logEvent, targetBuilder.Result, targetBuffer.Result, targetStream.Result); + } - WriteToFile(filename, logEvent, targetStream.Result, out var _); + var bytes = new ArraySegment(targetStream.Result.GetBuffer(), 0, (int)targetStream.Result.Length); + WriteBytesToFile(filename, logEvent, bytes); + } + } + catch (Exception ex) + { + InternalLogger.Error(ex, "{0}: Failed writing to FileName: '{1}'", this, filename); + throw; } } @@ -596,42 +605,53 @@ protected override void Write(LogEventInfo logEvent) protected override void Write(IList logEvents) { var buckets = logEvents.BucketSort(_getFileNameFromLayout); + foreach (var bucket in buckets) + { + if (string.IsNullOrEmpty(bucket.Key)) + { + InternalLogger.Warn("{0}: FileName Layout returned empty string. The path is not of a legal form.", this); + var emptyPathException = new ArgumentException("The path is not of a legal form."); + for (int i = 0; i < logEvents.Count; ++i) + { + logEvents[i].Continuation(emptyPathException); + } + continue; + } + + try + { + WriteLogEventsToFile(bucket.Key, bucket.Value); + } + catch (Exception ex) + { + InternalLogger.Error(ex, "{0}: Failed writing to FileName: '{1}'", this, bucket.Key); + if (ExceptionMustBeRethrown(ex)) + throw; + for (int i = 0; i < bucket.Value.Count; ++i) + bucket.Value[i].Continuation(ex); + } + } + } + + private void WriteLogEventsToFile(string filename, IList logEvents) + { using (var reusableStream = _reusableBatchFileWriteStream.Allocate()) { var ms = reusableStream.Result ?? new MemoryStream(); - foreach (var bucket in buckets) + int currentIndex = 0; + while (currentIndex < logEvents.Count) { - int bucketCount = bucket.Value.Count; - if (bucketCount <= 0) - continue; - - string filename = bucket.Key; - if (string.IsNullOrEmpty(filename)) - { - InternalLogger.Warn("{0}: FileName Layout returned empty string. The path is not of a legal form.", this); - var emptyPathException = new ArgumentException("The path is not of a legal form."); - for (int i = 0; i < bucketCount; ++i) - { - bucket.Value[i].Continuation(emptyPathException); - } - continue; - } + ms.Position = 0; + ms.SetLength(0); - int currentIndex = 0; - while (currentIndex < bucketCount) - { - ms.Position = 0; - ms.SetLength(0); + var logEventWriteCount = WriteToMemoryStream(logEvents, currentIndex, ms); + var bytes = new ArraySegment(ms.GetBuffer(), 0, (int)ms.Length); + WriteBytesToFile(filename, logEvents[currentIndex].LogEvent, bytes); - var written = WriteToMemoryStream(bucket.Value, currentIndex, ms); - WriteToFile(filename, bucket.Value[currentIndex].LogEvent, ms, out var lastException); - for (int i = 0; i < written; ++i) - { - bucket.Value[currentIndex++].Continuation(lastException); - } - } + for (int i = 0; i < logEventWriteCount; ++i) + logEvents[currentIndex++].Continuation(null); } } } @@ -703,24 +723,6 @@ protected virtual void RenderFormattedMessage(LogEventInfo logEvent, StringBuild Layout.Render(logEvent, target); } - private void WriteToFile(string filename, LogEventInfo firstLogEvent, MemoryStream ms, out Exception? lastException) - { - try - { - ArraySegment bytes = new ArraySegment(ms.GetBuffer(), 0, (int)ms.Length); - WriteBytesToFile(filename, firstLogEvent, bytes); - lastException = null; - } - catch (Exception ex) - { - InternalLogger.Error(ex, "{0}: Failed writing to FileName: '{1}'", this, filename); - if (ExceptionMustBeRethrown(ex)) - throw; - - lastException = ex; - } - } - private void WriteBytesToFile(string filename, LogEventInfo firstLogEvent, ArraySegment bytes) { bool hasWritten = true; From 19383a4079cb826e42567ed75fa3d85ff42dd4bf Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 21 Jun 2025 13:05:42 +0200 Subject: [PATCH 176/224] StringBuilderExt - Change Append2DigitsZeroPadded to array-lookup (#5889) --- src/NLog/Internal/AppendBuilderCreator.cs | 17 --------- src/NLog/Internal/StringBuilderExt.cs | 38 ++++++++++++++++--- .../LayoutRenderers/LevelLayoutRenderer.cs | 11 ++---- .../WrapperLayoutRendererBuilderBase.cs | 18 +++++++-- src/NLog/Targets/FileTarget.cs | 10 ++--- 5 files changed, 55 insertions(+), 39 deletions(-) diff --git a/src/NLog/Internal/AppendBuilderCreator.cs b/src/NLog/Internal/AppendBuilderCreator.cs index 1a0f25a8fb..2eea875dee 100644 --- a/src/NLog/Internal/AppendBuilderCreator.cs +++ b/src/NLog/Internal/AppendBuilderCreator.cs @@ -42,7 +42,6 @@ namespace NLog.Internal internal struct AppendBuilderCreator : IDisposable { private static readonly StringBuilderPool _builderPool = new StringBuilderPool(Environment.ProcessorCount * 2); - private readonly StringBuilder _appendTarget; /// /// Access the new builder allocated @@ -54,26 +53,10 @@ internal struct AppendBuilderCreator : IDisposable public AppendBuilderCreator(bool mustBeEmpty) { _builder = _builderPool.Acquire(); - _appendTarget = _builder.Item; - } - - public AppendBuilderCreator(StringBuilder appendTarget, bool mustBeEmpty) - { - _appendTarget = appendTarget; - if (_appendTarget.Length > 0 && mustBeEmpty) - { - _builder = _builderPool.Acquire(); - } - else - { - _builder = new StringBuilderPool.ItemHolder(_appendTarget, null, 0); - } } public void Dispose() { - if (!ReferenceEquals(_builder.Item, _appendTarget)) - _builder.Item.CopyTo(_appendTarget); _builder.Dispose(); } } diff --git a/src/NLog/Internal/StringBuilderExt.cs b/src/NLog/Internal/StringBuilderExt.cs index 10f3d71e97..a9cb5f28e7 100644 --- a/src/NLog/Internal/StringBuilderExt.cs +++ b/src/NLog/Internal/StringBuilderExt.cs @@ -36,6 +36,7 @@ namespace NLog.Internal using System; using System.Globalization; using System.IO; + using System.Linq; using System.Text; using NLog.MessageTemplates; @@ -75,6 +76,7 @@ public static void AppendFormattedValue(this StringBuilder builder, object? valu /// value to append public static void AppendInvariant(this StringBuilder builder, int value) { +#if NETFRAMEWORK // Deal with negative numbers if (value < 0) { @@ -86,6 +88,9 @@ public static void AppendInvariant(this StringBuilder builder, int value) { AppendInvariant(builder, (uint)value); } +#else + builder.Append(value); +#endif } /// @@ -97,6 +102,7 @@ public static void AppendInvariant(this StringBuilder builder, int value) /// value to append public static void AppendInvariant(this StringBuilder builder, uint value) { +#if NETFRAMEWORK if (value == 0) { builder.Append('0'); @@ -105,6 +111,9 @@ public static void AppendInvariant(this StringBuilder builder, uint value) int digitCount = CalculateDigitCount(value); ApppendValueWithDigitCount(builder, value, digitCount); +#else + builder.Append(value); +#endif } private static int CalculateDigitCount(uint value) @@ -386,9 +395,17 @@ public static bool EqualTo(this StringBuilder builder, string other) /// the number internal static void Append2DigitsZeroPadded(this StringBuilder builder, int number) { - builder.Append((char)((number / 10) + '0')); - builder.Append((char)((number % 10) + '0')); + if (number < 0 || number >= _zeroPaddedDigits.Length) + { + builder.Append((char)((number / 10) + '0')); + builder.Append((char)((number % 10) + '0')); + } + else + { + builder.Append(_zeroPaddedDigits[number]); + } } + private static readonly string[] _zeroPaddedDigits = Enumerable.Range(0, 60).Select(i => i.ToString("D2")).ToArray(); /// /// Append a number and pad with 0 to 4 digits @@ -397,10 +414,19 @@ internal static void Append2DigitsZeroPadded(this StringBuilder builder, int num /// the number internal static void Append4DigitsZeroPadded(this StringBuilder builder, int number) { - builder.Append((char)(((number / 1000) % 10) + '0')); - builder.Append((char)(((number / 100) % 10) + '0')); - builder.Append((char)(((number / 10) % 10) + '0')); - builder.Append((char)(((number / 1) % 10) + '0')); +#if !NETFRAMEWORK + if (number > 999 && number < 10000) + { + builder.Append(number); + } + else +#endif + { + builder.Append((char)(((number / 1000) % 10) + '0')); + builder.Append((char)(((number / 100) % 10) + '0')); + builder.Append((char)(((number / 10) % 10) + '0')); + builder.Append((char)((number % 10) + '0')); + } } /// diff --git a/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs b/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs index fcd5357244..fd707bf8ee 100644 --- a/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs @@ -100,14 +100,11 @@ protected override void Append(StringBuilder builder, LogEventInfo logEvent) private static string GetUpperCaseString(LogLevel level) { - try - { - return _upperCaseMapper[level.Ordinal]; - } - catch (IndexOutOfRangeException) - { + var ordinal = level.Ordinal; + if (ordinal < 0 || ordinal >= _upperCaseMapper.Length) return level.ToString().ToUpperInvariant(); - } + else + return _upperCaseMapper[ordinal]; } private string GetFullNameString(LogLevel level) diff --git a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs index 607e193a4b..10d767771e 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs @@ -35,6 +35,7 @@ namespace NLog.LayoutRenderers.Wrappers { using System; using System.Text; + using NLog.Internal; /// /// Base class for s which wrapping other s. @@ -47,11 +48,22 @@ public abstract class WrapperLayoutRendererBuilderBase : WrapperLayoutRendererBa /// protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBuilder builder, int orgLength) { - using (var localTarget = new Internal.AppendBuilderCreator(builder, true)) + if (builder.Length > 0) { + using (var localTarget = new AppendBuilderCreator(true)) + { #pragma warning disable CS0618 // Type or member is obsolete - RenderFormattedMessage(logEvent, localTarget.Builder); - TransformFormattedMesssage(logEvent, localTarget.Builder); + RenderFormattedMessage(logEvent, localTarget.Builder); + TransformFormattedMesssage(logEvent, localTarget.Builder); +#pragma warning restore CS0618 // Type or member is obsolete + localTarget.Builder.CopyTo(builder); + } + } + else + { +#pragma warning disable CS0618 // Type or member is obsolete + RenderFormattedMessage(logEvent, builder); + TransformFormattedMesssage(logEvent, builder); #pragma warning restore CS0618 // Type or member is obsolete } } diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index 77f184eed6..a412cee6ba 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -590,8 +590,7 @@ protected override void Write(LogEventInfo logEvent) RenderFormattedMessageToStream(logEvent, targetBuilder.Result, targetBuffer.Result, targetStream.Result); } - var bytes = new ArraySegment(targetStream.Result.GetBuffer(), 0, (int)targetStream.Result.Length); - WriteBytesToFile(filename, logEvent, bytes); + WriteBytesToFile(filename, logEvent, targetStream.Result); } } catch (Exception ex) @@ -647,8 +646,7 @@ private void WriteLogEventsToFile(string filename, IList logE ms.SetLength(0); var logEventWriteCount = WriteToMemoryStream(logEvents, currentIndex, ms); - var bytes = new ArraySegment(ms.GetBuffer(), 0, (int)ms.Length); - WriteBytesToFile(filename, logEvents[currentIndex].LogEvent, bytes); + WriteBytesToFile(filename, logEvents[currentIndex].LogEvent, ms); for (int i = 0; i < logEventWriteCount; ++i) logEvents[currentIndex++].Continuation(null); @@ -723,7 +721,7 @@ protected virtual void RenderFormattedMessage(LogEventInfo logEvent, StringBuild Layout.Render(logEvent, target); } - private void WriteBytesToFile(string filename, LogEventInfo firstLogEvent, ArraySegment bytes) + private void WriteBytesToFile(string filename, LogEventInfo firstLogEvent, MemoryStream ms) { bool hasWritten = true; if (!_openFileCache.TryGetValue(filename, out var openFile)) @@ -739,7 +737,7 @@ private void WriteBytesToFile(string filename, LogEventInfo firstLogEvent, Array openFile = RollArchiveFile(filename, openFile, firstLogEvent, hasWritten); } - openFile.FileAppender.Write(bytes.Array, bytes.Offset, bytes.Count); + openFile.FileAppender.Write(ms.GetBuffer(), 0, (int)ms.Length); if (AutoFlush) { From be83582280f7a841b0d3c6ae75b4f95aaf07e914 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 21 Jun 2025 15:08:31 +0200 Subject: [PATCH 177/224] Version 6.0 RTM (#5886) --- CHANGELOG.md | 53 +++++++++++-------- README.md | 4 +- build.ps1 | 2 +- .../Layouts/Log4JXmlEventLayout.cs | 1 + src/NLog/NLog.csproj | 31 ++--------- src/NLog/SetupLoadConfigurationExtensions.cs | 6 +-- .../Wrappers/ConcurrentRequestQueueTests.cs | 4 +- 7 files changed, 45 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1a41bce9f..ad06630839 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,38 @@ Date format: (year/month/day) ## Change Log +### Version 6.0 (2025/06/21) + +**Major Changes** + +- Support AOT builds without build warnings. +- New FileTarget without ConcurrentWrites support, but still support KeepFileOpen (true/false). +- Moved old FileTarget into the new nuget-package NLog.Targets.ConcurrentFile. +- Created new nuget-package NLog.Targets.AtomicFile that supports ConcurrentWrites for NET8 on both Windows / Linux. +- Created new nuget-package NLog.Targets.GZipFile that uses GZipStream for writing directly to compressed files. +- Moved MailTarget into the new nuget-package NLog.Targets.Mail. +- Moved NetworkTarget into the new nuget-package NLog.Targets.Network. +- New GelfTarget introduced for the new nuget-package NLog.Targets.Network. +- New SyslogTarget introduced for the new nuget-package NLog.Targets.Network. +- Moved TraceTarget and NLogTraceListener into the new nuget-package NLog.Targets.Trace. +- Moved WebServiceTarget into the new nuget-package NLog.Targets.WebService +- Renamed ChainsawTarget to Log4JXmlTarget to match Log4JXmlEventLayout +- Removed dependency on System.Text.RegularExpressions and introduced new nuget-package NLog.RegEx. +- Removed dependency on System.Xml.XmlReader by implementing own internal basic XML-Parser. +- Added support for params ReadOnlySpan when using C# 13 +- Skip LogEventInfo.Parameters-array-allocation when unable to defer message-template formatting. +- Updated NLog API with Nullable-support and introduced Layout.Empty +- Marked [RequiredParameter] as obsolete with Nullable-support, and instead validate options at initialization. + +NLog v6.0 release notes: https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html + +List of all [NLog 6.0 Pull Requests](https://github.com/NLog/NLog/pulls?q=is%3Apr+is%3Amerged+milestone:%226.0%22) +- [Breaking Changes](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22breaking%20change%22+is%3Amerged+milestone:%226.0%22) +- [Breaking Behavior Changes](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22breaking%20behavior%20change%22+is%3Amerged+milestone:%226.0%22) +- [Features](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22Feature%22+is%3Amerged+milestone:%226.0%22) +- [Improvements](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22Enhancement%22+is%3Amerged+milestone:%226.0%22) +- [Performance](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22Performance%22+is%3Amerged+milestone:%226.0%22) + ### Version 6.0 RC4 (2025/06/15) **Improvements** @@ -70,34 +102,13 @@ Date format: (year/month/day) ### Version 6.0 Preview 1 (2025/04/27) -**Major Changes** - -- Support AOT builds without build warnings. -- New FileTarget without ConcurrentWrites support, but still support KeepFileOpen true / false. -- Moved old FileTarget into the new nuget-package NLog.Targets.ConcurrentFile. -- Created new nuget-package NLog.Targets.AtomicFile that supports ConcurrentWrites for NET8 on both Windows / Linux. -- Created new nuget-package NLog.Targets.GZipFile that uses GZipStream for writing directly to compressed files. -- Moved MailTarget into the new nuget-package NLog.Targets.Mail. -- Moved NetworkTarget into the new nuget-package NLog.Targets.Network. -- New GelfTarget introduced for the new nuget-package NLog.Targets.Network. -- New SyslogTarget introduced for the new nuget-package NLog.Targets.Network. -- Moved TraceTarget and NLogTraceListener into the new nuget-package NLog.Targets.Trace. -- Moved WebServiceTarget into the new nuget-package NLog.Targets.WebService -- Removed dependency on System.Text.RegularExpressions and introduced new nuget-package NLog.RegEx. -- Removed dependency on System.Xml.XmlReader by implementing own internal basic XML-Parser. - NLog v6.0 release notes: https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html List of all [NLog 6.0 Pull Requests](https://github.com/NLog/NLog/pulls?q=is%3Apr+is%3Amerged+milestone:%226.0%22) - - [Breaking Changes](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22breaking%20change%22+is%3Amerged+milestone:%226.0%22) - - [Breaking Behavior Changes](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22breaking%20behavior%20change%22+is%3Amerged+milestone:%226.0%22) - - [Features](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22Feature%22+is%3Amerged+milestone:%226.0%22) - - [Improvements](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22Enhancement%22+is%3Amerged+milestone:%226.0%22) - - [Performance](https://github.com/NLog/NLog/pulls?q=is%3Apr+label%3A%22Performance%22+is%3Amerged+milestone:%226.0%22) ### Version 5.3.4 (2024/09/12) diff --git a/README.md b/README.md index f58f5613c2..2f579c1b86 100644 --- a/README.md +++ b/README.md @@ -44,9 +44,9 @@ Having troubles? Check the [troubleshooting guide](https://github.com/NLog/NLog/ ----- - ℹ️ NLog 6.0 will support AOT + ℹ️ NLog 6.0 supports AOT -[NLog 6.0 RC4](https://www.nuget.org/packages/NLog/6.0.0-rc4#releasenotes-body-tab) now available. See also [List of major changes in NLog 6.0](https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html) +[NLog 6.0](https://www.nuget.org/packages/NLog/6.0.0#releasenotes-body-tab) now available. See also [List of major changes in NLog 6.0](https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html) NLog Extensions diff --git a/build.ps1 b/build.ps1 index 6d0faef62a..53ffe9835a 100644 --- a/build.ps1 +++ b/build.ps1 @@ -3,7 +3,7 @@ dotnet --version $versionPrefix = "6.0.0" -$versionSuffix = "rc4" +$versionSuffix = "" $versionFile = $versionPrefix + "." + ${env:APPVEYOR_BUILD_NUMBER} $versionProduct = $versionPrefix; if (-Not $versionSuffix.Equals("")) diff --git a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs index da38e7aba7..165b5ca45e 100644 --- a/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs +++ b/src/NLog.Targets.Network/Layouts/Log4JXmlEventLayout.cs @@ -274,6 +274,7 @@ protected override void InitializeLayout() PropertiesElementKeyAttribute = "name", PropertiesElementValueAttribute = "value", IncludeEventProperties = true, + MaxRecursionLimit = 0, // ToString for everything }); } #pragma warning restore CS0618 // Type or member is obsolete diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index ab51d4f60d..145f6bb4ee 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -28,10 +28,10 @@ For ASP.NET Core, check: https://www.nuget.org/packages/NLog.Web.AspNetCore Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ -NLog v6.0 RC3 with the following major changes: +NLog v6.0 with the following major changes: - Support AOT builds without build warnings. -- New FileTarget without ConcurrentWrites support, but still support KeepFileOpen true / false. +- New FileTarget without ConcurrentWrites support, but still support KeepFileOpen (true/false). - Moved old FileTarget into the new nuget-package NLog.Targets.ConcurrentFile. - Created new nuget-package NLog.Targets.AtomicFile that supports ConcurrentWrites for NET8 on both Windows / Linux. - Created new nuget-package NLog.Targets.GZipFile that uses GZipStream for writing directly to compressed files. @@ -41,30 +41,13 @@ NLog v6.0 RC3 with the following major changes: - New SyslogTarget introduced for the new nuget-package NLog.Targets.Network. - Moved TraceTarget and NLogTraceListener into the new nuget-package NLog.Targets.Trace. - Moved WebServiceTarget into the new nuget-package NLog.Targets.WebService +- Renamed ChainsawTarget to Log4JXmlTarget to match Log4JXmlEventLayout - Removed dependency on System.Text.RegularExpressions and introduced new nuget-package NLog.RegEx. - Removed dependency on System.Xml.XmlReader by implementing own internal basic XML-Parser. -- Updated NLog API with Nullable-support and introduced Layout.Empty -- Marked [RequiredParameter] as obsolete, and replaced with explicit option validation during initialization. -- Marked LogEventInfo.SequenceID and ${sequenceid} as obsolete, and instead use ${counter:sequence=global}. - Added support for params ReadOnlySpan when using C# 13 -- Prioritize generic Logger-methods by marking legacy methods with [OverloadResolutionPriority(-1)] when using C# 13 - Skip LogEventInfo.Parameters-array-allocation when unable to defer message-template formatting. -- Renamed ChainsawTarget to Log4JXmlTarget to match Log4JXmlEventLayout -- Updated NLog.Schema to include intellisense for multiple NLog-assemblies. -- Fixed NLog XmlParser to support XML comments within XML processing instructions. -- NLog.Targets.Network now also supports NET35. -- Updated structs to be readonly to allow compiler optimizations. -- Updated interface ILoggingConfigurationElement to support nullable Values. -- Updated all projects to include <IsAotCompatible> -- Optimized ConsoleTarget to not use Console.WriteLine, and introduced option ForceWriteLine -- Added new LogEventInfo constructor that supports ReadOnlySpan -- Updated NLog.Schema nuget-package to include targets-file to copy NLog.xsd to project-folder. -- Improved configuration-file loading to advise about NLog nuget-packages for missing types. -- Log4JXmlEventLayout - Fixed IncludeEmptyValue for Parameters. -- Log4JXmlEventLayout - Enforce MaxRecursionLimit = 0 -- RegisterObjectTransformation to preserve public properties -- DatabaseTarget with support for AOT (@JohnVerheij) -- DatabaseTarget only assign ConnectionString when specified (@JohnVerheij) +- Updated NLog API with Nullable-support and introduced Layout.Empty +- Marked [RequiredParameter] as obsolete with Nullable-support, and instead validate options at initialization. NLog v6.0 release notes: https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html @@ -137,10 +120,6 @@ For all config options and platform support, check https://nlog-project.org/conf - - - - TextTemplatingFileGenerator diff --git a/src/NLog/SetupLoadConfigurationExtensions.cs b/src/NLog/SetupLoadConfigurationExtensions.cs index a69e5c8d64..0c590b045e 100644 --- a/src/NLog/SetupLoadConfigurationExtensions.cs +++ b/src/NLog/SetupLoadConfigurationExtensions.cs @@ -86,13 +86,13 @@ public static ISetupConfigurationLoggingRuleBuilder ForLogger(this ISetupLoadCon /// Defines for redirecting output from matching to wanted targets. /// /// Fluent interface parameter. - /// Restrict minimum LogLevel for names that matches this rule + /// Restrict minimum LogLevel for names that matches this rule /// Logger name pattern to check which names matches this rule /// Rule identifier to allow rule lookup - public static ISetupConfigurationLoggingRuleBuilder ForLogger(this ISetupLoadConfigurationBuilder configBuilder, LogLevel finalMinLevel, string loggerNamePattern = "*", string? ruleName = null) + public static ISetupConfigurationLoggingRuleBuilder ForLogger(this ISetupLoadConfigurationBuilder configBuilder, LogLevel minLevel, string loggerNamePattern = "*", string? ruleName = null) { var ruleBuilder = new SetupConfigurationLoggingRuleBuilder(configBuilder.LogFactory, configBuilder.Configuration, loggerNamePattern, ruleName); - ruleBuilder.LoggingRule.EnableLoggingForLevels(finalMinLevel ?? LogLevel.MinLevel, LogLevel.MaxLevel); + ruleBuilder.LoggingRule.EnableLoggingForLevels(minLevel ?? LogLevel.MinLevel, LogLevel.MaxLevel); return ruleBuilder; } diff --git a/tests/NLog.UnitTests/Targets/Wrappers/ConcurrentRequestQueueTests.cs b/tests/NLog.UnitTests/Targets/Wrappers/ConcurrentRequestQueueTests.cs index 461ba457f8..ef0630937e 100644 --- a/tests/NLog.UnitTests/Targets/Wrappers/ConcurrentRequestQueueTests.cs +++ b/tests/NLog.UnitTests/Targets/Wrappers/ConcurrentRequestQueueTests.cs @@ -95,7 +95,7 @@ public void Enqueue_WhenGrowBehaviourAndHighlyConcurrent_GrowOnce() Task.Run(EnqueueWhenAllThreadsReady); } - Assert.True(enqueued.Wait(5000)); + Assert.True(enqueued.Wait(10000)); enqueued.Reset(); // Assert @@ -109,8 +109,6 @@ public void Enqueue_WhenGrowBehaviourAndHighlyConcurrent_GrowOnce() requestQueue.QueueLimit = initialQueueLimit; } - return; - void EnqueueWhenAllThreadsReady() { var logEvent = new AsyncLogEventInfo(); From f9d5bf4f7af0de4bdb9c181e4b805637c33d27dc Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 22 Jun 2025 21:40:01 +0200 Subject: [PATCH 178/224] NLog v6 API Docs (#5892) --- src/NLog.Targets.GZipFile/GZipFileTarget.cs | 4 +-- .../NLog.Targets.GZipFile.csproj | 4 +-- .../Targets/Log4JXmlTarget.cs | 4 +-- .../NLog.Targets.Trace.csproj | 10 ++++-- src/NLog/Targets/ColoredConsoleTarget.cs | 2 +- .../NLogNugetPackages.csproj | 26 +++++++------- tools/SandcastleDocs/BuildDoc.cmd | 2 +- tools/SandcastleDocs/NLog.shfbproj | 36 ++++++++++++------- .../dll_to_doc/dll_to_doc.csproj | 16 ++++----- 9 files changed, 59 insertions(+), 45 deletions(-) diff --git a/src/NLog.Targets.GZipFile/GZipFileTarget.cs b/src/NLog.Targets.GZipFile/GZipFileTarget.cs index 519d9ccfa5..309520150d 100644 --- a/src/NLog.Targets.GZipFile/GZipFileTarget.cs +++ b/src/NLog.Targets.GZipFile/GZipFileTarget.cs @@ -40,9 +40,9 @@ namespace NLog.Targets /// Extended standard FileTarget with GZip compression as part of file-logging /// /// - /// See NLog Wiki + /// See NLog Wiki /// - /// Documentation on NLog Wiki + /// Documentation on NLog Wiki [Target("GZipFile")] public class GZipFileTarget : FileTarget { diff --git a/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj b/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj index bcdf545e86..fcca620b86 100644 --- a/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj +++ b/src/NLog.Targets.GZipFile/NLog.Targets.GZipFile.csproj @@ -15,8 +15,8 @@ Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ - FileTarget Docs: - https://github.com/NLog/NLog/wiki/File-target + GZipFile Target Docs: + https://github.com/nlog/nlog/wiki/GZip-File-target NLog;File;Archive;GZip;logging;log N.png diff --git a/src/NLog.Targets.Network/Targets/Log4JXmlTarget.cs b/src/NLog.Targets.Network/Targets/Log4JXmlTarget.cs index a49b3adb31..883d91796c 100644 --- a/src/NLog.Targets.Network/Targets/Log4JXmlTarget.cs +++ b/src/NLog.Targets.Network/Targets/Log4JXmlTarget.cs @@ -43,9 +43,9 @@ namespace NLog.Targets /// Sends log messages to the remote instance of Chainsaw / NLogViewer application using Log4J Xml /// /// - /// See NLog Wiki + /// See NLog Wiki /// - /// Documentation on NLog Wiki + /// Documentation on NLog Wiki /// ///

/// To set up the target in the configuration file, diff --git a/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj b/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj index 26578145ec..fcd52359b8 100644 --- a/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj +++ b/src/NLog.Targets.Trace/NLog.Targets.Trace.csproj @@ -15,8 +15,14 @@ Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ - Trace-Target Docs: - https://github.com/NLog/NLog/wiki/Trace-target +Trace-Target Docs: +https://github.com/NLog/NLog/wiki/Trace-target + +Trace-ActivityId Docs: +https://github.com/NLog/NLog/wiki/Trace-Activity-Id-Layout-Renderer + +NLogTraceListener Docs: +https://github.com/NLog/NLog/wiki/NLog-Trace-Listener-for-System-Diagnostics-Trace README.md NLog;Trace;Diagnostics;logging;log diff --git a/src/NLog/Targets/ColoredConsoleTarget.cs b/src/NLog/Targets/ColoredConsoleTarget.cs index 35adc76197..c9657cfe13 100644 --- a/src/NLog/Targets/ColoredConsoleTarget.cs +++ b/src/NLog/Targets/ColoredConsoleTarget.cs @@ -161,7 +161,7 @@ public ColoredConsoleTarget(string name) : this() /// /// /// level == LogLevel.Trace - /// DarkGray + /// Gray /// NoChange /// /// diff --git a/tools/NLogNugetPackages/NLogNugetPackages.csproj b/tools/NLogNugetPackages/NLogNugetPackages.csproj index 7d5b494127..989f09a290 100644 --- a/tools/NLogNugetPackages/NLogNugetPackages.csproj +++ b/tools/NLogNugetPackages/NLogNugetPackages.csproj @@ -7,28 +7,28 @@ - + - + - + - - - - - - - + + + + + + + - - + + - + \ No newline at end of file diff --git a/tools/SandcastleDocs/BuildDoc.cmd b/tools/SandcastleDocs/BuildDoc.cmd index 059e30996d..86a4f852f8 100644 --- a/tools/SandcastleDocs/BuildDoc.cmd +++ b/tools/SandcastleDocs/BuildDoc.cmd @@ -1,6 +1,6 @@ @echo off -set BuildVersion=5.0 +set BuildVersion=6.0 dotnet msbuild /t:restore,rebuild %~dp0\dll_to_doc /p:Configuration=Release /verbosity:minimal dotnet msbuild /t:restore %~dp0NLog.shfbproj /p:Configuration=Release /verbosity:minimal diff --git a/tools/SandcastleDocs/NLog.shfbproj b/tools/SandcastleDocs/NLog.shfbproj index 37ae00eb4d..1552d1d6e1 100644 --- a/tools/SandcastleDocs/NLog.shfbproj +++ b/tools/SandcastleDocs/NLog.shfbproj @@ -29,12 +29,34 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + @@ -43,20 +65,10 @@ - - - - - - - - - - NLog $(BuildVersion) Copyright %28c%29 2004-2024 NLog diff --git a/tools/SandcastleDocs/dll_to_doc/dll_to_doc.csproj b/tools/SandcastleDocs/dll_to_doc/dll_to_doc.csproj index 33c3f739bf..a3b2c31a2e 100644 --- a/tools/SandcastleDocs/dll_to_doc/dll_to_doc.csproj +++ b/tools/SandcastleDocs/dll_to_doc/dll_to_doc.csproj @@ -1,21 +1,17 @@  - - Exe - net472 + + Exe + net472 false - - - - - - + + true - + From 2d6cfb43c0115776585607e2d97972d5ccb20bc0 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 23 Jun 2025 22:15:06 +0200 Subject: [PATCH 179/224] XmlParser - Fix handling of XML comment just before closing-tag (#5895) --- src/NLog/Internal/XmlParser.cs | 1 + tests/NLog.UnitTests/Internal/XmlParserTests.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/NLog/Internal/XmlParser.cs b/src/NLog/Internal/XmlParser.cs index ee6cd13205..20d1e63997 100644 --- a/src/NLog/Internal/XmlParser.cs +++ b/src/NLog/Internal/XmlParser.cs @@ -250,6 +250,7 @@ public bool TryReadInnerText(out string innerText) while (_xmlSource.Current == '<' && _xmlSource.Peek() == '!') { _xmlSource.MoveNext(); + currentChar = _xmlSource.Current; if (_xmlSource.Peek() == '-') { // diff --git a/tests/NLog.UnitTests/Internal/XmlParserTests.cs b/tests/NLog.UnitTests/Internal/XmlParserTests.cs index e5f7905857..6b70b007e3 100644 --- a/tests/NLog.UnitTests/Internal/XmlParserTests.cs +++ b/tests/NLog.UnitTests/Internal/XmlParserTests.cs @@ -295,6 +295,8 @@ public void XmlParse_InnerText_Tokens(string xmlSource, string value) [Theory] [InlineData("")] [InlineData("\n\n")] + [InlineData("\n\n")] + [InlineData("\n\n\n")] [InlineData("abc${message}")] [InlineData("\n\nabc\n${message}\n\n")] public void XmlParse_Children(string xmlSource) From bec94cb84b87d26b0be96f841700da5159e8eef9 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 24 Jun 2025 20:47:04 +0200 Subject: [PATCH 180/224] ConditionExpression - Implicit string conversion operator with nullable (#5898) --- src/NLog/Conditions/ConditionExpression.cs | 8 ++------ src/NLog/Conditions/ConditionParser.cs | 2 +- src/NLog/Filters/ConditionBasedFilter.cs | 4 ++-- .../Wrappers/WhenLayoutRendererWrapper.cs | 6 +++--- src/NLog/Targets/ConsoleRowHighlightingRule.cs | 8 ++++---- src/NLog/Targets/ConsoleWordHighlightingRule.cs | 4 ++-- src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs | 4 ++-- src/NLog/Targets/Wrappers/FilteringRule.cs | 5 ++--- src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs | 8 ++++---- .../Targets/Wrappers/PostFilteringTargetWrapper.cs | 12 ++++++------ 10 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/NLog/Conditions/ConditionExpression.cs b/src/NLog/Conditions/ConditionExpression.cs index 3b34453615..02ea237a63 100644 --- a/src/NLog/Conditions/ConditionExpression.cs +++ b/src/NLog/Conditions/ConditionExpression.cs @@ -48,18 +48,14 @@ public abstract class ConditionExpression internal static readonly object BoxedTrue = true; internal static readonly object BoxedFalse = false; - ///

- /// Default Condition-value that evalutes to string.Empty - /// - public static ConditionExpression Empty { get; } = new ConditionLiteralExpression(string.Empty); - /// /// Converts condition text to a condition expression tree. /// /// Condition text to be converted. /// Condition expression tree. - public static implicit operator ConditionExpression(string conditionExpressionText) + public static implicit operator ConditionExpression?(string? conditionExpressionText) { + if (conditionExpressionText is null) return null; return ConditionParser.ParseExpression(conditionExpressionText); } diff --git a/src/NLog/Conditions/ConditionParser.cs b/src/NLog/Conditions/ConditionParser.cs index 1c1b4bd6e5..50f23b3682 100644 --- a/src/NLog/Conditions/ConditionParser.cs +++ b/src/NLog/Conditions/ConditionParser.cs @@ -214,7 +214,7 @@ private ConditionExpression ParseLiteralExpression() var simpleLayout = string.IsNullOrEmpty(stringTokenValue) ? SimpleLayout.Default : new SimpleLayout(stringTokenValue, _configurationItemFactory); _tokenizer.GetNextToken(); if (simpleLayout.IsFixedText) - return string.IsNullOrEmpty(simpleLayout.FixedText) ? ConditionLiteralExpression.Empty : new ConditionLiteralExpression(simpleLayout.FixedText); + return new ConditionLiteralExpression(simpleLayout.FixedText); else return new ConditionLayoutExpression(simpleLayout); } diff --git a/src/NLog/Filters/ConditionBasedFilter.cs b/src/NLog/Filters/ConditionBasedFilter.cs index f3c1af6601..a8d1d8d8eb 100644 --- a/src/NLog/Filters/ConditionBasedFilter.cs +++ b/src/NLog/Filters/ConditionBasedFilter.cs @@ -51,14 +51,14 @@ public class ConditionBasedFilter : Filter /// Gets or sets the condition expression. ///
/// - public ConditionExpression Condition { get; set; } = ConditionExpression.Empty; + public ConditionExpression? Condition { get; set; } internal FilterResult FilterDefaultAction { get; set; } = FilterResult.Neutral; /// protected override FilterResult Check(LogEventInfo logEvent) { - var val = Condition.Evaluate(logEvent); + var val = Condition?.Evaluate(logEvent); return ConditionExpression.BoxedTrue.Equals(val) ? Action : FilterDefaultAction; } } diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs index 7d7183d546..3d4cde235a 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs @@ -56,7 +56,7 @@ public sealed class WhenLayoutRendererWrapper : WrapperLayoutRendererBase, IRawV /// Gets or sets the condition that must be met for the layout to be printed. ///
/// - public ConditionExpression When { get; set; } = ConditionExpression.Empty; + public ConditionExpression? When { get; set; } /// /// If is not met, print this layout. @@ -67,7 +67,7 @@ public sealed class WhenLayoutRendererWrapper : WrapperLayoutRendererBase, IRawV /// protected override void InitializeLayoutRenderer() { - if (When is null || ReferenceEquals(When, ConditionExpression.Empty)) + if (When is null) throw new NLogConfigurationException("When-LayoutRenderer When-property must be assigned."); base.InitializeLayoutRenderer(); @@ -103,7 +103,7 @@ protected override string Transform(string text) private bool ShouldRenderInner(LogEventInfo logEvent) { - return When is null || ReferenceEquals(When, ConditionExpression.Empty) || true.Equals(When.Evaluate(logEvent)); + return When is null || true.Equals(When.Evaluate(logEvent)); } bool IRawValue.TryGetRawValue(LogEventInfo logEvent, out object? value) diff --git a/src/NLog/Targets/ConsoleRowHighlightingRule.cs b/src/NLog/Targets/ConsoleRowHighlightingRule.cs index 3567383a92..85ebb79bb5 100644 --- a/src/NLog/Targets/ConsoleRowHighlightingRule.cs +++ b/src/NLog/Targets/ConsoleRowHighlightingRule.cs @@ -55,7 +55,7 @@ public ConsoleRowHighlightingRule() /// The condition. /// Color of the foreground. /// Color of the background. - public ConsoleRowHighlightingRule(ConditionExpression condition, ConsoleOutputColor foregroundColor, ConsoleOutputColor backgroundColor) + public ConsoleRowHighlightingRule(ConditionExpression? condition, ConsoleOutputColor foregroundColor, ConsoleOutputColor backgroundColor) { Condition = condition; ForegroundColor = foregroundColor; @@ -65,13 +65,13 @@ public ConsoleRowHighlightingRule(ConditionExpression condition, ConsoleOutputCo /// /// Gets the default highlighting rule. Doesn't change the color. /// - public static ConsoleRowHighlightingRule Default { get; } = new ConsoleRowHighlightingRule(ConditionExpression.Empty, ConsoleOutputColor.NoChange, ConsoleOutputColor.NoChange); + public static ConsoleRowHighlightingRule Default { get; } = new ConsoleRowHighlightingRule(null, ConsoleOutputColor.NoChange, ConsoleOutputColor.NoChange); /// /// Gets or sets the condition that must be met in order to set the specified foreground and background color. /// /// - public ConditionExpression Condition { get; set; } = ConditionExpression.Empty; + public ConditionExpression? Condition { get; set; } /// /// Gets or sets the foreground color. @@ -90,7 +90,7 @@ public ConsoleRowHighlightingRule(ConditionExpression condition, ConsoleOutputCo /// public bool CheckCondition(LogEventInfo logEvent) { - return Condition is null || ReferenceEquals(Condition, ConditionExpression.Empty) || true.Equals(Condition.Evaluate(logEvent)); + return Condition is null || true.Equals(Condition.Evaluate(logEvent)); } } } diff --git a/src/NLog/Targets/ConsoleWordHighlightingRule.cs b/src/NLog/Targets/ConsoleWordHighlightingRule.cs index e96f976cc1..b0322817e8 100644 --- a/src/NLog/Targets/ConsoleWordHighlightingRule.cs +++ b/src/NLog/Targets/ConsoleWordHighlightingRule.cs @@ -68,7 +68,7 @@ public ConsoleWordHighlightingRule(string text, ConsoleOutputColor foregroundCol /// Gets or sets the condition that must be met before scanning the row for highlight of words /// /// - public ConditionExpression Condition { get; set; } = ConditionExpression.Empty; + public ConditionExpression? Condition { get; set; } /// /// Gets or sets the text to be matched. You must specify either text or regex. @@ -155,7 +155,7 @@ private int FindNextWordForHighlighting(string haystack, int? prevIndex) /// internal bool CheckCondition(LogEventInfo logEvent) { - return Condition is null || ReferenceEquals(Condition, ConditionExpression.Empty) || true.Equals(Condition.Evaluate(logEvent)); + return Condition is null || true.Equals(Condition.Evaluate(logEvent)); } } } diff --git a/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs b/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs index b4652ca15a..3e7f1055ee 100644 --- a/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs @@ -64,7 +64,7 @@ public class AutoFlushTargetWrapper : WrapperTargetBase /// a flush on the wrapped target. ///
/// - public ConditionExpression Condition { get; set; } = ConditionExpression.Empty; + public ConditionExpression? Condition { get; set; } /// /// Delay the flush until the LogEvent has been confirmed as written @@ -149,7 +149,7 @@ private bool TargetSupportsAsyncFlush() /// Logging event to be written out. protected override void Write(AsyncLogEventInfo logEvent) { - if (Condition is null || ReferenceEquals(Condition, ConditionExpression.Empty) || ConditionExpression.BoxedTrue.Equals(Condition.Evaluate(logEvent.LogEvent))) + if (Condition is null || ConditionExpression.BoxedTrue.Equals(Condition.Evaluate(logEvent.LogEvent))) { if (AsyncFlush) { diff --git a/src/NLog/Targets/Wrappers/FilteringRule.cs b/src/NLog/Targets/Wrappers/FilteringRule.cs index 083fd199fe..ac4e7b82a4 100644 --- a/src/NLog/Targets/Wrappers/FilteringRule.cs +++ b/src/NLog/Targets/Wrappers/FilteringRule.cs @@ -46,7 +46,6 @@ public class FilteringRule /// Initializes a new instance of the FilteringRule class. /// public FilteringRule() - : this(ConditionExpression.Empty, ConditionExpression.Empty) { } @@ -65,12 +64,12 @@ public FilteringRule(ConditionExpression whenExistsExpression, ConditionExpressi /// Gets or sets the condition to be tested. ///
/// - public ConditionExpression Exists { get; set; } + public ConditionExpression? Exists { get; set; } /// /// Gets or sets the resulting filter to be applied when the condition matches. /// /// - public ConditionExpression Filter { get; set; } + public ConditionExpression? Filter { get; set; } } } diff --git a/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs b/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs index 56db1bfa40..e520e6063a 100644 --- a/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs @@ -98,7 +98,7 @@ public FilteringTargetWrapper(Target wrappedTarget, ConditionExpression conditio /// to the wrapped target. ///
/// - public ConditionExpression? Condition { get => (Filter as ConditionBasedFilter)?.Condition; set => Filter = CreateFilter(value ?? ConditionExpression.Empty); } + public ConditionExpression? Condition { get => (Filter as ConditionBasedFilter)?.Condition; set => Filter = CreateFilter(value); } /// /// Gets or sets the filter. Log events who evaluates to will be discarded @@ -109,7 +109,7 @@ public FilteringTargetWrapper(Target wrappedTarget, ConditionExpression conditio /// protected override void InitializeTarget() { - if (Filter is null || ReferenceEquals(Condition, ConditionExpression.Empty)) + if (Filter is null || ReferenceEquals(Filter, ConditionBasedFilter.Empty)) throw new NLogConfigurationException($"FilteringTargetWrapper Filter-property must be assigned. Filter LogEvents using blank Filter not supported."); base.InitializeTarget(); @@ -153,9 +153,9 @@ private static bool ShouldLogEvent(AsyncLogEventInfo logEvent, Filter filter) } } - private static ConditionBasedFilter CreateFilter(ConditionExpression value) + private static ConditionBasedFilter CreateFilter(ConditionExpression? value) { - if (value is null || ReferenceEquals(value, ConditionExpression.Empty)) + if (value is null) return ConditionBasedFilter.Empty; return new ConditionBasedFilter { Condition = value, FilterDefaultAction = FilterResult.Ignore }; diff --git a/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs b/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs index 2870389a14..f65d2e1287 100644 --- a/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs @@ -95,7 +95,7 @@ public PostFilteringTargetWrapper(string name, Target wrappedTarget) /// Gets or sets the default filter to be applied when no specific rule matches. /// /// - public ConditionExpression DefaultFilter { get; set; } = ConditionExpression.Empty; + public ConditionExpression? DefaultFilter { get; set; } /// /// Gets the collection of filtering rules. The rules are processed top-down @@ -113,10 +113,10 @@ protected override void InitializeTarget() foreach (var rule in Rules) { - if (rule.Exists is null || ReferenceEquals(rule.Exists, ConditionExpression.Empty)) + if (rule.Exists is null) throw new NLogConfigurationException("PostFilteringTargetWrapper When-Rules with unassigned Exists-property."); - if (rule.Filter is null || ReferenceEquals(rule.Filter, ConditionExpression.Empty)) + if (rule.Filter is null) throw new NLogConfigurationException("PostFilteringTargetWrapper When-Rules with unassigned Filter-property."); } } @@ -139,7 +139,7 @@ protected override void Write(IList logEvents) InternalLogger.Trace("{0}: Running on {1} events", this, logEvents.Count); var resultFilter = EvaluateAllRules(logEvents); - if (resultFilter is null || ReferenceEquals(resultFilter, ConditionExpression.Empty)) + if (resultFilter is null) { WrappedTarget?.WriteAsyncLogEvents(logEvents); } @@ -175,7 +175,7 @@ private static bool ShouldLogEvent(AsyncLogEventInfo logEvent, ConditionExpressi /// /// /// - private ConditionExpression EvaluateAllRules(IList logEvents) + private ConditionExpression? EvaluateAllRules(IList logEvents) { if (Rules.Count == 0) return DefaultFilter; @@ -185,7 +185,7 @@ private ConditionExpression EvaluateAllRules(IList logEvents) for (int j = 0; j < Rules.Count; ++j) { var rule = Rules[j]; - var v = rule.Exists.Evaluate(logEvents[i].LogEvent); + var v = rule.Exists?.Evaluate(logEvents[i].LogEvent); if (ConditionExpression.BoxedTrue.Equals(v)) { InternalLogger.Trace("{0}: Rule matched: {1}", this, rule.Exists); From 23f7421d4072a4f4765045c3a38aa8f8175fe261 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 24 Jun 2025 21:35:27 +0200 Subject: [PATCH 181/224] TimeSource - Updated XML docs with Wiki-links (#5901) --- src/NLog/Time/AccurateLocalTimeSource.cs | 4 ++++ src/NLog/Time/AccurateUtcTimeSource.cs | 4 ++++ src/NLog/Time/CachedTimeSource.cs | 4 ++++ src/NLog/Time/FastLocalTimeSource.cs | 4 ++++ src/NLog/Time/FastUtcTimeSource.cs | 4 ++++ src/NLog/Time/TimeSource.cs | 4 ++++ 6 files changed, 24 insertions(+) diff --git a/src/NLog/Time/AccurateLocalTimeSource.cs b/src/NLog/Time/AccurateLocalTimeSource.cs index 20798109e4..9a74fb8659 100644 --- a/src/NLog/Time/AccurateLocalTimeSource.cs +++ b/src/NLog/Time/AccurateLocalTimeSource.cs @@ -38,6 +38,10 @@ namespace NLog.Time /// /// Current local time retrieved directly from DateTime.Now. /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [TimeSource("AccurateLocal")] public sealed class AccurateLocalTimeSource : TimeSource { diff --git a/src/NLog/Time/AccurateUtcTimeSource.cs b/src/NLog/Time/AccurateUtcTimeSource.cs index 9f45c12cc5..161cbfe628 100644 --- a/src/NLog/Time/AccurateUtcTimeSource.cs +++ b/src/NLog/Time/AccurateUtcTimeSource.cs @@ -38,6 +38,10 @@ namespace NLog.Time /// /// Current UTC time retrieved directly from DateTime.UtcNow. /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [TimeSource("AccurateUTC")] public sealed class AccurateUtcTimeSource : TimeSource { diff --git a/src/NLog/Time/CachedTimeSource.cs b/src/NLog/Time/CachedTimeSource.cs index 04156d176e..6b2e35423e 100644 --- a/src/NLog/Time/CachedTimeSource.cs +++ b/src/NLog/Time/CachedTimeSource.cs @@ -38,6 +38,10 @@ namespace NLog.Time /// /// Fast time source that updates current time only once per tick (15.6 milliseconds). /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki public abstract class CachedTimeSource : TimeSource { private int _lastTicks = -1; diff --git a/src/NLog/Time/FastLocalTimeSource.cs b/src/NLog/Time/FastLocalTimeSource.cs index 07bfc29f1f..0e44cf4be4 100644 --- a/src/NLog/Time/FastLocalTimeSource.cs +++ b/src/NLog/Time/FastLocalTimeSource.cs @@ -38,6 +38,10 @@ namespace NLog.Time /// /// Fast local time source that is updated once per tick (15.6 milliseconds). /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [TimeSource("FastLocal")] public sealed class FastLocalTimeSource : CachedTimeSource { diff --git a/src/NLog/Time/FastUtcTimeSource.cs b/src/NLog/Time/FastUtcTimeSource.cs index d3bd786d0e..deabaae891 100644 --- a/src/NLog/Time/FastUtcTimeSource.cs +++ b/src/NLog/Time/FastUtcTimeSource.cs @@ -38,6 +38,10 @@ namespace NLog.Time /// /// Fast UTC time source that is updated once per tick (15.6 milliseconds). /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [TimeSource("FastUTC")] public sealed class FastUtcTimeSource : CachedTimeSource { diff --git a/src/NLog/Time/TimeSource.cs b/src/NLog/Time/TimeSource.cs index c2246ac10d..87ff248585 100644 --- a/src/NLog/Time/TimeSource.cs +++ b/src/NLog/Time/TimeSource.cs @@ -40,6 +40,10 @@ namespace NLog.Time /// /// Defines source of current time. /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki [NLogConfigurationItem] public abstract class TimeSource { From 87887c4ebb026e79eef7407d2ba5781f7e514f83 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 26 Jun 2025 08:37:49 +0200 Subject: [PATCH 182/224] ConditionBasedFilter - Changed Condition to non-nullable (#5902) --- src/NLog/Filters/ConditionBasedFilter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NLog/Filters/ConditionBasedFilter.cs b/src/NLog/Filters/ConditionBasedFilter.cs index a8d1d8d8eb..1a04215001 100644 --- a/src/NLog/Filters/ConditionBasedFilter.cs +++ b/src/NLog/Filters/ConditionBasedFilter.cs @@ -51,7 +51,7 @@ public class ConditionBasedFilter : Filter /// Gets or sets the condition expression. ///
/// - public ConditionExpression? Condition { get; set; } + public ConditionExpression Condition { get; set; } = ConditionLiteralExpression.Null; internal FilterResult FilterDefaultAction { get; set; } = FilterResult.Neutral; From 12ffd2e59748be39ebc7080d64037c9a42662eee Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 27 Jun 2025 18:23:39 +0200 Subject: [PATCH 183/224] AtomicFileTarget - Added net8.0-windows without mono-dependency (#5891) --- build.ps1 | 2 +- .../AtomicFileTarget.cs | 33 ++++++++++--------- .../NLog.Targets.AtomicFile.csproj | 10 ++++-- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/build.ps1 b/build.ps1 index 53ffe9835a..cd6b694861 100644 --- a/build.ps1 +++ b/build.ps1 @@ -44,8 +44,8 @@ create-package 'NLog.Targets.GZipFile' '"net45;net46;netstandard2.0;netstandard2 create-package 'NLog.OutputDebugString' '"net35;net45;net46;netstandard2.0;netstandard2.1"' create-package 'NLog.RegEx' '"net35;net45;net46;netstandard2.0;netstandard2.1"' create-package 'NLog.WindowsRegistry' '"net35;net45;net46;netstandard2.0;netstandard2.1"' -create-package 'NLog.Targets.ConcurrentFile' '"net35;net45;net46;netstandard2.0"' msbuild /t:Restore,Pack ./src/NLog.Targets.AtomicFile/ /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount +create-package 'NLog.Targets.ConcurrentFile' '"net35;net45;net46;netstandard2.0"' create-package 'NLog.WindowsEventLog' '"netstandard2.0;netstandard2.1"' msbuild /t:xsd /t:NuGetSchemaPackage ./src/NLog.proj /p:Configuration=Release /p:BuildNetFX45=true /p:BuildVersion=$versionProduct /p:Configuration=Release /p:BuildLabelOverride=NONE /verbosity:minimal diff --git a/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs b/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs index 7da85e1c9e..fcbb349691 100644 --- a/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs +++ b/src/NLog.Targets.AtomicFile/AtomicFileTarget.cs @@ -41,9 +41,9 @@ namespace NLog.Targets /// Extended standard FileTarget with atomic file append for multi-process logging to the same file ///
/// - /// See NLog Wiki + /// See NLog Wiki /// - /// Documentation on NLog Wiki + /// Documentation on NLog Wiki [Target("AtomFile")] [Target("AtomicFile")] public class AtomicFileTarget : FileTarget @@ -107,26 +107,27 @@ private Stream CreateAtomicFileStream(string filePath) bufferSize: 1, // No internal buffer, write directly from user-buffer FileOptions.None); #else - if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) - { - var systemRights = System.Security.AccessControl.FileSystemRights.AppendData | System.Security.AccessControl.FileSystemRights.Synchronize; - return FileSystemAclExtensions.Create( - new FileInfo(filePath), - FileMode.Append, - systemRights, - fileShare, - bufferSize: 1, // No internal buffer, write directly from user-buffer - FileOptions.None, - fileSecurity: null); - } - else + +#if !WINDOWS + if (!System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) { return CreateUnixStream(filePath); } #endif + + var systemRights = System.Security.AccessControl.FileSystemRights.AppendData | System.Security.AccessControl.FileSystemRights.Synchronize; + return FileSystemAclExtensions.Create( + new FileInfo(filePath), + FileMode.Append, + systemRights, + fileShare, + bufferSize: 1, // No internal buffer, write directly from user-buffer + FileOptions.None, + fileSecurity: null); +#endif } -#if !NETFRAMEWORK +#if !NETFRAMEWORK && !WINDOWS private Stream CreateUnixStream(string filePath) { // Use 0666 (read/write for all) diff --git a/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj index 4e3c0c91d1..3436039836 100644 --- a/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj +++ b/src/NLog.Targets.AtomicFile/NLog.Targets.AtomicFile.csproj @@ -1,7 +1,7 @@  - net35;net46;net8.0 + net35;net46;net8.0;net8.0-windows NLog.Targets.AtomicFile NLog @@ -13,8 +13,8 @@ Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ - FileTarget Docs: - https://github.com/NLog/NLog/wiki/File-target + AtomFile Target Docs: + https://github.com/NLog/NLog/wiki/Atomic-File-target NLog;File;Archive;NTFS;logging;log N.png @@ -58,6 +58,10 @@ NLog.Targets.AtomicFile for NET8 + + NLog.Targets.AtomicFile for NET8 Windows + + From e843833a7f6e411cc86e21660fa4dca6faa930f0 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 27 Jun 2025 20:31:27 +0200 Subject: [PATCH 184/224] XmlParser - Allow InnerText with greater-than character (#5905) --- src/NLog/Internal/XmlParser.cs | 5 +---- tests/NLog.UnitTests/Internal/XmlParserTests.cs | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/NLog/Internal/XmlParser.cs b/src/NLog/Internal/XmlParser.cs index 20d1e63997..7a1b9985b6 100644 --- a/src/NLog/Internal/XmlParser.cs +++ b/src/NLog/Internal/XmlParser.cs @@ -491,10 +491,7 @@ private string ReadUntilChar(char expectedChar, bool includeSpaces = false) } if (chr == '<' && (!includeSpaces || expectedChar == '<')) - throw new XmlParserException($"Invalid XML document. Cannot parse value with '<'"); - - if (chr == '>' && (!includeSpaces || expectedChar == '<')) - throw new XmlParserException($"Invalid XML document. Cannot parse value with '>'"); + throw new XmlParserException($"Invalid XML document. Cannot parse value with '<', maybe encode to <"); if (includeSpaces && chr == '&') { diff --git a/tests/NLog.UnitTests/Internal/XmlParserTests.cs b/tests/NLog.UnitTests/Internal/XmlParserTests.cs index 6b70b007e3..54028c8fea 100644 --- a/tests/NLog.UnitTests/Internal/XmlParserTests.cs +++ b/tests/NLog.UnitTests/Internal/XmlParserTests.cs @@ -297,8 +297,10 @@ public void XmlParse_InnerText_Tokens(string xmlSource, string value) [InlineData("\n\n")] [InlineData("\n\n")] [InlineData("\n\n\n")] + [InlineData("\n= LogLevel.Warn'/>\n")] [InlineData("abc${message}")] [InlineData("\n\nabc\n${message}\n\n")] + [InlineData("\n\nabc\nlevel >= LogLevel.Warn\n\n")] public void XmlParse_Children(string xmlSource) { var xmlDocument = new XmlParser(xmlSource).LoadDocument(out var _); From 01ace0ef7c6aee9097d0b7b9d5bd7ed2210d9127 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 27 Jun 2025 22:22:20 +0200 Subject: [PATCH 185/224] ConditionExpression without reflection (#5906) --- src/NLog/Config/AssemblyExtensionTypes.cs | 12 ++++++++++++ src/NLog/Config/AssemblyExtensionTypes.tt | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/NLog/Config/AssemblyExtensionTypes.cs b/src/NLog/Config/AssemblyExtensionTypes.cs index d12dd21cbd..310bc955c0 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.cs +++ b/src/NLog/Config/AssemblyExtensionTypes.cs @@ -374,6 +374,18 @@ public static void RegisterTimeSourceTypes(ConfigurationItemFactory factory, boo public static void RegisterConditionTypes(ConfigurationItemFactory factory, bool skipCheckExists) { + factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); + factory.RegisterTypeProperties(() => null); + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("length")) factory.GetConditionMethodFactory().RegisterOneParameter("length", (logEvent, arg1) => NLog.Conditions.ConditionMethods.Length(arg1?.ToString())); if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("equals")) diff --git a/src/NLog/Config/AssemblyExtensionTypes.tt b/src/NLog/Config/AssemblyExtensionTypes.tt index c192f6ddc9..92f9797c36 100644 --- a/src/NLog/Config/AssemblyExtensionTypes.tt +++ b/src/NLog/Config/AssemblyExtensionTypes.tt @@ -235,6 +235,18 @@ namespace NLog.Config public static void RegisterConditionTypes(ConfigurationItemFactory factory, bool skipCheckExists) { +<# + foreach(var type in AllTypes) + { + if (typeof(NLog.Conditions.ConditionExpression).IsAssignableFrom(type)) + { +#> + factory.RegisterTypeProperties<<#= type #>>(() => null); +<# + } + } +#> + if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("length")) factory.GetConditionMethodFactory().RegisterOneParameter("length", (logEvent, arg1) => NLog.Conditions.ConditionMethods.Length(arg1?.ToString())); if (skipCheckExists || !factory.GetConditionMethodFactory().CheckTypeAliasExists("equals")) From 1e2afaa62c0b37a7fd81fa28e5a9ec005c66fb61 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 28 Jun 2025 00:00:42 +0200 Subject: [PATCH 186/224] Version 6.0.1 (#5904) --- CHANGELOG.md | 10 ++++++++++ build.ps1 | 2 +- src/NLog/NLog.csproj | 27 +++++++-------------------- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad06630839..dcec9024a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ Date format: (year/month/day) ## Change Log +### Version 6.0.1 (2025/06/27) + +**Improvements** + +- Changed ConditionExpression to be nullable by default since no Condition means no filtering. (#5898) (@snakefoot) +- Include ConditionExpression in the static type registration. (#5906) (@snakefoot) +- Fixed the new XML parser to handle XML comments just before end-tag. (#5895) (@snakefoot) +- Fixed the new XML parser to allow InnerText with greater-than characters. (#5905) (@snakefoot) +- Updated NLog.Targets.AtomicFile to support net8.0-windows without dependency on Mono.Posix.NETStandard. (#5891) (@snakefoot) + ### Version 6.0 (2025/06/21) **Major Changes** diff --git a/build.ps1 b/build.ps1 index cd6b694861..6f9f3885cd 100644 --- a/build.ps1 +++ b/build.ps1 @@ -2,7 +2,7 @@ # creates NuGet package at \artifacts dotnet --version -$versionPrefix = "6.0.0" +$versionPrefix = "6.0.1" $versionSuffix = "" $versionFile = $versionPrefix + "." + ${env:APPVEYOR_BUILD_NUMBER} $versionProduct = $versionPrefix; diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index 145f6bb4ee..002a70d18e 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -28,26 +28,13 @@ For ASP.NET Core, check: https://www.nuget.org/packages/NLog.Web.AspNetCore Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ -NLog v6.0 with the following major changes: - -- Support AOT builds without build warnings. -- New FileTarget without ConcurrentWrites support, but still support KeepFileOpen (true/false). -- Moved old FileTarget into the new nuget-package NLog.Targets.ConcurrentFile. -- Created new nuget-package NLog.Targets.AtomicFile that supports ConcurrentWrites for NET8 on both Windows / Linux. -- Created new nuget-package NLog.Targets.GZipFile that uses GZipStream for writing directly to compressed files. -- Moved MailTarget into the new nuget-package NLog.Targets.Mail. -- Moved NetworkTarget into the new nuget-package NLog.Targets.Network. -- New GelfTarget introduced for the new nuget-package NLog.Targets.Network. -- New SyslogTarget introduced for the new nuget-package NLog.Targets.Network. -- Moved TraceTarget and NLogTraceListener into the new nuget-package NLog.Targets.Trace. -- Moved WebServiceTarget into the new nuget-package NLog.Targets.WebService -- Renamed ChainsawTarget to Log4JXmlTarget to match Log4JXmlEventLayout -- Removed dependency on System.Text.RegularExpressions and introduced new nuget-package NLog.RegEx. -- Removed dependency on System.Xml.XmlReader by implementing own internal basic XML-Parser. -- Added support for params ReadOnlySpan when using C# 13 -- Skip LogEventInfo.Parameters-array-allocation when unable to defer message-template formatting. -- Updated NLog API with Nullable-support and introduced Layout.Empty -- Marked [RequiredParameter] as obsolete with Nullable-support, and instead validate options at initialization. +Changelog: + +- Changed ConditionExpression to be nullable by default since no Condition means no filtering. (#5898) (@snakefoot) +- Include ConditionExpression in the static type registration. (#5906) (@snakefoot) +- Fixed the new XML parser to handle XML comments just before end-tag. (#5895) (@snakefoot) +- Fixed the new XML parser to allow InnerText with greater-than characters. (#5905) (@snakefoot) +- Updated NLog.Targets.AtomicFile to support net8.0-windows without dependency on Mono.Posix.NETStandard. (#5891) (@snakefoot) NLog v6.0 release notes: https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html From e6bd4b596248cd0bdacf481347ad4f8c7a1cea57 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 28 Jun 2025 00:10:32 +0200 Subject: [PATCH 187/224] Update CHANGELOG.md --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcec9024a5..83a282aad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,11 @@ Date format: (year/month/day) **Improvements** -- Changed ConditionExpression to be nullable by default since no Condition means no filtering. (#5898) (@snakefoot) -- Include ConditionExpression in the static type registration. (#5906) (@snakefoot) -- Fixed the new XML parser to handle XML comments just before end-tag. (#5895) (@snakefoot) -- Fixed the new XML parser to allow InnerText with greater-than characters. (#5905) (@snakefoot) -- Updated NLog.Targets.AtomicFile to support net8.0-windows without dependency on Mono.Posix.NETStandard. (#5891) (@snakefoot) +- [#5898](https://github.com/NLog/NLog/pull/5898) Changed ConditionExpression to be nullable by default since no Condition means no filtering. (@snakefoot) +- [#5906](https://github.com/NLog/NLog/pull/5906) Include ConditionExpression in the static type registration. (@snakefoot) +- [#5895](https://github.com/NLog/NLog/pull/5895) Fixed the new XML parser to handle XML comments just before end-tag. (@snakefoot) +- [#5905](https://github.com/NLog/NLog/pull/5905) Fixed the new XML parser to allow InnerText with greater-than characters. (@snakefoot) +- [#5891](https://github.com/NLog/NLog/pull/5891) Updated NLog.Targets.AtomicFile to support net8.0-windows without dependency on Mono.Posix.NETStandard. (@snakefoot) ### Version 6.0 (2025/06/21) From 45ae3f006d8a374de96397578a2c85ba31b9aad7 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 28 Jun 2025 00:17:13 +0200 Subject: [PATCH 188/224] Update build.ps1 --- build.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.ps1 b/build.ps1 index 6f9f3885cd..8e6c38ebe1 100644 --- a/build.ps1 +++ b/build.ps1 @@ -44,8 +44,8 @@ create-package 'NLog.Targets.GZipFile' '"net45;net46;netstandard2.0;netstandard2 create-package 'NLog.OutputDebugString' '"net35;net45;net46;netstandard2.0;netstandard2.1"' create-package 'NLog.RegEx' '"net35;net45;net46;netstandard2.0;netstandard2.1"' create-package 'NLog.WindowsRegistry' '"net35;net45;net46;netstandard2.0;netstandard2.1"' -msbuild /t:Restore,Pack ./src/NLog.Targets.AtomicFile/ /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount create-package 'NLog.Targets.ConcurrentFile' '"net35;net45;net46;netstandard2.0"' +msbuild /t:Restore,Pack ./src/NLog.Targets.AtomicFile/ /p:VersionPrefix=$versionPrefix /p:VersionSuffix=$versionSuffix /p:FileVersion=$versionFile /p:ProductVersion=$versionProduct /p:Configuration=Release /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg /p:ContinuousIntegrationBuild=true /p:EmbedUntrackedSources=true /p:PackageOutputPath=..\..\artifacts /verbosity:minimal /maxcpucount create-package 'NLog.WindowsEventLog' '"netstandard2.0;netstandard2.1"' msbuild /t:xsd /t:NuGetSchemaPackage ./src/NLog.proj /p:Configuration=Release /p:BuildNetFX45=true /p:BuildVersion=$versionProduct /p:Configuration=Release /p:BuildLabelOverride=NONE /verbosity:minimal From ef2c5f85369ac1d673cab2eb930e828292d158e2 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 29 Jun 2025 09:02:59 +0200 Subject: [PATCH 189/224] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2f579c1b86..97dac440aa 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ NLog is a free logging platform for .NET with rich log routing and management capabilities. It makes it easy to produce and manage high-quality logs for your application regardless of its size or complexity. -It can process diagnostic messages emitted from any .NET language, augment -them with contextual information, format them according to your preference -and send them to one or more targets such as file or database. +Handles both structured logging and traditional logging from any .NET language, +augment with contextual information, format according to your preference +and send them to one or more targets such as file or console. Major and minor releases will be posted on [project news](https://nlog-project.org/archives/). From ce5ce0d236e93b9bf91d12e6314a5fe4688e5f18 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 29 Jun 2025 21:57:19 +0200 Subject: [PATCH 190/224] Update SECURITY.md --- SECURITY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 0936586416..6bad768ba3 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,8 +6,8 @@ Currently being supported with security updates. | Version | Supported | | ------- | ------------------ | -| 5.x | :white_check_mark: | -| 4.7.x | :white_check_mark: | +| 6.x | :white_check_mark: | +| 5.x | :white_check_mark: | | < 4.7 | :x: | ## Reporting a Vulnerability From 7e8589d938878bd35fe50cc9f93e6e30ba511dcd Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 30 Jun 2025 18:53:29 +0200 Subject: [PATCH 191/224] ConfigurationItemFactory - Added extension hints for webservice and activityid (#5909) --- src/NLog/Config/Factory.cs | 8 ++++++++ tools/NLogNugetPackages/NLogNugetPackages.csproj | 8 ++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/NLog/Config/Factory.cs b/src/NLog/Config/Factory.cs index 70b4a2f750..3a43288add 100644 --- a/src/NLog/Config/Factory.cs +++ b/src/NLog/Config/Factory.cs @@ -282,6 +282,10 @@ public static TBaseType CreateInstance(this IFactory facto { message += " - Extension NLog.Targets.Network not included?"; } + else if (normalName?.StartsWith("webservice", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.Targets.WebService not included?"; + } else if (normalName?.StartsWith("atomFile", StringComparison.OrdinalIgnoreCase) == true || normalName?.StartsWith("atomicFile", StringComparison.OrdinalIgnoreCase) == true) { message += " - Extension NLog.Targets.AtomicFile not included?"; @@ -294,6 +298,10 @@ public static TBaseType CreateInstance(this IFactory facto { message += " - Extension NLog.Targets.Trace not included?"; } + else if (normalName?.StartsWith("activityid", StringComparison.OrdinalIgnoreCase) == true) + { + message += " - Extension NLog.Targets.Trace not included?"; + } else if (normalName?.StartsWith("mailkit", StringComparison.OrdinalIgnoreCase) == true) { message += " - Extension NLog.MailKit not included?"; diff --git a/tools/NLogNugetPackages/NLogNugetPackages.csproj b/tools/NLogNugetPackages/NLogNugetPackages.csproj index 989f09a290..5e8ee72428 100644 --- a/tools/NLogNugetPackages/NLogNugetPackages.csproj +++ b/tools/NLogNugetPackages/NLogNugetPackages.csproj @@ -8,8 +8,8 @@ - - + + @@ -23,10 +23,10 @@ - + - + From e59cd0a50007ad640ed994f614ad2f19417e2e87 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 3 Jul 2025 00:09:26 +0200 Subject: [PATCH 192/224] NLog.Targets.Trace - Updated nuget-package README.md (#5912) --- src/NLog.Targets.Network/README.md | 14 ++++---------- src/NLog.Targets.Trace/README.md | 18 +++++++++++++++--- .../Wrappers/FallbackGroupTargetTests.cs | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/NLog.Targets.Network/README.md b/src/NLog.Targets.Network/README.md index 81e9bd590a..8ee6022472 100644 --- a/src/NLog.Targets.Network/README.md +++ b/src/NLog.Targets.Network/README.md @@ -4,31 +4,25 @@ NLog Network Target for sending mesages using TCP / UDP sockets with support for If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) -See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Network-target) for available options and examples. +See the [NLog Wiki - Network Target](https://github.com/NLog/NLog/wiki/Network-target) for available options and examples. ## NLog SysLog Target NLog Syslog Target combines the NLog NetworkTarget with NLog SyslogLayout -If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) - -See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Syslog-target) for available options and examples. +See the [NLog Wiki - Syslog Target](https://github.com/NLog/NLog/wiki/Syslog-target) for available options and examples. ## NLog GELF Target NLog Gelf Target combines the NLog NetworkTarget with NLog GelfLayout for Graylog Extended Logging Format (GELF) -If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) - -See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Gelf-target) for available options and examples. +See the [NLog Wiki - Gelf Target](https://github.com/NLog/NLog/wiki/Gelf-target) for available options and examples. ## NLog Log4JXml Target NLog Log4JXml Target combines the NLog NetworkTarget with NLog Log4JXmlEventLayout for NLogViewer / Chainsaw. -If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) - -See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Chainsaw-target) for available options and examples. +See the [NLog Wiki - Chainsaw Target](https://github.com/NLog/NLog/wiki/Chainsaw-target) for available options and examples. ## Register Extension diff --git a/src/NLog.Targets.Trace/README.md b/src/NLog.Targets.Trace/README.md index cb32e66f2c..533e3eb835 100644 --- a/src/NLog.Targets.Trace/README.md +++ b/src/NLog.Targets.Trace/README.md @@ -1,10 +1,22 @@ -# NLog WebService Target +# NLog Trace Target -NLog Trace Target for forwarding to System.Diagnostic.Trace for each logevent, with support for also subscribing using TraceListener. +NLog Trace Target for writing to System.Diagnostics.Trace for each logevent. If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) -See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Trace-target) for available options and examples. +See the [NLog Wiki - Trace Target](https://github.com/NLog/NLog/wiki/Trace-target) for available options and examples. + +# NLog TraceListener + +NLog TraceListener for redirecting System.Diagnostics.Trace into NLog Logger output. + +See the [NLog Wiki - NLogTraceListener](https://github.com/NLog/NLog/wiki/NLog-Trace-Listener-for-System-Diagnostics-Trace) for available options and examples. + +# NLog ActivityId LayoutRenderer + +NLog LayoutRenderer `${activityid}` renders Guid from System.Diagnostics.Trace.CorrelationManager.ActivityId + +See the [NLog Wiki - ActivityId](https://github.com/NLog/NLog/wiki/Trace-Activity-Id-Layout-Renderer) for available options and examples. ## Register Extension diff --git a/tests/NLog.UnitTests/Targets/Wrappers/FallbackGroupTargetTests.cs b/tests/NLog.UnitTests/Targets/Wrappers/FallbackGroupTargetTests.cs index 8e3acc8d70..607800cf96 100644 --- a/tests/NLog.UnitTests/Targets/Wrappers/FallbackGroupTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/Wrappers/FallbackGroupTargetTests.cs @@ -294,7 +294,7 @@ public void FallbackGroupTargetAsyncTest() ManualResetEvent resetEvent = new ManualResetEvent(false); myTarget1Async.Flush((ex) => { Assert.Null(ex); resetEvent.Set(); }); - resetEvent.WaitOne(1000); + Assert.True(resetEvent.WaitOne(10000)); Assert.Equal(10, exceptions.Count); for (var i = 0; i < 10; ++i) From 274a8093aa41a7b7fc0558dbfd3fac6f499208ef Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 3 Jul 2025 09:00:48 +0200 Subject: [PATCH 193/224] NLog.Targets.Network - Fixed ReadMe.md with link to Log4JXml-target (#5913) --- src/NLog.Targets.Network/Layouts/SyslogFacility.cs | 8 ++++---- src/NLog.Targets.Network/README.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NLog.Targets.Network/Layouts/SyslogFacility.cs b/src/NLog.Targets.Network/Layouts/SyslogFacility.cs index 923be196bb..6bfa67c994 100644 --- a/src/NLog.Targets.Network/Layouts/SyslogFacility.cs +++ b/src/NLog.Targets.Network/Layouts/SyslogFacility.cs @@ -72,16 +72,16 @@ public enum SyslogFacility /// FTP daemon - LOG_FTP Ftp = 11, - /// NTP subsystem + /// NTP subsystem - LOG_NTP Ntp = 12, - /// Log audit + /// Log audit - LOG_SECURITY Audit = 13, - /// Log alert + /// Log alert - LOG_CONSOLE Alert = 14, - /// Clock daemon + /// Clock daemon / Scheduling daemon Clock2 = 15, /// Reserved for local use - LOG_LOCAL0 diff --git a/src/NLog.Targets.Network/README.md b/src/NLog.Targets.Network/README.md index 8ee6022472..c12519d033 100644 --- a/src/NLog.Targets.Network/README.md +++ b/src/NLog.Targets.Network/README.md @@ -22,7 +22,7 @@ See the [NLog Wiki - Gelf Target](https://github.com/NLog/NLog/wiki/Gelf-target) NLog Log4JXml Target combines the NLog NetworkTarget with NLog Log4JXmlEventLayout for NLogViewer / Chainsaw. -See the [NLog Wiki - Chainsaw Target](https://github.com/NLog/NLog/wiki/Chainsaw-target) for available options and examples. +See the [NLog Wiki - Log4JXml Target](https://github.com/NLog/NLog/wiki/Log4JXml-target) for available options and examples. ## Register Extension From 844dfead882314375d0ffe5e8ccab1232effb2fc Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 5 Jul 2025 15:19:09 +0200 Subject: [PATCH 194/224] Log4JXmlTarget - Removed alias NLogViewer as conflicts with other nuget-packages (#5918) --- src/NLog.Targets.Network/Targets/Log4JXmlTarget.cs | 1 - src/NLog/Config/ConfigurationItemFactory.cs | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/NLog.Targets.Network/Targets/Log4JXmlTarget.cs b/src/NLog.Targets.Network/Targets/Log4JXmlTarget.cs index 883d91796c..b1b166f321 100644 --- a/src/NLog.Targets.Network/Targets/Log4JXmlTarget.cs +++ b/src/NLog.Targets.Network/Targets/Log4JXmlTarget.cs @@ -59,7 +59,6 @@ namespace NLog.Targets /// [Target("Log4JXml")] [Target("Chainsaw")] - [Target("NLogViewer")] public class Log4JXmlTarget : NetworkTarget { private readonly Log4JXmlEventLayout _log4JLayout = new Log4JXmlEventLayout(); diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index dd4b11554a..682d5977de 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -491,6 +491,7 @@ private void RegisterAllTargets(bool skipCheckExists) #if !NETFRAMEWORK SafeRegisterNamedType(_targets, "eventlog", "NLog.Targets.EventLogTarget, NLog.WindowsEventLog", skipCheckExists); #endif + SafeRegisterNamedType(_targets, "gzipfile", "NLog.Targets.GZipFileTarget, NLog.Targets.GZipFile", skipCheckExists); SafeRegisterNamedType(_targets, "impersonatingwrapper", "NLog.Targets.Wrappers.ImpersonatingTargetWrapper, NLog.WindowsIdentity", skipCheckExists); SafeRegisterNamedType(_targets, "logreceiverservice", "NLog.Targets.LogReceiverWebServiceTarget, NLog.Wcf", skipCheckExists); SafeRegisterNamedType(_targets, "outputdebugstring", "NLog.Targets.OutputDebugStringTarget, NLog.OutputDebugString", skipCheckExists); @@ -503,6 +504,7 @@ private void RegisterAllTargets(bool skipCheckExists) SafeRegisterNamedType(_targets, "mail", "NLog.Targets.MailTarget, NLog.Targets.Mail", skipCheckExists); SafeRegisterNamedType(_targets, "email", "NLog.Targets.MailTarget, NLog.Targets.Mail", skipCheckExists); SafeRegisterNamedType(_targets, "smtp", "NLog.Targets.MailTarget, NLog.Targets.Mail", skipCheckExists); + SafeRegisterNamedType(_targets, "mailkit", "NLog.MailKit.MailTarget, NLog.MailKit", skipCheckExists); SafeRegisterNamedType(_targets, "performancecounter", "NLog.Targets.PerformanceCounterTarget, NLog.PerformanceCounter", skipCheckExists); SafeRegisterNamedType(_targets, "richtextbox", "NLog.Windows.Forms.RichTextBoxTarget, NLog.Windows.Forms", skipCheckExists); SafeRegisterNamedType(_targets, "messagebox", "NLog.Windows.Forms.MessageBoxTarget, NLog.Windows.Forms", skipCheckExists); @@ -531,9 +533,14 @@ private void RegisterAllLayoutRenderers(bool skipCheckExists) #if !NET35 && !NET40 SafeRegisterNamedType(_layoutRenderers, "configsetting", "NLog.Extensions.Logging.ConfigSettingLayoutRenderer, NLog.Extensions.Logging", skipCheckExists); SafeRegisterNamedType(_layoutRenderers, "microsoftconsolelayout", "NLog.Extensions.Logging.MicrosoftConsoleLayoutRenderer, NLog.Extensions.Logging", skipCheckExists); + SafeRegisterNamedType(_layoutRenderers, "hostappname", "NLog.Extensions.Hosting.HostAppNameLayoutRenderer, NLog.Extensions.Hosting", skipCheckExists); + SafeRegisterNamedType(_layoutRenderers, "hostenvironment", "NLog.Extensions.Hosting.HostEnvironmentLayoutRenderer, NLog.Extensions.Hosting", skipCheckExists); + SafeRegisterNamedType(_layoutRenderers, "hostrootdir", "NLog.Extensions.Hosting.HostRootDirLayoutRenderer, NLog.Extensions.Hosting", skipCheckExists); #endif + SafeRegisterNamedType(_layoutRenderers, "localip", "NLog.LayoutRenderers.LocalIpAddressLayoutRenderer, NLog.Targets.Network", skipCheckExists); SafeRegisterNamedType(_layoutRenderers, "performancecounter", "NLog.LayoutRenderers.PerformanceCounterLayoutRenderer, NLog.PerformanceCounter", skipCheckExists); SafeRegisterNamedType(_layoutRenderers, "registry", "NLog.LayoutRenderers.RegistryLayoutRenderer, NLog.WindowsRegistry", skipCheckExists); + SafeRegisterNamedType(_layoutRenderers, "regexreplace", "NLog.LayoutRenderers.RegexReplaceLayoutRendererWrapper, NLog.RegEx", skipCheckExists); SafeRegisterNamedType(_layoutRenderers, "windowsidentity", "NLog.LayoutRenderers.WindowsIdentityLayoutRenderer, NLog.WindowsIdentity", skipCheckExists); SafeRegisterNamedType(_layoutRenderers, "rtblink", "NLog.Windows.Forms.RichTextBoxLinkLayoutRenderer, NLog.Windows.Forms", skipCheckExists); SafeRegisterNamedType(_layoutRenderers, "activity", "NLog.LayoutRenderers.ActivityTraceLayoutRenderer, NLog.DiagnosticSource", skipCheckExists); From 13af42c153581da23dd7f1dc8d931f17673407ca Mon Sep 17 00:00:00 2001 From: oikku Date: Sat, 5 Jul 2025 19:42:42 +0300 Subject: [PATCH 195/224] ReplaceNewLinesLayoutRendererWrapper - Replace all line ending characters (#5915) --- .../ReplaceNewLinesLayoutRendererWrapper.cs | 73 +++++++++++----- .../Wrappers/ReplaceNewLinesTests.cs | 84 +++++++++++++++++++ 2 files changed, 135 insertions(+), 22 deletions(-) diff --git a/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs index f32dc623ba..b6862d4b92 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs @@ -36,7 +36,6 @@ namespace NLog.LayoutRenderers.Wrappers using System; using System.Text; using NLog.Config; - using NLog.Internal; /// /// Replaces newline characters from the result of another layout renderer with spaces. @@ -51,24 +50,11 @@ namespace NLog.LayoutRenderers.Wrappers [ThreadAgnostic] public sealed class ReplaceNewLinesLayoutRendererWrapper : WrapperLayoutRendererBase { - private const string WindowsNewLine = "\r\n"; - private const string UnixNewLine = "\n"; - /// /// Gets or sets a value indicating the string that should be used to replace newlines. /// /// - public string Replacement - { - get => _replacement; - set - { - _replacement = value; - _replaceWithNewLines = value?.IndexOf('\n') >= 0; - } - } - private string _replacement = " "; - private bool _replaceWithNewLines; + public string Replacement { get; set; } = " "; /// /// Gets or sets a value indicating the string that should be used to replace newlines. @@ -82,19 +68,62 @@ protected override void RenderInnerAndTransform(LogEventInfo logEvent, StringBui Inner?.Render(logEvent, builder); if (builder.Length > orgLength) { - var containsNewLines = builder.IndexOf('\n', orgLength) >= 0; - if (containsNewLines) + int lineEndIndex = IndexOfLineEndCharacters(builder, orgLength); + if (lineEndIndex > -1) { - string str = builder.ToString(orgLength, builder.Length - orgLength) - .Replace(WindowsNewLine, _replaceWithNewLines ? UnixNewLine : Replacement) - .Replace(UnixNewLine, Replacement); - + orgLength = lineEndIndex > orgLength ? lineEndIndex - 1 : orgLength; + string str = builder.ToString(orgLength, builder.Length - orgLength); builder.Length = orgLength; - builder.Append(str); + + bool ignoreNewLine = false; + foreach (var chr in str) + { + if (IsLineEndCharacter(chr)) // switches on chr, and allow method-reuse + { + if (!ignoreNewLine || chr != '\n') + { + builder.Append(Replacement); + } + ignoreNewLine = chr == '\r'; + } + else + { + builder.Append(chr); + ignoreNewLine = false; + } + } + } + } + } + + private static bool IsLineEndCharacter(char ch) + { + switch (ch) + { + case '\r': // CARRIAGE RETURN + case '\n': // LINE FEED + case '\f': // FORM FEED (FF) + case '\u0085': // NEXT LINE (NEL) + case '\u2028': // LINE SEPARATOR + case '\u2029': // PARAGRAPH SEPARATOR + return true; + } + return false; + } + + private static int IndexOfLineEndCharacters(StringBuilder builder, int startPosition) + { + for (int i = startPosition; i < builder.Length; i++) + { + if (IsLineEndCharacter(builder[i])) + { + return i; } } + return -1; } + /// protected override string Transform(string text) { diff --git a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/ReplaceNewLinesTests.cs b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/ReplaceNewLinesTests.cs index d7db7a82d5..4fde146c78 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/Wrappers/ReplaceNewLinesTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/Wrappers/ReplaceNewLinesTests.cs @@ -135,5 +135,89 @@ public void ReplaceNewLineWithNewLineSeparationStringTest() // Assert Assert.Equal("bar\r\n123\r\n", result); } + + [Fact] + public void ReplaceCarriageReturnWithSpecifiedSeparationStringTest() + { + // Arrange + var foo = "bar\r123"; + SimpleLayout l = "${replace-newlines:replacement=|:${event-properties:foo}}"; + // Act + var result = l.Render(LogEventInfo.Create(LogLevel.Info, null, null, "{foo}", new[] { foo })); + // Assert + Assert.Equal("bar|123", result); + } + + [Fact] + public void ReplaceUnicodeLineEndingsWithSpecifiedSeparationStringTest() + { + // Arrange + var foo = "line1\nline2\r\nline3\rline4\u0085line5\u2028line6\u000Cline7\u2029line8"; + SimpleLayout l = "${replace-newlines:replacement=|:${event-properties:foo}}"; + // Act + var result = l.Render(LogEventInfo.Create(LogLevel.Info, null, null, "{foo}", new[] { foo })); + // Assert + Assert.Equal("line1|line2|line3|line4|line5|line6|line7|line8", result); + } + + [Fact] + public void ReplaceUnicodeLineEndingsWithSpecifiedMulticharacterSeparationStringTest() + { + // Arrange + var foo = "line1\nline2\r\nline3\rline4\u0085line5\u2028line6\u000Cline7\u2029line8\r\n"; + SimpleLayout l = "${replace-newlines:replacement=||||:${event-properties:foo}}"; + // Act + var result = l.Render(LogEventInfo.Create(LogLevel.Info, null, null, "{foo}", new[] { foo })); + // Assert + Assert.Equal("line1||||line2||||line3||||line4||||line5||||line6||||line7||||line8||||", result); + } + + [Fact] + public void ReplaceUnicodeConsecutiveLineEndingsWithSpecifiedMulticharacterSeparationStringTest() + { + // Arrange + var foo = "line1\r\r\n\nline2"; + SimpleLayout l = "${replace-newlines:replacement=||:${event-properties:foo}}"; + // Act + var result = l.Render(LogEventInfo.Create(LogLevel.Info, null, null, "{foo}", new[] { foo })); + // Assert + Assert.Equal("line1||||||line2", result); + } + + [Fact] + public void ReplaceWindowsLineEndingsEndOfTextWithSpecifiedSeparationStringTest() + { + // Arrange + var foo = "line1\r\n"; + SimpleLayout l = "${replace-newlines:replacement=|:${event-properties:foo}}"; + // Act + var result = l.Render(LogEventInfo.Create(LogLevel.Info, null, null, "{foo}", new[] { foo })); + // Assert + Assert.Equal("line1|", result); + } + + [Fact] + public void ReplaceUnicodeLineEndingsWithDefault() + { + // Arrange + var foo = "line1\nline2\r\nline3\rline4\u0085line5\u2028line6\u000Cline7\u2029line8"; + SimpleLayout l = "${replace-newlines:${event-properties:foo}}"; + // Act + var result = l.Render(LogEventInfo.Create(LogLevel.Info, null, null, "{foo}", new[] { foo })); + // Assert + Assert.Equal("line1 line2 line3 line4 line5 line6 line7 line8", result); + } + + [Fact] + public void ReplaceSingleLineEndingWithDefault() + { + // Arrange + var foo = "\n"; + SimpleLayout l = "${replace-newlines:${event-properties:foo}}"; + // Act + var result = l.Render(LogEventInfo.Create(LogLevel.Info, null, null, "{foo}", new[] { foo })); + // Assert + Assert.Equal(" ", result); + } } } From b964c0cf3afec5699bf0c6b213fb47dbd857be4e Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 6 Jul 2025 07:29:13 +0200 Subject: [PATCH 196/224] XML docs for Targets and Layouts with remarks about default value (#5919) --- .../Conditions/RegexConditionMethods.cs | 2 +- src/NLog.RegEx/Internal/RegexHelper.cs | 2 +- .../RegexReplaceLayoutRendererWrapper.cs | 6 +- .../ConsoleWordHighlightingRuleRegex.cs | 2 +- .../ConcurrentFileTarget.cs | 16 ++--- .../FileAppenders/BaseFileAppender.cs | 2 +- .../FileAppenders/ICreateFileParameters.cs | 2 +- .../FileArchiveModes/FileArchiveModeBase.cs | 2 +- .../FileArchiveModes/IFileArchiveMode.cs | 2 +- .../Internal/StringHelpers.cs | 2 +- src/NLog.Targets.Mail/MailTarget.cs | 4 +- .../Layouts/GelfLayout.cs | 4 +- .../Layouts/SyslogLayout.cs | 8 +-- .../Targets/NetworkTarget.cs | 7 +- src/NLog.Targets.Trace/NLogTraceListener.cs | 4 +- .../WebServiceTarget.cs | 4 +- .../RegistryLayoutRenderer.cs | 2 +- src/NLog/Common/AsyncHelpers.cs | 2 +- src/NLog/Common/ConversionHelpers.cs | 6 +- src/NLog/Common/InternalLogger-generated.tt | 2 +- src/NLog/Common/InternalLogger.cs | 6 +- src/NLog/Conditions/ConditionTokenizer.cs | 8 +-- src/NLog/Config/ConfigSectionHandler.cs | 2 +- src/NLog/Config/ConfigurationItemFactory.cs | 6 +- src/NLog/Config/IFactory.cs | 2 +- src/NLog/Internal/EnvironmentHelper.cs | 8 --- src/NLog/Internal/ExceptionHelper.cs | 8 +-- src/NLog/Internal/ILogMessageFormatter.cs | 7 +- .../Internal/LogMessageTemplateFormatter.cs | 5 +- src/NLog/Internal/MruCache.cs | 4 +- src/NLog/Internal/StringBuilderExt.cs | 4 +- src/NLog/Internal/TargetWithFilterChain.cs | 13 ---- src/NLog/Internal/XmlParser.cs | 4 +- .../AllEventPropertiesLayoutRenderer.cs | 2 +- .../ExceptionLayoutRenderer.cs | 4 +- .../LayoutRenderers/LevelLayoutRenderer.cs | 2 +- .../LayoutRenderers/NewLineLayoutRenderer.cs | 3 +- .../JsonEncodeLayoutRendererWrapper.cs | 2 +- .../LowercaseLayoutRendererWrapper.cs | 2 +- .../NoRawValueLayoutRendererWrapper.cs | 2 +- .../Wrappers/PaddingLayoutRendererWrapper.cs | 2 +- .../Wrappers/ReplaceLayoutRendererWrapper.cs | 4 +- .../TrimWhiteSpaceLayoutRendererWrapper.cs | 2 +- .../UppercaseLayoutRendererWrapper.cs | 2 +- .../UrlEncodeLayoutRendererWrapper.cs | 6 +- .../Wrappers/WrapLineLayoutRendererWrapper.cs | 2 +- .../XmlEncodeLayoutRendererWrapper.cs | 4 +- src/NLog/Layouts/CSV/CsvColumn.cs | 6 +- src/NLog/Layouts/CSV/CsvLayout.cs | 8 ++- src/NLog/Layouts/JSON/JsonArrayLayout.cs | 4 +- src/NLog/Layouts/JSON/JsonAttribute.cs | 14 ++-- src/NLog/Layouts/JSON/JsonLayout.cs | 19 ++++-- src/NLog/Layouts/Layout.cs | 6 +- src/NLog/Layouts/SimpleLayout.cs | 2 +- src/NLog/Layouts/XML/XmlAttribute.cs | 9 ++- src/NLog/Layouts/XML/XmlElement.cs | 8 ++- src/NLog/Layouts/XML/XmlElementBase.cs | 25 ++++--- src/NLog/Layouts/XML/XmlLayout.cs | 6 +- src/NLog/LogFactory.cs | 21 ++---- src/NLog/LogLevel.cs | 4 +- src/NLog/LogManager.cs | 2 +- src/NLog/ScopeContext.cs | 2 +- .../SetupSerializationBuilderExtensions.cs | 6 +- src/NLog/Targets/AsyncTaskTarget.cs | 9 +++ src/NLog/Targets/ColoredConsoleTarget.cs | 11 ++- .../Targets/ConsoleRowHighlightingRule.cs | 3 + src/NLog/Targets/ConsoleTarget.cs | 8 ++- .../Targets/ConsoleWordHighlightingRule.cs | 6 ++ src/NLog/Targets/EventLogTarget.cs | 9 ++- src/NLog/Targets/FileTarget.cs | 68 ++++++++++++------- src/NLog/Targets/JsonSerializeOptions.cs | 17 +++-- src/NLog/Targets/LineEndingMode.cs | 6 +- src/NLog/Targets/MemoryTarget.cs | 3 +- src/NLog/Targets/MethodCallParameter.cs | 5 ++ src/NLog/Targets/MethodCallTarget.cs | 5 +- src/NLog/Targets/NullTarget.cs | 1 + src/NLog/Targets/Target.cs | 3 +- src/NLog/Targets/TargetPropertyWithContext.cs | 8 ++- src/NLog/Targets/TargetWithContext.cs | 14 +++- src/NLog/Targets/TargetWithLayout.cs | 5 +- .../TargetWithLayoutHeaderAndFooter.cs | 10 +-- .../Targets/Wrappers/AsyncTargetWrapper.cs | 2 +- .../WebServiceTargetTests.cs | 4 -- .../LayoutRenderers/ExceptionTests.cs | 12 ++-- 84 files changed, 311 insertions(+), 237 deletions(-) diff --git a/src/NLog.RegEx/Conditions/RegexConditionMethods.cs b/src/NLog.RegEx/Conditions/RegexConditionMethods.cs index e212c96e7e..a7ddc488f5 100644 --- a/src/NLog.RegEx/Conditions/RegexConditionMethods.cs +++ b/src/NLog.RegEx/Conditions/RegexConditionMethods.cs @@ -17,7 +17,7 @@ public static class RegexConditionMethods /// The string to search for a match. /// The regular expression pattern to match. /// A string consisting of the desired options for the test. The possible values are those of the separated by commas. - /// true if the regular expression finds a match; otherwise, false. + /// if the regular expression finds a match; otherwise, . [ConditionMethod("regex-matches")] public static bool RegexMatches(string input, string pattern, [Optional, DefaultParameterValue("")] string options) { diff --git a/src/NLog.RegEx/Internal/RegexHelper.cs b/src/NLog.RegEx/Internal/RegexHelper.cs index d803a7e999..0dd2a0a1d6 100644 --- a/src/NLog.RegEx/Internal/RegexHelper.cs +++ b/src/NLog.RegEx/Internal/RegexHelper.cs @@ -67,7 +67,7 @@ public string? RegexPattern } /// - /// Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. + /// Compile the ? This can improve the performance, but at the costs of more memory usage. If , the Regex Cache is used. /// public bool CompileRegex { get; set; } diff --git a/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs b/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs index dd24867e6d..a6085cf9f1 100644 --- a/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs +++ b/src/NLog.RegEx/LayoutRenderers/Wrappers/RegexReplaceLayoutRendererWrapper.cs @@ -84,19 +84,19 @@ public sealed class RegexReplaceLayoutRendererWrapper : WrapperLayoutRendererBas /// /// Gets or sets a value indicating whether to ignore case. /// - /// A value of true if case should be ignored when searching; otherwise, false. + /// A value of if case should be ignored when searching; otherwise, . /// public bool IgnoreCase { get; set; } /// /// Gets or sets a value indicating whether to search for whole words. /// - /// A value of true if whole words should be searched for; otherwise, false. + /// A value of if whole words should be searched for; otherwise, . /// public bool WholeWords { get; set; } /// - /// Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. + /// Compile the ? This can improve the performance, but at the costs of more memory usage. If , the Regex Cache is used. /// /// public bool CompileRegex { get; set; } diff --git a/src/NLog.RegEx/Targets/ConsoleWordHighlightingRuleRegex.cs b/src/NLog.RegEx/Targets/ConsoleWordHighlightingRuleRegex.cs index 8972385149..b60db0f146 100644 --- a/src/NLog.RegEx/Targets/ConsoleWordHighlightingRuleRegex.cs +++ b/src/NLog.RegEx/Targets/ConsoleWordHighlightingRuleRegex.cs @@ -56,7 +56,7 @@ public string? Regex } /// - /// Compile the ? This can improve the performance, but at the costs of more memory usage. If false, the Regex Cache is used. + /// Compile the ? This can improve the performance, but at the costs of more memory usage. If , the Regex Cache is used. /// /// public bool CompileRegex diff --git a/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs b/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs index 0e9c38cc81..e0e3fcdfc4 100644 --- a/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs +++ b/src/NLog.Targets.ConcurrentFile/ConcurrentFileTarget.cs @@ -206,7 +206,7 @@ private FilePathLayout CreateFileNameLayout(Layout value) /// /// Cleanup invalid values in a filename, e.g. slashes in a filename. If set to true, this can impact the performance of massive writes. - /// If set to false, nothing gets written when the filename is wrong. + /// If set to , nothing gets written when the filename is wrong. /// /// public bool CleanupFileName @@ -247,7 +247,7 @@ public FilePathKind FileNameKind /// Gets or sets a value indicating whether to create directories if they do not exist. /// /// - /// Setting this to false may improve performance a bit, but you'll receive an error + /// Setting this to may improve performance a bit, but you'll receive an error /// when attempting to write to a directory that's not present. /// /// @@ -272,8 +272,8 @@ public FilePathKind FileNameKind /// Gets or sets a value indicating whether to keep log file open instead of opening and closing it on each logging event. /// /// - /// KeepFileOpen = true gives the best performance, and ensure the file-lock is not lost to other applications.
- /// KeepFileOpen = false gives the best compability, but slow performance and lead to file-locking issues with other applications. + /// KeepFileOpen = gives the best performance, and ensure the file-lock is not lost to other applications.
+ /// KeepFileOpen = gives the best compatibility, but slow performance and lead to file-locking issues with other applications. ///
/// public bool KeepFileOpen @@ -410,7 +410,7 @@ public bool ConcurrentWrites } /// - /// Obsolete and replaced by = false with NLog v5.3. + /// Obsolete and replaced by = with NLog v5.3. /// Gets or sets a value indicating whether concurrent writes to the log file by multiple processes on different network hosts. /// /// @@ -424,7 +424,7 @@ public bool ConcurrentWrites /// /// Gets or sets a value indicating whether to write BOM (byte order mark) in created files. /// - /// Defaults to true for UTF-16 and UTF-32 + /// Defaults to for UTF-16 and UTF-32 /// /// public bool WriteBom @@ -490,7 +490,7 @@ public bool ArchiveOldFileOnStartup /// Gets or sets a value of the file size threshold to archive old log file on startup. ///
/// - /// This option won't work if is set to false + /// This option won't work if is set to /// Default value is 0 which means that the file is archived as soon as archival on /// startup is enabled. /// @@ -1725,7 +1725,7 @@ private string GetArchiveFileNamePattern(string fileName, LogEventInfo eventInfo /// The size in bytes of the next chunk of data to be written in the file. /// The DateTime of the previous log event for this file. /// File has just been opened. - /// True when archive operation of the file was completed (by this target or a concurrent target) + /// when archive operation of the file was completed (by this target or a concurrent target) private bool TryArchiveFile(string fileName, LogEventInfo ev, int upcomingWriteSize, DateTime previousLogEventTimestamp, bool initializedNewFile) { if (!IsArchivingEnabled) diff --git a/src/NLog.Targets.ConcurrentFile/FileAppenders/BaseFileAppender.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/BaseFileAppender.cs index fad3144044..f805c1f3ae 100644 --- a/src/NLog.Targets.ConcurrentFile/FileAppenders/BaseFileAppender.cs +++ b/src/NLog.Targets.ConcurrentFile/FileAppenders/BaseFileAppender.cs @@ -156,7 +156,7 @@ public void Dispose() /// /// Releases unmanaged and - optionally - managed resources. /// - /// True to release both managed and unmanaged resources; false to release only unmanaged resources. + /// to release both managed and unmanaged resources; to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (disposing) diff --git a/src/NLog.Targets.ConcurrentFile/FileAppenders/ICreateFileParameters.cs b/src/NLog.Targets.ConcurrentFile/FileAppenders/ICreateFileParameters.cs index 42b0a3591b..48c9ccdebc 100644 --- a/src/NLog.Targets.ConcurrentFile/FileAppenders/ICreateFileParameters.cs +++ b/src/NLog.Targets.ConcurrentFile/FileAppenders/ICreateFileParameters.cs @@ -64,7 +64,7 @@ internal interface ICreateFileParameters /// Gets or sets a value indicating whether to create directories if they do not exist. ///
/// - /// Setting this to false may improve performance a bit, but you'll receive an error + /// Setting this to may improve performance a bit, but you'll receive an error /// when attempting to write to a directory that's not present. /// bool CreateDirs { get; } diff --git a/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeBase.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeBase.cs index 9dab892ab4..990eab96dc 100644 --- a/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeBase.cs +++ b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/FileArchiveModeBase.cs @@ -60,7 +60,7 @@ protected FileArchiveModeBase(bool isArchiveCleanupEnabled) /// Base archive file pattern /// Maximum number of archive files that should be kept /// Maximum days of archive files that should be kept - /// True, when archive cleanup is needed + /// , when archive cleanup is needed public virtual bool AttemptCleanupOnInitializeFile(string archiveFilePath, int maxArchiveFiles, int maxArchiveDays) { if (maxArchiveFiles > 0 && _lastArchiveFileCount++ > maxArchiveFiles) diff --git a/src/NLog.Targets.ConcurrentFile/FileArchiveModes/IFileArchiveMode.cs b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/IFileArchiveMode.cs index 4b5ea2dad0..52bdf68f2c 100644 --- a/src/NLog.Targets.ConcurrentFile/FileArchiveModes/IFileArchiveMode.cs +++ b/src/NLog.Targets.ConcurrentFile/FileArchiveModes/IFileArchiveMode.cs @@ -46,7 +46,7 @@ interface IFileArchiveMode /// Base archive file pattern /// Maximum number of archive files that should be kept /// Maximum days of archive files that should be kept - /// True, when archive cleanup is needed + /// , when archive cleanup is needed bool AttemptCleanupOnInitializeFile(string archiveFilePath, int maxArchiveFiles, int maxArchiveDays); /// diff --git a/src/NLog.Targets.ConcurrentFile/Internal/StringHelpers.cs b/src/NLog.Targets.ConcurrentFile/Internal/StringHelpers.cs index 29ec718e68..2f75552012 100644 --- a/src/NLog.Targets.ConcurrentFile/Internal/StringHelpers.cs +++ b/src/NLog.Targets.ConcurrentFile/Internal/StringHelpers.cs @@ -54,7 +54,7 @@ internal static bool IsNullOrWhiteSpace(string value) /// /// Compares the contents of a StringBuilder and a String /// - /// True when content is the same + /// when content is the same public static bool EqualTo(this StringBuilder builder, string other) { if (builder.Length != other.Length) diff --git a/src/NLog.Targets.Mail/MailTarget.cs b/src/NLog.Targets.Mail/MailTarget.cs index 03c74f103a..625619309d 100644 --- a/src/NLog.Targets.Mail/MailTarget.cs +++ b/src/NLog.Targets.Mail/MailTarget.cs @@ -196,7 +196,7 @@ public Layout From /// /// Gets or sets a value indicating whether to add new lines between log entries. /// - /// A value of true if new lines should be added; otherwise, false. + /// A value of if new lines should be added; otherwise, . /// public bool AddNewLines { get; set; } @@ -292,7 +292,7 @@ public Layout Body /// /// Gets or sets a value indicating whether NewLine characters in the body should be replaced with
tags. ///
- /// Only happens when is set to true. + /// Only happens when is set to . /// public bool ReplaceNewlineWithBrTagInHtml { get; set; } diff --git a/src/NLog.Targets.Network/Layouts/GelfLayout.cs b/src/NLog.Targets.Network/Layouts/GelfLayout.cs index 35cb4309c2..d4b9a31ec6 100644 --- a/src/NLog.Targets.Network/Layouts/GelfLayout.cs +++ b/src/NLog.Targets.Network/Layouts/GelfLayout.cs @@ -102,7 +102,7 @@ public Layout GelfFacility public bool ExcludeEmptyProperties { get; set; } /// - /// List of property names to exclude when is true + /// List of property names to exclude when is /// #if NET35 public HashSet ExcludeProperties { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); @@ -111,7 +111,7 @@ public Layout GelfFacility #endif /// - /// List of property names to include when is true + /// List of property names to include when is /// #if NET35 public HashSet IncludeProperties { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); diff --git a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs index 98568c14a7..5397f38e01 100644 --- a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs +++ b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs @@ -43,7 +43,7 @@ public class SyslogLayout : CompoundLayout public bool Rfc5424 { get; set; } /// - /// Gets or sets a value indicating what DateTime format should be used when = true + /// Gets or sets a value indicating what DateTime format should be used when = /// public Layout SyslogTimestamp { get; set; } = "${date:format=o}"; @@ -126,17 +126,17 @@ public Layout SyslogProcessId public SyslogFacility SyslogFacility { get; set; } = SyslogFacility.User; /// - /// Gets or sets the prefix for StructuredData when = true + /// Gets or sets the prefix for StructuredData when = /// public Layout StructuredDataId { get; set; } = "meta"; /// - /// Gets or sets a value indicating whether LogEvent Properties should be included for StructuredData when = true + /// Gets or sets a value indicating whether LogEvent Properties should be included for StructuredData when = /// public bool IncludeEventProperties { get; set; } /// - /// List of StructuredData Parameters to include when = true + /// List of StructuredData Parameters to include when = /// [ArrayParameter(typeof(TargetPropertyWithContext), "StructuredDataParam")] public List StructuredDataParams { get; } = new List(); diff --git a/src/NLog.Targets.Network/Targets/NetworkTarget.cs b/src/NLog.Targets.Network/Targets/NetworkTarget.cs index ed4ead7691..e7c5f873a0 100644 --- a/src/NLog.Targets.Network/Targets/NetworkTarget.cs +++ b/src/NLog.Targets.Network/Targets/NetworkTarget.cs @@ -171,7 +171,7 @@ public LineEndingMode LineEnding public NetworkTargetConnectionsOverflowAction OnConnectionOverflow { get; set; } = NetworkTargetConnectionsOverflowAction.Discard; /// - /// Gets or sets the maximum queue size for a single connection. Requires = true + /// Gets or sets the maximum queue size for a single connection. Requires = /// /// /// When having reached the maximum limit, then action will apply. @@ -196,7 +196,7 @@ public LineEndingMode LineEnding public event EventHandler? LogEventDropped; /// - /// Gets or sets the size of the connection cache (number of connections which are kept alive). Requires = true + /// Gets or sets the size of the connection cache (number of connections which are kept alive). Requires = /// /// public int ConnectionCacheSize { get; set; } = 5; @@ -252,8 +252,9 @@ public LineEndingMode LineEnding public int SendTimeoutSeconds { get; set; } = 100; /// - /// Gets or sets whether to disable the delayed ACK timer, and avoid delay of 200 ms. Default = true. + /// Gets or sets whether to disable the delayed ACK timer, and avoid delay of 200 ms. /// + /// Default: public bool NoDelay { get; set; } = true; /// diff --git a/src/NLog.Targets.Trace/NLogTraceListener.cs b/src/NLog.Targets.Trace/NLogTraceListener.cs index 8946648506..68dd942af5 100644 --- a/src/NLog.Targets.Trace/NLogTraceListener.cs +++ b/src/NLog.Targets.Trace/NLogTraceListener.cs @@ -142,7 +142,7 @@ public bool DisableFlush /// Gets a value indicating whether the trace listener is thread safe. /// /// - /// true if the trace listener is thread safe; otherwise, false. The default is false. + /// if the trace listener is thread safe; otherwise, false. The default is . public override bool IsThreadSafe => true; /// @@ -251,7 +251,7 @@ public override void Fail(string message, string detailMessage) } /// - /// Flushes the output (if is not true) buffer with the default timeout of 15 seconds. + /// Flushes the output (if is not ) buffer with the default timeout of 15 seconds. /// public override void Flush() { diff --git a/src/NLog.Targets.WebService/WebServiceTarget.cs b/src/NLog.Targets.WebService/WebServiceTarget.cs index ede89be93c..f367bc1139 100644 --- a/src/NLog.Targets.WebService/WebServiceTarget.cs +++ b/src/NLog.Targets.WebService/WebServiceTarget.cs @@ -185,7 +185,7 @@ public WebServiceProxyType ProxyType /// /// Gets or sets a value whether escaping be done according to Rfc3986 (Supports Internationalized Resource Identifiers - IRIs) /// - /// A value of true if Rfc3986; otherwise, false for legacy Rfc2396. + /// A value of if Rfc3986; otherwise, for legacy Rfc2396. /// [Obsolete("Replaced by WebUtility.UrlEncode. Marked obsolete with NLog 6.0")] public bool EscapeDataRfc3986 { get; set; } @@ -193,7 +193,7 @@ public WebServiceProxyType ProxyType /// /// Gets or sets a value whether escaping be done according to the old NLog style (Very non-standard) /// - /// A value of true if legacy encoding; otherwise, false for standard UTF8 encoding. + /// A value of if legacy encoding; otherwise, for standard UTF8 encoding. /// [Obsolete("Replaced by WebUtility.UrlEncode. Marked obsolete with NLog 6.0")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs b/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs index fd49d7d491..4415763903 100644 --- a/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs +++ b/src/NLog.WindowsRegistry/RegistryLayoutRenderer.cs @@ -61,7 +61,7 @@ public class RegistryLayoutRenderer : LayoutRenderer /// /// Require escaping backward slashes in . Need to be backwards-compatible. /// - /// When true: + /// When : /// /// `\` in value should be configured as `\\` /// `\\` in value should be configured as `\\\\`. diff --git a/src/NLog/Common/AsyncHelpers.cs b/src/NLog/Common/AsyncHelpers.cs index 8a1a70d32c..1205a0476e 100644 --- a/src/NLog/Common/AsyncHelpers.cs +++ b/src/NLog/Common/AsyncHelpers.cs @@ -301,7 +301,7 @@ public static AsyncContinuation PreventMultipleCalls(AsyncContinuation asyncCont var sb = new StringBuilder(); string separator = string.Empty; - string newline = EnvironmentHelper.NewLine; + string newline = Environment.NewLine; foreach (var ex in exceptions) { sb.Append(separator); diff --git a/src/NLog/Common/ConversionHelpers.cs b/src/NLog/Common/ConversionHelpers.cs index 624ab4629c..1aace1c56e 100644 --- a/src/NLog/Common/ConversionHelpers.cs +++ b/src/NLog/Common/ConversionHelpers.cs @@ -47,7 +47,7 @@ public static class ConversionHelpers /// Input value /// Output value /// Default value - /// Returns false if the input value could not be parsed + /// Returns if the input value could not be parsed public static bool TryParseEnum(string inputValue, out TEnum resultValue, TEnum defaultValue = default(TEnum)) where TEnum : struct { if (!TryParseEnum(inputValue, true, out resultValue)) @@ -89,9 +89,9 @@ internal static bool TryParseEnum(string inputValue, Type enumType, out object? /// /// The enumeration type to which to convert value. /// The string representation of the enumeration name or underlying value to convert. - /// true to ignore case; false to consider case. + /// to ignore case; to consider case. /// When this method returns, result contains an object of type TEnum whose value is represented by value if the parse operation succeeds. If the parse operation fails, result contains the default value of the underlying type of TEnum. Note that this value need not be a member of the TEnum enumeration. This parameter is passed uninitialized. - /// true if the value parameter was converted successfully; otherwise, false. + /// if the value parameter was converted successfully; otherwise, . /// Wrapper because Enum.TryParse is not present in .net 3.5 internal static bool TryParseEnum(string inputValue, bool ignoreCase, out TEnum resultValue) where TEnum : struct { diff --git a/src/NLog/Common/InternalLogger-generated.tt b/src/NLog/Common/InternalLogger-generated.tt index 7b22142d23..4866b24b7c 100644 --- a/src/NLog/Common/InternalLogger-generated.tt +++ b/src/NLog/Common/InternalLogger-generated.tt @@ -200,4 +200,4 @@ namespace NLog.Common } #> } -} \ No newline at end of file +} diff --git a/src/NLog/Common/InternalLogger.cs b/src/NLog/Common/InternalLogger.cs index 937e15b26e..7ae9c29f72 100644 --- a/src/NLog/Common/InternalLogger.cs +++ b/src/NLog/Common/InternalLogger.cs @@ -385,7 +385,7 @@ private static string CreateLogLine(Exception? ex, LogLevel level, string fullMe /// Determine if logging should be avoided because of exception type. /// /// The exception to check. - /// true if logging should be avoided; otherwise, false. + /// if logging should be avoided; otherwise, . private static bool IsSeriousException(Exception? exception) { return exception != null && exception.MustBeRethrownImmediately(); @@ -395,7 +395,7 @@ private static bool IsSeriousException(Exception? exception) /// Determine if logging is enabled for given LogLevel ///
/// The for the log event. - /// true if logging is enabled; otherwise, false. + /// if logging is enabled; otherwise, . private static bool IsLogLevelEnabled(LogLevel logLevel) { return !ReferenceEquals(_logLevel, LogLevel.Off) && _logLevel.CompareTo(logLevel) <= 0; @@ -404,7 +404,7 @@ private static bool IsLogLevelEnabled(LogLevel logLevel) /// /// Determine if logging is enabled. /// - /// true if logging is enabled; otherwise, false. + /// if logging is enabled; otherwise, . internal static bool HasActiveLoggers() { if (InternalEventOccurred is null && LogWriter is null) diff --git a/src/NLog/Conditions/ConditionTokenizer.cs b/src/NLog/Conditions/ConditionTokenizer.cs index 4b9e648027..d3c87f2890 100644 --- a/src/NLog/Conditions/ConditionTokenizer.cs +++ b/src/NLog/Conditions/ConditionTokenizer.cs @@ -97,7 +97,7 @@ public string EatKeyword() ///
/// The keyword. /// - /// A value of true if current keyword is equal to the specified value; otherwise, false. + /// A value of if current keyword is equal to the specified value; otherwise, . /// public bool IsKeyword(string keyword) { @@ -113,7 +113,7 @@ public bool IsKeyword(string keyword) /// Gets or sets a value indicating whether the tokenizer has reached the end of the token stream. ///
/// - /// A value of true if the tokenizer has reached the end of the token stream; otherwise, false. + /// A value of if the tokenizer has reached the end of the token stream; otherwise, . /// public bool IsEOF() { @@ -124,7 +124,7 @@ public bool IsEOF() /// Gets or sets a value indicating whether current token is a number. ///
/// - /// A value of true if current token is a number; otherwise, false. + /// A value of if current token is a number; otherwise, . /// public bool IsNumber() { @@ -136,7 +136,7 @@ public bool IsNumber() ///
/// The token type. /// - /// A value of true if current token is of specified type; otherwise, false. + /// A value of if current token is of specified type; otherwise, . /// public bool IsToken(ConditionTokenType tokenType) { diff --git a/src/NLog/Config/ConfigSectionHandler.cs b/src/NLog/Config/ConfigSectionHandler.cs index 50368264ae..8ca8779255 100644 --- a/src/NLog/Config/ConfigSectionHandler.cs +++ b/src/NLog/Config/ConfigSectionHandler.cs @@ -83,7 +83,7 @@ public static LoggingConfiguration? AppConfig /// of the relevant app.config section. ///
/// The XmlReader that reads from the configuration file. - /// true to serialize only the collection key properties; otherwise, false. + /// to serialize only the collection key properties; otherwise, . protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey) { try diff --git a/src/NLog/Config/ConfigurationItemFactory.cs b/src/NLog/Config/ConfigurationItemFactory.cs index 682d5977de..dc1adbf403 100644 --- a/src/NLog/Config/ConfigurationItemFactory.cs +++ b/src/NLog/Config/ConfigurationItemFactory.cs @@ -284,9 +284,9 @@ public IJsonConverter JsonConverter /// Perform message template parsing and formatting of LogEvent messages (True = Always, False = Never, Null = Auto Detect) ///
/// - /// - Null (Auto Detect) : NLog-parser checks for positional parameters, and will then fallback to string.Format-rendering. - /// - True: Always performs the parsing of and rendering of using the NLog-parser (Allows custom formatting with ) - /// - False: Always performs parsing and rendering using string.Format (Fastest if not using structured logging) + /// - (Auto Detect) : NLog-parser checks for positional parameters, and will then fallback to string.Format-rendering. + /// - : Always performs the parsing of and rendering of using the NLog-parser (Allows custom formatting with ) + /// - : Always performs parsing and rendering using string.Format (Fastest if not using structured logging) /// public bool? ParseMessageTemplates { diff --git a/src/NLog/Config/IFactory.cs b/src/NLog/Config/IFactory.cs index 820cc7c8c4..b00a17fd64 100644 --- a/src/NLog/Config/IFactory.cs +++ b/src/NLog/Config/IFactory.cs @@ -54,7 +54,7 @@ public interface IFactory where TBaseType : class /// /// Tries to create an item instance with type-alias /// - /// True if instance was created successfully, false otherwise. + /// if instance was created successfully, otherwise. bool TryCreateInstance(string typeAlias, out TBaseType? result); } } diff --git a/src/NLog/Internal/EnvironmentHelper.cs b/src/NLog/Internal/EnvironmentHelper.cs index e1aa5004e5..57aac223f5 100644 --- a/src/NLog/Internal/EnvironmentHelper.cs +++ b/src/NLog/Internal/EnvironmentHelper.cs @@ -41,14 +41,6 @@ namespace NLog.Internal ///
internal static class EnvironmentHelper { - internal static string NewLine - { - get - { - return Environment.NewLine; - } - } - internal static string GetMachineName() { try diff --git a/src/NLog/Internal/ExceptionHelper.cs b/src/NLog/Internal/ExceptionHelper.cs index f6955758c5..277e9f2537 100644 --- a/src/NLog/Internal/ExceptionHelper.cs +++ b/src/NLog/Internal/ExceptionHelper.cs @@ -61,7 +61,7 @@ public static void MarkAsLoggedToInternalLogger(this Exception exception) /// Is this exception logged to the ? ///
/// - /// trueif the has been logged to the . + /// if the has been logged to the . public static bool IsLoggedToInternalLogger(this Exception exception) { if (exception?.Data?.Count > 0) @@ -72,14 +72,14 @@ public static bool IsLoggedToInternalLogger(this Exception exception) } /// - /// Determines whether the exception must be rethrown and logs the error to the if is false. + /// Determines whether the exception must be rethrown and logs the error to the if is . /// /// Advised to log first the error to the before calling this method. /// /// The exception to check. /// Target Object context of the exception. /// Target Method context of the exception. - /// trueif the must be rethrown, false otherwise. + /// if the must be rethrown, otherwise. public static bool MustBeRethrown(this Exception exception, IInternalLoggerContext? loggerContext = null, string? callerMemberName = null) { if (exception.MustBeRethrownImmediately()) @@ -119,7 +119,7 @@ public static bool MustBeRethrown(this Exception exception, IInternalLoggerConte /// Only used this method in special cases. ///
/// The exception to check. - /// trueif the must be rethrown, false otherwise. + /// if the must be rethrown, otherwise. public static bool MustBeRethrownImmediately(this Exception exception) { if (exception is StackOverflowException) diff --git a/src/NLog/Internal/ILogMessageFormatter.cs b/src/NLog/Internal/ILogMessageFormatter.cs index 53981628f4..7fcb19cfa6 100644 --- a/src/NLog/Internal/ILogMessageFormatter.cs +++ b/src/NLog/Internal/ILogMessageFormatter.cs @@ -41,7 +41,10 @@ namespace NLog.Internal internal interface ILogMessageFormatter { /// - /// Perform message template parsing and formatting of LogEvent messages (True = Always, False = Never, Null = Auto Detect) + /// Perform message template parsing and formatting of LogEvent messages: + /// - = Always + /// - = Never + /// - = Auto Detect /// bool? EnableMessageTemplateParser { get; } @@ -56,7 +59,7 @@ internal interface ILogMessageFormatter /// Has the logevent properties? ///
/// LogEvent with message to be formatted - /// False when logevent has no properties to be extracted + /// when logevent has no properties to be extracted bool HasProperties(LogEventInfo logEvent); /// diff --git a/src/NLog/Internal/LogMessageTemplateFormatter.cs b/src/NLog/Internal/LogMessageTemplateFormatter.cs index 437176fba6..45d2115554 100644 --- a/src/NLog/Internal/LogMessageTemplateFormatter.cs +++ b/src/NLog/Internal/LogMessageTemplateFormatter.cs @@ -49,7 +49,7 @@ internal sealed class LogMessageTemplateFormatter : ILogMessageFormatter private IValueFormatter? _valueFormatter; /// - /// When true: Do not fallback to StringBuilder.Format for positional templates + /// When Do not fallback to StringBuilder.Format for positional templates /// private readonly bool _forceMessageTemplateRenderer; private readonly bool _singleTargetOnly; @@ -58,9 +58,8 @@ internal sealed class LogMessageTemplateFormatter : ILogMessageFormatter /// New formatter /// /// - /// When true: Do not fallback to StringBuilder.Format for positional templates + /// When Do not fallback to StringBuilder.Format for positional templates /// - /// public LogMessageTemplateFormatter(LogFactory logFactory, bool forceMessageTemplateRenderer, bool singleTargetOnly) { _logFactory = logFactory; diff --git a/src/NLog/Internal/MruCache.cs b/src/NLog/Internal/MruCache.cs index 550ea2eebd..f6d68cfd8b 100644 --- a/src/NLog/Internal/MruCache.cs +++ b/src/NLog/Internal/MruCache.cs @@ -60,7 +60,7 @@ public MruCache(int maxCapacity) ///
/// Key of the item to be inserted in the cache. /// Value of the item to be inserted in the cache. - /// true when the key does not already exist in the cache, false otherwise. + /// when the key does not already exist in the cache, otherwise. public bool TryAddValue(TKey key, TValue value) { lock (_dictionary) @@ -151,7 +151,7 @@ private void PruneCache() ///
/// Key of the item to be searched in the cache. /// Output value of the item found in the cache. - /// True when the key is found in the cache, false otherwise. + /// when the key is found in the cache, otherwise. public bool TryGetValue(TKey key, out TValue? value) { MruCacheItem item; diff --git a/src/NLog/Internal/StringBuilderExt.cs b/src/NLog/Internal/StringBuilderExt.cs index a9cb5f28e7..ac1d180970 100644 --- a/src/NLog/Internal/StringBuilderExt.cs +++ b/src/NLog/Internal/StringBuilderExt.cs @@ -351,7 +351,7 @@ private static bool CharArrayContains(char searchChar, char[] needles) /// /// Correct implementation of that also works when is not the same /// - /// True when content is the same + /// when content is the same public static bool EqualTo(this StringBuilder builder, StringBuilder other) { var builderLength = builder.Length; @@ -372,7 +372,7 @@ public static bool EqualTo(this StringBuilder builder, StringBuilder other) /// /// Compares the contents of a StringBuilder and a String /// - /// True when content is the same + /// when content is the same public static bool EqualTo(this StringBuilder builder, string other) { if (builder.Length != other.Length) diff --git a/src/NLog/Internal/TargetWithFilterChain.cs b/src/NLog/Internal/TargetWithFilterChain.cs index 806b1ab904..0350545ad1 100644 --- a/src/NLog/Internal/TargetWithFilterChain.cs +++ b/src/NLog/Internal/TargetWithFilterChain.cs @@ -395,29 +395,16 @@ public CallSiteKey(string? methodName, string? fileSourceName, int fileSourceLin public readonly string FileSourceName; public readonly int FileSourceLineNumber; - /// - /// Serves as a hash function for a particular type. - /// public override int GetHashCode() { return MethodName.GetHashCode() ^ FileSourceName.GetHashCode() ^ FileSourceLineNumber; } - /// - /// Determines if two objects are equal in value. - /// - /// Other object to compare to. - /// True if objects are equal, false otherwise. public override bool Equals(object obj) { return obj is CallSiteKey key && Equals(key); } - /// - /// Determines if two objects of the same type are equal in value. - /// - /// Other object to compare to. - /// True if objects are equal, false otherwise. public bool Equals(CallSiteKey other) { return FileSourceLineNumber == other.FileSourceLineNumber diff --git a/src/NLog/Internal/XmlParser.cs b/src/NLog/Internal/XmlParser.cs index 7a1b9985b6..17d7d763a8 100644 --- a/src/NLog/Internal/XmlParser.cs +++ b/src/NLog/Internal/XmlParser.cs @@ -171,7 +171,7 @@ public bool TryReadProcessingInstructions(out IList? processin /// /// Reads a start element. /// - /// True if start element was found. + /// if start element was found. /// Something unexpected has failed. public bool TryReadStartElement(out string? name, out List>? attributes) { @@ -200,7 +200,7 @@ public bool TryReadStartElement(out string? name, out List /// The name of the element to skip. - /// True if an end element was skipped; otherwise, false. + /// if an end element was skipped; otherwise, . /// Something unexpected has failed. public bool TryReadEndElement(string name) { diff --git a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs index c8e0a952e6..71b2b2ab32 100644 --- a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs @@ -83,7 +83,7 @@ public string Separator private string _separatorOriginal = ", "; /// - /// Gets or sets whether empty property-values should be included in the output. Default = false + /// Gets or sets whether empty property-values should be included in the output. /// /// Empty value is either null or empty string /// diff --git a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs index 77b6ae5ba2..17a5c0bba5 100644 --- a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs @@ -187,7 +187,7 @@ public string ExceptionDataSeparator /// Gets or sets the separator between inner exceptions. ///
/// - public string InnerExceptionSeparator { get; set; } = EnvironmentHelper.NewLine; + public string InnerExceptionSeparator { get; set; } = Environment.NewLine; /// /// Gets or sets whether to render innermost Exception from @@ -441,7 +441,7 @@ protected virtual void AppendToString(StringBuilder sb, Exception ex) sb.Append($"{ex.GetType()}: {exceptionMessage}"); if (innerException != null) { - sb.Append(Environment.NewLine); + sb.AppendLine(); AppendToString(sb, innerException); } } diff --git a/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs b/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs index fd707bf8ee..9a5b65346c 100644 --- a/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs @@ -70,7 +70,7 @@ public class LevelLayoutRenderer : LayoutRenderer, IRawValue, IStringValueRender /// /// Gets or sets a value indicating whether upper case conversion should be applied. /// - /// A value of true if upper case conversion should be applied otherwise, false. + /// A value of if upper case conversion should be applied otherwise, . /// public bool Uppercase { get; set; } diff --git a/src/NLog/LayoutRenderers/NewLineLayoutRenderer.cs b/src/NLog/LayoutRenderers/NewLineLayoutRenderer.cs index ae6d132e40..2c58e6c476 100644 --- a/src/NLog/LayoutRenderers/NewLineLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/NewLineLayoutRenderer.cs @@ -35,7 +35,6 @@ namespace NLog.LayoutRenderers { using System.Text; using NLog.Config; - using NLog.Internal; /// /// A newline literal. @@ -48,7 +47,7 @@ public class NewLineLayoutRenderer : LayoutRenderer /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { - builder.Append(EnvironmentHelper.NewLine); + builder.AppendLine(); } } } diff --git a/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs index 7aa9a757fb..3cdd5c5967 100644 --- a/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs @@ -64,7 +64,7 @@ public sealed class JsonEncodeLayoutRendererWrapper : WrapperLayoutRendererBase public bool EscapeUnicode { get; set; } = true; /// - /// Should forward slashes be escaped? If true, / will be converted to \/ + /// Should forward slashes be escaped? If , / will be converted to \/ /// /// /// If not set explicitly then the value of the parent will be used as default. diff --git a/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs index 702af64400..30e7863d5d 100644 --- a/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs @@ -55,7 +55,7 @@ public sealed class LowercaseLayoutRendererWrapper : WrapperLayoutRendererBase /// /// Gets or sets a value indicating whether lower case conversion should be applied. /// - /// A value of true if lower case conversion should be applied; otherwise, false. + /// A value of if lower case conversion should be applied; otherwise, . /// public bool Lowercase { get; set; } = true; diff --git a/src/NLog/LayoutRenderers/Wrappers/NoRawValueLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/NoRawValueLayoutRendererWrapper.cs index 59d5d4b7c2..44ffdd70e5 100644 --- a/src/NLog/LayoutRenderers/Wrappers/NoRawValueLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/NoRawValueLayoutRendererWrapper.cs @@ -50,7 +50,7 @@ public sealed class NoRawValueLayoutRendererWrapper : WrapperLayoutRendererBase /// /// Gets or sets a value indicating whether to disable the IRawValue-interface /// - /// A value of true if IRawValue-interface should be ignored; otherwise, false. + /// A value of if IRawValue-interface should be ignored; otherwise, . /// public bool NoRawValue { get; set; } = true; diff --git a/src/NLog/LayoutRenderers/Wrappers/PaddingLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/PaddingLayoutRendererWrapper.cs index f146226594..89b8b61dec 100644 --- a/src/NLog/LayoutRenderers/Wrappers/PaddingLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/PaddingLayoutRendererWrapper.cs @@ -78,7 +78,7 @@ public sealed class PaddingLayoutRendererWrapper : WrapperLayoutRendererBase /// /// Gets or sets a value indicating whether a value that has - /// been truncated (when is true) + /// been truncated (when is ) /// will be left-aligned (characters removed from the right) /// or right-aligned (characters removed from the left). The /// default is left alignment. diff --git a/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs index 39c7af0c9e..10e61f61a5 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs @@ -89,14 +89,14 @@ public string ReplaceWith /// /// Gets or sets a value indicating whether to ignore case. /// - /// A value of true if case should be ignored when searching; otherwise, false. + /// A value of if case should be ignored when searching; otherwise, . /// public bool IgnoreCase { get; set; } /// /// Gets or sets a value indicating whether to search for whole words /// - /// A value of true if whole words should be searched for; otherwise, false. + /// A value of if whole words should be searched for; otherwise, . /// public bool WholeWords { get; set; } diff --git a/src/NLog/LayoutRenderers/Wrappers/TrimWhiteSpaceLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/TrimWhiteSpaceLayoutRendererWrapper.cs index 19f7e9631e..9afe760f00 100644 --- a/src/NLog/LayoutRenderers/Wrappers/TrimWhiteSpaceLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/TrimWhiteSpaceLayoutRendererWrapper.cs @@ -54,7 +54,7 @@ public sealed class TrimWhiteSpaceLayoutRendererWrapper : WrapperLayoutRendererB /// /// Gets or sets a value indicating whether lower case conversion should be applied. /// - /// A value of true if lower case conversion should be applied; otherwise, false. + /// A value of if lower case conversion should be applied; otherwise, . /// public bool TrimWhiteSpace { get; set; } = true; diff --git a/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs index 521b9afb6e..d226380ccb 100644 --- a/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs @@ -60,7 +60,7 @@ public sealed class UppercaseLayoutRendererWrapper : WrapperLayoutRendererBase /// /// Gets or sets a value indicating whether upper case conversion should be applied. /// - /// A value of true if upper case conversion should be applied otherwise, false. + /// A value of if upper case conversion should be applied otherwise, . /// public bool Uppercase { get; set; } = true; diff --git a/src/NLog/LayoutRenderers/Wrappers/UrlEncodeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/UrlEncodeLayoutRendererWrapper.cs index e2870696e1..2dbeeac789 100644 --- a/src/NLog/LayoutRenderers/Wrappers/UrlEncodeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/UrlEncodeLayoutRendererWrapper.cs @@ -62,21 +62,21 @@ public UrlEncodeLayoutRendererWrapper() /// /// Gets or sets a value indicating whether spaces should be translated to '+' or '%20'. /// - /// A value of true if space should be translated to '+'; otherwise, false. + /// A value of if space should be translated to '+'; otherwise, . /// public bool SpaceAsPlus { get; set; } /// /// Gets or sets a value whether escaping be done according to Rfc3986 (Supports Internationalized Resource Identifiers - IRIs) /// - /// A value of true if Rfc3986; otherwise, false for legacy Rfc2396. + /// A value of if Rfc3986; otherwise, for legacy Rfc2396. /// public bool EscapeDataRfc3986 { get; set; } /// /// Gets or sets a value whether escaping be done according to the old NLog style (Very non-standard) /// - /// A value of true if legacy encoding; otherwise, false for standard UTF8 encoding. + /// A value of if legacy encoding; otherwise, for standard UTF8 encoding. /// [Obsolete("Instead use default Rfc2396 or EscapeDataRfc3986. Marked obsolete with NLog v5.3")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/NLog/LayoutRenderers/Wrappers/WrapLineLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WrapLineLayoutRendererWrapper.cs index 67ef7f2304..6018e29552 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WrapLineLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WrapLineLayoutRendererWrapper.cs @@ -83,7 +83,7 @@ protected override string Transform(string text) if (chunkLength + pos < text.Length) { - result.Append(Environment.NewLine); + result.AppendLine(); } } diff --git a/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs index 7ee5e71fe3..0758065c07 100644 --- a/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/XmlEncodeLayoutRendererWrapper.cs @@ -53,18 +53,20 @@ public sealed class XmlEncodeLayoutRendererWrapper : WrapperLayoutRendererBase /// /// Gets or sets whether output should be encoded with XML-string escaping. /// - /// Ensures always valid XML, but gives a performance hit + /// Default: /// public bool XmlEncode { get; set; } = true; /// /// Gets or sets whether output should be wrapped using CDATA section instead of XML-string escaping /// + /// Default: public bool CDataEncode { get; set; } = false; /// /// Gets or sets a value indicating whether to transform newlines (\r\n) into ( ) /// + /// Default: /// public bool XmlEncodeNewlines { get; set; } diff --git a/src/NLog/Layouts/CSV/CsvColumn.cs b/src/NLog/Layouts/CSV/CsvColumn.cs index dcac013e6a..de2c306642 100644 --- a/src/NLog/Layouts/CSV/CsvColumn.cs +++ b/src/NLog/Layouts/CSV/CsvColumn.cs @@ -67,12 +67,14 @@ public CsvColumn(string name, Layout layout) /// /// Gets or sets the name of the column. /// + /// [Required] Default: /// public string Name { get; set; } /// /// Gets or sets the layout used for rendering the column value. /// + /// [Required] Default: /// public Layout Layout { get; set; } @@ -80,7 +82,9 @@ public CsvColumn(string name, Layout layout) /// Gets or sets the override of Quoting mode /// /// - /// and are faster than the default + /// Default: . + /// + /// For faster performance then consider and /// /// public CsvQuotingMode Quoting { get => _quoting ?? CsvQuotingMode.Auto; set => _quoting = value; } diff --git a/src/NLog/Layouts/CSV/CsvLayout.cs b/src/NLog/Layouts/CSV/CsvLayout.cs index 9599513edb..99ca049caa 100644 --- a/src/NLog/Layouts/CSV/CsvLayout.cs +++ b/src/NLog/Layouts/CSV/CsvLayout.cs @@ -79,33 +79,37 @@ public CsvLayout() public IList Columns { get; } = new List(); /// - /// Gets or sets a value indicating whether CVS should include header. + /// Gets or sets a value indicating whether CSV should include header. /// - /// A value of true if CVS should include header; otherwise, false. + /// Default: /// public bool WithHeader { get; set; } = true; /// /// Gets or sets the column delimiter. /// + /// Default: /// public CsvColumnDelimiterMode Delimiter { get; set; } = CsvColumnDelimiterMode.Auto; /// /// Gets or sets the quoting mode. /// + /// Default: /// public CsvQuotingMode Quoting { get; set; } = CsvQuotingMode.Auto; /// /// Gets or sets the quote Character. /// + /// Default: " /// public string QuoteChar { get; set; } = "\""; /// /// Gets or sets the custom column delimiter value (valid when is set to ). /// + /// Default: /// public string CustomColumnDelimiter { get; set; } = string.Empty; diff --git a/src/NLog/Layouts/JSON/JsonArrayLayout.cs b/src/NLog/Layouts/JSON/JsonArrayLayout.cs index 7d494b92ee..a5b16dac37 100644 --- a/src/NLog/Layouts/JSON/JsonArrayLayout.cs +++ b/src/NLog/Layouts/JSON/JsonArrayLayout.cs @@ -61,14 +61,16 @@ public class JsonArrayLayout : Layout public IList Items { get; } = new List(); /// - /// Gets or sets the option to suppress the extra spaces in the output json. Default: true + /// Gets or sets the option to suppress the extra spaces in the output json. /// + /// Default: /// public bool SuppressSpaces { get; set; } = true; /// /// Gets or sets the option to render the empty object value {} /// + /// Default: /// public bool RenderEmptyObject { get; set; } = true; diff --git a/src/NLog/Layouts/JSON/JsonAttribute.cs b/src/NLog/Layouts/JSON/JsonAttribute.cs index 61214bc898..11ec3eb128 100644 --- a/src/NLog/Layouts/JSON/JsonAttribute.cs +++ b/src/NLog/Layouts/JSON/JsonAttribute.cs @@ -79,6 +79,7 @@ public JsonAttribute(string name, Layout layout, bool encode) /// /// Gets or sets the name of the attribute. /// + /// [Required] Default: /// public string Name { @@ -102,48 +103,51 @@ public string Name /// /// Gets or sets the layout used for rendering the attribute value. /// + /// [Required] Default: /// public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } /// /// Gets or sets the result value type, for conversion of layout rendering output /// + /// Default: /// public Type? ValueType { get => _layoutInfo.ValueType; set => _layoutInfo.ValueType = value; } /// /// Gets or sets the fallback value when result value is not available /// + /// Default: /// public Layout? DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } /// /// Gets or sets whether output should be encoded as Json-String-Property, or be treated as valid json. /// + /// Default: /// public bool Encode { get; set; } /// /// Gets or sets a value indicating whether to escape non-ascii characters /// + /// Default: /// public bool EscapeUnicode { get; set; } /// /// Should forward slashes be escaped? If true, / will be converted to \/ /// - /// - /// If not set explicitly then the value of the parent will be used as default. - /// + /// Default: /// [Obsolete("Marked obsolete since forward slash are valid JSON. Marked obsolete with NLog v5.4")] [EditorBrowsable(EditorBrowsableState.Never)] public bool EscapeForwardSlash { get; set; } /// - /// Gets or sets whether empty attribute value should be included in the output. Default = false + /// Gets or sets whether empty attribute value should be included in the output. /// - /// Empty value is either null or empty string + /// Default: . Empty value is either null or empty string /// public bool IncludeEmptyValue { diff --git a/src/NLog/Layouts/JSON/JsonLayout.cs b/src/NLog/Layouts/JSON/JsonLayout.cs index 00588b1e88..6b396f0cda 100644 --- a/src/NLog/Layouts/JSON/JsonLayout.cs +++ b/src/NLog/Layouts/JSON/JsonLayout.cs @@ -103,8 +103,9 @@ public JsonLayout() private readonly List _attributes = new List(); /// - /// Gets or sets the option to suppress the extra spaces in the output json. Default: true + /// Gets or sets the option to suppress the extra spaces in the output json. /// + /// Default: /// public bool SuppressSpaces { @@ -123,6 +124,7 @@ public bool SuppressSpaces /// /// Gets or sets the option to render the empty object value {} /// + /// Default: /// public bool RenderEmptyObject { get => _renderEmptyObject ?? true; set => _renderEmptyObject = value; } private bool? _renderEmptyObject; @@ -130,6 +132,7 @@ public bool SuppressSpaces /// /// Auto indent and create new lines /// + /// Default: /// public bool IndentJson { @@ -150,18 +153,21 @@ public bool IndentJson /// /// Gets or sets the option to include all properties from the log event (as JSON) /// + /// Default: /// public bool IncludeEventProperties { get; set; } /// /// Gets or sets a value indicating whether to include contents of the dictionary. /// + /// Default: /// public bool IncludeGdc { get; set; } /// /// Gets or sets whether to include the contents of the dictionary. /// + /// Default: /// public bool IncludeScopeProperties { get => _includeScopeProperties ?? (_includeMdlc == true || _includeMdc == true); set => _includeScopeProperties = value; } private bool? _includeScopeProperties; @@ -171,6 +177,7 @@ public bool IndentJson /// /// Gets or sets the option to include all properties from the log event (as JSON) /// + /// Default: /// [Obsolete("Replaced by IncludeEventProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] @@ -181,6 +188,7 @@ public bool IndentJson /// /// Gets or sets a value indicating whether to include contents of the dictionary. /// + /// Default: /// [Obsolete("Replaced by IncludeScopeProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] @@ -192,6 +200,7 @@ public bool IndentJson /// /// Gets or sets a value indicating whether to include contents of the dictionary. ///
+ /// Default: /// [Obsolete("Replaced by IncludeScopeProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] @@ -201,6 +210,7 @@ public bool IndentJson /// /// Gets or sets the option to exclude null/empty properties from the log event (as JSON) /// + /// Default: /// public bool ExcludeEmptyProperties { get; set; } @@ -217,15 +227,14 @@ public bool IndentJson /// /// How far should the JSON serializer follow object references before backing off /// + /// Default: /// public int MaxRecursionLimit { get; set; } = 1; /// - /// Should forward slashes be escaped? If true, / will be converted to \/ + /// Should forward slashes be escaped? If , / will be converted to \/ /// - /// - /// If not set explicitly then the value of the parent will be used as default. - /// + /// Default: /// [Obsolete("Marked obsolete with NLog 5.5. Should never escape forward slash")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/NLog/Layouts/Layout.cs b/src/NLog/Layouts/Layout.cs index ce07e633c5..06cc973925 100644 --- a/src/NLog/Layouts/Layout.cs +++ b/src/NLog/Layouts/Layout.cs @@ -122,7 +122,7 @@ public static Layout FromString([Localizable(false)] string layoutText, Configur /// Parses the specified string as LayoutRenderer-expression into a . ///
/// The layout string. - /// Whether should be thrown on parse errors (false = replace unrecognized tokens with a space). + /// Whether should be thrown on parse errors ( = replace unrecognized tokens with a space). /// Instance of . public static Layout FromString([Localizable(false)] string layoutText, bool throwConfigExceptions) { @@ -515,8 +515,8 @@ internal string ToStringWithNestedItems(IList nestedItems, Func /// Try get value ///
/// - /// rawValue if return result is true - /// false if we could not determine the rawValue + /// rawValue if return result is + /// if we could not determine the rawValue internal virtual bool TryGetRawValue(LogEventInfo logEvent, out object? rawValue) { rawValue = null; diff --git a/src/NLog/Layouts/SimpleLayout.cs b/src/NLog/Layouts/SimpleLayout.cs index 369534e226..0b74923de3 100644 --- a/src/NLog/Layouts/SimpleLayout.cs +++ b/src/NLog/Layouts/SimpleLayout.cs @@ -156,7 +156,7 @@ internal SimpleLayout(LayoutRenderer[] layoutRenderers, [Localizable(false)] str public bool IsFixedText => FixedText != null; /// - /// Get the fixed text. Only set when is true + /// Get the fixed text. Only set when is /// public string? FixedText { get; } diff --git a/src/NLog/Layouts/XML/XmlAttribute.cs b/src/NLog/Layouts/XML/XmlAttribute.cs index 9f3d4a2870..f3c31f86f0 100644 --- a/src/NLog/Layouts/XML/XmlAttribute.cs +++ b/src/NLog/Layouts/XML/XmlAttribute.cs @@ -79,6 +79,7 @@ public XmlAttribute(string name, Layout layout, bool encode) /// /// Gets or sets the name of the attribute. /// + /// [Required] Default: /// public string Name { get => _name; set => _name = XmlHelper.XmlConvertToElementName(value?.Trim() ?? string.Empty); } private string _name = string.Empty; @@ -86,31 +87,35 @@ public XmlAttribute(string name, Layout layout, bool encode) /// /// Gets or sets the layout used for rendering the attribute value. /// + /// [Required] Default: /// public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } /// /// Gets or sets the result value type, for conversion of layout rendering output /// + /// Default: /// public Type? ValueType { get => _layoutInfo.ValueType; set => _layoutInfo.ValueType = value; } /// /// Gets or sets the fallback value when result value is not available /// + /// Default: /// public Layout? DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } /// /// Gets or sets whether output should be encoded with Xml-string escaping, or be treated as valid xml-attribute-value /// + /// Default: /// public bool Encode { get; set; } /// - /// Gets or sets whether empty attribute value should be included in the output. Default = false + /// Gets or sets whether empty attribute value should be included in the output. /// - /// Empty value is either null or empty string + /// Default: . Empty value is either null or empty string /// public bool IncludeEmptyValue { diff --git a/src/NLog/Layouts/XML/XmlElement.cs b/src/NLog/Layouts/XML/XmlElement.cs index af11b7daf3..30bb55f54e 100644 --- a/src/NLog/Layouts/XML/XmlElement.cs +++ b/src/NLog/Layouts/XML/XmlElement.cs @@ -64,9 +64,7 @@ public XmlElement(string elementName, Layout elementValue) : base(elementName, e /// /// Name of the element /// - /// - /// Default value "item" - /// + /// [Required] Default: item /// public string Name { @@ -77,6 +75,7 @@ public string Name /// /// Value inside the element /// + /// Default: /// public Layout Value { @@ -87,12 +86,14 @@ public Layout Value /// /// Gets or sets the layout used for rendering the XML-element InnerText. /// + /// Default: /// public Layout Layout { get => Value; set => Value = value; } /// /// Gets or sets whether output should be encoded with XML-string escaping, or be treated as valid xml-element-value /// + /// Default: /// public bool Encode { @@ -103,6 +104,7 @@ public bool Encode /// /// Gets or sets whether output should be wrapped using CDATA section instead of XML-string escaping /// + /// Default: public bool CDataEncode { get => LayoutWrapper.CDataEncode; diff --git a/src/NLog/Layouts/XML/XmlElementBase.cs b/src/NLog/Layouts/XML/XmlElementBase.cs index 2d30be008f..b15f349f8f 100644 --- a/src/NLog/Layouts/XML/XmlElementBase.cs +++ b/src/NLog/Layouts/XML/XmlElementBase.cs @@ -83,6 +83,7 @@ protected XmlElementBase(string elementName, Layout elementValue) /// /// Auto indent and create new lines /// + /// Default: /// public bool IndentXml { get; set; } @@ -108,20 +109,23 @@ protected XmlElementBase(string elementName, Layout elementValue) public List? ContextProperties { get; set; } /// - /// Gets or sets whether empty XML-element should be included in the output. Default = false + /// Gets or sets whether empty XML-element should be included in the output. /// + /// Default: . Empty value is either null or empty string /// public bool IncludeEmptyValue { get; set; } /// /// Gets or sets the option to include all properties from the log event (as XML) /// + /// Default: /// public bool IncludeEventProperties { get; set; } /// /// Gets or sets whether to include the contents of the dictionary. /// + /// Default: /// public bool IncludeScopeProperties { get => _includeScopeProperties ?? (_includeMdlc == true || _includeMdc == true); set => _includeScopeProperties = value; } private bool? _includeScopeProperties; @@ -131,6 +135,7 @@ protected XmlElementBase(string elementName, Layout elementValue) /// /// Gets or sets a value indicating whether to include contents of the dictionary. /// + /// Default: /// [Obsolete("Replaced by IncludeScopeProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] @@ -142,6 +147,7 @@ protected XmlElementBase(string elementName, Layout elementValue) /// /// Gets or sets a value indicating whether to include contents of the dictionary. /// + /// Default: /// [Obsolete("Replaced by IncludeScopeProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] @@ -153,13 +159,14 @@ protected XmlElementBase(string elementName, Layout elementValue) /// /// Gets or sets the option to include all properties from the log event (as XML) /// + /// Default: /// [Obsolete("Replaced by IncludeEventProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] public bool IncludeAllProperties { get => IncludeEventProperties; set => IncludeEventProperties = value; } /// - /// List of property names to exclude when is true + /// List of property names to exclude when is /// /// #if !NET35 @@ -196,9 +203,7 @@ public string PropertiesElementName /// /// When null (or empty) then key-attribute is not included /// - /// - /// Will replace newlines in attribute-value with - /// + /// Default: key . Newlines in attribute-value will be replaced with /// public string PropertiesElementKeyAttribute { get; set; } = DefaultPropertyKeyAttribute; @@ -206,25 +211,23 @@ public string PropertiesElementName /// XML attribute name to use when rendering property-value /// /// When null (or empty) then value-attribute is not included and - /// value is formatted as XML-element-value + /// value is formatted as XML-element-value. /// - /// - /// Skips closing element tag when using attribute for value - /// - /// Will replace newlines in attribute-value with - /// + /// Default: . Newlines in attribute-value will be replaced with /// public string PropertiesElementValueAttribute { get; set; } = string.Empty; /// /// XML element name to use for rendering IList-collections items /// + /// Default: item /// public string PropertiesCollectionItemName { get; set; } = DefaultCollectionItemName; /// /// How far should the XML serializer follow object references before backing off /// + /// Default: /// public int MaxRecursionLimit { get; set; } = 1; diff --git a/src/NLog/Layouts/XML/XmlLayout.cs b/src/NLog/Layouts/XML/XmlLayout.cs index 27cd237aeb..b025e3a6dd 100644 --- a/src/NLog/Layouts/XML/XmlLayout.cs +++ b/src/NLog/Layouts/XML/XmlLayout.cs @@ -68,9 +68,7 @@ public XmlLayout(string elementName, Layout elementValue) : base(elementName, el /// /// Name of the root XML element /// - /// - /// Default value "logevent" - /// + /// [Required] Default: logevent /// public string ElementName { @@ -81,6 +79,7 @@ public string ElementName /// /// Value inside the root XML element /// + /// Default: /// public Layout ElementValue { @@ -91,6 +90,7 @@ public Layout ElementValue /// /// Determines whether or not this attribute will be Xml encoded. /// + /// Default: /// public bool ElementEncode { diff --git a/src/NLog/LogFactory.cs b/src/NLog/LogFactory.cs index f54043c7ad..72336b1ceb 100644 --- a/src/NLog/LogFactory.cs +++ b/src/NLog/LogFactory.cs @@ -172,7 +172,7 @@ internal static IAppEnvironment DefaultAppEnvironment /// /// Gets or sets a value indicating whether exceptions should be thrown. See also . /// - /// A value of true if exception should be thrown; otherwise, false. + /// A value of if exception should be thrown; otherwise, . /// By default exceptions are not thrown under any circumstances. public bool ThrowExceptions { get; set; } @@ -181,7 +181,7 @@ internal static IAppEnvironment DefaultAppEnvironment /// /// If null then is used. /// - /// A value of true if exception should be thrown; otherwise, false. + /// A value of if exception should be thrown; otherwise, . /// /// This option is for backwards-compatibility. /// By default exceptions are not thrown under any circumstances. @@ -863,8 +863,8 @@ private void CloseOldConfig(LoggingConfiguration oldConfig) /// /// Shutdown logging without flushing async /// - /// True to release both managed and unmanaged resources; - /// false to release only unmanaged resources. + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (disposing) @@ -1105,29 +1105,16 @@ public LoggerCacheKey(string name, Type concreteType) ConcreteType = concreteType; } - /// - /// Serves as a hash function for a particular type. - /// public override int GetHashCode() { return ConcreteType.GetHashCode() ^ Name.GetHashCode(); } - /// - /// Determines if two objects are equal in value. - /// - /// Other object to compare to. - /// True if objects are equal, false otherwise. public override bool Equals(object obj) { return obj is LoggerCacheKey key && Equals(key); } - /// - /// Determines if two objects of the same type are equal in value. - /// - /// Other object to compare to. - /// True if objects are equal, false otherwise. public bool Equals(LoggerCacheKey other) { return (ConcreteType == other.ConcreteType) && string.Equals(other.Name, Name, StringComparison.Ordinal); diff --git a/src/NLog/LogLevel.cs b/src/NLog/LogLevel.cs index cb46634d66..260b967e29 100644 --- a/src/NLog/LogLevel.cs +++ b/src/NLog/LogLevel.cs @@ -369,8 +369,8 @@ public override bool Equals(object? obj) /// Determines whether the specified instance is equal to this instance. /// /// The to compare with this instance. - /// Value of true if the specified is equal to - /// this instance; otherwise, false. + /// Value of if the specified is equal to + /// this instance; otherwise, . public bool Equals(LogLevel? other) { return _ordinal == other?._ordinal; diff --git a/src/NLog/LogManager.cs b/src/NLog/LogManager.cs index 58e98bc089..18ee4fa998 100644 --- a/src/NLog/LogManager.cs +++ b/src/NLog/LogManager.cs @@ -90,7 +90,7 @@ public static bool ThrowExceptions /// /// Gets or sets a value indicating whether should be thrown. /// - /// A value of true if exception should be thrown; otherwise, false. + /// A value of if exception should be thrown; otherwise, . /// /// This option is for backwards-compatibility. /// By default exceptions are not thrown under any circumstances. diff --git a/src/NLog/ScopeContext.cs b/src/NLog/ScopeContext.cs index 32d326c298..ecfbc0c659 100644 --- a/src/NLog/ScopeContext.cs +++ b/src/NLog/ScopeContext.cs @@ -273,7 +273,7 @@ public static void Clear() /// /// Name of property /// When this method returns, contains the value associated with the specified key - /// Returns true when value is found with the specified key + /// Returns when value is found with the specified key /// Scope dictionary keys are case-insensitive public static bool TryGetProperty(string key, out object? value) { diff --git a/src/NLog/SetupSerializationBuilderExtensions.cs b/src/NLog/SetupSerializationBuilderExtensions.cs index 6cfa9bb65f..6e55d37bce 100644 --- a/src/NLog/SetupSerializationBuilderExtensions.cs +++ b/src/NLog/SetupSerializationBuilderExtensions.cs @@ -46,9 +46,9 @@ public static class SetupSerializationBuilderExtensions { /// /// Enable/disables the NLog Message Template Parsing: - /// - True = Always use NLog mesage-template-parser and formatting. - /// - False = Never use NLog-parser and only use string.Format (Disable support for message-template-syntax). - /// - Null = Auto detection of message-template-syntax, with fallback to string.Format (Default Behavior). + /// - = Always use NLog mesage-template-parser and formatting. + /// - = Never use NLog-parser and only use string.Format (Disable support for message-template-syntax). + /// - = Auto detection of message-template-syntax, with fallback to string.Format (Default Behavior). /// public static ISetupSerializationBuilder ParseMessageTemplates(this ISetupSerializationBuilder setupBuilder, bool? enable) { diff --git a/src/NLog/Targets/AsyncTaskTarget.cs b/src/NLog/Targets/AsyncTaskTarget.cs index fa54723c44..cfd84049ca 100644 --- a/src/NLog/Targets/AsyncTaskTarget.cs +++ b/src/NLog/Targets/AsyncTaskTarget.cs @@ -92,24 +92,28 @@ public abstract class AsyncTaskTarget : TargetWithContext /// /// How many milliseconds to delay the actual write operation to optimize for batching /// + /// Default: /// public int TaskDelayMilliseconds { get; set; } = 1; /// /// How many seconds a Task is allowed to run before it is cancelled. /// + /// Default: /// public int TaskTimeoutSeconds { get; set; } = 150; /// /// How many attempts to retry the same Task, before it is aborted /// + /// Default: /// public int RetryCount { get; set; } /// /// How many milliseconds to wait before next retry (will double with each retry) /// + /// Default: ms /// public int RetryDelayMilliseconds { get => _retryDelayMilliseconds ?? ((RetryCount > 0 || OverflowAction != AsyncTargetWrapperOverflowAction.Discard) ? 500 : 50); set => _retryDelayMilliseconds = value; } private int? _retryDelayMilliseconds; @@ -118,6 +122,7 @@ public abstract class AsyncTaskTarget : TargetWithContext /// Gets or sets whether to use the locking queue, instead of a lock-free concurrent queue /// The locking queue is less concurrent when many logger threads, but reduces memory allocation /// + /// Default: /// public bool ForceLockingQueue { get => _forceLockingQueue ?? false; set => _forceLockingQueue = value; } private bool? _forceLockingQueue; @@ -126,6 +131,7 @@ public abstract class AsyncTaskTarget : TargetWithContext /// Gets or sets the action to be taken when the lazy writer thread request queue count /// exceeds the set limit. /// + /// Default: /// public AsyncTargetWrapperOverflowAction OverflowAction { @@ -136,6 +142,7 @@ public AsyncTargetWrapperOverflowAction OverflowAction /// /// Gets or sets the limit on the number of requests in the lazy writer thread request queue. /// + /// Default: /// public int QueueLimit { @@ -147,12 +154,14 @@ public int QueueLimit /// Gets or sets the number of log events that should be processed in a batch /// by the lazy writer thread. /// + /// Default: /// public int BatchSize { get; set; } = 1; /// /// Task Scheduler used for processing async Tasks /// + /// Default: protected virtual TaskScheduler TaskScheduler => TaskScheduler.Default; /// diff --git a/src/NLog/Targets/ColoredConsoleTarget.cs b/src/NLog/Targets/ColoredConsoleTarget.cs index c9657cfe13..463070791b 100644 --- a/src/NLog/Targets/ColoredConsoleTarget.cs +++ b/src/NLog/Targets/ColoredConsoleTarget.cs @@ -127,7 +127,7 @@ public ColoredConsoleTarget(string name) : this() /// Gets or sets a value indicating whether to use default row highlighting rules. /// /// - /// The default rules are: + /// Default: which enables the following rules: /// /// /// @@ -187,16 +187,18 @@ public Encoding Encoding /// /// Gets or sets a value indicating whether to auto-check if the console is available. - /// - Disables console writing if Environment.UserInteractive = False (Windows Service) + /// - Disables console writing if Environment.UserInteractive = (Windows Service) /// - Disables console writing if Console Standard Input is not available (Non-Console-App) /// + /// Default: /// public bool DetectConsoleAvailable { get; set; } /// /// Gets or sets a value indicating whether to auto-check if the console has been redirected to file - /// - Disables coloring logic when System.Console.IsOutputRedirected = true + /// - Disables coloring logic when System.Console.IsOutputRedirected = /// + /// Default: /// public bool DetectOutputRedirected { get; set; } @@ -204,6 +206,7 @@ public Encoding Encoding /// Gets or sets a value indicating whether to auto-flush after /// /// + /// Default: . /// Normally not required as standard Console.Out will have = true, but not when pipe to file /// /// @@ -212,12 +215,14 @@ public Encoding Encoding /// /// Enables output using ANSI Color Codes /// + /// Default: /// public bool EnableAnsiOutput { get; set; } /// /// Support NO_COLOR=1 environment variable. See also /// + /// Default: NO_COLOR=1 /// public Layout NoColor { get; set; } = Layout.FromMethod((evt) => new string[] { "1", "TRUE" }.Contains(NLog.Internal.EnvironmentHelper.GetSafeEnvironmentVariable("NO_COLOR")?.Trim().ToUpper())); diff --git a/src/NLog/Targets/ConsoleRowHighlightingRule.cs b/src/NLog/Targets/ConsoleRowHighlightingRule.cs index 85ebb79bb5..fc5a25f126 100644 --- a/src/NLog/Targets/ConsoleRowHighlightingRule.cs +++ b/src/NLog/Targets/ConsoleRowHighlightingRule.cs @@ -70,18 +70,21 @@ public ConsoleRowHighlightingRule(ConditionExpression? condition, ConsoleOutputC /// /// Gets or sets the condition that must be met in order to set the specified foreground and background color. /// + /// Default: /// public ConditionExpression? Condition { get; set; } /// /// Gets or sets the foreground color. /// + /// Default: /// public ConsoleOutputColor ForegroundColor { get; set; } = ConsoleOutputColor.NoChange; /// /// Gets or sets the background color. /// + /// Default: /// public ConsoleOutputColor BackgroundColor { get; set; } = ConsoleOutputColor.NoChange; diff --git a/src/NLog/Targets/ConsoleTarget.cs b/src/NLog/Targets/ConsoleTarget.cs index 59482bce98..d10385dd97 100644 --- a/src/NLog/Targets/ConsoleTarget.cs +++ b/src/NLog/Targets/ConsoleTarget.cs @@ -100,7 +100,6 @@ public sealed class ConsoleTarget : TargetWithLayoutHeaderAndFooter /// /// The encoding for writing messages to the . /// - /// Has side effect /// public Encoding Encoding { @@ -115,16 +114,17 @@ public Encoding Encoding /// /// Gets or sets a value indicating whether to auto-check if the console is available - /// - Disables console writing if Environment.UserInteractive = False (Windows Service) + /// - Disables console writing if Environment.UserInteractive = (Windows Service) /// - Disables console writing if Console Standard Input is not available (Non-Console-App) /// + /// Default: /// public bool DetectConsoleAvailable { get; set; } /// /// Gets or sets a value indicating whether to auto-flush after /// - /// + /// Default: . /// Normally not required as standard Console.Out will have = true, but not when pipe to file /// /// @@ -133,12 +133,14 @@ public Encoding Encoding /// /// Gets or sets whether to force (slower) instead of the faster internal buffering. /// + /// Default: /// public bool ForceWriteLine { get; set; } /// /// Gets or sets whether to activate internal buffering to allow batch writing, instead of using /// + /// Default: /// [Obsolete("Replaced by ForceWriteLine. Marked obsolete with NLog v6.0")] public bool WriteBuffer { get => !ForceWriteLine; set => ForceWriteLine = !value; } diff --git a/src/NLog/Targets/ConsoleWordHighlightingRule.cs b/src/NLog/Targets/ConsoleWordHighlightingRule.cs index b0322817e8..82f1792fb0 100644 --- a/src/NLog/Targets/ConsoleWordHighlightingRule.cs +++ b/src/NLog/Targets/ConsoleWordHighlightingRule.cs @@ -67,12 +67,14 @@ public ConsoleWordHighlightingRule(string text, ConsoleOutputColor foregroundCol /// /// Gets or sets the condition that must be met before scanning the row for highlight of words /// + /// Default: /// public ConditionExpression? Condition { get; set; } /// /// Gets or sets the text to be matched. You must specify either text or regex. /// + /// Default: /// public string Text { get => _text; set => _text = string.IsNullOrEmpty(value) ? string.Empty : value; } private string _text = string.Empty; @@ -80,24 +82,28 @@ public ConsoleWordHighlightingRule(string text, ConsoleOutputColor foregroundCol /// /// Gets or sets a value indicating whether to match whole words only. /// + /// Default: /// public bool WholeWords { get; set; } /// /// Gets or sets a value indicating whether to ignore case when comparing texts. /// + /// Default: /// public bool IgnoreCase { get; set; } /// /// Gets or sets the foreground color. /// + /// Default: /// public ConsoleOutputColor ForegroundColor { get; set; } = ConsoleOutputColor.NoChange; /// /// Gets or sets the background color. /// + /// Default: /// public ConsoleOutputColor BackgroundColor { get; set; } = ConsoleOutputColor.NoChange; diff --git a/src/NLog/Targets/EventLogTarget.cs b/src/NLog/Targets/EventLogTarget.cs index 294716b532..6a2ae5bbdd 100644 --- a/src/NLog/Targets/EventLogTarget.cs +++ b/src/NLog/Targets/EventLogTarget.cs @@ -101,24 +101,28 @@ internal EventLogTarget(IEventLogWrapper eventLogWrapper, string sourceName) /// /// Gets or sets the name of the machine on which Event Log service is running. /// + /// Default: /// public Layout MachineName { get; set; } = Layout.Empty; /// /// Gets or sets the layout that renders event ID. /// + /// Default: ${event-properties:item=EventId} /// public Layout EventId { get; set; } = "${event-properties:item=EventId}"; /// /// Gets or sets the layout that renders event Category. /// + /// Default: /// public Layout? Category { get; set; } /// /// Optional entry type. When not set, or when not convertible to then determined by /// + /// Default: /// public Layout? EntryType { get; set; } @@ -126,7 +130,7 @@ internal EventLogTarget(IEventLogWrapper eventLogWrapper, string sourceName) /// Gets or sets the value to be used as the event Source. /// /// - /// By default this is the friendly name of the current AppDomain. + /// [Required] Default: /// /// public Layout Source { get; set; } = Layout.Empty; @@ -134,12 +138,14 @@ internal EventLogTarget(IEventLogWrapper eventLogWrapper, string sourceName) /// /// Gets or sets the name of the Event Log to write to. This can be System, Application or any user-defined name. /// + /// [Required] Default: Application /// public Layout Log { get; set; } = "Application"; /// /// Gets or sets the message length limit to write to the Event Log. /// + /// Default: /// public Layout MaxMessageLength { get; set; } = EventLogMaxMessageLength; @@ -156,6 +162,7 @@ internal EventLogTarget(IEventLogWrapper eventLogWrapper, string sourceName) /// /// Gets or sets the action to take if the message is larger than the option. /// + /// Default: /// public EventLogTargetOverflowAction OnOverflow { get; set; } = EventLogTargetOverflowAction.Truncate; diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index a412cee6ba..f6ad11dc0c 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -61,8 +61,9 @@ public class FileTarget : TargetWithLayoutHeaderAndFooter /// Gets or sets the name of the file to write to. /// /// - /// This FileName string is a layout which may include instances of layout renderers. - /// This lets you use a single target to write to multiple files. + /// [Required] Default: . + /// When not absolute path then relative path will be resolved against . + /// The FileName Layout supports layout-renderers, where a single FileTarget can write to multiple files. /// /// /// The following value makes NLog write logging events to files based on the log level in the directory where @@ -88,8 +89,9 @@ public Layout FileName /// Gets or sets a value indicating whether to create directories if they do not exist. /// /// - /// Setting this to false may improve performance a bit, but you'll receive an error - /// when attempting to write to a directory that's not present. + /// Default: . + /// Setting this to may improve performance a bit, but will always fail + /// when attempt writing to a non-existing directory. /// /// public bool CreateDirs { get; set; } = true; @@ -98,6 +100,7 @@ public Layout FileName /// Gets or sets a value indicating whether to delete old log file on startup. /// /// + /// Default: . /// When current log-file exists, then it is deleted (and resetting sequence number) /// /// @@ -106,6 +109,7 @@ public Layout FileName /// /// Gets or sets a value indicating whether to replace file contents on each write instead of appending log message at the end. /// + /// Default: /// public bool ReplaceFileContentsOnEachWrite { get; set; } @@ -113,8 +117,9 @@ public Layout FileName /// Gets or sets a value indicating whether to keep log file open instead of opening and closing it on each logging event. /// /// - /// KeepFileOpen = true gives the best performance, and ensure the file-lock is not lost to other applications.
- /// KeepFileOpen = false gives the best compability, but slow performance and lead to file-locking issues with other applications. + /// Default: . + /// KeepFileOpen = gives the best performance, and ensure the file-lock is not lost to other applications.
+ /// KeepFileOpen = gives the best compatibility, but slow performance and lead to file-locking issues with other applications. ///
/// public bool KeepFileOpen { get; set; } = true; @@ -122,32 +127,33 @@ public Layout FileName /// /// Gets or sets a value indicating whether to enable log file(s) to be deleted. /// + /// Default: /// public bool EnableFileDelete { get; set; } = true; /// /// Gets or sets the line ending mode. /// + /// Default: /// public LineEndingMode LineEnding { get; set; } = LineEndingMode.Default; /// /// Gets or sets a value indicating whether to automatically flush the file buffers after each log message. /// + /// Default: /// public bool AutoFlush { get; set; } = true; /// - /// Gets or sets the number of files to be kept open. Setting this to a higher value may improve performance - /// in a situation where a single File target is writing to many files - /// (such as splitting by level or by logger). + /// Gets or sets the maximum number of files to be kept open. /// /// - /// The files are managed on a LRU (least recently used) basis, which flushes - /// the files that have not been used for the longest period of time should the - /// cache become full. As a rule of thumb, you shouldn't set this parameter to - /// a very high value. A number like 10-15 shouldn't be exceeded, because you'd - /// be keeping a large number of files open which consumes system resources. + /// Default: . Higher number might improve performance when single FileTarget + /// is writing to many files (such as splitting by loglevel or by logger-name). + /// Files are closed in FIFO (First in First out) ordering, so the oldest + /// file-handle is closed first. Careful with number higher than 10-15, + /// because a large number of open files consumes system resources. /// /// public int OpenFileCacheSize { get; set; } = 5; @@ -155,24 +161,28 @@ public Layout FileName /// /// Gets or sets the maximum number of seconds that files are kept open. Zero or negative means disabled. /// + /// Default: /// public int OpenFileCacheTimeout { get; set; } /// /// Gets or sets the maximum number of seconds before open files are flushed. Zero or negative means disabled. /// + /// Default: /// public int OpenFileFlushTimeout { get; set; } /// /// Gets or sets the log file buffer size in bytes. /// + /// Default: /// public int BufferSize { get; set; } = 32768; /// /// Gets or sets the file encoding. /// + /// Default: /// public Encoding Encoding { @@ -190,14 +200,14 @@ public Encoding Encoding /// Gets or sets whether or not this target should just discard all data that its asked to write. /// Mostly used for when testing NLog Stack except final write /// + /// Default: /// public bool DiscardAll { get; set; } /// /// Gets or sets a value indicating whether to write BOM (byte order mark) in created files. - /// - /// Defaults to true for UTF-16 and UTF-32 /// + /// Default: (Unless UTF16 / UTF32) /// public bool WriteBom { @@ -207,11 +217,9 @@ public bool WriteBom private bool? _writeBom; /// - /// Gets or sets a value indicating whether to archive old log file on startup. + /// Gets or sets a value indicating whether any existing log-file should be archived on startup. /// - /// - /// When current log-file exists, then roll to the next sequence number - /// + /// Default: /// public bool ArchiveOldFileOnStartup { get; set; } @@ -220,6 +228,7 @@ public bool WriteBom /// Default value is , which means only write header when initial file is empty (Ex. ensures valid CSV files) /// /// + /// Default: . /// Alternative use to ensure each application session gets individual log-file. /// /// @@ -229,6 +238,7 @@ public bool WriteBom /// Gets or sets a value specifying the date format when using . /// Obsolete and only here for Legacy reasons, instead use . /// + /// Default: /// [Obsolete("Instead use ArchiveSuffixFormat. Marked obsolete with NLog 6.0")] public string? ArchiveDateFormat @@ -247,8 +257,9 @@ public string? ArchiveDateFormat private string? _archiveDateFormat; /// - /// Gets or sets the size in bytes above which log files will be automatically archived. + /// Gets or sets the size in bytes above which log files will be automatically archived. Zero or negative means disabled. /// + /// Default: /// public long ArchiveAboveSize { @@ -265,6 +276,7 @@ public long ArchiveAboveSize /// Gets or sets a value indicating whether to trigger archive operation based on time-period, by moving active-file to file-path specified by /// /// + /// Default: . /// Archive move operation only works if is static in nature, and not rolling automatically because of ${date} or ${shortdate} /// /// NLog FileTarget probes the file-birthtime to recognize when time-period has passed, but file-birthtime is not supported by all filesystems. @@ -287,6 +299,7 @@ public FileArchivePeriod ArchiveEvery /// Use to control suffix format, instead of now obsolete token {#} /// /// + /// Default: . /// Archive file-move operation only works if is static in nature, and not rolling automatically because of ${date} or ${shortdate} . /// /// Legacy archive file-move operation can fail because of file-locks, so file-archiving can stop working because of environment issues (Other applications locking files). @@ -331,8 +344,9 @@ public Layout? ArchiveFileName private static readonly string _legacySequenceArchiveSuffixFormat = "_{0:00}"; // Cater for ArchiveNumbering.Sequence /// - /// Gets or sets the maximum number of archive files that should be kept. + /// Gets or sets the maximum number of archive files that should be kept. Negative means disabled. /// + /// Default: /// public int MaxArchiveFiles { @@ -346,8 +360,9 @@ public int MaxArchiveFiles private int _maxArchiveFiles = -1; /// - /// Gets or sets the maximum days of archive files that should be kept. + /// Gets or sets the maximum days of archive files that should be kept. Zero or negative means disabled. /// + /// Default: /// public int MaxArchiveDays { @@ -364,6 +379,7 @@ public int MaxArchiveDays /// Gets or sets the way file archives are numbered. /// Obsolete and only here for Legacy reasons, instead use . /// + /// Default: Sequence /// [Obsolete("Instead use ArchiveSuffixFormat. Marked obsolete with NLog 6.0")] public string ArchiveNumbering @@ -390,9 +406,10 @@ public string ArchiveNumbering /// Gets or sets the format-string to convert archive sequence-number by using string.Format /// /// - /// Ex. to prefix with leading zero's then one can use _{0:000} . + /// Default: _{0:00} . + /// Ex. to prefix archive sequence number with leading zero's then one can use _{0:000} . /// - /// Legacy archive-logic with uses default suffix _{1:yyyyMMdd}_{0:00} . + /// Legacy archive-logic with can use suffix _{1:yyyyMMdd}_{0:00} . /// /// public string ArchiveSuffixFormat @@ -437,6 +454,7 @@ public string ArchiveSuffixFormat /// /// Gets or sets a value indicating whether the footer should be written only when the file is archived. /// + /// Default: /// public bool WriteFooterOnArchivingOnly { get; set; } diff --git a/src/NLog/Targets/JsonSerializeOptions.cs b/src/NLog/Targets/JsonSerializeOptions.cs index 1a01f66b3e..bff64fe168 100644 --- a/src/NLog/Targets/JsonSerializeOptions.cs +++ b/src/NLog/Targets/JsonSerializeOptions.cs @@ -44,6 +44,7 @@ public class JsonSerializeOptions /// /// Add quotes around object keys? /// + /// Default: [Obsolete("Marked obsolete with NLog 5.0.")] [EditorBrowsable(EditorBrowsableState.Never)] public bool QuoteKeys { get; set; } = true; @@ -51,6 +52,7 @@ public class JsonSerializeOptions /// /// Format provider for value /// + /// Default: [Obsolete("Marked obsolete with NLog 5.0. Should always be InvariantCulture.")] [EditorBrowsable(EditorBrowsableState.Never)] public IFormatProvider? FormatProvider { get; set; } @@ -58,30 +60,35 @@ public class JsonSerializeOptions /// /// Format string for value /// + /// Default: [Obsolete("Marked obsolete with NLog 5.0. Should always be InvariantCulture.")] [EditorBrowsable(EditorBrowsableState.Never)] public string? Format { get; set; } /// - /// Should non-ascii characters be encoded. Default: false + /// Should non-ascii characters be encoded. /// + /// Default: public bool EscapeUnicode { get; set; } /// - /// Should forward slashes be escaped? If true, / will be converted to \/ + /// Should forward slashes be escaped? If , / will be converted to \/ /// + /// Default: [Obsolete("Marked obsolete with NLog 5.5. Should never escape forward slash")] [EditorBrowsable(EditorBrowsableState.Never)] public bool EscapeForwardSlash { get; set; } /// - /// Serialize enum as string value. Default: false + /// Serialize enum as integer value. /// + /// Default: public bool EnumAsInteger { get; set; } /// - /// Gets or sets the option to suppress the extra spaces in the output json. Default: true + /// Gets or sets the option to suppress the extra spaces in the output json. /// + /// Default: public bool SuppressSpaces { get; set; } = true; /// @@ -89,11 +96,13 @@ public class JsonSerializeOptions /// /// Any other characters will be converted to underscore character (_) /// + /// Default: public bool SanitizeDictionaryKeys { get; set; } /// /// How far down the rabbit hole should the Json Serializer go with object-reflection before stopping /// + /// Default: public int MaxRecursionLimit { get; set; } = 10; } } diff --git a/src/NLog/Targets/LineEndingMode.cs b/src/NLog/Targets/LineEndingMode.cs index 1d8e8625cf..7b6cef07eb 100644 --- a/src/NLog/Targets/LineEndingMode.cs +++ b/src/NLog/Targets/LineEndingMode.cs @@ -45,9 +45,9 @@ namespace NLog.Targets public sealed class LineEndingMode : IEquatable { /// - /// Insert platform-dependent end-of-line sequence after each line. + /// Insert platform-dependent sequence after each line. /// - public static readonly LineEndingMode Default = new LineEndingMode("Default", EnvironmentHelper.NewLine); + public static readonly LineEndingMode Default = new LineEndingMode("Default", Environment.NewLine); /// /// Insert CR LF sequence (ASCII 13, ASCII 10) after each line. @@ -191,7 +191,7 @@ public override bool Equals(object obj) } /// Indicates whether the current object is equal to another object of the same type. - /// true if the current object is equal to the parameter; otherwise, false. + /// if the current object is equal to the parameter; otherwise, . /// An object to compare with this object. public bool Equals(LineEndingMode other) { diff --git a/src/NLog/Targets/MemoryTarget.cs b/src/NLog/Targets/MemoryTarget.cs index b0ecb02ec6..549f1bf9e4 100644 --- a/src/NLog/Targets/MemoryTarget.cs +++ b/src/NLog/Targets/MemoryTarget.cs @@ -89,8 +89,9 @@ public MemoryTarget(string name) : this() public IList Logs => _logs; /// - /// Gets or sets the max number of items to have in memory + /// Gets or sets the max number of items to have in memory. Zero or Negative means no limit. /// + /// Default: 0 /// public int MaxLogsCount { get; set; } diff --git a/src/NLog/Targets/MethodCallParameter.cs b/src/NLog/Targets/MethodCallParameter.cs index cbe1e6c6ee..db06bee798 100644 --- a/src/NLog/Targets/MethodCallParameter.cs +++ b/src/NLog/Targets/MethodCallParameter.cs @@ -92,12 +92,14 @@ public MethodCallParameter(string name, Layout layout, Type type) /// /// Gets or sets the name of the parameter. /// + /// [Required] Default: /// public string Name { get; set; } = string.Empty; /// /// Gets or sets the layout used for rendering the method-parameter value. /// + /// [Required] Default: /// public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } @@ -105,6 +107,7 @@ public MethodCallParameter(string name, Layout layout, Type type) /// Obsolete and replaced by with NLog v4.6. /// Gets or sets the type of the parameter. Obsolete alias for /// + /// Default: typeof(string) /// [Obsolete("Use property ParameterType instead. Marked obsolete on NLog 4.6")] [EditorBrowsable(EditorBrowsableState.Never)] @@ -113,12 +116,14 @@ public MethodCallParameter(string name, Layout layout, Type type) /// /// Gets or sets the type of the parameter. /// + /// Default: typeof(string) /// public Type ParameterType { get => _layoutInfo.ValueType ?? typeof(string); set => _layoutInfo.ValueType = value; } /// /// Gets or sets the fallback value when result value is not available /// + /// Default: /// public Layout? DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } diff --git a/src/NLog/Targets/MethodCallTarget.cs b/src/NLog/Targets/MethodCallTarget.cs index d19c8918dc..1a79b75745 100644 --- a/src/NLog/Targets/MethodCallTarget.cs +++ b/src/NLog/Targets/MethodCallTarget.cs @@ -65,14 +65,15 @@ public sealed class MethodCallTarget : MethodCallTargetBase /// Gets or sets the class name. /// /// + /// Default: public string ClassName { get; set; } = string.Empty; /// /// Gets or sets the method name. The method must be public and static. /// - /// Use the AssemblyQualifiedName , https://msdn.microsoft.com/en-us/library/system.type.assemblyqualifiedname(v=vs.110).aspx - /// e.g. + /// Use the AssemblyQualifiedName - /// + /// Default: /// public string MethodName { get; set; } = string.Empty; diff --git a/src/NLog/Targets/NullTarget.cs b/src/NLog/Targets/NullTarget.cs index a08aab8ad3..e5bde8b5f4 100644 --- a/src/NLog/Targets/NullTarget.cs +++ b/src/NLog/Targets/NullTarget.cs @@ -57,6 +57,7 @@ public sealed class NullTarget : TargetWithLayout /// /// Gets or sets a value indicating whether to perform layout calculation. /// + /// Default: /// public bool FormatMessage { get; set; } diff --git a/src/NLog/Targets/Target.cs b/src/NLog/Targets/Target.cs index f8117aad0d..40ec07afd8 100644 --- a/src/NLog/Targets/Target.cs +++ b/src/NLog/Targets/Target.cs @@ -69,6 +69,7 @@ public abstract class Target : ISupportsInitialize, IInternalLoggerContext, IDis /// /// Gets or sets the name of the target. /// + /// [Required] Default: /// public string Name { @@ -516,7 +517,7 @@ internal void Close() /// /// Releases unmanaged and - optionally - managed resources. /// - /// True to release both managed and unmanaged resources; false to release only unmanaged resources. + /// to release both managed and unmanaged resources; to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (disposing && _isInitialized) diff --git a/src/NLog/Targets/TargetPropertyWithContext.cs b/src/NLog/Targets/TargetPropertyWithContext.cs index 298df99d04..76efd31d03 100644 --- a/src/NLog/Targets/TargetPropertyWithContext.cs +++ b/src/NLog/Targets/TargetPropertyWithContext.cs @@ -64,31 +64,35 @@ public TargetPropertyWithContext(string name, Layout layout) /// /// Gets or sets the name of the property. /// + /// [Required] Default: /// public string Name { get; set; } /// /// Gets or sets the layout used for rendering the property value. /// + /// [Required] Default: /// public Layout Layout { get => _layoutInfo.Layout; set => _layoutInfo.Layout = value; } /// /// Gets or sets the type of the property. /// + /// Default: /// public Type PropertyType { get => _layoutInfo.ValueType ?? typeof(string); set => _layoutInfo.ValueType = value; } /// /// Gets or sets the fallback value when result value is not available /// + /// Default: /// public Layout? DefaultValue { get => _layoutInfo.DefaultValue; set => _layoutInfo.DefaultValue = value; } /// - /// Gets or sets whether empty property value should be included in the output. Default = false + /// Gets or sets whether empty property value should be included in the output. /// - /// Empty value is either null or empty string + /// Default: . Empty value is either null or empty string /// public bool IncludeEmptyValue { diff --git a/src/NLog/Targets/TargetWithContext.cs b/src/NLog/Targets/TargetWithContext.cs index 742fd3e4cf..16335ebc1f 100644 --- a/src/NLog/Targets/TargetWithContext.cs +++ b/src/NLog/Targets/TargetWithContext.cs @@ -92,18 +92,21 @@ public sealed override Layout Layout /// /// Gets or sets the option to include all properties from the log events /// + /// Default: /// public bool IncludeEventProperties { get => _contextLayout.IncludeEventProperties; set => _contextLayout.IncludeEventProperties = value; } /// /// Gets or sets whether to include the contents of the properties-dictionary. /// + /// Default: /// public bool IncludeScopeProperties { get => _contextLayout.IncludeScopeProperties; set => _contextLayout.IncludeScopeProperties = value; } /// /// Gets or sets whether to include the contents of the nested-state-stack. /// + /// Default: /// public bool IncludeScopeNested { get => _contextLayout.IncludeScopeNested; set => _contextLayout.IncludeScopeNested = value; } @@ -111,6 +114,7 @@ public sealed override Layout Layout /// Obsolete and replaced by with NLog v5. /// Gets or sets whether to include the contents of the -dictionary. /// + /// Default: /// [Obsolete("Replaced by IncludeScopeProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] @@ -120,6 +124,7 @@ public sealed override Layout Layout /// Obsolete and replaced by with NLog v5. /// Gets or sets whether to include the contents of the -stack. /// + /// Default: /// [Obsolete("Replaced by IncludeScopeNested. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] @@ -129,6 +134,7 @@ public sealed override Layout Layout /// Obsolete and replaced by with NLog v5. /// Gets or sets whether to include the contents of the -properties. /// + /// Default: /// [Obsolete("Replaced by IncludeScopeProperties. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] @@ -138,6 +144,7 @@ public sealed override Layout Layout /// Obsolete and replaced by with NLog v5. /// Gets or sets whether to include the contents of the -stack. /// + /// Default: /// [Obsolete("Replaced by IncludeScopeNested. Marked obsolete on NLog 5.0")] [EditorBrowsable(EditorBrowsableState.Never)] @@ -146,18 +153,21 @@ public sealed override Layout Layout /// /// Gets or sets a value indicating whether to include contents of the dictionary /// + /// Default: /// public bool IncludeGdc { get; set; } /// /// Gets or sets a value indicating whether to include call site (class and method name) in the /// + /// Default: /// public bool IncludeCallSite { get => _contextLayout.IncludeCallSite; set => _contextLayout.IncludeCallSite = value; } /// /// Gets or sets a value indicating whether to include source info (file name and line number) in the /// + /// Default: /// public bool IncludeCallSiteStackTrace { get => _contextLayout.IncludeCallSiteStackTrace; set => _contextLayout.IncludeCallSiteStackTrace = value; } @@ -169,7 +179,7 @@ public sealed override Layout Layout public virtual IList ContextProperties { get; } = new List(); /// - /// List of property names to exclude when is true + /// List of property names to exclude when is /// /// #if !NET35 @@ -206,7 +216,7 @@ protected override void InitializeTarget() /// Check if logevent has properties (or context properties) /// /// - /// True if properties should be included + /// if properties should be included protected bool ShouldIncludeProperties(LogEventInfo logEvent) { return IncludeGdc diff --git a/src/NLog/Targets/TargetWithLayout.cs b/src/NLog/Targets/TargetWithLayout.cs index 36d32da835..808f7e5c10 100644 --- a/src/NLog/Targets/TargetWithLayout.cs +++ b/src/NLog/Targets/TargetWithLayout.cs @@ -33,7 +33,6 @@ namespace NLog.Targets { - using NLog.Config; using NLog.Layouts; /// @@ -71,9 +70,7 @@ protected TargetWithLayout() /// /// Gets or sets the layout used to format log messages. /// - /// - /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} - /// + /// [Required] Default: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} /// public virtual Layout Layout { get; set; } } diff --git a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs index e8e621a989..6ecf13723e 100644 --- a/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs +++ b/src/NLog/Targets/TargetWithLayoutHeaderAndFooter.cs @@ -54,13 +54,7 @@ protected TargetWithLayoutHeaderAndFooter() _layout = layout is LayoutWithHeaderAndFooter headerAndFooter ? headerAndFooter.Layout : layout; } - /// - /// Gets or sets the text to be rendered. - /// - /// - /// The default value of the layout is: ${longdate}|${level:uppercase=true}|${logger}|${message:withexception=true} - /// - /// + /// public override Layout Layout { get => _layout; @@ -87,6 +81,7 @@ public override Layout Layout /// /// Gets or sets the footer. /// + /// Default: /// public Layout? Footer { @@ -107,6 +102,7 @@ public Layout? Footer /// /// Gets or sets the header. /// + /// Default: /// public Layout? Header { diff --git a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs index 698ec2b9f5..ee368129b6 100644 --- a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs @@ -351,7 +351,7 @@ protected virtual void StartLazyWriterTimer() /// Attempts to start an instant timer-worker-thread which can write /// queued log messages. /// - /// Returns true when scheduled a timer-worker-thread + /// Returns when scheduled a timer-worker-thread protected virtual bool StartInstantWriterTimer() { return StartLazyWriterThread(true); diff --git a/tests/NLog.Targets.WebService.Tests/WebServiceTargetTests.cs b/tests/NLog.Targets.WebService.Tests/WebServiceTargetTests.cs index 4bec13b109..571d98de64 100644 --- a/tests/NLog.Targets.WebService.Tests/WebServiceTargetTests.cs +++ b/tests/NLog.Targets.WebService.Tests/WebServiceTargetTests.cs @@ -193,10 +193,6 @@ private class StreamMock : MemoryStream public byte[] bytes; public string stringed; - /// - /// Releases the unmanaged resources used by the class and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { //save stuff before dispose diff --git a/tests/NLog.UnitTests/LayoutRenderers/ExceptionTests.cs b/tests/NLog.UnitTests/LayoutRenderers/ExceptionTests.cs index e697e5fca8..f0d9b5e72d 100644 --- a/tests/NLog.UnitTests/LayoutRenderers/ExceptionTests.cs +++ b/tests/NLog.UnitTests/LayoutRenderers/ExceptionTests.cs @@ -347,8 +347,8 @@ public void InnerExceptionTest() Exception ex = GetNestedExceptionWithStackTrace(exceptionMessage); logFactory.GetCurrentClassLogger().Error(ex, "msg"); - logFactory.AssertDebugLastMessage("ApplicationException Wrapper2" + EnvironmentHelper.NewLine + - "ArgumentException Wrapper1" + EnvironmentHelper.NewLine + + logFactory.AssertDebugLastMessage("ApplicationException Wrapper2" + Environment.NewLine + + "ArgumentException Wrapper1" + Environment.NewLine + "CustomArgumentException Test exception"); } @@ -853,8 +853,8 @@ public void ExcpetionTestAPI() Exception ex = GetNestedExceptionWithStackTrace(exceptionMessage); logFactory.GetCurrentClassLogger().Error(ex, "msg"); - logFactory.AssertDebugLastMessage("ApplicationException Wrapper2" + EnvironmentHelper.NewLine + - "ArgumentException Wrapper1" + EnvironmentHelper.NewLine + + logFactory.AssertDebugLastMessage("ApplicationException Wrapper2" + Environment.NewLine + + "ArgumentException Wrapper1" + Environment.NewLine + "CustomArgumentException Test exception"); var t = (DebugTarget)logFactory.Configuration.AllTargets[0]; @@ -881,8 +881,8 @@ public void InnerExceptionTestAPI() Exception ex = GetNestedExceptionWithStackTrace(exceptionMessage); logFactory.GetCurrentClassLogger().Error(ex, "msg"); - logFactory.AssertDebugLastMessage("ApplicationException Wrapper2" + EnvironmentHelper.NewLine + - "Wrapper1" + EnvironmentHelper.NewLine + + logFactory.AssertDebugLastMessage("ApplicationException Wrapper2" + Environment.NewLine + + "Wrapper1" + Environment.NewLine + "Test exception"); } From 44d2cacd0b8b8b2862da5dfd4fe077697c2ce662 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 6 Jul 2025 08:50:07 +0200 Subject: [PATCH 197/224] FileTarget - Activate legacy ArchiveFileName when ArchiveSuffixFormat contains legacy placeholder (#5921) --- src/NLog/Targets/FileTarget.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index f6ad11dc0c..c26ef9015b 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -309,7 +309,7 @@ public FileArchivePeriod ArchiveEvery /// public Layout? ArchiveFileName { - get => _archiveFileName; + get => _archiveFileName ?? (_archiveSuffixFormat?.IndexOf("{1") >= 0 ? FileName : null); set { var archiveSuffixFormat = _archiveSuffixFormat; @@ -439,6 +439,7 @@ public string ArchiveSuffixFormat { if (!string.IsNullOrEmpty(value) && ArchiveFileName is SimpleLayout simpleLayout) { + // When legacy ArchiveFileName only contains file-extension, then strip away leading underscore from suffix var fileName = Path.GetFileNameWithoutExtension(simpleLayout.OriginalText); if (StringHelpers.IsNullOrWhiteSpace(fileName) && value.IndexOf('_') == 0) { From 1a2549b64acabde5c05ecc2848bc597f7a020933 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 6 Jul 2025 10:45:30 +0200 Subject: [PATCH 198/224] XML docs for LayoutRenderers with remarks about default value (#5922) --- .../AllEventPropertiesLayoutRenderer.cs | 5 +++- .../AppDomainLayoutRenderer.cs | 12 ++++++-- .../AppSettingLayoutRenderer.cs | 2 ++ .../AssemblyVersionLayoutRenderer.cs | 13 ++++----- .../LayoutRenderers/BaseDirLayoutRenderer.cs | 10 +++++-- .../CallSiteFileNameLayoutRenderer.cs | 3 ++ .../LayoutRenderers/CallSiteLayoutRenderer.cs | 9 ++++++ .../CallSiteLineNumberLayoutRenderer.cs | 2 ++ .../LayoutRenderers/CounterLayoutRenderer.cs | 3 ++ .../CurrentDirLayoutRenderer.cs | 6 ++-- .../LayoutRenderers/DateLayoutRenderer.cs | 3 ++ .../EnvironmentLayoutRenderer.cs | 2 ++ .../EnvironmentUserLayoutRenderer.cs | 4 +++ .../EventPropertiesLayoutRenderer.cs | 5 ++++ .../ExceptionDataLayoutRenderer.cs | 5 +++- .../ExceptionLayoutRenderer.cs | 9 ++++++ .../LayoutRenderers/FuncLayoutRenderer.cs | 2 ++ .../GarbageCollectorInfoLayoutRenderer.cs | 1 + src/NLog/LayoutRenderers/GdcLayoutRenderer.cs | 3 ++ .../LayoutRenderers/GuidLayoutRenderer.cs | 2 ++ .../LayoutRenderers/IdentityLayoutRenderer.cs | 4 +++ .../InstallContextLayoutRenderer.cs | 1 + .../LayoutRenderers/LevelLayoutRenderer.cs | 3 +- .../LoggerNameLayoutRenderer.cs | 28 +++++++++++-------- .../LayoutRenderers/LongDateLayoutRenderer.cs | 1 + .../LayoutRenderers/MessageLayoutRenderer.cs | 13 +++------ .../LayoutRenderers/NLogDirLayoutRenderer.cs | 2 ++ .../ProcessDirLayoutRenderer.cs | 6 ++-- .../ProcessInfoLayoutRenderer.cs | 8 ++++-- .../ProcessNameLayoutRenderer.cs | 1 + .../ProcessTimeLayoutRenderer.cs | 2 ++ .../ScopeContextIndentLayoutRenderer.cs | 1 + .../ScopeContextNestedStatesLayoutRenderer.cs | 5 ++++ .../ScopeContextPropertyLayoutRenderer.cs | 3 ++ .../ScopeContextTimingLayoutRenderer.cs | 7 ++++- .../ShortDateLayoutRenderer.cs | 1 + .../SpecialFolderLayoutRenderer.cs | 8 ++++-- .../StackTraceLayoutRenderer.cs | 6 ++++ .../LayoutRenderers/TempDirLayoutRenderer.cs | 2 ++ .../LayoutRenderers/TimeLayoutRenderer.cs | 3 ++ .../LayoutRenderers/VariableLayoutRenderer.cs | 3 +- .../Wrappers/CachedLayoutRendererWrapper.cs | 6 +++- ...ileSystemNormalizeLayoutRendererWrapper.cs | 1 + .../JsonEncodeLayoutRendererWrapper.cs | 6 ++-- .../Wrappers/LeftLayoutRendererWrapper.cs | 4 ++- .../LowercaseLayoutRendererWrapper.cs | 1 + .../NoRawValueLayoutRendererWrapper.cs | 2 +- .../Wrappers/ObjectPathRendererWrapper.cs | 8 ++++-- .../OnExceptionLayoutRendererWrapper.cs | 1 + .../OnHasPropertiesLayoutRendererWrapper.cs | 1 + .../Wrappers/PaddingLayoutRendererWrapper.cs | 4 +++ .../Wrappers/ReplaceLayoutRendererWrapper.cs | 8 +++--- .../ReplaceNewLinesLayoutRendererWrapper.cs | 4 ++- .../Wrappers/RightLayoutRendererWrapper.cs | 3 +- .../Wrappers/Rot13LayoutRendererWrapper.cs | 1 + .../SubstringLayoutRendererWrapper.cs | 4 +-- .../TrimWhiteSpaceLayoutRendererWrapper.cs | 4 +-- .../UppercaseLayoutRendererWrapper.cs | 4 ++- .../UrlEncodeLayoutRendererWrapper.cs | 16 +++-------- .../WhenEmptyLayoutRendererWrapper.cs | 1 + .../Wrappers/WhenLayoutRendererWrapper.cs | 2 ++ .../Wrappers/WrapLineLayoutRendererWrapper.cs | 6 ++-- .../Wrappers/WrapperLayoutRendererBase.cs | 1 + 63 files changed, 213 insertions(+), 84 deletions(-) diff --git a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs index 71b2b2ab32..bb83f6f8b8 100644 --- a/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AllEventPropertiesLayoutRenderer.cs @@ -69,6 +69,7 @@ public AllEventPropertiesLayoutRenderer() /// /// Gets or sets string that will be used to separate key/value pairs. /// + /// Default: , /// public string Separator { @@ -85,13 +86,14 @@ public string Separator /// /// Gets or sets whether empty property-values should be included in the output. /// - /// Empty value is either null or empty string + /// Default: . Empty value is either null or empty string /// public bool IncludeEmptyValues { get; set; } /// /// Gets or sets whether to include the contents of the properties-dictionary. /// + /// Default: /// public bool IncludeScopeProperties { get; set; } @@ -147,6 +149,7 @@ public string Format /// /// Gets or sets the culture used for rendering. /// + /// Default: /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; diff --git a/src/NLog/LayoutRenderers/AppDomainLayoutRenderer.cs b/src/NLog/LayoutRenderers/AppDomainLayoutRenderer.cs index 40b9778675..1bb698f2ae 100644 --- a/src/NLog/LayoutRenderers/AppDomainLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AppDomainLayoutRenderer.cs @@ -76,10 +76,16 @@ internal AppDomainLayoutRenderer(IAppEnvironment appEnvironment) } /// - /// Format string. Possible values: "Short", "Long" or custom like {0} {1}. Default "Long" - /// The first parameter is the AppDomain.Id, the second the second the AppDomain.FriendlyName - /// This string is used in + /// Gets or sets format-string for displaying details. + /// This string is used in where + /// first parameter is and second is /// + /// + /// Default: Long . How alias names are mapped: + /// Short = {0:00} + /// Long = {0:0000}:{1} + /// Friendly = {1} + /// /// [DefaultParameter] public string Format { get; set; } = LongFormatCode; diff --git a/src/NLog/LayoutRenderers/AppSettingLayoutRenderer.cs b/src/NLog/LayoutRenderers/AppSettingLayoutRenderer.cs index 69a8bd3424..9f61cde897 100644 --- a/src/NLog/LayoutRenderers/AppSettingLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AppSettingLayoutRenderer.cs @@ -60,6 +60,7 @@ public sealed class AppSettingLayoutRenderer : LayoutRenderer, IStringValueRende /// /// The AppSetting item-name /// + /// [Required] Default: /// [DefaultParameter] public string Item { get; set; } = string.Empty; @@ -76,6 +77,7 @@ public sealed class AppSettingLayoutRenderer : LayoutRenderer, IStringValueRende /// /// The default value to render if the AppSetting value is null. /// + /// Default: /// public string Default { get; set; } = string.Empty; diff --git a/src/NLog/LayoutRenderers/AssemblyVersionLayoutRenderer.cs b/src/NLog/LayoutRenderers/AssemblyVersionLayoutRenderer.cs index 5281804314..1a061beece 100644 --- a/src/NLog/LayoutRenderers/AssemblyVersionLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/AssemblyVersionLayoutRenderer.cs @@ -56,6 +56,7 @@ public class AssemblyVersionLayoutRenderer : LayoutRenderer /// /// The (full) name of the assembly. If null, using the entry assembly. /// + /// Default: /// [DefaultParameter] public string Name { get; set; } = string.Empty; @@ -63,16 +64,14 @@ public class AssemblyVersionLayoutRenderer : LayoutRenderer /// /// Gets or sets the type of assembly version to retrieve. /// - /// - /// Some version type and platform combinations are not fully supported. - /// - UWP earlier than .NET Standard 1.5: Value for is always returned unless the parameter is specified. - /// + /// Default: /// public AssemblyVersionType Type { get; set; } = AssemblyVersionType.Assembly; /// /// The default value to render if the Version is not available /// + /// Default: /// public string Default { get => _default ?? GenerateDefaultValue(); set => _default = value; } private string? _default; @@ -81,10 +80,8 @@ public class AssemblyVersionLayoutRenderer : LayoutRenderer /// Gets or sets the custom format of the assembly version output. /// /// - /// Supported placeholders are 'major', 'minor', 'build' and 'revision'. - /// The default .NET template for version numbers is 'major.minor.build.revision'. See - /// https://docs.microsoft.com/en-gb/dotnet/api/system.version?view=netframework-4.7.2#remarks - /// for details. + /// Default: major.minor.build.revision . + /// Supported placeholders are 'major', 'minor', 'build' and 'revision'. For more details /// /// public string Format diff --git a/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs index 4108a3bbbb..37d5dfbf2c 100644 --- a/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/BaseDirLayoutRenderer.cs @@ -60,13 +60,15 @@ public class BaseDirLayoutRenderer : LayoutRenderer /// /// Use base dir of current process. Alternative one can just use ${processdir} /// + /// Default: /// public bool ProcessDir { get; set; } /// /// Fallback to the base dir of current process, when AppDomain.BaseDirectory is Temp-Path (.NET Core 3 - Single File Publish) /// - /// + /// Default: + /// public bool FixTempDir { get; set; } /// @@ -86,14 +88,16 @@ internal BaseDirLayoutRenderer(IAppEnvironment appEnvironment) } /// - /// Gets or sets the name of the file to be Path.Combine()'d with the base directory. + /// Gets or sets the name of the file to be Path.Combine()'d with the directory name. /// + /// Default: /// public string File { get; set; } = string.Empty; /// - /// Gets or sets the name of the directory to be Path.Combine()'d with the base directory. + /// Gets or sets the name of the directory to be Path.Combine()'d with the directory name. /// + /// Default: /// public string Dir { get; set; } = string.Empty; diff --git a/src/NLog/LayoutRenderers/CallSiteFileNameLayoutRenderer.cs b/src/NLog/LayoutRenderers/CallSiteFileNameLayoutRenderer.cs index 8872b3c544..199dc29aa0 100644 --- a/src/NLog/LayoutRenderers/CallSiteFileNameLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CallSiteFileNameLayoutRenderer.cs @@ -52,18 +52,21 @@ public class CallSiteFileNameLayoutRenderer : LayoutRenderer, IUsesStackTrace, I /// /// Gets or sets a value indicating whether to include source file path. /// + /// Default: /// public bool IncludeSourcePath { get; set; } = true; /// /// Gets or sets the number of frames to skip. /// + /// Default: /// public int SkipFrames { get; set; } /// /// Logger should capture StackTrace, if it was not provided manually /// + /// Default: /// public bool CaptureStackTrace { get; set; } = true; diff --git a/src/NLog/LayoutRenderers/CallSiteLayoutRenderer.cs b/src/NLog/LayoutRenderers/CallSiteLayoutRenderer.cs index 6f8974c803..1bd740126d 100644 --- a/src/NLog/LayoutRenderers/CallSiteLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CallSiteLayoutRenderer.cs @@ -52,24 +52,28 @@ public class CallSiteLayoutRenderer : LayoutRenderer, IUsesStackTrace /// /// Gets or sets a value indicating whether to render the class name. /// + /// Default: /// public bool ClassName { get; set; } = true; /// /// Gets or sets a value indicating whether to render the include the namespace with . /// + /// Default: /// public bool IncludeNamespace { get; set; } = true; /// /// Gets or sets a value indicating whether to render the method name. /// + /// Default: /// public bool MethodName { get; set; } = true; /// /// Gets or sets a value indicating whether the method name will be cleaned up if it is detected as an anonymous delegate. /// + /// Default: /// public bool CleanNamesOfAnonymousDelegates { get; set; } = true; @@ -77,30 +81,35 @@ public class CallSiteLayoutRenderer : LayoutRenderer, IUsesStackTrace /// Gets or sets a value indicating whether the method and class names will be cleaned up if it is detected as an async continuation /// (everything after an await-statement inside of an async method). /// + /// Default: /// public bool CleanNamesOfAsyncContinuations { get; set; } = true; /// /// Gets or sets the number of frames to skip. /// + /// Default: /// public int SkipFrames { get; set; } /// /// Gets or sets a value indicating whether to render the source file name and line number. /// + /// Default: /// public bool FileName { get; set; } /// /// Gets or sets a value indicating whether to include source file path. /// + /// Default: /// public bool IncludeSourcePath { get; set; } = true; /// /// Logger should capture StackTrace, if it was not provided manually /// + /// Default: /// public bool CaptureStackTrace { get; set; } = true; diff --git a/src/NLog/LayoutRenderers/CallSiteLineNumberLayoutRenderer.cs b/src/NLog/LayoutRenderers/CallSiteLineNumberLayoutRenderer.cs index 1654938d38..7cb321d173 100644 --- a/src/NLog/LayoutRenderers/CallSiteLineNumberLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CallSiteLineNumberLayoutRenderer.cs @@ -51,12 +51,14 @@ public class CallSiteLineNumberLayoutRenderer : LayoutRenderer, IUsesStackTrace, /// /// Gets or sets the number of frames to skip. /// + /// Default: /// public int SkipFrames { get; set; } /// /// Logger should capture StackTrace, if it was not provided manually /// + /// Default: /// public bool CaptureStackTrace { get; set; } = true; diff --git a/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs b/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs index 351fdfe55a..2900ee8146 100644 --- a/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CounterLayoutRenderer.cs @@ -74,6 +74,7 @@ public GlobalSequence(string sequenceName, long initialValue) /// /// Gets or sets the initial value of the counter. /// + /// Default: /// public long Value { get => _value; set => _value = value; } private long _value; @@ -81,12 +82,14 @@ public GlobalSequence(string sequenceName, long initialValue) /// /// Gets or sets the value for incrementing the counter for every layout rendering. /// + /// Default: /// public int Increment { get; set; } = 1; /// /// Gets or sets the name of the sequence. Different named sequences can have individual values. /// + /// Default: /// public Layout? Sequence { get; set; } diff --git a/src/NLog/LayoutRenderers/CurrentDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/CurrentDirLayoutRenderer.cs index 4e84d16011..69330a784d 100644 --- a/src/NLog/LayoutRenderers/CurrentDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/CurrentDirLayoutRenderer.cs @@ -50,14 +50,16 @@ namespace NLog.LayoutRenderers public class CurrentDirLayoutRenderer : LayoutRenderer, IStringValueRenderer { /// - /// Gets or sets the name of the file to be Path.Combine()'d with the current directory. + /// Gets or sets the name of the file to be Path.Combine()'d with the directory name. /// + /// Default: /// public string File { get; set; } = string.Empty; /// - /// Gets or sets the name of the directory to be Path.Combine()'d with the current directory. + /// Gets or sets the name of the directory to be Path.Combine()'d with the directory name. /// + /// Default: /// public string Dir { get; set; } = string.Empty; diff --git a/src/NLog/LayoutRenderers/DateLayoutRenderer.cs b/src/NLog/LayoutRenderers/DateLayoutRenderer.cs index 49761c23bb..c25a95d334 100644 --- a/src/NLog/LayoutRenderers/DateLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/DateLayoutRenderer.cs @@ -61,12 +61,14 @@ public DateLayoutRenderer() /// /// Gets or sets the culture used for rendering. /// + /// Default: /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; /// /// Gets or sets the date format. Can be any argument accepted by DateTime.ToString(format). /// + /// Default: yyyy/MM/dd HH:mm:ss.fff /// [DefaultParameter] public string Format @@ -91,6 +93,7 @@ public string Format /// /// Gets or sets a value indicating whether to output UTC time instead of local time. /// + /// Default: /// public bool UniversalTime { diff --git a/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs b/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs index bb6eaed0d3..5f53e150b2 100644 --- a/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/EnvironmentLayoutRenderer.cs @@ -52,6 +52,7 @@ public class EnvironmentLayoutRenderer : LayoutRenderer, IStringValueRenderer /// /// Gets or sets the name of the environment variable. /// + /// [Required] Default: /// [DefaultParameter] public string Variable { get; set; } = string.Empty; @@ -59,6 +60,7 @@ public class EnvironmentLayoutRenderer : LayoutRenderer, IStringValueRenderer /// /// Gets or sets the default value to be used when the environment variable is not set. /// + /// Default: /// public string Default { get; set; } = string.Empty; diff --git a/src/NLog/LayoutRenderers/EnvironmentUserLayoutRenderer.cs b/src/NLog/LayoutRenderers/EnvironmentUserLayoutRenderer.cs index be8285dc57..a4e617ac6e 100644 --- a/src/NLog/LayoutRenderers/EnvironmentUserLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/EnvironmentUserLayoutRenderer.cs @@ -51,18 +51,21 @@ public class EnvironmentUserLayoutRenderer : LayoutRenderer, IStringValueRendere /// /// Gets or sets a value indicating whether username should be included. /// + /// Default: /// public bool UserName { get; set; } = true; /// /// Gets or sets a value indicating whether domain name should be included. /// + /// Default: /// public bool Domain { get; set; } /// /// Gets or sets the default value to be used when the User is not set. /// + /// Default: UserUnknown /// public string DefaultUser { get; set; } = "UserUnknown"; @@ -70,6 +73,7 @@ public class EnvironmentUserLayoutRenderer : LayoutRenderer, IStringValueRendere /// Gets or sets the default value to be used when the Domain is not set. /// /// + /// Default: DomainUnknown public string DefaultDomain { get; set; } = "DomainUnknown"; /// diff --git a/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs b/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs index 95b97d043c..01bc1cc648 100644 --- a/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/EventPropertiesLayoutRenderer.cs @@ -59,6 +59,7 @@ public class EventPropertiesLayoutRenderer : LayoutRenderer, IRawValue, IStringV /// /// Gets or sets the name of the item. /// + /// [Required] Default: /// [DefaultParameter] public string Item { get => _item?.ToString() ?? string.Empty; set => _item = (value is null || !IgnoreCase) ? (value ?? string.Empty) : new PropertiesDictionary.IgnoreCasePropertyKey(value); } @@ -67,18 +68,21 @@ public class EventPropertiesLayoutRenderer : LayoutRenderer, IRawValue, IStringV /// /// Format string for conversion from object to string. /// + /// Default: /// public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. /// + /// Default: /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; /// /// Gets or sets the object-property-navigation-path for lookup of nested property /// + /// Default: /// public string ObjectPath { @@ -89,6 +93,7 @@ public string ObjectPath /// /// Gets or sets whether to perform case-sensitive property-name lookup /// + /// Default: /// public bool IgnoreCase { diff --git a/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs b/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs index a4cd964f3b..97ab39b9aa 100644 --- a/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ExceptionDataLayoutRenderer.cs @@ -37,7 +37,6 @@ namespace NLog.LayoutRenderers using System.Globalization; using System.Text; using NLog.Config; - using NLog.Internal; /// /// Render information of @@ -56,6 +55,7 @@ public class ExceptionDataLayoutRenderer : LayoutRenderer /// /// Gets or sets the key to search the exception Data for /// + /// [Required] Default: /// [DefaultParameter] public string Item { get; set; } = string.Empty; @@ -63,18 +63,21 @@ public class ExceptionDataLayoutRenderer : LayoutRenderer /// /// Gets or sets whether to render innermost Exception from /// + /// Default: /// public bool BaseException { get; set; } /// /// Format string for conversion from object to string. /// + /// Default: /// public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. /// + /// Default: /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; diff --git a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs index 17a5c0bba5..bb8503c126 100644 --- a/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ExceptionLayoutRenderer.cs @@ -114,6 +114,7 @@ public ExceptionLayoutRenderer() /// properties: Message, Type, ShortType, ToString, Method, StackTrace. /// This parameter value is case-insensitive. /// + /// [Required] Default: ToString,Data /// [DefaultParameter] public string Format @@ -132,6 +133,7 @@ public string Format /// properties: Message, Type, ShortType, ToString, Method, StackTrace. /// This parameter value is case-insensitive. /// + /// Default: /// public string? InnerFormat { @@ -147,6 +149,7 @@ public string? InnerFormat /// /// Gets or sets the separator used to concatenate parts specified in the Format. /// + /// Default: /// public string Separator { @@ -163,6 +166,7 @@ public string Separator /// /// Gets or sets the separator used to concatenate exception data specified in the Format. /// + /// Default: ; /// public string ExceptionDataSeparator { @@ -180,18 +184,21 @@ public string ExceptionDataSeparator /// Gets or sets the maximum number of inner exceptions to include in the output. /// By default inner exceptions are not enabled for compatibility with NLog 1.0. /// + /// Default: /// public int MaxInnerExceptionLevel { get; set; } /// /// Gets or sets the separator between inner exceptions. /// + /// Default: /// public string InnerExceptionSeparator { get; set; } = Environment.NewLine; /// /// Gets or sets whether to render innermost Exception from /// + /// Default: /// public bool BaseException { get; set; } @@ -199,11 +206,13 @@ public string ExceptionDataSeparator /// /// Gets or sets whether to collapse exception tree using /// + /// Default: /// #else /// /// Gets or sets whether to collapse exception tree using AggregateException.Flatten() /// + /// Default: /// #endif public bool FlattenException { get; set; } = true; diff --git a/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs b/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs index 0de6528118..cddf3e602e 100644 --- a/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs @@ -81,12 +81,14 @@ public FuncLayoutRenderer(string layoutRendererName, Func /// Format string for conversion from object to string. /// + /// Default: /// public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. /// + /// Default: /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; diff --git a/src/NLog/LayoutRenderers/GarbageCollectorInfoLayoutRenderer.cs b/src/NLog/LayoutRenderers/GarbageCollectorInfoLayoutRenderer.cs index 56f6375c29..58f2e4cc55 100644 --- a/src/NLog/LayoutRenderers/GarbageCollectorInfoLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/GarbageCollectorInfoLayoutRenderer.cs @@ -50,6 +50,7 @@ public class GarbageCollectorInfoLayoutRenderer : LayoutRenderer /// /// Gets or sets the property to retrieve. /// + /// Default: /// public GarbageCollectorProperty Property { get; set; } = GarbageCollectorProperty.TotalMemory; diff --git a/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs b/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs index d3952b7840..f17342fdc1 100644 --- a/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/GdcLayoutRenderer.cs @@ -54,6 +54,7 @@ public class GdcLayoutRenderer : LayoutRenderer, IRawValue, IStringValueRenderer /// /// Gets or sets the name of the item. /// + /// [Required] Default: /// [DefaultParameter] public string Item { get; set; } = string.Empty; @@ -61,12 +62,14 @@ public class GdcLayoutRenderer : LayoutRenderer, IRawValue, IStringValueRenderer /// /// Format string for conversion from object to string. /// + /// Default: /// public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. /// + /// Default: /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; diff --git a/src/NLog/LayoutRenderers/GuidLayoutRenderer.cs b/src/NLog/LayoutRenderers/GuidLayoutRenderer.cs index c95c11520d..92d4abd117 100644 --- a/src/NLog/LayoutRenderers/GuidLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/GuidLayoutRenderer.cs @@ -52,12 +52,14 @@ public class GuidLayoutRenderer : LayoutRenderer, IRawValue, IStringValueRendere /// /// Gets or sets the GUID format as accepted by Guid.ToString() method. /// + /// Default: N /// public string Format { get; set; } = "N"; /// /// Generate the Guid from the NLog LogEvent (Will be the same for all targets) /// + /// Default: /// public bool GeneratedFromLogEvent { get; set; } diff --git a/src/NLog/LayoutRenderers/IdentityLayoutRenderer.cs b/src/NLog/LayoutRenderers/IdentityLayoutRenderer.cs index 34af58b3ee..20d621d523 100644 --- a/src/NLog/LayoutRenderers/IdentityLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/IdentityLayoutRenderer.cs @@ -50,24 +50,28 @@ public class IdentityLayoutRenderer : LayoutRenderer /// Gets or sets the separator to be used when concatenating /// parts of identity information. /// + /// Default: : /// public string Separator { get; set; } = ":"; /// /// Gets or sets a value indicating whether to render Thread.CurrentPrincipal.Identity.Name. /// + /// Default: /// public bool Name { get; set; } = true; /// /// Gets or sets a value indicating whether to render Thread.CurrentPrincipal.Identity.AuthenticationType. /// + /// Default: /// public bool AuthType { get; set; } = true; /// /// Gets or sets a value indicating whether to render Thread.CurrentPrincipal.Identity.IsAuthenticated. /// + /// Default: /// public bool IsAuthenticated { get; set; } = true; diff --git a/src/NLog/LayoutRenderers/InstallContextLayoutRenderer.cs b/src/NLog/LayoutRenderers/InstallContextLayoutRenderer.cs index 7cf73beb1d..69d0548685 100644 --- a/src/NLog/LayoutRenderers/InstallContextLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/InstallContextLayoutRenderer.cs @@ -50,6 +50,7 @@ public class InstallContextLayoutRenderer : LayoutRenderer /// /// Gets or sets the name of the parameter. /// + /// [Required] Default: /// [DefaultParameter] public string Parameter { get; set; } = string.Empty; diff --git a/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs b/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs index 9a5b65346c..5cb4b71e87 100644 --- a/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LevelLayoutRenderer.cs @@ -64,13 +64,14 @@ public class LevelLayoutRenderer : LayoutRenderer, IRawValue, IStringValueRender /// /// Gets or sets a value indicating the output format of the level. /// + /// Default: /// public LevelFormat Format { get; set; } = LevelFormat.Name; /// /// Gets or sets a value indicating whether upper case conversion should be applied. /// - /// A value of if upper case conversion should be applied otherwise, . + /// Default: /// public bool Uppercase { get; set; } diff --git a/src/NLog/LayoutRenderers/LoggerNameLayoutRenderer.cs b/src/NLog/LayoutRenderers/LoggerNameLayoutRenderer.cs index 58ccf7b9e1..dc4ced0285 100644 --- a/src/NLog/LayoutRenderers/LoggerNameLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LoggerNameLayoutRenderer.cs @@ -52,63 +52,67 @@ public class LoggerNameLayoutRenderer : LayoutRenderer, IStringValueRenderer /// /// Gets or sets a value indicating whether to render short logger name (the part after the trailing dot character). /// + /// Default: /// public bool ShortName { get; set; } /// /// Gets or sets a value indicating whether to render prefix of logger name (the part before the trailing dot character). /// + /// Default: /// public bool PrefixName { get; set; } /// protected override void Append(StringBuilder builder, LogEventInfo logEvent) { + var loggerName = logEvent.LoggerName; if (ShortName) { - int lastDot = TryGetLastDotForShortName(logEvent); + int lastDot = TryGetLastDotForShortName(loggerName); if (lastDot >= 0) { - builder.Append(logEvent.LoggerName, lastDot + 1, logEvent.LoggerName.Length - lastDot - 1); + builder.Append(loggerName, lastDot + 1, loggerName.Length - lastDot - 1); return; } } else if (PrefixName) { - int lastDot = TryGetLastDotForShortName(logEvent); + int lastDot = TryGetLastDotForShortName(loggerName); if (lastDot > 0) { - builder.Append(logEvent.LoggerName, 0, lastDot); + builder.Append(loggerName, 0, lastDot); return; } } - builder.Append(logEvent.LoggerName); + builder.Append(loggerName); } string IStringValueRenderer.GetFormattedString(LogEventInfo logEvent) { + var loggerName = logEvent.LoggerName; if (ShortName) { - int lastDot = TryGetLastDotForShortName(logEvent); + int lastDot = TryGetLastDotForShortName(loggerName); if (lastDot >= 0) { - return logEvent.LoggerName.Substring(lastDot + 1); + return loggerName.Substring(lastDot + 1); } } else if (PrefixName) { - int lastDot = TryGetLastDotForShortName(logEvent); + int lastDot = TryGetLastDotForShortName(loggerName); if (lastDot > 0) { - return logEvent.LoggerName.Substring(0, lastDot); + return loggerName.Substring(0, lastDot); } } - return logEvent.LoggerName ?? string.Empty; + return loggerName ?? string.Empty; } - private static int TryGetLastDotForShortName(LogEventInfo logEvent) + private static int TryGetLastDotForShortName(string loggerName) { - return logEvent.LoggerName?.LastIndexOf('.') ?? -1; + return loggerName?.LastIndexOf('.') ?? -1; } } } diff --git a/src/NLog/LayoutRenderers/LongDateLayoutRenderer.cs b/src/NLog/LayoutRenderers/LongDateLayoutRenderer.cs index a2f59adc82..90a7dc4f2d 100644 --- a/src/NLog/LayoutRenderers/LongDateLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/LongDateLayoutRenderer.cs @@ -52,6 +52,7 @@ public class LongDateLayoutRenderer : LayoutRenderer, IRawValue /// /// Gets or sets a value indicating whether to output UTC time instead of local time. /// + /// Default: /// public bool UniversalTime { get => _universalTime ?? false; set => _universalTime = value; } private bool? _universalTime; diff --git a/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs b/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs index 7b8379175b..12eb926573 100644 --- a/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/MessageLayoutRenderer.cs @@ -49,29 +49,24 @@ namespace NLog.LayoutRenderers [ThreadAgnostic] public class MessageLayoutRenderer : LayoutRenderer, IStringValueRenderer { - /// - /// Initializes a new instance of the class. - /// - public MessageLayoutRenderer() - { - ExceptionSeparator = "|"; - } - /// /// Gets or sets a value indicating whether to log exception along with message. /// + /// Default: /// public bool WithException { get; set; } /// /// Gets or sets the string that separates message from the exception. /// + /// Default: | /// - public string ExceptionSeparator { get; set; } + public string ExceptionSeparator { get; set; } = "|"; /// /// Gets or sets whether it should render the raw message without formatting parameters /// + /// Default: /// public bool Raw { get; set; } diff --git a/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs index 490c3bed9c..91e121db88 100644 --- a/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/NLogDirLayoutRenderer.cs @@ -54,12 +54,14 @@ public class NLogDirLayoutRenderer : LayoutRenderer /// /// Gets or sets the name of the file to be Path.Combine()'d with the directory name. /// + /// Default: /// public string File { get; set; } = string.Empty; /// /// Gets or sets the name of the directory to be Path.Combine()'d with the directory name. /// + /// Default: /// public string Dir { get; set; } = string.Empty; diff --git a/src/NLog/LayoutRenderers/ProcessDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessDirLayoutRenderer.cs index 9a7c74cbff..9307edd137 100644 --- a/src/NLog/LayoutRenderers/ProcessDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessDirLayoutRenderer.cs @@ -57,14 +57,16 @@ public class ProcessDirLayoutRenderer : LayoutRenderer private string? _nlogCombinedPath; /// - /// Gets or sets the name of the file to be Path.Combine()'d with the process directory. + /// Gets or sets the name of the file to be Path.Combine()'d with the directory name. /// + /// Default: /// public string File { get; set; } = string.Empty; /// - /// Gets or sets the name of the directory to be Path.Combine()'d with the process directory. + /// Gets or sets the name of the directory to be Path.Combine()'d with the directory name. /// + /// Default: /// public string Dir { get; set; } = string.Empty; diff --git a/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs index caad3fb23b..64114f0727 100644 --- a/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessInfoLayoutRenderer.cs @@ -56,19 +56,23 @@ public class ProcessInfoLayoutRenderer : LayoutRenderer /// /// Gets or sets the property to retrieve. /// + /// Default: /// [DefaultParameter] public ProcessInfoProperty Property { get; set; } = ProcessInfoProperty.Id; /// - /// Gets or sets the format-string to use if the property supports it (Ex. DateTime / TimeSpan / Enum) + /// Gets or sets the format string used when converting the property value to a string, when the + /// property supports formatting (e.g., , , or enum types). /// - /// + /// Default: + /// public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. /// + /// Default: /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; diff --git a/src/NLog/LayoutRenderers/ProcessNameLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessNameLayoutRenderer.cs index 1b531fe253..f4df4ca39c 100644 --- a/src/NLog/LayoutRenderers/ProcessNameLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessNameLayoutRenderer.cs @@ -55,6 +55,7 @@ public class ProcessNameLayoutRenderer : LayoutRenderer /// /// Gets or sets a value indicating whether to write the full path to the process executable. /// + /// Default: /// public bool FullName { get; set; } diff --git a/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs b/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs index d8e62a73ce..b56c976144 100644 --- a/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ProcessTimeLayoutRenderer.cs @@ -53,12 +53,14 @@ public class ProcessTimeLayoutRenderer : LayoutRenderer, IRawValue /// /// Gets or sets a value indicating whether to output in culture invariant format /// + /// Default: /// public bool Invariant { get => ReferenceEquals(Culture, CultureInfo.InvariantCulture); set => Culture = value ? CultureInfo.InvariantCulture : CultureInfo.CurrentCulture; } /// /// Gets or sets the culture used for rendering. /// + /// Default: /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; diff --git a/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs index a98a5de750..151cad3ebf 100644 --- a/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextIndentLayoutRenderer.cs @@ -50,6 +50,7 @@ public sealed class ScopeContextIndentLayoutRenderer : LayoutRenderer /// /// Gets or sets the indent token. /// + /// Default: /// [DefaultParameter] public Layout Indent { get; set; } = Layout.FromLiteral(" "); diff --git a/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs index 3ef2037f58..123f743290 100644 --- a/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextNestedStatesLayoutRenderer.cs @@ -54,18 +54,21 @@ public sealed class ScopeContextNestedStatesLayoutRenderer : LayoutRenderer /// /// Gets or sets the number of top stack frames to be rendered. /// + /// Default: /// public int TopFrames { get; set; } = -1; /// /// Gets or sets the number of bottom stack frames to be rendered. /// + /// Default: /// public int BottomFrames { get; set; } = -1; /// /// Gets or sets the separator to be used for concatenating nested logical context output. /// + /// Default: /// public string Separator { @@ -82,12 +85,14 @@ public string Separator /// /// Gets or sets how to format each nested state. Ex. like JSON = @ /// + /// Default: /// public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. /// + /// Default: /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; diff --git a/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs index ffa22155a4..3ec0b3777c 100644 --- a/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextPropertyLayoutRenderer.cs @@ -53,6 +53,7 @@ public sealed class ScopeContextPropertyLayoutRenderer : LayoutRenderer, IString /// /// Gets or sets the name of the item. /// + /// [Required] Default: /// [DefaultParameter] public string Item { get; set; } = string.Empty; @@ -60,12 +61,14 @@ public sealed class ScopeContextPropertyLayoutRenderer : LayoutRenderer, IString /// /// Format string for conversion from object to string. /// + /// Default: /// public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. /// + /// Default: /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; diff --git a/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs index d47d1f7194..5cebb4cbc4 100644 --- a/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs @@ -52,18 +52,21 @@ public sealed class ScopeContextTimingLayoutRenderer : LayoutRenderer /// /// Gets or sets whether to only include the duration of the last scope created /// + /// Default: /// public bool CurrentScope { get; set; } /// /// Gets or sets whether to just display the scope creation time, and not the duration /// + /// Default: /// public bool StartTime { get; set; } /// /// Gets or sets whether to just display the scope creation time, and not the duration /// + /// Default: /// [Obsolete("Replaced by StartTime. Marked obsolete on NLog 6.0")] public bool ScopeBeginTime { get => StartTime; set => StartTime = value; } @@ -73,13 +76,15 @@ public sealed class ScopeContextTimingLayoutRenderer : LayoutRenderer /// /// When Format has not been specified, then it will render TimeSpan.TotalMilliseconds /// + /// Default: /// public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. /// - /// + /// Default: + /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; /// diff --git a/src/NLog/LayoutRenderers/ShortDateLayoutRenderer.cs b/src/NLog/LayoutRenderers/ShortDateLayoutRenderer.cs index 5552d5de98..ca71ec7f9d 100644 --- a/src/NLog/LayoutRenderers/ShortDateLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ShortDateLayoutRenderer.cs @@ -53,6 +53,7 @@ public class ShortDateLayoutRenderer : LayoutRenderer, IRawValue, IStringValueRe /// /// Gets or sets a value indicating whether to output UTC time instead of local time. /// + /// Default: /// public bool UniversalTime { get => _universalTime ?? false; set => _universalTime = value; } private bool? _universalTime; diff --git a/src/NLog/LayoutRenderers/SpecialFolderLayoutRenderer.cs b/src/NLog/LayoutRenderers/SpecialFolderLayoutRenderer.cs index 04552905b8..f59fd255df 100644 --- a/src/NLog/LayoutRenderers/SpecialFolderLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/SpecialFolderLayoutRenderer.cs @@ -68,18 +68,20 @@ public class SpecialFolderLayoutRenderer : LayoutRenderer /// /// [DefaultParameter] - public Environment.SpecialFolder Folder { get; set; } + public Environment.SpecialFolder Folder { get; set; } = Environment.SpecialFolder.CommonApplicationData; /// /// Gets or sets the name of the file to be Path.Combine()'d with the directory name. /// - /// + /// Default: + /// public string File { get; set; } = string.Empty; /// /// Gets or sets the name of the directory to be Path.Combine()'d with the directory name. /// - /// + /// Default: + /// public string Dir { get; set; } = string.Empty; /// diff --git a/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs b/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs index 84d084d37e..95f50913f9 100644 --- a/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/StackTraceLayoutRenderer.cs @@ -52,24 +52,28 @@ public class StackTraceLayoutRenderer : LayoutRenderer, IUsesStackTrace /// /// Gets or sets the output format of the stack trace. /// + /// Default: /// public StackTraceFormat Format { get; set; } = StackTraceFormat.Flat; /// /// Gets or sets the number of top stack frames to be rendered. /// + /// Default: /// public int TopFrames { get; set; } = 3; /// /// Gets or sets the number of frames to skip. /// + /// Default: /// public int SkipFrames { get; set; } /// /// Gets or sets the stack frame separator string. /// + /// Default: => /// public string Separator { @@ -86,12 +90,14 @@ public string Separator /// /// Logger should capture StackTrace, if it was not provided manually /// + /// Default: /// public bool CaptureStackTrace { get; set; } = true; /// /// Gets or sets whether to render StackFrames in reverse order /// + /// Default: /// public bool Reverse { get; set; } diff --git a/src/NLog/LayoutRenderers/TempDirLayoutRenderer.cs b/src/NLog/LayoutRenderers/TempDirLayoutRenderer.cs index de23cd6756..560131762e 100644 --- a/src/NLog/LayoutRenderers/TempDirLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/TempDirLayoutRenderer.cs @@ -58,12 +58,14 @@ public class TempDirLayoutRenderer : LayoutRenderer /// /// Gets or sets the name of the file to be Path.Combine()'d with the directory name. /// + /// Default: /// public string File { get; set; } = string.Empty; /// /// Gets or sets the name of the directory to be Path.Combine()'d with the directory name. /// + /// Default: /// public string Dir { get; set; } = string.Empty; diff --git a/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs b/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs index d6664f793d..0791c9ad5a 100644 --- a/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/TimeLayoutRenderer.cs @@ -53,6 +53,7 @@ public class TimeLayoutRenderer : LayoutRenderer, IRawValue /// /// Gets or sets a value indicating whether to output UTC time instead of local time. /// + /// Default: /// public bool UniversalTime { get => _universalTime ?? false; set => _universalTime = value; } private bool? _universalTime; @@ -60,12 +61,14 @@ public class TimeLayoutRenderer : LayoutRenderer, IRawValue /// /// Gets or sets a value indicating whether to output in culture invariant format /// + /// Default: /// public bool Invariant { get => ReferenceEquals(Culture, CultureInfo.InvariantCulture); set => Culture = value ? CultureInfo.InvariantCulture : CultureInfo.CurrentCulture; } /// /// Gets or sets the culture used for rendering. /// + /// Default: /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; diff --git a/src/NLog/LayoutRenderers/VariableLayoutRenderer.cs b/src/NLog/LayoutRenderers/VariableLayoutRenderer.cs index 2fc9c0cabd..dad60afd8a 100644 --- a/src/NLog/LayoutRenderers/VariableLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/VariableLayoutRenderer.cs @@ -50,6 +50,7 @@ public class VariableLayoutRenderer : LayoutRenderer /// /// Gets or sets the name of the NLog variable. /// + /// [Required] Default: /// [DefaultParameter] public string Name { get; set; } = string.Empty; @@ -57,7 +58,7 @@ public class VariableLayoutRenderer : LayoutRenderer /// /// Gets or sets the default value to be used when the variable is not set. /// - /// Not used if Name is null + /// Default: /// public string Default { get; set; } = string.Empty; diff --git a/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs index 467d348228..c46caeb70f 100644 --- a/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/CachedLayoutRendererWrapper.cs @@ -77,24 +77,28 @@ public enum ClearCacheOption /// /// Gets or sets a value indicating whether this is enabled. /// + /// Default: /// public bool Cached { get; set; } = true; /// /// Gets or sets a value indicating when the cache is cleared. /// + /// Default: | /// public ClearCacheOption ClearCache { get; set; } = ClearCacheOption.OnInit | ClearCacheOption.OnClose; /// - /// Cachekey. If the cachekey changes, resets the value. For example, the cachekey would be the current day.s + /// Gets or sets whether to reset cached value when CacheKey output changes. Example CacheKey could render current day, so the cached-value is reset on day roll. /// + /// Default: /// public Layout? CacheKey { get; set; } /// /// Gets or sets a value indicating how many seconds the value should stay cached until it expires /// + /// Default: /// public int CachedSeconds { diff --git a/src/NLog/LayoutRenderers/Wrappers/FileSystemNormalizeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/FileSystemNormalizeLayoutRendererWrapper.cs index 7a6e69d15e..0cbbdab126 100644 --- a/src/NLog/LayoutRenderers/Wrappers/FileSystemNormalizeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/FileSystemNormalizeLayoutRendererWrapper.cs @@ -54,6 +54,7 @@ public sealed class FileSystemNormalizeLayoutRendererWrapper : WrapperLayoutRend /// Gets or sets a value indicating whether to modify the output of this renderer so it can be used as a part of file path /// (illegal characters are replaced with '_'). /// + /// Default: /// public bool FSNormalize { get; set; } = true; diff --git a/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs index 3cdd5c5967..6bf855e647 100644 --- a/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/JsonEncodeLayoutRendererWrapper.cs @@ -54,21 +54,21 @@ public sealed class JsonEncodeLayoutRendererWrapper : WrapperLayoutRendererBase /// /// Gets or sets whether output should be encoded with Json-string escaping. /// + /// Default: /// public bool JsonEncode { get; set; } = true; /// /// Gets or sets a value indicating whether to escape non-ascii characters /// + /// Default: /// public bool EscapeUnicode { get; set; } = true; /// /// Should forward slashes be escaped? If , / will be converted to \/ /// - /// - /// If not set explicitly then the value of the parent will be used as default. - /// + /// Default: /// [Obsolete("Marked obsolete with NLog 5.5. Should never escape forward slash")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/NLog/LayoutRenderers/Wrappers/LeftLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/LeftLayoutRendererWrapper.cs index 452fd5e9bc..b33a50f19b 100644 --- a/src/NLog/LayoutRenderers/Wrappers/LeftLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/LeftLayoutRendererWrapper.cs @@ -51,14 +51,16 @@ namespace NLog.LayoutRenderers.Wrappers public sealed class LeftLayoutRendererWrapper : WrapperLayoutRendererBase { /// - /// Gets or sets the length in characters. + /// Gets or sets the length in characters. Zero or negative means disabled. /// + /// Default: /// public int Length { get; set; } /// /// Same as -property, so it can be used as ambient property. /// + /// Default: /// /// ${message:truncate=80} /// diff --git a/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs index 30e7863d5d..7aa46f0d98 100644 --- a/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/LowercaseLayoutRendererWrapper.cs @@ -71,6 +71,7 @@ public sealed class LowercaseLayoutRendererWrapper : WrapperLayoutRendererBase /// /// Gets or sets the culture used for rendering. /// + /// Default: /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; diff --git a/src/NLog/LayoutRenderers/Wrappers/NoRawValueLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/NoRawValueLayoutRendererWrapper.cs index 44ffdd70e5..840bea8dee 100644 --- a/src/NLog/LayoutRenderers/Wrappers/NoRawValueLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/NoRawValueLayoutRendererWrapper.cs @@ -50,7 +50,7 @@ public sealed class NoRawValueLayoutRendererWrapper : WrapperLayoutRendererBase /// /// Gets or sets a value indicating whether to disable the IRawValue-interface /// - /// A value of if IRawValue-interface should be ignored; otherwise, . + /// Default: /// public bool NoRawValue { get; set; } = true; diff --git a/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs index a2e05745bb..e943868524 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ObjectPathRendererWrapper.cs @@ -60,6 +60,7 @@ public sealed class ObjectPathRendererWrapper : WrapperLayoutRendererBase, IRawV /// /// Shortcut for /// + /// Default: /// public string Path { @@ -70,6 +71,7 @@ public string Path /// /// Gets or sets the object-property-navigation-path for lookup of nested property /// + /// [Required] Default: /// public string ObjectPath { @@ -80,13 +82,15 @@ public string ObjectPath /// /// Format string for conversion from object to string. /// - /// + /// Default: + /// public string? Format { get; set; } /// /// Gets or sets the culture used for rendering. /// - /// + /// Default: + /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; /// diff --git a/src/NLog/LayoutRenderers/Wrappers/OnExceptionLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/OnExceptionLayoutRendererWrapper.cs index 9b7d3fae8f..af20d37192 100644 --- a/src/NLog/LayoutRenderers/Wrappers/OnExceptionLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/OnExceptionLayoutRendererWrapper.cs @@ -51,6 +51,7 @@ public sealed class OnExceptionLayoutRendererWrapper : WrapperLayoutRendererBase /// /// If is not found, print this layout. /// + /// Default: /// public Layout Else { get; set; } = Layout.Empty; diff --git a/src/NLog/LayoutRenderers/Wrappers/OnHasPropertiesLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/OnHasPropertiesLayoutRendererWrapper.cs index 07cc1c2fb0..bc14c03c2b 100644 --- a/src/NLog/LayoutRenderers/Wrappers/OnHasPropertiesLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/OnHasPropertiesLayoutRendererWrapper.cs @@ -54,6 +54,7 @@ public sealed class OnHasPropertiesLayoutRendererWrapper : WrapperLayoutRenderer /// /// If is not found, print this layout. /// + /// Default: /// public Layout Else { get; set; } = Layout.Empty; diff --git a/src/NLog/LayoutRenderers/Wrappers/PaddingLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/PaddingLayoutRendererWrapper.cs index 89b8b61dec..2e74cecae8 100644 --- a/src/NLog/LayoutRenderers/Wrappers/PaddingLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/PaddingLayoutRendererWrapper.cs @@ -57,6 +57,7 @@ public sealed class PaddingLayoutRendererWrapper : WrapperLayoutRendererBase /// Gets or sets the number of characters to pad the output to. /// /// + /// Default: . /// Positive padding values cause left padding, negative values /// cause right padding to the desired width. /// @@ -66,6 +67,7 @@ public sealed class PaddingLayoutRendererWrapper : WrapperLayoutRendererBase /// /// Gets or sets the padding character. /// + /// Default: /// public char PadCharacter { get; set; } = ' '; @@ -73,6 +75,7 @@ public sealed class PaddingLayoutRendererWrapper : WrapperLayoutRendererBase /// Gets or sets a value indicating whether to trim the /// rendered text to the absolute value of the padding length. /// + /// Default: /// public bool FixedLength { get; set; } @@ -83,6 +86,7 @@ public sealed class PaddingLayoutRendererWrapper : WrapperLayoutRendererBase /// or right-aligned (characters removed from the left). The /// default is left alignment. /// + /// Default: /// public PaddingHorizontalAlignment AlignmentOnTruncation { get; set; } = PaddingHorizontalAlignment.Left; diff --git a/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs index 10e61f61a5..47ebe3779d 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ReplaceLayoutRendererWrapper.cs @@ -55,7 +55,7 @@ public sealed class ReplaceLayoutRendererWrapper : WrapperLayoutRendererBase /// /// Gets or sets the text to search for. /// - /// The text search for. + /// [Required] Default: /// public string SearchFor { @@ -72,7 +72,7 @@ public string SearchFor /// /// Gets or sets the replacement string. /// - /// The replacement string. + /// Default: /// public string ReplaceWith { @@ -89,14 +89,14 @@ public string ReplaceWith /// /// Gets or sets a value indicating whether to ignore case. /// - /// A value of if case should be ignored when searching; otherwise, . + /// Default: /// public bool IgnoreCase { get; set; } /// /// Gets or sets a value indicating whether to search for whole words /// - /// A value of if whole words should be searched for; otherwise, . + /// Default: /// public bool WholeWords { get; set; } diff --git a/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs index b6862d4b92..c3359ad707 100644 --- a/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/ReplaceNewLinesLayoutRendererWrapper.cs @@ -53,12 +53,14 @@ public sealed class ReplaceNewLinesLayoutRendererWrapper : WrapperLayoutRenderer /// /// Gets or sets a value indicating the string that should be used to replace newlines. /// + /// Default: /// public string Replacement { get; set; } = " "; /// - /// Gets or sets a value indicating the string that should be used to replace newlines. + /// Gets or sets a value indicating the string that should be used to replace newlines (alias for ) /// + /// Default: /// public string ReplaceNewLines { get => Replacement; set => Replacement = value; } diff --git a/src/NLog/LayoutRenderers/Wrappers/RightLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/RightLayoutRendererWrapper.cs index e2a099fffa..743688854f 100644 --- a/src/NLog/LayoutRenderers/Wrappers/RightLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/RightLayoutRendererWrapper.cs @@ -50,8 +50,9 @@ namespace NLog.LayoutRenderers.Wrappers public sealed class RightLayoutRendererWrapper : WrapperLayoutRendererBase { /// - /// Gets or sets the length in characters. + /// Gets or sets the length in characters. Zero or negative means disabled. /// + /// Default: /// public int Length { get; set; } diff --git a/src/NLog/LayoutRenderers/Wrappers/Rot13LayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/Rot13LayoutRendererWrapper.cs index 57682cad2c..931713ed5a 100644 --- a/src/NLog/LayoutRenderers/Wrappers/Rot13LayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/Rot13LayoutRendererWrapper.cs @@ -58,6 +58,7 @@ public sealed class Rot13LayoutRendererWrapper : WrapperLayoutRendererBase /// The layout to be wrapped. /// This variable is for backwards compatibility /// + [Obsolete("Replaced by Inner. Marked obsolete with NLog 2.0")] public Layout Text { get => Inner; diff --git a/src/NLog/LayoutRenderers/Wrappers/SubstringLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/SubstringLayoutRendererWrapper.cs index adaf1954ae..cea1086293 100644 --- a/src/NLog/LayoutRenderers/Wrappers/SubstringLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/SubstringLayoutRendererWrapper.cs @@ -57,14 +57,14 @@ public sealed class SubstringLayoutRendererWrapper : WrapperLayoutRendererBase /// /// Gets or sets the start index. /// - /// Index + /// Default: /// public int Start { get; set; } /// /// Gets or sets the length in characters. If null, then the whole string /// - /// Index + /// Default: /// public int? Length { get; set; } diff --git a/src/NLog/LayoutRenderers/Wrappers/TrimWhiteSpaceLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/TrimWhiteSpaceLayoutRendererWrapper.cs index 9afe760f00..446cf9d9ea 100644 --- a/src/NLog/LayoutRenderers/Wrappers/TrimWhiteSpaceLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/TrimWhiteSpaceLayoutRendererWrapper.cs @@ -52,9 +52,9 @@ namespace NLog.LayoutRenderers.Wrappers public sealed class TrimWhiteSpaceLayoutRendererWrapper : WrapperLayoutRendererBase { /// - /// Gets or sets a value indicating whether lower case conversion should be applied. + /// Gets or sets a value indicating whether whitespace should be trimmed. /// - /// A value of if lower case conversion should be applied; otherwise, . + /// Default: /// public bool TrimWhiteSpace { get; set; } = true; diff --git a/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs index d226380ccb..7e0c0c702d 100644 --- a/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/UppercaseLayoutRendererWrapper.cs @@ -60,13 +60,14 @@ public sealed class UppercaseLayoutRendererWrapper : WrapperLayoutRendererBase /// /// Gets or sets a value indicating whether upper case conversion should be applied. /// - /// A value of if upper case conversion should be applied otherwise, . + /// Default: /// public bool Uppercase { get; set; } = true; /// /// Same as -property, so it can be used as ambient property. /// + /// Default: /// /// ${level:toupper} /// @@ -76,6 +77,7 @@ public sealed class UppercaseLayoutRendererWrapper : WrapperLayoutRendererBase /// /// Gets or sets the culture used for rendering. /// + /// Default: /// public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; diff --git a/src/NLog/LayoutRenderers/Wrappers/UrlEncodeLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/UrlEncodeLayoutRendererWrapper.cs index 2dbeeac789..579db0932f 100644 --- a/src/NLog/LayoutRenderers/Wrappers/UrlEncodeLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/UrlEncodeLayoutRendererWrapper.cs @@ -51,32 +51,24 @@ namespace NLog.LayoutRenderers.Wrappers [ThreadAgnostic] public sealed class UrlEncodeLayoutRendererWrapper : WrapperLayoutRendererBase { - /// - /// Initializes a new instance of the class. - /// - public UrlEncodeLayoutRendererWrapper() - { - SpaceAsPlus = true; - } - /// /// Gets or sets a value indicating whether spaces should be translated to '+' or '%20'. /// - /// A value of if space should be translated to '+'; otherwise, . + /// Default: /// - public bool SpaceAsPlus { get; set; } + public bool SpaceAsPlus { get; set; } = true; /// /// Gets or sets a value whether escaping be done according to Rfc3986 (Supports Internationalized Resource Identifiers - IRIs) /// - /// A value of if Rfc3986; otherwise, for legacy Rfc2396. + /// Default: /// public bool EscapeDataRfc3986 { get; set; } /// /// Gets or sets a value whether escaping be done according to the old NLog style (Very non-standard) /// - /// A value of if legacy encoding; otherwise, for standard UTF8 encoding. + /// Default: /// [Obsolete("Instead use default Rfc2396 or EscapeDataRfc3986. Marked obsolete with NLog v5.3")] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs index e48d063619..86ae39e285 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenEmptyLayoutRendererWrapper.cs @@ -52,6 +52,7 @@ public sealed class WhenEmptyLayoutRendererWrapper : WrapperLayoutRendererBase, /// /// Gets or sets the layout to be rendered when Inner-layout produces empty result. /// + /// Default: /// public Layout WhenEmpty { get; set; } = Layout.Empty; diff --git a/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs index 3d4cde235a..baf4e7f04c 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WhenLayoutRendererWrapper.cs @@ -55,12 +55,14 @@ public sealed class WhenLayoutRendererWrapper : WrapperLayoutRendererBase, IRawV /// /// Gets or sets the condition that must be met for the layout to be printed. /// + /// [Required] Default: /// public ConditionExpression? When { get; set; } /// /// If is not met, print this layout. /// + /// Default: /// public Layout Else { get; set; } = Layout.Empty; diff --git a/src/NLog/LayoutRenderers/Wrappers/WrapLineLayoutRendererWrapper.cs b/src/NLog/LayoutRenderers/Wrappers/WrapLineLayoutRendererWrapper.cs index 6018e29552..fc87cb5dc1 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WrapLineLayoutRendererWrapper.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WrapLineLayoutRendererWrapper.cs @@ -47,11 +47,9 @@ namespace NLog.LayoutRenderers.Wrappers public sealed class WrapLineLayoutRendererWrapper : WrapperLayoutRendererBase { /// - /// Gets or sets the line length for wrapping. + /// Gets or sets the line length for wrapping. Only positive values are allowed. /// - /// - /// Only positive values are allowed - /// + /// Default: /// public int WrapLine { get; set; } = 80; diff --git a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs index 41533eae6b..b5d682ffaa 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBase.cs @@ -53,6 +53,7 @@ public abstract class WrapperLayoutRendererBase : LayoutRenderer /// /// [DefaultParameter] so Inner: is not required if it's the first /// + /// Default: /// [DefaultParameter] public Layout Inner { get; set; } = Layout.Empty; From 0b1edd5b26b503da21598be521ca2afbe41a9921 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 9 Jul 2025 22:36:30 +0200 Subject: [PATCH 199/224] NLog.Targets.GZipFile - Improve support for ArchiveAboveSize (#5911) --- src/NLog.Targets.GZipFile/GZipFileTarget.cs | 3 + src/NLog/Targets/ConsoleTarget.cs | 2 +- .../ExclusiveFileLockingAppender.cs | 43 ++++++---- .../BaseFileArchiveHandler.cs | 10 ++- .../RollingArchiveFileHandler.cs | 19 ++++- src/NLog/Targets/FileTarget.cs | 4 +- .../GZipFileTests.cs | 78 +++++++++++++++++++ .../NLog.UnitTests/Targets/FileTargetTests.cs | 10 ++- 8 files changed, 144 insertions(+), 25 deletions(-) diff --git a/src/NLog.Targets.GZipFile/GZipFileTarget.cs b/src/NLog.Targets.GZipFile/GZipFileTarget.cs index 309520150d..e81d774ed3 100644 --- a/src/NLog.Targets.GZipFile/GZipFileTarget.cs +++ b/src/NLog.Targets.GZipFile/GZipFileTarget.cs @@ -88,7 +88,10 @@ protected override void InitializeTarget() protected override Stream CreateFileStream(string filePath, int bufferSize) { if (!EnableArchiveFileCompression || CompressionLevel == CompressionLevel.NoCompression || !ArchiveOldFileOnStartup || !KeepFileOpen) + { + NLog.Common.InternalLogger.Debug("{0}: File Compression has been disabled, fallback to normal FileStream", this); return base.CreateFileStream(filePath, bufferSize); + } var underlyingStream = base.CreateFileStream(filePath, bufferSize); var compressStream = new GZipStream(underlyingStream, CompressionLevel); diff --git a/src/NLog/Targets/ConsoleTarget.cs b/src/NLog/Targets/ConsoleTarget.cs index d10385dd97..1c1436c4fb 100644 --- a/src/NLog/Targets/ConsoleTarget.cs +++ b/src/NLog/Targets/ConsoleTarget.cs @@ -142,7 +142,7 @@ public Encoding Encoding /// /// Default: /// - [Obsolete("Replaced by ForceWriteLine. Marked obsolete with NLog v6.0")] + [Obsolete("Replaced by ForceWriteLine (but inverted value). Marked obsolete with NLog v6.0")] public bool WriteBuffer { get => !ForceWriteLine; set => ForceWriteLine = !value; } /// diff --git a/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs index b512759c0b..b44a08ea9a 100644 --- a/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs +++ b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs @@ -87,28 +87,24 @@ public ExclusiveFileLockingAppender(FileTarget fileTarget, string filePath) OpenStreamTime = Time.TimeSource.Current.Time; _lastFileDeletedCheck = Environment.TickCount; - RefreshFileBirthTimeUtc(true); + var fileInfoSize = RefreshFileBirthTimeUtc(true); _fileStream = _fileTarget.CreateFileStreamWithRetry(this, fileTarget.BufferSize, initialFileOpen: true); - _countedFileSize = RefreshCountedFileSize(); + _countedFileSize = RefreshCountedFileSize(_fileStream, fileInfoSize); } - private bool SkipRefreshFileBirthTime() - { - return (_fileTarget.ArchiveFileName is null && _fileTarget.ArchiveEvery == FileArchivePeriod.None); - } - - private void RefreshFileBirthTimeUtc(bool forceRefresh) + private long RefreshFileBirthTimeUtc(bool forceRefresh) { FileLastModified = NLog.Time.TimeSource.Current.Time; - if (SkipRefreshFileBirthTime()) - return; + if (_fileTarget.ArchiveFileName is null && _fileTarget.ArchiveEvery == FileArchivePeriod.None && _fileTarget.ArchiveAboveSize <= 0) + return 0; try { FileInfo fileInfo = new FileInfo(_filePath); - if (fileInfo.Exists && fileInfo.Length != 0) + long fileInfoSize = fileInfo.Exists ? fileInfo.Length : 0; + if (fileInfoSize > 0) { var fileBirthTimeUtc = FileInfoHelper.LookupValidFileCreationTimeUtc(fileInfo) ?? DateTime.MinValue; var fileBirthTime = fileBirthTimeUtc != DateTime.MinValue ? NLog.Time.TimeSource.Current.FromSystemTime(fileBirthTimeUtc) : OpenStreamTime; @@ -117,10 +113,13 @@ private void RefreshFileBirthTimeUtc(bool forceRefresh) FileBirthTime = fileBirthTime; FileLastModified = NLog.Time.TimeSource.Current.FromSystemTime(fileInfo.LastWriteTimeUtc); } + + return fileInfoSize; } catch (Exception ex) { InternalLogger.Debug(ex, "{0}: Failed to refresh BirthTime for file: '{1}'", _fileTarget, _filePath); + return 0; } } @@ -131,8 +130,7 @@ public void Write(byte[] buffer, int offset, int count) { MonitorFileHasBeenDeleted(); _lastFileDeletedCheck = Environment.TickCount; - if (!SkipRefreshFileBirthTime()) - FileLastModified = NLog.Time.TimeSource.Current.Time; + FileLastModified = NLog.Time.TimeSource.Current.Time; } _fileStream.Write(buffer, offset, count); @@ -162,14 +160,25 @@ private void MonitorFileHasBeenDeleted() InternalLogger.Debug("{0}: Recreating FileStream because no longer File.Exists: '{1}'", _fileTarget, _filePath); SafeCloseFile(_filePath, _fileStream); _fileStream = _fileTarget.CreateFileStreamWithRetry(this, _fileTarget.BufferSize, initialFileOpen: false); - _countedFileSize = RefreshCountedFileSize(); - RefreshFileBirthTimeUtc(false); + var fileInfoSize = RefreshFileBirthTimeUtc(false); + _countedFileSize = RefreshCountedFileSize(_fileStream, fileInfoSize); } } - private long? RefreshCountedFileSize() + private long? RefreshCountedFileSize(Stream fileStream, long fileInfoSize) { - return (_fileTarget.ArchiveAboveSize > 0 && _fileTarget.GetType().Equals(typeof(FileTarget))) ? _fileStream.Length : default(long?); + if (_fileTarget.ArchiveAboveSize > 0) + { + if (_fileTarget.GetType().Equals(typeof(FileTarget))) + return fileStream.Length; + + if (fileStream.GetType().Equals(typeof(FileStream))) + return null; // NLog.Targets.AtomicFile should not use counted filesize + + return fileInfoSize; // NLog.Targets.GZipFile should use counted uncompressed filesize + } + + return null; } private void SafeCloseFile(string filepath, Stream? fileStream) diff --git a/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs b/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs index 5032135918..f2708e45d6 100644 --- a/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs +++ b/src/NLog/Targets/FileArchiveHandlers/BaseFileArchiveHandler.cs @@ -55,7 +55,7 @@ protected bool DeleteOldFilesBeforeArchive(string filePath, bool initialFileOpen // - First start with removing the oldest files string fileDirectory = Path.GetDirectoryName(filePath); // Replace all non-letter with '*' replace all '**' with single '*' - string fileWildcard = GetFileNameWildcard(filePath); + string fileWildcard = GetDeleteOldFileNameWildcard(filePath); return DeleteOldFilesBeforeArchive(fileDirectory, fileWildcard, initialFileOpen, excludeFileName); } @@ -270,7 +270,7 @@ private static bool TryParseStartSequenceNumber(string archiveFileName, int seqS } } - private static string GetFileNameWildcard(string filepath) + private static string GetDeleteOldFileNameWildcard(string filepath) { var filename = Path.GetFileNameWithoutExtension(filepath) ?? string.Empty; var fileext = Path.GetExtension(filepath) ?? string.Empty; @@ -311,6 +311,12 @@ private static string GetFileNameWildcard(string filepath) if (lastLength > 0) { + if (lastStart > 0 && lastLength > 1 && !char.IsDigit(filename[lastStart + 1])) + { + lastStart += 1; + lastLength -= 1; + } + var prefix = filename.Substring(0, lastStart); var suffix = filename.Substring(lastStart + lastLength, filename.Length - lastStart - lastLength); return string.IsNullOrEmpty(suffix) ? string.Concat(prefix, "*", fileext) : string.Concat(prefix, "*", suffix, "*", fileext); diff --git a/src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs b/src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs index ea3c6ec6c5..ee03fa1d05 100644 --- a/src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs +++ b/src/NLog/Targets/FileArchiveHandlers/RollingArchiveFileHandler.cs @@ -69,7 +69,7 @@ public virtual int ArchiveBeforeOpenFile(string newFileName, LogEventInfo firstL if (initialFileOpen) { - if (_fileTarget.ArchiveOldFileOnStartup || _fileTarget.ArchiveAboveSize != 0 || _fileTarget.ArchiveEvery != FileArchivePeriod.None) + if (_fileTarget.ArchiveOldFileOnStartup || _fileTarget.ArchiveAboveSize > 0 || _fileTarget.ArchiveEvery != FileArchivePeriod.None) { var newFilePath = FileTarget.CleanFullFilePath(newFileName); return RollToInitialSequenceNumber(newFilePath); @@ -85,6 +85,18 @@ private int RollToInitialSequenceNumber(string newFilePath) try { + if (AllowOptimizedRollingForArchiveAboveSize()) + { + // Fast FileSize check of a single file, and skip enumerating all archive-files + var newFileInfo = new FileInfo(newFilePath); + var newFileLength = newFileInfo.Exists ? newFileInfo.Length : 0; + if (newFileLength > 0 && newFileLength < _fileTarget.ArchiveAboveSize) + { + InternalLogger.Debug("{0}: Archive rolling skipped because file-size={1} < ArchiveAboveSize for file: {2}", _fileTarget, newFileLength, newFilePath); + return newSequenceNumber; + } + } + var filedir = Path.GetDirectoryName(newFilePath); if (string.IsNullOrEmpty(filedir)) return 0; @@ -139,5 +151,10 @@ private int RollToInitialSequenceNumber(string newFilePath) return newSequenceNumber; } } + + private bool AllowOptimizedRollingForArchiveAboveSize() + { + return _fileTarget.ArchiveAboveSize > 0 && _fileTarget.ArchiveEvery == FileArchivePeriod.None && !_fileTarget.ArchiveOldFileOnStartup && !_fileTarget.DeleteOldFileOnStartup && _fileTarget.GetType().Equals(typeof(FileTarget)); + } } } diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index c26ef9015b..8279c8557b 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -751,7 +751,7 @@ private void WriteBytesToFile(string filename, LogEventInfo firstLogEvent, Memor try { - if (ArchiveAboveSize != 0 || ArchiveEvery != FileArchivePeriod.None) + if (ArchiveAboveSize > 0 || ArchiveEvery != FileArchivePeriod.None) { openFile = RollArchiveFile(filename, openFile, firstLogEvent, hasWritten); } @@ -805,7 +805,7 @@ private OpenFileAppender RollArchiveFile(string filename, OpenFileAppender openF private bool MustArchiveFile(IFileAppender fileAppender, LogEventInfo firstLogEvent) { - if (ArchiveAboveSize != 0 && MustArchiveBySize(fileAppender)) + if (ArchiveAboveSize > 0 && MustArchiveBySize(fileAppender)) return true; if (ArchiveEvery != FileArchivePeriod.None && MustArchiveEveryTimePeriod(fileAppender, firstLogEvent)) diff --git a/tests/NLog.Targets.GZipFile.Tests/GZipFileTests.cs b/tests/NLog.Targets.GZipFile.Tests/GZipFileTests.cs index 9ce16a3be2..e76195911f 100644 --- a/tests/NLog.Targets.GZipFile.Tests/GZipFileTests.cs +++ b/tests/NLog.Targets.GZipFile.Tests/GZipFileTests.cs @@ -102,6 +102,84 @@ public void SimpleFileGZipStream_AutoFlush_False() } } + [Fact] + public void SimpleFileGZipStream_ArchiveAboveSize() + { + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFileName = Path.Combine(tempDir, "log.gzip"); + var rollFileName = Path.Combine(tempDir, "log_01.gzip"); + + try + { + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => + { + cfg.ForLogger().WriteTo(new GZipFileTarget() { FileName = logFileName, Layout = "${message}", LineEnding = LineEndingMode.LF, CompressionLevel = CompressionLevel.Optimal, ArchiveAboveSize = 10, ArchiveSuffixFormat = "_{0:00}" }); + }).LogFactory; + + logFactory.GetCurrentClassLogger().Info("Hello"); + logFactory.GetCurrentClassLogger().Info("World"); + logFactory.GetCurrentClassLogger().Info("Again"); + logFactory.Shutdown(); + + using (var logFile = new StreamReader(new GZipStream(new FileStream(logFileName, FileMode.Open), CompressionMode.Decompress))) + { + Assert.Equal("Hello", logFile.ReadLine()); + Assert.Equal("World", logFile.ReadLine()); + Assert.Null(logFile.ReadLine()); + } + + using (var logFile = new StreamReader(new GZipStream(new FileStream(rollFileName, FileMode.Open), CompressionMode.Decompress))) + { + Assert.Equal("Again", logFile.ReadLine()); + Assert.Null(logFile.ReadLine()); + } + } + finally + { + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + + [Fact] + public void SimpleFileGZipStream_ArchiveFileName_ArchiveAboveSize() + { + var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); + var logFileName = Path.Combine(tempDir, "log.gzip"); + var rollFileName = Path.Combine(tempDir, "log_01.gzip"); + + try + { + var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => + { + cfg.ForLogger().WriteTo(new GZipFileTarget() { FileName = logFileName, Layout = "${message}", LineEnding = LineEndingMode.LF, CompressionLevel = CompressionLevel.Optimal, ArchiveAboveSize = 10, ArchiveSuffixFormat = "_{0:00}", ArchiveFileName = logFileName }); + }).LogFactory; + + logFactory.GetCurrentClassLogger().Info("Hello"); + logFactory.GetCurrentClassLogger().Info("World"); + logFactory.GetCurrentClassLogger().Info("Again"); + logFactory.Shutdown(); + + using (var logFile = new StreamReader(new GZipStream(new FileStream(logFileName, FileMode.Open), CompressionMode.Decompress))) + { + Assert.Equal("Again", logFile.ReadLine()); + Assert.Null(logFile.ReadLine()); + } + + using (var logFile = new StreamReader(new GZipStream(new FileStream(rollFileName, FileMode.Open), CompressionMode.Decompress))) + { + Assert.Equal("Hello", logFile.ReadLine()); + Assert.Equal("World", logFile.ReadLine()); + Assert.Null(logFile.ReadLine()); + } + } + finally + { + if (Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); + } + } + [Fact] public void SimpleFileGZipStream_ArchiveOldFileOnStartup() { diff --git a/tests/NLog.UnitTests/Targets/FileTargetTests.cs b/tests/NLog.UnitTests/Targets/FileTargetTests.cs index 58f58005c1..bf234e1378 100644 --- a/tests/NLog.UnitTests/Targets/FileTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/FileTargetTests.cs @@ -2691,8 +2691,10 @@ public void FileTarget_LogAndArchiveFilesWithSameName_ShouldArchive() } } - [Fact] - public void FileTarget_ArchiveAboveSize_NewStyle_RollWhenFull() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void FileTarget_ArchiveAboveSize_NewStyle_RollWhenFull(bool reloadConfig) { var tempDir = Path.Combine(Path.GetTempPath(), "nlog_" + Guid.NewGuid().ToString()); var logFile = Path.Combine(tempDir, "Application"); @@ -2713,11 +2715,15 @@ public void FileTarget_ArchiveAboveSize_NewStyle_RollWhenFull() LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); logger.Debug("aaaa"); + if (reloadConfig) + LogManager.Setup().ReloadConfiguration(); logger.Debug("bbbb"); // Not roll (new style so all agree when rolling) logger.Debug("cccc"); // Roll logger.Debug("dddd"); // Not roll (new style so all agree when rolling) logger.Debug("eeee"); // Roll logger.Debug("ffff"); // Not roll (new style so all agree when rolling) + if (reloadConfig) + LogManager.Setup().ReloadConfiguration(); logger.Debug("gggg"); // Roll LogManager.Configuration = null; // Flush From 1943bc2cd716a0263fa1f3d0288ab84fe5eb9c53 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 10 Jul 2025 14:57:07 +0200 Subject: [PATCH 200/224] Updated unit-tests from NET6 to NET8 (#5923) --- run-tests.ps1 | 14 +++++++------- tests/NLog.RegEx.Tests/NLog.RegEx.Tests.csproj | 2 +- .../NLog.Targets.ConcurrentFile.Tests.csproj | 2 +- .../NLog.Targets.GZipFile.Tests.csproj | 2 +- .../NLog.Targets.Mail.Tests.csproj | 2 +- .../NLog.Targets.Network.Tests.csproj | 2 +- .../NLog.Targets.Trace.Tests.csproj | 2 +- .../NLog.Targets.WebService.Tests.csproj | 2 +- tests/NLog.UnitTests/Config/ExtensionTests.cs | 8 ++++---- tests/NLog.UnitTests/NLog.UnitTests.csproj | 12 ++++-------- .../NLog.WindowsRegistry.Tests.csproj | 2 +- 11 files changed, 23 insertions(+), 27 deletions(-) diff --git a/run-tests.ps1 b/run-tests.ps1 index 1ef00f18b9..3dff9e3a68 100644 --- a/run-tests.ps1 +++ b/run-tests.ps1 @@ -2,7 +2,7 @@ dotnet restore ./tests/NLog.UnitTests/ if (-Not $LastExitCode -eq 0) { exit $LastExitCode } -dotnet test ./tests/NLog.UnitTests/ --framework net6.0 --configuration release --no-restore +dotnet test ./tests/NLog.UnitTests/ --framework net8.0 --configuration release --no-restore if (-Not $LastExitCode -eq 0) { exit $LastExitCode } @@ -87,27 +87,27 @@ else if (-Not $LastExitCode -eq 0) { exit $LastExitCode } - dotnet test ./tests/NLog.RegEx.Tests/ --framework net6.0 --configuration release + dotnet test ./tests/NLog.RegEx.Tests/ --framework net8.0 --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } - dotnet test ./tests/NLog.Targets.Mail.Tests/ --framework net6.0 --configuration release + dotnet test ./tests/NLog.Targets.Mail.Tests/ --framework net8.0 --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } - dotnet test ./tests/NLog.Targets.Network.Tests/ --framework net6.0 --configuration release + dotnet test ./tests/NLog.Targets.Network.Tests/ --framework net8.0 --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } - dotnet test ./tests/NLog.Targets.Trace.Tests/ --framework net6.0 --configuration release + dotnet test ./tests/NLog.Targets.Trace.Tests/ --framework net8.0 --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } - dotnet test ./tests/NLog.Targets.WebService.Tests/ --framework net6.0 --configuration release + dotnet test ./tests/NLog.Targets.WebService.Tests/ --framework net8.0 --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } - dotnet test ./tests/NLog.Targets.GZipFile.Tests/ --framework net6.0 --configuration release + dotnet test ./tests/NLog.Targets.GZipFile.Tests/ --framework net8.0 --configuration release if (-Not $LastExitCode -eq 0) { exit $LastExitCode } diff --git a/tests/NLog.RegEx.Tests/NLog.RegEx.Tests.csproj b/tests/NLog.RegEx.Tests/NLog.RegEx.Tests.csproj index d27defab47..995d2603ba 100644 --- a/tests/NLog.RegEx.Tests/NLog.RegEx.Tests.csproj +++ b/tests/NLog.RegEx.Tests/NLog.RegEx.Tests.csproj @@ -3,7 +3,7 @@ 17.0 net462 - net462;net6.0 + net462;net8.0 false diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/NLog.Targets.ConcurrentFile.Tests.csproj b/tests/NLog.Targets.ConcurrentFile.Tests/NLog.Targets.ConcurrentFile.Tests.csproj index 727db04cc1..0098685719 100644 --- a/tests/NLog.Targets.ConcurrentFile.Tests/NLog.Targets.ConcurrentFile.Tests.csproj +++ b/tests/NLog.Targets.ConcurrentFile.Tests/NLog.Targets.ConcurrentFile.Tests.csproj @@ -3,7 +3,7 @@ 17.0 net462 - net462;net6.0 + net462;net8.0 false diff --git a/tests/NLog.Targets.GZipFile.Tests/NLog.Targets.GZipFile.Tests.csproj b/tests/NLog.Targets.GZipFile.Tests/NLog.Targets.GZipFile.Tests.csproj index a6b7fc7d28..f3c3169915 100644 --- a/tests/NLog.Targets.GZipFile.Tests/NLog.Targets.GZipFile.Tests.csproj +++ b/tests/NLog.Targets.GZipFile.Tests/NLog.Targets.GZipFile.Tests.csproj @@ -3,7 +3,7 @@ 17.0 net462 - net462;net6.0 + net462;net8.0 false diff --git a/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj b/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj index d9cd805f6e..88575c8e9b 100644 --- a/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj +++ b/tests/NLog.Targets.Mail.Tests/NLog.Targets.Mail.Tests.csproj @@ -3,7 +3,7 @@ 17.0 net462 - net462;net6.0 + net462;net8.0 false diff --git a/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj b/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj index 1f5b538d89..3e3376f641 100644 --- a/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj +++ b/tests/NLog.Targets.Network.Tests/NLog.Targets.Network.Tests.csproj @@ -3,7 +3,7 @@ 17.0 net462 - net462;net6.0 + net462;net8.0 false diff --git a/tests/NLog.Targets.Trace.Tests/NLog.Targets.Trace.Tests.csproj b/tests/NLog.Targets.Trace.Tests/NLog.Targets.Trace.Tests.csproj index 0a8cbdf511..8874bd9a74 100644 --- a/tests/NLog.Targets.Trace.Tests/NLog.Targets.Trace.Tests.csproj +++ b/tests/NLog.Targets.Trace.Tests/NLog.Targets.Trace.Tests.csproj @@ -3,7 +3,7 @@ 17.0 net462 - net462;net6.0 + net462;net8.0 false diff --git a/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj b/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj index fb8df76dc9..c2921825c2 100644 --- a/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj +++ b/tests/NLog.Targets.WebService.Tests/NLog.Targets.WebService.Tests.csproj @@ -3,7 +3,7 @@ 17.0 net462 - net462;net6.0 + net462;net8.0 false diff --git a/tests/NLog.UnitTests/Config/ExtensionTests.cs b/tests/NLog.UnitTests/Config/ExtensionTests.cs index 43222e165e..f14e78a48c 100644 --- a/tests/NLog.UnitTests/Config/ExtensionTests.cs +++ b/tests/NLog.UnitTests/Config/ExtensionTests.cs @@ -52,11 +52,11 @@ public class ExtensionTests : NLogTestBase private static string GetExtensionAssemblyFullPath() { -#if NETSTANDARD +#if NETFRAMEWORK + return extensionAssemblyFullPath1; +#else Assert.NotNull(typeof(FooLayout)); return typeof(FooLayout).GetTypeInfo().Assembly.Location; -#else - return extensionAssemblyFullPath1; #endif } @@ -625,7 +625,7 @@ private static Assembly LoadManuallyLoadedExtensionDll() var configurationDirectory = nlogDirectory.Parent; var testsDirectory = configurationDirectory.Parent.Parent.Parent; var manuallyLoadedAssemblyPath = Path.Combine(testsDirectory.FullName, "ManuallyLoadedExtension", "bin", configurationDirectory.Name, -#if NETSTANDARD +#if !NETFRAMEWORK "netstandard2.0", #elif NET35 || NET40 || NET45 "net462", diff --git a/tests/NLog.UnitTests/NLog.UnitTests.csproj b/tests/NLog.UnitTests/NLog.UnitTests.csproj index e0b00b5e75..328bdf4d7b 100644 --- a/tests/NLog.UnitTests/NLog.UnitTests.csproj +++ b/tests/NLog.UnitTests/NLog.UnitTests.csproj @@ -3,7 +3,7 @@ 17.0 net462 - net462;net6.0 + net462;net8.0 false @@ -20,18 +20,14 @@ false - + $(DefineConstants);NET35 - + $(DefineConstants);NET45 - - $(DefineConstants);NETSTANDARD - - $(DefineConstants);MONO @@ -58,7 +54,7 @@ - + ../../src/NLog/bin/$(Configuration)/$(TestTargetFramework)/NLog.dll True diff --git a/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj b/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj index 484938e3bd..a4a5ce6d47 100644 --- a/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj +++ b/tests/NLog.WindowsRegistry.Tests/NLog.WindowsRegistry.Tests.csproj @@ -3,7 +3,7 @@ 17.0 net462 - net462;net6.0 + net462;net8.0 false From 48ead9acfadd6a610cd4b0dab5e39f9d47ab030b Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 11 Jul 2025 21:20:18 +0200 Subject: [PATCH 201/224] AsyncTargetWrapper - Updated FullBatchSizeWriteLimit default value from 5 to 10 (#5924) --- src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs index ee368129b6..25da32b3a0 100644 --- a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs @@ -232,7 +232,7 @@ public int QueueLimit /// Performance is better when writing many small batches, than writing a single large batch /// /// - public int FullBatchSizeWriteLimit { get; set; } = 5; + public int FullBatchSizeWriteLimit { get; set; } = 10; /// /// Gets or sets whether to use the locking queue, instead of a lock-free concurrent queue From e8b31dd2f86ce7b934c7e6c3674fc176ac09c849 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 12 Jul 2025 07:19:44 +0200 Subject: [PATCH 202/224] XML docs for Target Wrappers with remarks about default value (#5925) --- src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs | 12 ++++++------ .../Targets/Wrappers/AutoFlushTargetWrapper.cs | 5 +++-- .../Targets/Wrappers/BufferingTargetWrapper.cs | 14 ++++++++------ src/NLog/Targets/Wrappers/FallbackGroupTarget.cs | 2 ++ .../Targets/Wrappers/FilteringTargetWrapper.cs | 2 ++ src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs | 1 + src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs | 8 ++------ .../Targets/Wrappers/PostFilteringTargetWrapper.cs | 1 + .../Targets/Wrappers/RepeatingTargetWrapper.cs | 1 + src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs | 3 +++ src/NLog/Targets/Wrappers/WrapperTargetBase.cs | 1 + 11 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs index 25da32b3a0..200bdae6d7 100644 --- a/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/AsyncTargetWrapper.cs @@ -145,12 +145,14 @@ public AsyncTargetWrapper(Target wrappedTarget, int queueLimit, AsyncTargetWrapp /// Gets or sets the number of log events that should be processed in a batch /// by the lazy writer thread. /// + /// Default: /// public int BatchSize { get; set; } = 200; /// /// Gets or sets the time in milliseconds to sleep between batches. (1 or less means trigger on new activity) /// + /// Default: ms /// public int TimeToSleepBetweenBatches { get; set; } = 1; @@ -208,6 +210,7 @@ public event EventHandler EventQueueGrow /// Gets or sets the action to be taken when the lazy writer thread request queue count /// exceeds the set limit. /// + /// Default: /// public AsyncTargetWrapperOverflowAction OverflowAction { @@ -218,6 +221,7 @@ public AsyncTargetWrapperOverflowAction OverflowAction /// /// Gets or sets the limit on the number of requests in the lazy writer thread request queue. /// + /// Default: /// public int QueueLimit { @@ -228,18 +232,14 @@ public int QueueLimit /// /// Gets or sets the number of batches of to write before yielding into /// - /// - /// Performance is better when writing many small batches, than writing a single large batch - /// + /// Default: . Better performance when writing small batches, than single large batch. /// public int FullBatchSizeWriteLimit { get; set; } = 10; /// /// Gets or sets whether to use the locking queue, instead of a lock-free concurrent queue /// - /// - /// The locking queue is less concurrent when many logger threads, but reduces memory allocation - /// + /// Default: . Queue with Monitor.Lock is less concurrent when many logger threads, but reduces memory allocation /// public bool ForceLockingQueue { get => _forceLockingQueue ?? false; set => _forceLockingQueue = value; } private bool? _forceLockingQueue; diff --git a/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs b/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs index 3e7f1055ee..87c0c11c2c 100644 --- a/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/AutoFlushTargetWrapper.cs @@ -63,14 +63,14 @@ public class AutoFlushTargetWrapper : WrapperTargetBase /// Gets or sets the condition expression. Log events who meet this condition will cause /// a flush on the wrapped target. /// + /// Default: /// public ConditionExpression? Condition { get; set; } /// /// Delay the flush until the LogEvent has been confirmed as written /// - /// If not explicitly set, then disabled by default for and AsyncTaskTarget - /// + /// Default: . When not explicitly set, then automatically disabled when or AsyncTaskTarget /// public bool AsyncFlush { @@ -82,6 +82,7 @@ public bool AsyncFlush /// /// Only flush when LogEvent matches condition. Ignore explicit-flush, config-reload-flush and shutdown-flush /// + /// Default: /// public bool FlushOnConditionOnly { get; set; } diff --git a/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs b/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs index 698ec57c57..fa69dc6180 100644 --- a/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/BufferingTargetWrapper.cs @@ -122,6 +122,7 @@ public BufferingTargetWrapper(Target wrappedTarget, int bufferSize, int flushTim /// /// Gets or sets the number of log events to be buffered. /// + /// Default: /// public Layout BufferSize { get; set; } @@ -129,6 +130,7 @@ public BufferingTargetWrapper(Target wrappedTarget, int bufferSize, int flushTim /// Gets or sets the timeout (in milliseconds) after which the contents of buffer will be flushed /// if there's no write in the specified period of time. Use -1 to disable timed flushes. /// + /// Default: . Zero or Negative means disabled. /// public Layout FlushTimeout { get; set; } @@ -136,8 +138,9 @@ public BufferingTargetWrapper(Target wrappedTarget, int bufferSize, int flushTim /// Gets or sets a value indicating whether to use sliding timeout. /// /// - /// This value determines how the inactivity period is determined. If sliding timeout is enabled, - /// the inactivity timer is reset after each write, if it is disabled - inactivity timer will + /// Default: . + /// This value determines how the inactivity period is determined. When + /// the inactivity timer is reset after each write, if - inactivity timer will /// count from the first event written to the buffer. /// /// @@ -147,10 +150,9 @@ public BufferingTargetWrapper(Target wrappedTarget, int bufferSize, int flushTim /// Gets or sets the action to take if the buffer overflows. /// /// - /// Setting to will replace the - /// oldest event with new events without sending events down to the wrapped target, and - /// setting to will flush the - /// entire buffer to the wrapped target. + /// Default: . Setting to + /// will flush the entire buffer to the wrapped target. Setting to + /// will replace the oldest event with new events without sending events down to the wrapped target. /// /// public BufferingTargetWrapperOverflowAction OverflowAction { get; set; } diff --git a/src/NLog/Targets/Wrappers/FallbackGroupTarget.cs b/src/NLog/Targets/Wrappers/FallbackGroupTarget.cs index 26fd3fd2b9..858397a166 100644 --- a/src/NLog/Targets/Wrappers/FallbackGroupTarget.cs +++ b/src/NLog/Targets/Wrappers/FallbackGroupTarget.cs @@ -93,12 +93,14 @@ public FallbackGroupTarget(params Target[] targets) /// /// Gets or sets a value indicating whether to return to the first target after any successful write. /// + /// Default: /// public bool ReturnToFirstOnSuccess { get; set; } /// /// Gets or sets whether to enable batching, but fallback will be handled individually /// + /// Default: /// public bool EnableBatchWrite { get; set; } = true; diff --git a/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs b/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs index e520e6063a..5f8eaf56bc 100644 --- a/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/FilteringTargetWrapper.cs @@ -97,12 +97,14 @@ public FilteringTargetWrapper(Target wrappedTarget, ConditionExpression conditio /// Gets or sets the condition expression. Log events who meet this condition will be forwarded /// to the wrapped target. /// + /// Default: /// public ConditionExpression? Condition { get => (Filter as ConditionBasedFilter)?.Condition; set => Filter = CreateFilter(value); } /// /// Gets or sets the filter. Log events who evaluates to will be discarded /// + /// [Required] Default: /// public Filter Filter { get; set; } diff --git a/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs b/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs index e0106f4930..96c2a153f9 100644 --- a/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/GroupByTargetWrapper.cs @@ -53,6 +53,7 @@ public class GroupByTargetWrapper : WrapperTargetBase /// /// Identifier to perform group-by /// + /// [Required] Default: public Layout Key { get; set; } = Layout.Empty; /// diff --git a/src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs b/src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs index 4fc3e06274..4a21295ec1 100644 --- a/src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/LimitingTargetWrapper.cs @@ -95,18 +95,14 @@ public LimitingTargetWrapper(Target wrappedTarget, int messageLimit, TimeSpan in /// /// Gets or sets the maximum allowed number of messages written per . /// - /// - /// Messages received after has been reached in the current will be discarded. - /// + /// Default: . Messages received after has been reached in the current will be discarded. /// public Layout MessageLimit { get; set; } /// /// Gets or sets the interval in which messages will be written up to the number of messages. /// - /// - /// Messages received after has been reached in the current will be discarded. - /// + /// Default: hour. Messages received after has been reached in the current will be discarded. /// public Layout Interval { get; set; } diff --git a/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs b/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs index f65d2e1287..3ee07ec2cc 100644 --- a/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/PostFilteringTargetWrapper.cs @@ -94,6 +94,7 @@ public PostFilteringTargetWrapper(string name, Target wrappedTarget) /// /// Gets or sets the default filter to be applied when no specific rule matches. /// + /// Default: /// public ConditionExpression? DefaultFilter { get; set; } diff --git a/src/NLog/Targets/Wrappers/RepeatingTargetWrapper.cs b/src/NLog/Targets/Wrappers/RepeatingTargetWrapper.cs index 32220b5399..c1962f4904 100644 --- a/src/NLog/Targets/Wrappers/RepeatingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/RepeatingTargetWrapper.cs @@ -91,6 +91,7 @@ public RepeatingTargetWrapper(Target wrappedTarget, int repeatCount) /// /// Gets or sets the number of times to repeat each log message. /// + /// Default: /// public int RepeatCount { get; set; } = 3; diff --git a/src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs b/src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs index e5eb8c64d5..53834b4d54 100644 --- a/src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs +++ b/src/NLog/Targets/Wrappers/RetryingTargetWrapper.cs @@ -101,18 +101,21 @@ public RetryingTargetWrapper(Target wrappedTarget, int retryCount, int retryDela /// /// Gets or sets the number of retries that should be attempted on the wrapped target in case of a failure. /// + /// Default: /// public Layout RetryCount { get; set; } /// /// Gets or sets the time to wait between retries in milliseconds. /// + /// Default: ms /// public Layout RetryDelayMilliseconds { get; set; } /// /// Gets or sets whether to enable batching, and only apply single delay when a whole batch fails /// + /// Default: /// public bool EnableBatchWrite { get; set; } = true; diff --git a/src/NLog/Targets/Wrappers/WrapperTargetBase.cs b/src/NLog/Targets/Wrappers/WrapperTargetBase.cs index 0aa6ce97ab..b13247c543 100644 --- a/src/NLog/Targets/Wrappers/WrapperTargetBase.cs +++ b/src/NLog/Targets/Wrappers/WrapperTargetBase.cs @@ -44,6 +44,7 @@ public abstract class WrapperTargetBase : Target /// /// Gets or sets the target that is wrapped by this target. /// + /// [Required] Default: /// public Target? WrappedTarget { From e360886052548178b1a4448991ff74afe1a32678 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 12 Jul 2025 13:54:10 +0200 Subject: [PATCH 203/224] SplunkTarget - NetworkTarget with SplunkLayout (#5926) --- .../Layouts/GelfLayout.cs | 49 ++- .../Layouts/SplunkLayout.cs | 275 +++++++++++++++ .../Layouts/SyslogLayout.cs | 47 ++- .../NetworkLogEventDroppedEventArgs.cs | 4 +- .../Targets/SplunkTarget.cs | 95 ++++++ .../NetworkTargetTests.cs | 2 + .../SplunkLayoutTests.cs | 312 ++++++++++++++++++ 7 files changed, 767 insertions(+), 17 deletions(-) create mode 100644 src/NLog.Targets.Network/Layouts/SplunkLayout.cs create mode 100644 src/NLog.Targets.Network/Targets/SplunkTarget.cs create mode 100644 tests/NLog.Targets.Network.Tests/SplunkLayoutTests.cs diff --git a/src/NLog.Targets.Network/Layouts/GelfLayout.cs b/src/NLog.Targets.Network/Layouts/GelfLayout.cs index d4b9a31ec6..1741027f04 100644 --- a/src/NLog.Targets.Network/Layouts/GelfLayout.cs +++ b/src/NLog.Targets.Network/Layouts/GelfLayout.cs @@ -1,13 +1,46 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using NLog.Config; -using NLog.LayoutRenderers; -using NLog.Targets; +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// namespace NLog.Layouts { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using NLog.Config; + using NLog.LayoutRenderers; + using NLog.Targets; + /// /// GELF (Graylog Extended Log Format) is a JSON-based, structured log format for Graylog Log Management. /// @@ -489,7 +522,7 @@ internal static SyslogLevel ToSyslogLevel(LogLevel logLevel) private readonly string _beginJsonPropertyName = ",\""; private readonly string _completeJsonPropertyName = "\":"; private const string GelfVersion11 = "1.1"; - private static DateTime UnixDateStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + private static readonly DateTime UnixDateStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); private const int ShortMessageMaxLength = 250; private const int FullMessageMaxLength = 16383; // Truncate due to: https://github.com/Graylog2/graylog2-server/issues/873 diff --git a/src/NLog.Targets.Network/Layouts/SplunkLayout.cs b/src/NLog.Targets.Network/Layouts/SplunkLayout.cs new file mode 100644 index 0000000000..50ae3e23e1 --- /dev/null +++ b/src/NLog.Targets.Network/Layouts/SplunkLayout.cs @@ -0,0 +1,275 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Layouts +{ + using System; + using System.Collections.Generic; + using NLog.Config; + + /// + /// Splunk JSON-based, structured log format for Splunk Log Management. + /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki + /// + /// { + /// "time": 1426279439, // epoch time + /// "host": "localhost", + /// "source": "random-data-generator", + /// "sourcetype": "my_sample_data", + /// "index": "main", + /// "event": { + /// "message": "Something happened", + /// "level": "Info" + /// } + /// } + /// + [Layout("SplunkLayout")] + [ThreadAgnostic] + [AppDomainFixedOutput] + public class SplunkLayout : JsonLayout + { + private static readonly DateTime UnixDateStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + /// + /// Gets the array of attributes for the "event"-section + /// + [ArrayParameter(typeof(JsonAttribute), "splunkevent")] + public IList? SplunkEvents + { + get + { + var index = LookupNamedAttributeIndex("event"); + return index >= 0 ? (Attributes[index]?.Layout as JsonLayout)?.Attributes : null; + } + } + + /// + /// Gets or sets Splunk Message Host-attribute + /// + public Layout SplunkHostName + { + get + { + var index = LookupNamedAttributeIndex("host"); + return index >= 0 ? Attributes[index].Layout : Layout.Empty; + } + set + { + var index = LookupNamedAttributeIndex("host"); + if (index >= 0) + Attributes[index].Layout = value; + } + } + + /// + /// Gets or sets Splunk Message Source-attribute. Example the name of the application. + /// + public Layout SplunkSourceName + { + get + { + var index = LookupNamedAttributeIndex("source"); + return index >= 0 ? Attributes[index].Layout : Layout.Empty; + } + set + { + var index = LookupNamedAttributeIndex("source"); + if (index >= 0) + Attributes[index].Layout = value; + } + } + + /// + /// Gets or sets Splunk Message SourceType-attribute. SourceType can be used hint for choosing Splunk Indexer + /// + public Layout SplunkSourceType + { + get + { + var index = LookupNamedAttributeIndex("sourcetype"); + return index >= 0 ? Attributes[index].Layout : Layout.Empty; + } + set + { + var index = LookupNamedAttributeIndex("sourcetype"); + if (index >= 0) + Attributes[index].Layout = value; + } + } + + /// + /// Gets or sets Splunk Message Index-attribute, that controls which event data is to be indexed. + /// + public Layout SplunkIndex + { + get + { + var index = LookupNamedAttributeIndex("index"); + return index >= 0 ? Attributes[index].Layout : Layout.Empty; + } + set + { + var index = LookupNamedAttributeIndex("index"); + if (index >= 0) + Attributes[index].Layout = value; + } + } + + /// + /// Gets or sets the option to include all properties from the log events + /// + public new bool IncludeEventProperties + { + get + { + var index = LookupNamedAttributeIndex("event"); + return index >= 0 && (Attributes[index].Layout as JsonLayout)?.IncludeEventProperties == true; + } + set + { + var index = LookupNamedAttributeIndex("event"); + if (index >= 0 && Attributes[index].Layout is JsonLayout jsonLayout) + jsonLayout.IncludeEventProperties = value; + } + } + + /// + /// Gets or sets whether to include the contents of the properties-dictionary. + /// + public new bool IncludeScopeProperties + { + get + { + var index = LookupNamedAttributeIndex("event"); + return index >= 0 && (Attributes[index].Layout as JsonLayout)?.IncludeScopeProperties == true; + } + set + { + var index = LookupNamedAttributeIndex("event"); + if (index >= 0 && Attributes[index].Layout is JsonLayout jsonLayout) + jsonLayout.IncludeScopeProperties = value; + } + } + + /// + /// Gets or sets the option to exclude null/empty properties from the log event (as JSON) + /// + public new bool ExcludeEmptyProperties + { + get + { + var index = LookupNamedAttributeIndex("event"); + return index >= 0 && (Attributes[index].Layout as JsonLayout)?.ExcludeEmptyProperties == true; + } + set + { + var index = LookupNamedAttributeIndex("event"); + if (index >= 0 && Attributes[index].Layout is JsonLayout jsonLayout) + jsonLayout.ExcludeEmptyProperties = value; + } + } + + /// + /// List of property names to exclude when is + /// +#if NET35 + public new HashSet? ExcludeProperties +#else + public new ISet? ExcludeProperties +#endif + { + get + { + var index = LookupNamedAttributeIndex("event"); + return index >= 0 && Attributes[index].Layout is JsonLayout jsonLayout ? jsonLayout.ExcludeProperties : null; + } + set + { + var index = LookupNamedAttributeIndex("event"); + if (index >= 0 && Attributes[index].Layout is JsonLayout jsonLayout && value != null) + jsonLayout.ExcludeProperties = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + public SplunkLayout() + { + Attributes.Add(new JsonAttribute("time", Layout.FromMethod((evt) => ToUnixTimeStamp(evt.TimeStamp))) { Encode = false }); + Attributes.Add(new JsonAttribute("host", "${hostname}")); + Attributes.Add(new JsonAttribute("source", "${processname}")); + Attributes.Add(new JsonAttribute("sourcetype", Layout.Empty)); + Attributes.Add(new JsonAttribute("index", Layout.Empty)); + Attributes.Add(new JsonAttribute("event", new JsonLayout() { IncludeEventProperties = true }) { Encode = false }); + } + + /// + protected override void InitializeLayout() + { + var index = LookupNamedAttributeIndex("event"); + if (index >= 0 && Attributes[index].Layout is JsonLayout jsonLayout && jsonLayout.Attributes.Count == 0) + { + jsonLayout.Attributes.Add(new JsonAttribute("level", "${level}")); + jsonLayout.Attributes.Add(new JsonAttribute("message", "${message}")); + jsonLayout.Attributes.Add(new JsonAttribute("logger", "${logger}")); + jsonLayout.Attributes.Add(new JsonAttribute("exception_type", "${exception:Format=Type}")); + jsonLayout.Attributes.Add(new JsonAttribute("exception_msg", "${exception:Format=Message}")); + jsonLayout.Attributes.Add(new JsonAttribute("exception", "${exception:Format=ToString}")); + } + + base.InitializeLayout(); + } + + internal static decimal ToUnixTimeStamp(DateTime timeStamp) + { + return Convert.ToDecimal(timeStamp.ToUniversalTime().Subtract(UnixDateStart).TotalSeconds); + } + + private int LookupNamedAttributeIndex(string attributeName) + { + for (int i = 0; i < Attributes.Count; ++i) + { + if (attributeName.Equals(Attributes[i].Name, StringComparison.OrdinalIgnoreCase)) + { + return i; + } + } + return -1; + } + } +} diff --git a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs index 5397f38e01..f06ece418a 100644 --- a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs +++ b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs @@ -1,13 +1,46 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using NLog.Config; -using NLog.LayoutRenderers; -using NLog.Targets; +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// namespace NLog.Layouts { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using NLog.Config; + using NLog.LayoutRenderers; + using NLog.Targets; + /// /// A specialized layout that renders Syslog-formatted events in format Rfc3164 / Rfc5424 /// diff --git a/src/NLog.Targets.Network/Targets/NetworkLogEventDroppedEventArgs.cs b/src/NLog.Targets.Network/Targets/NetworkLogEventDroppedEventArgs.cs index a639a0634c..2ffb747028 100644 --- a/src/NLog.Targets.Network/Targets/NetworkLogEventDroppedEventArgs.cs +++ b/src/NLog.Targets.Network/Targets/NetworkLogEventDroppedEventArgs.cs @@ -31,10 +31,10 @@ // THE POSSIBILITY OF SUCH DAMAGE. // -using System; - namespace NLog.Targets { + using System; + /// /// Arguments for events. /// diff --git a/src/NLog.Targets.Network/Targets/SplunkTarget.cs b/src/NLog.Targets.Network/Targets/SplunkTarget.cs new file mode 100644 index 0000000000..717ce7e53b --- /dev/null +++ b/src/NLog.Targets.Network/Targets/SplunkTarget.cs @@ -0,0 +1,95 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets +{ + using System.Collections.Generic; + using NLog.Layouts; + + /// + /// Sends log messages to Splunk server using either TCP or UDP with Splunk-JSON-format + /// + /// + /// See NLog Wiki + /// + /// Documentation on NLog Wiki + [Target("Splunk")] + public class SplunkTarget : NetworkTarget + { + private readonly SplunkLayout _splunkLayout = new SplunkLayout(); + + /// + public Layout SplunkHostName { get => _splunkLayout.SplunkHostName; set => _splunkLayout.SplunkHostName = value; } + + /// + public Layout SplunkSourceName { get => _splunkLayout.SplunkSourceName; set => _splunkLayout.SplunkSourceName = value; } + + /// + public Layout SplunkSourceType { get => _splunkLayout.SplunkSourceType; set => _splunkLayout.SplunkSourceType = value; } + + /// + public Layout SplunkIndex { get => _splunkLayout.SplunkIndex; set => _splunkLayout.SplunkIndex = value; } + + /// + public bool IncludeEventProperties { get => _splunkLayout.IncludeEventProperties; set => _splunkLayout.IncludeEventProperties = value; } + + /// + public bool IncludeScopeProperties { get => _splunkLayout.IncludeScopeProperties; set => _splunkLayout.IncludeScopeProperties = value; } + + /// + public bool ExcludeEmptyProperties { get => _splunkLayout.ExcludeEmptyProperties; set => _splunkLayout.ExcludeEmptyProperties = value; } + + /// +#if NET35 + public HashSet? ExcludeProperties { get => _splunkLayout.ExcludeProperties; set => _splunkLayout.ExcludeProperties = value; } +#else + public ISet? ExcludeProperties { get => _splunkLayout.ExcludeProperties; set => _splunkLayout.ExcludeProperties = value; } +#endif + + /// + public override Layout Layout + { + get => _splunkLayout; + set { /* Fixed SplunkLayout */ } // NOSONAR + } + + /// + /// Initializes a new instance of the class. + /// + public SplunkTarget() + { + LineEnding = LineEndingMode.LF; + Layout = _splunkLayout; + } + } +} diff --git a/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs index 8bfc017267..2a35bcca6d 100644 --- a/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs +++ b/tests/NLog.Targets.Network.Tests/NetworkTargetTests.cs @@ -1177,9 +1177,11 @@ public void AsyncConnectionFailureOnCloseShouldNotDeadlock() [Theory] [InlineData("none", SslProtocols.None)] //we can't set it on "" +#pragma warning disable SYSLIB0039 // Type or member is obsolete [InlineData("tls", SslProtocols.Tls)] [InlineData("tls11", SslProtocols.Tls11)] [InlineData("tls,tls11", SslProtocols.Tls11 | SslProtocols.Tls)] +#pragma warning restore SYSLIB0039 // Type or member is obsolete #if NET6_0_OR_GREATER [InlineData("Tls13", SslProtocols.Tls13)] #endif diff --git a/tests/NLog.Targets.Network.Tests/SplunkLayoutTests.cs b/tests/NLog.Targets.Network.Tests/SplunkLayoutTests.cs new file mode 100644 index 0000000000..be9954af05 --- /dev/null +++ b/tests/NLog.Targets.Network.Tests/SplunkLayoutTests.cs @@ -0,0 +1,312 @@ +// +// Copyright (c) 2004-2024 Jaroslaw Kowalski , Kim Christensen, Julian Verdurmen +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of Jaroslaw Kowalski nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace NLog.Targets.Network +{ + using System; + using NLog.Layouts; + using Xunit; + + public class SplunkLayoutTests + { + private static readonly string HostName = ResolveHostname(); + + public SplunkLayoutTests() + { + LogManager.ThrowExceptions = true; + LogManager.Setup().SetupExtensions(ext => + { + ext.RegisterLayout(); + }); + } + + [Fact] + public void CanRenderSplunk() + { + var splunkLayout = new SplunkLayout(); + + var memTarget = new NLog.Targets.MemoryTarget() { Layout = splunkLayout }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + { + var loggerName = "TestLogger"; + var dateTime = DateTime.Now; + var message = "hello, splunk :)"; + var logLevel = LogLevel.Info; + + var logEvent = new LogEventInfo + { + LoggerName = loggerName, + Level = logLevel, + Message = message, + TimeStamp = dateTime, + }; + + logFactory.GetLogger(loggerName).Log(logEvent); + Assert.Single(memTarget.Logs); + var renderedSplunk = memTarget.Logs[0]; + + var expectedDateTime = SplunkLayout.ToUnixTimeStamp(dateTime); + var processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName; + var expectedSplunk = string.Format(System.Globalization.CultureInfo.InvariantCulture, + "{{" + "\"time\":{0}," + + "\"host\":\"{1}\"," + + "\"source\":\"{2}\"," + + "\"event\":{{" + + "\"level\":\"{3}\"," + + "\"message\":\"{4}\"," + + "\"logger\":\"{5}\"" + + "}}}}", + expectedDateTime, + HostName, + processName, + logLevel, + message, + loggerName); + + Assert.Equal(expectedSplunk, renderedSplunk); + } + } + + [Fact] + public void CanRenderSplunkSourceType() + { + var splunkLayout = new SplunkLayout(); + + var memTarget = new NLog.Targets.MemoryTarget() { Layout = splunkLayout }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget)).LogFactory) + { + var loggerName = "TestLogger"; + var facility = "TestFacility"; + var dateTime = DateTime.Now; + var message = "hello, splunk :)"; + var logLevel = LogLevel.Info; + + splunkLayout.SplunkSourceType = facility; + + var logEvent = new LogEventInfo + { + LoggerName = loggerName, + Level = logLevel, + Message = message, + TimeStamp = dateTime, + }; + + logFactory.GetLogger(loggerName).Log(logEvent); + Assert.Single(memTarget.Logs); + var renderedSplunk = memTarget.Logs[0]; + + var expectedDateTime = SplunkLayout.ToUnixTimeStamp(dateTime); + var processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName; + var expectedSplunk = string.Format(System.Globalization.CultureInfo.InvariantCulture, + "{{" + "\"time\":{0}," + + "\"host\":\"{1}\"," + + "\"source\":\"{2}\"," + + "\"sourcetype\":\"{3}\"," + + "\"event\":{{" + + "\"level\":\"{4}\"," + + "\"message\":\"{5}\"," + + "\"logger\":\"{6}\"" + + "}}}}", + expectedDateTime, + HostName, + processName, + facility, + logLevel, + message, + loggerName); + + Assert.Equal(expectedSplunk, renderedSplunk); + } + } + + [Fact] + public void CanRenderSplunkAdditionalEventCustomMessage() + { + var splunkLayout = new SplunkLayout(); + splunkLayout.SplunkEvents.Add(new JsonAttribute("threadid", "${threadid}") { Encode = false }); + + var memTarget = new NLog.Targets.MemoryTarget() { Layout = splunkLayout }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget).WithAsync()).LogFactory) + { + var loggerName = "TestLogger"; + var dateTime = DateTime.Now; + var message = "hello, splunk :)"; + var logLevel = LogLevel.Info; + + var logEvent = new LogEventInfo + { + LoggerName = loggerName, + Level = logLevel, + Message = message, + TimeStamp = dateTime, + }; + + logFactory.GetLogger(loggerName).Log(logEvent); + logFactory.Flush(); + Assert.Single(memTarget.Logs); + var renderedSplunk = memTarget.Logs[0]; + + int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId; + var expectedDateTime = SplunkLayout.ToUnixTimeStamp(dateTime); + var processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName; + var expectedSplunk = string.Format(System.Globalization.CultureInfo.InvariantCulture, + "{{" + "\"time\":{0}," + + "\"host\":\"{1}\"," + + "\"source\":\"{2}\"," + + "\"event\":{{" + + "\"threadid\":{3}" + + "}}}}", + expectedDateTime, + HostName, + processName, + threadId); + + Assert.Equal(expectedSplunk, renderedSplunk); + } + } + + [Fact] + public void CanRenderEventProperties() + { + var splunkLayout = new SplunkLayout(); + splunkLayout.SplunkEvents.Add(new JsonAttribute("mt", "${message:raw=true}")); + splunkLayout.IncludeEventProperties = true; + + var memTarget = new NLog.Targets.MemoryTarget() { Layout = splunkLayout }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget).WithAsync()).LogFactory) + { + var loggerName = "TestLogger"; + var dateTime = DateTime.Now; + var message = "hello, {world} from {RequestId} :)"; + var logLevel = LogLevel.Info; + var requestId = Guid.NewGuid(); + + var logEvent = new LogEventInfo + { + TimeStamp = dateTime, + LoggerName = loggerName, + Level = logLevel, + Message = message, + Parameters = new object[] { "Splunk", requestId }, + }; + + logFactory.GetLogger(loggerName).Log(logEvent); + logFactory.Flush(); + Assert.Single(memTarget.Logs); + var renderedSplunk = memTarget.Logs[0]; + + var expectedDateTime = SplunkLayout.ToUnixTimeStamp(dateTime); + var processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName; + var expectedSplunk = string.Format(System.Globalization.CultureInfo.InvariantCulture, + "{{" + "\"time\":{0}," + + "\"host\":\"{1}\"," + + "\"source\":\"{2}\"," + + "\"event\":{{" + + "\"mt\":\"{3}\"," + + "\"world\":\"Splunk\"," + + "\"RequestId\":\"{4}\"" + + "}}}}", + expectedDateTime, + HostName, + processName, + message, + requestId); + + Assert.Equal(expectedSplunk, renderedSplunk); + } + } + + [Fact] + public void CanRenderScopeContext() + { + var splunkLayout = new SplunkLayout(); + splunkLayout.SplunkEvents.Add(new JsonAttribute("mt", "${message:raw=true}")); + splunkLayout.IncludeEventProperties = true; + splunkLayout.IncludeScopeProperties = true; + splunkLayout.ExcludeProperties.Add("World"); + + var memTarget = new NLog.Targets.MemoryTarget() { Layout = splunkLayout }; + using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget).WithAsync()).LogFactory) + { + var loggerName = "TestLogger"; + var dateTime = DateTime.Now; + var message = "hello, {world} :)"; + var logLevel = LogLevel.Info; + + var logEvent = new LogEventInfo + { + TimeStamp = dateTime, + LoggerName = loggerName, + Level = logLevel, + Message = message, + Parameters = new object[] { "Splunk" }, + }; + + var requestId = Guid.NewGuid(); + using (logFactory.GetLogger(loggerName).PushScopeProperty("RequestId", requestId)) + { + logFactory.GetLogger(loggerName).Log(logEvent); + } + logFactory.Flush(); + Assert.Single(memTarget.Logs); + var renderedSplunk = memTarget.Logs[0]; + + var expectedDateTime = SplunkLayout.ToUnixTimeStamp(dateTime); + var processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName; + var expectedSplunk = string.Format(System.Globalization.CultureInfo.InvariantCulture, + "{{" + "\"time\":{0}," + + "\"host\":\"{1}\"," + + "\"source\":\"{2}\"," + + "\"event\":{{" + + "\"mt\":\"{3}\"," + + "\"RequestId\":\"{4}\"" + + "}}}}", + expectedDateTime, + HostName, + processName, + message, + requestId); + + Assert.Equal(expectedSplunk, renderedSplunk); + } + } + + static string ResolveHostname() + { + return Environment.GetEnvironmentVariable("HOSTNAME") + ?? Environment.GetEnvironmentVariable("COMPUTERNAME") + ?? Environment.GetEnvironmentVariable("MACHINENAME") + ?? Environment.MachineName; + } + } +} From 308cf2d6276170f2d09addb779cc42e60f628574 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 13 Jul 2025 18:56:59 +0200 Subject: [PATCH 204/224] GelfLayout - Align with SplunkLayout (#5927) --- src/NLog.Targets.Network/Layouts/GelfLayout.cs | 8 ++++---- tests/NLog.Targets.Network.Tests/GelfLayoutTests.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NLog.Targets.Network/Layouts/GelfLayout.cs b/src/NLog.Targets.Network/Layouts/GelfLayout.cs index 1741027f04..b4735872af 100644 --- a/src/NLog.Targets.Network/Layouts/GelfLayout.cs +++ b/src/NLog.Targets.Network/Layouts/GelfLayout.cs @@ -173,11 +173,11 @@ protected override void InitializeLayout() { if (GelfFields.Count == 0) { - GelfFields.Add(new TargetPropertyWithContext("_logLevel", "${level}")); + GelfFields.Add(new TargetPropertyWithContext("_loglevel", "${level}")); GelfFields.Add(new TargetPropertyWithContext("_logger", "${logger}")); - GelfFields.Add(new TargetPropertyWithContext("_exceptionType", "${exception:Format=Type}") { IncludeEmptyValue = false }); - GelfFields.Add(new TargetPropertyWithContext("_exceptionMessage", "${exception:Format=Message}") { IncludeEmptyValue = false }); - GelfFields.Add(new TargetPropertyWithContext("_stackTrace", "${exception:Format=ToString}") { IncludeEmptyValue = false }); + GelfFields.Add(new TargetPropertyWithContext("_exception_type", "${exception:Format=Type}")); + GelfFields.Add(new TargetPropertyWithContext("_exception_msg", "${exception:Format=Message}")); + GelfFields.Add(new TargetPropertyWithContext("_exception", "${exception:Format=ToString}")); } // CompoundLayout includes optimization, so only doing precalculate/caching of relevant Layouts (instead of the entire GELF-message) diff --git a/tests/NLog.Targets.Network.Tests/GelfLayoutTests.cs b/tests/NLog.Targets.Network.Tests/GelfLayoutTests.cs index efa3f941ec..758c717511 100644 --- a/tests/NLog.Targets.Network.Tests/GelfLayoutTests.cs +++ b/tests/NLog.Targets.Network.Tests/GelfLayoutTests.cs @@ -82,7 +82,7 @@ public void CanRenderGelf() + "\"short_message\":\"{1}\"," + "\"timestamp\":{2}," + "\"level\":{3}," - + "\"_logLevel\":\"{4}\"," + + "\"_loglevel\":\"{4}\"," + "\"_logger\":\"{5}\"" + "}}", HostName, From 93506c3a9eca2116e8268da68477b3530e3c49fe Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 15 Jul 2025 11:27:53 +0200 Subject: [PATCH 205/224] XmlLoggingConfiguration - Improve handling of invalid XML (#5929) --- .../Config/LoggingConfigurationFileLoader.cs | 3 ++- src/NLog/Config/XmlLoggingConfiguration.cs | 5 ++-- tests/NLog.UnitTests/Config/ReloadTests.cs | 9 +++---- tests/NLog.UnitTests/LogFactoryTests.cs | 26 +++++++++++++++++-- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/NLog/Config/LoggingConfigurationFileLoader.cs b/src/NLog/Config/LoggingConfigurationFileLoader.cs index 92df616217..672daff1d4 100644 --- a/src/NLog/Config/LoggingConfigurationFileLoader.cs +++ b/src/NLog/Config/LoggingConfigurationFileLoader.cs @@ -142,7 +142,8 @@ private LoggingConfiguration LoadXmlLoggingConfigurationFile(LogFactory logFacto if (ex.MustBeRethrown() || (logFactory.ThrowConfigExceptions ?? logFactory.ThrowExceptions)) throw; - if (ThrowXmlConfigExceptions(configFile, ex is XmlParserException, logFactory, out var autoReload)) + var invalidXml = ex is XmlParserException || ex.InnerException is XmlParserException; + if (ThrowXmlConfigExceptions(configFile, invalidXml, logFactory, out var autoReload)) throw; return CreateEmptyDefaultConfig(configFile, logFactory, autoReload); diff --git a/src/NLog/Config/XmlLoggingConfiguration.cs b/src/NLog/Config/XmlLoggingConfiguration.cs index 42a14738b1..7faacd78b3 100644 --- a/src/NLog/Config/XmlLoggingConfiguration.cs +++ b/src/NLog/Config/XmlLoggingConfiguration.cs @@ -381,8 +381,9 @@ private void ParseFromTextReader(TextReader textReader, string? filePath) } catch (Exception exception) { - var configurationException = new NLogConfigurationException($"Exception when loading configuration {filePath}", exception); - InternalLogger.Error(exception, configurationException.Message); + var filePathValue = string.IsNullOrEmpty(filePath) ? "" : $" FilePath: {filePath}"; + var configurationException = new NLogConfigurationException($"Failed loading NLog configuration. {exception.Message} {filePathValue}", exception); + InternalLogger.Error(configurationException, configurationException.Message); throw configurationException; } } diff --git a/tests/NLog.UnitTests/Config/ReloadTests.cs b/tests/NLog.UnitTests/Config/ReloadTests.cs index afbb3175ad..dc5d600b5f 100644 --- a/tests/NLog.UnitTests/Config/ReloadTests.cs +++ b/tests/NLog.UnitTests/Config/ReloadTests.cs @@ -718,12 +718,11 @@ public void TestReloadingInvalidConfiguration() } } - [Fact] - public void TestThrowExceptionWhenInvalidXml() + [Theory] + [InlineData(@"")] + [InlineData(@"")] + public void TestThrowExceptionWhenInvalidXml(string invalidXmlConfig) { - var invalidXmlConfig = @" - "; - string tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(tempDir); diff --git a/tests/NLog.UnitTests/LogFactoryTests.cs b/tests/NLog.UnitTests/LogFactoryTests.cs index 6d7f088f6d..5f17492d4b 100644 --- a/tests/NLog.UnitTests/LogFactoryTests.cs +++ b/tests/NLog.UnitTests/LogFactoryTests.cs @@ -155,7 +155,7 @@ public void DisposeAsync_TargetTimeout_Async() #endif [Fact] - public void InvalidXMLConfiguration_DoesNotThrowErrorWhen_ThrowExceptionFlagIsNotSet() + public void InvalidXMLConfigurationValue_DoesNotThrowErrorWhen_ThrowExceptionFlagIsNotSet() { using (new NoThrowNLogExceptions()) { @@ -171,7 +171,7 @@ public void InvalidXMLConfiguration_DoesNotThrowErrorWhen_ThrowExceptionFlagIsNo } [Fact] - public void InvalidXMLConfiguration_ThrowErrorWhen_ThrowExceptionFlagIsSet() + public void InvalidXMLConfigurationValue_ThrowErrorWhen_ThrowExceptionFlagIsSet() { Boolean ExceptionThrown = false; try @@ -192,6 +192,28 @@ public void InvalidXMLConfiguration_ThrowErrorWhen_ThrowExceptionFlagIsSet() Assert.True(ExceptionThrown); } + [Fact] + public void InvalidXMLConfiguration_ThrowErrorWhen_ThrowExceptionFlagIsSet() + { + Boolean ExceptionThrown = false; + try + { + new LogFactory().Setup().LoadConfigurationFromXml(@" + + + + + + "); + } + catch (Exception) + { + ExceptionThrown = true; + } + + Assert.True(ExceptionThrown); + } + [Fact] [Obsolete("Replaced by LogFactory.Setup().LoadConfigurationFromFile(). Marked obsolete on NLog 5.2")] public void Configuration_InaccessibleNLog_doesNotThrowException() From 7dabd6d87a7b57a5fb1f68006410b45173aa9b5a Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 15 Jul 2025 14:15:13 +0200 Subject: [PATCH 206/224] XmlParser - Handle XML comments after root-end-tag (#5930) --- src/NLog/Internal/XmlParser.cs | 5 +++++ src/NLog/Targets/FileTarget.cs | 2 +- .../ConcurrentFileTargetTests.cs | 8 +++++++- tests/NLog.UnitTests/Internal/XmlParserTests.cs | 4 ++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/NLog/Internal/XmlParser.cs b/src/NLog/Internal/XmlParser.cs index 17d7d763a8..d11fdb1176 100644 --- a/src/NLog/Internal/XmlParser.cs +++ b/src/NLog/Internal/XmlParser.cs @@ -115,6 +115,11 @@ public XmlParserElement LoadDocument(out IList? processingInst throw new XmlParserException($"Invalid XML document. Cannot parse end-tag: {currentRoot.Name}"); SkipWhiteSpaces(); + while (_xmlSource.Peek() == '!' && _xmlSource.Current == '<') + { + _xmlSource.MoveNext(); + SkipXmlComment(); + } if (_xmlSource.MoveNext()) throw new XmlParserException($"Invalid XML document. Unexpected characters after end-tag: {currentRoot.Name}"); diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index 8279c8557b..b3bec9ee45 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -464,7 +464,7 @@ private int OpenFileMonitorTimerInterval get { if (OpenFileFlushTimeout <= 0 || AutoFlush || !KeepFileOpen) - return OpenFileCacheTimeout; + return (OpenFileCacheTimeout > 500 && OpenFileCacheTimeout < 3600) ? 300 : OpenFileCacheTimeout; else if (OpenFileCacheTimeout <= 0) return OpenFileFlushTimeout; else diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs index 6c033f48ec..39d3b11d98 100644 --- a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs @@ -1177,7 +1177,13 @@ public void AutoFlushTest(bool autoFlush, bool keepFileOpen, int autoFlushTimeou AssertFileContents(logFile, string.Empty, Encoding.UTF8); if (autoFlushTimeout > 0) { - Thread.Sleep(TimeSpan.FromSeconds(autoFlushTimeout * 1.5)); + for (int i = 0; i < 3; ++i) + { + Thread.Sleep(TimeSpan.FromSeconds(autoFlushTimeout * 1.5)); + var fileInfo = new FileInfo(logFile); + if (fileInfo.Exists && fileInfo.Length > 0) + break; + } AssertFileContents(logFile, "Debug aaa\nInfo bbb\nWarn ccc\n", Encoding.UTF8); } } diff --git a/tests/NLog.UnitTests/Internal/XmlParserTests.cs b/tests/NLog.UnitTests/Internal/XmlParserTests.cs index 54028c8fea..b01e8b1c35 100644 --- a/tests/NLog.UnitTests/Internal/XmlParserTests.cs +++ b/tests/NLog.UnitTests/Internal/XmlParserTests.cs @@ -179,6 +179,10 @@ public void XmlParse_InvalidDocument(string xmlSource) [InlineData("\n\n")] [InlineData("\n\n")] [InlineData("")] + [InlineData("\n")] + [InlineData("")] + [InlineData("\n\n")] + [InlineData("\n\n\n")] [InlineData("")] [InlineData("\n")] [InlineData("")] From ccdb211d626b4fb0d460782a2f22c4e35432b6fa Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Tue, 15 Jul 2025 23:11:53 +0200 Subject: [PATCH 207/224] Fixed Sonar code smells (#5931) --- src/NLog/Internal/TargetWithFilterChain.cs | 1 + .../LocalIpAddressLayoutRendererTests.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NLog/Internal/TargetWithFilterChain.cs b/src/NLog/Internal/TargetWithFilterChain.cs index 0350545ad1..80c42d5050 100644 --- a/src/NLog/Internal/TargetWithFilterChain.cs +++ b/src/NLog/Internal/TargetWithFilterChain.cs @@ -379,6 +379,7 @@ public void WriteToLoggerTargets(Type loggerType, LogEventInfo logEvent, LogFact LoggerImpl.Write(loggerType, this, logEvent, logFactory); } + private #if !NETFRAMEWORK readonly #endif diff --git a/tests/NLog.Targets.Network.Tests/LocalIpAddressLayoutRendererTests.cs b/tests/NLog.Targets.Network.Tests/LocalIpAddressLayoutRendererTests.cs index 697de21abf..e39a9eb3a6 100644 --- a/tests/NLog.Targets.Network.Tests/LocalIpAddressLayoutRendererTests.cs +++ b/tests/NLog.Targets.Network.Tests/LocalIpAddressLayoutRendererTests.cs @@ -248,7 +248,7 @@ public void LocalIpAddress_RetrieverThrowsException_RenderEmptyString() internal sealed class NetworkInterfaceRetrieverBuilder { - private readonly IDictionary>> _ips = new Dictionary>>(); + private readonly Dictionary>> _ips = new Dictionary>>(); struct NetworkInfo { From b6c5e2511dc143da07ae4f32f52abffbd2d6277c Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 16 Jul 2025 12:41:42 +0200 Subject: [PATCH 208/224] Handle invalid message template when skipping parameters array (#5933) --- src/NLog/Logger.cs | 29 ++++++++++++++++++++++++++--- tests/NLog.UnitTests/LoggerTests.cs | 10 ++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/NLog/Logger.cs b/src/NLog/Logger.cs index 9a776052d0..0b423d5a55 100644 --- a/src/NLog/Logger.cs +++ b/src/NLog/Logger.cs @@ -639,9 +639,7 @@ private void WriteToTargetsWithSpan(ITargetWithFilterChain targetsForLevel, LogL if (templateEnumerator.MoveNext() && !templateEnumerator.Current.MaybePositionalTemplate) { // Convert parameters into Properties and skip Parameters-array-allocation (Like with Microsoft Extension Logging) - var formattedMessage = Factory.AutoMessageTemplateFormatter.Render(ref templateEnumerator, formatProvider, in args, out var messageTemplateParameters); - var logEvent = new LogEventInfo(level, Name, formattedMessage, message, messageTemplateParameters); - logEvent.Exception = exception; + LogEventInfo logEvent = RenderPreformattedLogEvent(Factory.AutoMessageTemplateFormatter, level, exception, formatProvider, message, args, ref templateEnumerator); WriteLogEventToTargets(logEvent, targetsForLevel); } else @@ -653,6 +651,31 @@ private void WriteToTargetsWithSpan(ITargetWithFilterChain targetsForLevel, LogL } } } + + private LogEventInfo RenderPreformattedLogEvent(LogMessageTemplateFormatter messageTemplateFormatter, LogLevel level, Exception? exception, IFormatProvider? formatProvider, string messageTemplate, in ReadOnlySpan args, ref MessageTemplates.TemplateEnumerator templateEnumerator) + { + string formattedMessage = messageTemplate; + IList? messageTemplateParameters = null; + + try + { + formattedMessage = messageTemplateFormatter.Render(ref templateEnumerator, formatProvider, in args, out messageTemplateParameters); + } + catch (Exception ex) + { + Common.InternalLogger.Warn(ex, "Error when formatting a message."); + if (ex.MustBeRethrown()) + throw; + } + + var logEvent = new LogEventInfo(level, Name, formattedMessage, messageTemplate, (IList?)null); + if (messageTemplateParameters is null) + logEvent.Parameters = args.ToArray(); // Failed to parse message-template, so lets captures message-format-parameters + else + logEvent.CreatePropertiesInternal(messageTemplateParameters); + logEvent.Exception = exception; + return logEvent; + } #endif #endregion diff --git a/tests/NLog.UnitTests/LoggerTests.cs b/tests/NLog.UnitTests/LoggerTests.cs index 9faac2749a..0709b30d6c 100644 --- a/tests/NLog.UnitTests/LoggerTests.cs +++ b/tests/NLog.UnitTests/LoggerTests.cs @@ -2393,9 +2393,15 @@ public void StructuredEventsTest1() if (enabled == 1) AssertDebugLastMessage("debug", "A|hello from 1=James, 2=Mike, 3=Jane"); logger.Error("message {a} {b}", 1, 2); - if (enabled == 1) + if (enabled == 1) AssertDebugLastMessage("debug", "A|message 1 2"); + + using (new NoThrowNLogExceptions()) { - AssertDebugLastMessage("debug", "A|message 1 2"); + logger.Error("invalid format {age:l}", 30); + if (enabled == 1) AssertDebugLastMessage("debug", "A|invalid format {age:l}"); + + logger.Error("invalid format {guid:l}", Guid.NewGuid()); + if (enabled == 1) AssertDebugLastMessage("debug", "A|invalid format {guid:l}"); } logger.Error("message{a}{b}{c}", 1, 2, 3); From 7098d50efc373b75cec5cdd0aaf667ff6454c9c9 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 18 Jul 2025 07:53:49 +0200 Subject: [PATCH 209/224] Improve NLog XSD Schema with better handling of typed Layout (#5935) --- tools/DumpApiXml/DocFileBuilder.cs | 36 +++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/tools/DumpApiXml/DocFileBuilder.cs b/tools/DumpApiXml/DocFileBuilder.cs index 50e5d3cdac..15baa75e53 100644 --- a/tools/DumpApiXml/DocFileBuilder.cs +++ b/tools/DumpApiXml/DocFileBuilder.cs @@ -18,6 +18,7 @@ public class DocFileBuilder { typeof(bool).FullName, "Boolean" }, { typeof(char).FullName, "Char" }, { typeof(byte).FullName, "Byte" }, + { typeof(short).FullName, "Integer" }, { typeof(CultureInfo).FullName, "Culture" }, { typeof(Encoding).FullName, "Encoding" }, { "NLog.Layouts.Layout", "Layout" }, @@ -118,7 +119,6 @@ private string FixWhitespace(string p) private static string GetTypeName(Type type) { string simpleName; - type = GetUnderlyingType(type); if (simpleTypeNames.TryGetValue(type.FullName, out simpleName)) @@ -131,6 +131,11 @@ private static string GetTypeName(Type type) private static Type GetUnderlyingType(Type type) { + if (type.IsGenericType && type.Name.StartsWith("Layout")) + { + type = type.GetGenericArguments()[0]; + } + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) { type = type.GetGenericArguments()[0]; @@ -479,6 +484,35 @@ private static bool TryGetPropertyDefaultValue(object configInstance, PropertyIn return true; } + var underlyingType = GetUnderlyingType(propInfo.PropertyType); + if (propertyValue != null && underlyingType != null && !ReferenceEquals(underlyingType, propInfo.PropertyType) && !underlyingType.Equals(propertyValue?.GetType())) + { + try + { + if (underlyingType.IsEnum) + { + var enumStringValue = propertyValue?.ToString(); + defaultValue = Enum.Parse(underlyingType, enumStringValue, true).ToString(); + return true; + } + else if (typeof(IConvertible).IsAssignableFrom(underlyingType)) + { + var convStringValue = propertyValue?.ToString(); + propertyValue = Convert.ChangeType(convStringValue, underlyingType); + } + else + { + defaultValue = null; + return false; + } + } + catch + { + defaultValue = null; + return false; + } + } + IConvertible convertibleValue = propertyValue as IConvertible; if (convertibleValue == null && propertyValue != null) { From 53b21e4ba148ebe608dde22435b6de3c2becfa79 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 19 Jul 2025 10:03:07 +0200 Subject: [PATCH 210/224] Mark Assembly loading with RequiresUnreferencedCodeAttribute for AOT (#5937) --- src/NLog/Config/AssemblyExtensionLoader.cs | 25 +++++++------ src/NLog/Config/LoggingConfigurationParser.cs | 16 +++++++-- .../DynamicallyAccessedMemberTypes.cs | 35 +++++++++++++++++++ 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/src/NLog/Config/AssemblyExtensionLoader.cs b/src/NLog/Config/AssemblyExtensionLoader.cs index 7e34620324..84bd6ad64c 100644 --- a/src/NLog/Config/AssemblyExtensionLoader.cs +++ b/src/NLog/Config/AssemblyExtensionLoader.cs @@ -43,13 +43,7 @@ namespace NLog.Config [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] internal sealed class AssemblyExtensionLoader : IAssemblyExtensionLoader { - [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2072")] - public void LoadTypeFromName(ConfigurationItemFactory factory, string typeName, string itemNamePrefix) - { - var configType = PropertyTypeConverter.ConvertToType(typeName, true); - factory.RegisterType(configType, itemNamePrefix); - } - + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Assembly.GetTypes() Incompatible with trimming.")] public void LoadAssemblyFromName(ConfigurationItemFactory factory, string assemblyName, string itemNamePrefix) { if (SkipAlreadyLoadedAssembly(factory, assemblyName, itemNamePrefix)) @@ -63,6 +57,7 @@ public void LoadAssemblyFromName(ConfigurationItemFactory factory, string assemb LoadAssembly(factory, extensionAssembly, itemNamePrefix); } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Assembly.GetTypes() Incompatible with trimming.")] public void LoadAssembly(ConfigurationItemFactory factory, Assembly assembly, string itemNamePrefix) { AssemblyHelpers.LogAssemblyVersion(assembly); @@ -103,7 +98,7 @@ public void LoadAssembly(ConfigurationItemFactory factory, Assembly assembly, st /// Assembly to scan. /// Usable types from the given assembly. /// Types which cannot be loaded are skipped. - [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2026")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Assembly.GetTypes() Incompatible with trimming.")] private static Type[] SafeGetTypes(Assembly assembly) { try @@ -258,6 +253,7 @@ private static bool IsNLogItemTypeAlreadyRegistered(IFact return false; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Assembly.GetTypes() Incompatible with trimming.")] public void LoadAssemblyFromPath(ConfigurationItemFactory factory, string assemblyFileName, string? baseDirectory, string itemNamePrefix) { var assembly = LoadAssemblyFromPath(assemblyFileName, baseDirectory); @@ -265,6 +261,7 @@ public void LoadAssemblyFromPath(ConfigurationItemFactory factory, string assemb LoadAssembly(factory, assembly, itemNamePrefix); } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Assembly.GetTypes() Incompatible with trimming.")] public void ScanForAutoLoadExtensions(ConfigurationItemFactory factory) { try @@ -312,6 +309,7 @@ public void ScanForAutoLoadExtensions(ConfigurationItemFactory factory) InternalLogger.Debug("Auto loading done"); } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Assembly.LoadFrom() Incompatible with trimming.")] private HashSet LoadNLogExtensionAssemblies(ConfigurationItemFactory factory, Assembly nlogAssembly, string[] extensionDlls) { HashSet alreadyRegistered = new HashSet(StringComparer.OrdinalIgnoreCase) @@ -348,6 +346,7 @@ private HashSet LoadNLogExtensionAssemblies(ConfigurationItemFactory fac return alreadyRegistered; } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Assembly.GetTypes() Incompatible with trimming.")] private void RegisterAppDomainAssemblies(ConfigurationItemFactory factory, Assembly nlogAssembly, HashSet alreadyRegistered) { alreadyRegistered.Add(nlogAssembly.FullName); @@ -464,8 +463,7 @@ private static string[] GetNLogExtensionFiles(string assemblyLocation) /// file or path, including .dll /// basepath, optional /// - [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2026")] - [Obsolete("Instead use RegisterType, as dynamic Assembly loading will be moved out. Marked obsolete with NLog v5.2")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Assembly.LoadFrom() Incompatible with trimming.")] private static Assembly LoadAssemblyFromPath(string assemblyFileName, string? baseDirectory = null) { string fullFileName = assemblyFileName; @@ -523,14 +521,15 @@ private static bool IsAssemblyMatch(AssemblyName expected, AssemblyName actual) internal interface IAssemblyExtensionLoader { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Assembly.GetTypes() Incompatible with trimming.")] void ScanForAutoLoadExtensions(ConfigurationItemFactory factory); - + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Assembly.GetTypes() Incompatible with trimming.")] void LoadAssemblyFromPath(ConfigurationItemFactory factory, string assemblyFileName, string? baseDirectory, string itemNamePrefix); + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Assembly.GetTypes() Incompatible with trimming.")] void LoadAssemblyFromName(ConfigurationItemFactory factory, string assemblyName, string itemNamePrefix); - void LoadTypeFromName(ConfigurationItemFactory factory, string typeName, string itemNamePrefix); - + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Assembly.GetTypes() Incompatible with trimming.")] void LoadAssembly(ConfigurationItemFactory factory, Assembly assembly, string itemNamePrefix); } } diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index 7ea37392d8..24d549decd 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -198,12 +198,18 @@ private void SetNLogElementSettings(ILoggingConfigurationElement nlogConfig) if (autoLoadExtensions) { - ConfigurationItemFactory.Default.AssemblyLoader.ScanForAutoLoadExtensions(ConfigurationItemFactory.Default); + ScanForAutoLoadExtensions(); } LogFactory.ServiceRepository.ParseMessageTemplates(LogFactory, parseMessageTemplates); } + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2026")] + private static void ScanForAutoLoadExtensions() + { + ConfigurationItemFactory.Default.AssemblyLoader.ScanForAutoLoadExtensions(ConfigurationItemFactory.Default); + } + /// /// Builds list with unique keys, using last value of duplicates. High priority keys placed first. /// @@ -354,11 +360,15 @@ private void ParseExtensionsElement(ValidatedConfigurationElement extensionsElem } } + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2072")] private void RegisterExtension(string typeName, string itemNamePrefix) { try { - ConfigurationItemFactory.Default.AssemblyLoader.LoadTypeFromName(ConfigurationItemFactory.Default, typeName, itemNamePrefix); + var configType = PropertyTypeConverter.ConvertToType(typeName, true); +#pragma warning disable CS0618 // Type or member is obsolete + ConfigurationItemFactory.Default.RegisterType(configType, itemNamePrefix); +#pragma warning restore CS0618 // Type or member is obsolete } catch (Exception exception) { @@ -372,6 +382,7 @@ private void RegisterExtension(string typeName, string itemNamePrefix) } } + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2026")] private void ParseExtensionWithAssemblyFile(string assemblyFile, string? baseDirectory, string prefix) { try @@ -396,6 +407,7 @@ private bool RegisterExtensionFromAssemblyName(string assemblyName, string origi return ParseExtensionWithAssemblyName(assemblyName, string.Empty); } + [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming - Allow extension loading from config", "IL2026")] private bool ParseExtensionWithAssemblyName(string assemblyName, string itemNamePrefix) { try diff --git a/src/NLog/Internal/DynamicallyAccessedMemberTypes.cs b/src/NLog/Internal/DynamicallyAccessedMemberTypes.cs index d9f6a1990e..96274ea008 100644 --- a/src/NLog/Internal/DynamicallyAccessedMemberTypes.cs +++ b/src/NLog/Internal/DynamicallyAccessedMemberTypes.cs @@ -228,6 +228,41 @@ public UnconditionalSuppressMessageAttribute(string category, string checkId) /// public string? Justification { get; set; } } + + /// + /// Indicates that the specified method requires dynamic access to code that is not referenced + /// statically, for example through . + /// + /// + /// This allows tools to understand which methods are unsafe to call when removing unreferenced + /// code from an application. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] + internal sealed class RequiresUnreferencedCodeAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified message. + /// + /// + /// A message that contains information about the usage of unreferenced code. + /// + public RequiresUnreferencedCodeAttribute(string message) + { + Message = message; + } + + /// + /// Gets a message that contains information about the usage of unreferenced code. + /// + public string Message { get; } + + /// + /// Gets or sets an optional URL that contains more information about the method, + /// why it requires unreferenced code, and what options a consumer has to deal with it. + /// + public string? Url { get; set; } + } } #endif From b129c89e7f4a3f53c8937f0d587e8a8d933ba29b Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 19 Jul 2025 12:40:04 +0200 Subject: [PATCH 211/224] Logger - Align WriteToTargets with WriteToTargetsWithSpan (#5938) --- src/NLog/Logger-V1Compat.cs | 240 +++++++++++++++++------------------ src/NLog/Logger-generated.cs | 72 +++++------ src/NLog/Logger-generated.tt | 12 +- src/NLog/Logger.cs | 104 ++++++--------- 4 files changed, 196 insertions(+), 232 deletions(-) diff --git a/src/NLog/Logger-V1Compat.cs b/src/NLog/Logger-V1Compat.cs index c38b7c0f8c..748dd32a98 100644 --- a/src/NLog/Logger-V1Compat.cs +++ b/src/NLog/Logger-V1Compat.cs @@ -95,18 +95,15 @@ public void Log(LogLevel level, IFormatProvider? formatProvider, object? value) #endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2) { -#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); if (targetsForLevel != null) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, arg1, arg2); - } #else - if (IsEnabled(level)) - { - WriteToTargets(level, message, new[] { arg1, arg2 }); - } + WriteToTargets(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2 }); #endif + } } /// @@ -124,18 +121,15 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object? arg1, object? arg2, object? arg3) { -#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); if (targetsForLevel != null) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); - } #else - if (IsEnabled(level)) - { - WriteToTargets(level, message, new[] { arg1, arg2, arg3 }); - } + WriteToTargets(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2, arg3 }); #endif + } } /// @@ -173,7 +167,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] { if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -212,7 +206,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] { if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -251,7 +245,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] { if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -290,7 +284,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] { if (IsEnabled(level)) { - WriteToTargets(level, message, new object?[] { argument }); + WriteToTargets(level, Factory.DefaultCultureInfo, message, new object?[] { argument }); } } @@ -329,7 +323,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] { if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -368,7 +362,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] { if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -407,7 +401,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] { if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -446,7 +440,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] { if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -485,7 +479,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] { if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -503,18 +497,15 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] #endif public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, object? argument) { -#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); if (targetsForLevel != null) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(targetsForLevel, level, null, formatProvider, message, argument); - } #else - if (IsEnabled(level)) - { - WriteToTargets(level, formatProvider, message, new object?[] { argument }); - } + WriteToTargets(targetsForLevel, level, null, formatProvider, message, new object?[] { argument }); #endif + } } /// @@ -530,18 +521,15 @@ public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(fa #endif public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, object? argument) { -#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); if (targetsForLevel != null) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, argument); - } #else - if (IsEnabled(level)) - { - WriteToTargets(level, message, new object?[] { argument }); - } + WriteToTargets(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif + } } /// @@ -581,7 +569,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] { if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -622,7 +610,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] { if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -663,7 +651,7 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] { if (IsEnabled(level)) { - WriteToTargets(level, message, new object[] { argument }); + WriteToTargets(level, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -722,7 +710,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, arg1, arg2); #else - WriteToTargets(LogLevel.Trace, message, new object?[] { arg1, arg2 }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2 }); #endif } } @@ -746,7 +734,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); #else - WriteToTargets(LogLevel.Trace, message, new object?[] { arg1, arg2, arg3 }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2, arg3 }); #endif } } @@ -784,7 +772,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -821,7 +809,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -858,7 +846,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -895,7 +883,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, new object?[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object?[] { argument }); } } @@ -932,7 +920,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -969,7 +957,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1006,7 +994,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1043,7 +1031,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1080,7 +1068,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1124,7 +1112,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Trace, message, new object?[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif } } @@ -1164,7 +1152,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1203,7 +1191,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1242,7 +1230,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, new object[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1301,7 +1289,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, arg1, arg2); #else - WriteToTargets(LogLevel.Debug, message, new object?[] { arg1, arg2 }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2 }); #endif } } @@ -1325,7 +1313,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); #else - WriteToTargets(LogLevel.Debug, message, new object?[] { arg1, arg2, arg3 }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2, arg3 }); #endif } } @@ -1363,7 +1351,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1400,7 +1388,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1437,7 +1425,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1474,7 +1462,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object?[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object?[] { argument }); } } @@ -1511,7 +1499,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1548,7 +1536,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1585,7 +1573,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1622,7 +1610,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1659,7 +1647,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1703,7 +1691,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Debug, message, new object?[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif } } @@ -1743,7 +1731,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1782,7 +1770,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1821,7 +1809,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, new object[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1880,7 +1868,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, arg1, arg2); #else - WriteToTargets(LogLevel.Info, message, new object?[] { arg1, arg2 }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2 }); #endif } } @@ -1904,7 +1892,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); #else - WriteToTargets(LogLevel.Info, message, new object?[] { arg1, arg2, arg3 }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2, arg3 }); #endif } } @@ -1942,7 +1930,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -1979,7 +1967,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2016,7 +2004,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2053,7 +2041,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object?[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object?[] { argument }); } } @@ -2090,7 +2078,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2127,7 +2115,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2164,7 +2152,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2201,7 +2189,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2238,7 +2226,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2282,7 +2270,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Info, message, new object?[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif } } @@ -2322,7 +2310,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2361,7 +2349,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2400,7 +2388,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, new object[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2459,7 +2447,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, arg1, arg2); #else - WriteToTargets(LogLevel.Warn, message, new object?[] { arg1, arg2 }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2 }); #endif } } @@ -2483,7 +2471,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); #else - WriteToTargets(LogLevel.Warn, message, new object?[] { arg1, arg2, arg3 }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2, arg3 }); #endif } } @@ -2521,7 +2509,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2558,7 +2546,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2595,7 +2583,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2632,7 +2620,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object?[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object?[] { argument }); } } @@ -2669,7 +2657,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2706,7 +2694,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2743,7 +2731,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2780,7 +2768,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2817,7 +2805,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2861,7 +2849,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Warn, message, new object?[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif } } @@ -2901,7 +2889,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2940,7 +2928,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -2979,7 +2967,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, new object[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3038,7 +3026,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, arg1, arg2); #else - WriteToTargets(LogLevel.Error, message, new object?[] { arg1, arg2 }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2 }); #endif } } @@ -3062,7 +3050,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); #else - WriteToTargets(LogLevel.Error, message, new object?[] { arg1, arg2, arg3 }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2, arg3 }); #endif } } @@ -3100,7 +3088,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3137,7 +3125,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3174,7 +3162,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3211,7 +3199,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object?[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object?[] { argument }); } } @@ -3248,7 +3236,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3285,7 +3273,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3322,7 +3310,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3359,7 +3347,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3396,7 +3384,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3440,7 +3428,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Error, message, new object?[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif } } @@ -3480,7 +3468,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3519,7 +3507,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3558,7 +3546,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, new object[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3617,7 +3605,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, arg1, arg2); #else - WriteToTargets(LogLevel.Fatal, message, new object?[] { arg1, arg2 }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2 }); #endif } } @@ -3641,7 +3629,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, arg1, arg2, arg3); #else - WriteToTargets(LogLevel.Fatal, message, new object?[] { arg1, arg2, arg3 }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object?[] { arg1, arg2, arg3 }); #endif } } @@ -3679,7 +3667,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3716,7 +3704,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3753,7 +3741,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3790,7 +3778,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object?[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object?[] { argument }); } } @@ -3827,7 +3815,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3864,7 +3852,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3901,7 +3889,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3938,7 +3926,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -3975,7 +3963,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -4019,7 +4007,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Fatal, message, new object?[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif } } @@ -4059,7 +4047,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -4098,7 +4086,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object[] { argument }); } } @@ -4137,7 +4125,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, new object[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object[] { argument }); } } diff --git a/src/NLog/Logger-generated.cs b/src/NLog/Logger-generated.cs index 606177a26c..cb88e62038 100644 --- a/src/NLog/Logger-generated.cs +++ b/src/NLog/Logger-generated.cs @@ -180,7 +180,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] string message { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, message, args); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, args); } } @@ -224,7 +224,7 @@ public void Trace(Exception? exception, [Localizable(false)] string message) { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, exception, message, null); + WriteToTargets(LogLevel.Trace, exception, Factory.DefaultCultureInfo, message, null); } } @@ -239,7 +239,7 @@ public void Trace(Exception? exception, [Localizable(false)][StructuredMessageTe { if (IsTraceEnabled) { - WriteToTargets(LogLevel.Trace, exception, message, args); + WriteToTargets(LogLevel.Trace, exception, Factory.DefaultCultureInfo, message, args); } } @@ -293,7 +293,7 @@ public void Trace([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Trace, message, new object?[] { argument }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif } } @@ -336,7 +336,7 @@ public void Trace([Localizable(false)][StructuredMessage #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.Trace, message, new object?[] { argument1, argument2 }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2 }); #endif } } @@ -383,7 +383,7 @@ public void Trace([Localizable(false)][Struc #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Trace, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Trace, message, new object?[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Trace, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -472,7 +472,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] string message { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, message, args); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, args); } } @@ -516,7 +516,7 @@ public void Debug(Exception? exception, [Localizable(false)] string message) { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, exception, message, null); + WriteToTargets(LogLevel.Debug, exception, Factory.DefaultCultureInfo, message, null); } } @@ -531,7 +531,7 @@ public void Debug(Exception? exception, [Localizable(false)][StructuredMessageTe { if (IsDebugEnabled) { - WriteToTargets(LogLevel.Debug, exception, message, args); + WriteToTargets(LogLevel.Debug, exception, Factory.DefaultCultureInfo, message, args); } } @@ -585,7 +585,7 @@ public void Debug([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Debug, message, new object?[] { argument }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif } } @@ -628,7 +628,7 @@ public void Debug([Localizable(false)][StructuredMessage #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.Debug, message, new object?[] { argument1, argument2 }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2 }); #endif } } @@ -675,7 +675,7 @@ public void Debug([Localizable(false)][Struc #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Debug, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Debug, message, new object?[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Debug, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -764,7 +764,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] string message, { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, message, args); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, args); } } @@ -808,7 +808,7 @@ public void Info(Exception? exception, [Localizable(false)] string message) { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, exception, message, null); + WriteToTargets(LogLevel.Info, exception, Factory.DefaultCultureInfo, message, null); } } @@ -823,7 +823,7 @@ public void Info(Exception? exception, [Localizable(false)][StructuredMessageTem { if (IsInfoEnabled) { - WriteToTargets(LogLevel.Info, exception, message, args); + WriteToTargets(LogLevel.Info, exception, Factory.DefaultCultureInfo, message, args); } } @@ -877,7 +877,7 @@ public void Info([Localizable(false)][StructuredMessageTemplate] stri #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Info, message, new object?[] { argument }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif } } @@ -920,7 +920,7 @@ public void Info([Localizable(false)][StructuredMessageT #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.Info, message, new object?[] { argument1, argument2 }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2 }); #endif } } @@ -967,7 +967,7 @@ public void Info([Localizable(false)][Struct #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Info, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Info, message, new object?[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Info, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -1056,7 +1056,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] string message, { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, message, args); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, args); } } @@ -1100,7 +1100,7 @@ public void Warn(Exception? exception, [Localizable(false)] string message) { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, exception, message, null); + WriteToTargets(LogLevel.Warn, exception, Factory.DefaultCultureInfo, message, null); } } @@ -1115,7 +1115,7 @@ public void Warn(Exception? exception, [Localizable(false)][StructuredMessageTem { if (IsWarnEnabled) { - WriteToTargets(LogLevel.Warn, exception, message, args); + WriteToTargets(LogLevel.Warn, exception, Factory.DefaultCultureInfo, message, args); } } @@ -1169,7 +1169,7 @@ public void Warn([Localizable(false)][StructuredMessageTemplate] stri #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Warn, message, new object?[] { argument }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif } } @@ -1212,7 +1212,7 @@ public void Warn([Localizable(false)][StructuredMessageT #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.Warn, message, new object?[] { argument1, argument2 }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2 }); #endif } } @@ -1259,7 +1259,7 @@ public void Warn([Localizable(false)][Struct #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Warn, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Warn, message, new object?[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Warn, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -1348,7 +1348,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] string message { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, message, args); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, args); } } @@ -1392,7 +1392,7 @@ public void Error(Exception? exception, [Localizable(false)] string message) { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, exception, message, null); + WriteToTargets(LogLevel.Error, exception, Factory.DefaultCultureInfo, message, null); } } @@ -1407,7 +1407,7 @@ public void Error(Exception? exception, [Localizable(false)][StructuredMessageTe { if (IsErrorEnabled) { - WriteToTargets(LogLevel.Error, exception, message, args); + WriteToTargets(LogLevel.Error, exception, Factory.DefaultCultureInfo, message, args); } } @@ -1461,7 +1461,7 @@ public void Error([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Error, message, new object?[] { argument }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif } } @@ -1504,7 +1504,7 @@ public void Error([Localizable(false)][StructuredMessage #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.Error, message, new object?[] { argument1, argument2 }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2 }); #endif } } @@ -1551,7 +1551,7 @@ public void Error([Localizable(false)][Struc #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Error, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Error, message, new object?[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Error, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2, argument3 }); #endif } } @@ -1640,7 +1640,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] string message { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, message, args); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, args); } } @@ -1684,7 +1684,7 @@ public void Fatal(Exception? exception, [Localizable(false)] string message) { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, exception, message, null); + WriteToTargets(LogLevel.Fatal, exception, Factory.DefaultCultureInfo, message, null); } } @@ -1699,7 +1699,7 @@ public void Fatal(Exception? exception, [Localizable(false)][StructuredMessageTe { if (IsFatalEnabled) { - WriteToTargets(LogLevel.Fatal, exception, message, args); + WriteToTargets(LogLevel.Fatal, exception, Factory.DefaultCultureInfo, message, args); } } @@ -1753,7 +1753,7 @@ public void Fatal([Localizable(false)][StructuredMessageTemplate] str #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.Fatal, message, new object?[] { argument }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif } } @@ -1796,7 +1796,7 @@ public void Fatal([Localizable(false)][StructuredMessage #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.Fatal, message, new object?[] { argument1, argument2 }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2 }); #endif } } @@ -1843,7 +1843,7 @@ public void Fatal([Localizable(false)][Struc #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.Fatal, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.Fatal, message, new object?[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.Fatal, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2, argument3 }); #endif } } diff --git a/src/NLog/Logger-generated.tt b/src/NLog/Logger-generated.tt index 18ac5a1eb2..3734e77d57 100644 --- a/src/NLog/Logger-generated.tt +++ b/src/NLog/Logger-generated.tt @@ -156,7 +156,7 @@ namespace NLog { if (Is<#=level#>Enabled) { - WriteToTargets(LogLevel.<#=level#>, message, args); + WriteToTargets(LogLevel.<#=level#>, Factory.DefaultCultureInfo, message, args); } } @@ -200,7 +200,7 @@ namespace NLog { if (Is<#=level#>Enabled) { - WriteToTargets(LogLevel.<#=level#>, exception, message, null); + WriteToTargets(LogLevel.<#=level#>, exception, Factory.DefaultCultureInfo, message, null); } } @@ -215,7 +215,7 @@ namespace NLog { if (Is<#=level#>Enabled) { - WriteToTargets(LogLevel.<#=level#>, exception, message, args); + WriteToTargets(LogLevel.<#=level#>, exception, Factory.DefaultCultureInfo, message, args); } } @@ -269,7 +269,7 @@ namespace NLog #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, argument); #else - WriteToTargets(LogLevel.<#=level#>, message, new object?[] { argument }); + WriteToTargets(LogLevel.<#=level#>, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif } } @@ -312,7 +312,7 @@ namespace NLog #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, argument1, argument2); #else - WriteToTargets(LogLevel.<#=level#>, message, new object?[] { argument1, argument2 }); + WriteToTargets(LogLevel.<#=level#>, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2 }); #endif } } @@ -359,7 +359,7 @@ namespace NLog #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(LogLevel.<#=level#>, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); #else - WriteToTargets(LogLevel.<#=level#>, message, new object?[] { argument1, argument2, argument3 }); + WriteToTargets(LogLevel.<#=level#>, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2, argument3 }); #endif } } diff --git a/src/NLog/Logger.cs b/src/NLog/Logger.cs index 0b423d5a55..1dd913062a 100644 --- a/src/NLog/Logger.cs +++ b/src/NLog/Logger.cs @@ -337,11 +337,11 @@ public void Log(LogLevel level, IFormatProvider? formatProvider, T? value) /// A function returning message to be written. Function is not evaluated if logging is not enabled. public void Log(LogLevel level, LogMessageGenerator messageFunc) { - if (IsEnabled(level)) + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) { Guard.ThrowIfNull(messageFunc); - - WriteToTargets(level, messageFunc()); + WriteToTargets(targetsForLevel, level, null, Factory.DefaultCultureInfo, messageFunc(), null); } } @@ -355,9 +355,10 @@ public void Log(LogLevel level, LogMessageGenerator messageFunc) [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { - if (IsEnabled(level)) + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) { - WriteToTargets(level, formatProvider, message, args); + WriteToTargets(targetsForLevel, level, null, formatProvider, message, args); } } @@ -368,9 +369,10 @@ public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(fa /// Log message. public void Log(LogLevel level, [Localizable(false)] string message) { - if (IsEnabled(level)) + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) { - WriteToTargets(level, message); + WriteToTargets(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, null); } } @@ -383,9 +385,10 @@ public void Log(LogLevel level, [Localizable(false)] string message) [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { - if (IsEnabled(level)) + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) { - WriteToTargets(level, message, args); + WriteToTargets(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, args); } } @@ -399,9 +402,10 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, Exception? exception, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { - if (IsEnabled(level)) + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) { - WriteToTargets(level, exception, message, args); + WriteToTargets(targetsForLevel, level, exception, Factory.DefaultCultureInfo, message, args); } } @@ -416,9 +420,10 @@ public void Log(LogLevel level, Exception? exception, [Localizable(false)][Struc [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, Exception? exception, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, params object?[] args) { - if (IsEnabled(level)) + var targetsForLevel = GetTargetsForLevelSafe(level); + if (targetsForLevel != null) { - WriteToTargets(level, exception, formatProvider, message, args); + WriteToTargets(targetsForLevel, level, exception, formatProvider, message, args); } } @@ -433,18 +438,15 @@ public void Log(LogLevel level, Exception? exception, IFormatProvider? formatPro [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { -#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); if (targetsForLevel != null) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(targetsForLevel, level, null, formatProvider, message, argument); - } #else - if (IsEnabled(level)) - { - WriteToTargets(level, formatProvider, message, new object?[] { argument }); + WriteToTargets(targetsForLevel, level, null, formatProvider, message, new object?[] { argument }); +#endif } -#endif } /// @@ -457,18 +459,15 @@ public void Log(LogLevel level, IFormatProvider? formatProvider, [Loc [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument? argument) { -#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); if (targetsForLevel != null) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, argument); - } #else - if (IsEnabled(level)) - { - WriteToTargets(level, message, new object?[] { argument }); - } + WriteToTargets(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, new object?[] { argument }); #endif + } } /// @@ -484,18 +483,15 @@ public void Log(LogLevel level, [Localizable(false)][StructuredMessag [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { -#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); if (targetsForLevel != null) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(targetsForLevel, level, null, formatProvider, message, argument1, argument2); - } #else - if (IsEnabled(level)) - { - WriteToTargets(level, formatProvider, message, new object?[] { argument1, argument2 }); - } + WriteToTargets(targetsForLevel, level, null, formatProvider, message, new object?[] { argument1, argument2 }); #endif + } } /// @@ -510,18 +506,15 @@ public void Log(LogLevel level, IFormatProvider? formatP [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2) { -#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); if (targetsForLevel != null) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, argument1, argument2); - } #else - if (IsEnabled(level)) - { - WriteToTargets(level, message, new object?[] { argument1, argument2 }); - } + WriteToTargets(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2 }); #endif + } } /// @@ -539,18 +532,15 @@ public void Log(LogLevel level, [Localizable(false)][Str [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, IFormatProvider? formatProvider, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { -#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); if (targetsForLevel != null) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(targetsForLevel, level, null, formatProvider, message, argument1, argument2, argument3); - } #else - if (IsEnabled(level)) - { - WriteToTargets(level, formatProvider, message, new object?[] { argument1, argument2, argument3 }); - } + WriteToTargets(targetsForLevel, level, null, formatProvider, message, new object?[] { argument1, argument2, argument3 }); #endif + } } /// @@ -567,18 +557,15 @@ public void Log(LogLevel level, IFormatProvi [MessageTemplateFormatMethod("message")] public void Log(LogLevel level, [Localizable(false)][StructuredMessageTemplate] string message, TArgument1? argument1, TArgument2? argument2, TArgument3? argument3) { -#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER var targetsForLevel = GetTargetsForLevelSafe(level); if (targetsForLevel != null) { +#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER WriteToTargetsWithSpan(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, argument1, argument2, argument3); - } #else - if (IsEnabled(level)) - { - WriteToTargets(level, message, new object?[] { argument1, argument2, argument3 }); - } + WriteToTargets(targetsForLevel, level, null, Factory.DefaultCultureInfo, message, new object?[] { argument1, argument2, argument3 }); #endif + } } #if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER @@ -835,9 +822,10 @@ internal void Initialize(string name, ITargetWithFilterChain[] targetsByLevel, L SetConfiguration(targetsByLevel); } - private void WriteToTargets(LogLevel level, string message, object?[] args) + private void WriteToTargets(ITargetWithFilterChain targetsForLevel, LogLevel level, Exception? exception, IFormatProvider? formatProvider, string message, object?[]? args) { - WriteToTargets(level, Factory.DefaultCultureInfo, message, args); + var logEvent = LogEventInfo.Create(level, Name, exception, formatProvider, message, args); + WriteLogEventToTargets(logEvent, targetsForLevel); } private void WriteToTargets(LogLevel level, IFormatProvider? formatProvider, string message, object?[] args) @@ -872,20 +860,8 @@ private void WriteToTargets(LogLevel level, IFormatProvider? formatProvider, } } - private void WriteToTargets(LogLevel level, Exception? ex, string message, object?[]? args) - { - var targetsForLevel = GetTargetsForLevel(level); - if (targetsForLevel != null) - { - // Translate Exception with missing LogEvent message as log single value - var logEvent = message is null && ex != null && !(args?.Length > 0) ? - LogEventInfo.Create(level, Name, ExceptionMessageFormatProvider.Instance, ex) : - LogEventInfo.Create(level, Name, ex, Factory.DefaultCultureInfo, message ?? string.Empty, args); - WriteLogEventToTargets(logEvent, targetsForLevel); - } - } - private void WriteToTargets(LogLevel level, Exception? ex, IFormatProvider? formatProvider, string message, object?[] args) + private void WriteToTargets(LogLevel level, Exception? ex, IFormatProvider? formatProvider, string message, object?[]? args) { var targetsForLevel = GetTargetsForLevel(level); if (targetsForLevel != null) From 57d368ff20ba485e3e851fbed2e498c12398302a Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 20 Jul 2025 16:37:17 +0200 Subject: [PATCH 212/224] NLog v6.0.2 (#5939) --- CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++++++ build.ps1 | 2 +- src/NLog/NLog.csproj | 25 ++++++++++++++++++++----- 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83a282aad2..f81ff568df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,43 @@ Date format: (year/month/day) ## Change Log +### Version 6.0.2 (2025/07/20) + +**Improvements** + +- [#5930](https://github.com/NLog/NLog/pull/5930) XmlParser - Handle XML comments after root-end-tag. (@snakefoot) +- [#5929](https://github.com/NLog/NLog/pull/5929) XmlLoggingConfiguration - Improve handling of invalid XML. (@snakefoot) +- [#5933](https://github.com/NLog/NLog/pull/5933) Handle invalid message template when skipping parameters array. (@snakefoot) +- [#5915](https://github.com/NLog/NLog/pull/5915) ReplaceNewLinesLayoutRendererWrapper - Replace more line ending characters. (@oikku) +- [#5911](https://github.com/NLog/NLog/pull/5911) NLog.Targets.GZipFile - Improve support for ArchiveAboveSize. (@snakefoot) +- [#5921](https://github.com/NLog/NLog/pull/5921) FileTarget - Activate legacy ArchiveFileName when ArchiveSuffixFormat contains legacy placeholder. (@snakefoot) +- [#5924](https://github.com/NLog/NLog/pull/5924) AsyncTargetWrapper - Updated FullBatchSizeWriteLimit default value from 5 to 10. (@snakefoot) +- [#5937](https://github.com/NLog/NLog/pull/5937) Mark Assembly loading with RequiresUnreferencedCodeAttribute for AOT. (@snakefoot) +- [#5938](https://github.com/NLog/NLog/pull/5938) Logger - Align WriteToTargets with WriteToTargetsWithSpan. (@snakefoot) +- [#5909](https://github.com/NLog/NLog/pull/5909) ConfigurationItemFactory - Added extension hints for webservice and activityid. (@snakefoot) +- [#5918](https://github.com/NLog/NLog/pull/5918) Log4JXmlTarget - Removed alias NLogViewer as conflicts with other nuget-packages. (@snakefoot) +- [#5926](https://github.com/NLog/NLog/pull/5926) SplunkTarget - NetworkTarget with SplunkLayout. (@snakefoot) +- [#5927](https://github.com/NLog/NLog/pull/5927) GelfLayout - Align with SplunkLayout. (@snakefoot) +- [#5913](https://github.com/NLog/NLog/pull/5913) NLog.Targets.Network - Updated nuget-package README.md. (@snakefoot) +- [#5912](https://github.com/NLog/NLog/pull/5912) NLog.Targets.Trace - Updated nuget-package README.md. (@snakefoot) +- [#5919](https://github.com/NLog/NLog/pull/5919) XML docs for Targets and Layouts with remarks about default value. (@snakefoot) +- [#5922](https://github.com/NLog/NLog/pull/5922) XML docs for LayoutRenderers with remarks about default value. (@snakefoot) +- [#5925](https://github.com/NLog/NLog/pull/5925) XML docs for Target Wrappers with remarks about default value. (@snakefoot) +- [#5935](https://github.com/NLog/NLog/pull/5935) Improve NLog XSD Schema with better handling of typed Layout. (@snakefoot) +- [#5923](https://github.com/NLog/NLog/pull/5923) Updated unit-tests from NET6 to NET8. (@snakefoot) + +### Version 5.5.1 (2025/07/18) + +**Improvements** + +- [#5858](https://github.com/NLog/NLog/pull/5858) ConsoleTarget - Added ForceWriteLine to match NLog v6 Schema (#5858) (@snakefoot) +- [#5866](https://github.com/NLog/NLog/pull/5866) Layout.FromLiteral to match NLog v6 (#5866) (@snakefoot) +- [#5888](https://github.com/NLog/NLog/pull/5888) ChainsawTarget with type-alias Log4JXml to match NLog v6 (#5888) (@snakefoot) +- [#5883](https://github.com/NLog/NLog/pull/5883) AsyncTargetWrapper - LogEventDropped and EventQueueGrow events fixes (#5883) (@dance) +- [#5890](https://github.com/NLog/NLog/pull/5890) StringBuilderExt - Change Append2DigitsZeroPadded to array-lookup (#5890) (@snakefoot) +- [#5936](https://github.com/NLog/NLog/pull/5936) XmlLayout - Support MaxRecursionLimit == 0 (#5936) (@snakefoot) +- [#5936](https://github.com/NLog/NLog/pull/5936) RegisterObjectTransformation so build trimming will keep public properties (#5936) (@snakefoot) + ### Version 6.0.1 (2025/06/27) **Improvements** diff --git a/build.ps1 b/build.ps1 index 8e6c38ebe1..972fd7cd23 100644 --- a/build.ps1 +++ b/build.ps1 @@ -2,7 +2,7 @@ # creates NuGet package at \artifacts dotnet --version -$versionPrefix = "6.0.1" +$versionPrefix = "6.0.2" $versionSuffix = "" $versionFile = $versionPrefix + "." + ${env:APPVEYOR_BUILD_NUMBER} $versionProduct = $versionPrefix; diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index 002a70d18e..8fae7e29ec 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -30,11 +30,26 @@ For ASP.NET Core, check: https://www.nuget.org/packages/NLog.Web.AspNetCore Changelog: -- Changed ConditionExpression to be nullable by default since no Condition means no filtering. (#5898) (@snakefoot) -- Include ConditionExpression in the static type registration. (#5906) (@snakefoot) -- Fixed the new XML parser to handle XML comments just before end-tag. (#5895) (@snakefoot) -- Fixed the new XML parser to allow InnerText with greater-than characters. (#5905) (@snakefoot) -- Updated NLog.Targets.AtomicFile to support net8.0-windows without dependency on Mono.Posix.NETStandard. (#5891) (@snakefoot) +- (#5930) XmlParser - Handle XML comments after root-end-tag. (@snakefoot) +- (#5929) XmlLoggingConfiguration - Improve handling of invalid XML. (@snakefoot) +- (#5933) Handle invalid message template when skipping parameters array. (@snakefoot) +- (#5915) ReplaceNewLinesLayoutRendererWrapper - Replace more line ending characters. (@oikku) +- (#5911) NLog.Targets.GZipFile - Improve support for ArchiveAboveSize. (@snakefoot) +- (#5921) FileTarget - Activate legacy ArchiveFileName when ArchiveSuffixFormat contains legacy placeholder. (@snakefoot) +- (#5924) AsyncTargetWrapper - Updated FullBatchSizeWriteLimit default value from 5 to 10. (@snakefoot) +- (#5937) Mark Assembly loading with RequiresUnreferencedCodeAttribute for AOT. (@snakefoot) +- (#5938) Logger - Align WriteToTargets with WriteToTargetsWithSpan. (@snakefoot) +- (#5909) ConfigurationItemFactory - Added extension hints for webservice and activityid. (@snakefoot) +- (#5918) Log4JXmlTarget - Removed alias NLogViewer as conflicts with other nuget-packages. (@snakefoot) +- (#5926) SplunkTarget - NetworkTarget with SplunkLayout. (@snakefoot) +- (#5927) GelfLayout - Align with SplunkLayout. (@snakefoot) +- (#5913) NLog.Targets.Network - Updated nuget-package README.md. (@snakefoot) +- (#5912) NLog.Targets.Trace - Updated nuget-package README.md. (@snakefoot) +- (#5919) XML docs for Targets and Layouts with remarks about default value. (@snakefoot) +- (#5922) XML docs for LayoutRenderers with remarks about default value. (@snakefoot) +- (#5925) XML docs for Target Wrappers with remarks about default value. (@snakefoot) +- (#5935) Improve NLog XSD Schema with better handling of typed Layout. (@snakefoot) +- (#5923) Updated unit-tests from NET6 to NET8. (@snakefoot) NLog v6.0 release notes: https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html From c553412b41efd3a4f1648b9c06f07e8ac261651d Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 20 Jul 2025 22:38:30 +0200 Subject: [PATCH 213/224] Fixed SplunkTarget to include SplunkFields property (#5940) --- .../Layouts/SplunkLayout.cs | 4 ++-- .../Layouts/SyslogLayout.cs | 2 +- src/NLog.Targets.Network/README.md | 19 +++++++++++++------ .../Targets/SplunkTarget.cs | 5 +++++ .../SplunkLayoutTests.cs | 6 +++--- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/NLog.Targets.Network/Layouts/SplunkLayout.cs b/src/NLog.Targets.Network/Layouts/SplunkLayout.cs index 50ae3e23e1..5d0e0cfffc 100644 --- a/src/NLog.Targets.Network/Layouts/SplunkLayout.cs +++ b/src/NLog.Targets.Network/Layouts/SplunkLayout.cs @@ -67,8 +67,8 @@ public class SplunkLayout : JsonLayout /// /// Gets the array of attributes for the "event"-section /// - [ArrayParameter(typeof(JsonAttribute), "splunkevent")] - public IList? SplunkEvents + [ArrayParameter(typeof(JsonAttribute), "SplunkField")] + public IList? SplunkFields { get { diff --git a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs index f06ece418a..73531fb584 100644 --- a/src/NLog.Targets.Network/Layouts/SyslogLayout.cs +++ b/src/NLog.Targets.Network/Layouts/SyslogLayout.cs @@ -195,7 +195,7 @@ public SyslogLayout() /// protected override void InitializeLayout() { - // CompoundLayout includes optimization, so only doing precalculate/caching of relevant Layouts (instead of the entire SysLog-message) + // CompoundLayout includes optimization, so only doing precalculate/caching of relevant Layouts (instead of the entire Syslog-message) Layouts.Clear(); if (!IncludeEventProperties) { diff --git a/src/NLog.Targets.Network/README.md b/src/NLog.Targets.Network/README.md index c12519d033..ac984fb2d9 100644 --- a/src/NLog.Targets.Network/README.md +++ b/src/NLog.Targets.Network/README.md @@ -1,20 +1,26 @@ # NLog Network Target -NLog Network Target for sending mesages using TCP / UDP sockets with support for SSL / TSL. +NLog Network Target for sending messages using TCP / UDP sockets with support for SSL / TSL. If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) See the [NLog Wiki - Network Target](https://github.com/NLog/NLog/wiki/Network-target) for available options and examples. -## NLog SysLog Target +## NLog Syslog Target -NLog Syslog Target combines the NLog NetworkTarget with NLog SyslogLayout +NLog Syslog Target combines the NLog NetworkTarget with NLog SyslogLayout. See the [NLog Wiki - Syslog Target](https://github.com/NLog/NLog/wiki/Syslog-target) for available options and examples. +## NLog Splunk Target + +NLog Splunk Target combines the NLog NetworkTarget with NLog SplunkLayout. + +See the [NLog Wiki - Splunk Target](https://github.com/NLog/NLog/wiki/Splunk-target) for available options and examples. + ## NLog GELF Target -NLog Gelf Target combines the NLog NetworkTarget with NLog GelfLayout for Graylog Extended Logging Format (GELF) +NLog Gelf Target combines the NLog NetworkTarget with NLog GelfLayout for Graylog Extended Logging Format (GELF). See the [NLog Wiki - Gelf Target](https://github.com/NLog/NLog/wiki/Gelf-target) for available options and examples. @@ -40,11 +46,12 @@ Alternative register from code using [fluent configuration API](https://github.c LogManager.Setup().SetupExtensions(ext => { ext.RegisterTarget(); ext.RegisterTarget(); - ext.RegisterTarget(); - ext.RegisterTarget(); ext.RegisterTarget(); + ext.RegisterTarget(); + ext.RegisterTarget(); ext.RegisterLayout(); ext.RegisterLayout(); + ext.RegisterLayout(); ext.RegisterLayout(); }); ``` diff --git a/src/NLog.Targets.Network/Targets/SplunkTarget.cs b/src/NLog.Targets.Network/Targets/SplunkTarget.cs index 717ce7e53b..16da62d5e9 100644 --- a/src/NLog.Targets.Network/Targets/SplunkTarget.cs +++ b/src/NLog.Targets.Network/Targets/SplunkTarget.cs @@ -34,6 +34,7 @@ namespace NLog.Targets { using System.Collections.Generic; + using NLog.Config; using NLog.Layouts; /// @@ -60,6 +61,10 @@ public class SplunkTarget : NetworkTarget /// public Layout SplunkIndex { get => _splunkLayout.SplunkIndex; set => _splunkLayout.SplunkIndex = value; } + /// + [ArrayParameter(typeof(JsonAttribute), "SplunkField")] + public IList? SplunkFields { get => _splunkLayout.SplunkFields; } + /// public bool IncludeEventProperties { get => _splunkLayout.IncludeEventProperties; set => _splunkLayout.IncludeEventProperties = value; } diff --git a/tests/NLog.Targets.Network.Tests/SplunkLayoutTests.cs b/tests/NLog.Targets.Network.Tests/SplunkLayoutTests.cs index be9954af05..ca184e5122 100644 --- a/tests/NLog.Targets.Network.Tests/SplunkLayoutTests.cs +++ b/tests/NLog.Targets.Network.Tests/SplunkLayoutTests.cs @@ -153,7 +153,7 @@ public void CanRenderSplunkSourceType() public void CanRenderSplunkAdditionalEventCustomMessage() { var splunkLayout = new SplunkLayout(); - splunkLayout.SplunkEvents.Add(new JsonAttribute("threadid", "${threadid}") { Encode = false }); + splunkLayout.SplunkFields.Add(new JsonAttribute("threadid", "${threadid}") { Encode = false }); var memTarget = new NLog.Targets.MemoryTarget() { Layout = splunkLayout }; using (var logFactory = new LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(memTarget).WithAsync()).LogFactory) @@ -199,7 +199,7 @@ public void CanRenderSplunkAdditionalEventCustomMessage() public void CanRenderEventProperties() { var splunkLayout = new SplunkLayout(); - splunkLayout.SplunkEvents.Add(new JsonAttribute("mt", "${message:raw=true}")); + splunkLayout.SplunkFields.Add(new JsonAttribute("mt", "${message:raw=true}")); splunkLayout.IncludeEventProperties = true; var memTarget = new NLog.Targets.MemoryTarget() { Layout = splunkLayout }; @@ -250,7 +250,7 @@ public void CanRenderEventProperties() public void CanRenderScopeContext() { var splunkLayout = new SplunkLayout(); - splunkLayout.SplunkEvents.Add(new JsonAttribute("mt", "${message:raw=true}")); + splunkLayout.SplunkFields.Add(new JsonAttribute("mt", "${message:raw=true}")); splunkLayout.IncludeEventProperties = true; splunkLayout.IncludeScopeProperties = true; splunkLayout.ExcludeProperties.Add("World"); From 53810e78e5b18f079255c0c0fba302c7a24fb433 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Mon, 21 Jul 2025 19:14:14 +0200 Subject: [PATCH 214/224] Verify ExpandoObject are still supported with NLog v6 (#5942) --- src/NLog/Internal/ObjectReflectionCache.cs | 13 +++++-------- .../Targets/DefaultJsonSerializerTestsBase.cs | 6 ++++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/NLog/Internal/ObjectReflectionCache.cs b/src/NLog/Internal/ObjectReflectionCache.cs index 8c1f91bd91..243295a80f 100644 --- a/src/NLog/Internal/ObjectReflectionCache.cs +++ b/src/NLog/Internal/ObjectReflectionCache.cs @@ -37,9 +37,6 @@ namespace NLog.Internal using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -#if !NET35 && !NET40 && NETFRAMEWORK - using System.Dynamic; -#endif using System.Linq; using System.Reflection; using NLog.Common; @@ -142,7 +139,7 @@ public bool TryLookupExpandoObject(object value, out ObjectPropertyList objectPr } #if !NET35 && !NET40 && NETFRAMEWORK - if (value is DynamicObject d) + if (value is System.Dynamic.DynamicObject d) { var dictionary = DynamicObjectToDict(d); objectPropertyList = new ObjectPropertyList(dictionary); @@ -556,7 +553,7 @@ public bool Equals(ObjectPropertyInfos other) } #if !NET35 && !NET40 && NETFRAMEWORK - private static Dictionary DynamicObjectToDict(DynamicObject d) + private static Dictionary DynamicObjectToDict(System.Dynamic.DynamicObject d) { var newVal = new Dictionary(); foreach (var propName in d.GetDynamicMemberNames()) @@ -571,9 +568,9 @@ private static Dictionary DynamicObjectToDict(DynamicObject d) } /// - /// Binder for retrieving value of + /// Binder for retrieving value of /// - private sealed class GetBinderAdapter : GetMemberBinder + private sealed class GetBinderAdapter : System.Dynamic.GetMemberBinder { internal GetBinderAdapter(string name) : base(name, false) @@ -581,7 +578,7 @@ internal GetBinderAdapter(string name) } /// - public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) + public override System.Dynamic.DynamicMetaObject FallbackGetMember(System.Dynamic.DynamicMetaObject target, System.Dynamic.DynamicMetaObject errorSuggestion) { return target; } diff --git a/tests/NLog.UnitTests/Targets/DefaultJsonSerializerTestsBase.cs b/tests/NLog.UnitTests/Targets/DefaultJsonSerializerTestsBase.cs index 6d78f8800d..1dedec45d8 100644 --- a/tests/NLog.UnitTests/Targets/DefaultJsonSerializerTestsBase.cs +++ b/tests/NLog.UnitTests/Targets/DefaultJsonSerializerTestsBase.cs @@ -563,8 +563,7 @@ private class CustomNullProperty : IConvertible public override string ToString() => "nullValue"; } -#if !NET35 && !NET40 && NETFRAMEWORK - +#if !NET35 && !NET40 [Fact] public void SerializeExpandoObject_Test() { @@ -574,6 +573,9 @@ public void SerializeExpandoObject_Test() var actual = SerializeObject(object1); Assert.Equal("{\"Id\":123,\"Name\":\"test name\"}", actual); } +#endif + +#if !NET35 && !NET40 && NETFRAMEWORK [Fact] public void SerializeDynamicObject_Test() From c23cb693d5ccf9712f93ca1e0b15c9cd2e2dcde3 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Wed, 23 Jul 2025 21:52:54 +0200 Subject: [PATCH 215/224] NLog.Targets.AtomicFile - Added README.md for nuget-package (#5945) --- src/NLog.Targets.AtomicFile/README.md | 32 +++++++++++++++++++++++ src/NLog.Targets.ConcurrentFile/README.md | 7 ++++- src/NLog.Targets.GZipFile/README.md | 25 ++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 src/NLog.Targets.AtomicFile/README.md create mode 100644 src/NLog.Targets.GZipFile/README.md diff --git a/src/NLog.Targets.AtomicFile/README.md b/src/NLog.Targets.AtomicFile/README.md new file mode 100644 index 0000000000..9952e02c8c --- /dev/null +++ b/src/NLog.Targets.AtomicFile/README.md @@ -0,0 +1,32 @@ +# NLog AtomFile Target + +NLog File Target writing to file using operating system API for atomic file appending (O_APPEND), so multiple processes can write concurrently to the same file. + +If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) + +See the [NLog Wiki](https://github.com/NLog/NLog/wiki/Atomic-File-target) for available options and examples. + +## Linux Support + +Linux requires platform specific publish for [Mono.Posix.NETStandard](https://www.nuget.org/packages/Mono.Posix.NETStandard) nuget-package: +``` +dotnet publish with --framework net8.0 --configuration release --runtime linux-x64 +``` + +## Register Extension + +NLog will only recognize type-alias `AtomFile` when loading from `NLog.config`-file, if having added extension to `NLog.config`-file: + +```xml + + + +``` + +Alternative register from code using [fluent configuration API](https://github.com/NLog/NLog/wiki/Fluent-Configuration-API): + +```csharp +LogManager.Setup().SetupExtensions(ext => { + ext.RegisterTarget(); +}); +``` diff --git a/src/NLog.Targets.ConcurrentFile/README.md b/src/NLog.Targets.ConcurrentFile/README.md index 9cb8cba78e..fa294ff90f 100644 --- a/src/NLog.Targets.ConcurrentFile/README.md +++ b/src/NLog.Targets.ConcurrentFile/README.md @@ -1,6 +1,11 @@ # NLog Concurrent File Target -NLog File Target with support for ConcurrentWrites where multiple processes can write to the same file. +NLog File Target with support for ConcurrentWrites-option where multiple processes can write to the same file. + +This is the legacy FileTarget from NLog v5, if unable to use the new optimized FileTarget included with NLog v6. + +Notice one must explicit configure `ConcurrentWrites="true"` to support concurrent writing to same file. +Alternative consider using the new [NLog.Targets.AtomicFile](https://www.nuget.org/packages/NLog.Targets.AtomicFile) nuget-package. If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) diff --git a/src/NLog.Targets.GZipFile/README.md b/src/NLog.Targets.GZipFile/README.md new file mode 100644 index 0000000000..bb44424119 --- /dev/null +++ b/src/NLog.Targets.GZipFile/README.md @@ -0,0 +1,25 @@ +# NLog GZipFile Target + +NLog File Target writing to file with GZip compression using GZipStream. + +If having trouble with output, then check [NLog InternalLogger](https://github.com/NLog/NLog/wiki/Internal-Logging) for clues. See also [Troubleshooting NLog](https://github.com/NLog/NLog/wiki/Logging-Troubleshooting) + +See the [NLog Wiki](https://github.com/NLog/NLog/wiki/GZip-File-target) for available options and examples. + +## Register Extension + +NLog will only recognize type-alias `GZipFile` when loading from `NLog.config`-file, if having added extension to `NLog.config`-file: + +```xml + + + +``` + +Alternative register from code using [fluent configuration API](https://github.com/NLog/NLog/wiki/Fluent-Configuration-API): + +```csharp +LogManager.Setup().SetupExtensions(ext => { + ext.RegisterTarget(); +}); +``` From 1cb43892d03f87b04bd1bf60e9a1badaa6e860a9 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 26 Jul 2025 12:10:45 +0200 Subject: [PATCH 216/224] FileTarget - Added more file-archive exception handling when KeepFileOpen=false (#5947) --- .../MinimalFileLockingAppender.cs | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/NLog/Targets/FileAppenders/MinimalFileLockingAppender.cs b/src/NLog/Targets/FileAppenders/MinimalFileLockingAppender.cs index a1e336e9d2..c8fa9856a3 100644 --- a/src/NLog/Targets/FileAppenders/MinimalFileLockingAppender.cs +++ b/src/NLog/Targets/FileAppenders/MinimalFileLockingAppender.cs @@ -54,12 +54,23 @@ public DateTime FileLastModified { get { - var fileInfo = new FileInfo(_filePath); - if (fileInfo.Exists && fileInfo.Length != 0) + try { - return Time.TimeSource.Current.FromSystemTime(fileInfo.LastWriteTimeUtc); + var fileInfo = new FileInfo(_filePath); + if (fileInfo.Exists && fileInfo.Length != 0) + { + return Time.TimeSource.Current.FromSystemTime(fileInfo.LastWriteTimeUtc); + } + return OpenStreamTime; + } + catch (Exception ex) + { + NLog.Common.InternalLogger.Error(ex, "{0}: Failed to lookup FileInfo.LastWriteTimeUtc for file: {1}", _fileTarget, _filePath); + if (ex.MustBeRethrown()) + throw; + + return OpenStreamTime; } - return OpenStreamTime; } } @@ -88,9 +99,20 @@ public long FileSize { get { - var fileInfo = new FileInfo(_filePath); - var fileSize = fileInfo.Exists ? fileInfo.Length : 0; - return fileSize; + try + { + var fileInfo = new FileInfo(_filePath); + var fileSize = fileInfo.Exists ? fileInfo.Length : 0; + return fileSize; + } + catch (Exception ex) + { + NLog.Common.InternalLogger.Error(ex, "{0}: Failed to lookup FileInfo.Length for file: {1}", _fileTarget, _filePath); + if (ex.MustBeRethrown()) + throw; + + return 0; + } } } From 2b59ad1c315b110ef340db226cd0b29835df4efc Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 26 Jul 2025 14:00:37 +0200 Subject: [PATCH 217/224] FileTarget - Closing on OpenFileCacheTimeout apply least recently used (#5948) --- .../FileAppenders/DiscardAllFileAppender.cs | 2 + .../ExclusiveFileLockingAppender.cs | 4 +- .../Targets/FileAppenders/IFileAppender.cs | 1 + .../MinimalFileLockingAppender.cs | 6 ++- src/NLog/Targets/FileTarget.cs | 39 +++++++++---------- 5 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/NLog/Targets/FileAppenders/DiscardAllFileAppender.cs b/src/NLog/Targets/FileAppenders/DiscardAllFileAppender.cs index 868d6b1df0..eeed44e7bd 100644 --- a/src/NLog/Targets/FileAppenders/DiscardAllFileAppender.cs +++ b/src/NLog/Targets/FileAppenders/DiscardAllFileAppender.cs @@ -41,6 +41,8 @@ internal sealed class DiscardAllFileAppender : IFileAppender public DateTime OpenStreamTime { get; } + public DateTime LastWriteTime => OpenStreamTime; + public DateTime FileLastModified => OpenStreamTime; public DateTime NextArchiveTime => DateTime.MaxValue; diff --git a/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs index b44a08ea9a..b2c38944a7 100644 --- a/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs +++ b/src/NLog/Targets/FileAppenders/ExclusiveFileLockingAppender.cs @@ -53,6 +53,8 @@ internal sealed class ExclusiveFileLockingAppender : IFileAppender public DateTime OpenStreamTime { get; } + public DateTime LastWriteTime => FileLastModified; + public DateTime FileLastModified { get; private set; } private DateTime FileBirthTime @@ -97,7 +99,7 @@ private long RefreshFileBirthTimeUtc(bool forceRefresh) { FileLastModified = NLog.Time.TimeSource.Current.Time; - if (_fileTarget.ArchiveFileName is null && _fileTarget.ArchiveEvery == FileArchivePeriod.None && _fileTarget.ArchiveAboveSize <= 0) + if (_fileTarget.ArchiveEvery == FileArchivePeriod.None && _fileTarget.ArchiveAboveSize <= 0 && _fileTarget.ArchiveFileName is null) return 0; try diff --git a/src/NLog/Targets/FileAppenders/IFileAppender.cs b/src/NLog/Targets/FileAppenders/IFileAppender.cs index 26d42fbeaa..aaa90f936e 100644 --- a/src/NLog/Targets/FileAppenders/IFileAppender.cs +++ b/src/NLog/Targets/FileAppenders/IFileAppender.cs @@ -44,6 +44,7 @@ internal interface IFileAppender : IDisposable long FileSize { get; } DateTime OpenStreamTime { get; } + DateTime LastWriteTime { get; } DateTime FileLastModified { get; } DateTime NextArchiveTime { get; } diff --git a/src/NLog/Targets/FileAppenders/MinimalFileLockingAppender.cs b/src/NLog/Targets/FileAppenders/MinimalFileLockingAppender.cs index c8fa9856a3..1d4986d386 100644 --- a/src/NLog/Targets/FileAppenders/MinimalFileLockingAppender.cs +++ b/src/NLog/Targets/FileAppenders/MinimalFileLockingAppender.cs @@ -50,6 +50,8 @@ internal sealed class MinimalFileLockingAppender : IFileAppender public DateTime OpenStreamTime { get; } + public DateTime LastWriteTime { get; private set; } + public DateTime FileLastModified { get @@ -121,7 +123,7 @@ public MinimalFileLockingAppender(FileTarget fileTarget, string filePath) _fileTarget = fileTarget; _filePath = filePath; _initialFileOpen = true; - OpenStreamTime = Time.TimeSource.Current.Time; + OpenStreamTime = LastWriteTime = Time.TimeSource.Current.Time; } public void Write(byte[] buffer, int offset, int count) @@ -144,6 +146,8 @@ public void Write(byte[] buffer, int offset, int count) } } } + + LastWriteTime = NLog.Time.TimeSource.Current.Time; } public void Dispose() diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index b3bec9ee45..fb019db69f 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -151,8 +151,8 @@ public Layout FileName /// /// Default: . Higher number might improve performance when single FileTarget /// is writing to many files (such as splitting by loglevel or by logger-name). - /// Files are closed in FIFO (First in First out) ordering, so the oldest - /// file-handle is closed first. Careful with number higher than 10-15, + /// Files are closed in LRU (least recently used) ordering, so files unused + /// for longest period are closed first. Careful with number higher than 10-15, /// because a large number of open files consumes system resources. /// /// @@ -309,7 +309,7 @@ public FileArchivePeriod ArchiveEvery /// public Layout? ArchiveFileName { - get => _archiveFileName ?? (_archiveSuffixFormat?.IndexOf("{1") >= 0 ? FileName : null); + get => _archiveFileName ?? (_archiveSuffixFormatLegacy ? FileName : null); set { var archiveSuffixFormat = _archiveSuffixFormat; @@ -448,9 +448,11 @@ public string ArchiveSuffixFormat } _archiveSuffixFormat = value; + _archiveSuffixFormatLegacy = _archiveSuffixFormat?.IndexOf("{1") >= 0; } } private string? _archiveSuffixFormat; + private bool _archiveSuffixFormatLegacy; /// /// Gets or sets a value indicating whether the footer should be written only when the file is archived. @@ -499,7 +501,6 @@ public OpenFileAppender(IFileAppender fileAppender, int sequenceNumber) private readonly SortHelpers.KeySelector _getFileNameFromLayout; - private DateTime _lastWriteTime; private Timer? _openFileMonitorTimer; /// @@ -770,10 +771,6 @@ private void WriteBytesToFile(string filename, LogEventInfo firstLogEvent, Memor openFile.FileAppender.Dispose(); throw; } - finally - { - _lastWriteTime = firstLogEvent.TimeStamp; - } } private OpenFileAppender RollArchiveFile(string filename, OpenFileAppender openFile, LogEventInfo firstLogEvent, bool hasWritten) @@ -787,8 +784,8 @@ private OpenFileAppender RollArchiveFile(string filename, OpenFileAppender openF lastSequenceNo = openFile.SequenceNumber; DateTime? previousFileLastModified = skipFileLastModified ? default(DateTime?) : openFile.FileAppender.FileLastModified; - if (_lastWriteTime > DateTime.MinValue && previousFileLastModified > _lastWriteTime && (previousFileLastModified == openFile.FileAppender.OpenStreamTime || firstLogEvent.TimeStamp.Date == previousFileLastModified?.Date)) - previousFileLastModified = _lastWriteTime; + if (previousFileLastModified > openFile.FileAppender.LastWriteTime && (previousFileLastModified == openFile.FileAppender.OpenStreamTime || firstLogEvent.TimeStamp.Date == previousFileLastModified?.Date)) + previousFileLastModified = openFile.FileAppender.LastWriteTime; // Close file and roll to next file if (hasWritten) @@ -931,12 +928,12 @@ private void PruneOpenFileCache() while (_openFileCache.Count >= OpenFileCacheSize) { - // Close the oldest filestream (not the least recently used) + // Closing the least recently used DateTime oldestFileTime = DateTime.MaxValue; KeyValuePair oldestOpenFile = default; foreach (var oldOpenFile in _openFileCache) { - if (oldOpenFile.Value.FileAppender.OpenStreamTime < oldestFileTime) + if (oldOpenFile.Value.FileAppender.LastWriteTime < oldestFileTime) { oldestOpenFile = oldOpenFile; } @@ -1035,10 +1032,10 @@ private void OpenFileMonitorTimer(object state) if (OpenFileFlushTimeout > 0 && !AutoFlush) { DateTime flushTime = Time.TimeSource.Current.Time.AddSeconds(-(OpenFileFlushTimeout + 1) * 1.5); - if (_lastWriteTime > flushTime) + // Only Flush when something has been written + foreach (var openFile in _openFileCache) { - // Only Flush when something has been written - foreach (var openFile in _openFileCache) + if (openFile.Value.FileAppender.LastWriteTime > flushTime) { openFile.Value.FileAppender.Flush(); } @@ -1062,22 +1059,24 @@ private void OpenFileMonitorTimer(object state) private void PruneOpenFileCacheUsingTimeout() { DateTime closeTime = Time.TimeSource.Current.Time.AddSeconds(-OpenFileCacheTimeout); - bool oldFilesMustBeClosed = false; + bool unusedFileMustBeClosed = false; foreach (var openFile in _openFileCache) { - if (openFile.Value.FileAppender.OpenStreamTime < closeTime) + // Closing the least recently used, because dangerous to momentarily close "active" file-handles, + // since other background services might take over the file-handle and block application logging. + if (openFile.Value.FileAppender.LastWriteTime < closeTime) { - oldFilesMustBeClosed = true; + unusedFileMustBeClosed = true; break; } } - if (oldFilesMustBeClosed) + if (unusedFileMustBeClosed) { foreach (var openFile in _openFileCache.ToList()) { - if (openFile.Value.FileAppender.OpenStreamTime < closeTime) + if (openFile.Value.FileAppender.LastWriteTime < closeTime) { CloseFile(openFile.Key, openFile.Value); } From abc9507eb970c172445d2f1a04a43b3dd5813983 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 27 Jul 2025 16:37:23 +0200 Subject: [PATCH 218/224] SplunkLayout - Restore SplunkEvents but as obsolete (#5950) --- src/NLog.Targets.Network/Layouts/SplunkLayout.cs | 9 +++++++++ src/NLog.Targets.WebService/WebServiceTarget.cs | 1 + src/NLog/Config/RequiredParameterAttribute.cs | 2 ++ src/NLog/Config/StackTraceUsage.cs | 2 ++ src/NLog/Config/XmlLoggingConfiguration.cs | 3 +++ src/NLog/LayoutRenderers/FuncLayoutRenderer.cs | 2 ++ .../LayoutRenderers/ScopeContextTimingLayoutRenderer.cs | 2 ++ .../Wrappers/WrapperLayoutRendererBuilderBase.cs | 2 ++ src/NLog/Layouts/LayoutRenderOptions.cs | 2 ++ src/NLog/Layouts/SimpleLayout.cs | 1 + src/NLog/LogEventInfo.cs | 3 +++ src/NLog/LogManager.cs | 1 + src/NLog/MappedDiagnosticsContext.cs | 2 ++ src/NLog/MappedDiagnosticsLogicalContext.cs | 2 ++ src/NLog/NestedDiagnosticsContext.cs | 2 ++ src/NLog/NestedDiagnosticsLogicalContext.cs | 2 ++ src/NLog/Targets/ConsoleTarget.cs | 1 + src/NLog/Targets/FileTarget.cs | 3 +++ 18 files changed, 42 insertions(+) diff --git a/src/NLog.Targets.Network/Layouts/SplunkLayout.cs b/src/NLog.Targets.Network/Layouts/SplunkLayout.cs index 5d0e0cfffc..c8fe0c8163 100644 --- a/src/NLog.Targets.Network/Layouts/SplunkLayout.cs +++ b/src/NLog.Targets.Network/Layouts/SplunkLayout.cs @@ -35,6 +35,7 @@ namespace NLog.Layouts { using System; using System.Collections.Generic; + using System.ComponentModel; using NLog.Config; /// @@ -77,6 +78,14 @@ public IList? SplunkFields } } + /// + /// Gets the array of attributes for the "event"-section + /// + [ArrayParameter(typeof(JsonAttribute), "splunkevent")] + [Obsolete("Replaced by SplunkFields to match GelfFields. Marked obsolete with NLog.Targets.Network v6.1")] + [EditorBrowsable(EditorBrowsableState.Never)] + public IList? SplunkEvents => SplunkFields; + /// /// Gets or sets Splunk Message Host-attribute /// diff --git a/src/NLog.Targets.WebService/WebServiceTarget.cs b/src/NLog.Targets.WebService/WebServiceTarget.cs index f367bc1139..86921d7b9c 100644 --- a/src/NLog.Targets.WebService/WebServiceTarget.cs +++ b/src/NLog.Targets.WebService/WebServiceTarget.cs @@ -188,6 +188,7 @@ public WebServiceProxyType ProxyType /// A value of if Rfc3986; otherwise, for legacy Rfc2396. /// [Obsolete("Replaced by WebUtility.UrlEncode. Marked obsolete with NLog 6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public bool EscapeDataRfc3986 { get; set; } /// diff --git a/src/NLog/Config/RequiredParameterAttribute.cs b/src/NLog/Config/RequiredParameterAttribute.cs index 73c2e24a67..7a385d9c28 100644 --- a/src/NLog/Config/RequiredParameterAttribute.cs +++ b/src/NLog/Config/RequiredParameterAttribute.cs @@ -34,6 +34,7 @@ namespace NLog.Config { using System; + using System.ComponentModel; using JetBrains.Annotations; /// @@ -43,6 +44,7 @@ namespace NLog.Config [AttributeUsage(AttributeTargets.Property)] [MeansImplicitUse] [Obsolete("Instead perform relevant config validation in InitializeTarget / InitializeLayout. Marked obsolete with NLog v6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public sealed class RequiredParameterAttribute : Attribute { } diff --git a/src/NLog/Config/StackTraceUsage.cs b/src/NLog/Config/StackTraceUsage.cs index ad064b999b..727d94c14d 100644 --- a/src/NLog/Config/StackTraceUsage.cs +++ b/src/NLog/Config/StackTraceUsage.cs @@ -34,6 +34,7 @@ namespace NLog.Config { using System; + using System.ComponentModel; /// /// Value indicating how stack trace should be captured when processing the log event. @@ -70,6 +71,7 @@ public enum StackTraceUsage /// Stack trace should be captured. This option won't add the filenames and linenumbers. /// [Obsolete("Replace with `WithStackTrace`. Marked obsolete on NLog 5.0")] + [EditorBrowsable(EditorBrowsableState.Never)] WithoutSource = WithStackTrace, /// diff --git a/src/NLog/Config/XmlLoggingConfiguration.cs b/src/NLog/Config/XmlLoggingConfiguration.cs index 7faacd78b3..42ea57cd2c 100644 --- a/src/NLog/Config/XmlLoggingConfiguration.cs +++ b/src/NLog/Config/XmlLoggingConfiguration.cs @@ -121,6 +121,7 @@ public XmlLoggingConfiguration(TextReader xmlSource, string? filePath, LogFactor /// /// XML reader to read from. [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public XmlLoggingConfiguration(System.Xml.XmlReader reader) : this(reader, null) { } @@ -130,6 +131,7 @@ public XmlLoggingConfiguration(System.Xml.XmlReader reader) /// XmlReader containing the configuration section. /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public XmlLoggingConfiguration(System.Xml.XmlReader reader, string? fileName) : this(reader, fileName, LogManager.LogFactory) { } @@ -141,6 +143,7 @@ public XmlLoggingConfiguration(System.Xml.XmlReader reader, string? fileName) /// Path to the config-file that contains the element (to be used as a base for including other files). null is allowed. /// The to which to apply any applicable configuration values. [Obsolete("Instead use TextReader as input. Marked obsolete with NLog 6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public XmlLoggingConfiguration(System.Xml.XmlReader reader, string? fileName, LogFactory logFactory) : base(logFactory) { diff --git a/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs b/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs index cddf3e602e..70e2b85f2b 100644 --- a/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/FuncLayoutRenderer.cs @@ -34,6 +34,7 @@ namespace NLog.LayoutRenderers { using System; + using System.ComponentModel; using System.Globalization; using System.Text; using NLog.Config; @@ -76,6 +77,7 @@ public FuncLayoutRenderer(string layoutRendererName, Func [Obsolete("Public API-property was a mistake. Marked obsolete with NLog v6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public Func RenderMethod => _renderMethod; /// diff --git a/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs b/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs index 5cebb4cbc4..106308bdc8 100644 --- a/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs +++ b/src/NLog/LayoutRenderers/ScopeContextTimingLayoutRenderer.cs @@ -34,6 +34,7 @@ namespace NLog.LayoutRenderers { using System; + using System.ComponentModel; using System.Globalization; using System.Text; using NLog.Internal; @@ -69,6 +70,7 @@ public sealed class ScopeContextTimingLayoutRenderer : LayoutRenderer /// Default: /// [Obsolete("Replaced by StartTime. Marked obsolete on NLog 6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public bool ScopeBeginTime { get => StartTime; set => StartTime = value; } /// diff --git a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs index 10d767771e..5e592a7c85 100644 --- a/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs +++ b/src/NLog/LayoutRenderers/Wrappers/WrapperLayoutRendererBuilderBase.cs @@ -34,6 +34,7 @@ namespace NLog.LayoutRenderers.Wrappers { using System; + using System.ComponentModel; using System.Text; using NLog.Internal; @@ -43,6 +44,7 @@ namespace NLog.LayoutRenderers.Wrappers /// This expects the transformation to work on a /// [Obsolete("Inherit from WrapperLayoutRendererBase and override RenderInnerAndTransform() instead. Marked obsolete in NLog 5.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public abstract class WrapperLayoutRendererBuilderBase : WrapperLayoutRendererBase { /// diff --git a/src/NLog/Layouts/LayoutRenderOptions.cs b/src/NLog/Layouts/LayoutRenderOptions.cs index 969da6c355..d29a23ab78 100644 --- a/src/NLog/Layouts/LayoutRenderOptions.cs +++ b/src/NLog/Layouts/LayoutRenderOptions.cs @@ -34,6 +34,7 @@ namespace NLog.Layouts { using System; + using System.ComponentModel; /// /// Options available for @@ -49,6 +50,7 @@ public enum LayoutRenderOptions /// Layout renderer method can handle concurrent threads /// [Obsolete("All LayoutRenderers and Layout should be ThreadSafe by default. Marked obsolete with NLog 5.0")] + [EditorBrowsable(EditorBrowsableState.Never)] ThreadSafe = 1, /// /// Layout renderer method is agnostic to current thread context. This means it will render the same result independent of thread-context. diff --git a/src/NLog/Layouts/SimpleLayout.cs b/src/NLog/Layouts/SimpleLayout.cs index 0b74923de3..13a469a461 100644 --- a/src/NLog/Layouts/SimpleLayout.cs +++ b/src/NLog/Layouts/SimpleLayout.cs @@ -207,6 +207,7 @@ internal SimpleLayout(LayoutRenderer[] layoutRenderers, [Localizable(false)] str /// '${' with '${literal:text=${}' /// [Obsolete("Instead use Layout.FromLiteral()")] + [EditorBrowsable(EditorBrowsableState.Never)] public static string Escape([Localizable(false)] string text) { return text.Replace("${", @"${literal:text=\$\{}"); diff --git a/src/NLog/LogEventInfo.cs b/src/NLog/LogEventInfo.cs index 2e5ac0ef75..66cedbdb8b 100644 --- a/src/NLog/LogEventInfo.cs +++ b/src/NLog/LogEventInfo.cs @@ -261,6 +261,7 @@ public int SequenceID /// Gets the stack frame of the method that did the logging. /// [Obsolete("Instead use ${callsite} or CallerMemberName. Marked obsolete with NLog 5.3")] + [EditorBrowsable(EditorBrowsableState.Never)] public StackFrame? UserStackFrame => CallSiteInformation?.UserStackFrame; /// @@ -268,6 +269,7 @@ public int SequenceID /// code (not the NLog code). /// [Obsolete("Instead use ${callsite} or CallerMemberName. Marked obsolete with NLog 5.4")] + [EditorBrowsable(EditorBrowsableState.Never)] public int UserStackFrameNumber => CallSiteInformation?.UserStackFrameNumberLegacy ?? CallSiteInformation?.UserStackFrameNumber ?? 0; /// @@ -600,6 +602,7 @@ public void SetStackTrace(StackTrace stackTrace) /// The stack trace. /// Index of the first user stack frame within the stack trace (Negative means NLog should skip stackframes from System-assemblies). [Obsolete("Instead use SetStackTrace or SetCallerInfo. Marked obsolete with NLog 5.4")] + [EditorBrowsable(EditorBrowsableState.Never)] public void SetStackTrace(StackTrace stackTrace, int userStackFrame) { GetCallSiteInformationInternal().SetStackTrace(stackTrace, userStackFrame >= 0 ? userStackFrame : (int?)null); diff --git a/src/NLog/LogManager.cs b/src/NLog/LogManager.cs index 18ee4fa998..ba79c5fc46 100644 --- a/src/NLog/LogManager.cs +++ b/src/NLog/LogManager.cs @@ -178,6 +178,7 @@ public static LogFactory LoadConfiguration(string configFile) /// /// The assembly to skip. [Obsolete("Replaced by LogManager.Setup().SetupLogFactory(setup => setup.AddCallSiteHiddenAssembly(assembly)). Marked obsolete on NLog 5.3")] + [EditorBrowsable(EditorBrowsableState.Never)] public static void AddHiddenAssembly(Assembly assembly) { CallSiteInformation.AddCallSiteHiddenAssembly(assembly); diff --git a/src/NLog/MappedDiagnosticsContext.cs b/src/NLog/MappedDiagnosticsContext.cs index 51950cf100..1f258dccfc 100644 --- a/src/NLog/MappedDiagnosticsContext.cs +++ b/src/NLog/MappedDiagnosticsContext.cs @@ -35,6 +35,7 @@ namespace NLog { using System; using System.Collections.Generic; + using System.ComponentModel; using NLog.Config; using NLog.Internal; @@ -45,6 +46,7 @@ namespace NLog /// Stores the dictionary in the thread-local static variable, and provides methods to output dictionary values in layouts. /// [Obsolete("Replaced by ScopeContext.PushProperty or Logger.PushScopeProperty using ${scopeproperty}. Marked obsolete on NLog 5.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public static class MappedDiagnosticsContext { /// diff --git a/src/NLog/MappedDiagnosticsLogicalContext.cs b/src/NLog/MappedDiagnosticsLogicalContext.cs index 77232ca825..fee717f154 100644 --- a/src/NLog/MappedDiagnosticsLogicalContext.cs +++ b/src/NLog/MappedDiagnosticsLogicalContext.cs @@ -35,6 +35,7 @@ namespace NLog { using System; using System.Collections.Generic; + using System.ComponentModel; using NLog.Internal; /// @@ -49,6 +50,7 @@ namespace NLog /// NLog library so that state can be maintained for multiple threads in asynchronous situations. /// [Obsolete("Replaced by ScopeContext.PushProperty or Logger.PushScopeProperty using ${scopeproperty}. Marked obsolete on NLog 5.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public static class MappedDiagnosticsLogicalContext { /// diff --git a/src/NLog/NestedDiagnosticsContext.cs b/src/NLog/NestedDiagnosticsContext.cs index c8d64b6d75..e924651266 100644 --- a/src/NLog/NestedDiagnosticsContext.cs +++ b/src/NLog/NestedDiagnosticsContext.cs @@ -34,6 +34,7 @@ namespace NLog { using System; + using System.ComponentModel; using System.Linq; using NLog.Internal; @@ -44,6 +45,7 @@ namespace NLog /// Stores the stack in the thread-local static variable, and provides methods to output the values in layouts. /// [Obsolete("Replaced by ScopeContext.PushNestedState or Logger.PushScopeNested using ${scopenested}. Marked obsolete on NLog 5.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public static class NestedDiagnosticsContext { /// diff --git a/src/NLog/NestedDiagnosticsLogicalContext.cs b/src/NLog/NestedDiagnosticsLogicalContext.cs index eed302d16a..610002b3ce 100644 --- a/src/NLog/NestedDiagnosticsLogicalContext.cs +++ b/src/NLog/NestedDiagnosticsLogicalContext.cs @@ -34,6 +34,7 @@ namespace NLog { using System; + using System.ComponentModel; using System.Linq; using NLog.Internal; @@ -44,6 +45,7 @@ namespace NLog /// Stores the stack in the logical thread callcontexte, and provides methods to output the values in layouts. /// [Obsolete("Replaced by ScopeContext.PushNestedState or Logger.PushScopeNested using ${scopenested}. Marked obsolete on NLog 5.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public static class NestedDiagnosticsLogicalContext { /// diff --git a/src/NLog/Targets/ConsoleTarget.cs b/src/NLog/Targets/ConsoleTarget.cs index 1c1436c4fb..c3bb0e754a 100644 --- a/src/NLog/Targets/ConsoleTarget.cs +++ b/src/NLog/Targets/ConsoleTarget.cs @@ -143,6 +143,7 @@ public Encoding Encoding /// Default: /// [Obsolete("Replaced by ForceWriteLine (but inverted value). Marked obsolete with NLog v6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public bool WriteBuffer { get => !ForceWriteLine; set => ForceWriteLine = !value; } /// diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index fb019db69f..50e54900cf 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -35,6 +35,7 @@ namespace NLog.Targets { using System; using System.Collections.Generic; + using System.ComponentModel; using System.IO; using System.Linq; using System.Text; @@ -241,6 +242,7 @@ public bool WriteBom /// Default: /// [Obsolete("Instead use ArchiveSuffixFormat. Marked obsolete with NLog 6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public string? ArchiveDateFormat { get => _archiveDateFormat; @@ -382,6 +384,7 @@ public int MaxArchiveDays /// Default: Sequence /// [Obsolete("Instead use ArchiveSuffixFormat. Marked obsolete with NLog 6.0")] + [EditorBrowsable(EditorBrowsableState.Never)] public string ArchiveNumbering { get => _archiveNumbering ?? "Sequence"; From 00f05838669c797407708b251665c83325ed2828 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Thu, 7 Aug 2025 20:34:42 +0200 Subject: [PATCH 219/224] FileTarget - Close old files when reaching OpenFileCacheSize (#5952) --- .../Targets/ConsoleWordHighlightingRule.cs | 2 +- src/NLog/Targets/FileTarget.cs | 2 +- .../ConcurrentFileTargetTests.cs | 15 +++++++++++ .../Targets/AsyncTaskTargetTest.cs | 4 +-- .../NLog.UnitTests/Targets/FileTargetTests.cs | 26 ++++++++++++++----- 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/NLog/Targets/ConsoleWordHighlightingRule.cs b/src/NLog/Targets/ConsoleWordHighlightingRule.cs index 82f1792fb0..8c6bb8b95c 100644 --- a/src/NLog/Targets/ConsoleWordHighlightingRule.cs +++ b/src/NLog/Targets/ConsoleWordHighlightingRule.cs @@ -72,7 +72,7 @@ public ConsoleWordHighlightingRule(string text, ConsoleOutputColor foregroundCol public ConditionExpression? Condition { get; set; } /// - /// Gets or sets the text to be matched. You must specify either text or regex. + /// Gets or sets the text to be matched. /// /// Default: /// diff --git a/src/NLog/Targets/FileTarget.cs b/src/NLog/Targets/FileTarget.cs index 50e54900cf..0dde541328 100644 --- a/src/NLog/Targets/FileTarget.cs +++ b/src/NLog/Targets/FileTarget.cs @@ -941,7 +941,7 @@ private void PruneOpenFileCache() oldestOpenFile = oldOpenFile; } } - if (!string.IsNullOrEmpty(oldestOpenFile.Key)) + if (string.IsNullOrEmpty(oldestOpenFile.Key)) break; CloseFileWithFooter(oldestOpenFile.Key, oldestOpenFile.Value, false); diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs index 39d3b11d98..cc9779d4da 100644 --- a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs @@ -403,6 +403,7 @@ public void RollingArchiveEveryMonth() ArchiveNumbering = ArchiveNumberingMode.Rolling, ArchiveEvery = FileArchiveEveryPeriod.Month, MaxArchiveFiles = 1, + EnableFileDelete = false, }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -426,6 +427,20 @@ public void RollingArchiveEveryMonth() Assert.Equal(14, Path.GetFileName(file).Length); } + var fileOpenList = files.Where(f => + { + try + { + File.Delete(f); + return false; + } + catch + { + return true; + } + }).ToList(); + Assert.Equal(fileTarget.OpenFileCacheSize, fileOpenList.Count); + LogManager.Configuration = null; // Flush and close } finally diff --git a/tests/NLog.UnitTests/Targets/AsyncTaskTargetTest.cs b/tests/NLog.UnitTests/Targets/AsyncTaskTargetTest.cs index 22dc5d33e5..4dc47a07e8 100644 --- a/tests/NLog.UnitTests/Targets/AsyncTaskTargetTest.cs +++ b/tests/NLog.UnitTests/Targets/AsyncTaskTargetTest.cs @@ -55,11 +55,11 @@ class AsyncTaskTestTarget : AsyncTaskTarget public Type RequiredDependency { get; set; } - public bool WaitForWriteEvent(int timeoutMilliseconds = 1000) + public bool WaitForWriteEvent(int timeoutMilliseconds = 5000) { #if DEBUG if (System.Diagnostics.Debugger.IsAttached) - timeoutMilliseconds = timeoutMilliseconds * 60; + timeoutMilliseconds = timeoutMilliseconds * 10; #endif if (_writeEvent.WaitOne(TimeSpan.FromMilliseconds(timeoutMilliseconds))) { diff --git a/tests/NLog.UnitTests/Targets/FileTargetTests.cs b/tests/NLog.UnitTests/Targets/FileTargetTests.cs index bf234e1378..1d921fa4be 100644 --- a/tests/NLog.UnitTests/Targets/FileTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/FileTargetTests.cs @@ -360,6 +360,7 @@ public void RollingArchiveEveryMonday(bool keepFileOpen) ArchiveEvery = FileArchivePeriod.Monday, MaxArchiveFiles = 1, KeepFileOpen = keepFileOpen, + EnableFileDelete = false, }; LogManager.Setup().LoadConfiguration(c => c.ForLogger().WriteTo(fileTarget)); @@ -373,10 +374,17 @@ public void RollingArchiveEveryMonday(bool keepFileOpen) } } + LogManager.Flush(); + + Assert.True(File.Exists(Path.Combine(tempDir, "OO_AppName.log"))); + Assert.Equal(3, new FileInfo(Path.Combine(tempDir, "OO_AppName.log")).Length); + File.Delete(Path.Combine(tempDir, "OO_AppName.log")); + Assert.False(File.Exists(Path.Combine(tempDir, "OO_AppName.log"))); + LogManager.Configuration = null; // Flush and close var files = new DirectoryInfo(tempDir).GetFiles(); - Assert.Equal(25, files.Length); + Assert.Equal(24, files.Length); foreach (var file in files) { @@ -2318,7 +2326,9 @@ public void MultiFileWrite() { FileName = Path.Combine(tempDir, "${level}.txt"), LineEnding = LineEndingMode.LF, - Layout = "${message}" + Layout = "${message}", + OpenFileCacheSize = 4, + EnableFileDelete = false, }; LogManager.Setup().LoadConfiguration(c => c.ForLogger(LogLevel.Debug).WriteTo(fileTarget)); @@ -2334,7 +2344,7 @@ public void MultiFileWrite() logger.Fatal("eee"); } - LogManager.Configuration = null; // Flush + LogManager.Configuration = null; // Flush and close Assert.False(File.Exists(Path.Combine(tempDir, "Trace.txt"))); @@ -2370,7 +2380,8 @@ public void BufferedMultiFileWrite() { FileName = Path.Combine(tempDir, "${level}.txt"), LineEnding = LineEndingMode.LF, - Layout = "${message}" + Layout = "${message}", + OpenFileCacheSize = 4, }; LogManager.Setup().LoadConfiguration(c => c.ForLogger(LogLevel.Debug).WriteTo(new BufferingTargetWrapper(fileTarget, 10))); @@ -2386,7 +2397,7 @@ public void BufferedMultiFileWrite() logger.Fatal("eee"); } - LogManager.Configuration = null; // Flush + LogManager.Configuration = null; // Flush and close Assert.False(File.Exists(Path.Combine(tempDir, "Trace.txt"))); @@ -2422,7 +2433,8 @@ public void AsyncMultiFileWrite() { FileName = Path.Combine(tempDir, "${level}.txt"), LineEnding = LineEndingMode.LF, - Layout = "${message} ${threadid}" + Layout = "${message} ${threadid}", + OpenFileCacheSize = 4, }; // this also checks that thread-volatile layouts @@ -2443,7 +2455,7 @@ public void AsyncMultiFileWrite() logger.Fatal("eee"); } - LogManager.Configuration = null; // Flush + LogManager.Configuration = null; // Flush and close Assert.False(File.Exists(Path.Combine(tempDir, "Trace.txt"))); From c44b4d1cd65c221c91eb0086db49c1ed03d2b9be Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 8 Aug 2025 10:47:57 +0200 Subject: [PATCH 220/224] NLog.Targets.Network nuget-package links (#5953) --- .../INetworkInterfaceRetriever.cs | 0 .../NetworkInterfaceRetriever.cs | 0 .../LocalIpAddressLayoutRenderer.cs | 0 .../NLog.Targets.Network.csproj | 21 ++++++++++++++++--- .../ConcurrentFileTargetTests.cs | 4 +++- .../NLog.UnitTests/Properties/AssemblyInfo.cs | 4 ++++ 6 files changed, 25 insertions(+), 4 deletions(-) rename src/NLog.Targets.Network/{ => Internal}/INetworkInterfaceRetriever.cs (100%) rename src/NLog.Targets.Network/{ => Internal}/NetworkInterfaceRetriever.cs (100%) rename src/NLog.Targets.Network/{ => LayoutRenderers}/LocalIpAddressLayoutRenderer.cs (100%) diff --git a/src/NLog.Targets.Network/INetworkInterfaceRetriever.cs b/src/NLog.Targets.Network/Internal/INetworkInterfaceRetriever.cs similarity index 100% rename from src/NLog.Targets.Network/INetworkInterfaceRetriever.cs rename to src/NLog.Targets.Network/Internal/INetworkInterfaceRetriever.cs diff --git a/src/NLog.Targets.Network/NetworkInterfaceRetriever.cs b/src/NLog.Targets.Network/Internal/NetworkInterfaceRetriever.cs similarity index 100% rename from src/NLog.Targets.Network/NetworkInterfaceRetriever.cs rename to src/NLog.Targets.Network/Internal/NetworkInterfaceRetriever.cs diff --git a/src/NLog.Targets.Network/LocalIpAddressLayoutRenderer.cs b/src/NLog.Targets.Network/LayoutRenderers/LocalIpAddressLayoutRenderer.cs similarity index 100% rename from src/NLog.Targets.Network/LocalIpAddressLayoutRenderer.cs rename to src/NLog.Targets.Network/LayoutRenderers/LocalIpAddressLayoutRenderer.cs diff --git a/src/NLog.Targets.Network/NLog.Targets.Network.csproj b/src/NLog.Targets.Network/NLog.Targets.Network.csproj index 2a1f6e764d..6dac87c59d 100644 --- a/src/NLog.Targets.Network/NLog.Targets.Network.csproj +++ b/src/NLog.Targets.Network/NLog.Targets.Network.csproj @@ -15,9 +15,24 @@ Copyright (c) 2004-$(CurrentYear) NLog Project - https://nlog-project.org/ - NetworkTarget Docs: - https://github.com/NLog/NLog/wiki/Network-target - +NetworkTarget Docs: +https://github.com/NLog/NLog/wiki/Network-target + +SyslogTarget Docs: +https://github.com/NLog/NLog/wiki/Syslog-target + +Log4JXmlTarget Docs: +https://github.com/NLog/NLog/wiki/Log4JXml-target + +GelfTarget Docs: +https://github.com/NLog/NLog/wiki/Gelf-target + +SplunkTarget Docs: +https://github.com/NLog/NLog/wiki/Splunk-target + +Local IP Docs: +https://github.com/NLog/NLog/wiki/Local-IP-Address-Layout-Renderer + README.md NLog;TCPIP;TCP;UDP;Network;logging;log N.png diff --git a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs index cc9779d4da..883c9f4e5a 100644 --- a/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs +++ b/tests/NLog.Targets.ConcurrentFile.Tests/ConcurrentFileTargetTests.cs @@ -439,9 +439,11 @@ public void RollingArchiveEveryMonth() return true; } }).ToList(); - Assert.Equal(fileTarget.OpenFileCacheSize, fileOpenList.Count); LogManager.Configuration = null; // Flush and close + + if (fileOpenList.Count >= fileTarget.OpenFileCacheSize) + Assert.Equal(fileTarget.OpenFileCacheSize, fileOpenList.Count); } finally { diff --git a/tests/NLog.UnitTests/Properties/AssemblyInfo.cs b/tests/NLog.UnitTests/Properties/AssemblyInfo.cs index 32cfaa48e6..8adb8e8068 100644 --- a/tests/NLog.UnitTests/Properties/AssemblyInfo.cs +++ b/tests/NLog.UnitTests/Properties/AssemblyInfo.cs @@ -31,6 +31,7 @@ // THE POSSIBILITY OF SUCH DAMAGE. // +using System.Diagnostics.CodeAnalysis; using Xunit.Abstractions; using Xunit.Sdk; @@ -38,6 +39,9 @@ [assembly: Xunit.TestFramework("NLogThrowExceptionsDefault", "NLog.UnitTests")] +[assembly: SuppressMessage("Performance", "CA1861:Avoid constant arrays as arguments", Justification = "Not production code.")] +[assembly: SuppressMessage("Maintainability", "CA1822:Member does not access instance data and can be marked as static", Justification = "Not production code.")] + public class NLogThrowExceptionsDefault : XunitTestFramework { public NLogThrowExceptionsDefault(IMessageSink messageSink) From 7ba11a30e8fe03c42f3aae66d65cf0f4135b6a31 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Fri, 8 Aug 2025 17:07:54 +0200 Subject: [PATCH 221/224] ConsoleWordHighlightingRule - Support List of Words for simpler configuration (#5954) --- .../Targets/ConsoleWordHighlightingRule.cs | 150 ++++++++++++++++-- .../Targets/ColoredConsoleTargetTests.cs | 21 +++ 2 files changed, 156 insertions(+), 15 deletions(-) diff --git a/src/NLog/Targets/ConsoleWordHighlightingRule.cs b/src/NLog/Targets/ConsoleWordHighlightingRule.cs index 8c6bb8b95c..338a46cc84 100644 --- a/src/NLog/Targets/ConsoleWordHighlightingRule.cs +++ b/src/NLog/Targets/ConsoleWordHighlightingRule.cs @@ -33,6 +33,7 @@ namespace NLog.Targets { + using System; using System.Collections.Generic; using NLog.Conditions; using NLog.Config; @@ -72,13 +73,19 @@ public ConsoleWordHighlightingRule(string text, ConsoleOutputColor foregroundCol public ConditionExpression? Condition { get; set; } /// - /// Gets or sets the text to be matched. + /// Gets or sets the text to be matched for Highlighting. /// /// Default: /// public string Text { get => _text; set => _text = string.IsNullOrEmpty(value) ? string.Empty : value; } private string _text = string.Empty; + /// + /// Gets or sets the list of words to be matched for Highlighting. + /// + /// + public List? Words { get; set; } + /// /// Gets or sets a value indicating whether to match whole words only. /// @@ -114,44 +121,157 @@ public ConsoleWordHighlightingRule(string text, ConsoleOutputColor foregroundCol internal protected virtual IEnumerable>? GetWordsForHighlighting(string haystack) { if (ReferenceEquals(_text, string.Empty)) + { + if (Words?.Count > 0) + { + return YieldWordMatchesForHighlighting(haystack, Words); + } return null; + } + + return YieldMatchesForHighlighting(_text, haystack); + } + + private IEnumerable>? YieldWordMatchesForHighlighting(string haystack, List words) + { + IEnumerable>? allMatches = null; + foreach (var needle in words) + { + if (string.IsNullOrEmpty(needle)) + continue; + + var needleMatch = YieldMatchesForHighlighting(needle, haystack); + if (needleMatch is null) + continue; + + allMatches = allMatches is null ? needleMatch : MergeWordMatches(allMatches, needleMatch); + } + + return allMatches; + } + + private static IEnumerable> MergeWordMatches(IEnumerable> allMatches, IEnumerable> needleMatch) + { + if (needleMatch is IList> singleMatch && singleMatch.Count == 1) + { + var match = singleMatch[0]; + var allMatchesList = PrepareAllMatchesList(allMatches, 1); + MergeAllNeedleMatches(allMatchesList, match); + return allMatchesList; + } + else + { + var allMatchesList = PrepareAllMatchesList(allMatches, 3); + int startIndex = 0; + foreach (var match in needleMatch) + { + startIndex = MergeAllNeedleMatches(allMatchesList, match, startIndex); + } + return allMatchesList; + } + } + + private static int MergeAllNeedleMatches(IList> allMatchesList, KeyValuePair newMatch, int startIndex = 0) + { + for (int i = startIndex; i < allMatchesList.Count; ++i) + { + var existingMatch = allMatchesList[i]; + if (NeedleMatchOverlaps(newMatch, existingMatch)) + { + newMatch = MergeNeedleMatch(newMatch, existingMatch); + allMatchesList[i] = newMatch; + // Handle that the new merged match can also overlap following matches + while (i < allMatchesList.Count - 1 && NeedleMatchOverlaps(newMatch, allMatchesList[i + 1])) + { + newMatch = MergeNeedleMatch(newMatch, allMatchesList[i + 1]); + allMatchesList[i] = newMatch; + allMatchesList.RemoveAt(i + 1); + } + return i; + } + else if (newMatch.Key < existingMatch.Key) + { + allMatchesList.Insert(i, newMatch); + return i + 1; + } + } + + allMatchesList.Add(newMatch); + return allMatchesList.Count; + } + + private static bool NeedleMatchOverlaps(KeyValuePair first, KeyValuePair second) + { + if (first.Key < second.Key) + return (first.Key + first.Value) > second.Key; + else + return (second.Key + second.Value) > first.Key; + } + + private static KeyValuePair MergeNeedleMatch(KeyValuePair first, KeyValuePair second) + { + if (first.Key < second.Key) + return new KeyValuePair(first.Key, Math.Max(first.Key + first.Value, second.Key + second.Value) - first.Key); + else + return new KeyValuePair(second.Key, Math.Max(first.Key + first.Value, second.Key + second.Value) - second.Key); + } + + private static IList> PrepareAllMatchesList(IEnumerable> allMatches, int extraCapacity) + { + int existingCapacity = 3; + + if (allMatches is IList> allMatchesList) + { + if (!allMatchesList.IsReadOnly) + return allMatchesList; - int firstIndex = FindNextWordForHighlighting(haystack, null); + existingCapacity = Math.Max(allMatchesList.Count, existingCapacity); + } + + allMatchesList = new List>(existingCapacity + extraCapacity); + foreach (var match in allMatches) + allMatchesList.Add(match); + return allMatchesList; + } + + private IEnumerable>? YieldMatchesForHighlighting(string needle, string haystack) + { + int firstIndex = FindNextWordForHighlighting(needle, haystack, null); if (firstIndex < 0) return null; - int nextIndex = FindNextWordForHighlighting(haystack, firstIndex); + int nextIndex = FindNextWordForHighlighting(needle, haystack, firstIndex); if (nextIndex < 0) - return new[] { new KeyValuePair(firstIndex, Text.Length) }; + return new[] { new KeyValuePair(firstIndex, needle.Length) }; - return YieldWordsForHighlighting(haystack, firstIndex, nextIndex); + return YieldWordsForHighlighting(needle, haystack, firstIndex, nextIndex); } - private IEnumerable> YieldWordsForHighlighting(string haystack, int firstIndex, int nextIndex) + private IEnumerable> YieldWordsForHighlighting(string needle, string haystack, int firstIndex, int nextIndex) { - yield return new KeyValuePair(firstIndex, _text.Length); + yield return new KeyValuePair(firstIndex, needle.Length); - yield return new KeyValuePair(nextIndex, _text.Length); + yield return new KeyValuePair(nextIndex, needle.Length); int index = nextIndex; while (index >= 0) { - index = FindNextWordForHighlighting(haystack, index); + index = FindNextWordForHighlighting(needle, haystack, index); if (index >= 0) - yield return new KeyValuePair(index, _text.Length); + yield return new KeyValuePair(index, needle.Length); } } - private int FindNextWordForHighlighting(string haystack, int? prevIndex) + private int FindNextWordForHighlighting(string needle, string haystack, int? prevIndex) { - int index = prevIndex.HasValue ? prevIndex.Value + _text.Length : 0; + int index = prevIndex.HasValue ? prevIndex.Value + needle.Length : 0; while (index >= 0) { - index = IgnoreCase ? haystack.IndexOf(_text, index, System.StringComparison.CurrentCultureIgnoreCase) : haystack.IndexOf(_text, index); - if (index < 0 || (!WholeWords || StringHelpers.IsWholeWord(haystack, _text, index))) + index = IgnoreCase ? haystack.IndexOf(needle, index, System.StringComparison.CurrentCultureIgnoreCase) : haystack.IndexOf(needle, index); + if (index < 0 || (!WholeWords || StringHelpers.IsWholeWord(haystack, needle, index))) return index; - index += _text.Length; + index += needle.Length; } return index; } diff --git a/tests/NLog.UnitTests/Targets/ColoredConsoleTargetTests.cs b/tests/NLog.UnitTests/Targets/ColoredConsoleTargetTests.cs index 60c50c96bd..e04c5e7930 100644 --- a/tests/NLog.UnitTests/Targets/ColoredConsoleTargetTests.cs +++ b/tests/NLog.UnitTests/Targets/ColoredConsoleTargetTests.cs @@ -224,6 +224,27 @@ public void ColoredConsoleAnsi_RowColor_VerificationTest(string inputText, strin string.Empty); } + [Theory] + [InlineData("The big warning message", "The \x1b[31mbig\x1b[0m warning message\x1b[0m")] + [InlineData("The big\r\nwarning message", "The \x1b[31mbig\x1b[0m\x1b[0m\r\nwarning message\x1b[0m")] + [InlineData("The bigger warning message", "The \x1b[31mbigger\x1b[0m warning message\x1b[0m")] + [InlineData("The big big bigger warning message", "The \x1b[31mbig big bigger\x1b[0m warning message\x1b[0m")] + public void ColoredConsoleAnsi_WordColor_VerificationTest(string inputText, string expectedResult) + { + var target = new ColoredConsoleTarget { Layout = "${message}", EnableAnsiOutput = true }; + target.UseDefaultRowHighlightingRules = false; + target.WordHighlightingRules.Add(new ConsoleWordHighlightingRule + { + Words = new List(new string[] { "big", "bigger", "big big bigger", "b" }), + ForegroundColor = ConsoleOutputColor.DarkRed, + BackgroundColor = ConsoleOutputColor.NoChange + }); + + AssertOutput(target, inputText, + new string[] { expectedResult }, + string.Empty); + } + [Fact] public void ColoredConsoleAnsi_RowColorWithWordHighlight_VerificationTest() { From a4aacb7f67f59cdb81d2d04981064ae209eea543 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 9 Aug 2025 11:45:36 +0200 Subject: [PATCH 222/224] LogMessageTemplateFormatter - Also use IValueFormatter for positional templates (#5955) --- src/NLog/Config/LoggingConfigurationParser.cs | 2 +- .../Internal/LogMessageTemplateFormatter.cs | 10 +- src/NLog/MessageTemplates/ValueFormatter.cs | 51 +-- .../MessageTemplates/RendererTests.cs | 46 ++- .../MessageTemplates/ValueFormatterTest.cs | 353 +++++++++--------- 5 files changed, 252 insertions(+), 210 deletions(-) diff --git a/src/NLog/Config/LoggingConfigurationParser.cs b/src/NLog/Config/LoggingConfigurationParser.cs index 24d549decd..a090791e34 100644 --- a/src/NLog/Config/LoggingConfigurationParser.cs +++ b/src/NLog/Config/LoggingConfigurationParser.cs @@ -128,7 +128,7 @@ private void SetNLogElementSettings(ILoggingConfigurationElement nlogConfig) var sortedList = CreateUniqueSortedListFromConfig(nlogConfig); CultureInfo? defaultCultureInfo = DefaultCultureInfo ?? LogFactory._defaultCultureInfo; - bool? parseMessageTemplates = null; + bool? parseMessageTemplates = LogFactory.ServiceRepository.ResolveParseMessageTemplates(); bool internalLoggerEnabled = false; bool autoLoadExtensions = false; foreach (var configItem in sortedList) diff --git a/src/NLog/Internal/LogMessageTemplateFormatter.cs b/src/NLog/Internal/LogMessageTemplateFormatter.cs index 45d2115554..5cf5408c3c 100644 --- a/src/NLog/Internal/LogMessageTemplateFormatter.cs +++ b/src/NLog/Internal/LogMessageTemplateFormatter.cs @@ -279,17 +279,13 @@ internal string Render(ref TemplateEnumerator templateEnumerator, IFormatProvide private void RenderHolePositional(StringBuilder sb, in Hole hole, IFormatProvider? formatProvider, object? value) { - if (value is null) - { - sb.Append("NULL"); - } - else if (hole.CaptureType == CaptureType.Normal) + if (hole.CaptureType == CaptureType.Serialize) { - MessageTemplates.ValueFormatter.FormatToString(value, hole.Format, formatProvider, sb); + RenderHole(sb, CaptureType.Serialize, hole.Format, formatProvider, value); } else { - RenderHole(sb, hole.CaptureType, hole.Format, formatProvider, value); + RenderHole(sb, CaptureType.Stringify, hole.Format ?? string.Empty, formatProvider, value); } } diff --git a/src/NLog/MessageTemplates/ValueFormatter.cs b/src/NLog/MessageTemplates/ValueFormatter.cs index 87e39c6637..e8e39fbcca 100644 --- a/src/NLog/MessageTemplates/ValueFormatter.cs +++ b/src/NLog/MessageTemplates/ValueFormatter.cs @@ -118,9 +118,12 @@ public bool FormatValue(object? value, string? format, CaptureType captureType, } case CaptureType.Stringify: { - builder.Append('"'); - FormatToString(value, null, formatProvider, builder); - builder.Append('"'); + bool includeQuotes = format is null || _legacyStringQuotes; + if (includeQuotes) + builder.Append('"'); + FormatToString(value, format, formatProvider, builder); + if (includeQuotes) + builder.Append('"'); return true; } default: @@ -165,34 +168,37 @@ private bool SerializeSimpleObject(object? value, string? format, IFormatProvide return true; } - if (value is null) + // Optimize for types that are pretty much invariant in all cultures when no format-string + if (value is IConvertible convertible) { - builder.Append("NULL"); + SerializeConvertibleObject(convertible, format, formatProvider, builder); return true; } - // Optimize for types that are pretty much invariant in all cultures when no format-string - if (value is IConvertible convertibleValue) + if (value is IFormattable formattable) { - SerializeConvertibleObject(convertibleValue, format, formatProvider, builder); +#if !NETFRAMEWORK + if (string.IsNullOrEmpty(format)) + builder.AppendFormat(formatProvider, "{0}", formattable); // Support ISpanFormattable + else +#endif + builder.Append(formattable.ToString(format, formatProvider)); return true; } - else - { - if (!string.IsNullOrEmpty(format) && value is IFormattable formattable) - { - builder.Append(formattable.ToString(format, formatProvider)); - return true; - } - if (convertToString) - { - SerializeConvertToString(value, formatProvider, builder); - return true; - } + if (value is null) + { + builder.Append("NULL"); + return true; + } - return false; + if (convertToString) + { + SerializeConvertToString(value, formatProvider, builder); + return true; } + + return false; } private void SerializeConvertibleObject(IConvertible value, string? format, IFormatProvider? formatProvider, StringBuilder builder) @@ -371,8 +377,7 @@ private void SerializeCollectionItem(object item, string? format, IFormatProvide /// Append to this public static void FormatToString(object? value, string? format, IFormatProvider? formatProvider, StringBuilder builder) { - var stringValue = value as string; - if (stringValue != null) + if (value is string stringValue) { builder.Append(stringValue); } diff --git a/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs b/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs index ee89787d84..152d44b999 100644 --- a/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs +++ b/tests/NLog.UnitTests/MessageTemplates/RendererTests.cs @@ -35,6 +35,7 @@ namespace NLog.UnitTests.MessageTemplates { using System; using System.Globalization; + using NLog.Config; using Xunit; public class RendererTests @@ -105,6 +106,23 @@ public void RenderDateTime(string input, string arg, string expected) RenderAndTest(input, culture, new object[] { dt }, expected); } + [Theory] + [InlineData("test {0}", "1970-01-01", "test 1970-01-01 00:00:00")] + [InlineData("test {0:u}", "1970-01-01", "test 1970-01-01 00:00:00Z")] + [InlineData("test {0:MM/dd/yy}", "1970-01-01", "test 01/01/70")] + public void RenderDateTimeOverride(string input, string arg, string expected) + { + var culture = CultureInfo.InvariantCulture; + + var logFactory = new LogFactory(); + var orgValueFormatter = logFactory.ServiceRepository.ResolveService(); + var newValueFormatter = new OverrideValueFormatter(orgValueFormatter); + logFactory.ServiceRepository.RegisterSingleton(newValueFormatter); + + DateTime dt = DateTime.Parse(arg, culture, DateTimeStyles.AdjustToUniversal); + RenderAndTest(input, culture, new object[] { dt }, expected, logFactory); + } + [Theory] [InlineData("test {0:c}", "1:2:3:4.5", "test 1.02:03:04.5000000")] [InlineData("test {0:hh\\:mm\\:ss\\.ff}", "1:2:3:4.5", "test 02:03:04.50")] @@ -127,12 +145,36 @@ public void RenderDateTimeOffset(string input, string arg, string expected) RenderAndTest(input, culture, new object[] { dto }, expected); } - private static void RenderAndTest(string input, CultureInfo culture, object[] args, string expected) + private static void RenderAndTest(string input, CultureInfo culture, object[] args, string expected, LogFactory logFactory = null) { var logEventInfoAlways = new LogEventInfo(LogLevel.Info, "Logger", culture, input, args); - logEventInfoAlways.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(LogManager.LogFactory, true, false).MessageFormatter, null); + logEventInfoAlways.SetMessageFormatter(new NLog.Internal.LogMessageTemplateFormatter(logFactory ?? new LogFactory(), true, false).MessageFormatter, null); var templateAlways = logEventInfoAlways.MessageTemplateParameters; Assert.Equal(expected, logEventInfoAlways.FormattedMessage); } + + private sealed class OverrideValueFormatter : IValueFormatter + { + private readonly IValueFormatter _orgValueFormatter; + + public OverrideValueFormatter(IValueFormatter orgValueFormatter) + { + _orgValueFormatter = orgValueFormatter; + } + + public bool FormatValue(object value, string format, NLog.MessageTemplates.CaptureType captureType, IFormatProvider formatProvider, System.Text.StringBuilder builder) + { + if (string.IsNullOrEmpty(format)) + { + if (value is DateTime) + { + formatProvider = System.Globalization.CultureInfo.InvariantCulture; + format = "yyyy-MM-dd HH:mm:ss.FFFFFFFK"; + } + } + + return _orgValueFormatter.FormatValue(value, format, captureType, formatProvider, builder); + } + } } } diff --git a/tests/NLog.UnitTests/MessageTemplates/ValueFormatterTest.cs b/tests/NLog.UnitTests/MessageTemplates/ValueFormatterTest.cs index 47e71c14bf..6eaa97f9fc 100644 --- a/tests/NLog.UnitTests/MessageTemplates/ValueFormatterTest.cs +++ b/tests/NLog.UnitTests/MessageTemplates/ValueFormatterTest.cs @@ -42,169 +42,12 @@ namespace NLog.UnitTests.MessageTemplates { public class ValueFormatterTest : NLogTestBase { - enum TestData - { - Foo, Bar - }; - private sealed class Test : IFormattable, IConvertible - { - public Test() - { - Str = "Test"; - Integer = 1; - } - - public Test(TypeCode typeCode) : this() - { - TypeCode = typeCode; - } - - public TestData Data { get; set; } - public string Str { get; set; } - - public int Integer { get; set; } - - public TypeCode TypeCode { get; set; } - - public TypeCode GetTypeCode() - { - return TypeCode; - } - - public bool ToBoolean(IFormatProvider provider) - { - return true; - } - - public byte ToByte(IFormatProvider provider) - { - return 1; - } - - public char ToChar(IFormatProvider provider) - { - return 't'; - } - - public DateTime ToDateTime(IFormatProvider provider) - { - return new DateTime(2019, 7, 28); - } - - public decimal ToDecimal(IFormatProvider provider) - { - return Integer; - } - - public double ToDouble(IFormatProvider provider) - { - return Integer; - } - - public short ToInt16(IFormatProvider provider) - { - return 1; - } - - public int ToInt32(IFormatProvider provider) - { - return Integer; - } - - public long ToInt64(IFormatProvider provider) - { - return Integer; - } - - public sbyte ToSByte(IFormatProvider provider) - { - return 1; - } - - public float ToSingle(IFormatProvider provider) - { - return Integer; - } - - public string ToString(string format, IFormatProvider formatProvider) - { - return Str; - } - - public string ToString(IFormatProvider provider) - { - return Str; - } - - public object ToType(Type conversionType, IFormatProvider provider) - { - return Integer; - } - - public ushort ToUInt16(IFormatProvider provider) - { - return 1; - } - - public uint ToUInt32(IFormatProvider provider) - { - return 1; - } - - public ulong ToUInt64(IFormatProvider provider) - { - return 1; - } - } - - private sealed class Test1 - { - public Test1() - { - Str = "Test"; - Integer = 1; - } - public string Str { get; set; } - - public int Integer { get; set; } - } - - private sealed class Test2 - { - public Test2() - { - Str = "Test"; - Integer = 1; - } - public string Str { get; set; } - - public int Integer { get; set; } - - } - - private sealed class RecursiveTest - { - public RecursiveTest(int integer) - { - Integer = integer + 1; - } - public RecursiveTest Next => new RecursiveTest(Integer); - - public int Integer { get; set; } - - } - - private static ValueFormatter CreateValueFormatter() - { - return new ValueFormatter(LogManager.LogFactory.ServiceRepository, legacyStringQuotes: false); - } - [Fact] public void TestSerialisationOfStringToJsonIsSuccessful() { var str = "Test"; StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(str, string.Empty, CaptureType.Serialize, null, builder); + var result = CreateValueFormatter().FormatValue(str, null, CaptureType.Serialize, null, builder); Assert.True(result); Assert.Equal("\"Test\"", builder.ToString()); } @@ -214,7 +57,7 @@ public void TestSerialisationOfClassObjectToJsonIsSuccessful() { var @class = new Test2(); StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Serialize, null, builder); + var result = CreateValueFormatter().FormatValue(@class, null, CaptureType.Serialize, null, builder); Assert.True(result); Assert.Equal("{\"Str\":\"Test\", \"Integer\":1}", builder.ToString()); } @@ -224,7 +67,7 @@ public void TestSerialisationOfRecursiveClassObjectToJsonIsSuccessful() { var @class = new RecursiveTest(0); StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Serialize, null, builder); + var result = CreateValueFormatter().FormatValue(@class, null, CaptureType.Serialize, null, builder); Assert.True(result); var actual = builder.ToString(); @@ -240,7 +83,7 @@ public void TestStringifyOfStringIsSuccessful() { var @class = "str"; StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Stringify, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(@class, null, CaptureType.Stringify, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.Equal("\"str\"", builder.ToString()); } @@ -250,7 +93,7 @@ public void TestStringifyOfIFormatableObjectIsSuccessful() { var @class = new Test(); StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Stringify, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(@class, null, CaptureType.Stringify, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.Equal("\"Test\"", builder.ToString()); } @@ -260,7 +103,7 @@ public void TestStringifyOfNonIFormatableObjectIsSuccessful() { var @class = new Test1(); StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Stringify, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(@class, null, CaptureType.Stringify, new CultureInfo("fr-FR"), builder); Assert.True(result); var expectedValue = $"\"{typeof(Test1).FullName}\""; Assert.Equal(expectedValue, builder.ToString()); @@ -271,7 +114,7 @@ public void TestSerializationOfListObjectIsSuccessful() { var list = new List() { 1, 2, 3, 4, 5, 6 }; StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(list, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(list, null, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.Equal("1, 2, 3, 4, 5, 6", builder.ToString()); } @@ -281,7 +124,7 @@ public void TestSerializationOfDictionaryObjectIsSuccessful() { var list = new Dictionary() { { 1, new Test() }, { 2, new Test1() } }; StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(list, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(list, null, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.Equal($"1=Test, 2={typeof(Test1).FullName}", builder.ToString()); } @@ -291,7 +134,7 @@ public void TestSerializationOfCollectionOfListObjectWithDepth2IsNotSuccessful() { var list = new List>>>() { new List>>() { new List>() { new List() { 1, 2 }, new List() { 3, 4 } }, new List>() { new List() { 4, 5 }, new List() { 6, 7 } } } }; StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(list, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(list, null, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.NotEqual("1,2,3,4,5,6,7", builder.ToString()); } @@ -301,7 +144,7 @@ public void TestSerializationWillbeSkippedForElementsThatHaveRepeatedElements() { var list = new List>>>() { new List>>() { new List>() { new List() { 1, 2 }, new List() { 1, 2 } }, new List>() { new List() { 1, 2 }, new List() { 1, 2 } } } }; StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(list, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(list, null, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.NotEqual("1,2", builder.ToString()); } @@ -313,7 +156,7 @@ public void TestSerializationWillbeSkippedForElementsThatHaveRepeatedElements() public void TestSerializationWillBeSuccessfulForNull(CaptureType captureType, string expected) { StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(null, string.Empty, captureType, null, builder); + var result = CreateValueFormatter().FormatValue(null, null, captureType, null, builder); Assert.True(result); Assert.Equal(expected, builder.ToString()); } @@ -323,7 +166,7 @@ public void TestSerializationWillBeSuccessfulForNullObjects() { object list = null; StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(list, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(list, null, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.Equal("NULL", builder.ToString()); } @@ -333,7 +176,7 @@ public void TestSerializationOfStringIsSuccessful() { var @class = "str"; StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(@class, null, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.Equal("str", builder.ToString()); } @@ -343,7 +186,7 @@ public void TestSerialisationOfIConvertibleObjectIsSuccessful() { var @class = new Test(TypeCode.Object); StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(@class, null, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.Equal("Test", builder.ToString()); } @@ -353,7 +196,7 @@ public void TestSerialisationOfIConvertibleStringObjectIsSuccessful() { var @class = new Test(TypeCode.String); StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(@class, null, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); var expectedValue = $"{typeof(Test).FullName}"; Assert.Equal(expectedValue, builder.ToString()); @@ -364,7 +207,7 @@ public void TestSerialisationOfIConvertibleBooleanObjectIsSuccessful() { var @class = new Test(TypeCode.Boolean); StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(@class, null, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.Equal("true", builder.ToString()); } @@ -374,7 +217,7 @@ public void TestSerialisationOfIConvertibleCharObjectIsSuccessful() { var @class = new Test(TypeCode.Char); StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(@class, null, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.Equal("t", builder.ToString()); } @@ -392,7 +235,7 @@ public void TestSerialisationOfIConvertibleNumericObjectIsSuccessful(TypeCode co { var @class = new Test(code); StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(@class, null, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.Equal("1", builder.ToString()); } @@ -402,7 +245,7 @@ public void TestSerialisationOfIConvertibleEnumObjectIsSuccessful() { var @class = new Test(TypeCode.Byte); StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(@class.Data, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(@class.Data, null, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.Equal("Foo", builder.ToString()); } @@ -412,9 +255,165 @@ public void TestSerialisationOfIConvertibleDateTimeObjectIsSuccessful() { var @class = new Test(TypeCode.DateTime); StringBuilder builder = new StringBuilder(); - var result = CreateValueFormatter().FormatValue(@class, string.Empty, CaptureType.Normal, new CultureInfo("fr-FR"), builder); + var result = CreateValueFormatter().FormatValue(@class, null, CaptureType.Normal, new CultureInfo("fr-FR"), builder); Assert.True(result); Assert.Equal("Test", builder.ToString()); } + + private static ValueFormatter CreateValueFormatter() + { + return new ValueFormatter(LogManager.LogFactory.ServiceRepository, legacyStringQuotes: false); + } + + enum TestData + { + Foo, Bar + }; + + private sealed class Test : IFormattable, IConvertible + { + public Test() + { + Str = "Test"; + Integer = 1; + } + + public Test(TypeCode typeCode) : this() + { + TypeCode = typeCode; + } + + public TestData Data { get; set; } + public string Str { get; set; } + + public int Integer { get; set; } + + public TypeCode TypeCode { get; set; } + + public TypeCode GetTypeCode() + { + return TypeCode; + } + + public bool ToBoolean(IFormatProvider provider) + { + return true; + } + + public byte ToByte(IFormatProvider provider) + { + return 1; + } + + public char ToChar(IFormatProvider provider) + { + return 't'; + } + + public DateTime ToDateTime(IFormatProvider provider) + { + return new DateTime(2019, 7, 28); + } + + public decimal ToDecimal(IFormatProvider provider) + { + return Integer; + } + + public double ToDouble(IFormatProvider provider) + { + return Integer; + } + + public short ToInt16(IFormatProvider provider) + { + return 1; + } + + public int ToInt32(IFormatProvider provider) + { + return Integer; + } + + public long ToInt64(IFormatProvider provider) + { + return Integer; + } + + public sbyte ToSByte(IFormatProvider provider) + { + return 1; + } + + public float ToSingle(IFormatProvider provider) + { + return Integer; + } + + public string ToString(string format, IFormatProvider formatProvider) + { + return Str; + } + + public string ToString(IFormatProvider provider) + { + return Str; + } + + public object ToType(Type conversionType, IFormatProvider provider) + { + return Integer; + } + + public ushort ToUInt16(IFormatProvider provider) + { + return 1; + } + + public uint ToUInt32(IFormatProvider provider) + { + return 1; + } + + public ulong ToUInt64(IFormatProvider provider) + { + return 1; + } + } + + private sealed class Test1 + { + public Test1() + { + Str = "Test"; + Integer = 1; + } + public string Str { get; set; } + + public int Integer { get; set; } + } + + private sealed class Test2 + { + public Test2() + { + Str = "Test"; + Integer = 1; + } + public string Str { get; set; } + + public int Integer { get; set; } + } + + private sealed class RecursiveTest + { + public RecursiveTest(int integer) + { + Integer = integer + 1; + } + public RecursiveTest Next => new RecursiveTest(Integer); + + public int Integer { get; set; } + } } } From bd3ce8bb100836f6a20e98b43a39bea9a7d63793 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sat, 9 Aug 2025 14:41:25 +0200 Subject: [PATCH 223/224] ValueFormatter - Reduce code complexity (#5958) --- src/NLog/MessageTemplates/ValueFormatter.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/NLog/MessageTemplates/ValueFormatter.cs b/src/NLog/MessageTemplates/ValueFormatter.cs index e8e39fbcca..2cdd09f7b5 100644 --- a/src/NLog/MessageTemplates/ValueFormatter.cs +++ b/src/NLog/MessageTemplates/ValueFormatter.cs @@ -143,7 +143,7 @@ public bool FormatValue(object? value, string? format, CaptureType captureType, /// public bool FormatObject(object? value, string? format, IFormatProvider? formatProvider, StringBuilder builder) { - if (SerializeSimpleObject(value, format, formatProvider, builder, false)) + if (SerializeSimpleObject(value, format, formatProvider, builder)) { return true; } @@ -160,7 +160,7 @@ public bool FormatObject(object? value, string? format, IFormatProvider? formatP /// /// Try serializing a scalar (string, int, NULL) or simple type (IFormattable) /// - private bool SerializeSimpleObject(object? value, string? format, IFormatProvider? formatProvider, StringBuilder builder, bool convertToString = true) + private bool SerializeSimpleObject(object? value, string? format, IFormatProvider? formatProvider, StringBuilder builder) { if (value is string stringValue) { @@ -192,12 +192,6 @@ private bool SerializeSimpleObject(object? value, string? format, IFormatProvide return true; } - if (convertToString) - { - SerializeConvertToString(value, formatProvider, builder); - return true; - } - return false; } @@ -364,8 +358,8 @@ private void SerializeCollectionItem(object item, string? format, IFormatProvide SerializeConvertibleObject(convertible, format, formatProvider, builder); else if (item is IEnumerable enumerable) SerializeWithoutCyclicLoop(enumerable, format, formatProvider, builder, objectsInPath, depth + 1); - else - SerializeSimpleObject(item, format, formatProvider, builder); + else if (!SerializeSimpleObject(item, format, formatProvider, builder)) + SerializeConvertToString(item, formatProvider, builder); } /// From e276e1969a52b8ecc5cc84c1670a516efea0cb19 Mon Sep 17 00:00:00 2001 From: Rolf Kristensen Date: Sun, 10 Aug 2025 20:18:57 +0200 Subject: [PATCH 224/224] Version 6.0.3 (#5959) --- CHANGELOG.md | 12 +++++++ build.ps1 | 2 +- src/NLog/Config/Factory.cs | 68 ++++++++++++++++++++++++++------------ src/NLog/NLog.csproj | 28 +++++----------- 4 files changed, 67 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f81ff568df..60557ab251 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ Date format: (year/month/day) ## Change Log +### Version 6.0.3 (2025/08/10) + +**Improvements** +- [#5952](https://github.com/NLog/NLog/pull/5952) FileTarget - Close old files when reaching OpenFileCacheSize. (@snakefoot) +- [#5948](https://github.com/NLog/NLog/pull/5948) FileTarget - Closing on OpenFileCacheTimeout apply least recently used. (@snakefoot) +- [#5947](https://github.com/NLog/NLog/pull/5947) FileTarget - Improved file-archive exception handling when KeepFileOpen=false. (@snakefoot) +- [#5954](https://github.com/NLog/NLog/pull/5954) ColoredConsoleTarget - Added Words-property for easy highlighting of many words without RegEx. (@snakefoot) +- [#5955](https://github.com/NLog/NLog/pull/5955) LogMessageTemplateFormatter - Also use IValueFormatter for positional templates. (@snakefoot) +- [#5953](https://github.com/NLog/NLog/pull/5953) NLog.Targets.Network - Updated links in README.md for nuget-package. (@snakefoot) +- [#5945](https://github.com/NLog/NLog/pull/5945) NLog.Targets.AtomicFile - Added README.md for nuget-package. (@snakefoot) +- [#5940](https://github.com/NLog/NLog/pull/5940) SplunkTarget - Support SplunkFields-property. (@snakefoot) + ### Version 6.0.2 (2025/07/20) **Improvements** diff --git a/build.ps1 b/build.ps1 index 972fd7cd23..5e3cbd4f12 100644 --- a/build.ps1 +++ b/build.ps1 @@ -2,7 +2,7 @@ # creates NuGet package at \artifacts dotnet --version -$versionPrefix = "6.0.2" +$versionPrefix = "6.0.3" $versionSuffix = "" $versionFile = $versionPrefix + "." + ${env:APPVEYOR_BUILD_NUMBER} $versionProduct = $versionPrefix; diff --git a/src/NLog/Config/Factory.cs b/src/NLog/Config/Factory.cs index 3a43288add..68d882a127 100644 --- a/src/NLog/Config/Factory.cs +++ b/src/NLog/Config/Factory.cs @@ -241,8 +241,8 @@ public static TBaseType CreateInstance(this IFactory facto var normalName = NormalizeName(typeAlias); var message = $"Failed to create {typeof(TBaseType).Name} with unknown type-alias: '{typeAlias}'"; - if (normalName != null && (normalName.StartsWith("aspnet", StringComparison.OrdinalIgnoreCase) || - normalName.StartsWith("iis", StringComparison.OrdinalIgnoreCase))) + if (normalName.StartsWith("aspnet", StringComparison.OrdinalIgnoreCase) || + normalName.StartsWith("iis", StringComparison.OrdinalIgnoreCase)) { #if NETFRAMEWORK message += " - Extension NLog.Web not included?"; @@ -250,83 +250,107 @@ public static TBaseType CreateInstance(this IFactory facto message += " - Extension NLog.Web.AspNetCore not included?"; #endif } - else if (normalName?.StartsWith("database", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("HostAppName", StringComparison.OrdinalIgnoreCase)) + { + message += " - Extension NLog.Web.AspNetCore not included?"; + } + else if (normalName.StartsWith("HostEnvironment", StringComparison.OrdinalIgnoreCase)) + { + message += " - Extension NLog.Web.AspNetCore not included?"; + } + else if (normalName.StartsWith("HostRootDir", StringComparison.OrdinalIgnoreCase)) + { + message += " - Extension NLog.Web.AspNetCore not included?"; + } + else if (normalName.StartsWith("configsetting", StringComparison.OrdinalIgnoreCase)) + { + message += " - Extension NLog.Extensions.Logging not included?"; + } + else if (normalName.StartsWith("MicrosoftConsoleJsonLayout", StringComparison.OrdinalIgnoreCase)) + { + message += " - Extension NLog.Extensions.Logging not included?"; + } + else if (normalName.StartsWith("MicrosoftConsoleLayout", StringComparison.OrdinalIgnoreCase)) + { + message += " - Extension NLog.Extensions.Logging not included?"; + } + else if (normalName.StartsWith("database", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Database not included?"; } - else if (normalName?.StartsWith("network", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("network", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Targets.Network not included?"; } - else if (normalName?.StartsWith("nlogviewer", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("nlogviewer", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Targets.Network not included?"; } - else if (normalName?.StartsWith("chainsaw", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("chainsaw", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Targets.Network not included?"; } - else if (normalName?.StartsWith("Log4JXml", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("Log4JXml", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Targets.Network not included?"; } - else if (normalName?.StartsWith("syslog", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("syslog", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Targets.Network not included?"; } - else if (normalName?.StartsWith("gelf", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("gelf", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Targets.Network not included?"; } - else if (normalName?.StartsWith("localip", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("localip", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Targets.Network not included?"; } - else if (normalName?.StartsWith("webservice", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("webservice", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Targets.WebService not included?"; } - else if (normalName?.StartsWith("atomFile", StringComparison.OrdinalIgnoreCase) == true || normalName?.StartsWith("atomicFile", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("atomFile", StringComparison.OrdinalIgnoreCase) || normalName.StartsWith("atomicFile", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Targets.AtomicFile not included?"; } - else if (normalName?.StartsWith("GZipFile", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("GZipFile", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Targets.GZipFile not included?"; } - else if (normalName?.StartsWith("trace", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("trace", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Targets.Trace not included?"; } - else if (normalName?.StartsWith("activityid", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("activityid", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Targets.Trace not included?"; } - else if (normalName?.StartsWith("mailkit", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("mailkit", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.MailKit not included?"; } - else if (normalName?.StartsWith("mail", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("mail", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.Targets.Mail not included?"; } - else if (normalName?.StartsWith("eventlog", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("eventlog", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.WindowsEventLog not included?"; } - else if (normalName?.StartsWith("windowsidentity", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("windowsidentity", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.WindowsIdentity not included?"; } - else if (normalName?.StartsWith("outputdebugstring", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("outputdebugstring", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.OutputDebugString not included?"; } - else if (normalName?.StartsWith("performancecounter", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("performancecounter", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.PerformanceCounter not included?"; } - else if (normalName?.StartsWith("regexreplace", StringComparison.OrdinalIgnoreCase) == true) + else if (normalName.StartsWith("regexreplace", StringComparison.OrdinalIgnoreCase)) { message += " - Extension NLog.RegEx not included?"; } diff --git a/src/NLog/NLog.csproj b/src/NLog/NLog.csproj index 8fae7e29ec..bef9696cdd 100644 --- a/src/NLog/NLog.csproj +++ b/src/NLog/NLog.csproj @@ -30,26 +30,14 @@ For ASP.NET Core, check: https://www.nuget.org/packages/NLog.Web.AspNetCore Changelog: -- (#5930) XmlParser - Handle XML comments after root-end-tag. (@snakefoot) -- (#5929) XmlLoggingConfiguration - Improve handling of invalid XML. (@snakefoot) -- (#5933) Handle invalid message template when skipping parameters array. (@snakefoot) -- (#5915) ReplaceNewLinesLayoutRendererWrapper - Replace more line ending characters. (@oikku) -- (#5911) NLog.Targets.GZipFile - Improve support for ArchiveAboveSize. (@snakefoot) -- (#5921) FileTarget - Activate legacy ArchiveFileName when ArchiveSuffixFormat contains legacy placeholder. (@snakefoot) -- (#5924) AsyncTargetWrapper - Updated FullBatchSizeWriteLimit default value from 5 to 10. (@snakefoot) -- (#5937) Mark Assembly loading with RequiresUnreferencedCodeAttribute for AOT. (@snakefoot) -- (#5938) Logger - Align WriteToTargets with WriteToTargetsWithSpan. (@snakefoot) -- (#5909) ConfigurationItemFactory - Added extension hints for webservice and activityid. (@snakefoot) -- (#5918) Log4JXmlTarget - Removed alias NLogViewer as conflicts with other nuget-packages. (@snakefoot) -- (#5926) SplunkTarget - NetworkTarget with SplunkLayout. (@snakefoot) -- (#5927) GelfLayout - Align with SplunkLayout. (@snakefoot) -- (#5913) NLog.Targets.Network - Updated nuget-package README.md. (@snakefoot) -- (#5912) NLog.Targets.Trace - Updated nuget-package README.md. (@snakefoot) -- (#5919) XML docs for Targets and Layouts with remarks about default value. (@snakefoot) -- (#5922) XML docs for LayoutRenderers with remarks about default value. (@snakefoot) -- (#5925) XML docs for Target Wrappers with remarks about default value. (@snakefoot) -- (#5935) Improve NLog XSD Schema with better handling of typed Layout. (@snakefoot) -- (#5923) Updated unit-tests from NET6 to NET8. (@snakefoot) +- [#5952] FileTarget - Close old files when reaching OpenFileCacheSize. (@snakefoot) +- [#5948] FileTarget - Closing on OpenFileCacheTimeout apply least recently used. (@snakefoot) +- [#5947] FileTarget - Improved file-archive exception handling when KeepFileOpen=false. (@snakefoot) +- [#5954] ColoredConsoleTarget - Added Words-property for easy highlighting of many words without RegEx. (@snakefoot) +- [#5955] LogMessageTemplateFormatter - Also use IValueFormatter for positional templates. (@snakefoot) +- [#5953] NLog.Targets.Network - Updated links in README.md for nuget-package. (@snakefoot) +- [#5945] NLog.Targets.AtomicFile - Added README.md for nuget-package. (@snakefoot) +- [#5940] SplunkTarget - Support SplunkFields-property. (@snakefoot) NLog v6.0 release notes: https://nlog-project.org/2025/04/29/nlog-6-0-major-changes.html
Condition