Skip to content
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

FileLifecycleHooks #80

Merged
merged 20 commits into from
Apr 22, 2019
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f7bfe4e
Enabled FileSink and RollingFileSink's output stream in another strea…
cocowalla Feb 1, 2019
9eee731
Rename StreamWrapper to FileLifecycleHooks
cocowalla Feb 14, 2019
83d817f
Ignore Rider cache/options files
cocowalla Feb 14, 2019
63c3601
Add docs re wrapped stream ownership
cocowalla Feb 14, 2019
fca6eb9
Check for directory existence before attempting access.
billrob Mar 12, 2019
0c65484
Merge pull request #88 from billrob/dev
nblumhardt Mar 13, 2019
e604418
Improve log message when FileLifecycleHooks provided for a shared log…
cocowalla Apr 20, 2019
873f6d4
Throw clear exception when wrapping the output stream returns null
cocowalla Apr 20, 2019
e310ab9
Throw when using hooks with a shared file
cocowalla Apr 20, 2019
1864db1
Merge branch 'feature/stream-wrapper' of https://github.com/cocowalla…
nblumhardt Apr 22, 2019
b660b51
Make FileSink constructor changes non-breaking; fix a bug whereby a s…
nblumhardt Apr 22, 2019
b904968
Reenable an old test that was lost
nblumhardt Apr 22, 2019
b6090cc
A test for the encoding fix
nblumhardt Apr 22, 2019
34344ad
Move FileLifecycleHooks down under Serilog.Sinks.File - avoids namesp…
nblumhardt Apr 22, 2019
0ae234e
Enable hooks and encoding for auditing methods
nblumhardt Apr 22, 2019
55fcb2b
Add backwards-compatible configuration overloads
nblumhardt Apr 22, 2019
d4fa80a
Expose encoding to OnOpened() hook; switch to AppVeyor Linux builds
nblumhardt Apr 22, 2019
9f7352d
Fix Linux build script targets
nblumhardt Apr 22, 2019
ca0ac8c
Test for header writing
nblumhardt Apr 22, 2019
5b3d64a
OnOpened() -> OnFileOpened()
nblumhardt Apr 22, 2019
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ bld/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# Rider cache/options directory
.idea

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 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)
# 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)

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

Expand Down
13 changes: 10 additions & 3 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
version: '{build}'
skip_tags: true
image: Visual Studio 2017
image:
- Visual Studio 2017
- Ubuntu
configuration: Release
install:
- ps: mkdir -Force ".\build\" | Out-Null
build_script:
- ps: ./Build.ps1
for:
-
matrix:
only:
- image: Ubuntu
build_script:
- sh build.sh
test: off
artifacts:
- path: artifacts/Serilog.*.nupkg
Expand Down
10 changes: 8 additions & 2 deletions build.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
#!/bin/bash
#!/bin/bash

set -e
dotnet --info
dotnet --list-sdks
dotnet restore

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

