Skip to content

Commit cb15532

Browse files
authored
Merge pull request #15 from serilog/dev
3.0.0 Release
2 parents 8f6975a + ff584d1 commit cb15532

15 files changed

+688
-129
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,10 @@ To emit JSON, rather than plain text, a formatter can be specified:
3636

3737
To configure an alternative formatter in XML `<appSettings>`, specify the formatter's assembly-qualified type name as the setting `value`.
3838

39+
### Performance
40+
41+
By default, the file sink will flush each event written through it to disk. To improve write performance, specifying `buffered: true` will permit the underlying stream to buffer writes.
42+
43+
The [Serilog.Sinks.Async](https://github.com/serilog/serilog-sinks-async) package can be used to wrap the file sink and perform all disk accss on a background worker thread.
44+
3945
_Copyright &copy; 2016 Serilog Contributors - Provided under the [Apache License, Version 2.0](http://apache.org/licenses/LICENSE-2.0.html)._

example/Sample/Program.cs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.IO;
3+
using Serilog;
4+
using Serilog.Debugging;
5+
6+
namespace Sample
7+
{
8+
public class Program
9+
{
10+
public static void Main(string[] args)
11+
{
12+
SelfLog.Enable(Console.Out);
13+
14+
Log.Logger = new LoggerConfiguration()
15+
.WriteTo.File("log.txt")
16+
.CreateLogger();
17+
18+
var sw = System.Diagnostics.Stopwatch.StartNew();
19+
20+
for (var i = 0; i < 1000000; ++i)
21+
{
22+
Log.Information("Hello, file logger!");
23+
}
24+
25+
Log.CloseAndFlush();
26+
27+
sw.Stop();
28+
29+
Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds} ms");
30+
Console.WriteLine($"Size: {new FileInfo("log.txt").Length}");
31+
32+
Console.WriteLine("Press any key to delete the temporary log file...");
33+
Console.ReadKey(true);
34+
35+
File.Delete("log.txt");
36+
}
37+
}
38+
}

example/Sample/Sample.xproj

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
5+
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
6+
</PropertyGroup>
7+
8+
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
9+
<PropertyGroup Label="Globals">
10+
<ProjectGuid>a34235a2-a717-4a1c-bf5c-f4a9e06e1260</ProjectGuid>
11+
<RootNamespace>Sample</RootNamespace>
12+
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
13+
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
14+
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
15+
</PropertyGroup>
16+
17+
<PropertyGroup>
18+
<SchemaVersion>2.0</SchemaVersion>
19+
</PropertyGroup>
20+
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
21+
</Project>

example/Sample/project.json

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"buildOptions": {
3+
"emitEntryPoint": true
4+
},
5+
6+
"dependencies": {
7+
"Serilog.Sinks.File": { "target": "project" }
8+
},
9+
10+
"frameworks": {
11+
"netcoreapp1.0": {
12+
"imports": "dnxcore50",
13+
"dependencies": {
14+
"Microsoft.NETCore.App": {
15+
"type": "platform",
16+
"version": "1.0.0"
17+
}
18+
}
19+
},
20+
"net4.5": {}
21+
},
22+
"runtimes": { "win10-x64": {} }
23+
}

