/// <summary>
 /// Allocates object[]-array for <see cref="LogEventInfo.Parameters"/> after checking
 /// for mismatch between Microsoft Extension Logging and NLog Message Template Parser
 /// </summary>
 /// <remarks>
 /// Cannot trust the parameters received from Microsoft Extension Logging, as extra parameters can be injected
 /// </remarks>
 private static void CaptureLogEventInfoParameters(LogEventInfo logEvent, NLogMessageParameterList messageParameters, MessageTemplateParameters messageTemplateParameters)
 {
     if (messageTemplateParameters.IsPositional)
     {
         logEvent.Parameters = CreatePositionalLogEventInfoParameters(messageParameters, messageTemplateParameters, out var extraProperties);
         if (extraProperties?.Count > 0)
         {
             CaptureMessagePropertiesList(logEvent, extraProperties);
         }
     }
     else if (!AllParameterCorrectlyPositionalMapped(messageParameters, messageTemplateParameters))
     {
         // Resolves mismatch between the input from Microsoft Extension Logging TState and NLog Message Template Parser
         logEvent.Parameters = CreateStructuredLogEventInfoParameters(messageParameters, messageTemplateParameters, out var extraProperties);
         if (extraProperties?.Count > 0)
         {
             CaptureMessagePropertiesList(logEvent, extraProperties);
         }
     }
     else
     {
         // Everything is mapped correctly, inject messageParameters directly as params-array
         var paramsArray = new object[messageTemplateParameters.Count];
         for (int i = 0; i < paramsArray.Length; ++i)
         {
             paramsArray[i] = messageParameters[i].Value;
         }
         logEvent.Parameters = paramsArray;
     }
 }
Beispiel #2
0
        /// <summary>
        /// Checks if the already parsed input message-parameters must be sent through
        /// the NLog MessageTemplate Parser for proper handling of message-template syntax.
        /// </summary>
        /// <remarks>
        /// Using the NLog MesageTemplate Parser will hurt performance: 1 x Microsoft Parser - 2 x NLog Parser - 1 x NLog Formatter
        /// </remarks>
        private LogEventInfo TryParseLogEventInfo(LogLevel nLogLogLevel, NLogMessageParameterList messageParameters)
        {
            if (messageParameters?.OriginalMessage != null && (messageParameters.HasComplexParameters || (_options.ParseMessageTemplates && messageParameters.Count > 0)))
            {
                // NLog MessageTemplate Parser must be used
                var eventInfo = new LogEventInfo(nLogLogLevel, _logger.Name, null, messageParameters.OriginalMessage as string, _singleItemArray);
                var messagetTemplateParameters = eventInfo.MessageTemplateParameters;   // Forces parsing of OriginalMessage
                if (messagetTemplateParameters.Count > 0)
                {
                    // We have parsed the message and found parameters, now we need to do the parameter mapping
                    eventInfo.Parameters = CreateLogEventInfoParameters(messageParameters, messagetTemplateParameters, out var extraProperties);
                    if (extraProperties?.Count > 0)
                    {
                        // Need to harvest additional parameters
                        foreach (var property in extraProperties)
                        {
                            eventInfo.Properties[property.Name] = property.Value;
                        }
                    }
                    return(eventInfo);
                }

                return(null);    // Parsing not possible
            }

            return(null);    // Parsing not needed
        }
        private static object[] CreateStructuredLogEventInfoParameters(NLogMessageParameterList messageParameters, MessageTemplateParameters messageTemplateParameters, out List <KeyValuePair <string, object> > extraProperties)
        {
            extraProperties = null;

            var paramsArray           = new object[messageTemplateParameters.Count];
            int startPos              = 0;
            int messageParameterCount = messageParameters.Count;

            for (int i = 0; i < messageParameterCount; ++i)
            {
                var propertyName = messageParameters[i].Name;

                bool extraProperty = true;
                for (int j = startPos; j < messageTemplateParameters.Count; ++j)
                {
                    if (propertyName.Equals(messageTemplateParameters[j].Name, StringComparison.Ordinal))
                    {
                        extraProperty  = false;
                        paramsArray[j] = messageParameters[i].Value;
                        if (startPos == j)
                        {
                            startPos++;
                        }
                        break;
                    }
                }

                if (extraProperty)
                {
                    extraProperties = AddExtraProperty(extraProperties, messageParameters[i]);
                }
            }

            return(paramsArray);
        }
        private static object[] CreatePositionalLogEventInfoParameters(NLogMessageParameterList messageParameters, MessageTemplateParameters messageTemplateParameters, out List <KeyValuePair <string, object> > extraProperties)
        {
            extraProperties = null;

            var maxIndex = FindMaxIndex(messageTemplateParameters);

            object[] paramsArray           = null;
            int      messageParameterCount = messageParameters.Count;

            for (int i = 0; i < messageParameterCount; ++i)
            {
                // First positional name is the startPos
                if (char.IsDigit(messageParameters[i].Name[0]) && paramsArray == null)
                {
                    paramsArray = new object[maxIndex + 1];
                    for (int j = 0; j <= maxIndex; ++j)
                    {
                        if (i + j < messageParameterCount)
                        {
                            paramsArray[j] = messageParameters[i + j].Value;
                        }
                    }
                    i += maxIndex;
                }
                else
                {
                    extraProperties = AddExtraProperty(extraProperties, messageParameters[i]);
                }
            }

            return(paramsArray ?? new object[maxIndex + 1]);
        }
