diff --git a/.gitignore b/.gitignore index f1e3d20..2fad90d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,252 +1,5 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015 cache/options directory .vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# DNX -project.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider .idea/ -*.sln.iml +obj/ +bin/ +*.user diff --git a/Build.ps1 b/Build.ps1 index 1178d7c..7256b6c 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -2,7 +2,7 @@ Write-Output "build: Build started" Push-Location $PSScriptRoot -if(Test-Path .\artifacts) { +if (Test-Path .\artifacts) { Write-Output "build: Cleaning .\artifacts" Remove-Item .\artifacts -Force -Recurse } @@ -18,18 +18,18 @@ Write-Output "build: Build version suffix is $buildSuffix" & dotnet build --configuration Release --version-suffix=$buildSuffix /p:ContinuousIntegrationBuild=true -if($LASTEXITCODE -ne 0) { throw 'build failed' } +if ($LASTEXITCODE -ne 0) { throw 'build failed' } -if($suffix) { +if ($suffix) { & dotnet pack src\Serilog.Formatting.Compact --configuration Release --no-build --no-restore -o artifacts --version-suffix=$suffix } else { & dotnet pack src\Serilog.Formatting.Compact --configuration Release --no-build --no-restore -o artifacts } -if($LASTEXITCODE -ne 0) { throw 'pack failed' } +if ($LASTEXITCODE -ne 0) { throw 'pack failed' } Write-Output "build: Testing" & dotnet test test\Serilog.Formatting.Compact.Tests --configuration Release --no-build --no-restore -if($LASTEXITCODE -ne 0) { throw 'unit tests failed' } +if ($LASTEXITCODE -ne 0) { throw 'unit tests failed' } diff --git a/README.md b/README.md index 730bd41..3328f27 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A simple `Hello, {User}` event. Install from [NuGet](https://nuget.org/packages/Serilog.Formatting.Compact): ```powershell -Install-Package Serilog.Formatting.Compact +dotnet add package Serilog.Formatting.Compact ``` The formatter is used in conjunction with sinks that accept `ITextFormatter`. For example, the [file](https://github.com/serilog/serilog-sinks-file) sink: @@ -26,6 +26,7 @@ Log.Logger = new LoggerConfiguration() .CreateLogger(); ``` #### XML `` configuration + To specify the formatter in XML `` provide its assembly-qualified type name: ```xml @@ -87,6 +88,8 @@ The format defines a handful of reified properties that have special meaning: | `@x` | Exception | A language-dependent error representation potentially including backtrace | | | `@i` | Event id | An implementation specific event id (string or number) | | | `@r` | Renderings | If `@mt` includes tokens with programming-language-specific formatting, an array of pre-rendered values for each such token | May be omitted; if present, the count of renderings must match the count of formatted tokens exactly | +| `@tr` | Trace id | The id of the trace that was active when the event was created, if any | | +| `@sp` | Span id | The id of the span that was active when the event was created, if any | | The `@` sigil may be escaped at the start of a user property name by doubling, e.g. `@@name` denotes a property called `@name`. @@ -152,3 +155,7 @@ Several tools are available for working with the CLEF format. * **[Compact Log Format Viewer](https://github.com/warrenbuckley/Compact-Log-Format-Viewer)** - a cross-platform viewer for CLEF files * **[`seqcli`](https://github.com/datalust/seqcli)** - pretty-`print` CLEF files at the command-line, or `ingest` CLEF files into [Seq](https://datalust.co/seq) for search, and analysis * **[_Serilog.Formatting.Compact.Reader_](https://github.com/serilog/serilog-formatting-compact-reader)** - convert CLEF documents back into Serilog `LogEvent`s + +### Customizing output + +_Serilog.Formatting.Compact_ is not intended to provide customizable formatters. See [this blog post](https://nblumhardt.com/2021/06/customize-serilog-json-output/) for comprehensive Serilog JSON output customization examples. diff --git a/appveyor.yml b/appveyor.yml index 3022cf7..1163cae 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,10 +2,8 @@ version: '{build}' skip_tags: true image: Visual Studio 2022 configuration: Release -install: - - ps: ./Setup.ps1 build_script: -- ps: ./Build.ps1 +- pwsh: ./Build.ps1 test: false artifacts: - path: artifacts/Serilog.*.nupkg diff --git a/global.json b/global.json deleted file mode 100644 index 8960f9e..0000000 --- a/global.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sdk": { - "version": "7.0.304" - } -} \ No newline at end of file diff --git a/serilog-formatting-compact.sln b/serilog-formatting-compact.sln index c0f7b86..336cab0 100644 --- a/serilog-formatting-compact.sln +++ b/serilog-formatting-compact.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26114.2 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34330.188 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{037440DE-440B-4129-9F7A-09B42D00397E}" EndProject @@ -10,15 +10,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{E9D1B5 appveyor.yml = appveyor.yml Build.ps1 = Build.ps1 CHANGES.md = CHANGES.md + LICENSE = LICENSE README.md = README.md assets\Serilog.snk = assets\Serilog.snk + Setup.ps1 = Setup.ps1 EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{67B1C971-75EE-4ABE-B184-66AAC8D9D572}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Formatting.Compact", "src\Serilog.Formatting.Compact\Serilog.Formatting.Compact.csproj", "{E0BB862A-8B2C-46B5-9C90-B2F94C32EAB3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Formatting.Compact", "src\Serilog.Formatting.Compact\Serilog.Formatting.Compact.csproj", "{E0BB862A-8B2C-46B5-9C90-B2F94C32EAB3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Formatting.Compact.Tests", "test\Serilog.Formatting.Compact.Tests\Serilog.Formatting.Compact.Tests.csproj", "{BAEECC42-EC0C-4955-8AA1-BD3F4F0F4AAD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Formatting.Compact.Tests", "test\Serilog.Formatting.Compact.Tests\Serilog.Formatting.Compact.Tests.csproj", "{BAEECC42-EC0C-4955-8AA1-BD3F4F0F4AAD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/serilog-formatting-compact.sln.DotSettings b/serilog-formatting-compact.sln.DotSettings deleted file mode 100644 index 72ec599..0000000 --- a/serilog-formatting-compact.sln.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - DO_NOT_SHOW \ No newline at end of file diff --git a/src/Serilog.Formatting.Compact/CompactJsonFormatter.cs b/src/Serilog.Formatting.Compact/CompactJsonFormatter.cs new file mode 100644 index 0000000..8299e75 --- /dev/null +++ b/src/Serilog.Formatting.Compact/CompactJsonFormatter.cs @@ -0,0 +1,137 @@ +// 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// 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; + +/// +/// An that writes events in a compact JSON format. +/// +public class CompactJsonFormatter: ITextFormatter +{ + 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"); + } + + /// + /// 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) + { + 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 = ""; + // ReSharper disable once PossibleMultipleEnumeration + foreach (var 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(']'); + } + + 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('\"'); + } + + if (logEvent.SpanId != null) + { + output.Write(",\"@sp\":\""); + output.Write(logEvent.SpanId.Value.ToHexString()); + output.Write('\"'); + } + + foreach (var property in logEvent.Properties) + { + 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); + } + + output.Write('}'); + } +} \ No newline at end of file diff --git a/src/Serilog.Formatting.Compact/EventIdHash.cs b/src/Serilog.Formatting.Compact/EventIdHash.cs new file mode 100644 index 0000000..786aac7 --- /dev/null +++ b/src/Serilog.Formatting.Compact/EventIdHash.cs @@ -0,0 +1,51 @@ +// 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; + +namespace Serilog.Formatting.Compact; + +/// +/// Hash functions for message templates. See . +/// +public static class EventIdHash +{ + /// + /// 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)); + + // 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) + { + hash += messageTemplate[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; + } + } +} \ No newline at end of file diff --git a/src/Serilog.Formatting.Compact/Formatting/Compact/CompactJsonFormatter.cs b/src/Serilog.Formatting.Compact/Formatting/Compact/CompactJsonFormatter.cs deleted file mode 100644 index 992740c..0000000 --- a/src/Serilog.Formatting.Compact/Formatting/Compact/CompactJsonFormatter.cs +++ /dev/null @@ -1,137 +0,0 @@ -// 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. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// 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 -{ - /// - /// An that writes events in a compact JSON format. - /// - public class CompactJsonFormatter: ITextFormatter - { - 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"); - } - - /// - /// 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) - { - 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(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.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.SpanId != null) - { - output.Write(",\"@sp\":\""); - output.Write(logEvent.SpanId.Value.ToHexString()); - output.Write('\"'); - } - - foreach (var property in logEvent.Properties) - { - 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); - } - - output.Write('}'); - } - } -} diff --git a/src/Serilog.Formatting.Compact/Formatting/Compact/EventIdHash.cs b/src/Serilog.Formatting.Compact/Formatting/Compact/EventIdHash.cs deleted file mode 100644 index 8c4dcdc..0000000 --- a/src/Serilog.Formatting.Compact/Formatting/Compact/EventIdHash.cs +++ /dev/null @@ -1,52 +0,0 @@ -// 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. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System; - -namespace Serilog.Formatting.Compact -{ - /// - /// Hash functions for message templates. See . - /// - public static class EventIdHash - { - /// - /// 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)); - - // 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) - { - 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/Formatting/Compact/RenderedCompactJsonFormatter.cs b/src/Serilog.Formatting.Compact/Formatting/Compact/RenderedCompactJsonFormatter.cs deleted file mode 100644 index fbe0dd2..0000000 --- a/src/Serilog.Formatting.Compact/Formatting/Compact/RenderedCompactJsonFormatter.cs +++ /dev/null @@ -1,120 +0,0 @@ -// 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. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// 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 -{ - /// - /// 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 - { - 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(); - } - - /// - /// 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("\",\"@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('\"'); - } - - if (logEvent.SpanId != null) - { - output.Write(",\"@sp\":\""); - output.Write(logEvent.SpanId.Value.ToHexString()); - output.Write('\"'); - } - - foreach (var property in logEvent.Properties) - { - 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); - } - - output.Write('}'); - } - } -} diff --git a/src/Serilog.Formatting.Compact/Properties/AssemblyInfo.cs b/src/Serilog.Formatting.Compact/Properties/AssemblyInfo.cs deleted file mode 100644 index 72c1391..0000000 --- a/src/Serilog.Formatting.Compact/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Serilog.Formatting.Compact.Tests, PublicKey=" + - "0024000004800000940000000602000000240000525341310004000001000100fb8d13fd344a1c" + - "6fe0fe83ef33c1080bf30690765bc6eb0df26ebfdf8f21670c64265b30db09f73a0dea5b3db4c9" + - "d18dbf6d5a25af5ce9016f281014d79dc3b4201ac646c451830fc7e61a2dfd633d34c39f87b818" + - "94191652df5ac63cc40c77f3542f702bda692e6e8a9158353df189007a49da0f3cfd55eb250066" + - "b19485ec")] diff --git a/src/Serilog.Formatting.Compact/RenderedCompactJsonFormatter.cs b/src/Serilog.Formatting.Compact/RenderedCompactJsonFormatter.cs new file mode 100644 index 0000000..075a729 --- /dev/null +++ b/src/Serilog.Formatting.Compact/RenderedCompactJsonFormatter.cs @@ -0,0 +1,119 @@ +// 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// 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; + +/// +/// 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 +{ + 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(); + } + + /// + /// 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("\",\"@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('\"'); + } + + if (logEvent.SpanId != null) + { + output.Write(",\"@sp\":\""); + output.Write(logEvent.SpanId.Value.ToHexString()); + output.Write('\"'); + } + + foreach (var property in logEvent.Properties) + { + 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); + } + + output.Write('}'); + } +} \ No newline at end of file diff --git a/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj b/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj index 06def8b..64aa9fb 100644 --- a/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj +++ b/src/Serilog.Formatting.Compact/Serilog.Formatting.Compact.csproj @@ -1,11 +1,11 @@ - + A simple, compact JSON-based event format for Serilog. - 2.0.0 + 3.0.0 Serilog Contributors net462;net471 - $(TargetFrameworks);netstandard2.0;netstandard2.1;net6.0;net7.0 + $(TargetFrameworks);netstandard2.0;net6.0;net8.0 true true ../../assets/Serilog.snk @@ -15,12 +15,12 @@ serilog;json https://github.com/serilog/serilog-formatting-compact Apache-2.0 - True https://github.com/serilog/serilog-formatting-compact git 6.0-recommended - Serilog README.md + enable + 12 @@ -28,9 +28,10 @@ - + + diff --git a/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarksConfig.cs b/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarksConfig.cs index b0bcddd..e61aaa6 100644 --- a/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarksConfig.cs +++ b/test/Serilog.Formatting.Compact.Tests/FormattingBenchmarksConfig.cs @@ -7,7 +7,7 @@ public class FormattingBenchmarksConfig : ManualConfig { public FormattingBenchmarksConfig() { - this.AddJob(Job.Default.WithIterationCount(10)); + AddJob(Job.Default.WithIterationCount(10)); } } } \ No newline at end of file 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..20f010c 100644 --- a/test/Serilog.Formatting.Compact.Tests/Serilog.Formatting.Compact.Tests.csproj +++ b/test/Serilog.Formatting.Compact.Tests/Serilog.Formatting.Compact.Tests.csproj @@ -2,7 +2,7 @@ net462;net48 - $(TargetFrameworks);netcoreapp3.1;net6.0;net7.0; + $(TargetFrameworks);net6.0;net8.0; Serilog.Formatting.Compact.Tests ../../assets/Serilog.snk true