1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Globalization ;
4
+ using System . IO ;
5
+ using System . Reflection ;
6
+ using System . Text ;
7
+ using System . Xml ;
8
+ using AwesomeAssertions ;
9
+ using Microsoft . Extensions . Configuration ;
10
+ using Microsoft . Extensions . Configuration . Json ;
11
+ using NuGet . Versioning ;
12
+ using ReflectionMagic ;
13
+ using Serilog . Core ;
14
+ using Serilog . Settings . Configuration ;
15
+ using Serilog . Sinks . File ;
16
+ using Tomlyn . Extensions . Configuration ;
17
+ using Xunit ;
18
+
19
+ namespace Serilog . Formatting . Log4Net . Tests ;
20
+
21
+ public class SerilogSettingsConfigurationTest
22
+ {
23
+ private static readonly PropertyFilter DefaultPropertyFilter ;
24
+ private static readonly MessageFormatter DefaultMessageFormatter ;
25
+ private static readonly ExceptionFormatter DefaultExceptionFormatter ;
26
+ private static readonly SemanticVersion MicrosoftExtensionsConfigurationVersion ;
27
+
28
+ static SerilogSettingsConfigurationTest ( )
29
+ {
30
+ var defaultBuilder = new Log4NetTextFormatterOptionsBuilder ( ) . AsDynamic ( ) ;
31
+
32
+ DefaultPropertyFilter = defaultBuilder . _filterProperty ;
33
+ DefaultMessageFormatter = defaultBuilder . _formatMessage ;
34
+ DefaultExceptionFormatter = defaultBuilder . _formatException ;
35
+
36
+ var assembly = typeof ( JsonStreamConfigurationSource ) . Assembly ;
37
+ var informationalVersion = assembly . GetCustomAttribute < AssemblyInformationalVersionAttribute > ( ) ? . InformationalVersion ;
38
+ MicrosoftExtensionsConfigurationVersion = SemanticVersion . Parse ( informationalVersion ?? throw new InvalidDataException ( $ "The [AssemblyInformationalVersion] attribute is missing from { assembly } ") ) ;
39
+ }
40
+
41
+
42
+ [ Fact ]
43
+ public void ConfigureDefault ( )
44
+ {
45
+ // lang=toml
46
+ var config =
47
+ """
48
+ [[Serilog.WriteTo]]
49
+ Name = 'File'
50
+ Args.path = 'logs.xml'
51
+ Args.formatter.type = 'Serilog.Formatting.Log4Net.Log4NetTextFormatter, Serilog.Formatting.Log4Net'
52
+ """ ;
53
+
54
+ var options = GetLog4NetTextFormatterOptions ( config ) ;
55
+
56
+ options . Should ( ) . BeEquivalentTo ( new Log4NetTextFormatterOptions (
57
+ formatProvider : null ,
58
+ cDataMode : CDataMode . Always ,
59
+ nullText : "(null)" ,
60
+ xmlNamespace : new XmlQualifiedName ( "log4net" , "http://logging.apache.org/log4net/schemas/log4net-events-1.2/" ) ,
61
+ xmlWriterSettings : new XmlWriterSettings { Indent = true , IndentChars = " " , ConformanceLevel = ConformanceLevel . Fragment } ,
62
+ filterProperty : DefaultPropertyFilter ,
63
+ formatMessage : DefaultMessageFormatter ,
64
+ formatException : DefaultExceptionFormatter
65
+ ) ) ;
66
+ }
67
+
68
+ [ Theory ]
69
+ [ InlineData ( CDataMode . Always ) ]
70
+ [ InlineData ( CDataMode . Never ) ]
71
+ [ InlineData ( CDataMode . IfNeeded ) ]
72
+ public void ConfigureCDataMode ( CDataMode cDataMode )
73
+ {
74
+ // lang=toml
75
+ var config =
76
+ $ """
77
+ [[Serilog.WriteTo]]
78
+ Name = 'File'
79
+ Args.path = 'logs.xml'
80
+ Args.formatter.type = 'Serilog.Formatting.Log4Net.Log4NetTextFormatter, Serilog.Formatting.Log4Net'
81
+ Args.formatter.cDataMode = '{ cDataMode } '
82
+ """ ;
83
+
84
+ var options = GetLog4NetTextFormatterOptions ( config ) ;
85
+
86
+ options . Should ( ) . BeEquivalentTo ( new Log4NetTextFormatterOptions (
87
+ formatProvider : null ,
88
+ cDataMode : cDataMode ,
89
+ nullText : "(null)" ,
90
+ xmlNamespace : new XmlQualifiedName ( "log4net" , "http://logging.apache.org/log4net/schemas/log4net-events-1.2/" ) ,
91
+ xmlWriterSettings : new XmlWriterSettings { Indent = true , IndentChars = " " , ConformanceLevel = ConformanceLevel . Fragment } ,
92
+ filterProperty : DefaultPropertyFilter ,
93
+ formatMessage : DefaultMessageFormatter ,
94
+ formatException : DefaultExceptionFormatter
95
+ ) ) ;
96
+ }
97
+
98
+ [ Theory ]
99
+ [ InlineData ( "iv" ) ]
100
+ [ InlineData ( "fr-CH" ) ]
101
+ public void ConfigureFormatProvider ( string formatProvider )
102
+ {
103
+ // lang=toml
104
+ var config =
105
+ $ """
106
+ [[Serilog.WriteTo]]
107
+ Name = 'File'
108
+ Args.path = 'logs.xml'
109
+ Args.formatter.type = 'Serilog.Formatting.Log4Net.Log4NetTextFormatter, Serilog.Formatting.Log4Net'
110
+ Args.formatter.formatProvider = '{ formatProvider } '
111
+ """ ;
112
+
113
+ var options = GetLog4NetTextFormatterOptions ( config ) ;
114
+
115
+ options . FormatProvider . Should ( ) . BeSameAs ( CultureInfo . GetCultureInfo ( formatProvider ) ) ;
116
+ }
117
+
118
+ [ SkippableTheory ]
119
+ [ InlineData ( null ) ]
120
+ [ InlineData ( "" ) ]
121
+ [ InlineData ( "<null>" ) ]
122
+ [ InlineData ( "🌀" ) ]
123
+ public void ConfigureNullText ( string ? nullText )
124
+ {
125
+ // Null values preserved in configuration was introduced in .NET 10 Preview 7
126
+ // See https://learn.microsoft.com/en-us/dotnet/core/compatibility/extensions/10.0/configuration-null-values-preserved
127
+ Skip . If ( nullText == null && MicrosoftExtensionsConfigurationVersion . Major < 10 , "null values are preserved in configuration since .NET 10 Preview 7" ) ;
128
+
129
+ // lang=json
130
+ var config =
131
+ $$ """
132
+ {
133
+ "Serilog": {
134
+ "WriteTo:File": {
135
+ "Name": "File",
136
+ "Args": {
137
+ "path": "logs.xml",
138
+ "formatter": {
139
+ "type": "Serilog.Formatting.Log4Net.Log4NetTextFormatter, Serilog.Formatting.Log4Net",
140
+ "nullText": {{ ( nullText == null ? "null" : $ "\" { nullText } \" ") }}
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ """ ;
147
+ var options = GetLog4NetTextFormatterOptions ( config ) ;
148
+
149
+ options . NullText . Should ( ) . Be ( nullText ) ;
150
+ }
151
+
152
+ [ Fact ]
153
+ public void ConfigureNoXmlNamespace ( )
154
+ {
155
+ // lang=toml
156
+ const string config =
157
+ """
158
+ [[Serilog.WriteTo]]
159
+ Name = 'File'
160
+ Args.path = 'logs.xml'
161
+ Args.formatter.type = 'Serilog.Formatting.Log4Net.Log4NetTextFormatter, Serilog.Formatting.Log4Net'
162
+ Args.formatter.noXmlNamespace = true
163
+ """ ;
164
+
165
+ var options = GetLog4NetTextFormatterOptions ( config ) ;
166
+
167
+ options . XmlNamespace . Should ( ) . BeNull ( ) ;
168
+ }
169
+
170
+ private static Log4NetTextFormatterOptions GetLog4NetTextFormatterOptions ( string config )
171
+ {
172
+ using var configStream = new MemoryStream ( Encoding . UTF8 . GetBytes ( config ) ) ;
173
+ var configBuilder = new ConfigurationBuilder ( ) ;
174
+ var configuration = ( config . StartsWith ( '{' ) ? configBuilder . AddJsonStream ( configStream ) : configBuilder . AddTomlStream ( configStream ) ) . Build ( ) ;
175
+ var options = new ConfigurationReaderOptions ( typeof ( ILogger ) . Assembly , typeof ( FileSink ) . Assembly , typeof ( Log4NetTextFormatter ) . Assembly ) ;
176
+ using var logger = new LoggerConfiguration ( ) . ReadFrom . Configuration ( configuration , options ) . CreateLogger ( ) ;
177
+ IEnumerable < ILogEventSink > sinks = logger . AsDynamic ( ) . _sink . _sinks ;
178
+ var fileSink = sinks . Should ( ) . ContainSingle ( ) . Which . Should ( ) . BeOfType < FileSink > ( ) . Subject ;
179
+ return fileSink . AsDynamic ( ) . _textFormatter . _options ;
180
+ }
181
+ }
0 commit comments