@@ -42,10 +42,13 @@ public static class FileLoggerConfigurationExtensions
42
42
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
43
43
/// <param name="outputTemplate">A message template describing the format used to write to the sink.
44
44
/// the default is "{Timestamp} [{Level}] {Message}{NewLine}{Exception}".</param>
45
- /// <param name="fileSizeLimitBytes">The maximum size, in bytes, to which a log file will be allowed to grow.
46
- /// For unrestricted growth, pass null. The default is 1 GB.</param>
45
+ /// <param name="fileSizeLimitBytes">The approximate maximum size, in bytes, to which a log file will be allowed to grow.
46
+ /// For unrestricted growth, pass null. The default is 1 GB. To avoid writing partial events, the last event within the limit
47
+ /// will be written in full even if it exceeds the limit.</param>
47
48
/// <param name="buffered">Indicates if flushing to the output file can be buffered or not. The default
48
49
/// is false.</param>
50
+ /// <param name="shared">Allow the log file to be shared by multiple processes. The default is false.</param>
51
+ /// <param name="flushToDiskInterval">If provided, a full disk flush will be performed periodically at the specified interval.</param>
49
52
/// <returns>Configuration object allowing method chaining.</returns>
50
53
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
51
54
public static LoggerConfiguration File (
@@ -56,14 +59,16 @@ public static LoggerConfiguration File(
56
59
IFormatProvider formatProvider = null ,
57
60
long ? fileSizeLimitBytes = DefaultFileSizeLimitBytes ,
58
61
LoggingLevelSwitch levelSwitch = null ,
59
- bool buffered = false )
62
+ bool buffered = false ,
63
+ bool shared = false ,
64
+ TimeSpan ? flushToDiskInterval = null )
60
65
{
61
66
if ( sinkConfiguration == null ) throw new ArgumentNullException ( nameof ( sinkConfiguration ) ) ;
62
67
if ( path == null ) throw new ArgumentNullException ( nameof ( path ) ) ;
63
68
if ( outputTemplate == null ) throw new ArgumentNullException ( nameof ( outputTemplate ) ) ;
64
69
65
70
var formatter = new MessageTemplateTextFormatter ( outputTemplate , formatProvider ) ;
66
- return File ( sinkConfiguration , formatter , path , restrictedToMinimumLevel , fileSizeLimitBytes , levelSwitch , buffered ) ;
71
+ return File ( sinkConfiguration , formatter , path , restrictedToMinimumLevel , fileSizeLimitBytes , levelSwitch , buffered : buffered , shared : shared , flushToDiskInterval : flushToDiskInterval ) ;
67
72
}
68
73
69
74
/// <summary>
@@ -72,18 +77,21 @@ public static LoggerConfiguration File(
72
77
/// <param name="sinkConfiguration">Logger sink configuration.</param>
73
78
/// <param name="formatter">A formatter, such as <see cref="JsonFormatter"/>, to convert the log events into
74
79
/// text for the file. If control of regular text formatting is required, use the other
75
- /// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool)"/>
80
+ /// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool, bool, TimeSpan? )"/>
76
81
/// and specify the outputTemplate parameter instead.
77
82
/// </param>
78
83
/// <param name="path">Path to the file.</param>
79
84
/// <param name="restrictedToMinimumLevel">The minimum level for
80
85
/// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
81
86
/// <param name="levelSwitch">A switch allowing the pass-through minimum level
82
87
/// to be changed at runtime.</param>
83
- /// <param name="fileSizeLimitBytes">The maximum size, in bytes, to which a log file will be allowed to grow.
84
- /// For unrestricted growth, pass null. The default is 1 GB.</param>
88
+ /// <param name="fileSizeLimitBytes">The approximate maximum size, in bytes, to which a log file will be allowed to grow.
89
+ /// For unrestricted growth, pass null. The default is 1 GB. To avoid writing partial events, the last event within the limit
90
+ /// will be written in full even if it exceeds the limit.</param>
85
91
/// <param name="buffered">Indicates if flushing to the output file can be buffered or not. The default
86
92
/// is false.</param>
93
+ /// <param name="shared">Allow the log file to be shared by multiple processes. The default is false.</param>
94
+ /// <param name="flushToDiskInterval">If provided, a full disk flush will be performed periodically at the specified interval.</param>
87
95
/// <returns>Configuration object allowing method chaining.</returns>
88
96
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
89
97
public static LoggerConfiguration File (
@@ -93,28 +101,128 @@ public static LoggerConfiguration File(
93
101
LogEventLevel restrictedToMinimumLevel = LevelAlias . Minimum ,
94
102
long ? fileSizeLimitBytes = DefaultFileSizeLimitBytes ,
95
103
LoggingLevelSwitch levelSwitch = null ,
96
- bool buffered = false )
104
+ bool buffered = false ,
105
+ bool shared = false ,
106
+ TimeSpan ? flushToDiskInterval = null )
107
+ {
108
+ return ConfigureFile ( sinkConfiguration . Sink , formatter , path , restrictedToMinimumLevel , fileSizeLimitBytes , levelSwitch , buffered : buffered , shared : shared , flushToDiskInterval : flushToDiskInterval ) ;
109
+ }
110
+
111
+ /// <summary>
112
+ /// Write log events to the specified file.
113
+ /// </summary>
114
+ /// <param name="sinkConfiguration">Logger sink configuration.</param>
115
+ /// <param name="path">Path to the file.</param>
116
+ /// <param name="restrictedToMinimumLevel">The minimum level for
117
+ /// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
118
+ /// <param name="levelSwitch">A switch allowing the pass-through minimum level
119
+ /// to be changed at runtime.</param>
120
+ /// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
121
+ /// <param name="outputTemplate">A message template describing the format used to write to the sink.
122
+ /// the default is "{Timestamp} [{Level}] {Message}{NewLine}{Exception}".</param>
123
+ /// <returns>Configuration object allowing method chaining.</returns>
124
+ /// <remarks>The file will be written using the UTF-8 character set.</remarks>
125
+ public static LoggerConfiguration File (
126
+ this LoggerAuditSinkConfiguration sinkConfiguration ,
127
+ string path ,
128
+ LogEventLevel restrictedToMinimumLevel = LevelAlias . Minimum ,
129
+ string outputTemplate = DefaultOutputTemplate ,
130
+ IFormatProvider formatProvider = null ,
131
+ LoggingLevelSwitch levelSwitch = null )
97
132
{
98
133
if ( sinkConfiguration == null ) throw new ArgumentNullException ( nameof ( sinkConfiguration ) ) ;
134
+ if ( path == null ) throw new ArgumentNullException ( nameof ( path ) ) ;
135
+ if ( outputTemplate == null ) throw new ArgumentNullException ( nameof ( outputTemplate ) ) ;
136
+
137
+ var formatter = new MessageTemplateTextFormatter ( outputTemplate , formatProvider ) ;
138
+ return File ( sinkConfiguration , formatter , path , restrictedToMinimumLevel , levelSwitch ) ;
139
+ }
140
+
141
+ /// <summary>
142
+ /// Write log events to the specified file.
143
+ /// </summary>
144
+ /// <param name="sinkConfiguration">Logger sink configuration.</param>
145
+ /// <param name="formatter">A formatter, such as <see cref="JsonFormatter"/>, to convert the log events into
146
+ /// text for the file. If control of regular text formatting is required, use the other
147
+ /// overload of <see cref="File(LoggerAuditSinkConfiguration, string, LogEventLevel, string, IFormatProvider, LoggingLevelSwitch)"/>
148
+ /// and specify the outputTemplate parameter instead.
149
+ /// </param>
150
+ /// <param name="path">Path to the file.</param>
151
+ /// <param name="restrictedToMinimumLevel">The minimum level for
152
+ /// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
153
+ /// <param name="levelSwitch">A switch allowing the pass-through minimum level
154
+ /// to be changed at runtime.</param>
155
+ /// <returns>Configuration object allowing method chaining.</returns>
156
+ /// <remarks>The file will be written using the UTF-8 character set.</remarks>
157
+ public static LoggerConfiguration File (
158
+ this LoggerAuditSinkConfiguration sinkConfiguration ,
159
+ ITextFormatter formatter ,
160
+ string path ,
161
+ LogEventLevel restrictedToMinimumLevel = LevelAlias . Minimum ,
162
+ LoggingLevelSwitch levelSwitch = null )
163
+ {
164
+ return ConfigureFile ( sinkConfiguration . Sink , formatter , path , restrictedToMinimumLevel , null , levelSwitch , false , true ) ;
165
+ }
166
+
167
+ static LoggerConfiguration ConfigureFile (
168
+ this Func < ILogEventSink , LogEventLevel , LoggingLevelSwitch , LoggerConfiguration > addSink ,
169
+ ITextFormatter formatter ,
170
+ string path ,
171
+ LogEventLevel restrictedToMinimumLevel = LevelAlias . Minimum ,
172
+ long ? fileSizeLimitBytes = DefaultFileSizeLimitBytes ,
173
+ LoggingLevelSwitch levelSwitch = null ,
174
+ bool buffered = false ,
175
+ bool propagateExceptions = false ,
176
+ bool shared = false ,
177
+ TimeSpan ? flushToDiskInterval = null )
178
+ {
179
+ if ( addSink == null ) throw new ArgumentNullException ( nameof ( addSink ) ) ;
99
180
if ( formatter == null ) throw new ArgumentNullException ( nameof ( formatter ) ) ;
100
181
if ( path == null ) throw new ArgumentNullException ( nameof ( path ) ) ;
182
+ if ( fileSizeLimitBytes . HasValue && fileSizeLimitBytes < 0 ) throw new ArgumentException ( "Negative value provided; file size limit must be non-negative" ) ;
101
183
102
- FileSink sink ;
103
- try
184
+ if ( shared )
104
185
{
105
- sink = new FileSink ( path , formatter , fileSizeLimitBytes , buffered : buffered ) ;
186
+ #if ATOMIC_APPEND
187
+ if ( buffered )
188
+ throw new ArgumentException ( "Buffered writes are not available when file sharing is enabled." , nameof ( buffered ) ) ;
189
+ #else
190
+ throw new NotSupportedException ( "File sharing is not supported on this platform." ) ;
191
+ #endif
106
192
}
107
- catch ( ArgumentException )
193
+
194
+ ILogEventSink sink ;
195
+ try
108
196
{
109
- throw ;
197
+ #if ATOMIC_APPEND
198
+ if ( shared )
199
+ {
200
+ sink = new SharedFileSink ( path , formatter , fileSizeLimitBytes ) ;
201
+ }
202
+ else
203
+ {
204
+ #endif
205
+ sink = new FileSink ( path , formatter , fileSizeLimitBytes , buffered : buffered ) ;
206
+ #if ATOMIC_APPEND
207
+ }
208
+ #endif
110
209
}
111
210
catch ( Exception ex )
112
211
{
113
212
SelfLog . WriteLine ( "Unable to open file sink for {0}: {1}" , path , ex ) ;
114
- return sinkConfiguration . Sink ( new NullSink ( ) ) ;
213
+
214
+ if ( propagateExceptions )
215
+ throw ;
216
+
217
+ return addSink ( new NullSink ( ) , LevelAlias . Maximum , null ) ;
218
+ }
219
+
220
+ if ( flushToDiskInterval . HasValue )
221
+ {
222
+ sink = new PeriodicFlushToDiskSink ( sink , flushToDiskInterval . Value ) ;
115
223
}
116
224
117
- return sinkConfiguration . Sink ( sink , restrictedToMinimumLevel , levelSwitch ) ;
225
+ return addSink ( sink , restrictedToMinimumLevel , levelSwitch ) ;
118
226
}
119
227
}
120
- }
228
+ }
0 commit comments