Beispiel #5
0
        public void Log <TState>(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception exception, Func <TState, Exception, string> formatter)
        {
            var nLogLogLevel = ConvertLogLevel(logLevel);

            if (!IsEnabled(nLogLogLevel))
            {
                return;
            }
            if (formatter == null)
            {
                throw new ArgumentNullException(nameof(formatter));
            }

            var messageParameters = NLogMessageParameterList.TryParse(_options.CaptureMessageTemplates ? state as IReadOnlyList <KeyValuePair <string, object> > : null);

            LogEventInfo eventInfo =
                TryParseLogEventInfo(nLogLogLevel, messageParameters) ??
                CreateLogEventInfo(nLogLogLevel, formatter(state, exception), messageParameters);

            if (exception != null)
            {
                eventInfo.Exception = exception;
            }

            CaptureEventId(eventInfo, eventId);

            if (messageParameters == null)
            {
                CaptureMessageProperties(eventInfo, state);
            }

            _logger.Log(typeof(Microsoft.Extensions.Logging.ILogger), eventInfo);
        }
Beispiel #6
0
 /// <summary>
 /// Allocates object[]-array for <see cref="LogEventInfo.Parameters"/> after checking
 /// for mismatch between Microsoft Extension Logging and NLog Message Template Parser
 /// </summary>
 /// <remarks>
 /// Cannot trust the parameters received from Microsoft Extension Logging, as extra parameters can be injected
 /// </remarks>
 private static object[] CreateLogEventInfoParameters(NLogMessageParameterList messageParameters, MessageTemplateParameters messagetTemplateParameters, out List <MessageTemplateParameter> extraProperties)
 {
     if (AllParameterCorrectlyPositionalMapped(messageParameters, messagetTemplateParameters))
     {
         // Everything is mapped correctly, inject messageParameters directly as params-array
         extraProperties = null;
         var paramsArray = new object[messagetTemplateParameters.Count];
         for (int i = 0; i < paramsArray.Length; ++i)
         {
             paramsArray[i] = messageParameters[i].Value;
         }
         return(paramsArray);
     }
     else
     {
         // Resolves mismatch between the input from Microsoft Extension Logging TState and NLog Message Template Parser
         if (messagetTemplateParameters.IsPositional)
         {
             return(CreatePositionalLogEventInfoParameters(messageParameters, messagetTemplateParameters, out extraProperties));
         }
         else
         {
             return(CreateStructuredLogEventInfoParameters(messageParameters, messagetTemplateParameters, out extraProperties));
         }
     }
 }
