diff --git a/.changeset/new-seas-enjoy.md b/.changeset/new-seas-enjoy.md
new file mode 100644
index 0000000..a6197bc
--- /dev/null
+++ b/.changeset/new-seas-enjoy.md
@@ -0,0 +1,5 @@
+---
+"alexaka1.serilog.extensions.formatting": minor
+---
+
+Add IUtf8SpanFormattable support as fallback option
diff --git a/Directory.Build.props b/Directory.Build.props
index 6d2c547..aedb937 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -31,6 +31,6 @@
$(DefineConstants);FEATURE_SPAN;FEATURE_DATE_AND_TIME_ONLY;FEATURE_ISPANFORMATTABLE;
- $(DefineConstants);FEATURE_SPAN;FEATURE_DATE_AND_TIME_ONLY;FEATURE_ISPANFORMATTABLE;FEATURE_JSON_NAMING_POLICY;
+ $(DefineConstants);FEATURE_SPAN;FEATURE_DATE_AND_TIME_ONLY;FEATURE_ISPANFORMATTABLE;FEATURE_JSON_NAMING_POLICY;FEATURE_IUTF8SPANFORMATTABLE;
diff --git a/README.md b/README.md
index daaf49f..010d427 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
## Utf8JsonFormatter
-A simple JSON formatter for Serilog that uses the `System.Text.Json.Utf8JsonWriter` to write the log events to the output stream.
+A simple JSON formatter for Serilog that uses the `System.Text.Json.Utf8JsonWriter` to write the log events to the output stream. As the name suggests, it is entirely built around UTF-8, with all the [.NET optimizations for UTF-8](https://github.com/dotnet/runtime/issues/81500), so using other encodings will most likely result in invalid characters. The default for the File sink is UTF-8.
### Usage
diff --git a/src/Serilog.Extensions.Formatting/Utf8JsonFormatter.cs b/src/Serilog.Extensions.Formatting/Utf8JsonFormatter.cs
index 2247ea2..ac362a3 100644
--- a/src/Serilog.Extensions.Formatting/Utf8JsonFormatter.cs
+++ b/src/Serilog.Extensions.Formatting/Utf8JsonFormatter.cs
@@ -30,6 +30,7 @@ public class Utf8JsonFormatter : ITextFormatter, IDisposable, IAsyncDisposable
private readonly StringWriter _sw;
private readonly Utf8JsonWriter _writer;
private const string TimeFormat = "O";
+ private const string TimeSpanFormat = "c";
#if FEATURE_DATE_AND_TIME_ONLY
private const string DateOnlyFormat = "O";
#endif
@@ -339,15 +340,23 @@ private void VisitScalarValue(ScalarValue value)
break;
case TimeSpan timeSpan:
{
-#if FEATURE_ISPANFORMATTABLE
+#if FEATURE_IUTF8SPANFORMATTABLE
+ Span buffer = stackalloc byte[_spanBufferSize];
+ if (timeSpan.TryFormat(buffer, out int written, formatProvider: _formatProvider,
+ format: TimeSpanFormat))
+ {
+ // fallback to string
+ _writer.WriteStringValue(Encoding.UTF8.GetString(buffer.Slice(0, written)));
+ }
+#elif FEATURE_ISPANFORMATTABLE
Span buffer = stackalloc char[_spanBufferSize];
if (timeSpan.TryFormat(buffer, out int written, formatProvider: _formatProvider,
- format: "c"))
+ format: TimeSpanFormat))
{
_writer.WriteStringValue(buffer.Slice(0, written));
}
#else
- _writer.WriteStringValue(timeSpan.ToString("c", _formatProvider));
+ _writer.WriteStringValue(timeSpan.ToString(TimeSpanFormat, _formatProvider));
#endif
break;
@@ -387,6 +396,18 @@ private void VisitScalarValue(ScalarValue value)
{
_writer.WriteStringValue(vt.ToString());
}
+#if FEATURE_IUTF8SPANFORMATTABLE
+ else if (vt is IUtf8SpanFormattable utf8Span)
+ {
+ Span buffer = stackalloc byte[_spanBufferSize * 2];
+ if (utf8Span.TryFormat(buffer, out int written, provider: _formatProvider,
+ format: default))
+ {
+ // fallback to string
+ _writer.WriteStringValue(Encoding.UTF8.GetString(buffer.Slice(0, written)));
+ }
+ }
+#endif
#if FEATURE_ISPANFORMATTABLE
else if (vt is ISpanFormattable span)
{
@@ -404,6 +425,19 @@ private void VisitScalarValue(ScalarValue value)
}
break;
+#if FEATURE_IUTF8SPANFORMATTABLE
+ case IUtf8SpanFormattable span:
+ {
+ Span buffer = stackalloc byte[_spanBufferSize * 4];
+ if (span.TryFormat(buffer, out int written, provider: _formatProvider, format: default))
+ {
+ // fallback to string
+ _writer.WriteStringValue(buffer.Slice(0, written));
+ }
+
+ break;
+ }
+#endif
#if FEATURE_ISPANFORMATTABLE
case ISpanFormattable span:
{
diff --git a/test/Serilog.Extensions.Formatting.Benchmark/README.md b/test/Serilog.Extensions.Formatting.Benchmark/README.md
index 7c42966..2843a6e 100644
--- a/test/Serilog.Extensions.Formatting.Benchmark/README.md
+++ b/test/Serilog.Extensions.Formatting.Benchmark/README.md
@@ -1,3 +1,7 @@
```shell
-dotnet run -c Release -f net8.0 -- -f * --r net8.0 net6.0 net48
+dotnet run -c Release -f net8.0 -- -f * -r net481 net6.0 --join
+```
+
+```shell
+dotnet run -c Release -f net481 -- -f * -r net481 --join
```
diff --git a/test/Serilog.Extensions.Formatting.Benchmark/Serilog.Extensions.Formatting.Benchmark.csproj b/test/Serilog.Extensions.Formatting.Benchmark/Serilog.Extensions.Formatting.Benchmark.csproj
index 168cb41..90cea88 100644
--- a/test/Serilog.Extensions.Formatting.Benchmark/Serilog.Extensions.Formatting.Benchmark.csproj
+++ b/test/Serilog.Extensions.Formatting.Benchmark/Serilog.Extensions.Formatting.Benchmark.csproj
@@ -1,7 +1,7 @@
- net8.0;net6.0;net48;net472
+ net8.0;net6.0;net481;net472
Exe
false
false
diff --git a/test/Serilog.Extensions.Formatting.Benchmark/packages.lock.json b/test/Serilog.Extensions.Formatting.Benchmark/packages.lock.json
index 7143bf9..b90a67e 100644
--- a/test/Serilog.Extensions.Formatting.Benchmark/packages.lock.json
+++ b/test/Serilog.Extensions.Formatting.Benchmark/packages.lock.json
@@ -469,7 +469,7 @@
}
}
},
- ".NETFramework,Version=v4.8": {
+ ".NETFramework,Version=v4.8.1": {
"BenchmarkDotNet": {
"type": "Direct",
"requested": "[0.14.0, )",
@@ -890,7 +890,7 @@
}
}
},
- ".NETFramework,Version=v4.8/win7-x86": {
+ ".NETFramework,Version=v4.8.1/win7-x86": {
"Gee.External.Capstone": {
"type": "Transitive",
"resolved": "2.3.0",
diff --git a/test/Serilog.Extensions.Formatting.Test/Serilog.Extensions.Formatting.Test.csproj b/test/Serilog.Extensions.Formatting.Test/Serilog.Extensions.Formatting.Test.csproj
index d48a15a..6885018 100644
--- a/test/Serilog.Extensions.Formatting.Test/Serilog.Extensions.Formatting.Test.csproj
+++ b/test/Serilog.Extensions.Formatting.Test/Serilog.Extensions.Formatting.Test.csproj
@@ -1,7 +1,7 @@
- net8.0;net6.0;net48;net472
+ net8.0;net6.0;net481;net472
false
true
false
diff --git a/test/Serilog.Extensions.Formatting.Test/SerilogJsonFormatterTests.cs b/test/Serilog.Extensions.Formatting.Test/SerilogJsonFormatterTests.cs
index ec14bd3..f17c733 100644
--- a/test/Serilog.Extensions.Formatting.Test/SerilogJsonFormatterTests.cs
+++ b/test/Serilog.Extensions.Formatting.Test/SerilogJsonFormatterTests.cs
@@ -634,7 +634,7 @@ public void AnISpanFormattablePropertySerializesAsStringValue()
public void AnISpanFormattablePropertySerializesAsStringValueNet6()
{
string name = Some.String();
- var value = Version.Parse("1.2.3.4");
+ var value = new Uri("https://www.google.com");
var @event = Some.InformationEvent();
@event.AddOrUpdateProperty(new LogEventProperty(name, new ScalarValue(value)));
diff --git a/test/Serilog.Extensions.Formatting.Test/packages.lock.json b/test/Serilog.Extensions.Formatting.Test/packages.lock.json
index 58e382c..a777076 100644
--- a/test/Serilog.Extensions.Formatting.Test/packages.lock.json
+++ b/test/Serilog.Extensions.Formatting.Test/packages.lock.json
@@ -257,7 +257,7 @@
}
}
},
- ".NETFramework,Version=v4.8": {
+ ".NETFramework,Version=v4.8.1": {
"coverlet.collector": {
"type": "Direct",
"requested": "[6.0.2, )",