Skip to content

Commit c8418ed

Browse files
authored
Merge pull request #30 from serilog/dev
3.2.0 Release
2 parents 7be22d6 + 8c866ab commit c8418ed

15 files changed

+258
-36
lines changed

.editorconfig

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
1-
root=true
1+
root = true
22

33
[*]
4+
trim_trailing_whitespace = true
5+
insert_final_newline = true
46
indent_style = space
57
indent_size = 4
8+
9+
[*.{csproj,json,config,yml}]
10+
indent_size = 2
11+
12+
[*.sh]
13+
end_of_line = lf
14+
15+
[*.{cmd, bat}]
16+
end_of_line = crlf

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,4 @@ _Pvt_Extensions
234234

235235
# FAKE - F# Make
236236
.fake/
237+
example/Sample/log.txt

.travis.yml

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
language: csharp
2+
3+
#dotnet cli require Ubuntu 14.04
4+
sudo: required
5+
dist: trusty
6+
7+
#dotnet cli require OSX 10.10
8+
osx_image: xcode7.1
9+
10+
addons:
11+
apt:
12+
packages:
13+
- gettext
14+
- libcurl4-openssl-dev
15+
- libicu-dev
16+
- libssl-dev
17+
- libunwind8
18+
- zlib1g
19+
20+
os:
21+
- linux
22+
23+
env:
24+
global:
25+
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
26+
- TMP: /tmp
27+
28+
matrix:
29+
- CLI_VERSION=1.0.0-preview2-003121
30+
- CLI_VERSION=Latest
31+
32+
matrix:
33+
allow_failures:
34+
- env: CLI_VERSION=Latest
35+
36+
before_install:
37+
- if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; brew link --force openssl; fi
38+
# Download script to install dotnet cli
39+
- if test "$CLI_OBTAIN_URL" == ""; then export CLI_OBTAIN_URL="https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0-preview2/scripts/obtain/dotnet-install.sh"; fi
40+
- curl -L --create-dirs $CLI_OBTAIN_URL -o ./scripts/obtain/install.sh
41+
- find ./scripts -name "*.sh" -exec chmod +x {} \;
42+
- export DOTNET_INSTALL_DIR="$PWD/.dotnetcli"
43+
# use bash to workaround bug https://github.com/dotnet/cli/issues/1725
44+
- sudo bash ./scripts/obtain/install.sh --channel "preview" --version "$CLI_VERSION" --install-dir "$DOTNET_INSTALL_DIR" --no-path
45+
# add dotnet to PATH
46+
- export PATH="$DOTNET_INSTALL_DIR:$PATH"
47+
48+
script:
49+
- ./build.sh

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Serilog.Sinks.File [![Build status](https://ci.appveyor.com/api/projects/status/hh9gymy0n6tne46j?svg=true)](https://ci.appveyor.com/project/serilog/serilog-sinks-file) [![NuGet Version](http://img.shields.io/nuget/v/Serilog.Sinks.File.svg?style=flat)](https://www.nuget.org/packages/Serilog.Sinks.File/) [![Documentation](https://img.shields.io/badge/docs-wiki-yellow.svg)](https://github.com/serilog/serilog/wiki) [![Join the chat at https://gitter.im/serilog/serilog](https://img.shields.io/gitter/room/serilog/serilog.svg)](https://gitter.im/serilog/serilog)
1+
# Serilog.Sinks.File [![Build status](https://ci.appveyor.com/api/projects/status/hh9gymy0n6tne46j?svg=true)](https://ci.appveyor.com/project/serilog/serilog-sinks-file) [![Travis build](https://travis-ci.org/serilog/serilog-sinks-file.svg)](https://travis-ci.org/serilog/serilog-sinks-file) [![NuGet Version](http://img.shields.io/nuget/v/Serilog.Sinks.File.svg?style=flat)](https://www.nuget.org/packages/Serilog.Sinks.File/) [![Documentation](https://img.shields.io/badge/docs-wiki-yellow.svg)](https://github.com/serilog/serilog/wiki) [![Join the chat at https://gitter.im/serilog/serilog](https://img.shields.io/gitter/room/serilog/serilog.svg)](https://gitter.im/serilog/serilog)
22

33
Writes [Serilog](https://serilog.net) events to a text file.
44

build.sh

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/bash
2+
dotnet restore --no-cache
3+
for path in src/*/project.json; do
4+
dirname="$(dirname "${path}")"
5+
dotnet build ${dirname} -f netstandard1.3 -c Release
6+
done
7+
8+
for path in test/*.Tests/project.json; do
9+
dirname="$(dirname "${path}")"
10+
dotnet build ${dirname} -f netcoreapp1.0 -c Release
11+
dotnet test ${dirname} -f netcoreapp1.0 -c Release
12+
done

example/Sample/Program.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ public static void Main(string[] args)
1111
{
1212
SelfLog.Enable(Console.Out);
1313

14+
var sw = System.Diagnostics.Stopwatch.StartNew();
15+
1416
Log.Logger = new LoggerConfiguration()
1517
.WriteTo.File("log.txt")
1618
.CreateLogger();
1719

18-
var sw = System.Diagnostics.Stopwatch.StartNew();
19-
2020
for (var i = 0; i < 1000000; ++i)
2121
{
2222
Log.Information("Hello, file logger!");

serilog-sinks-file.sln

+3
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{037440DE-440
77
EndProject
88
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{E9D1B5E1-DEB9-4A04-8BAB-24EC7240ADAF}"
99
ProjectSection(SolutionItems) = preProject
10+
.editorconfig = .editorconfig
11+
.travis.yml = .travis.yml
1012
appveyor.yml = appveyor.yml
1113
Build.ps1 = Build.ps1
14+
build.sh = build.sh
1215
global.json = global.json
1316
NuGet.Config = NuGet.Config
1417
README.md = README.md

src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs

+2-14
Original file line numberDiff line numberDiff line change
@@ -180,32 +180,20 @@ static LoggerConfiguration ConfigureFile(
180180
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
181181
if (path == null) throw new ArgumentNullException(nameof(path));
182182
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0) throw new ArgumentException("Negative value provided; file size limit must be non-negative");
183-
184-
if (shared)
185-
{
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
192-
}
183+
if (shared && buffered)
184+
throw new ArgumentException("Buffered writes are not available when file sharing is enabled.", nameof(buffered));
193185

194186
ILogEventSink sink;
195187
try
196188
{
197-
#if ATOMIC_APPEND
198189
if (shared)
199190
{
200191
sink = new SharedFileSink(path, formatter, fileSizeLimitBytes);
201192
}
202193
else
203194
{
204-
#endif
205195
sink = new FileSink(path, formatter, fileSizeLimitBytes, buffered: buffered);
206-
#if ATOMIC_APPEND
207196
}
208-
#endif
209197
}
210198
catch (Exception ex)
211199
{

src/Serilog.Sinks.File/Sinks/File/SharedFileSink.cs renamed to src/Serilog.Sinks.File/Sinks/File/SharedFileSink.AtomicAppend.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ public sealed class SharedFileSink : ILogEventSink, IFlushableFileSink, IDisposa
5252
/// <returns>Configuration object allowing method chaining.</returns>
5353
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
5454
/// <exception cref="IOException"></exception>
55-
public SharedFileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBytes,
56-
Encoding encoding = null)
55+
public SharedFileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBytes, Encoding encoding = null)
5756
{
5857
if (path == null) throw new ArgumentNullException(nameof(path));
5958
if (textFormatter == null) throw new ArgumentNullException(nameof(textFormatter));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// Copyright 2013-2016 Serilog Contributors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#if OS_MUTEX
16+
17+
using System;
18+
using System.IO;
19+
using System.Text;
20+
using Serilog.Core;
21+
using Serilog.Events;
22+
using Serilog.Formatting;
23+
using System.Threading;
24+
using Serilog.Debugging;
25+
26+
namespace Serilog.Sinks.File
27+
{
28+
/// <summary>
29+
/// Write log events to a disk file.
30+
/// </summary>
31+
public sealed class SharedFileSink : ILogEventSink, IFlushableFileSink, IDisposable
32+
{
33+
readonly TextWriter _output;
34+
readonly FileStream _underlyingStream;
35+
readonly ITextFormatter _textFormatter;
36+
readonly long? _fileSizeLimitBytes;
37+
readonly object _syncRoot = new object();
38+
39+
const string MutexNameSuffix = ".serilog";
40+
const int MutexWaitTimeout = 10000;
41+
readonly Mutex _mutex;
42+
43+
/// <summary>Construct a <see cref="FileSink"/>.</summary>
44+
/// <param name="path">Path to the file.</param>
45+
/// <param name="textFormatter">Formatter used to convert log events to text.</param>
46+
/// <param name="fileSizeLimitBytes">The approximate maximum size, in bytes, to which a log file will be allowed to grow.
47+
/// For unrestricted growth, pass null. The default is 1 GB. To avoid writing partial events, the last event within the limit
48+
/// will be written in full even if it exceeds the limit.</param>
49+
/// <param name="encoding">Character encoding used to write the text file. The default is UTF-8 without BOM.</param>
50+
/// <returns>Configuration object allowing method chaining.</returns>
51+
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
52+
/// <exception cref="IOException"></exception>
53+
public SharedFileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBytes, Encoding encoding = null)
54+
{
55+
if (path == null) throw new ArgumentNullException(nameof(path));
56+
if (textFormatter == null) throw new ArgumentNullException(nameof(textFormatter));
57+
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0)
58+
throw new ArgumentException("Negative value provided; file size limit must be non-negative");
59+
60+
_textFormatter = textFormatter;
61+
_fileSizeLimitBytes = fileSizeLimitBytes;
62+
63+
var directory = Path.GetDirectoryName(path);
64+
if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory))
65+
{
66+
Directory.CreateDirectory(directory);
67+
}
68+
69+
var mutexName = Path.GetFullPath(path).Replace(Path.DirectorySeparatorChar, ':') + MutexNameSuffix;
70+
_mutex = new Mutex(false, mutexName);
71+
_underlyingStream = System.IO.File.Open(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
72+
_output = new StreamWriter(_underlyingStream, encoding ?? new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
73+
}
74+
75+
/// <summary>
76+
/// Emit the provided log event to the sink.
77+
/// </summary>
78+
/// <param name="logEvent">The log event to write.</param>
79+
public void Emit(LogEvent logEvent)
80+
{
81+
if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
82+
83+
lock (_syncRoot)
84+
{
85+
if (!TryAcquireMutex())
86+
return;
87+
88+
try
89+
{
90+
_underlyingStream.Seek(0, SeekOrigin.End);
91+
if (_fileSizeLimitBytes != null)
92+
{
93+
if (_underlyingStream.Length >= _fileSizeLimitBytes.Value)
94+
return;
95+
}
96+
97+
_textFormatter.Format(logEvent, _output);
98+
_output.Flush();
99+
_underlyingStream.Flush();
100+
}
101+
finally
102+
{
103+
ReleaseMutex();
104+
}
105+
}
106+
}
107+
108+
/// <inheritdoc />
109+
public void Dispose()
110+
{
111+
lock (_syncRoot)
112+
{
113+
_output.Dispose();
114+
_mutex.Dispose();
115+
}
116+
}
117+
118+
/// <inheritdoc />
119+
public void FlushToDisk()
120+
{
121+
lock (_syncRoot)
122+
{
123+
if (!TryAcquireMutex())
124+
return;
125+
126+
try
127+
{
128+
_underlyingStream.Flush(true);
129+
}
130+
finally
131+
{
132+
ReleaseMutex();
133+
}
134+
}
135+
}
136+
137+
bool TryAcquireMutex()
138+
{
139+
try
140+
{
141+
if (!_mutex.WaitOne(MutexWaitTimeout))
142+
{
143+
SelfLog.WriteLine("Shared file mutex could not be acquired within {0} ms", MutexWaitTimeout);
144+
return false;
145+
}
146+
}
147+
catch (AbandonedMutexException)
148+
{
149+
SelfLog.WriteLine("Inherited shared file mutex after abandonment by another process");
150+
}
151+
152+
return true;
153+
}
154+
155+
void ReleaseMutex()
156+
{
157+
_mutex.ReleaseMutex();
158+
}
159+
}
160+
}
161+
162+
#endif

src/Serilog.Sinks.File/project.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "3.1.1-*",
2+
"version": "3.2.0-*",
33
"description": "Write Serilog events to a text file in plain or JSON format.",
44
"authors": [ "Serilog Contributors" ],
55
"packOptions": {
@@ -20,12 +20,14 @@
2020
"buildOptions": { "define": [ "ATOMIC_APPEND" ] }
2121
},
2222
"netstandard1.3": {
23+
"buildOptions": { "define": [ "OS_MUTEX" ] },
2324
"dependencies": {
2425
"System.IO": "4.1.0",
2526
"System.IO.FileSystem": "4.0.1",
2627
"System.IO.FileSystem.Primitives": "4.0.1",
2728
"System.Text.Encoding.Extensions": "4.0.11",
28-
"System.Threading.Timer": "4.0.1"
29+
"System.Threading.Timer": "4.0.1",
30+
"System.Threading": "4.0.11"
2931
}
3032
}
3133
}

test/Serilog.Sinks.File.Tests/FileLoggerConfigurationExtensionsTests.cs

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
using System;
22
using System.Threading;
3-
using Serilog;
43
using Serilog.Sinks.File.Tests.Support;
54
using Serilog.Tests.Support;
65
using Xunit;
6+
using System.IO;
77

88
namespace Serilog.Tests
99
{
1010
public class FileLoggerConfigurationExtensionsTests
1111
{
12-
const string InvalidPath = "/\\";
12+
static readonly string InvalidPath = new string(Path.GetInvalidPathChars());
1313

1414
[Fact]
1515
public void WhenWritingCreationExceptionsAreSuppressed()
@@ -66,7 +66,6 @@ public void WhenFlushingToDiskReportedFileSinkCanBeCreatedAndDisposed()
6666
}
6767
}
6868

69-
#if ATOMIC_APPEND
7069
[Fact]
7170
public void WhenFlushingToDiskReportedSharedFileSinkCanBeCreatedAndDisposed()
7271
{
@@ -79,6 +78,5 @@ public void WhenFlushingToDiskReportedSharedFileSinkCanBeCreatedAndDisposed()
7978
Thread.Sleep(TimeSpan.FromSeconds(1));
8079
}
8180
}
82-
#endif
8381
}
8482
}

test/Serilog.Sinks.File.Tests/SharedFileSinkTests.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
#if ATOMIC_APPEND
2-
3-
using System.IO;
1+
using System.IO;
42
using Xunit;
53
using Serilog.Formatting.Json;
64
using Serilog.Sinks.File.Tests.Support;
@@ -102,5 +100,3 @@ public void WhenLimitIsNotSpecifiedFileSizeIsNotRestricted()
102100
}
103101
}
104102
}
105-
106-
#endif

0 commit comments

Comments
 (0)