Beispiel #7
0
        private static object[] CreateStructuredLogEventInfoParameters(NLogMessageParameterList messageParameters, MessageTemplateParameters messagetTemplateParameters, out List <MessageTemplateParameter> extraProperties)
        {
            extraProperties = null;

            var paramsArray = new object[messagetTemplateParameters.Count];
            int startPos    = 0;

            for (int i = 0; i < messageParameters.Count; ++i)
            {
                bool extraProperty = true;
                for (int j = startPos; j < messagetTemplateParameters.Count; ++j)
                {
                    if (messageParameters[i].Name == messagetTemplateParameters[j].Name)
                    {
                        extraProperty  = false;
                        paramsArray[j] = messageParameters[i].Value;
                        if (startPos == j)
                        {
                            startPos++;
                        }
                        break;
                    }
                }

                if (extraProperty)
                {
                    extraProperties = AddExtraProperty(extraProperties, messageParameters[i]);
                }
            }

            return(paramsArray);
        }
 private static void SetEventInfoParameters(LogEventInfo eventInfo, NLogMessageParameterList messageTemplateParameters)
 {
     eventInfo.Parameters = new object[messageTemplateParameters.Count + 1];
     for (int i = 0; i < messageTemplateParameters.Count; ++i)
     {
         eventInfo.Parameters[i] = messageTemplateParameters[i].Value;
     }
 }
        private LogEventInfo CreateLogEventInfo(LogLevel nLogLogLevel, string message, IReadOnlyList <KeyValuePair <string, object> > parameterList)
        {
            if (parameterList != null && parameterList.Count > 1)
            {
                // More than a single parameter (last parameter is the {OriginalFormat})
                var firstParameterName = parameterList[0].Key;
                if (!string.IsNullOrEmpty(firstParameterName))
                {
                    if (firstParameterName.Length != 1 || !char.IsDigit(firstParameterName[0]))
                    {
#if NETSTANDARD2_0
                        var    originalFormat  = parameterList[parameterList.Count - 1];
                        string originalMessage = null;
                        if (originalFormat.Key == OriginalFormatPropertyName)
                        {
                            // Attempt to capture original message with placeholders
                            originalMessage = originalFormat.Value as string;
                        }

                        var messageTemplateParameters = new NLogMessageParameterList(parameterList, originalMessage != null);
                        var eventInfo = new LogEventInfo(nLogLogLevel, _logger.Name, originalMessage ?? message, messageTemplateParameters);
                        if (originalMessage != null)
                        {
                            eventInfo.Parameters = new object[messageTemplateParameters.Count + 1];
                            for (int i = 0; i < messageTemplateParameters.Count; ++i)
                            {
                                eventInfo.Parameters[i] = messageTemplateParameters[i].Value;
                            }
                            eventInfo.Parameters[messageTemplateParameters.Count] = message;
                            eventInfo.MessageFormatter = (l) => (string)l.Parameters[l.Parameters.Length - 1];
                        }
                        return(eventInfo);
#else
                        var eventInfo = LogEventInfo.Create(nLogLogLevel, _logger.Name, message);
                        for (int i = 0; i < parameterList.Count; ++i)
                        {
                            var parameter = parameterList[i];
                            if (string.IsNullOrEmpty(parameter.Key))
                            {
                                break;  // Skip capture of invalid parameters
                            }
                            var parameterName = parameter.Key;
                            switch (parameterName[0])
                            {
                            case '@': parameterName = parameterName.Substring(1); break;

                            case '$': parameterName = parameterName.Substring(1); break;
                            }
                            eventInfo.Properties[parameterName] = parameter.Value;
                        }
                        return(eventInfo);
#endif
                    }
                }
            }
            return(LogEventInfo.Create(nLogLogLevel, _logger.Name, message));
        }
        private static void SetLogEventMessageFormatter(LogEventInfo logEvent, NLogMessageParameterList messageTemplateParameters, string formattedMessage)
        {
            var parameters = new object[messageTemplateParameters.Count + 1];

            for (int i = 0; i < parameters.Length - 1; ++i)
            {
                parameters[i] = messageTemplateParameters[i].Value;
            }
            parameters[parameters.Length - 1] = formattedMessage;
            logEvent.Parameters       = parameters;
            logEvent.MessageFormatter = (l) => (string)l.Parameters[l.Parameters.Length - 1];
        }
        /// <summary>
        /// Create Log Event with multiple parameters (last parameter is the {OriginalFormat})
        /// </summary>
        private LogEventInfo CreateLogEventInfoWithMultipleParameters(LogLevel nLogLogLevel, string message, IReadOnlyList <KeyValuePair <string, object> > parameterList)
        {
            var messageTemplateParameters = new NLogMessageParameterList(parameterList);
            var originalMessage           = messageTemplateParameters.OriginalMessage as string;
            var logEvent = new LogEventInfo(nLogLogLevel, _logger.Name, originalMessage ?? message, messageTemplateParameters);

            if (originalMessage != null)
            {
                SetLogEventMessageFormatter(logEvent, messageTemplateParameters, message);
            }
            return(logEvent);
        }
        /// <summary>
        /// Are all parameters positional and correctly mapped?
        /// </summary>
        /// <param name="messageParameters"></param>
        /// <param name="messageTemplateParameters"></param>
        /// <returns>true if correct</returns>
        private static bool AllParameterCorrectlyPositionalMapped(NLogMessageParameterList messageParameters, MessageTemplateParameters messageTemplateParameters)
        {
            if (messageTemplateParameters.Count != messageParameters.Count)
            {
                return(false);
            }

            for (int i = 0; i < messageTemplateParameters.Count; ++i)
            {
                if (!messageParameters[i].Name.Equals(messageTemplateParameters[i].Name, StringComparison.Ordinal))
                {
                    return(false);
                }
            }

            return(true);
        }
