diff --git a/.gitignore b/.gitignore
index 94420dc..483adfa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -234,3 +234,4 @@ _Pvt_Extensions
# FAKE - F# Make
.fake/
+example/Sample/log.txt
diff --git a/example/Sample/Program.cs b/example/Sample/Program.cs
index 99a158b..89e6c95 100644
--- a/example/Sample/Program.cs
+++ b/example/Sample/Program.cs
@@ -11,12 +11,12 @@ public static void Main(string[] args)
{
SelfLog.Enable(Console.Out);
+ var sw = System.Diagnostics.Stopwatch.StartNew();
+
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!");
diff --git a/src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs b/src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs
index 571f414..353e6c6 100644
--- a/src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs
+++ b/src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs
@@ -180,32 +180,20 @@ static LoggerConfiguration ConfigureFile(
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");
-
- if (shared)
- {
-#if ATOMIC_APPEND
- if (buffered)
- throw new ArgumentException("Buffered writes are not available when file sharing is enabled.", nameof(buffered));
-#else
- throw new NotSupportedException("File sharing is not supported on this platform.");
-#endif
- }
+ if (shared && buffered)
+ throw new ArgumentException("Buffered writes are not available when file sharing is enabled.", nameof(buffered));
ILogEventSink sink;
try
{
-#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)
{
diff --git a/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.cs b/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.AtomicAppend.cs
similarity index 98%
rename from src/Serilog.Sinks.File/Sinks/File/SharedFileSink.cs
rename to src/Serilog.Sinks.File/Sinks/File/SharedFileSink.AtomicAppend.cs
index bbb5142..4ea5022 100644
--- a/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.cs
+++ b/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.AtomicAppend.cs
@@ -52,8 +52,7 @@ public sealed class SharedFileSink : ILogEventSink, IFlushableFileSink, IDisposa
/// Configuration object allowing method chaining.
/// The file will be written using the UTF-8 character set.
///
- public SharedFileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBytes,
- Encoding encoding = null)
+ public SharedFileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBytes, Encoding encoding = null)
{
if (path == null) throw new ArgumentNullException(nameof(path));
if (textFormatter == null) throw new ArgumentNullException(nameof(textFormatter));
diff --git a/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.OSMutex.cs b/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.OSMutex.cs
new file mode 100644
index 0000000..d3cf809
--- /dev/null
+++ b/src/Serilog.Sinks.File/Sinks/File/SharedFileSink.OSMutex.cs
@@ -0,0 +1,162 @@
+// Copyright 2013-2016 Serilog Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#if OS_MUTEX
+
+using System;
+using System.IO;
+using System.Text;
+using Serilog.Core;
+using Serilog.Events;
+using Serilog.Formatting;
+using System.Threading;
+using Serilog.Debugging;
+
+namespace Serilog.Sinks.File
+{
+ ///
+ /// Write log events to a disk file.
+ ///
+ public sealed class SharedFileSink : ILogEventSink, IFlushableFileSink, IDisposable
+ {
+ readonly TextWriter _output;
+ readonly FileStream _underlyingStream;
+ readonly ITextFormatter _textFormatter;
+ readonly long? _fileSizeLimitBytes;
+ readonly object _syncRoot = new object();
+
+ const string MutexNameSuffix = ".serilog";
+ const int MutexWaitTimeout = 10000;
+ readonly Mutex _mutex;
+
+ /// Construct a .
+ /// Path to the file.
+ /// Formatter used to convert log events to text.
+ /// 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.
+ /// Character encoding used to write the text file. The default is UTF-8 without BOM.
+ /// Configuration object allowing method chaining.
+ /// The file will be written using the UTF-8 character set.
+ ///
+ public SharedFileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBytes, Encoding encoding = null)
+ {
+ if (path == null) throw new ArgumentNullException(nameof(path));
+ if (textFormatter == null) throw new ArgumentNullException(nameof(textFormatter));
+ if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0)
+ throw new ArgumentException("Negative value provided; file size limit must be non-negative");
+
+ _textFormatter = textFormatter;
+ _fileSizeLimitBytes = fileSizeLimitBytes;
+
+ var directory = Path.GetDirectoryName(path);
+ if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory))
+ {
+ Directory.CreateDirectory(directory);
+ }
+
+ var mutexName = Path.GetFullPath(path).Replace(Path.DirectorySeparatorChar, ':') + MutexNameSuffix;
+ _mutex = new Mutex(false, mutexName);
+ _underlyingStream = System.IO.File.Open(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
+ _output = new StreamWriter(_underlyingStream, encoding ?? new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
+ }
+
+ ///
+ /// Emit the provided log event to the sink.
+ ///
+ /// The log event to write.
+ public void Emit(LogEvent logEvent)
+ {
+ if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
+
+ lock (_syncRoot)
+ {
+ if (!TryAcquireMutex())
+ return;
+
+ try
+ {
+ _underlyingStream.Seek(0, SeekOrigin.End);
+ if (_fileSizeLimitBytes != null)
+ {
+ if (_underlyingStream.Length >= _fileSizeLimitBytes.Value)
+ return;
+ }
+
+ _textFormatter.Format(logEvent, _output);
+ _output.Flush();
+ _underlyingStream.Flush();
+ }
+ finally
+ {
+ ReleaseMutex();
+ }
+ }
+ }
+
+ ///
+ public void Dispose()
+ {
+ lock (_syncRoot)
+ {
+ _output.Dispose();
+ _mutex.Dispose();
+ }
+ }
+
+ ///
+ public void FlushToDisk()
+ {
+ lock (_syncRoot)
+ {
+ if (!TryAcquireMutex())
+ return;
+
+ try
+ {
+ _underlyingStream.Flush(true);
+ }
+ finally
+ {
+ ReleaseMutex();
+ }
+ }
+ }
+
+ bool TryAcquireMutex()
+ {
+ try
+ {
+ if (!_mutex.WaitOne(MutexWaitTimeout))
+ {
+ SelfLog.WriteLine("Shared file mutex could not be acquired within {0} ms", MutexWaitTimeout);
+ return false;
+ }
+ }
+ catch (AbandonedMutexException)
+ {
+ SelfLog.WriteLine("Inherited shared file mutex after abandonment by another process");
+ }
+
+ return true;
+ }
+
+ void ReleaseMutex()
+ {
+ _mutex.ReleaseMutex();
+ }
+ }
+}
+
+#endif
diff --git a/src/Serilog.Sinks.File/project.json b/src/Serilog.Sinks.File/project.json
index 8adf2e2..73378e9 100644
--- a/src/Serilog.Sinks.File/project.json
+++ b/src/Serilog.Sinks.File/project.json
@@ -20,12 +20,14 @@
"buildOptions": { "define": [ "ATOMIC_APPEND" ] }
},
"netstandard1.3": {
+ "buildOptions": { "define": [ "OS_MUTEX" ] },
"dependencies": {
"System.IO": "4.1.0",
"System.IO.FileSystem": "4.0.1",
"System.IO.FileSystem.Primitives": "4.0.1",
"System.Text.Encoding.Extensions": "4.0.11",
- "System.Threading.Timer": "4.0.1"
+ "System.Threading.Timer": "4.0.1",
+ "System.Threading": "4.0.11"
}
}
}
diff --git a/test/Serilog.Sinks.File.Tests/SharedFileSinkTests.cs b/test/Serilog.Sinks.File.Tests/SharedFileSinkTests.cs
index d0bd751..f63eac1 100644
--- a/test/Serilog.Sinks.File.Tests/SharedFileSinkTests.cs
+++ b/test/Serilog.Sinks.File.Tests/SharedFileSinkTests.cs
@@ -1,6 +1,4 @@
-#if ATOMIC_APPEND
-
-using System.IO;
+using System.IO;
using Xunit;
using Serilog.Formatting.Json;
using Serilog.Sinks.File.Tests.Support;
@@ -102,5 +100,3 @@ public void WhenLimitIsNotSpecifiedFileSizeIsNotRestricted()
}
}
}
-
-#endif
diff --git a/test/Serilog.Sinks.File.Tests/project.json b/test/Serilog.Sinks.File.Tests/project.json
index 1fa084d..3f14b0e 100644
--- a/test/Serilog.Sinks.File.Tests/project.json
+++ b/test/Serilog.Sinks.File.Tests/project.json
@@ -19,9 +19,6 @@
]
},
"net4.5.2": {
- "buildOptions": {
- "define": ["ATOMIC_APPEND"]
- }
}
}
}