Skip to content
This repository has been archived by the owner on Jun 1, 2024. It is now read-only.

Commit

Permalink
Added FormatStackTraceAsArray option to write StackTrace as an array (#…
Browse files Browse the repository at this point in the history
…230)

* Github releases added to yaml

* Create new release

* fix title of changelog (#211)

* Added FormatStackTraceAsArray option to write StackTrace as an array

* Added unit tests, minor code-style changes
  • Loading branch information
oscarmorasu authored and mivano committed Apr 8, 2019
1 parent f38b376 commit f9f8077
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 14 deletions.
7 changes: 5 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
== Changelog
7.0
## Changelog

7.1
* DurableElasticsearchSink is rewritten to use the same base code as the sink for Serilog.Sinks.Seq. Nuget Serilog.Sinks.File is now used instead of deprecated Serilog.Sinks.RollingFile. Lots of new fintuning options for file storage is added in ElasticsearchSinkOptions. Updated Serilog.Sinks.Elasticsearch.Sample.Main with SetupLoggerWithPersistantStorage with all available options for durable mode.
* Changed datatype on singleEventSizePostingLimit from int to long? with default value null. to make it possible ro reuse code from Sinks.Seq .
* IndexDecider didnt worked well in buffer mode because of LogEvent was null. Added BufferIndexDecider.
* Added BufferCleanPayload and an example which makes it possible to cleanup your invalid logging document if rejected from elastic because of inconsistent datatype on a field. It'seasy to miss errors in the self log now its possible to se logrows which is bad for elasticsearch in the elastic log.
* Added BufferRetainedInvalidPayloadsLimitBytes A soft limit for the number of bytes to use for storing failed requests.
* Added BufferFileCountLimit The maximum number of log files that will be retained.
* Formatting has been moved to seperate package.

6.4
* Render message by default (#160).
* Expose interface-typed options via appsettings (#162)
Expand Down
3 changes: 0 additions & 3 deletions GitVersion.yaml

This file was deleted.

2 changes: 1 addition & 1 deletion GitVersion.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
next-version: 6.4.0
next-version: 7.0.0
branches: {}
ignore:
sha: []
7 changes: 7 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ deploy:
secure: bd9z4P73oltOXudAjPehwp9iDKsPtC+HbgshOrSgoyQKr5xVK+bxJQngrDJkHdY8
on:
branch: /^(master|dev)$/
- provider: GitHub
auth_token:
secure: XSO0LDYd89yw5rAQ8HvAgdX7NBo1m4bEqHlj0NZxtA6zKunLwCSYoVHU+k3cvQIP
on:
branch: master
artifact: /Serilog.*\.nupkg/
tag: v$(appveyor_build_version)
install:
- choco install gitversion.portable -y
assembly_info:
Expand Down
13 changes: 13 additions & 0 deletions src/Serilog.Formatting.Elasticsearch/DefaultJsonFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,19 @@ protected virtual void WriteJsonProperty(string name, object value, ref string p
precedingDelimiter = ",";
}

/// <summary>
/// Writes out a json property with an array as the value
/// </summary>
protected virtual void WriteJsonArrayProperty(string name, IEnumerable sequence, ref string precedingDelimiter, TextWriter output)
{
output.Write(precedingDelimiter);
output.Write("\"");
output.Write(name);
output.Write("\":");
WriteSequence(sequence, output);
precedingDelimiter = ",";
}

/// <summary>
/// Allows a subclass to write out objects that have no configured literal writer.
/// </summary>
Expand Down
23 changes: 20 additions & 3 deletions src/Serilog.Formatting.Elasticsearch/ElasticsearchJsonFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class ElasticsearchJsonFormatter : DefaultJsonFormatter
{
readonly IElasticsearchSerializer _serializer;
readonly bool _inlineFields;
readonly bool _formatStackTraceAsArray;

/// <summary>
/// Render message property name
Expand Down Expand Up @@ -69,6 +70,7 @@ public class ElasticsearchJsonFormatter : DefaultJsonFormatter
/// <param name="serializer">Inject a serializer to force objects to be serialized over being ToString()</param>
/// <param name="inlineFields">When set to true values will be written at the root of the json document</param>
/// <param name="renderMessageTemplate">If true, the message template will be rendered and written to the output as a
/// <param name="formatStackTraceAsArray">If true, splits the StackTrace by new line and writes it as a an array of strings</param>
/// property named RenderedMessageTemplate.</param>
public ElasticsearchJsonFormatter(
bool omitEnclosingObject = false,
Expand All @@ -77,11 +79,13 @@ public ElasticsearchJsonFormatter(
IFormatProvider formatProvider = null,
IElasticsearchSerializer serializer = null,
bool inlineFields = false,
bool renderMessageTemplate = true)
bool renderMessageTemplate = true,
bool formatStackTraceAsArray = false)
: base(omitEnclosingObject, closingDelimiter, renderMessage, formatProvider, renderMessageTemplate)
{
_serializer = serializer;
_inlineFields = inlineFields;
_formatStackTraceAsArray = formatStackTraceAsArray;
}

/// <summary>
Expand Down Expand Up @@ -219,8 +223,16 @@ protected void WriteSingleException(Exception exception, ref string delim, TextW
this.WriteJsonProperty("ClassName", className, ref delim, output);
this.WriteJsonProperty("Message", exception.Message, ref delim, output);
this.WriteJsonProperty("Source", source, ref delim, output);
this.WriteJsonProperty("StackTraceString", stackTrace, ref delim, output);
this.WriteJsonProperty("RemoteStackTraceString", remoteStackTrace, ref delim, output);
if (_formatStackTraceAsArray)
{
this.WriteMultilineString("StackTrace", stackTrace, ref delim, output);
this.WriteMultilineString("RemoteStackTrace", stackTrace, ref delim, output);
}
else
{
this.WriteJsonProperty("StackTraceString", stackTrace, ref delim, output);
this.WriteJsonProperty("RemoteStackTraceString", remoteStackTrace, ref delim, output);
}
this.WriteJsonProperty("RemoteStackIndex", remoteStackIndex, ref delim, output);
this.WriteStructuredExceptionMethod(exceptionMethod, ref delim, output);
this.WriteJsonProperty("HResult", hresult, ref delim, output);
Expand All @@ -230,7 +242,12 @@ protected void WriteSingleException(Exception exception, ref string delim, TextW
//JsonNET assumes string, simplejson writes array of numerics.
//Skip for now
//this.WriteJsonProperty("WatsonBuckets", watsonBuckets, ref delim, output);
}

private void WriteMultilineString(string name, string value, ref string delimeter, TextWriter output)
{
string[] lines = value.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
WriteJsonArrayProperty(name, lines, ref delimeter, output);
}

private void WriteStructuredExceptionMethod(string exceptionMethodString, ref string delim, TextWriter output)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,15 @@ public class ExceptionAsObjectJsonFormatter : ElasticsearchJsonFormatter
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
/// <param name="serializer">Inject a serializer to force objects to be serialized over being ToString()</param>
/// <param name="inlineFields">When set to true values will be written at the root of the json document</param>
public ExceptionAsObjectJsonFormatter(bool omitEnclosingObject = false, string closingDelimiter = null, bool renderMessage = false, IFormatProvider formatProvider = null, IElasticsearchSerializer serializer = null, bool inlineFields = false) : base(omitEnclosingObject, closingDelimiter, renderMessage, formatProvider, serializer, inlineFields)
/// <param name="formatStackTraceAsArray">If true, splits the StackTrace by new line and writes it as a an array of strings</param>
public ExceptionAsObjectJsonFormatter(bool omitEnclosingObject = false,
string closingDelimiter = null,
bool renderMessage = false,
IFormatProvider formatProvider = null,
IElasticsearchSerializer serializer = null,
bool inlineFields = false,
bool formatStackTraceAsArray = false)
: base(omitEnclosingObject, closingDelimiter, renderMessage, formatProvider, serializer, inlineFields, formatStackTraceAsArray)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@ public int QueueSizeLimit
/// </summary>
public int? BufferFileCountLimit { get; set; }

/// <summary>
/// When set to true splits the StackTrace by new line and writes it as a an array of strings.
/// </summary>
public bool FormatStackTraceAsArray { get; set; }

/// <summary>
/// Configures the elasticsearch sink defaults
/// </summary>
Expand All @@ -265,6 +270,7 @@ public ElasticsearchSinkOptions()
this.QueueSizeLimit = 100000;
this.BufferFileCountLimit = 31;
this.BufferFileSizeLimitBytes = 100L * 1024 * 1024;
this.FormatStackTraceAsArray = false;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ public static ITextFormatter CreateDefaultFormatter(ElasticsearchSinkOptions opt
formatProvider: options.FormatProvider,
closingDelimiter: string.Empty,
serializer: options.Serializer,
inlineFields: options.InlineFields
inlineFields: options.InlineFields,
formatStackTraceAsArray: options.FormatStackTraceAsArray
);
}

Expand All @@ -104,7 +105,8 @@ public static ITextFormatter CreateDefaultDurableFormatter(ElasticsearchSinkOpti
formatProvider: options.FormatProvider,
closingDelimiter: Environment.NewLine,
serializer: options.Serializer,
inlineFields: options.InlineFields
inlineFields: options.InlineFields,
formatStackTraceAsArray: options.FormatStackTraceAsArray
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,49 @@ static LogEvent CreateLogEvent() =>
(
DateTimeOffset.Now,
LogEventLevel.Debug,
exception: null,
//exception: CreateThrownException(),
exception: null,
messageTemplate: new MessageTemplate(Enumerable.Empty<MessageTemplateToken>()),
properties: Enumerable.Empty<LogEventProperty>()
);

static Exception CreateThrownException()
{
Exception retEx = null;
try
{
ThrowException();
}
catch (Exception ex)
{
retEx = ex;
}
return retEx;
}

static void ThrowException()
{
try
{
ThrowInnerException();
}
catch(Exception ex)
{
throw new Exception("Test exception message", ex);
}
}

static void ThrowInnerException()
{
throw new Exception("Test inner exception message");
}

static LogEvent CreateLogEventWithException() =>
new LogEvent
(
DateTimeOffset.Now,
LogEventLevel.Debug,
exception: CreateThrownException(),
messageTemplate: new MessageTemplate(Enumerable.Empty<MessageTemplateToken>()),
properties: Enumerable.Empty<LogEventProperty>()
);
Expand Down Expand Up @@ -57,7 +99,7 @@ static void DoesNotContainsProperty(string propertyToCheck, string result) =>
StringComparison.CurrentCultureIgnoreCase
);

static string FormatProperty(string property) => $"\"{property}\":";
static string FormatProperty(string property) => $"\"{property}\":";
#endregion

[Theory]
Expand Down Expand Up @@ -134,5 +176,61 @@ public void When_provide_closing_delimiter_should_use_it()
Assert.EndsWith("closingDelimiter", result);
});
}

[Fact]
public void DefaultJsonFormater_Should_Render_Exceptions()
{
CheckProperties(
CreateLogEventWithException,
new ElasticsearchJsonFormatter(),
result =>
{
string exceptionsProperty = FormatProperty("exceptions");
ContainsProperty(exceptionsProperty, result);
string exceptionsValue = result.Substring(result.IndexOf(exceptionsProperty) + exceptionsProperty.Length);
// Check the exceptions property is a JSON array
Assert.StartsWith("[", exceptionsValue);
string stackTraceProperty = FormatProperty("StackTraceString");
ContainsProperty(stackTraceProperty, exceptionsValue);
DoesNotContainsProperty(FormatProperty("StackTrace"), exceptionsValue);
string stackTraceValue = exceptionsValue.Substring(exceptionsValue.IndexOf(stackTraceProperty) + stackTraceProperty.Length);
// Check the StackTraceString property is a JSON string
Assert.StartsWith("\"", stackTraceValue);
});
}

[Fact]
public void DefaultJsonFormater_Should_Render_Exceptions_With_StackTrace_As_Array()
{
CheckProperties(
CreateLogEventWithException,
new ElasticsearchJsonFormatter(formatStackTraceAsArray: true),
result =>
{
string exceptionsProperty = FormatProperty("exceptions");
ContainsProperty(exceptionsProperty, result);
string exceptionsValue = result.Substring(result.IndexOf(exceptionsProperty) + exceptionsProperty.Length);
// Check the exceptions property is a JSON array
Assert.StartsWith("[", exceptionsValue);
string stackTraceProperty = FormatProperty("StackTrace");
ContainsProperty(stackTraceProperty, exceptionsValue);
DoesNotContainsProperty(FormatProperty("StackTraceString"), exceptionsValue);
string stackTraceValue = exceptionsValue.Substring(exceptionsValue.IndexOf(stackTraceProperty) + stackTraceProperty.Length);
// Check the StackTrace property is a JSON array
Assert.StartsWith("[", stackTraceValue);
});
}
}
}

0 comments on commit f9f8077

Please sign in to comment.