/// <summary> /// Pops a log info instance from the stack, prepares the text line, and writes the line to the text file. /// </summary> void WriteLogLine() { LogEntry Info = null; if (InfoQueue.TryDequeue(out Info)) { string S; StringBuilder SB = new StringBuilder(); SB.Append(Pad(Info.TimeStampUtc.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss.ff"), Lengths["Time"])); SB.Append(Pad(Info.HostName, Lengths["Host"])); SB.Append(Pad(Info.UserName, Lengths["User"])); SB.Append(Pad(Info.Level.ToString(), Lengths["Level"])); SB.Append(Pad(Info.EventId != null ? Info.EventId.ToString() : "", Lengths["EventId"])); SB.Append(Pad(Info.Category, Lengths["Category"])); S = ""; if (Info.Scopes != null && Info.Scopes.Count > 0) { LogScopeInfo SI = Info.Scopes.Last(); if (!string.IsNullOrWhiteSpace(SI.Text)) { S = SI.Text; } else { } } SB.Append(Pad(S, Lengths["Scope"])); string Text = Info.Text; /* writing properties is too much for a text file logger * if (Info.StateProperties != null && Info.StateProperties.Count > 0) * { * Text = Text + " Properties = " + Newtonsoft.Json.JsonConvert.SerializeObject(Info.StateProperties); * } */ if (!string.IsNullOrWhiteSpace(Text)) { SB.Append(Text.Replace("\r\n", " ").Replace("\r", " ").Replace("\n", " ")); } SB.AppendLine(); WriteLine(SB.ToString()); } }
/// <summary> /// Creates a log entry, actually a log info instance, fill the properties of that log info, and then passes it to the associated logger provider. /// <para>WARNING: It's easier to use the ILogger extension methods than this method, since it requires a lot of parameters, so calling it could be a very complicated action.</para> /// </summary> void ILogger.Log <TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func <TState, Exception, string> formatter) { if ((this as ILogger).IsEnabled(logLevel)) { LogEntry Info = new LogEntry(); Info.Category = this.Category; Info.Level = logLevel; // well, the passed default formatter function does not takes the exception into account // SEE: https://github.com/aspnet/Extensions/blob/master/src/Logging/Logging.Abstractions/src/LoggerExtensions.cs Info.Text = exception?.Message ?? state.ToString(); // formatter(state, exception) Info.Exception = exception; Info.EventId = eventId; Info.State = state; // well, you never know what it really is if (state is string) { Info.StateText = state.ToString(); } // in case we have to do with a message template, lets get the keys and values (for Structured Logging providers) // SEE: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging#log-message-template // SEE: https://softwareengineering.stackexchange.com/questions/312197/benefits-of-structured-logging-vs-basic-logging else if (state is IEnumerable <KeyValuePair <string, object> > Properties) { Info.StateProperties = new Dictionary <string, object>(); foreach (KeyValuePair <string, object> item in Properties) { Info.StateProperties[item.Key] = item.Value; } } // gather info about scope(s), if any if (Provider.ScopeProvider != null) { Provider.ScopeProvider.ForEachScope((value, loggingProps) => { if (Info.Scopes == null) { Info.Scopes = new List <LogScopeInfo>(); } LogScopeInfo Scope = new LogScopeInfo(); Info.Scopes.Add(Scope); if (value is string) { Scope.Text = value.ToString(); } else if (value is IEnumerable <KeyValuePair <string, object> > props) { if (Scope.Properties == null) { Scope.Properties = new Dictionary <string, object>(); } foreach (var pair in props) { Scope.Properties[pair.Key] = pair.Value; } } }, state); } Provider.WriteLog(Info); } }