Skip to content

Commit a2a5ce7

Browse files
committed
Log4JXmlNamespaceResolver hack
1 parent 7df550c commit a2a5ce7

File tree

3 files changed

+35
-28
lines changed

3 files changed

+35
-28
lines changed

src/Log4JXmlNamespaceResolver.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Collections.Generic;
2+
using System.Xml;
3+
4+
namespace Serilog.Formatting.Log4Net;
5+
6+
internal sealed class Log4JXmlNamespaceResolver : IXmlNamespaceResolver
7+
{
8+
private const string Prefix = "log4j";
9+
private const string NamespaceName = "http://jakarta.apache.org/log4j/";
10+
11+
internal static readonly Log4JXmlNamespaceResolver Instance = new();
12+
13+
/// <summary>
14+
/// The XML namespace used for Log4j events.
15+
/// </summary>
16+
/// <remarks>https://github.com/apache/log4j/blob/v1_2_17/src/main/java/org/apache/log4j/xml/XMLLayout.java#L137</remarks>
17+
internal static readonly XmlQualifiedName Log4JXmlNamespace = new(Prefix, NamespaceName);
18+
19+
private static readonly Dictionary<string, string> Namespaces = new() { [Prefix] = NamespaceName };
20+
21+
public IDictionary<string, string> GetNamespacesInScope(XmlNamespaceScope scope) => Namespaces;
22+
public string? LookupNamespace(string prefix) => prefix == Prefix ? NamespaceName : null;
23+
public string? LookupPrefix(string namespaceName) => namespaceName == NamespaceName ? Prefix : null;
24+
}

src/Log4NetTextFormatter.cs

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Diagnostics.CodeAnalysis;
44
using System.IO;
55
using System.Linq;
6+
using System.Reflection;
67
using System.Text.RegularExpressions;
78
using System.Xml;
89
using Serilog.Core;
@@ -84,7 +85,7 @@ public Log4NetTextFormatter(Action<Log4NetTextFormatterOptionsBuilder>? configur
8485
var optionsBuilder = new Log4NetTextFormatterOptionsBuilder();
8586
configureOptions?.Invoke(optionsBuilder);
8687
_options = optionsBuilder.Build();
87-
_usesLog4JCompatibility = ReferenceEquals(Log4NetTextFormatterOptionsBuilder.Log4JXmlNamespace, _options.XmlNamespace);
88+
_usesLog4JCompatibility = ReferenceEquals(Log4JXmlNamespaceResolver.Log4JXmlNamespace, _options.XmlNamespace);
8889
}
8990

9091
/// <summary>
@@ -104,29 +105,17 @@ public void Format(LogEvent logEvent, TextWriter output)
104105
{
105106
throw new ArgumentNullException(nameof(output));
106107
}
107-
var xmlWriterOutput = _usesLog4JCompatibility ? new StringWriter() : output;
108-
using var writer = XmlWriter.Create(xmlWriterOutput, _options.XmlWriterSettings);
109-
WriteEvent(logEvent, writer);
110-
writer.Flush();
108+
using var writer = XmlWriter.Create(output, _options.XmlWriterSettings);
111109
if (_usesLog4JCompatibility)
112110
{
113-
// log4j writes the XML "manually", see https://github.com/apache/log4j/blob/v1_2_17/src/main/java/org/apache/log4j/xml/XMLLayout.java#L137-L145
114-
// The resulting XML is impossible to write with a standard compliant XML writer such as XmlWriter.
115-
// That's why we write the event in a StringWriter then massage the output to remove the xmlns:log4j attribute to match log4j output.
116-
// The XML fragment becomes valid when surrounded by an external entity, see https://github.com/apache/log4j/blob/v1_2_17/src/main/java/org/apache/log4j/xml/XMLLayout.java#L31-L49
117-
const string log4JNamespaceAttribute = """
118-
xmlns:log4j="http://jakarta.apache.org/log4j/"
119-
""";
120-
var xmlString = ((StringWriter)xmlWriterOutput).ToString();
121-
var i = xmlString.IndexOf(log4JNamespaceAttribute, StringComparison.Ordinal);
122-
#if NETSTANDARD2_0
123-
output.Write(xmlString.Substring(0, i));
124-
output.Write(xmlString.Substring(i + log4JNamespaceAttribute.Length));
125-
#else
126-
output.Write(xmlString.AsSpan(0, i));
127-
output.Write(xmlString.AsSpan(i + log4JNamespaceAttribute.Length));
128-
#endif
111+
var predefinedNamespaces = writer.GetType().GetField("_predefinedNamespaces", BindingFlags.Instance | BindingFlags.NonPublic);
112+
if (predefinedNamespaces != null && typeof(IXmlNamespaceResolver).IsAssignableFrom(predefinedNamespaces.FieldType))
113+
{
114+
predefinedNamespaces.SetValue(writer, Log4JXmlNamespaceResolver.Instance);
115+
}
129116
}
117+
WriteEvent(logEvent, writer);
118+
writer.Flush();
130119
output.Write(_options.XmlWriterSettings.NewLineChars);
131120
}
132121

src/Log4NetTextFormatterOptionsBuilder.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,6 @@ public class Log4NetTextFormatterOptionsBuilder
1515
/// <remarks>https://github.com/apache/logging-log4net/blob/rel/2.0.8/src/Layout/XmlLayout.cs#L49</remarks>
1616
private static readonly XmlQualifiedName Log4NetXmlNamespace = new("log4net", "http://logging.apache.org/log4net/schemas/log4net-events-1.2/");
1717

18-
/// <summary>
19-
/// The XML namespace used for Log4j events.
20-
/// </summary>
21-
/// <remarks>https://github.com/apache/log4j/blob/v1_2_17/src/main/java/org/apache/log4j/xml/XMLLayout.java#L137</remarks>
22-
internal static readonly XmlQualifiedName Log4JXmlNamespace = new("log4j", "http://jakarta.apache.org/log4j/");
23-
2418
/// <summary>
2519
/// Initialize a new instance of the <see cref="Log4NetTextFormatterOptionsBuilder"/> class.
2620
/// </summary>
@@ -170,7 +164,7 @@ public void UseLog4JCompatibility()
170164
_lineEnding = LineEnding.CarriageReturn | LineEnding.LineFeed;
171165

172166
// https://github.com/apache/log4j/blob/v1_2_17/src/main/java/org/apache/log4j/xml/XMLLayout.java#L137
173-
_xmlNamespace = Log4JXmlNamespace;
167+
_xmlNamespace = Log4JXmlNamespaceResolver.Log4JXmlNamespace;
174168

175169
// https://github.com/apache/log4j/blob/v1_2_17/src/main/java/org/apache/log4j/xml/XMLLayout.java#L147
176170
_cDataMode = CDataMode.Always;

0 commit comments

Comments
 (0)