diff --git a/Serilog.Logfmt/Directory.Build.props b/Serilog.Logfmt/Directory.Build.props
index 1c93e46..e7d771c 100644
--- a/Serilog.Logfmt/Directory.Build.props
+++ b/Serilog.Logfmt/Directory.Build.props
@@ -4,7 +4,7 @@
https://github.com/eiximenis/Serilog.Logfmt/
https://github.com/eiximenis/Serilog.Logfmt
Serilog.Logfmt
- 1.0.1$(VersionSuffix)
+ 1.0.2$(VersionSuffix)
Eiximenis
Lo Crestià
true
diff --git a/Serilog.Logfmt/DoubleQuotesAction.cs b/Serilog.Logfmt/DoubleQuotesAction.cs
new file mode 100644
index 0000000..b1dabf4
--- /dev/null
+++ b/Serilog.Logfmt/DoubleQuotesAction.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Serilog.Logfmt
+{
+ enum DoubleQuotesAction
+ {
+ None,
+ ConvertToSingle,
+ Escape,
+ Remove
+ }
+}
diff --git a/Serilog.Logfmt/IDoubleQuotesOptions.cs b/Serilog.Logfmt/IDoubleQuotesOptions.cs
new file mode 100644
index 0000000..cf6d6ab
--- /dev/null
+++ b/Serilog.Logfmt/IDoubleQuotesOptions.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Serilog.Logfmt
+{
+ public interface IDoubleQuotesOptions
+ {
+ void Escape();
+ void ConvertToSingle();
+ void Preserve();
+ void Remove();
+ }
+}
diff --git a/Serilog.Logfmt/LogfmtFormatProvider.cs b/Serilog.Logfmt/LogfmtFormatProvider.cs
new file mode 100644
index 0000000..421b721
--- /dev/null
+++ b/Serilog.Logfmt/LogfmtFormatProvider.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Serilog.Logfmt
+{
+ internal class LogfmtFormatProvider : IFormatProvider, ICustomFormatter
+ {
+ public string Format(string format, object arg, IFormatProvider formatProvider)
+ {
+ return arg.ToString();
+ }
+
+ public object GetFormat(Type formatType)
+ {
+ return this;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Serilog.Logfmt/LogfmtFormatter.cs b/Serilog.Logfmt/LogfmtFormatter.cs
index f4996f6..7d692bc 100644
--- a/Serilog.Logfmt/LogfmtFormatter.cs
+++ b/Serilog.Logfmt/LogfmtFormatter.cs
@@ -3,6 +3,7 @@
using Serilog.Formatting;
using System;
using System.Collections.Generic;
+using System.Data.SqlTypes;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
@@ -46,8 +47,8 @@ public void Format(LogEvent logEvent, TextWriter output)
var msg = "";
using (var sw = new StringWriter())
{
- logEvent.RenderMessage(sw);
- msg = sw.ToString();
+ sw.WriteMessage(logEvent); // Don't use logEvent.RenderMessage(sw) due to extra quotes added.
+ msg = sw.ToLogfmtQuotedString(_options.DoubleQuotesAction);
}
output.WriteLine("{1}{0}{1} ", msg, msg.Contains(" ") ? "\"" : "");
diff --git a/Serilog.Logfmt/LogfmtOptions.cs b/Serilog.Logfmt/LogfmtOptions.cs
index 5dd696a..8e9660c 100644
--- a/Serilog.Logfmt/LogfmtOptions.cs
+++ b/Serilog.Logfmt/LogfmtOptions.cs
@@ -6,7 +6,7 @@
namespace Serilog.Logfmt
{
- public class LogfmtOptions
+ public class LogfmtOptions : IDoubleQuotesOptions
{
internal bool NormalizeCase { get; private set; }
@@ -14,6 +14,8 @@ public class LogfmtOptions
internal LogExceptionOptions ExceptionOptions {get;}
+ internal DoubleQuotesAction DoubleQuotesAction { get; private set; }
+
internal Func PropertyKeyFilter { get; private set; }
public LogfmtOptions()
@@ -22,6 +24,7 @@ public LogfmtOptions()
GrafanaLevels = true;
PropertyKeyFilter = k => false;
ExceptionOptions = new LogExceptionOptions();
+ DoubleQuotesAction = DoubleQuotesAction.ConvertToSingle;
}
public LogfmtOptions PreserveCase()
@@ -30,6 +33,12 @@ public LogfmtOptions PreserveCase()
return this;
}
+ public LogfmtOptions OnDoubleQuotes(Action optionsAction)
+ {
+ optionsAction?.Invoke(this);
+ return this;
+ }
+
public LogfmtOptions PreserveSerilogLogLevels()
{
GrafanaLevels = false;
@@ -46,7 +55,24 @@ public LogfmtOptions OnException(Action optionsAction)
{
optionsAction?.Invoke(ExceptionOptions);
return this;
- }
+ }
+ void IDoubleQuotesOptions.Escape()
+ {
+ DoubleQuotesAction = DoubleQuotesAction.Escape;
+ }
+
+ void IDoubleQuotesOptions.ConvertToSingle()
+ {
+ DoubleQuotesAction = DoubleQuotesAction.ConvertToSingle;
+ }
+ void IDoubleQuotesOptions.Preserve()
+ {
+ DoubleQuotesAction = DoubleQuotesAction.None;
+ }
+ void IDoubleQuotesOptions.Remove()
+ {
+ DoubleQuotesAction = DoubleQuotesAction.Remove;
+ }
}
}
diff --git a/Serilog.Logfmt/LogfmtValueFormatter.cs b/Serilog.Logfmt/LogfmtValueFormatter.cs
index 3098575..f3d3149 100644
--- a/Serilog.Logfmt/LogfmtValueFormatter.cs
+++ b/Serilog.Logfmt/LogfmtValueFormatter.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.IO;
+using System.Linq.Expressions;
using System.Text;
namespace Serilog.Logfmt
@@ -31,6 +32,22 @@ protected override bool VisitScalarValue(TextWriter state, ScalarValue scalar)
{
var svalue = scalar.Value?.ToString() ?? "";
var needQuotes = svalue.Contains(" ");
+ if (needQuotes)
+ {
+ switch (_options.DoubleQuotesAction)
+ {
+ case DoubleQuotesAction.ConvertToSingle:
+ svalue = svalue.Replace('"', '\'');
+ break;
+ case DoubleQuotesAction.Remove:
+ svalue = svalue.Replace(@"""", "");
+ break;
+ case DoubleQuotesAction.Escape:
+ svalue = svalue.Replace(@"""", @"\""");
+ break;
+ default: break;
+ }
+ }
state.Write("{1}{0}{1}", svalue, needQuotes ? "\"" : "");
return false;
}
diff --git a/Serilog.Logfmt/StringWriterExtensions.cs b/Serilog.Logfmt/StringWriterExtensions.cs
new file mode 100644
index 0000000..d579038
--- /dev/null
+++ b/Serilog.Logfmt/StringWriterExtensions.cs
@@ -0,0 +1,59 @@
+using Serilog.Events;
+using Serilog.Parsing;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace Serilog.Logfmt
+{
+ static class StringWriterExtensions
+ {
+ public static string ToLogfmtQuotedString(this StringWriter sw, DoubleQuotesAction action)
+ {
+ var sb = sw.GetStringBuilder();
+ switch (action)
+ {
+ case DoubleQuotesAction.ConvertToSingle:
+ sb.Replace('"', '\'');
+ break;
+ case DoubleQuotesAction.Escape:
+ sb.Replace(@"""", @"\""");
+ break;
+ case DoubleQuotesAction.Remove:
+ sb.Replace(@"""", "");
+ break;
+ default: // None
+ break;
+ }
+
+ return sb.ToString();
+ }
+
+ // From https://github.com/serilog/serilog/issues/936 for avoiding extra quotes on strings
+ public static void WriteMessage(this TextWriter tw, LogEvent logEvent)
+ {
+ Predicate isString = pv =>
+ {
+ var sv = pv as ScalarValue;
+ return (sv != null) && (sv.Value as string) != null;
+ };
+ foreach (var t in logEvent.MessageTemplate.Tokens)
+ {
+ var pt = t as PropertyToken;
+ LogEventPropertyValue propVal;
+ if (pt != null &&
+ logEvent.Properties.TryGetValue(pt.PropertyName, out propVal) &&
+ isString(propVal))
+ {
+ tw.Write(((ScalarValue)propVal).Value);
+ }
+ else
+ {
+ t.Render(logEvent.Properties, tw, null);
+ }
+ }
+ }
+ }
+}
diff --git a/TestApi/Program.cs b/TestApi/Program.cs
index 65f5dbe..4a9c3a9 100644
--- a/TestApi/Program.cs
+++ b/TestApi/Program.cs
@@ -4,6 +4,7 @@
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Logfmt;
+using Serilog.Sinks.SystemConsole.Themes;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -25,7 +26,8 @@ public static IHostBuilder CreateHostBuilder(string[] args) =>
{
config.MinimumLevel.Verbose()
.Enrich.FromLogContext()
- .WriteTo.Console(new LogfmtFormatter());
+ // .WriteTo.Console()
+ .WriteTo.Console(formatter: new LogfmtFormatter());
})
.ConfigureWebHostDefaults(webBuilder =>
{
diff --git a/TestApi/Startup.cs b/TestApi/Startup.cs
index e20d8cb..e991efc 100644
--- a/TestApi/Startup.cs
+++ b/TestApi/Startup.cs
@@ -3,6 +3,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -19,7 +20,7 @@ public void ConfigureServices(IServiceCollection services)
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory lf)
{
if (env.IsDevelopment())
{
@@ -32,6 +33,9 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
endpoints.MapGet("/", async context =>
{
+ var logger = lf.CreateLogger("Startup");
+ logger.LogInformation(@"Message with ""double quotes"" :) ");
+
await context.Response.WriteAsync("Hello World!");
});
});