From 052dac71e874b74cc6f3c6c34bec60f929164b4a Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Mon, 5 Feb 2024 10:53:56 +0300 Subject: [PATCH 1/7] Add editorconfig+SourceLink;convert to file-scoped namespaces --- .editorconfig | 265 ++++++++++++++++++ Directory.Build.props | 39 +++ serilog-formatting-compact.sln | 5 + .../CompactJsonFormatter.cs | 192 +++++++------ src/Serilog.Formatting.Compact/EventIdHash.cs | 51 ++-- .../RenderedCompactJsonFormatter.cs | 161 ++++++----- .../Serilog.Formatting.Compact.csproj | 45 +-- .../Benchmarks.cs | 13 +- .../CompactJsonFormatterTests.cs | 142 +++++----- .../EventIdHashTests.cs | 31 +- .../FormattingBenchmarks.cs | 108 ++++--- .../FormattingBenchmarksConfig.cs | 11 +- .../RenderedCompactJsonFormatterTests.cs | 111 ++++---- .../Serilog.Formatting.Compact.Tests.csproj | 40 ++- .../Support/Assertions.cs | 35 ++- .../Support/CollectorSink.cs | 17 +- .../Support/TextWriterSink.cs | 30 +- 17 files changed, 774 insertions(+), 522 deletions(-) create mode 100644 .editorconfig create mode 100644 Directory.Build.props diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..778e1d6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,265 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# Create portable, custom editor settings with EditorConfig +# https://docs.microsoft.com/en-us/visualstudio/ide/create-portable-custom-editor-options + +# .NET coding convention settings for EditorConfig +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference?view=vs-2019 + +# Language conventions +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019 + +# Formatting conventions +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019 + +# .NET naming conventions for EditorConfig +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions?view=vs-2019 + +# Top-most EditorConfig file +root = true + +# Editor default newlines with a newline ending every file +[*] +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[*.json] +insert_final_newline = false + +[*.cs] +indent_size = 4 + +# Do not insert newline for ApiApprovalTests +[*.txt] +insert_final_newline = false + +# Code files +[*.{cs,vb}] + +# .NET code style settings - "This." and "Me." qualifiers +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#this-and-me +dotnet_style_qualification_for_field = false:warning +dotnet_style_qualification_for_property = false:warning +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_event = false:warning + +# .NET code style settings - Language keywords instead of framework type names for type references +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#language-keywords +dotnet_style_predefined_type_for_locals_parameters_members = true:error +dotnet_style_predefined_type_for_member_access = true:error + +# .NET code style settings - Modifier preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#normalize-modifiers +dotnet_style_require_accessibility_modifiers = always:warning +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning +dotnet_style_readonly_field = true:warning + +# .NET code style settings - Parentheses preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#parentheses-preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion + +# .NET code style settings - Expression-level preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#expression-level-preferences +dotnet_style_object_initializer = true:error +dotnet_style_collection_initializer = true:error +dotnet_style_explicit_tuple_names = true:warning +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:warning +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_compound_assignment = true:warning + +# .NET code style settings - Null-checking preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#null-checking-preferences +dotnet_style_coalesce_expression = true:warning +dotnet_style_null_propagation = true:error + +# .NET code quality settings - Parameter preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#parameter-preferences +dotnet_code_quality_unused_parameters = all:warning + +# C# code style settings - Implicit and explicit types +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#implicit-and-explicit-types +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = true:warning +csharp_style_var_elsewhere = true:suggestion + +# C# code style settings - Expression-bodied members +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#expression-bodied-members +csharp_style_expression_bodied_methods = when_on_single_line:suggestion +csharp_style_expression_bodied_constructors = false:warning +csharp_style_expression_bodied_operators = when_on_single_line:warning +csharp_style_expression_bodied_properties = when_on_single_line:warning +csharp_style_expression_bodied_indexers = when_on_single_line:warning +csharp_style_expression_bodied_accessors = when_on_single_line:warning +csharp_style_expression_bodied_lambdas = when_on_single_line:warning +csharp_style_expression_bodied_local_functions = when_on_single_line:warning + +# C# code style settings - Pattern matching +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#pattern-matching +csharp_style_pattern_matching_over_is_with_cast_check = true:error +csharp_style_pattern_matching_over_as_with_null_check = true:error + +# C# code style settings - Inlined variable declaration +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#inlined-variable-declarations +csharp_style_inlined_variable_declaration = true:error + +# C# code style settings - C# expression-level preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#c-expression-level-preferences +csharp_prefer_simple_default_expression = true:suggestion + +# C# code style settings - C# null-checking preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#c-null-checking-preferences +csharp_style_throw_expression = true:warning +csharp_style_conditional_delegate_call = true:warning + +# C# code style settings - Code block preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#code-block-preferences +csharp_prefer_braces = when_multiline:suggestion + +# C# code style - Unused value preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#unused-value-preferences +csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion + +# C# code style - Index and range preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#index-and-range-preferences +csharp_style_prefer_index_operator = true:warning +csharp_style_prefer_range_operator = true:warning + +# C# code style - Miscellaneous preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#miscellaneous-preferences +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_using_directive_placement = outside_namespace:warning +csharp_prefer_static_local_function = true:suggestion +csharp_prefer_simple_using_statement = false:suggestion +csharp_style_prefer_switch_expression = true:suggestion + +# .NET formatting settings - Organize using directives +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019#organize-using-directives +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# C# formatting settings - New-line options +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019#new-line-options +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# C# formatting settings - Indentation options +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019#indentation-options +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents_when_block = false + +# C# formatting settings - Spacing options +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_after_comma = true +csharp_space_before_comma = false +csharp_space_after_dot = false +csharp_space_before_dot = false +csharp_space_after_semicolon_in_for_statement = true +csharp_space_before_semicolon_in_for_statement = false +csharp_space_around_declaration_statements = false +csharp_space_before_open_square_brackets = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_square_brackets = false + +# C# formatting settings - Wrap options +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019#wrap-options +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false + +# C# formatting settings - Namespace options +csharp_style_namespace_declarations = file_scoped:warning + +########## name all private fields using camelCase with underscore prefix ########## +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions?view=vs-2019 +# dotnet_naming_rule..symbols = +dotnet_naming_rule.private_fields_with_underscore.symbols = private_fields + +# dotnet_naming_symbols.. = +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private + +# dotnet_naming_rule..style = +dotnet_naming_rule.private_fields_with_underscore.style = prefix_underscore + +# dotnet_naming_style.. = +dotnet_naming_style.prefix_underscore.capitalization = camel_case +dotnet_naming_style.prefix_underscore.required_prefix = _ + +# dotnet_naming_rule..severity = +dotnet_naming_rule.private_fields_with_underscore.severity = warning + +########## name all constant fields using UPPER_CASE ########## +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions?view=vs-2019 +# dotnet_naming_rule..symbols = +dotnet_naming_rule.constant_fields_should_be_upper_case.symbols = constant_fields + +# dotnet_naming_symbols.. = +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const + +# dotnet_naming_rule..style = +dotnet_naming_rule.constant_fields_should_be_upper_case.style = upper_case_style + +# dotnet_naming_style.. = +dotnet_naming_style.upper_case_style.capitalization = all_upper +dotnet_naming_style.upper_case_style.word_separator = _ + +# dotnet_naming_rule..severity = +dotnet_naming_rule.constant_fields_should_be_upper_case.severity = warning + +########## Async methods should have "Async" suffix ########## +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions?view=vs-2019 +# dotnet_naming_rule..symbols = +dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods + +# dotnet_naming_symbols.. = +dotnet_naming_symbols.any_async_methods.applicable_kinds = method +dotnet_naming_symbols.any_async_methods.applicable_accessibilities = * +dotnet_naming_symbols.any_async_methods.required_modifiers = async + +# dotnet_naming_rule..style = +dotnet_naming_rule.async_methods_end_in_async.style = end_in_async_style + +# dotnet_naming_style.. = +dotnet_naming_style.end_in_async_style.capitalization = pascal_case +dotnet_naming_style.end_in_async_style.word_separator = +dotnet_naming_style.end_in_async_style.required_prefix = +dotnet_naming_style.end_in_async_style.required_suffix = Async + +# dotnet_naming_rule..severity = +dotnet_naming_rule.async_methods_end_in_async.severity = warning + +# Remove unnecessary import https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0005 +dotnet_diagnostic.IDE0005.severity = warning diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..aadf161 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,39 @@ + + + + 2.0.1 + latest + true + true + true + Serilog Contributors + $([System.DateTime]::Now.ToString(yyyy)) + Copyright © Serilog Contributors 2017-$(CurrentYear) + Apache-2.0 + false + serilog-extension-nuget.png + git + embedded + true + true + + True + serilog;json + enable + README.md + true + enable + true + true + ../../assets/Serilog.snk + true + 6.0-recommended + + + + + + + + + diff --git a/serilog-formatting-compact.sln b/serilog-formatting-compact.sln index 336cab0..d6bc396 100644 --- a/serilog-formatting-compact.sln +++ b/serilog-formatting-compact.sln @@ -7,9 +7,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{037440DE-440 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{E9D1B5E1-DEB9-4A04-8BAB-24EC7240ADAF}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig appveyor.yml = appveyor.yml Build.ps1 = Build.ps1 CHANGES.md = CHANGES.md + Directory.Build.props = Directory.Build.props LICENSE = LICENSE README.md = README.md assets\Serilog.snk = assets\Serilog.snk @@ -44,4 +46,7 @@ Global {E0BB862A-8B2C-46B5-9C90-B2F94C32EAB3} = {037440DE-440B-4129-9F7A-09B42D00397E} {BAEECC42-EC0C-4955-8AA1-BD3F4F0F4AAD} = {67B1C971-75EE-4ABE-B184-66AAC8D9D572} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AA2A0C20-8F2A-4EAE-ADD4-2CAFD022FB23} + EndGlobalSection EndGlobal diff --git a/src/Serilog.Formatting.Compact/CompactJsonFormatter.cs b/src/Serilog.Formatting.Compact/CompactJsonFormatter.cs index 992740c..8067c5e 100644 --- a/src/Serilog.Formatting.Compact/CompactJsonFormatter.cs +++ b/src/Serilog.Formatting.Compact/CompactJsonFormatter.cs @@ -1,4 +1,4 @@ -// Copyright 2016 Serilog Contributors +// Copyright 2016 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,126 +12,122 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System; using System.Globalization; -using System.IO; -using System.Linq; using Serilog.Events; using Serilog.Formatting.Json; using Serilog.Parsing; // ReSharper disable MemberCanBePrivate.Global -namespace Serilog.Formatting.Compact +namespace Serilog.Formatting.Compact; + +/// +/// An that writes events in a compact JSON format. +/// +public class CompactJsonFormatter: ITextFormatter { + private readonly JsonValueFormatter _valueFormatter; + /// - /// An that writes events in a compact JSON format. + /// Construct a , optionally supplying a formatter for + /// s on the event. /// - public class CompactJsonFormatter: ITextFormatter + /// A value formatter, or null. + public CompactJsonFormatter(JsonValueFormatter? valueFormatter = null) { - readonly JsonValueFormatter _valueFormatter; - - /// - /// Construct a , optionally supplying a formatter for - /// s on the event. - /// - /// A value formatter, or null. - public CompactJsonFormatter(JsonValueFormatter valueFormatter = null) - { - _valueFormatter = valueFormatter ?? new JsonValueFormatter(typeTagName: "$type"); - } + _valueFormatter = valueFormatter ?? new JsonValueFormatter(typeTagName: "$type"); + } - /// - /// Format the log event into the output. Subsequent events will be newline-delimited. - /// - /// The event to format. - /// The output. - public void Format(LogEvent logEvent, TextWriter output) - { - FormatEvent(logEvent, output, _valueFormatter); - output.WriteLine(); - } + /// + /// Format the log event into the output. Subsequent events will be newline-delimited. + /// + /// The event to format. + /// The output. + public void Format(LogEvent logEvent, TextWriter output) + { + FormatEvent(logEvent, output, _valueFormatter); + output.WriteLine(); + } - /// - /// Format the log event into the output. - /// - /// The event to format. - /// The output. - /// A value formatter for s on the event. - public static void FormatEvent(LogEvent logEvent, TextWriter output, JsonValueFormatter valueFormatter) + /// + /// Format the log event into the output. + /// + /// The event to format. + /// The output. + /// A value formatter for s on the event. + public static void FormatEvent(LogEvent logEvent, TextWriter output, JsonValueFormatter valueFormatter) + { + if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); + if (output == null) throw new ArgumentNullException(nameof(output)); + if (valueFormatter == null) throw new ArgumentNullException(nameof(valueFormatter)); + + output.Write("{\"@t\":\""); + output.Write(logEvent.Timestamp.UtcDateTime.ToString("O")); + output.Write("\",\"@mt\":"); + JsonValueFormatter.WriteQuotedJsonString(logEvent.MessageTemplate.Text, output); + + var tokensWithFormat = logEvent.MessageTemplate.Tokens + .OfType() + .Where(pt => pt.Format != null); + + // Better not to allocate an array in the 99.9% of cases where this is false + // ReSharper disable once PossibleMultipleEnumeration + if (tokensWithFormat.Any()) { - if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); - if (output == null) throw new ArgumentNullException(nameof(output)); - if (valueFormatter == null) throw new ArgumentNullException(nameof(valueFormatter)); - - output.Write("{\"@t\":\""); - output.Write(logEvent.Timestamp.UtcDateTime.ToString("O")); - output.Write("\",\"@mt\":"); - JsonValueFormatter.WriteQuotedJsonString(logEvent.MessageTemplate.Text, output); - - var tokensWithFormat = logEvent.MessageTemplate.Tokens - .OfType() - .Where(pt => pt.Format != null); - - // Better not to allocate an array in the 99.9% of cases where this is false - // ReSharper disable once PossibleMultipleEnumeration - if (tokensWithFormat.Any()) + output.Write(",\"@r\":["); + var delim = ""; + foreach (PropertyToken r in tokensWithFormat) { - output.Write(",\"@r\":["); - var delim = ""; - foreach (PropertyToken r in tokensWithFormat) - { - output.Write(delim); - delim = ","; - var space = new StringWriter(); - r.Render(logEvent.Properties, space, CultureInfo.InvariantCulture); - JsonValueFormatter.WriteQuotedJsonString(space.ToString(), output); - } - output.Write(']'); + output.Write(delim); + delim = ","; + var space = new StringWriter(); + r.Render(logEvent.Properties, space, CultureInfo.InvariantCulture); + JsonValueFormatter.WriteQuotedJsonString(space.ToString(), output); } + output.Write(']'); + } - if (logEvent.Level != LogEventLevel.Information) - { - output.Write(",\"@l\":\""); - output.Write(logEvent.Level); - output.Write('\"'); - } + if (logEvent.Level != LogEventLevel.Information) + { + output.Write(",\"@l\":\""); + output.Write(logEvent.Level); + output.Write('\"'); + } - if (logEvent.Exception != null) - { - output.Write(",\"@x\":"); - JsonValueFormatter.WriteQuotedJsonString(logEvent.Exception.ToString(), output); - } + if (logEvent.Exception != null) + { + output.Write(",\"@x\":"); + JsonValueFormatter.WriteQuotedJsonString(logEvent.Exception.ToString(), output); + } - if (logEvent.TraceId != null) - { - output.Write(",\"@tr\":\""); - output.Write(logEvent.TraceId.Value.ToHexString()); - output.Write('\"'); - } + if (logEvent.TraceId != null) + { + output.Write(",\"@tr\":\""); + output.Write(logEvent.TraceId.Value.ToHexString()); + output.Write('\"'); + } - if (logEvent.SpanId != null) - { - output.Write(",\"@sp\":\""); - output.Write(logEvent.SpanId.Value.ToHexString()); - output.Write('\"'); - } + if (logEvent.SpanId != null) + { + output.Write(",\"@sp\":\""); + output.Write(logEvent.SpanId.Value.ToHexString()); + output.Write('\"'); + } - foreach (var property in logEvent.Properties) + foreach (var property in logEvent.Properties) + { + var name = property.Key; + if (name.Length > 0 && name[0] == '@') { - var name = property.Key; - if (name.Length > 0 && name[0] == '@') - { - // Escape first '@' by doubling - name = '@' + name; - } - - output.Write(','); - JsonValueFormatter.WriteQuotedJsonString(name, output); - output.Write(':'); - valueFormatter.Format(property.Value, output); + // Escape first '@' by doubling + name = '@' + name; } - output.Write('}'); + output.Write(','); + JsonValueFormatter.WriteQuotedJsonString(name, output); + output.Write(':'); + valueFormatter.Format(property.Value, output); } + + output.Write('}'); } } diff --git a/src/Serilog.Formatting.Compact/EventIdHash.cs b/src/Serilog.Formatting.Compact/EventIdHash.cs index 51be4a0..b287276 100644 --- a/src/Serilog.Formatting.Compact/EventIdHash.cs +++ b/src/Serilog.Formatting.Compact/EventIdHash.cs @@ -12,41 +12,38 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System; +namespace Serilog.Formatting.Compact; -namespace Serilog.Formatting.Compact +/// +/// Hash functions for message templates. See . +/// +public static class EventIdHash { /// - /// Hash functions for message templates. See . + /// Compute a 32-bit hash of the provided . + /// The resulting hash value can be uses as an event id in lieu of transmitting + /// the full template string. /// - public static class EventIdHash + /// A message template. + /// A 32-bit hash of the template. + public static uint Compute(string messageTemplate) { - /// - /// Compute a 32-bit hash of the provided . - /// The resulting hash value can be uses as an event id in lieu of transmitting - /// the full template string. - /// - /// A message template. - /// A 32-bit hash of the template. - public static uint Compute(string messageTemplate) - { - if (messageTemplate == null) throw new ArgumentNullException(nameof(messageTemplate)); + if (messageTemplate == null) throw new ArgumentNullException(nameof(messageTemplate)); - // Jenkins one-at-a-time https://en.wikipedia.org/wiki/Jenkins_hash_function - unchecked + // Jenkins one-at-a-time https://en.wikipedia.org/wiki/Jenkins_hash_function + unchecked + { + uint hash = 0; + for (var i = 0; i < messageTemplate.Length; ++i) { - uint hash = 0; - for (var i = 0; i < messageTemplate.Length; ++i) - { - hash += messageTemplate[i]; - hash += (hash << 10); - hash ^= (hash >> 6); - } - hash += (hash << 3); - hash ^= (hash >> 11); - hash += (hash << 15); - return hash; + hash += messageTemplate[i]; + hash += (hash << 10); + hash ^= (hash >> 6); } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; } } } diff --git a/src/Serilog.Formatting.Compact/RenderedCompactJsonFormatter.cs b/src/Serilog.Formatting.Compact/RenderedCompactJsonFormatter.cs index 327cebc..5ad070a 100644 --- a/src/Serilog.Formatting.Compact/RenderedCompactJsonFormatter.cs +++ b/src/Serilog.Formatting.Compact/RenderedCompactJsonFormatter.cs @@ -1,4 +1,4 @@ -// Copyright 2016 Serilog Contributors +// Copyright 2016 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,109 +12,106 @@ // See the License for the specific language governing permissions and // limitations under the License. -using System; using System.Globalization; -using System.IO; using Serilog.Events; using Serilog.Formatting.Json; // ReSharper disable MemberCanBePrivate.Global -namespace Serilog.Formatting.Compact +namespace Serilog.Formatting.Compact; + +/// +/// An that writes events in a compact JSON format, for consumption in environments +/// without message template support. Message templates are rendered into text and a hashed event id is included. +/// +public class RenderedCompactJsonFormatter : ITextFormatter { + private readonly JsonValueFormatter _valueFormatter; + + /// + /// Construct a , optionally supplying a formatter for + /// s on the event. + /// + /// A value formatter, or null. + public RenderedCompactJsonFormatter(JsonValueFormatter? valueFormatter = null) + { + _valueFormatter = valueFormatter ?? new JsonValueFormatter(typeTagName: "$type"); + } + + /// + /// Format the log event into the output. Subsequent events will be newline-delimited. + /// + /// The event to format. + /// The output. + public void Format(LogEvent logEvent, TextWriter output) + { + FormatEvent(logEvent, output, _valueFormatter); + output.WriteLine(); + } + /// - /// An that writes events in a compact JSON format, for consumption in environments - /// without message template support. Message templates are rendered into text and a hashed event id is included. + /// Format the log event into the output. /// - public class RenderedCompactJsonFormatter : ITextFormatter + /// The event to format. + /// The output. + /// A value formatter for s on the event. + public static void FormatEvent(LogEvent logEvent, TextWriter output, JsonValueFormatter valueFormatter) { - readonly JsonValueFormatter _valueFormatter; + if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); + if (output == null) throw new ArgumentNullException(nameof(output)); + if (valueFormatter == null) throw new ArgumentNullException(nameof(valueFormatter)); - /// - /// Construct a , optionally supplying a formatter for - /// s on the event. - /// - /// A value formatter, or null. - public RenderedCompactJsonFormatter(JsonValueFormatter valueFormatter = null) + output.Write("{\"@t\":\""); + output.Write(logEvent.Timestamp.UtcDateTime.ToString("O")); + output.Write("\",\"@m\":"); + var message = logEvent.MessageTemplate.Render(logEvent.Properties, CultureInfo.InvariantCulture); + JsonValueFormatter.WriteQuotedJsonString(message, output); + output.Write(",\"@i\":\""); + var id = EventIdHash.Compute(logEvent.MessageTemplate.Text); + output.Write(id.ToString("x8",CultureInfo.InvariantCulture)); + output.Write('"'); + + if (logEvent.Level != LogEventLevel.Information) { - _valueFormatter = valueFormatter ?? new JsonValueFormatter(typeTagName: "$type"); + output.Write(",\"@l\":\""); + output.Write(logEvent.Level); + output.Write('\"'); } - /// - /// Format the log event into the output. Subsequent events will be newline-delimited. - /// - /// The event to format. - /// The output. - public void Format(LogEvent logEvent, TextWriter output) + if (logEvent.Exception != null) { - FormatEvent(logEvent, output, _valueFormatter); - output.WriteLine(); + output.Write(",\"@x\":"); + JsonValueFormatter.WriteQuotedJsonString(logEvent.Exception.ToString(), output); } - /// - /// Format the log event into the output. - /// - /// The event to format. - /// The output. - /// A value formatter for s on the event. - public static void FormatEvent(LogEvent logEvent, TextWriter output, JsonValueFormatter valueFormatter) + if (logEvent.TraceId != null) { - if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); - if (output == null) throw new ArgumentNullException(nameof(output)); - if (valueFormatter == null) throw new ArgumentNullException(nameof(valueFormatter)); - - output.Write("{\"@t\":\""); - output.Write(logEvent.Timestamp.UtcDateTime.ToString("O")); - output.Write("\",\"@m\":"); - var message = logEvent.MessageTemplate.Render(logEvent.Properties, CultureInfo.InvariantCulture); - JsonValueFormatter.WriteQuotedJsonString(message, output); - output.Write(",\"@i\":\""); - var id = EventIdHash.Compute(logEvent.MessageTemplate.Text); - output.Write(id.ToString("x8",CultureInfo.InvariantCulture)); - output.Write('"'); - - if (logEvent.Level != LogEventLevel.Information) - { - output.Write(",\"@l\":\""); - output.Write(logEvent.Level); - output.Write('\"'); - } - - if (logEvent.Exception != null) - { - output.Write(",\"@x\":"); - JsonValueFormatter.WriteQuotedJsonString(logEvent.Exception.ToString(), output); - } - - if (logEvent.TraceId != null) - { - output.Write(",\"@tr\":\""); - output.Write(logEvent.TraceId.Value.ToHexString()); - output.Write('\"'); - } + output.Write(",\"@tr\":\""); + output.Write(logEvent.TraceId.Value.ToHexString()); + output.Write('\"'); + } - if (logEvent.SpanId != null) - { - output.Write(",\"@sp\":\""); - output.Write(logEvent.SpanId.Value.ToHexString()); - output.Write('\"'); - } + if (logEvent.SpanId != null) + { + output.Write(",\"@sp\":\""); + output.Write(logEvent.SpanId.Value.ToHexString()); + output.Write('\"'); + } - foreach (var property in logEvent.Properties) + foreach (var property in logEvent.Properties) + { + var name = property.Key; + if (name.Length > 0 && name[0] == '@') { - var name = property.Key; - if (name.Length > 0 && name[0] == '@') - { - // Escape first '@' by doubling - name = '@' + name; - } - - output.Write(','); - JsonValueFormatter.WriteQuotedJsonString(name, output); - output.Write(':'); - valueFormatter.Format(property.Value, output); + // Escape first '@' by doubling + name = '@' + name; } - output.Write('}'); + output.Write(','); + JsonValueFormatter.WriteQuotedJsonString(name, output); + output.Write(':'); + valueFormatter.Format(property.Value, output); } + + output.Write('}'); } } diff --git a/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj b/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj index be739d6..9560ddf 100644 --- a/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj +++ b/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj @@ -1,36 +1,19 @@ - + - - A simple, compact JSON-based event format for Serilog. - 2.0.1 - Serilog Contributors - net462;net471 - $(TargetFrameworks);netstandard2.0;netstandard2.1;net6.0;net7.0 - true - true - ../../assets/Serilog.snk - true - true - serilog-extension-nuget.png - serilog;json - https://github.com/serilog/serilog-formatting-compact - Apache-2.0 - True - https://github.com/serilog/serilog-formatting-compact - git - 6.0-recommended - README.md - + + A simple, compact JSON-based event format for Serilog. + net462;net471 + $(TargetFrameworks);netstandard2.0;netstandard2.1;net6.0;net7.0 + True + - - 5 - + + 5 + - - - - - - + + + + diff --git a/test/Serilog.Formatting.Compact.Tests/Benchmarks.cs b/test/Serilog.Formatting.Compact.Tests/Benchmarks.cs index ce11c42..4e6d44a 100644 --- a/test/Serilog.Formatting.Compact.Tests/Benchmarks.cs +++ b/test/Serilog.Formatting.Compact.Tests/Benchmarks.cs @@ -1,14 +1,13 @@ using BenchmarkDotNet.Running; using Xunit; -namespace Serilog.Formatting.Compact.Tests +namespace Serilog.Formatting.Compact.Tests; + +public class Benchmarks { - public class Benchmarks + [Fact(Skip = "Not supported on CLI 1.0.0-preview3")] + public void Benchmark() { - [Fact(Skip = "Not supported on CLI 1.0.0-preview3")] - public void Benchmark() - { - BenchmarkRunner.Run(); - } + BenchmarkRunner.Run(); } } diff --git a/test/Serilog.Formatting.Compact.Tests/CompactJsonFormatterTests.cs b/test/Serilog.Formatting.Compact.Tests/CompactJsonFormatterTests.cs index bfc4371..6ef5ac1 100644 --- a/test/Serilog.Formatting.Compact.Tests/CompactJsonFormatterTests.cs +++ b/test/Serilog.Formatting.Compact.Tests/CompactJsonFormatterTests.cs @@ -1,98 +1,92 @@ -using System; using System.Diagnostics; -using System.Linq; using Newtonsoft.Json.Linq; using Serilog.Events; using Xunit; using Serilog.Formatting.Compact.Tests.Support; using Serilog.Parsing; +namespace Serilog.Formatting.Compact.Tests; -namespace Serilog.Formatting.Compact.Tests +public class CompactJsonFormatterTests { - public class CompactJsonFormatterTests + private static JObject AssertValidJson(Action act) { - JObject AssertValidJson(Action act) - { - return Assertions.AssertValidJson(new CompactJsonFormatter(), act); - } + return Assertions.AssertValidJson(new CompactJsonFormatter(), act); + } - [Fact] - public void AnEmptyEventIsValidJson() - { - AssertValidJson(log => log.Information("No properties")); - } + [Fact] + public void AnEmptyEventIsValidJson() + { + AssertValidJson(log => log.Information("No properties")); + } - [Fact] - public void AMinimalEventIsValidJson() - { - AssertValidJson(log => log.Information("One {Property}", 42)); - } + [Fact] + public void AMinimalEventIsValidJson() + { + AssertValidJson(log => log.Information("One {Property}", 42)); + } - [Fact] - public void MultiplePropertiesAreDelimited() - { - AssertValidJson(log => log.Information("Property {First} and {Second}", "One", "Two")); - } + [Fact] + public void MultiplePropertiesAreDelimited() + { + AssertValidJson(log => log.Information("Property {First} and {Second}", "One", "Two")); + } - [Fact] - public void ExceptionsAreFormattedToValidJson() - { - AssertValidJson(log => log.Information(new DivideByZeroException(), "With exception")); - } + [Fact] + public void ExceptionsAreFormattedToValidJson() + { + AssertValidJson(log => log.Information(new DivideByZeroException(), "With exception")); + } - [Fact] - public void ExceptionAndPropertiesAreValidJson() - { - AssertValidJson(log => log.Information(new DivideByZeroException(), "With exception and {Property}", 42)); - } + [Fact] + public void ExceptionAndPropertiesAreValidJson() + { + AssertValidJson(log => log.Information(new DivideByZeroException(), "With exception and {Property}", 42)); + } - [Fact] - public void RenderingsAreValidJson() - { - AssertValidJson(log => log.Information("One {Rendering:x8}", 42)); - } + [Fact] + public void RenderingsAreValidJson() + { + AssertValidJson(log => log.Information("One {Rendering:x8}", 42)); + } - [Fact] - public void MultipleRenderingsAreDelimited() - { - AssertValidJson(log => log.Information("Rendering {First:x8} and {Second:x8}", 1, 2)); - } + [Fact] + public void MultipleRenderingsAreDelimited() + { + AssertValidJson(log => log.Information("Rendering {First:x8} and {Second:x8}", 1, 2)); + } - [Fact] - public void AtPrefixedPropertyNamesAreEscaped() - { - // Not possible in message templates, but accepted this way - var jobject = AssertValidJson(log => log.ForContext("@Mistake", 42) - .Information("Hello")); + [Fact] + public void AtPrefixedPropertyNamesAreEscaped() + { + // Not possible in message templates, but accepted this way + var jobject = AssertValidJson(log => log.ForContext("@Mistake", 42) + .Information("Hello")); - JToken val; - Assert.True(jobject.TryGetValue("@@Mistake", out val)); - Assert.Equal(42, val.ToObject()); - } + Assert.True(jobject.TryGetValue("@@Mistake", out var val)); + Assert.Equal(42, val.ToObject()); + } - [Fact] - public void TimestampIsUtc() - { - // Not possible in message templates, but accepted this way - var jobject = AssertValidJson(log => log.Information("Hello")); + [Fact] + public void TimestampIsUTC() + { + // Not possible in message templates, but accepted this way + var jobject = AssertValidJson(log => log.Information("Hello")); - JToken val; - Assert.True(jobject.TryGetValue("@t", out val)); - Assert.EndsWith("Z", val.ToObject()); - } + Assert.True(jobject.TryGetValue("@t", out var val)); + Assert.EndsWith("Z", val.ToObject()); + } - [Fact] - public void TraceAndSpanIdsGenerateValidJson() - { - var traceId = ActivityTraceId.CreateRandom(); - var spanId = ActivitySpanId.CreateRandom(); - var evt = new LogEvent(DateTimeOffset.Now, LogEventLevel.Information, null, - new MessageTemplate(Enumerable.Empty()), Enumerable.Empty(), - traceId, spanId); - var json = AssertValidJson(log => log.Write(evt)); - Assert.Equal(traceId.ToHexString(), json["@tr"]); - Assert.Equal(spanId.ToHexString(), json["@sp"]); - } + [Fact] + public void TraceAndSpanIdsGenerateValidJson() + { + var traceId = ActivityTraceId.CreateRandom(); + var spanId = ActivitySpanId.CreateRandom(); + var evt = new LogEvent(DateTimeOffset.Now, LogEventLevel.Information, null, + new MessageTemplate(Enumerable.Empty()), Enumerable.Empty(), + traceId, spanId); + var json = AssertValidJson(log => log.Write(evt)); + Assert.Equal(traceId.ToHexString(), json["@tr"]); + Assert.Equal(spanId.ToHexString(), json["@sp"]); } } diff --git a/test/Serilog.Formatting.Compact.Tests/EventIdHashTests.cs b/test/Serilog.Formatting.Compact.Tests/EventIdHashTests.cs index 5231a14..d4ddd70 100644 --- a/test/Serilog.Formatting.Compact.Tests/EventIdHashTests.cs +++ b/test/Serilog.Formatting.Compact.Tests/EventIdHashTests.cs @@ -1,23 +1,22 @@ using Xunit; -namespace Serilog.Formatting.Compact.Tests +namespace Serilog.Formatting.Compact.Tests; + +public class EventIdHashTests { - public class EventIdHashTests + [Fact] + public void HashingIsConsistent() { - [Fact] - public void HashingIsConsistent() - { - var h1 = EventIdHash.Compute("Template 1"); - var h2 = EventIdHash.Compute("Template 1"); - Assert.Equal(h1, h2); - } + var h1 = EventIdHash.Compute("Template 1"); + var h2 = EventIdHash.Compute("Template 1"); + Assert.Equal(h1, h2); + } - [Fact] - public void DistinctHashesAreComputed() - { - var h1 = EventIdHash.Compute("Template 1"); - var h2 = EventIdHash.Compute("Template 2"); - Assert.NotEqual(h1, h2); - } + [Fact] + public void DistinctHashesAreComputed() + { + var h1 = EventIdHash.Compute("Template 1"); + var h2 = EventIdHash.Compute("Template 2"); + Assert.NotEqual(h1, h2); } } diff --git a/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarks.cs b/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarks.cs index b637c11..0d770d1 100644 --- a/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarks.cs +++ b/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarks.cs @@ -1,63 +1,61 @@ -using System; -using System.IO; using BenchmarkDotNet.Attributes; using Serilog.Events; using Serilog.Formatting.Compact.Tests.Support; using Serilog.Formatting.Json; -namespace Serilog.Formatting.Compact.Tests +namespace Serilog.Formatting.Compact.Tests; + +[Config(typeof(FormattingBenchmarksConfig))] +[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "Does not matter here")] +public class FormattingBenchmarks { - [Config(typeof(FormattingBenchmarksConfig))] - public class FormattingBenchmarks + private readonly LogEvent _evt; + private readonly ITextFormatter _jsonFormatter = new JsonFormatter(), + _compactFormatter = new CompactJsonFormatter(), + _renderedJsonFormatter = new JsonFormatter(renderMessage: true), + _renderedCompactFormatter = new RenderedCompactJsonFormatter(); + + public FormattingBenchmarks() + { + var collectorSink = new CollectorSink(); + + new LoggerConfiguration() + .WriteTo.Sink(collectorSink) + .CreateLogger() + .Information("Hello, {@User}, {N:x8} at {Now}", new { Name = "nblumhardt", Tags = new[] { 1, 2, 3 } }, 123, DateTime.Now); + + _evt = collectorSink.LastCollected; + } + + private StringWriter _buffer = null!; + + [GlobalSetup] + public void InitBuffer() + { + _buffer = new StringWriter(); + } + + [Benchmark(Baseline = true)] + public void JsonFormatter() + { + _jsonFormatter.Format(_evt, _buffer); + } + + [Benchmark] + public void CompactJsonFormatter() + { + _compactFormatter.Format(_evt, _buffer); + } + + [Benchmark] + public void RenderedJsonFormatter() + { + _renderedJsonFormatter.Format(_evt, _buffer); + } + + [Benchmark] + public void RenderedCompactJsonFormatter() { - readonly LogEvent _evt; - readonly ITextFormatter _jsonFormatter = new JsonFormatter(), - _compactFormatter = new CompactJsonFormatter(), - _renderedJsonFormatter = new JsonFormatter(renderMessage: true), - _renderedCompactFormatter = new RenderedCompactJsonFormatter(); - - public FormattingBenchmarks() - { - var collectorSink = new CollectorSink(); - - new LoggerConfiguration() - .WriteTo.Sink(collectorSink) - .CreateLogger() - .Information("Hello, {@User}, {N:x8} at {Now}", new { Name = "nblumhardt", Tags = new[] { 1, 2, 3 } }, 123, DateTime.Now); - - _evt = collectorSink.LastCollected; - } - - StringWriter _buffer; - - [GlobalSetup] - public void InitBuffer() - { - _buffer = new StringWriter(); - } - - [Benchmark(Baseline = true)] - public void JsonFormatter() - { - _jsonFormatter.Format(_evt, _buffer); - } - - [Benchmark] - public void CompactJsonFormatter() - { - _compactFormatter.Format(_evt, _buffer); - } - - [Benchmark] - public void RenderedJsonFormatter() - { - _renderedJsonFormatter.Format(_evt, _buffer); - } - - [Benchmark] - public void RenderedCompactJsonFormatter() - { - _renderedCompactFormatter.Format(_evt, _buffer); - } + _renderedCompactFormatter.Format(_evt, _buffer); } -} \ No newline at end of file +} diff --git a/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarksConfig.cs b/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarksConfig.cs index e61aaa6..1e28516 100644 --- a/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarksConfig.cs +++ b/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarksConfig.cs @@ -1,13 +1,12 @@ using BenchmarkDotNet.Configs; using BenchmarkDotNet.Jobs; -namespace Serilog.Formatting.Compact.Tests +namespace Serilog.Formatting.Compact.Tests; + +public class FormattingBenchmarksConfig : ManualConfig { - public class FormattingBenchmarksConfig : ManualConfig + public FormattingBenchmarksConfig() { - public FormattingBenchmarksConfig() - { - AddJob(Job.Default.WithIterationCount(10)); - } + AddJob(Job.Default.WithIterationCount(10)); } } \ No newline at end of file diff --git a/test/Serilog.Formatting.Compact.Tests/RenderedCompactJsonFormatterTests.cs b/test/Serilog.Formatting.Compact.Tests/RenderedCompactJsonFormatterTests.cs index 8e92b20..e568889 100644 --- a/test/Serilog.Formatting.Compact.Tests/RenderedCompactJsonFormatterTests.cs +++ b/test/Serilog.Formatting.Compact.Tests/RenderedCompactJsonFormatterTests.cs @@ -1,79 +1,74 @@ -using System; using Newtonsoft.Json.Linq; using Xunit; using Serilog.Formatting.Compact.Tests.Support; +using System.Globalization; +namespace Serilog.Formatting.Compact.Tests; -namespace Serilog.Formatting.Compact.Tests +public class RenderedCompactJsonFormatterTests { - public class RenderedCompactJsonFormatterTests + private static JObject AssertValidJson(Action act) { - JObject AssertValidJson(Action act) - { - return Assertions.AssertValidJson(new RenderedCompactJsonFormatter(), act); - } + return Assertions.AssertValidJson(new RenderedCompactJsonFormatter(), act); + } - [Fact] - public void AnEmptyEventIsValidJson() - { - AssertValidJson(log => log.Information("No properties")); - } + [Fact] + public void AnEmptyEventIsValidJson() + { + AssertValidJson(log => log.Information("No properties")); + } - [Fact] - public void AMinimalEventIsValidJson() - { - var jobject = AssertValidJson(log => log.Information("One {Property}", 42)); + [Fact] + public void AMinimalEventIsValidJson() + { + var jobject = AssertValidJson(log => log.Information("One {Property}", 42)); - JToken m; - Assert.True(jobject.TryGetValue("@m", out m)); - Assert.Equal("One 42", m.ToObject()); + Assert.True(jobject.TryGetValue("@m", out var m)); + Assert.Equal("One 42", m.ToObject()); - JToken i; - Assert.True(jobject.TryGetValue("@i", out i)); - Assert.Equal(EventIdHash.Compute("One {Property}").ToString("x8"), i.ToObject()); + Assert.True(jobject.TryGetValue("@i", out var i)); + Assert.Equal(EventIdHash.Compute("One {Property}").ToString("x8", CultureInfo.InvariantCulture), i.ToObject()); - } + } - [Fact] - public void MultiplePropertiesAreDelimited() - { - AssertValidJson(log => log.Information("Property {First} and {Second}", "One", "Two")); - } + [Fact] + public void MultiplePropertiesAreDelimited() + { + AssertValidJson(log => log.Information("Property {First} and {Second}", "One", "Two")); + } - [Fact] - public void ExceptionsAreFormattedToValidJson() - { - AssertValidJson(log => log.Information(new DivideByZeroException(), "With exception")); - } + [Fact] + public void ExceptionsAreFormattedToValidJson() + { + AssertValidJson(log => log.Information(new DivideByZeroException(), "With exception")); + } - [Fact] - public void ExceptionAndPropertiesAreValidJson() - { - AssertValidJson(log => log.Information(new DivideByZeroException(), "With exception and {Property}", 42)); - } + [Fact] + public void ExceptionAndPropertiesAreValidJson() + { + AssertValidJson(log => log.Information(new DivideByZeroException(), "With exception and {Property}", 42)); + } - [Fact] - public void RenderingsAreValidJson() - { - AssertValidJson(log => log.Information("One {Rendering:x8}", 42)); - } + [Fact] + public void RenderingsAreValidJson() + { + AssertValidJson(log => log.Information("One {Rendering:x8}", 42)); + } - [Fact] - public void MultipleRenderingsAreDelimited() - { - AssertValidJson(log => log.Information("Rendering {First:x8} and {Second:x8}", 1, 2)); - } + [Fact] + public void MultipleRenderingsAreDelimited() + { + AssertValidJson(log => log.Information("Rendering {First:x8} and {Second:x8}", 1, 2)); + } - [Fact] - public void AtPrefixedPropertyNamesAreEscaped() - { - // Not possible in message templates, but accepted this way - var jobject = AssertValidJson(log => log.ForContext("@Mistake", 42) - .Information("Hello")); + [Fact] + public void AtPrefixedPropertyNamesAreEscaped() + { + // Not possible in message templates, but accepted this way + var jobject = AssertValidJson(log => log.ForContext("@Mistake", 42) + .Information("Hello")); - JToken val; - Assert.True(jobject.TryGetValue("@@Mistake", out val)); - Assert.Equal(42, val.ToObject()); - } + Assert.True(jobject.TryGetValue("@@Mistake", out var val)); + Assert.Equal(42, val.ToObject()); } } diff --git a/test/Serilog.Formatting.Compact.Tests/Serilog.Formatting.Compact.Tests.csproj b/test/Serilog.Formatting.Compact.Tests/Serilog.Formatting.Compact.Tests.csproj index 6a3a324..fc8868e 100644 --- a/test/Serilog.Formatting.Compact.Tests/Serilog.Formatting.Compact.Tests.csproj +++ b/test/Serilog.Formatting.Compact.Tests/Serilog.Formatting.Compact.Tests.csproj @@ -1,29 +1,21 @@ - + - - net462;net48 - $(TargetFrameworks);netcoreapp3.1;net6.0;net7.0; - Serilog.Formatting.Compact.Tests - ../../assets/Serilog.snk - true - true - false - true - + + net462;net48 + $(TargetFrameworks);netcoreapp3.1;net6.0;net7.0; + $(NoWarn);1591 + - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + + + + - - - + + + diff --git a/test/Serilog.Formatting.Compact.Tests/Support/Assertions.cs b/test/Serilog.Formatting.Compact.Tests/Support/Assertions.cs index b276d34..53a39e0 100644 --- a/test/Serilog.Formatting.Compact.Tests/Support/Assertions.cs +++ b/test/Serilog.Formatting.Compact.Tests/Support/Assertions.cs @@ -1,30 +1,27 @@ -using System; -using System.IO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace Serilog.Formatting.Compact.Tests.Support +namespace Serilog.Formatting.Compact.Tests.Support; + +internal static class Assertions { - static class Assertions + private static readonly JsonSerializerSettings _settings = new() { - static readonly JsonSerializerSettings _settings = new JsonSerializerSettings - { - DateParseHandling = DateParseHandling.None - }; + DateParseHandling = DateParseHandling.None + }; - public static JObject AssertValidJson(ITextFormatter formatter, Action act) - { - var output = new StringWriter(); - var log = new LoggerConfiguration() - .WriteTo.Sink(new TextWriterSink(output, formatter)) - .CreateLogger(); + public static JObject AssertValidJson(ITextFormatter formatter, Action act) + { + var output = new StringWriter(); + var log = new LoggerConfiguration() + .WriteTo.Sink(new TextWriterSink(output, formatter)) + .CreateLogger(); - act(log); + act(log); - var json = output.ToString(); + var json = output.ToString(); - // Unfortunately this will not detect all JSON formatting issues; better than nothing however. - return JsonConvert.DeserializeObject(json, _settings); - } + // Unfortunately this will not detect all JSON formatting issues; better than nothing however. + return JsonConvert.DeserializeObject(json, _settings)!; } } diff --git a/test/Serilog.Formatting.Compact.Tests/Support/CollectorSink.cs b/test/Serilog.Formatting.Compact.Tests/Support/CollectorSink.cs index ccf3b4e..5a8f806 100644 --- a/test/Serilog.Formatting.Compact.Tests/Support/CollectorSink.cs +++ b/test/Serilog.Formatting.Compact.Tests/Support/CollectorSink.cs @@ -1,15 +1,14 @@ -using Serilog.Core; +using Serilog.Core; using Serilog.Events; -namespace Serilog.Formatting.Compact.Tests.Support +namespace Serilog.Formatting.Compact.Tests.Support; + +public class CollectorSink : ILogEventSink { - public class CollectorSink : ILogEventSink - { - public LogEvent LastCollected { get; private set; } + public LogEvent LastCollected { get; private set; } = null!; - public void Emit(LogEvent logEvent) - { - LastCollected = logEvent; - } + public void Emit(LogEvent logEvent) + { + LastCollected = logEvent; } } diff --git a/test/Serilog.Formatting.Compact.Tests/Support/TextWriterSink.cs b/test/Serilog.Formatting.Compact.Tests/Support/TextWriterSink.cs index 9449c53..adb0a3b 100644 --- a/test/Serilog.Formatting.Compact.Tests/Support/TextWriterSink.cs +++ b/test/Serilog.Formatting.Compact.Tests/Support/TextWriterSink.cs @@ -1,23 +1,21 @@ -using System.IO; -using Serilog.Core; +using Serilog.Core; using Serilog.Events; -namespace Serilog.Formatting.Compact.Tests.Support +namespace Serilog.Formatting.Compact.Tests.Support; + +public class TextWriterSink : ILogEventSink { - public class TextWriterSink : ILogEventSink - { - readonly StringWriter _output; - readonly ITextFormatter _formatter; + readonly StringWriter _output; + readonly ITextFormatter _formatter; - public TextWriterSink(StringWriter output, ITextFormatter formatter) - { - _output = output; - _formatter = formatter; - } + public TextWriterSink(StringWriter output, ITextFormatter formatter) + { + _output = output; + _formatter = formatter; + } - public void Emit(LogEvent logEvent) - { - _formatter.Format(logEvent, _output); - } + public void Emit(LogEvent logEvent) + { + _formatter.Format(logEvent, _output); } } \ No newline at end of file From 2290de599a33d3065dcf754be7f1eaf1091b7705 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Mon, 5 Feb 2024 10:59:52 +0300 Subject: [PATCH 2/7] rem --- src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj b/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj index 9560ddf..597fade 100644 --- a/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj +++ b/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj @@ -14,6 +14,7 @@ + From d236ea0c71a31d1a2dc81ef91eda03b16e4d9bc8 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Tue, 6 Feb 2024 11:56:20 +0300 Subject: [PATCH 3/7] fixes --- .editorconfig | 2 +- Directory.Build.props | 3 +-- src/Serilog.Formatting.Compact/CompactJsonFormatter.cs | 2 +- .../RenderedCompactJsonFormatter.cs | 2 +- .../CompactJsonFormatterTests.cs | 2 +- test/Serilog.Formatting.Compact.Tests/Support/Assertions.cs | 4 ++-- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.editorconfig b/.editorconfig index 778e1d6..b5342d6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -53,7 +53,7 @@ dotnet_style_predefined_type_for_member_access = true:error # .NET code style settings - Modifier preferences # https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#normalize-modifiers -dotnet_style_require_accessibility_modifiers = always:warning +dotnet_style_require_accessibility_modifiers = omit_if_default:error csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning dotnet_style_readonly_field = true:warning diff --git a/Directory.Build.props b/Directory.Build.props index aadf161..2a23d86 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -22,12 +22,11 @@ enable README.md true + 6.0-recommended enable true true ../../assets/Serilog.snk - true - 6.0-recommended diff --git a/src/Serilog.Formatting.Compact/CompactJsonFormatter.cs b/src/Serilog.Formatting.Compact/CompactJsonFormatter.cs index 8067c5e..8ef347d 100644 --- a/src/Serilog.Formatting.Compact/CompactJsonFormatter.cs +++ b/src/Serilog.Formatting.Compact/CompactJsonFormatter.cs @@ -25,7 +25,7 @@ namespace Serilog.Formatting.Compact; /// public class CompactJsonFormatter: ITextFormatter { - private readonly JsonValueFormatter _valueFormatter; + readonly JsonValueFormatter _valueFormatter; /// /// Construct a , optionally supplying a formatter for diff --git a/src/Serilog.Formatting.Compact/RenderedCompactJsonFormatter.cs b/src/Serilog.Formatting.Compact/RenderedCompactJsonFormatter.cs index 5ad070a..43f950e 100644 --- a/src/Serilog.Formatting.Compact/RenderedCompactJsonFormatter.cs +++ b/src/Serilog.Formatting.Compact/RenderedCompactJsonFormatter.cs @@ -25,7 +25,7 @@ namespace Serilog.Formatting.Compact; /// public class RenderedCompactJsonFormatter : ITextFormatter { - private readonly JsonValueFormatter _valueFormatter; + readonly JsonValueFormatter _valueFormatter; /// /// Construct a , optionally supplying a formatter for diff --git a/test/Serilog.Formatting.Compact.Tests/CompactJsonFormatterTests.cs b/test/Serilog.Formatting.Compact.Tests/CompactJsonFormatterTests.cs index 6ef5ac1..09cd018 100644 --- a/test/Serilog.Formatting.Compact.Tests/CompactJsonFormatterTests.cs +++ b/test/Serilog.Formatting.Compact.Tests/CompactJsonFormatterTests.cs @@ -68,7 +68,7 @@ public void AtPrefixedPropertyNamesAreEscaped() } [Fact] - public void TimestampIsUTC() + public void TimestampIsUtc() { // Not possible in message templates, but accepted this way var jobject = AssertValidJson(log => log.Information("Hello")); diff --git a/test/Serilog.Formatting.Compact.Tests/Support/Assertions.cs b/test/Serilog.Formatting.Compact.Tests/Support/Assertions.cs index 53a39e0..1f25841 100644 --- a/test/Serilog.Formatting.Compact.Tests/Support/Assertions.cs +++ b/test/Serilog.Formatting.Compact.Tests/Support/Assertions.cs @@ -3,9 +3,9 @@ namespace Serilog.Formatting.Compact.Tests.Support; -internal static class Assertions +static class Assertions { - private static readonly JsonSerializerSettings _settings = new() + static readonly JsonSerializerSettings _settings = new() { DateParseHandling = DateParseHandling.None }; From ab6544794f6abfb9b1a42487c10742b8a8494335 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Tue, 6 Feb 2024 11:57:36 +0300 Subject: [PATCH 4/7] more --- .../CompactJsonFormatterTests.cs | 2 +- .../FormattingBenchmarks.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Serilog.Formatting.Compact.Tests/CompactJsonFormatterTests.cs b/test/Serilog.Formatting.Compact.Tests/CompactJsonFormatterTests.cs index 09cd018..9e1627f 100644 --- a/test/Serilog.Formatting.Compact.Tests/CompactJsonFormatterTests.cs +++ b/test/Serilog.Formatting.Compact.Tests/CompactJsonFormatterTests.cs @@ -9,7 +9,7 @@ namespace Serilog.Formatting.Compact.Tests; public class CompactJsonFormatterTests { - private static JObject AssertValidJson(Action act) + static JObject AssertValidJson(Action act) { return Assertions.AssertValidJson(new CompactJsonFormatter(), act); } diff --git a/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarks.cs b/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarks.cs index 0d770d1..ab28d06 100644 --- a/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarks.cs +++ b/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarks.cs @@ -9,8 +9,8 @@ namespace Serilog.Formatting.Compact.Tests; [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "Does not matter here")] public class FormattingBenchmarks { - private readonly LogEvent _evt; - private readonly ITextFormatter _jsonFormatter = new JsonFormatter(), + readonly LogEvent _evt; + readonly ITextFormatter _jsonFormatter = new JsonFormatter(), _compactFormatter = new CompactJsonFormatter(), _renderedJsonFormatter = new JsonFormatter(renderMessage: true), _renderedCompactFormatter = new RenderedCompactJsonFormatter(); @@ -27,7 +27,7 @@ public FormattingBenchmarks() _evt = collectorSink.LastCollected; } - private StringWriter _buffer = null!; + StringWriter _buffer = null!; [GlobalSetup] public void InitBuffer() From 19df83a46b098985483432dc1ea435a7724194e9 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Tue, 6 Feb 2024 11:58:33 +0300 Subject: [PATCH 5/7] more --- .../RenderedCompactJsonFormatterTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Serilog.Formatting.Compact.Tests/RenderedCompactJsonFormatterTests.cs b/test/Serilog.Formatting.Compact.Tests/RenderedCompactJsonFormatterTests.cs index e568889..e874877 100644 --- a/test/Serilog.Formatting.Compact.Tests/RenderedCompactJsonFormatterTests.cs +++ b/test/Serilog.Formatting.Compact.Tests/RenderedCompactJsonFormatterTests.cs @@ -7,7 +7,7 @@ namespace Serilog.Formatting.Compact.Tests; public class RenderedCompactJsonFormatterTests { - private static JObject AssertValidJson(Action act) + static JObject AssertValidJson(Action act) { return Assertions.AssertValidJson(new RenderedCompactJsonFormatter(), act); } From 85dc7b048ca4ca173f80a19361595b2643c6c3e7 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Thu, 8 Feb 2024 08:53:10 +0300 Subject: [PATCH 6/7] snupkg --- Build.ps1 | 4 ++-- Directory.Build.props | 2 +- appveyor.yml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Build.ps1 b/Build.ps1 index 7256b6c..30aa70e 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -21,9 +21,9 @@ Write-Output "build: Build version suffix is $buildSuffix" if ($LASTEXITCODE -ne 0) { throw 'build failed' } if ($suffix) { - & dotnet pack src\Serilog.Formatting.Compact --configuration Release --no-build --no-restore -o artifacts --version-suffix=$suffix + & dotnet pack src\Serilog.Formatting.Compact --configuration Release --no-build --no-restore -o artifacts /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg --version-suffix=$suffix } else { - & dotnet pack src\Serilog.Formatting.Compact --configuration Release --no-build --no-restore -o artifacts + & dotnet pack src\Serilog.Formatting.Compact --configuration Release --no-build --no-restore -o artifacts /p:IncludeSymbols=true /p:SymbolPackageFormat=snupkg } if ($LASTEXITCODE -ne 0) { throw 'pack failed' } diff --git a/Directory.Build.props b/Directory.Build.props index 2a23d86..b2239de 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -13,7 +13,7 @@ false serilog-extension-nuget.png git - embedded + true true diff --git a/appveyor.yml b/appveyor.yml index 3022cf7..ee6128b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,13 +13,13 @@ deploy: - provider: NuGet api_key: secure: 4nUKbHgmKmedr6dDtCT2uWVLeQl+tFqO+y9LvRi3nk7cvD/DnOYI1ZqajsgDqxnH - skip_symbols: true + skip_symbols: false on: branch: /^(main|dev)$/ - provider: GitHub auth_token: secure: p4LpVhBKxGS5WqucHxFQ5c7C8cP74kbNB0Z8k9Oxx/PMaDQ1+ibmoexNqVU5ZlmX - artifact: /Serilog.*\.nupkg/ + artifact: /Serilog.*(\.|\.s)nupkg/ tag: v$(appveyor_build_version) on: branch: main From a1f2adefabd5864cd144486a405b428a8d7a8609 Mon Sep 17 00:00:00 2001 From: Ivan Maximov Date: Thu, 8 Feb 2024 08:59:58 +0300 Subject: [PATCH 7/7] remove True --- .../Serilog.Formatting.Compact.csproj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj b/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj index 597fade..338f592 100644 --- a/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj +++ b/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj @@ -1,10 +1,9 @@ - + A simple, compact JSON-based event format for Serilog. net462;net471 $(TargetFrameworks);netstandard2.0;netstandard2.1;net6.0;net7.0 - True