Skip to content

3.0.0 Release #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Aug 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,10 @@ To emit JSON, rather than plain text, a formatter can be specified:

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

### Performance

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.

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.

_Copyright &copy; 2016 Serilog Contributors - Provided under the [Apache License, Version 2.0](http://apache.org/licenses/LICENSE-2.0.html)._
38 changes: 38 additions & 0 deletions example/Sample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.IO;
using Serilog;
using Serilog.Debugging;

namespace Sample
{
public class Program
{
public static void Main(string[] args)
{
SelfLog.Enable(Console.Out);

Log.Logger = new LoggerConfiguration()
.WriteTo.File("log.txt")
.CreateLogger();

var sw = System.Diagnostics.Stopwatch.StartNew();

for (var i = 0; i < 1000000; ++i)
{
Log.Information("Hello, file logger!");
}

Log.CloseAndFlush();

sw.Stop();

Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds} ms");
Console.WriteLine($"Size: {new FileInfo("log.txt").Length}");

Console.WriteLine("Press any key to delete the temporary log file...");
Console.ReadKey(true);

File.Delete("log.txt");
}
}
}
21 changes: 21 additions & 0 deletions example/Sample/Sample.xproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>

<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>a34235a2-a717-4a1c-bf5c-f4a9e06e1260</ProjectGuid>
<RootNamespace>Sample</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>

<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
23 changes: 23 additions & 0 deletions example/Sample/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"buildOptions": {
"emitEntryPoint": true
},

"dependencies": {
"Serilog.Sinks.File": { "target": "project" }
},

"frameworks": {
"netcoreapp1.0": {
"imports": "dnxcore50",
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
}
}
},
"net4.5": {}
},
"runtimes": { "win10-x64": {} }
}
11 changes: 10 additions & 1 deletion serilog-sinks-file.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25123.0
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{037440DE-440B-4129-9F7A-09B42D00397E}"
EndProject
Expand All @@ -21,6 +21,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{7B927378-9
EndProject
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}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "example", "example", "{196B1544-C617-4D7C-96D1-628713BDD52A}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Sample", "example\Sample\Sample.xproj", "{A34235A2-A717-4A1C-BF5C-F4A9E06E1260}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -35,12 +39,17 @@ Global
{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Release|Any CPU.Build.0 = Release|Any CPU
{A34235A2-A717-4A1C-BF5C-F4A9E06E1260}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A34235A2-A717-4A1C-BF5C-F4A9E06E1260}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A34235A2-A717-4A1C-BF5C-F4A9E06E1260}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A34235A2-A717-4A1C-BF5C-F4A9E06E1260}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{57E0ED0E-0F45-48AB-A73D-6A92B7C32095} = {037440DE-440B-4129-9F7A-09B42D00397E}
{3C2D8E01-5580-426A-BDD9-EC59CD98E618} = {7B927378-9F16-4F6F-B3F6-156395136646}
{A34235A2-A717-4A1C-BF5C-F4A9E06E1260} = {196B1544-C617-4D7C-96D1-628713BDD52A}
EndGlobalSection
EndGlobal
128 changes: 113 additions & 15 deletions src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ public static class FileLoggerConfigurationExtensions
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
/// <param name="outputTemplate">A message template describing the format used to write to the sink.
/// the default is "{Timestamp} [{Level}] {Message}{NewLine}{Exception}".</param>
/// <param name="fileSizeLimitBytes">The maximum size, in bytes, to which a log file will be allowed to grow.
/// For unrestricted growth, pass null. The default is 1 GB.</param>
/// <param name="fileSizeLimitBytes">The approximate maximum size, in bytes, to which a log file will be allowed to grow.
/// For unrestricted growth, pass null. The default is 1 GB. To avoid writing partial events, the last event within the limit
/// will be written in full even if it exceeds the limit.</param>
/// <param name="buffered">Indicates if flushing to the output file can be buffered or not. The default
/// is false.</param>
/// <param name="shared">Allow the log file to be shared by multiple processes. The default is false.</param>
/// <returns>Configuration object allowing method chaining.</returns>
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
public static LoggerConfiguration File(
Expand All @@ -56,14 +58,15 @@ public static LoggerConfiguration File(
IFormatProvider formatProvider = null,
long? fileSizeLimitBytes = DefaultFileSizeLimitBytes,
LoggingLevelSwitch levelSwitch = null,
bool buffered = false)
bool buffered = false,
bool shared = false)
{
if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration));
if (path == null) throw new ArgumentNullException(nameof(path));
if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate));

var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider);
return File(sinkConfiguration, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes, levelSwitch, buffered);
return File(sinkConfiguration, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes, levelSwitch, buffered: buffered, shared: shared);
}

