Skip to content

Commit 36381f6

Browse files
authored
Merge pull request #80 from cocowalla/feature/stream-wrapper
`FileLifecycleHooks`
2 parents 0c65484 + 5b3d64a commit 36381f6

19 files changed

+555
-83
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ bld/
2626
# Uncomment if you have tasks that create the project's static files in wwwroot
2727
#wwwroot/
2828

29+
# Rider cache/options directory
30+
.idea
31+
2932
# MSTest test Results
3033
[Tt]est[Rr]esult*/
3134
[Bb]uild[Ll]og.*

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) [![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)
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)
22

33
Writes [Serilog](https://serilog.net) events to one or more text files.
44

appveyor.yml

+10-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
version: '{build}'
22
skip_tags: true
3-
image: Visual Studio 2017
3+
image:
4+
- Visual Studio 2017
5+
- Ubuntu
46
configuration: Release
5-
install:
6-
- ps: mkdir -Force ".\build\" | Out-Null
77
build_script:
88
- ps: ./Build.ps1
9+
for:
10+
-
11+
matrix:
12+
only:
13+
- image: Ubuntu
14+
build_script:
15+
- sh build.sh
916
test: off
1017
artifacts:
1118
- path: artifacts/Serilog.*.nupkg

build.sh

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
#!/bin/bash
1+
#!/bin/bash
2+
3+
set -e
24
dotnet --info
5+
dotnet --list-sdks
36
dotnet restore
47

8+
echo "🤖 Attempting to build..."
59
for path in src/**/*.csproj; do
6-
dotnet build -f netstandard1.3 -c Release ${path} -p:EnableSourceLink=true
10+
dotnet build -f netstandard1.3 -c Release ${path}
11+
dotnet build -f netstandard2.0 -c Release ${path}
712
done
813

14+
echo "🤖 Running tests..."
915
for path in test/*.Tests/*.csproj; do
1016
dotnet test -f netcoreapp2.0 -c Release ${path}
1117
done

serilog-sinks-file.sln

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ EndProject
88
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{E9D1B5E1-DEB9-4A04-8BAB-24EC7240ADAF}"
99
ProjectSection(SolutionItems) = preProject
1010
.editorconfig = .editorconfig
11-
.travis.yml = .travis.yml
1211
appveyor.yml = appveyor.yml
1312
Build.ps1 = Build.ps1
1413
build.sh = build.sh

src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs

+194-28
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2019 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+
using System.IO;
16+
using System.Text;
17+
18+
namespace Serilog.Sinks.File
19+
{
20+
/// <summary>
21+
/// Enables hooking into log file lifecycle events.
22+
/// </summary>
23+
public abstract class FileLifecycleHooks
24+
{
25+
/// <summary>
26+
/// Initialize or wrap the <paramref name="underlyingStream"/> opened on the log file. This can be used to write
27+
/// file headers, or wrap the stream in another that adds buffering, compression, encryption, etc. The underlying
28+
/// file may or may not be empty when this method is called.
29+
/// </summary>
30+
/// <remarks>
31+
/// A value must be returned from overrides of this method. Serilog will flush and/or dispose the returned value, but will not
32+
/// dispose the stream initially passed in unless it is itself returned.
33+
/// </remarks>
34+
/// <param name="underlyingStream">The underlying <see cref="Stream"/> opened on the log file.</param>
35+
/// <param name="encoding">The encoding to use when reading/writing to the stream.</param>
36+
/// <returns>The <see cref="Stream"/> Serilog should use when writing events to the log file.</returns>
37+
public virtual Stream OnFileOpened(Stream underlyingStream, Encoding encoding) => underlyingStream;
38+
}
39+
}

src/Serilog.Sinks.File/Sinks/File/FileSink.cs

+26-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2013-2016 Serilog Contributors
1+
// Copyright 2013-2016 Serilog Contributors
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@ namespace Serilog.Sinks.File
2323
/// <summary>
2424
/// Write log events to a disk file.
2525
/// </summary>
26-
[Obsolete("This type will be removed from the public API in a future version; use `WriteTo.File()` instead.")]
2726
public sealed class FileSink : IFileSink, IDisposable
2827
{
2928
readonly TextWriter _output;
@@ -44,15 +43,26 @@ public sealed class FileSink : IFileSink, IDisposable
4443
/// <param name="buffered">Indicates if flushing to the output file can be buffered or not. The default
4544
/// is false.</param>
4645
/// <returns>Configuration object allowing method chaining.</returns>
47-
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
46+
/// <remarks>This constructor preserves compatibility with early versions of the public API. New code should not depend on this type.</remarks>
4847
/// <exception cref="IOException"></exception>
48+
[Obsolete("This type and constructor will be removed from the public API in a future version; use `WriteTo.File()` instead.")]
4949
public FileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBytes, Encoding encoding = null, bool buffered = false)
50+
: this(path, textFormatter, fileSizeLimitBytes, encoding, buffered, null)
51+
{
52+
}
53+
54+
// This overload should be used internally; the overload above maintains compatibility with the earlier public API.
55+
internal FileSink(
56+
string path,
57+
ITextFormatter textFormatter,
58+
long? fileSizeLimitBytes,
59+
Encoding encoding,
60+
bool buffered,
61+
FileLifecycleHooks hooks)
5062
{
5163
if (path == null) throw new ArgumentNullException(nameof(path));
52-
if (textFormatter == null) throw new ArgumentNullException(nameof(textFormatter));
5364
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0) throw new ArgumentException("Negative value provided; file size limit must be non-negative.");
54-
55-
_textFormatter = textFormatter;
65+
_textFormatter = textFormatter ?? throw new ArgumentNullException(nameof(textFormatter));
5666
_fileSizeLimitBytes = fileSizeLimitBytes;
5767
_buffered = buffered;
5868

@@ -68,7 +78,16 @@ public FileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBy
6878
outputStream = _countingStreamWrapper = new WriteCountingStream(_underlyingStream);
6979
}
7080

71-
_output = new StreamWriter(outputStream, encoding ?? new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
81+
// Parameter reassignment.
82+
encoding = encoding ?? new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
83+
84+
if (hooks != null)
85+
{
86+
outputStream = hooks.OnFileOpened(outputStream, encoding) ??
87+
throw new InvalidOperationException($"The file lifecycle hook `{nameof(FileLifecycleHooks.OnFileOpened)}(...)` returned `null`.");
88+
}
89+
90+
_output = new StreamWriter(outputStream, encoding);
7291
}
7392

7493
bool IFileSink.EmitOrOverflow(LogEvent logEvent)

src/Serilog.Sinks.File/Sinks/File/PeriodicFlushToDiskSink.cs

+19-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
1-
using System;
1+
// Copyright 2016-2019 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+
using System;
216
using System.Threading;
317
using Serilog.Core;
418
using Serilog.Debugging;
@@ -9,6 +23,7 @@ namespace Serilog.Sinks.File
923
/// <summary>
1024
/// A sink wrapper that periodically flushes the wrapped sink to disk.
1125
/// </summary>
26+
[Obsolete("This type will be removed from the public API in a future version; use `WriteTo.File(flushToDiskInterval:)` instead.")]
1227
public class PeriodicFlushToDiskSink : ILogEventSink, IDisposable
1328
{
1429
readonly ILogEventSink _sink;
@@ -21,15 +36,12 @@ public class PeriodicFlushToDiskSink : ILogEventSink, IDisposable
2136
/// </summary>
2237
/// <param name="sink">The sink to wrap.</param>
2338
/// <param name="flushInterval">The interval at which to flush the underlying sink.</param>
24-
/// <exception cref="ArgumentNullException"></exception>
39+
/// <exception cref="ArgumentNullException"/>
2540
public PeriodicFlushToDiskSink(ILogEventSink sink, TimeSpan flushInterval)
2641
{
27-
if (sink == null) throw new ArgumentNullException(nameof(sink));
28-
29-
_sink = sink;
42+
_sink = sink ?? throw new ArgumentNullException(nameof(sink));
3043

31-
var flushable = sink as IFlushableFileSink;
32-
if (flushable != null)
44+
if (sink is IFlushableFileSink flushable)
3345
{
3446
_timer = new Timer(_ => FlushToDisk(flushable), null, flushInterval, flushInterval);
3547
}

src/Serilog.Sinks.File/Sinks/File/RollingFileSink.cs

+10-6
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
#pragma warning disable 618
16-
1715
using System;
1816
using System.IO;
1917
using System.Linq;
@@ -35,6 +33,7 @@ sealed class RollingFileSink : ILogEventSink, IFlushableFileSink, IDisposable
3533
readonly bool _buffered;
3634
readonly bool _shared;
3735
readonly bool _rollOnFileSizeLimit;
36+
readonly FileLifecycleHooks _hooks;
3837

3938
readonly object _syncRoot = new object();
4039
bool _isDisposed;
@@ -50,11 +49,12 @@ public RollingFileSink(string path,
5049
bool buffered,
5150
bool shared,
5251
RollingInterval rollingInterval,
53-
bool rollOnFileSizeLimit)
52+
bool rollOnFileSizeLimit,
53+
FileLifecycleHooks hooks)
5454
{
5555
if (path == null) throw new ArgumentNullException(nameof(path));
56-
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0) throw new ArgumentException("Negative value provided; file size limit must be non-negative");
57-
if (retainedFileCountLimit.HasValue && retainedFileCountLimit < 1) throw new ArgumentException("Zero or negative value provided; retained file count limit must be at least 1");
56+
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0) throw new ArgumentException("Negative value provided; file size limit must be non-negative.");
57+
if (retainedFileCountLimit.HasValue && retainedFileCountLimit < 1) throw new ArgumentException("Zero or negative value provided; retained file count limit must be at least 1.");
5858

5959
_roller = new PathRoller(path, rollingInterval);
6060
_textFormatter = textFormatter;
@@ -64,6 +64,7 @@ public RollingFileSink(string path,
6464
_buffered = buffered;
6565
_shared = shared;
6666
_rollOnFileSizeLimit = rollOnFileSizeLimit;
67+
_hooks = hooks;
6768
}
6869

6970
public void Emit(LogEvent logEvent)
@@ -146,8 +147,11 @@ void OpenFile(DateTime now, int? minSequence = null)
146147
try
147148
{
148149
_currentFile = _shared ?
150+
#pragma warning disable 618
149151
(IFileSink)new SharedFileSink(path, _textFormatter, _fileSizeLimitBytes, _encoding) :
150-
new FileSink(path, _textFormatter, _fileSizeLimitBytes, _encoding, _buffered);
152+
#pragma warning restore 618
153+
new FileSink(path, _textFormatter, _fileSizeLimitBytes, _encoding, _buffered, _hooks);
154+
151155
_currentFileSequence = sequence;
152156
}
153157
catch (IOException ex)

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

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2013-2016 Serilog Contributors
1+
// Copyright 2013-2019 Serilog Contributors
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@
1818
using System.IO;
1919
using System.Security.AccessControl;
2020
using System.Text;
21-
using Serilog.Core;
2221
using Serilog.Events;
2322
using Serilog.Formatting;
2423

@@ -51,17 +50,14 @@ public sealed class SharedFileSink : IFileSink, IDisposable
5150
/// will be written in full even if it exceeds the limit.</param>
5251
/// <param name="encoding">Character encoding used to write the text file. The default is UTF-8 without BOM.</param>
5352
/// <returns>Configuration object allowing method chaining.</returns>
54-
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
5553
/// <exception cref="IOException"></exception>
5654
public SharedFileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBytes, Encoding encoding = null)
5755
{
58-
if (path == null) throw new ArgumentNullException(nameof(path));
59-
if (textFormatter == null) throw new ArgumentNullException(nameof(textFormatter));
6056
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0)
6157
throw new ArgumentException("Negative value provided; file size limit must be non-negative");
6258

63-
_path = path;
64-
_textFormatter = textFormatter;
59+
_path = path ?? throw new ArgumentNullException(nameof(path));
60+
_textFormatter = textFormatter ?? throw new ArgumentNullException(nameof(textFormatter));
6561
_fileSizeLimitBytes = fileSizeLimitBytes;
6662

6763
var directory = Path.GetDirectoryName(path);

src/Serilog.Sinks.File/Sinks/File/SharedFileSink.OSMutex.cs

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2013-2016 Serilog Contributors
1+
// Copyright 2013-2019 Serilog Contributors
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
1717
using System;
1818
using System.IO;
1919
using System.Text;
20-
using Serilog.Core;
2120
using Serilog.Events;
2221
using Serilog.Formatting;
2322
using System.Threading;
@@ -28,6 +27,7 @@ namespace Serilog.Sinks.File
2827
/// <summary>
2928
/// Write log events to a disk file.
3029
/// </summary>
30+
[Obsolete("This type will be removed from the public API in a future version; use `WriteTo.File(shared: true)` instead.")]
3131
public sealed class SharedFileSink : IFileSink, IDisposable
3232
{
3333
readonly TextWriter _output;
@@ -53,11 +53,9 @@ public sealed class SharedFileSink : IFileSink, IDisposable
5353
public SharedFileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBytes, Encoding encoding = null)
5454
{
5555
if (path == null) throw new ArgumentNullException(nameof(path));
56-
if (textFormatter == null) throw new ArgumentNullException(nameof(textFormatter));
5756
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0)
5857
throw new ArgumentException("Negative value provided; file size limit must be non-negative");
59-
60-
_textFormatter = textFormatter;
58+
_textFormatter = textFormatter ?? throw new ArgumentNullException(nameof(textFormatter));
6159
_fileSizeLimitBytes = fileSizeLimitBytes;
6260

6361
var directory = Path.GetDirectoryName(path);

src/Serilog.Sinks.File/Sinks/File/WriteCountingStream.cs

+8-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2013-2016 Serilog Contributors
1+
// Copyright 2013-2019 Serilog Contributors
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -20,16 +20,14 @@ namespace Serilog.Sinks.File
2020
sealed class WriteCountingStream : Stream
2121
{
2222
readonly Stream _stream;
23-
long _countedLength;
2423

2524
public WriteCountingStream(Stream stream)
2625
{
27-
if (stream == null) throw new ArgumentNullException(nameof(stream));
28-
_stream = stream;
29-
_countedLength = stream.Length;
26+
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
27+
CountedLength = stream.Length;
3028
}
3129

32-
public long CountedLength => _countedLength;
30+
public long CountedLength { get; private set; }
3331

3432
protected override void Dispose(bool disposing)
3533
{
@@ -42,7 +40,7 @@ protected override void Dispose(bool disposing)
4240
public override void Write(byte[] buffer, int offset, int count)
4341
{
4442
_stream.Write(buffer, offset, count);
45-
_countedLength += count;
43+
CountedLength += count;
4644
}
4745

4846
public override void Flush() => _stream.Flush();
@@ -54,8 +52,8 @@ public override void Write(byte[] buffer, int offset, int count)
5452

5553
public override long Position
5654
{
57-
get { return _stream.Position; }
58-
set { throw new NotSupportedException(); }
55+
get => _stream.Position;
56+
set => throw new NotSupportedException();
5957
}
6058

6159
public override long Seek(long offset, SeekOrigin origin)
@@ -73,4 +71,4 @@ public override int Read(byte[] buffer, int offset, int count)
7371
throw new NotSupportedException();
7472
}
7573
}
76-
}
74+
}

0 commit comments

Comments
 (0)