Beispiel #13
0
        /// <summary>
        /// Are all parameters positional and correctly mapped?
        /// </summary>
        /// <param name="messageParameters"></param>
        /// <param name="messagetTemplateParameters"></param>
        /// <returns>true if correct</returns>
        private static bool AllParameterCorrectlyPositionalMapped(NLogMessageParameterList messageParameters, MessageTemplateParameters messagetTemplateParameters)
        {
            if (messagetTemplateParameters.Count != messageParameters.Count || messagetTemplateParameters.IsPositional)
            {
                return(false);
            }

            for (int i = 0; i < messagetTemplateParameters.Count; ++i)
            {
                if (messagetTemplateParameters[i].Name != messageParameters[i].Name)
                {
                    return(false);
                }
            }

            return(true);
        }
 private LogEventInfo CreateLogEventInfo <TState>(LogLevel nLogLogLevel, EventId eventId, TState state, Exception exception, Func <TState, Exception, string> formatter)
 {
     if (_options.CaptureMessageProperties && state is IReadOnlyList <KeyValuePair <string, object> > messagePropertyList)
     {
         if (_options.CaptureMessageTemplates)
         {
             var messageParameters = NLogMessageParameterList.TryParse(messagePropertyList);
             if (messageParameters.Count == 0)
             {
                 var logEvent = LogEventInfo.Create(nLogLogLevel, _logger.Name, formatter(state, exception));
                 CaptureEventIdProperties(logEvent, eventId);
                 return(logEvent);
             }
             else
             {
                 var logEvent = TryParseMessageTemplate(nLogLogLevel, messagePropertyList, messageParameters)
                                ?? CaptureMessageTemplate(nLogLogLevel, formatter(state, exception), messagePropertyList, messageParameters);
                 CaptureEventIdProperties(logEvent, eventId);
                 return(logEvent);
             }
         }
         else
         {
             var logEvent = LogEventInfo.Create(nLogLogLevel, _logger.Name, formatter(state, exception));
             CaptureMessagePropertiesList(logEvent, messagePropertyList);
             CaptureEventIdProperties(logEvent, eventId);
             return(logEvent);
         }
     }
     else
     {
         var logEvent = LogEventInfo.Create(nLogLogLevel, _logger.Name, formatter(state, exception));
         if (_options.CaptureMessageProperties)
         {
             if (state is IEnumerable <KeyValuePair <string, object> > messageProperties)
             {
                 CaptureMessageProperties(logEvent, messageProperties);
             }
             CaptureEventIdProperties(logEvent, eventId);
         }
         return(logEvent);
     }
 }
        /// <summary>
        /// Create Log Event with multiple parameters (last parameter is the {OriginalFormat})
        /// </summary>
        private LogEventInfo CreateLogEventInfoWithMultipleParameters(LogLevel nLogLogLevel, string message, IReadOnlyList <KeyValuePair <string, object> > parameterList)
        {
            var    originalFormat  = parameterList[parameterList.Count - 1];
            string originalMessage = null;

            if (originalFormat.Key == OriginalFormatPropertyName)
            {
                // Attempt to capture original message with placeholders
                originalMessage = originalFormat.Value as string;
            }

            var messageTemplateParameters = new NLogMessageParameterList(parameterList, originalMessage != null);
            var eventInfo = new LogEventInfo(nLogLogLevel, _logger.Name, originalMessage ?? message, messageTemplateParameters);

            if (originalMessage != null)
            {
                SetEventInfoParameters(eventInfo, messageTemplateParameters);
                eventInfo.Parameters[messageTemplateParameters.Count] = message;
                eventInfo.MessageFormatter = (l) => (string)l.Parameters[l.Parameters.Length - 1];
            }
            return(eventInfo);
        }