/// <summary>
Expand All @@ -72,18 +75,20 @@ public static LoggerConfiguration File(
/// <param name="sinkConfiguration">Logger sink configuration.</param>
/// <param name="formatter">A formatter, such as <see cref="JsonFormatter"/>, to convert the log events into
/// text for the file. If control of regular text formatting is required, use the other
/// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool)"/>
/// overload of <see cref="File(LoggerSinkConfiguration, string, LogEventLevel, string, IFormatProvider, long?, LoggingLevelSwitch, bool, bool)"/>
/// and specify the outputTemplate parameter instead.
/// </param>
/// <param name="path">Path to the file.</param>
/// <param name="restrictedToMinimumLevel">The minimum level for
/// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
/// <param name="levelSwitch">A switch allowing the pass-through minimum level
/// to be changed at runtime.</param>
/// <param name="fileSizeLimitBytes">The maximum size, in bytes, to which a log file will be allowed to grow.
/// For unrestricted growth, pass null. The default is 1 GB.</param>
/// <param name="fileSizeLimitBytes">The approximate maximum size, in bytes, to which a log file will be allowed to grow.
/// For unrestricted growth, pass null. The default is 1 GB. To avoid writing partial events, the last event within the limit
/// will be written in full even if it exceeds the limit.</param>
/// <param name="buffered">Indicates if flushing to the output file can be buffered or not. The default
/// is false.</param>
/// <param name="shared">Allow the log file to be shared by multiple processes. The default is false.</param>
/// <returns>Configuration object allowing method chaining.</returns>
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
public static LoggerConfiguration File(
Expand All @@ -93,28 +98,121 @@ public static LoggerConfiguration File(
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
long? fileSizeLimitBytes = DefaultFileSizeLimitBytes,
LoggingLevelSwitch levelSwitch = null,
bool buffered = false)
bool buffered = false,
bool shared = false)
{
return ConfigureFile(sinkConfiguration.Sink, formatter, path, restrictedToMinimumLevel, fileSizeLimitBytes, levelSwitch, buffered: buffered, shared: shared);
}

/// <summary>
/// Write log events to the specified file.
/// </summary>
/// <param name="sinkConfiguration">Logger sink configuration.</param>
/// <param name="path">Path to the file.</param>
/// <param name="restrictedToMinimumLevel">The minimum level for
/// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
/// <param name="levelSwitch">A switch allowing the pass-through minimum level
/// to be changed at runtime.</param>
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
/// <param name="outputTemplate">A message template describing the format used to write to the sink.
/// the default is "{Timestamp} [{Level}] {Message}{NewLine}{Exception}".</param>
/// <returns>Configuration object allowing method chaining.</returns>
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
public static LoggerConfiguration File(
this LoggerAuditSinkConfiguration sinkConfiguration,
string path,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
string outputTemplate = DefaultOutputTemplate,
IFormatProvider formatProvider = null,
LoggingLevelSwitch levelSwitch = null)
{
if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration));
if (path == null) throw new ArgumentNullException(nameof(path));
if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate));

var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider);
return File(sinkConfiguration, formatter, path, restrictedToMinimumLevel, levelSwitch);
}

/// <summary>
/// Write log events to the specified file.
/// </summary>
/// <param name="sinkConfiguration">Logger sink configuration.</param>
/// <param name="formatter">A formatter, such as <see cref="JsonFormatter"/>, to convert the log events into
/// text for the file. If control of regular text formatting is required, use the other
/// overload of <see cref="File(LoggerAuditSinkConfiguration, string, LogEventLevel, string, IFormatProvider, LoggingLevelSwitch)"/>
/// and specify the outputTemplate parameter instead.
/// </param>
/// <param name="path">Path to the file.</param>
/// <param name="restrictedToMinimumLevel">The minimum level for
/// events passed through the sink. Ignored when <paramref name="levelSwitch"/> is specified.</param>
/// <param name="levelSwitch">A switch allowing the pass-through minimum level
/// to be changed at runtime.</param>
/// <returns>Configuration object allowing method chaining.</returns>
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
public static LoggerConfiguration File(
this LoggerAuditSinkConfiguration sinkConfiguration,
ITextFormatter formatter,
string path,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
LoggingLevelSwitch levelSwitch = null)
{
return ConfigureFile(sinkConfiguration.Sink, formatter, path, restrictedToMinimumLevel, null, levelSwitch, false, true);
}

static LoggerConfiguration ConfigureFile(
this Func<ILogEventSink, LogEventLevel, LoggingLevelSwitch, LoggerConfiguration> addSink,
ITextFormatter formatter,
string path,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
long? fileSizeLimitBytes = DefaultFileSizeLimitBytes,
LoggingLevelSwitch levelSwitch = null,
bool buffered = false,
bool propagateExceptions = false,
bool shared = false)
{
if (addSink == null) throw new ArgumentNullException(nameof(addSink));
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
if (path == null) throw new ArgumentNullException(nameof(path));
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0) throw new ArgumentException("Negative value provided; file size limit must be non-negative");

FileSink sink;
try
if (shared)
{
sink = new FileSink(path, formatter, fileSizeLimitBytes, buffered: buffered);
#if !ATOMIC_APPEND
throw new NotSupportedException("File sharing is not supported on this platform.");
#endif

if (buffered)
throw new ArgumentException("Buffered writes are not available when file sharing is enabled.", nameof(buffered));
}
catch (ArgumentException)

ILogEventSink sink;
try
{
throw;
#if ATOMIC_APPEND
if (shared)
{
sink = new SharedFileSink(path, formatter, fileSizeLimitBytes);
}
else
{
#endif
sink = new FileSink(path, formatter, fileSizeLimitBytes, buffered: buffered);
#if ATOMIC_APPEND
}
#endif
}
catch (Exception ex)
{
SelfLog.WriteLine("Unable to open file sink for {0}: {1}", path, ex);
return sinkConfiguration.Sink(new NullSink());

if (propagateExceptions)
throw;

return addSink(new NullSink(), LevelAlias.Maximum, null);
}

return sinkConfiguration.Sink(sink, restrictedToMinimumLevel, levelSwitch);
return addSink(sink, restrictedToMinimumLevel, levelSwitch);
}
}
}

This file was deleted.

Loading