Skip to content

Feature/initial attempt #72

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

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f17cb52
initial serilog commit
Oct 10, 2018
fbeb47b
intial comments
Oct 10, 2018
28a52d6
test comment for reference
Oct 12, 2018
360de64
comments made around where to do compression
Oct 12, 2018
11f9662
comments on placement of compression
Oct 12, 2018
834b1b8
location to do compression o previous file
Oct 17, 2018
6a2884f
updates to comments on new method
Oct 17, 2018
1027296
file creation and deletion based off previous log
Oct 17, 2018
1e27cd8
basic zipping of previous log file at roll
Oct 17, 2018
62aca49
added parameters to rolling class, zip type set up in method
Oct 17, 2018
814e17f
paramters set up for compression and compression type
Oct 17, 2018
b2f32cf
GZIP method set up
Oct 17, 2018
67c1153
layout for gzip method set up
Oct 17, 2018
d58afda
gzip attempt
Oct 31, 2018
99f27a1
GZip basic functionality
Oct 31, 2018
9493330
check for uncompressd files and compress
Oct 31, 2018
c259969
remove compression bool
Oct 31, 2018
636b9f2
new Xunit file, error adding
Oct 31, 2018
fddd376
test written for checking compresison, need to delete extra created file
Nov 7, 2018
558fb65
log file still in use
Nov 7, 2018
64411b7
tests running, disposes log object now
Nov 7, 2018
91053dc
moved compression to new file
Nov 12, 2018
0be2657
initial commit of work on remote repo
Nov 12, 2018
14f3440
moved compression type into src
Nov 12, 2018
2d83c64
removed comments from methods
Nov 12, 2018
7489f97
moved compression tests into their own file
Nov 12, 2018
9a06121
added Assert that original .txt file is deleted in compression, roll
Nov 12, 2018
4b5ee31
completed initial PR comments, will resolve the rest on wednesday
Nov 12, 2018
a76b65b
updated for comments I did not have questions on / could implement now
Nov 16, 2018
4ccea1d
intial method to compress file in chunks for GZip
Nov 19, 2018
1325fa3
comments added to gzip method with current functionality
Nov 19, 2018
07b897a
updated gzip compress method to use chunking correctly
Nov 30, 2018
3b90756
add default value for compression type
Dec 3, 2018
5049097
replaced while loop with CopyTo() method
Dec 17, 2018
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
10 changes: 8 additions & 2 deletions example/Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.IO;
using Serilog;
using Serilog.Debugging;
using Serilog.Sinks.File;

namespace Sample
{
Expand All @@ -13,11 +14,15 @@ public static void Main(string[] args)

var sw = System.Diagnostics.Stopwatch.StartNew();

// create a log'name'.txt file every minute
Log.Logger = new LoggerConfiguration()
.WriteTo.File("log.txt")
.WriteTo.File("log.txt", rollingInterval: RollingInterval.Minute, compressionType: CompressionType.GZip)
.CreateLogger();

for (var i = 0; i < 1000000; ++i)
// two minute loop to create two log files
// need to determine when new file is created so compress before new file is seeded
var end = DateTime.UtcNow.AddMinutes(2);
while(DateTime.UtcNow < end)
{
Log.Information("Hello, file logger!");
}
Expand All @@ -28,6 +33,7 @@ public static void Main(string[] args)

Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds} ms");
Console.WriteLine($"Size: {new FileInfo("log.txt").Length}");
Console.WriteLine($"Path: {new FileInfo("log.txt").DirectoryName}");

