Skip to content

Commit c233ef8

Browse files
committed
Enable auditing (serilog/serilog#828)
1 parent 7b57c7b commit c233ef8

File tree

6 files changed

+154
-26
lines changed

6 files changed

+154
-26
lines changed

src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,27 +94,97 @@ public static LoggerConfiguration File(
9494
long? fileSizeLimitBytes = DefaultFileSizeLimitBytes,
9595
LoggingLevelSwitch levelSwitch = null,
9696
bool buffered = false)
97+
{
98+
return ConfigureFile(sinkConfiguration.Sink, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes, levelSwitch, buffered);
99+
}
100+
101+
/// <summary>
102+
/// Write log events to the specified file.
103+
/// </summary>
104+
/// <param name="sinkConfiguration">Logger sink configuration.</param>
105+
/// <param name="path">Path to the file.</param>
106+
/// <param name="restrictedToMinimumLevel">The minimum level for
107+
/// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
108+
/// <param name="levelSwitch">A switch allowing the pass-through minimum level
109+
/// to be changed at runtime.</param>
110+
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
111+
/// <param name="outputTemplate">A message template describing the format used to write to the sink.
112+
/// the default is "{Timestamp} [{Level}] {Message}{NewLine}{Exception}".</param>
113+
/// <returns>Configuration object allowing method chaining.</returns>
114+
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
115+
public static LoggerConfiguration File(
116+
this LoggerAuditSinkConfiguration sinkConfiguration,
117+
string path,
118+
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
119+
string outputTemplate = DefaultOutputTemplate,
120+
IFormatProvider formatProvider = null,
121+
LoggingLevelSwitch levelSwitch = null)
97122
{
98123
if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration));
124+
if (path == null) throw new ArgumentNullException(nameof(path));
125+
if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate));
126+
127+
var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider);
128+
return File(sinkConfiguration, formatter, path, restrictedToMinimumLevel, levelSwitch);
129+
}
130+
131+
/// <summary>
132+
/// Write log events to the specified file.
133+
/// </summary>
134+
/// <param name="sinkConfiguration">Logger sink configuration.</param>
135+
/// <param name="formatter">A formatter, such as <see cref="JsonFormatter"/>, to convert the log events into
136+
/// text for the file. If control of regular text formatting is required, use the other
137+
/// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool)"/>
138+
/// and specify the outputTemplate parameter instead.
139+
/// </param>
140+
/// <param name="path">Path to the file.</param>
141+
/// <param name="restrictedToMinimumLevel">The minimum level for
142+
/// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
143+
/// <param name="levelSwitch">A switch allowing the pass-through minimum level
144+
/// to be changed at runtime.</param>
145+
/// <returns>Configuration object allowing method chaining.</returns>
146+
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
147+
public static LoggerConfiguration File(
148+
this LoggerAuditSinkConfiguration sinkConfiguration,
149+
ITextFormatter formatter,
150+
string path,
151+
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
152+
LoggingLevelSwitch levelSwitch = null)
153+
{
154+
return ConfigureFile(sinkConfiguration.Sink, formatter, path, restrictedToMinimumLevel, null, levelSwitch, false, true);
155+
}
156+
157+
static LoggerConfiguration ConfigureFile(
158+
this Func<ILogEventSink, LogEventLevel, LoggingLevelSwitch, LoggerConfiguration> addSink,
159+
ITextFormatter formatter,
160+
string path,
161+
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
162+
long? fileSizeLimitBytes = DefaultFileSizeLimitBytes,
163+
LoggingLevelSwitch levelSwitch = null,
164+
bool buffered = false,
165+
bool propagateExceptions = false)
166+
{
167+
if (addSink == null) throw new ArgumentNullException(nameof(addSink));
99168
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
100169
if (path == null) throw new ArgumentNullException(nameof(path));
170+
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0) throw new ArgumentException("Negative value provided; file size limit must be non-negative");
101171

102172
FileSink sink;
103173
try
104174
{
105175
sink = new FileSink(path, formatter, fileSizeLimitBytes, buffered: buffered);
106176
}
107-
catch (ArgumentException)
108-
{
109-
throw;
110-
}
111177
catch (Exception ex)
112178
{
113179
SelfLog.WriteLine("Unable to open file sink for {0}: {1}", path, ex);
114-
return sinkConfiguration.Sink(new NullSink());
180+
181+
if (propagateExceptions)
182+
throw;
183+
184+
return addSink(new NullSink(), LevelAlias.Maximum, null);
115185
}
116186