serilog-sinks-file.sln

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio 14
4-
VisualStudioVersion = 14.0.25123.0
4+
VisualStudioVersion = 14.0.25420.1
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{037440DE-440B-4129-9F7A-09B42D00397E}"
77
EndProject
@@ -21,6 +21,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{7B927378-9
2121
EndProject
2222
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Serilog.Sinks.File.Tests", "test\Serilog.Sinks.File.Tests\Serilog.Sinks.File.Tests.xproj", "{3C2D8E01-5580-426A-BDD9-EC59CD98E618}"
2323
EndProject
24+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "example", "example", "{196B1544-C617-4D7C-96D1-628713BDD52A}"
25+
EndProject
26+
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Sample", "example\Sample\Sample.xproj", "{A34235A2-A717-4A1C-BF5C-F4A9E06E1260}"
27+
EndProject
2428
Global
2529
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2630
Debug|Any CPU = Debug|Any CPU
@@ -35,12 +39,17 @@ Global
3539
{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Debug|Any CPU.Build.0 = Debug|Any CPU
3640
{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Release|Any CPU.ActiveCfg = Release|Any CPU
3741
{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Release|Any CPU.Build.0 = Release|Any CPU
42+
{A34235A2-A717-4A1C-BF5C-F4A9E06E1260}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43+
{A34235A2-A717-4A1C-BF5C-F4A9E06E1260}.Debug|Any CPU.Build.0 = Debug|Any CPU
44+
{A34235A2-A717-4A1C-BF5C-F4A9E06E1260}.Release|Any CPU.ActiveCfg = Release|Any CPU
45+
{A34235A2-A717-4A1C-BF5C-F4A9E06E1260}.Release|Any CPU.Build.0 = Release|Any CPU
3846
EndGlobalSection
3947
GlobalSection(SolutionProperties) = preSolution
4048
HideSolutionNode = FALSE
4149
EndGlobalSection
4250
GlobalSection(NestedProjects) = preSolution
4351
{57E0ED0E-0F45-48AB-A73D-6A92B7C32095} = {037440DE-440B-4129-9F7A-09B42D00397E}
4452
{3C2D8E01-5580-426A-BDD9-EC59CD98E618} = {7B927378-9F16-4F6F-B3F6-156395136646}
53+
{A34235A2-A717-4A1C-BF5C-F4A9E06E1260} = {196B1544-C617-4D7C-96D1-628713BDD52A}
4554
EndGlobalSection
4655
EndGlobal

src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs

+113-15
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ public static class FileLoggerConfigurationExtensions
4242
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
4343
/// <param name="outputTemplate">A message template describing the format used to write to the sink.
4444
/// 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>
4748
/// <param name="buffered">Indicates if flushing to the output file can be buffered or not. The default
4849
/// is false.</param>
50+
/// <param name="shared">Allow the log file to be shared by multiple processes. The default is false.</param>
4951
/// <returns>Configuration object allowing method chaining.</returns>
5052
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
5153
public static LoggerConfiguration File(
@@ -56,14 +58,15 @@ public static LoggerConfiguration File(
5658
IFormatProvider formatProvider = null,
5759
long? fileSizeLimitBytes = DefaultFileSizeLimitBytes,
5860
LoggingLevelSwitch levelSwitch = null,
59-
bool buffered = false)
61+
bool buffered = false,
62+
bool shared = false)
6063
{
6164
if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration));
6265
if (path == null) throw new ArgumentNullException(nameof(path));
6366
if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate));
6467

6568
var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider);
66-
return File(sinkConfiguration, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes, levelSwitch, buffered);
69+
return File(sinkConfiguration, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes, levelSwitch, buffered: buffered, shared: shared);
6770
}
6871