Console.WriteLine("Press any key to delete the temporary log file...");
Console.ReadKey(true);
Expand Down
12 changes: 6 additions & 6 deletions serilog-sinks-file.sln
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ Global
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{57E0ED0E-0F45-48AB-A73D-6A92B7C32095}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{57E0ED0E-0F45-48AB-A73D-6A92B7C32095}.Debug|Any CPU.Build.0 = Debug|Any CPU
{57E0ED0E-0F45-48AB-A73D-6A92B7C32095}.Release|Any CPU.ActiveCfg = Release|Any CPU
{57E0ED0E-0F45-48AB-A73D-6A92B7C32095}.Release|Any CPU.Build.0 = Release|Any CPU
{57E0ED0E-0F45-48AB-A73D-6A92B7C32095}.Release|Any CPU.ActiveCfg = Debug|Any CPU
{57E0ED0E-0F45-48AB-A73D-6A92B7C32095}.Release|Any CPU.Build.0 = Debug|Any CPU
{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{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
{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Release|Any CPU.ActiveCfg = Debug|Any CPU
{3C2D8E01-5580-426A-BDD9-EC59CD98E618}.Release|Any CPU.Build.0 = Debug|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
{A34235A2-A717-4A1C-BF5C-F4A9E06E1260}.Release|Any CPU.ActiveCfg = Debug|Any CPU
{A34235A2-A717-4A1C-BF5C-F4A9E06E1260}.Release|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
35 changes: 35 additions & 0 deletions src/Serilog.Sinks.File/CompressionType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2017 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.

namespace Serilog
{
/// <summary>
/// Specifies the compression type
/// </summary>
public enum CompressionType
{
/// <summary>
/// Zip compression
/// </summary>
Zip,
/// <summary>
/// RGZip compression
/// </summary>
GZip,
/// <summary>
/// No compression
/// </summary>
None,
};
}
340 changes: 213 additions & 127 deletions src/Serilog.Sinks.File/FileLoggerConfigurationExtensions.cs

Large diffs are not rendered by default.

14 changes: 13 additions & 1 deletion src/Serilog.Sinks.File/Serilog.Sinks.File.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Description>Write Serilog events to text files in plain or JSON format.</Description>
Expand Down Expand Up @@ -27,6 +27,7 @@

<ItemGroup>
<PackageReference Include="Serilog" Version="2.5.0" />
<PackageReference Include="System.IO.Compression" Version="4.1.0" />
</ItemGroup>

<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
Expand All @@ -53,4 +54,15 @@
<PackageReference Include="System.Runtime.InteropServices" Version="4.1.0" />
</ItemGroup>

<ItemGroup>
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.3'">
<PackageReference Include="System.IO.Compression.ZipFile">
<Version>4.0.1</Version>
</PackageReference>
</ItemGroup>

</Project>
107 changes: 104 additions & 3 deletions src/Serilog.Sinks.File/Sinks/File/RollingFileSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ sealed class RollingFileSink : ILogEventSink, IFlushableFileSink, IDisposable
readonly bool _buffered;
readonly bool _shared;
readonly bool _rollOnFileSizeLimit;
readonly CompressionType _compressionType;

readonly object _syncRoot = new object();
bool _isDisposed;
Expand All @@ -50,13 +51,16 @@ public RollingFileSink(string path,
bool buffered,
bool shared,
RollingInterval rollingInterval,
bool rollOnFileSizeLimit)
bool rollOnFileSizeLimit,
CompressionType compressionType = CompressionType.None
)
{
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");

_roller = new PathRoller(path, rollingInterval);
_compressionType = compressionType;
_textFormatter = textFormatter;
_fileSizeLimitBytes = fileSizeLimitBytes;
_retainedFileCountLimit = retainedFileCountLimit;
Expand All @@ -66,6 +70,79 @@ public RollingFileSink(string path,
_rollOnFileSizeLimit = rollOnFileSizeLimit;
}

/// <summary>
/// Chooses which compression algorithm to run on the log file passed in within the given directory.
/// </summary>
/// <param name="logFile"> The log file to be compressed </param>
/// <param name="logDirectory"> The directory that holds logFile </param>
/// <param name="compressionType"> The compression algorithm to be used. Is of type CompressionType </param>
public void Compress(string logFile, string logDirectory, CompressionType compressionType)
{
switch (compressionType)
{
case CompressionType.Zip:
ZipCompress(logFile, logDirectory);
break;
case CompressionType.GZip:
GZipCompress(logFile, logDirectory);
break;
default:
throw new Exception("Compression type entered incorrectly or not supported.\n");
}
}

/// <summary>
/// Uses Zip compression to compress prevLog into a new, zipped directory.
/// The Zip algorithm is from System.IO.Compression
/// </summary>
/// <param name="logFile"> The log file to be compressed </param>
/// <param name="logDirectory"> The directory that holds logFile </param>
public void ZipCompress(string logFile, string logDirectory)
{
var readDirectory = Path.Combine(logDirectory, "new_dir");
Directory.CreateDirectory(readDirectory);
System.IO.File.Move(
Path.Combine(logDirectory, logFile), Path.Combine(readDirectory, logFile)
);

var zipName = Path.GetFileNameWithoutExtension(logFile);
var zip_path = Path.Combine(logDirectory, $"{zipName}.zip");
System.IO.Compression.ZipFile.CreateFromDirectory(readDirectory, zip_path);

Directory.Delete(readDirectory, true);
}

/// <summary>
/// Uses GZip compression algorithm to compress logFile into a .gzip file
/// The GZip algorithm is from System.IO.Compression
/// </summary>
/// <param name="logFile"> The log file to be compressed </param>
/// <param name="logDirectory"> The directory that holds logFile </param>
public void GZipCompress(string logFile, string logDirectory)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why public?

{

var inFile = Path.Combine(logDirectory, logFile);

var logName = Path.GetFileNameWithoutExtension(logFile);
var outFile = Path.Combine(logDirectory, $"{logName}.gz");

using (var outputStream = System.IO.File.Create(outFile))
{
using (var inputStream = System.IO.File.OpenRead(inFile))
{
using (var gzipStream = new System.IO.Compression.GZipStream(outputStream, System.IO.Compression.CompressionMode.Compress))
{
int bufferSize = 1024;
var buffer = new byte[bufferSize];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think buffer is being used?


inputStream.CopyTo(gzipStream, bufferSize);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such a small buffer size is going to result in sub-par compression ratios - what's the purpose of overriding the default buffer size here? (I guess the default is 64K?)

}
}
}

System.IO.File.Delete(inFile);
}

public void Emit(LogEvent logEvent)
{
if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
Expand Down Expand Up @@ -101,7 +178,31 @@ void AlignCurrentFileTo(DateTime now, bool nextSequence = false)
minSequence = _currentFileSequence.Value + 1;
}

var prevLog = Directory.GetFiles(_roller.LogFileDirectory, _roller.DirectorySearchPattern)
.Select(Path.GetFileName).LastOrDefault();
var logDirectory = _roller.LogFileDirectory;

CloseFile();

if (_compressionType != CompressionType.None)
{
Compress(prevLog, logDirectory, _compressionType);

var directoryFiles = Directory.GetFiles(_roller.LogFileDirectory, _roller.DirectorySearchPattern);

foreach (var directoryFile in directoryFiles)
{
var directoryFileName = Path.GetFileName(directoryFile);

// Not sure if we can assume log files will always be .txt?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope 😄

The filename (and extension) are configurable by the user

if (System.IO.Path.GetExtension(directoryFileName) == ".txt")
{
Compress(directoryFileName, logDirectory, _compressionType);
}
}

}

OpenFile(now, minSequence);
}
}
Expand All @@ -110,8 +211,6 @@ void OpenFile(DateTime now, int? minSequence = null)
{
var currentCheckpoint = _roller.GetCurrentCheckpoint(now);

// We only try periodically because repeated failures
// to open log files REALLY slow an app down.
_nextCheckpoint = _roller.GetNextCheckpoint(now) ?? now.AddMinutes(30);

var existingFiles = Enumerable.Empty<string>();
Expand Down Expand Up @@ -213,6 +312,8 @@ public void Dispose()

void CloseFile()
{


if (_currentFile != null)
{
(_currentFile as IDisposable)?.Dispose();
Expand Down
103 changes: 103 additions & 0 deletions test/Serilog.Sinks.File.Tests/RollingCompressionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System.Linq;
using Xunit;
using Serilog.Sinks.File.Tests.Support;
using System.IO;

namespace Serilog.Sinks.File.Tests
{
public class RollingCompressionTests
{
[Fact]
public void GZipCompressionCorrect()
{
var fileName = Some.String() + "log.txt";
var temp = Some.TempFolderPath();
var pathFormat = Path.Combine(temp, fileName);

using (var log = new LoggerConfiguration()
.WriteTo.File(pathFormat,
rollOnFileSizeLimit: true, fileSizeLimitBytes: 1,
compressionType: CompressionType.GZip)
.CreateLogger())
{

log.Information("test");
log.Information("test");

string[] compressedFiles = Directory.EnumerateFiles(temp).Where(name => name.Contains("-GZip")).ToArray();

foreach(var compressedFile in compressedFiles)
{
using (FileStream compressedStream = new FileStream(compressedFile, FileMode.Open))
{
byte[] compressedBytes = new byte[2];

compressedStream.Read(compressedBytes, 0, compressedBytes.Length);

// Magic Bytes for Zip
Assert.Equal(compressedBytes[0], 0x1f);
Assert.Equal(compressedBytes[1], 0x8b);
// fileName is original .txt file name
Assert.False(compressedFiles.Contains(fileName));
}
}

log.Dispose();

foreach (var loggedFile in Directory.GetFiles(temp))
{
System.IO.File.Delete(loggedFile);
}
Directory.Delete(temp);
}

}

[Fact]
public void ZipCompressionCorrect()
{

var fileName = Some.String() + "log.txt";
var temp = Some.TempFolderPath();
var pathFormat = Path.Combine(temp, fileName);

using (var log = new LoggerConfiguration()
.WriteTo.File(pathFormat,
rollOnFileSizeLimit: true, fileSizeLimitBytes: 1,
compressionType: CompressionType.Zip)
.CreateLogger())
{

log.Information("test");
log.Information("test");

string[] compressedFiles = Directory.EnumerateFiles(temp).Where(name => name.Contains("-Zip")).ToArray();

foreach (var compressedFile in compressedFiles)
{
using (FileStream compressedStream = new FileStream(compressedFile, FileMode.Open))
{

byte[] compressedBytes = new byte[2];

compressedStream.Read(compressedBytes, 0, compressedBytes.Length);

// Magic Bytes for Zip
Assert.Equal(compressedBytes[0], 0x50);
Assert.Equal(compressedBytes[1], 0x4B);
// fileName is original .txt file name
Assert.False(compressedFiles.Contains(fileName));
}
}

log.Dispose();

foreach (var loggedFile in Directory.GetFiles(temp))
{
System.IO.File.Delete(loggedFile);
}
Directory.Delete(temp);
}
}
}
}
2 changes: 1 addition & 1 deletion test/Serilog.Sinks.File.Tests/RollingFileSinkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public void IfTheLogFolderDoesNotExistItWillBeCreated()
var pathFormat = Path.Combine(folder, fileName);

ILogger log = null;

try
{
log = new LoggerConfiguration()
Expand Down