Beispiel #16
0
 private LogEventInfo CreateLogEventInfo(LogLevel nLogLogLevel, string message, NLogMessageParameterList messageParameters)
 {
     if (messageParameters?.HasComplexParameters == false)
     {
         // Parsing not needed, we take the fast route
         var originalMessage = messageParameters.OriginalMessage as string;
         var eventInfo       = new LogEventInfo(nLogLogLevel, _logger.Name, originalMessage ?? message, messageParameters.IsPositional ? _emptyParameterArray : messageParameters);
         if (originalMessage != null)
         {
             SetLogEventMessageFormatter(eventInfo, messageParameters, message);
         }
         return(eventInfo);
     }
     else
     {
         // Parsing failed or no messageParameters
         var eventInfo = LogEventInfo.Create(nLogLogLevel, _logger.Name, message);
         if (messageParameters?.Count > 0)
         {
             for (int i = 0; i < messageParameters.Count; ++i)
             {
                 var property = messageParameters[i];
                 eventInfo.Properties[property.Name] = property.Value;
             }
         }
         return(eventInfo);
     }
 }
Beispiel #17
0
 /// <summary>
 /// Convert IReadOnlyList to <see cref="NLogMessageParameterList"/>
 /// </summary>
 /// <param name="messageProperties"></param>
 /// <returns></returns>
 private NLogMessageParameterList TryParseMessageParameterList(IReadOnlyList <KeyValuePair <string, object> > messageProperties)
 {
     return((messageProperties != null && _options.CaptureMessageTemplates)
         ? NLogMessageParameterList.TryParse(messageProperties)
         : null);
 }
Beispiel #18
0
 private LogEventInfo TryCaptureMessageTemplate(LogLevel nLogLogLevel, string message, IReadOnlyList <KeyValuePair <string, object> > messageProperties, NLogMessageParameterList messageParameters)
 {
     if (messageParameters?.HasComplexParameters == false)
     {
         // Parsing not needed, we take the fast route
         var originalMessage = messageParameters.GetOriginalMessage(messageProperties);
         var eventInfo       = new LogEventInfo(nLogLogLevel, _logger.Name, originalMessage ?? message, messageParameters.IsPositional ? _emptyParameterArray : messageParameters);
         if (originalMessage != null)
         {
             SetLogEventMessageFormatter(eventInfo, messageParameters, message);
         }
         return(eventInfo);
     }
     return(null);
 }
        private LogEventInfo CaptureMessageTemplate(LogLevel nLogLogLevel, string message, IReadOnlyList <KeyValuePair <string, object> > messageProperties, NLogMessageParameterList messageParameters)
        {
            // Parsing not needed, we take the fast route
            var originalMessage = messageParameters.GetOriginalMessage(messageProperties) ?? message;
            var logEvent        = new LogEventInfo(nLogLogLevel, _logger.Name, originalMessage, messageParameters.IsPositional ? EmptyParameterArray : messageParameters);

            if (!ReferenceEquals(originalMessage, message))
            {
                SetLogEventMessageFormatter(logEvent, messageParameters, message);
            }
            return(logEvent);
        }
        /// <summary>
        /// Checks if the already parsed input message-parameters must be sent through
        /// the NLog MessageTemplate Parser for proper handling of message-template syntax (Ex. @)
        /// </summary>
        /// <remarks>
        /// Using the NLog MessageTemplate Parser will hurt performance: 1 x Microsoft Parser - 2 x NLog Parser - 1 x NLog Formatter
        /// </remarks>
        private LogEventInfo TryParseMessageTemplate(LogLevel nLogLogLevel, IReadOnlyList <KeyValuePair <string, object> > messageProperties, NLogMessageParameterList messageParameters)
        {
            if (messageParameters?.HasMessageTemplateSyntax(_options.ParseMessageTemplates) == true)
            {
                var originalMessage           = messageParameters.GetOriginalMessage(messageProperties);
                var logEvent                  = new LogEventInfo(nLogLogLevel, _logger.Name, null, originalMessage, SingleItemArray);
                var messageTemplateParameters = logEvent.MessageTemplateParameters;   // Forces parsing of OriginalMessage
                if (messageTemplateParameters.Count > 0)
                {
                    // We have parsed the message and found parameters, now we need to do the parameter mapping
                    CaptureLogEventInfoParameters(logEvent, messageParameters, messageTemplateParameters);
                    return(logEvent);
                }

                return(null);    // Parsing not possible
            }

            return(null);    // Parsing not needed
        }
