Skip to content

Commit

Permalink
Merge pull request #35 from serilog/dev
Browse files Browse the repository at this point in the history
3.2.0 Release
  • Loading branch information
nblumhardt authored Oct 25, 2016
2 parents 1ec387e + 1c88099 commit 983fb02
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 82 deletions.
5 changes: 2 additions & 3 deletions src/Serilog.Sinks.RollingFile/Serilog.Sinks.RollingFile.xproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>

<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>a3e6e5b4-995f-4c3d-9673-a4b6000f4e21</ProjectGuid>
<RootNamespace>Serilog.Sinks.RollingFile</RootNamespace>
<RootNamespace>Serilog</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>
31 changes: 31 additions & 0 deletions src/Serilog.Sinks.RollingFile/Sinks/RollingFile/IOErrors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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.

using System.IO;

namespace Serilog.Sinks.RollingFile
{
static class IOErrors
{
public static bool IsLockedFile(IOException ex)
{
#if HRESULTS
var errorCode = System.Runtime.InteropServices.Marshal.GetHRForException(ex) & ((1 << 16) - 1);
return errorCode == 32 || errorCode == 33;
#else
return true;
#endif
}
}
}
19 changes: 8 additions & 11 deletions src/Serilog.Sinks.RollingFile/Sinks/RollingFile/RollingFileSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.


using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Serilog.Core;
using Serilog.Debugging;
Expand Down Expand Up @@ -100,7 +98,7 @@ public void Emit(LogEvent logEvent)

