Skip to content

Commit 983fb02

Browse files
authored
Merge pull request #35 from serilog/dev
3.2.0 Release
2 parents 1ec387e + 1c88099 commit 983fb02

File tree

8 files changed

+184
-82
lines changed

8 files changed

+184
-82
lines changed

Diff for: src/Serilog.Sinks.RollingFile/Serilog.Sinks.RollingFile.xproj

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,15 @@
44
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
55
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
66
</PropertyGroup>
7-
87
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
98
<PropertyGroup Label="Globals">
109
<ProjectGuid>a3e6e5b4-995f-4c3d-9673-a4b6000f4e21</ProjectGuid>
11-
<RootNamespace>Serilog.Sinks.RollingFile</RootNamespace>
10+
<RootNamespace>Serilog</RootNamespace>
1211
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
1312
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
1413
</PropertyGroup>
1514
<PropertyGroup>
1615
<SchemaVersion>2.0</SchemaVersion>
1716
</PropertyGroup>
1817
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
19-
</Project>
18+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2013-2016 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+
17+
namespace Serilog.Sinks.RollingFile
18+
{
19+
static class IOErrors
20+
{
21+
public static bool IsLockedFile(IOException ex)
22+
{
23+
#if HRESULTS
24+
var errorCode = System.Runtime.InteropServices.Marshal.GetHRForException(ex) & ((1 << 16) - 1);
25+
return errorCode == 32 || errorCode == 33;
26+
#else
27+
return true;
28+
#endif
29+
}
30+
}
31+
}

Diff for: src/Serilog.Sinks.RollingFile/Sinks/RollingFile/RollingFileSink.cs

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

15-
1615
using System;
1716
using System.IO;
1817
using System.Linq;
19-
using System.Runtime.InteropServices;
2018
using System.Text;
2119
using Serilog.Core;
2220
using Serilog.Debugging;
@@ -100,7 +98,7 @@ public void Emit(LogEvent logEvent)
10098