echo "🤖 Running tests..."
for path in test/*.Tests/*.csproj; do
dotnet test -f netcoreapp2.0 -c Release ${path}
done
1 change: 0 additions & 1 deletion serilog-sinks-file.sln
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{E9D1B5E1-DEB9-4A04-8BAB-24EC7240ADAF}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.travis.yml = .travis.yml
appveyor.yml = appveyor.yml
Build.ps1 = Build.ps1
build.sh = build.sh
Expand Down
222 changes: 194 additions & 28 deletions src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions src/Serilog.Sinks.File/Sinks/File/FileLifecycleHooks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2019 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.

using System.IO;
using System.Text;

namespace Serilog.Sinks.File
{
/// <summary>
/// Enables hooking into log file lifecycle events.
/// </summary>
public abstract class FileLifecycleHooks
{
/// <summary>
/// Initialize or wrap the <paramref name="underlyingStream"/> opened on the log file. This can be used to write
/// file headers, or wrap the stream in another that adds buffering, compression, encryption, etc. The underlying
/// file may or may not be empty when this method is called.
/// </summary>
/// <remarks>
/// A value must be returned from overrides of this method. Serilog will flush and/or dispose the returned value, but will not
/// dispose the stream initially passed in unless it is itself returned.
/// </remarks>
/// <param name="underlyingStream">The underlying <see cref="Stream"/> opened on the log file.</param>
/// <param name="encoding">The encoding to use when reading/writing to the stream.</param>
/// <returns>The <see cref="Stream"/> Serilog should use when writing events to the log file.</returns>
public virtual Stream OnOpened(Stream underlyingStream, Encoding encoding) => underlyingStream;
}
}
33 changes: 26 additions & 7 deletions src/Serilog.Sinks.File/Sinks/File/FileSink.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2013-2016 Serilog Contributors
// 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.
Expand All @@ -23,7 +23,6 @@ namespace Serilog.Sinks.File
/// <summary>
/// Write log events to a disk file.
/// </summary>
[Obsolete("This type will be removed from the public API in a future version; use `WriteTo.File()` instead.")]
public sealed class FileSink : IFileSink, IDisposable
{
readonly TextWriter _output;
Expand All @@ -44,15 +43,26 @@ public sealed class FileSink : IFileSink, IDisposable
/// <param name="buffered">Indicates if flushing to the output file can be buffered or not. 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>
/// <remarks>This constructor preserves compatibility with early versions of the public API. New code should not depend on this type.</remarks>
/// <exception cref="IOException"></exception>
[Obsolete("This type and constructor will be removed from the public API in a future version; use `WriteTo.File()` instead.")]
public FileSink(string path, ITextFormatter textFormatter, long? fileSizeLimitBytes, Encoding encoding = null, bool buffered = false)
: this(path, textFormatter, fileSizeLimitBytes, encoding, buffered, null)
{
}

// This overload should be used internally; the overload above maintains compatibility with the earlier public API.
internal FileSink(
string path,
ITextFormatter textFormatter,
long? fileSizeLimitBytes,
Encoding encoding,
bool buffered,
FileLifecycleHooks hooks)
{
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;
_textFormatter = textFormatter ?? throw new ArgumentNullException(nameof(textFormatter));
_fileSizeLimitBytes = fileSizeLimitBytes;
_buffered = buffered;

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

_output = new StreamWriter(outputStream, encoding ?? new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
// Parameter reassignment.
encoding = encoding ?? new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);

if (hooks != null)
{
outputStream = hooks.OnOpened(outputStream, encoding) ??
throw new InvalidOperationException($"The file lifecycle hook `{nameof(FileLifecycleHooks.OnOpened)}(...)` returned `null`.");
}

_output = new StreamWriter(outputStream, encoding);
}

bool IFileSink.EmitOrOverflow(LogEvent logEvent)
Expand Down
26 changes: 19 additions & 7 deletions src/Serilog.Sinks.File/Sinks/File/PeriodicFlushToDiskSink.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
using System;
// Copyright 2016-2019 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.

using System;
using System.Threading;
using Serilog.Core;
using Serilog.Debugging;
Expand All @@ -9,6 +23,7 @@ namespace Serilog.Sinks.File
/// <summary>
/// A sink wrapper that periodically flushes the wrapped sink to disk.
/// </summary>
[Obsolete("This type will be removed from the public API in a future version; use `WriteTo.File(flushToDiskInterval:)` instead.")]
public class PeriodicFlushToDiskSink : ILogEventSink, IDisposable
{
readonly ILogEventSink _sink;
Expand All @@ -21,15 +36,12 @@ public class PeriodicFlushToDiskSink : ILogEventSink, IDisposable
/// </summary>
/// <param name="sink">The sink to wrap.</param>
/// <param name="flushInterval">The interval at which to flush the underlying sink.</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentNullException"/>
public PeriodicFlushToDiskSink(ILogEventSink sink, TimeSpan flushInterval)
{
if (sink == null) throw new ArgumentNullException(nameof(sink));

_sink = sink;
_sink = sink ?? throw new ArgumentNullException(nameof(sink));

var flushable = sink as IFlushableFileSink;
if (flushable != null)
if (sink is IFlushableFileSink flushable)
{
_timer = new Timer(_ => FlushToDisk(flushable), null, flushInterval, flushInterval);
}
Expand Down
21 changes: 14 additions & 7 deletions src/Serilog.Sinks.File/Sinks/File/RollingFileSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma warning disable 618

using System;
using System.IO;
using System.Linq;
Expand All @@ -35,6 +33,7 @@ sealed class RollingFileSink : ILogEventSink, IFlushableFileSink, IDisposable
readonly bool _buffered;
readonly bool _shared;
readonly bool _rollOnFileSizeLimit;
readonly FileLifecycleHooks _hooks;

readonly object _syncRoot = new object();
bool _isDisposed;
Expand All @@ -50,11 +49,12 @@ public RollingFileSink(string path,
bool buffered,
bool shared,
RollingInterval rollingInterval,
bool rollOnFileSizeLimit)
bool rollOnFileSizeLimit,
FileLifecycleHooks hooks)
{
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 (retainedFileCountLimit.HasValue && retainedFileCountLimit < 1) throw new ArgumentException("Zero or negative value provided; retained file count limit must be at least 1");
if (fileSizeLimitBytes.HasValue && fileSizeLimitBytes < 0) throw new ArgumentException("Negative value provided; file size limit must be non-negative.");
if (retainedFileCountLimit.HasValue && retainedFileCountLimit < 1) throw new ArgumentException("Zero or negative value provided; retained file count limit must be at least 1.");

_roller = new PathRoller(path, rollingInterval);
_textFormatter = textFormatter;
Expand All @@ -64,6 +64,7 @@ public RollingFileSink(string path,
_buffered = buffered;
_shared = shared;
_rollOnFileSizeLimit = rollOnFileSizeLimit;
_hooks = hooks;
}

public void Emit(LogEvent logEvent)
Expand Down Expand Up @@ -117,8 +118,11 @@ void OpenFile(DateTime now, int? minSequence = null)
var existingFiles = Enumerable.Empty<string>();
try
{
existingFiles = Directory.GetFiles(_roller.LogFileDirectory, _roller.DirectorySearchPattern)
if (Directory.Exists(_roller.LogFileDirectory))
{
existingFiles = Directory.GetFiles(_roller.LogFileDirectory, _roller.DirectorySearchPattern)
.Select(Path.GetFileName);
}
}
catch (DirectoryNotFoundException) { }

Expand All @@ -143,8 +147,11 @@ void OpenFile(DateTime now, int? minSequence = null)
try
{
_currentFile = _shared ?
#pragma warning disable 618
(IFileSink)new SharedFileSink(path, _textFormatter, _fileSizeLimitBytes, _encoding) :
new FileSink(path, _textFormatter, _fileSizeLimitBytes, _encoding, _buffered);
#pragma warning restore 618
new FileSink(path, _textFormatter, _fileSizeLimitBytes, _encoding, _buffered, _hooks);

_currentFileSequence = sequence;
}
catch (IOException ex)
Expand Down
10 changes: 3 additions & 7 deletions src/Serilog.Sinks.File/Sinks/File/SharedFileSink.AtomicAppend.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2013-2016 Serilog Contributors
// Copyright 2013-2019 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -18,7 +18,6 @@
using System.IO;
using System.Security.AccessControl;
using System.Text;
using Serilog.Core;
using Serilog.Events;
using Serilog.Formatting;

Expand Down Expand Up @@ -51,17 +50,14 @@ public sealed class SharedFileSink : IFileSink, IDisposable
/// will be written in full even if it exceeds the limit.</param>
/// <param name="encoding">Character encoding used to write the text file. The default is UTF-8 without BOM.</param>
/// <returns>Configuration object allowing method chaining.</returns>
/// <remarks>The file will be written using the UTF-8 character set.</remarks>
/// <exception cref="IOException"></exception>
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");

_path = path;
_textFormatter = textFormatter;
_path = path ?? throw new ArgumentNullException(nameof(path));
_textFormatter = textFormatter ?? throw new ArgumentNullException(nameof(textFormatter));
_fileSizeLimitBytes = fileSizeLimitBytes;

var directory = Path.GetDirectoryName(path);
Expand Down
8 changes: 3 additions & 5 deletions src/Serilog.Sinks.File/Sinks/File/SharedFileSink.OSMutex.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2013-2016 Serilog Contributors
// Copyright 2013-2019 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -17,7 +17,6 @@
using System;
using System.IO;
using System.Text;
using Serilog.Core;
using Serilog.Events;
using Serilog.Formatting;
using System.Threading;
Expand All @@ -28,6 +27,7 @@ namespace Serilog.Sinks.File
/// <summary>
/// Write log events to a disk file.
/// </summary>
[Obsolete("This type will be removed from the public API in a future version; use `WriteTo.File(shared: true)` instead.")]
public sealed class SharedFileSink : IFileSink, IDisposable
{
readonly TextWriter _output;
Expand All @@ -53,11 +53,9 @@ public sealed class SharedFileSink : IFileSink, IDisposable
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;
_textFormatter = textFormatter ?? throw new ArgumentNullException(nameof(textFormatter));
_fileSizeLimitBytes = fileSizeLimitBytes;

var directory = Path.GetDirectoryName(path);
Expand Down
Loading