lock (_syncRoot)
{
if (_isDisposed) throw new ObjectDisposedException("The rolling file has been disposed.");
if (_isDisposed) throw new ObjectDisposedException("The rolling log file has been disposed.");

AlignCurrentFileTo(Clock.DateTimeNow);

Expand All @@ -126,11 +124,11 @@ void AlignCurrentFileTo(DateTime now)

void OpenFile(DateTime now)
{
var date = now.Date;
var currentCheckpoint = _roller.GetCurrentCheckpoint(now);

// We only take one attempt at it because repeated failures
// to open log files REALLY slow an app down.
_nextCheckpoint = date.AddDays(1);
_nextCheckpoint = _roller.GetNextCheckpoint(now);

var existingFiles = Enumerable.Empty<string>();
try
Expand All @@ -140,13 +138,13 @@ void OpenFile(DateTime now)
}
catch (DirectoryNotFoundException) { }

var latestForThisDate = _roller
var latestForThisCheckpoint = _roller
.SelectMatches(existingFiles)
.Where(m => m.Date == date)
.Where(m => m.DateTime == currentCheckpoint)
.OrderByDescending(m => m.SequenceNumber)
.FirstOrDefault();

var sequence = latestForThisDate != null ? latestForThisDate.SequenceNumber : 0;
var sequence = latestForThisCheckpoint != null ? latestForThisCheckpoint.SequenceNumber : 0;

const int maxAttempts = 3;
for (var attempt = 0; attempt < maxAttempts; attempt++)
Expand All @@ -166,8 +164,7 @@ void OpenFile(DateTime now)
}
catch (IOException ex)
{
var errorCode = Marshal.GetHRForException(ex) & ((1 << 16) - 1);
if (errorCode == 32 || errorCode == 33)
if (IOErrors.IsLockedFile(ex))
{
SelfLog.WriteLine("Rolling file target {0} was locked, attempting to open next in sequence (attempt {1})", path, attempt + 1);
sequence++;
Expand Down Expand Up @@ -196,7 +193,7 @@ void ApplyRetentionPolicy(string currentFilePath)

var newestFirst = _roller
.SelectMatches(potentialMatches)
.OrderByDescending(m => m.Date)
.OrderByDescending(m => m.DateTime)
.ThenByDescending(m => m.SequenceNumber)
.Select(m => m.Filename);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ namespace Serilog.Sinks.RollingFile
{
class RollingLogFile
{
public RollingLogFile(string filename, DateTime date, int sequenceNumber)
public RollingLogFile(string filename, DateTime dateTime, int sequenceNumber)
{
Filename = filename;
Date = date;
DateTime = dateTime;
SequenceNumber = sequenceNumber;
}

public string Filename { get; }

public DateTime Date { get; }
public DateTime DateTime { get; }

public int SequenceNumber { get; }
}
Expand Down
73 changes: 73 additions & 0 deletions src/Serilog.Sinks.RollingFile/Sinks/RollingFile/Specifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 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.

using System;
using System.Linq;

namespace Serilog.Sinks.RollingFile
{
class Specifier
{
public static readonly Specifier Date = new Specifier("Date", "yyyyMMdd", TimeSpan.FromDays(1));
public static readonly Specifier Hour = new Specifier("Hour", "yyyyMMddHH", TimeSpan.FromHours(1));
public static readonly Specifier HalfHour = new Specifier("HalfHour", "yyyyMMddHHmm", TimeSpan.FromMinutes(30));

public string Token { get; }
public string Format { get; }
public TimeSpan Interval { get; }

Specifier(string name, string format, TimeSpan interval)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (format == null) throw new ArgumentNullException(nameof(format));

Token = "{" + name + "}";
Format = format;
Interval = interval;
}

public DateTime GetCurrentCheckpoint(DateTime instant)
{
if (this == Hour)
{
return instant.Date.AddHours(instant.Hour);
}

if (this == HalfHour)
{
var hour = instant.Date.AddHours(instant.Hour);
if (instant.Minute >= 30)
return hour.AddMinutes(30);
return hour;
}

return instant.Date;
}

public DateTime GetNextCheckpoint(DateTime instant) => GetCurrentCheckpoint(instant).Add(Interval);

public static bool TryGetSpecifier(string pathTemplate, out Specifier specifier)
{
if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate));

var specifiers = new[] { HalfHour, Hour, Date }.Where(s => pathTemplate.Contains(s.Token)).ToArray();

if (specifiers.Length > 1)
throw new ArgumentException("Only one interval specifier can be used in a rolling log file path.", nameof(pathTemplate));

specifier = specifiers.FirstOrDefault();
return specifier != null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// 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.Collections.Generic;
using System.Globalization;
Expand All @@ -25,52 +25,50 @@ namespace Serilog.Sinks.RollingFile
// Logs/log-{Date}.txt
//
class TemplatedPathRoller
{
const string OldStyleDateSpecifier = "{0}";
const string DateSpecifier = "{Date}";
const string DateFormat = "yyyyMMdd";
{
const string DefaultSeparator = "-";

const string SpecifierMatchGroup = "specifier";
const string SequenceNumberMatchGroup = "sequence";

readonly string _pathTemplate;
readonly Regex _filenameMatcher;
readonly Specifier _specifier = null;

public TemplatedPathRoller(string pathTemplate)
{
if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate));
if (pathTemplate.Contains(OldStyleDateSpecifier))
throw new ArgumentException("The old-style date specifier " + OldStyleDateSpecifier +
" is no longer supported, instead please use " + DateSpecifier);

var directory = Path.GetDirectoryName(pathTemplate);
if (string.IsNullOrEmpty(directory))
{
directory = Directory.GetCurrentDirectory();
}

directory = Path.GetFullPath(directory);
Specifier directorySpecifier;
if (Specifier.TryGetSpecifier(directory, out directorySpecifier))
throw new ArgumentException($"The {directorySpecifier.Token} specifier cannot form part of the directory name.");

if (directory.Contains(DateSpecifier))
throw new ArgumentException("The date cannot form part of the directory name");
directory = Path.GetFullPath(directory);

var filenameTemplate = Path.GetFileName(pathTemplate);
if (!filenameTemplate.Contains(DateSpecifier))
if (!Specifier.TryGetSpecifier(filenameTemplate, out _specifier))
{
_specifier = Specifier.Date;
filenameTemplate = Path.GetFileNameWithoutExtension(filenameTemplate) + DefaultSeparator +
DateSpecifier + Path.GetExtension(filenameTemplate);
_specifier.Token + Path.GetExtension(filenameTemplate);
}

var indexOfSpecifier = filenameTemplate.IndexOf(DateSpecifier, StringComparison.Ordinal);
var indexOfSpecifier = filenameTemplate.IndexOf(_specifier.Token, StringComparison.Ordinal);
var prefix = filenameTemplate.Substring(0, indexOfSpecifier);
var suffix = filenameTemplate.Substring(indexOfSpecifier + DateSpecifier.Length);
var suffix = filenameTemplate.Substring(indexOfSpecifier + _specifier.Token.Length);
_filenameMatcher = new Regex(
"^" +
Regex.Escape(prefix) +
"(?<date>\\d{" + DateFormat.Length + "})" +
"(?<inc>_[0-9]{3,}){0,1}" +
"(?<" + SpecifierMatchGroup + ">\\d{" + _specifier.Format.Length + "})" +
"(?<" + SequenceNumberMatchGroup + ">_[0-9]{3,}){0,1}" +
Regex.Escape(suffix) +
"$");

DirectorySearchPattern = filenameTemplate.Replace(DateSpecifier, "*");
DirectorySearchPattern = filenameTemplate.Replace(_specifier.Token, "*");
LogFileDirectory = directory;
_pathTemplate = Path.Combine(LogFileDirectory, filenameTemplate);
}
Expand All @@ -81,12 +79,14 @@ public TemplatedPathRoller(string pathTemplate)

public void GetLogFilePath(DateTime date, int sequenceNumber, out string path)
{
var tok = date.ToString(DateFormat, CultureInfo.InvariantCulture);
var currentCheckpoint = GetCurrentCheckpoint(date);

var tok = currentCheckpoint.ToString(_specifier.Format, CultureInfo.InvariantCulture);

if (sequenceNumber != 0)
tok += "_" + sequenceNumber.ToString("000", CultureInfo.InvariantCulture);

path = _pathTemplate.Replace(DateSpecifier, tok);
path = _pathTemplate.Replace(_specifier.Token, tok);
}

public IEnumerable<RollingLogFile> SelectMatches(IEnumerable<string> filenames)
Expand All @@ -97,26 +97,30 @@ public IEnumerable<RollingLogFile> SelectMatches(IEnumerable<string> filenames)
if (match.Success)
{
var inc = 0;
var incGroup = match.Groups["inc"];
var incGroup = match.Groups[SequenceNumberMatchGroup];
if (incGroup.Captures.Count != 0)
{
var incPart = incGroup.Captures[0].Value.Substring(1);
inc = int.Parse(incPart, CultureInfo.InvariantCulture);
}

DateTime date;
var datePart = match.Groups["date"].Captures[0].Value;
DateTime dateTime;
var dateTimePart = match.Groups[SpecifierMatchGroup].Captures[0].Value;
if (!DateTime.TryParseExact(
datePart,
DateFormat,
dateTimePart,
_specifier.Format,
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out date))
out dateTime))
continue;

yield return new RollingLogFile(filename, date, inc);
yield return new RollingLogFile(filename, dateTime, inc);
}
}
}

public DateTime GetCurrentCheckpoint(DateTime instant) => _specifier.GetCurrentCheckpoint(instant);

public DateTime GetNextCheckpoint(DateTime instant) => _specifier.GetNextCheckpoint(instant);
}
}
}
6 changes: 3 additions & 3 deletions src/Serilog.Sinks.RollingFile/project.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "3.1.0-*",
{
"version": "3.2.0-*",
"description": "The rolling file sink for Serilog - Simple .NET logging with fully-structured events",
"authors": [ "Serilog Contributors" ],
"packOptions": {
Expand All @@ -18,7 +18,7 @@
},
"frameworks": {
"net4.5": {
"buildOptions": {"define": ["SHARING"]}
"buildOptions": {"define": ["SHARING", "HRESULTS"]}
},
"netstandard1.3": {
"dependencies": {
Expand Down
Loading

0 comments on commit 983fb02

Please sign in to comment.