Skip to content

Commit

Permalink
Merge pull request #15 from serilog/dev
Browse files Browse the repository at this point in the history
3.0.0 Release
  • Loading branch information
nblumhardt authored Aug 26, 2016
2 parents 8f6975a + ff584d1 commit cb15532
Show file tree
Hide file tree
Showing 15 changed files with 688 additions and 129 deletions.
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

0 comments on commit cb15532

Please sign in to comment.