10199
lock (_syncRoot)
102100
{
103-
if (_isDisposed) throw new ObjectDisposedException("The rolling file has been disposed.");
101+
if (_isDisposed) throw new ObjectDisposedException("The rolling log file has been disposed.");
104102

105103
AlignCurrentFileTo(Clock.DateTimeNow);
106104

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

127125
void OpenFile(DateTime now)
128126
{
129-
var date = now.Date;
127+
var currentCheckpoint = _roller.GetCurrentCheckpoint(now);
130128

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

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

143-
var latestForThisDate = _roller
141+
var latestForThisCheckpoint = _roller
144142
.SelectMatches(existingFiles)
145-
.Where(m => m.Date == date)
143+
.Where(m => m.DateTime == currentCheckpoint)
146144
.OrderByDescending(m => m.SequenceNumber)
147145
.FirstOrDefault();
148146

149-
var sequence = latestForThisDate != null ? latestForThisDate.SequenceNumber : 0;
147+
var sequence = latestForThisCheckpoint != null ? latestForThisCheckpoint.SequenceNumber : 0;
150148

151149
const int maxAttempts = 3;
152150
for (var attempt = 0; attempt < maxAttempts; attempt++)
@@ -166,8 +164,7 @@ void OpenFile(DateTime now)
166164
}
167165
catch (IOException ex)
168166
{
169-
var errorCode = Marshal.GetHRForException(ex) & ((1 << 16) - 1);
170-
if (errorCode == 32 || errorCode == 33)
167+
if (IOErrors.IsLockedFile(ex))
171168
{
172169
SelfLog.WriteLine("Rolling file target {0} was locked, attempting to open next in sequence (attempt {1})", path, attempt + 1);
173170
sequence++;
@@ -196,7 +193,7 @@ void ApplyRetentionPolicy(string currentFilePath)
196193

197194
var newestFirst = _roller
198195
.SelectMatches(potentialMatches)
199-
.OrderByDescending(m => m.Date)
196+
.OrderByDescending(m => m.DateTime)
200197
.ThenByDescending(m => m.SequenceNumber)
201198
.Select(m => m.Filename);
202199

Diff for: src/Serilog.Sinks.RollingFile/Sinks/RollingFile/RollingLogFile.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,16 @@ namespace Serilog.Sinks.RollingFile
1818
{
1919
class RollingLogFile
2020
{
21-
public RollingLogFile(string filename, DateTime date, int sequenceNumber)
21+
public RollingLogFile(string filename, DateTime dateTime, int sequenceNumber)
2222
{
2323
Filename = filename;
24-
Date = date;
24+
DateTime = dateTime;
2525
SequenceNumber = sequenceNumber;
2626
}
2727

2828
public string Filename { get; }
2929

30-
public DateTime Date { get; }
30+
public DateTime DateTime { get; }
3131

3232
public int SequenceNumber { get; }
3333
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2013-2016 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;
16+
using System.Linq;
17+
18+
namespace Serilog.Sinks.RollingFile
19+
{
20+
class Specifier
21+
{
22+
public static readonly Specifier Date = new Specifier("Date", "yyyyMMdd", TimeSpan.FromDays(1));
23+
public static readonly Specifier Hour = new Specifier("Hour", "yyyyMMddHH", TimeSpan.FromHours(1));
24+
public static readonly Specifier HalfHour = new Specifier("HalfHour", "yyyyMMddHHmm", TimeSpan.FromMinutes(30));
25+
26+
public string Token { get; }
27+
public string Format { get; }
28+
public TimeSpan Interval { get; }
29+
30+
Specifier(string name, string format, TimeSpan interval)
31+
{
32+
if (name == null) throw new ArgumentNullException(nameof(name));
33+
if (format == null) throw new ArgumentNullException(nameof(format));
34+
35+
Token = "{" + name + "}";
36+
Format = format;
37+
Interval = interval;
38+
}
39+
40+
public DateTime GetCurrentCheckpoint(DateTime instant)
41+
{
42+
if (this == Hour)
43+
{
44+
return instant.Date.AddHours(instant.Hour);
45+
}
46+
47+
if (this == HalfHour)
48+
{
49+
var hour = instant.Date.AddHours(instant.Hour);
50+
if (instant.Minute >= 30)
51+
return hour.AddMinutes(30);
52+
return hour;
53+
}
54+
55+
return instant.Date;
56+
}
57+
58+
public DateTime GetNextCheckpoint(DateTime instant) => GetCurrentCheckpoint(instant).Add(Interval);
59+
60+
public static bool TryGetSpecifier(string pathTemplate, out Specifier specifier)
61+
{
62+
if (pathTemplate == null) throw new ArgumentNullException(nameof(pathTemplate));
63+
64+
var specifiers = new[] { HalfHour, Hour, Date }.Where(s => pathTemplate.Contains(s.Token)).ToArray();
65+
66+
if (specifiers.Length > 1)
67+
throw new ArgumentException("Only one interval specifier can be used in a rolling log file path.", nameof(pathTemplate));
68+
69+
specifier = specifiers.FirstOrDefault();
70+
return specifier != null;
71+
}
72+
}
73+
}

Diff for: src/Serilog.Sinks.RollingFile/Sinks/RollingFile/TemplatedPathRoller.cs

+34-30
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
14-
14+
1515
using System;
1616
using System.Collections.Generic;
1717
using System.Globalization;
@@ -25,52 +25,50 @@ namespace Serilog.Sinks.RollingFile
2525
// Logs/log-{Date}.txt
2626
//
2727
class TemplatedPathRoller
28-
{
29-
const string OldStyleDateSpecifier = "{0}";
30-
const string DateSpecifier = "{Date}";
31-
const string DateFormat = "yyyyMMdd";
28+
{
3229
const string DefaultSeparator = "-";
3330

31+
const string SpecifierMatchGroup = "specifier";
32+
const string SequenceNumberMatchGroup = "sequence";
33+
3434
readonly string _pathTemplate;
3535
readonly Regex _filenameMatcher;
36+
readonly Specifier _specifier = null;
3637

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

4442
var directory = Path.GetDirectoryName(pathTemplate);
4543
if (string.IsNullOrEmpty(directory))
46-
{
4744
directory = Directory.GetCurrentDirectory();
48-
}
4945

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

52-
if (directory.Contains(DateSpecifier))
53-
throw new ArgumentException("The date cannot form part of the directory name");
50+
directory = Path.GetFullPath(directory);
5451

5552
var filenameTemplate = Path.GetFileName(pathTemplate);
56-
if (!filenameTemplate.Contains(DateSpecifier))
53+
if (!Specifier.TryGetSpecifier(filenameTemplate, out _specifier))
5754
{
55+
_specifier = Specifier.Date;
5856
filenameTemplate = Path.GetFileNameWithoutExtension(filenameTemplate) + DefaultSeparator +
59-
DateSpecifier + Path.GetExtension(filenameTemplate);
57+
_specifier.Token + Path.GetExtension(filenameTemplate);
6058
}
6159

62-
var indexOfSpecifier = filenameTemplate.IndexOf(DateSpecifier, StringComparison.Ordinal);
60+
var indexOfSpecifier = filenameTemplate.IndexOf(_specifier.Token, StringComparison.Ordinal);
6361
var prefix = filenameTemplate.Substring(0, indexOfSpecifier);
64-
var suffix = filenameTemplate.Substring(indexOfSpecifier + DateSpecifier.Length);
62+
var suffix = filenameTemplate.Substring(indexOfSpecifier + _specifier.Token.Length);
6563
_filenameMatcher = new Regex(
6664
"^" +
6765
Regex.Escape(prefix) +
68-
"(?<date>\\d{" + DateFormat.Length + "})" +
69-
"(?<inc>_[0-9]{3,}){0,1}" +
66+
"(?<" + SpecifierMatchGroup + ">\\d{" + _specifier.Format.Length + "})" +
67+
"(?<" + SequenceNumberMatchGroup + ">_[0-9]{3,}){0,1}" +
7068
Regex.Escape(suffix) +
7169
"$");
7270

73-
DirectorySearchPattern = filenameTemplate.Replace(DateSpecifier, "*");
71+
DirectorySearchPattern = filenameTemplate.Replace(_specifier.Token, "*");
7472
LogFileDirectory = directory;
7573
_pathTemplate = Path.Combine(LogFileDirectory, filenameTemplate);
7674
}
@@ -81,12 +79,14 @@ public TemplatedPathRoller(string pathTemplate)
8179

8280
public void GetLogFilePath(DateTime date, int sequenceNumber, out string path)
8381
{
84-
var tok = date.ToString(DateFormat, CultureInfo.InvariantCulture);
82+
var currentCheckpoint = GetCurrentCheckpoint(date);
83+
84+
var tok = currentCheckpoint.ToString(_specifier.Format, CultureInfo.InvariantCulture);
8585

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

89-
path = _pathTemplate.Replace(DateSpecifier, tok);
89+
path = _pathTemplate.Replace(_specifier.Token, tok);
9090
}
9191

9292
public IEnumerable<RollingLogFile> SelectMatches(IEnumerable<string> filenames)
@@ -97,26 +97,30 @@ public IEnumerable<RollingLogFile> SelectMatches(IEnumerable<string> filenames)
9797
if (match.Success)
9898
{
9999
var inc = 0;
100-
var incGroup = match.Groups["inc"];
100+
var incGroup = match.Groups[SequenceNumberMatchGroup];
101101
if (incGroup.Captures.Count != 0)
102102
{
103103
var incPart = incGroup.Captures[0].Value.Substring(1);
104104
inc = int.Parse(incPart, CultureInfo.InvariantCulture);
105105
}
106106

107-
DateTime date;
108-
var datePart = match.Groups["date"].Captures[0].Value;
107+
DateTime dateTime;
108+
var dateTimePart = match.Groups[SpecifierMatchGroup].Captures[0].Value;
109109
if (!DateTime.TryParseExact(
110-
datePart,
111-
DateFormat,
110+
dateTimePart,
111+
_specifier.Format,
112112
CultureInfo.InvariantCulture,
113113
DateTimeStyles.None,
114-
out date))
114+
out dateTime))
115115
continue;
116116

117-
yield return new RollingLogFile(filename, date, inc);
117+
yield return new RollingLogFile(filename, dateTime, inc);
118118
}
119119
}
120120
}
121+
122+
public DateTime GetCurrentCheckpoint(DateTime instant) => _specifier.GetCurrentCheckpoint(instant);
123+
124+
public DateTime GetNextCheckpoint(DateTime instant) => _specifier.GetNextCheckpoint(instant);
121125
}
122-
}
126+
}

Diff for: src/Serilog.Sinks.RollingFile/project.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
{
2-
"version": "3.1.0-*",
1+
{
2+
"version": "3.2.0-*",
33
"description": "The rolling file sink for Serilog - Simple .NET logging with fully-structured events",
44
"authors": [ "Serilog Contributors" ],
55
"packOptions": {
@@ -18,7 +18,7 @@
1818
},
1919
"frameworks": {
2020
"net4.5": {
21-
"buildOptions": {"define": ["SHARING"]}
21+
"buildOptions": {"define": ["SHARING", "HRESULTS"]}
2222
},
2323
"netstandard1.3": {
2424
"dependencies": {

0 commit comments

Comments
 (0)