public void Format(IEnumerable <LogEvent> logEvents, ITextFormatter formatter, TextWriter output) { if (logEvents == null) { throw new ArgumentNullException(nameof(logEvents)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } List <LogEvent> logs = logEvents.ToList(); if (!logs.Any()) { return; } var streamsDictionary = new Dictionary <string, LokiContentStream>(); foreach (LogEvent logEvent in logs) { var labels = new List <LokiLabel>(); foreach (LokiLabel globalLabel in this.LogLabelProvider.GetLabels()) { labels.Add(new LokiLabel(globalLabel.Key, globalLabel.Value)); } var time = logEvent.Timestamp.ToString("o"); var sb = new StringBuilder(); using (var tw = new StringWriter(sb)) { formatter.Format(logEvent, tw); } HandleProperty("level", GetLevel(logEvent.Level), labels, sb); foreach (KeyValuePair <string, LogEventPropertyValue> property in logEvent.Properties) { HandleProperty(property.Key, property.Value.ToString(), labels, sb); } // Order the labels so they always get the same chunk in loki labels = labels.OrderBy(l => l.Key).ToList(); var key = string.Join(",", labels.Select(l => $"{l.Key}={l.Value}")); if (!streamsDictionary.TryGetValue(key, out var stream)) { streamsDictionary.Add(key, stream = new LokiContentStream()); stream.Labels.AddRange(labels); } // Loki doesn't like \r\n for new line, and we can't guarantee the message doesn't have any // in it, so we replace \r\n with \n on the final message stream.Entries.Add(new LokiEntry(time, sb.ToString().Replace("\r\n", "\n"))); } if (streamsDictionary.Count > 0) { var content = new LokiContent { Streams = streamsDictionary.Values.ToList() }; output.Write(content.Serialize()); } }
public void Format(IEnumerable <LogEvent> logEvents, ITextFormatter formatter, TextWriter output) { if (logEvents == null) { throw new ArgumentNullException(nameof(logEvents)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } List <LogEvent> logs = logEvents.ToList(); if (!logs.Any()) { return; } var content = new LokiContent(); foreach (LogEvent logEvent in logs) { var stream = new LokiContentStream(); content.Streams.Add(stream); stream.Labels.Add(new LokiLabel("level", GetLevel(logEvent.Level))); foreach (LokiLabel globalLabel in _globalLabels) { stream.Labels.Add(new LokiLabel(globalLabel.Key, globalLabel.Value)); } foreach (KeyValuePair <string, LogEventPropertyValue> property in logEvent.Properties) { // Some enrichers pass strings with quotes surrounding the values inside the string, // which results in redundant quotes after serialization and a "bad request" response. // To avoid this, remove all quotes from the value. // We also remove any \r\n newlines and replace with \n new lines to prevent "bad request" responses // We also remove backslashes and replace with forward slashes, Loki doesn't like those either stream.Labels.Add(new LokiLabel(property.Key, property.Value.ToString().Replace("\"", "").Replace("\r\n", "\n").Replace("\\", "/"))); } var time = logEvent.Timestamp.ToString("o"); var sb = new StringBuilder(); sb.AppendLine(logEvent.RenderMessage()); if (logEvent.Exception != null) { var e = logEvent.Exception; while (e != null) { sb.AppendLine(e.Message); sb.AppendLine(e.StackTrace); e = e.InnerException; } } // Loki doesn't like \r\n for new line, and we can't guarantee the message doesn't have any // in it, so we replace \r\n with \n on the final message // We also flip backslashes to forward slashes, Loki doesn't like those either. stream.Entries.Add(new LokiEntry(time, sb.ToString().Replace("\\", "/").Replace("\r\n", "\n"))); } if (content.Streams.Count > 0) { output.Write(content.Serialize()); } }
public void Format(IEnumerable <LogEvent> logEvents, ITextFormatter formatter, TextWriter output) { if (logEvents == null) { throw new ArgumentNullException(nameof(logEvents)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } List <LogEvent> logs = logEvents.ToList(); if (!logs.Any()) { return; } var content = new LokiContent(); foreach (LogEvent logEvent in logs) { var stream = new LokiContentStream(); content.Streams.Add(stream); stream.Labels.Add(new LokiLabel("level", GetLevel(logEvent.Level))); foreach (LokiLabel globalLabel in _globalLabels) { stream.Labels.Add(new LokiLabel(globalLabel.Key, globalLabel.Value)); } foreach (KeyValuePair <string, LogEventPropertyValue> property in logEvent.Properties) { // Some enrichers pass strings with quotes surrounding the values inside the string, // which results in redundant quotes after serialization and a "bad request" response. // To avoid this, remove all quotes from the value. stream.Labels.Add(new LokiLabel(property.Key, property.Value.ToString().Replace("\"", ""))); } var localTime = DateTime.Now; var localTimeAndOffset = new DateTimeOffset(localTime, TimeZoneInfo.Local.GetUtcOffset(localTime)); var time = localTimeAndOffset.ToString("o"); var sb = new StringBuilder(); sb.AppendLine(logEvent.RenderMessage()); if (logEvent.Exception != null) { var e = logEvent.Exception; while (e != null) { sb.AppendLine(e.Message); sb.AppendLine(e.StackTrace); e = e.InnerException; } } stream.Entries.Add(new LokiEntry(time, sb.ToString())); } if (content.Streams.Count > 0) { output.Write(content.Serialize()); } }