6972
/// <summary>
@@ -72,18 +75,20 @@ public static LoggerConfiguration File(
7275
/// <param name="sinkConfiguration">Logger sink configuration.</param>
7376
/// <param name="formatter">A formatter, such as <see cref="JsonFormatter"/>, to convert the log events into
7477
/// 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)"/>
78+
/// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool, bool)"/>
7679
/// and specify the outputTemplate parameter instead.
7780
/// </param>
7881
/// <param name="path">Path to the file.</param>
7982
/// <param name="restrictedToMinimumLevel">The minimum level for
8083
/// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
8184
/// <param name="levelSwitch">A switch allowing the pass-through minimum level
8285
/// 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>
86+
/// <param name="fileSizeLimitBytes">The approximate maximum size, in bytes, to which a log file will be allowed to grow.
87+
/// For unrestricted growth, pass null. The default is 1 GB. To avoid writing partial events, the last event within the limit
88+
/// will be written in full even if it exceeds the limit.</param>
8589
/// <param name="buffered">Indicates if flushing to the output file can be buffered or not. The default
8690
/// is false.</param>
91+
/// <param name="shared">Allow the log file to be shared by multiple processes. The default is false.</param>
8792
/// <returns>Configuration object allowing method chaining.</returns>
8893
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
8994
public static LoggerConfiguration File(
@@ -93,28 +98,121 @@ public static LoggerConfiguration File(
9398
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
9499
long? fileSizeLimitBytes = DefaultFileSizeLimitBytes,
95100
LoggingLevelSwitch levelSwitch = null,
96-
bool buffered = false)
101+
bool buffered = false,
102+
bool shared = false)
103+
{
104+
return ConfigureFile(sinkConfiguration.Sink, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes, levelSwitch, buffered: buffered, shared: shared);
105+
}
106+
107+
/// <summary>
108+
/// Write log events to the specified file.
109+
/// </summary>
110+
/// <param name="sinkConfiguration">Logger sink configuration.</param>
111+
/// <param name="path">Path to the file.</param>
112+
/// <param name="restrictedToMinimumLevel">The minimum level for
113+
/// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
114+
/// <param name="levelSwitch">A switch allowing the pass-through minimum level
115+
/// to be changed at runtime.</param>
116+
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
117+
/// <param name="outputTemplate">A message template describing the format used to write to the sink.
118+
/// the default is "{Timestamp} [{Level}] {Message}{NewLine}{Exception}".</param>
119+
/// <returns>Configuration object allowing method chaining.</returns>
120+
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
121+
public static LoggerConfiguration File(
122+
this LoggerAuditSinkConfiguration sinkConfiguration,
123+
string path,
124+
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
125+
string outputTemplate = DefaultOutputTemplate,
126+
IFormatProvider formatProvider = null,
127+
LoggingLevelSwitch levelSwitch = null)
97128
{
98129
if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration));
130+
if (path == null) throw new ArgumentNullException(nameof(path));
131+
if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate));
132+
133+
var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider);
134+
return File(sinkConfiguration, formatter, path, restrictedToMinimumLevel, levelSwitch);
135+
}
136+
137+
/// <summary>
138+
/// Write log events to the specified file.
139+
/// </summary>
140+
/// <param name="sinkConfiguration">Logger sink configuration.</param>
141+
/// <param name="formatter">A formatter, such as <see cref="JsonFormatter"/>, to convert the log events into
142+
/// text for the file. If control of regular text formatting is required, use the other
143+
/// overload of <see cref="File(LoggerAuditSinkConfiguration, string, LogEventLevel, string, IFormatProvider, LoggingLevelSwitch)"/>
144+
/// and specify the outputTemplate parameter instead.
145+
/// </param>
146+
/// <param name="path">Path to the file.</param>
147+
/// <param name="restrictedToMinimumLevel">The minimum level for
148+
/// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
149+
/// <param name="levelSwitch">A switch allowing the pass-through minimum level
150+
/// to be changed at runtime.</param>
151+
/// <returns>Configuration object allowing method chaining.</returns>
152+
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
153+
public static LoggerConfiguration File(
154+
this LoggerAuditSinkConfiguration sinkConfiguration,
155+
ITextFormatter formatter,
156+
string path,
157+
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
158+
LoggingLevelSwitch levelSwitch = null)
159+
{
160+
return ConfigureFile(sinkConfiguration.Sink, formatter, path, restrictedToMinimumLevel, null, levelSwitch, false, true);
161+
}
162+
163+
static LoggerConfiguration ConfigureFile(
164+
this Func<ILogEventSink, LogEventLevel, LoggingLevelSwitch, LoggerConfiguration> addSink,
165+
ITextFormatter formatter,
166+
string path,
167+
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
168+
long? fileSizeLimitBytes = DefaultFileSizeLimitBytes,
169+
LoggingLevelSwitch levelSwitch = null,
170+
bool buffered = false,
171+
bool propagateExceptions = false,
172+
bool shared = false)
173+
{
174+
if (addSink == null) throw new ArgumentNullException(nameof(addSink));
99175
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
100176
if (path == null) throw new ArgumentNullException(nameof(path));
177+
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0) throw new ArgumentException("Negative value provided; file size limit must be non-negative");
101178

102-
FileSink sink;
103-
try
179+
if (shared)
104180
{
105-
sink = new FileSink(path, formatter, fileSizeLimitBytes, buffered: buffered);
181+
#if !ATOMIC_APPEND
182+
throw new NotSupportedException("File sharing is not supported on this platform.");
183+
#endif
184+
185+
if (buffered)
186+
throw new ArgumentException("Buffered writes are not available when file sharing is enabled.", nameof(buffered));
106187
}
107-
catch (ArgumentException)
188+
189+
ILogEventSink sink;
190+
try
108191
{
109-
throw;
192+
#if ATOMIC_APPEND
193+
if (shared)
194+
{
195+
sink = new SharedFileSink(path, formatter, fileSizeLimitBytes);
196+
}
197+
else
198+
{
199+
#endif
200+
sink = new FileSink(path, formatter, fileSizeLimitBytes, buffered: buffered);
201+
#if ATOMIC_APPEND
202+
}
203+
#endif
110204
}
111205
catch (Exception ex)
112206
{
113207
SelfLog.WriteLine("Unable to open file sink for {0}: {1}", path, ex);
114-
return sinkConfiguration.Sink(new NullSink());
208+
209+
if (propagateExceptions)
210+
throw;
211+
212+
return addSink(new NullSink(), LevelAlias.Maximum, null);
115213
}
116214

117-
return sinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch);
215+
return addSink(sink, restrictedToMinimumLevel, levelSwitch);
118216
}
119217
}
120218
}

src/Serilog.Sinks.File/Sinks/File/CharacterCountLimitedTextWriter.cs

-74
This file was deleted.

0 commit comments

Comments
 (0)