117-
return sinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch);
187+
return addSink(sink, restrictedToMinimumLevel, levelSwitch);
118188
}
119189
}
120190
}

src/Serilog.Sinks.File/Sinks/File/FileSink.cs

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
using System.IO;
1717
using System.Text;
1818
using Serilog.Core;
19-
using Serilog.Debugging;
2019
using Serilog.Events;
2120
using Serilog.Formatting;
2221

@@ -53,7 +52,11 @@ public FileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBy
5352
_textFormatter = textFormatter;
5453
_buffered = buffered;
5554

56-
TryCreateDirectory(path);
55+
var directory = Path.GetDirectoryName(path);
56+
if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory))
57+
{
58+
Directory.CreateDirectory(directory);
59+
}
5760

5861
var file = System.IO.File.Open(path, FileMode.Append, FileAccess.Write, FileShare.Read);
5962
var outputWriter = new StreamWriter(file, encoding ?? new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
@@ -69,22 +72,6 @@ public FileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBy
6972
}
7073
}
7174

72-
static void TryCreateDirectory(string path)
73-
{
74-
try
75-
{
76-
var directory = Path.GetDirectoryName(path);
77-
if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory))
78-
{
79-
Directory.CreateDirectory(directory);
80-
}
81-
}
82-
catch (Exception ex)
83-
{
84-
SelfLog.WriteLine("Failed to create directory {0}: {1}", path, ex);
85-
}
86-
}
87-
8875
/// <summary>
8976
/// Emit the provided log event to the sink.
9077
/// </summary>

src/Serilog.Sinks.File/project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"iconUrl": "http://serilog.net/images/serilog-sink-nuget.png"
1010
},
1111
"dependencies": {
12-
"Serilog": "2.0.0"
12+
"Serilog": "2.2.0-dev-00688"
1313
},
1414
"buildOptions": {
1515
"keyFile": "../../assets/Serilog.snk",
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using Serilog;
3+
using Serilog.Sinks.File.Tests.Support;
4+
using Serilog.Tests.Support;
5+
using Xunit;
6+
7+
namespace Serilog.Tests
8+
{
9+
public class FileLoggerConfigurationExtensionsTests
10+
{
11+
const string InvalidPath = "/\\";
12+
13+
[Fact]
14+
public void WhenWritingCreationExceptionsAreSuppressed()
15+
{
16+
new LoggerConfiguration()
17+
.WriteTo.File(InvalidPath)
18+
.CreateLogger();
19+
}
20+
21+
[Fact]
22+
public void WhenAuditingCreationExceptionsPropagate()
23+
{
24+
Assert.Throws<ArgumentException>(() =>
25+
new LoggerConfiguration()
26+
.AuditTo.File(InvalidPath)
27+
.CreateLogger());
28+
}
29+
30+
[Fact]
31+
public void WhenWritingLoggingExceptionsAreSuppressed()
32+
{
33+
using (var tmp = TempFolder.ForCaller())
34+
using (var log = new LoggerConfiguration()
35+
.WriteTo.File(new ThrowingLogEventFormatter(), tmp.AllocateFilename())
36+
.CreateLogger())
37+
{
38+
log.Information("Hello");
39+
}
40+
}
41+
42+
[Fact]
43+
public void WhenAuditingLoggingExceptionsPropagate()
44+
{
45+
using (var tmp = TempFolder.ForCaller())
46+
using (var log = new LoggerConfiguration()
47+
.AuditTo.File(new ThrowingLogEventFormatter(), tmp.AllocateFilename())
48+
.CreateLogger())
49+
{
50+
var ex = Assert.Throws<AggregateException>(() => log.Information("Hello"));
51+
Assert.IsType<NotImplementedException>(ex.GetBaseException());
52+
}
53+
}
54+
}
55+
}

test/Serilog.Sinks.File.Tests/Sinks/File/FileSinkTests.cs renamed to test/Serilog.Sinks.File.Tests/FileSinkTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.IO;
1+
using System;
2+
using System.IO;
23
using Xunit;
34
using Serilog.Formatting.Json;
45
using Serilog.Sinks.File.Tests.Support;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.IO;
3+
using Serilog.Events;
4+
using Serilog.Formatting;
5+
6+
namespace Serilog.Tests.Support
7+
{
8+
public class ThrowingLogEventFormatter : ITextFormatter
9+
{
10+
public void Format(LogEvent logEvent, TextWriter output)
11+
{
12+
throw new NotImplementedException();
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)