Beispiel #21
0
        /// <summary>
        /// Checks if the already parsed input message-parameters must be sent through
        /// the NLog MessageTemplate Parser for proper handling of message-template syntax.
        /// </summary>
        /// <remarks>
        /// Using the NLog MesageTemplate Parser will hurt performance: 1 x Microsoft Parser - 2 x NLog Parser - 1 x NLog Formatter
        /// </remarks>
        private LogEventInfo TryParseMessageTemplate(LogLevel nLogLogLevel, IReadOnlyList <KeyValuePair <string, object> > messageProperties, out NLogMessageParameterList messageParameters)
        {
            messageParameters = TryParseMessageParameterList(messageProperties);

            if (messageParameters?.HasMessageTemplateSyntax(_options.ParseMessageTemplates) == true)
            {
                var originalMessage            = messageParameters.GetOriginalMessage(messageProperties);
                var eventInfo                  = new LogEventInfo(nLogLogLevel, _logger.Name, null, originalMessage, _singleItemArray);
                var messagetTemplateParameters = eventInfo.MessageTemplateParameters;   // Forces parsing of OriginalMessage
                if (messagetTemplateParameters.Count > 0)
                {
                    // We have parsed the message and found parameters, now we need to do the parameter mapping
                    eventInfo.Parameters = CreateLogEventInfoParameters(messageParameters, messagetTemplateParameters, out var extraProperties);
                    AddExtraPropertiesToLogEvent(eventInfo, extraProperties);
                    return(eventInfo);
                }

                return(null);    // Parsing not possible
            }

            return(null);    // Parsing not needed
        }
Beispiel #22
0
 private LogEventInfo CreateLogEventInfo(LogLevel nLogLogLevel, string formattedMessage, IReadOnlyList <KeyValuePair <string, object> > messageProperties, NLogMessageParameterList messageParameters)
 {
     return(TryCaptureMessageTemplate(nLogLogLevel, formattedMessage, messageProperties, messageParameters) ??
            CreateSimpleLogEventInfo(nLogLogLevel, formattedMessage, messageProperties, messageParameters));
 }
Beispiel #23
0
        private LogEventInfo CreateSimpleLogEventInfo(LogLevel nLogLogLevel, string message, IReadOnlyList <KeyValuePair <string, object> > messageProperties, NLogMessageParameterList messageParameters)
        {
            // Parsing failed or no messageParameters
            var eventInfo = LogEventInfo.Create(nLogLogLevel, _logger.Name, message);

            if (messageParameters != null)
            {
                for (int i = 0; i < messageParameters.Count; ++i)
                {
                    var property = messageParameters[i];
                    eventInfo.Properties[property.Name] = property.Value;
                }
            }
            else if (messageProperties != null && _options.CaptureMessageProperties)
            {
                CaptureMessagePropertiesList(eventInfo, messageProperties);
            }
            return(eventInfo);
        }