diff --git a/Build.ps1 b/Build.ps1
index 1694abc..35005d6 100644
--- a/Build.ps1
+++ b/Build.ps1
@@ -11,7 +11,11 @@ $suffix = @{ $true = ""; $false = "$branch-$revision"}[$branch -eq "master" -and
foreach ($src in ls src/Serilog.*) {
Push-Location $src
- & dotnet pack -c Release -o ..\..\.\artifacts --version-suffix=$suffix --include-source
+ if ($suffix) {
+ & dotnet pack -c Release -o ..\..\.\artifacts --version-suffix=$suffix --include-source
+ } else {
+ & dotnet pack -c Release -o ..\..\.\artifacts --include-source
+ }
if($LASTEXITCODE -ne 0) { exit 1 }
Pop-Location
diff --git a/appveyor.yml b/appveyor.yml
index 1e46b3a..7e5f9b5 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,13 +1,9 @@
version: '{build}'
skip_tags: true
-image: Visual Studio 2015
+image: Visual Studio 2017
configuration: Release
install:
- ps: mkdir -Force ".\build\" | Out-Null
- - ps: Invoke-WebRequest "https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0/scripts/obtain/dotnet-install.ps1" -OutFile ".\build\installcli.ps1"
- - ps: $env:DOTNET_INSTALL_DIR = "$pwd\.dotnetcli"
- - ps: '& .\build\installcli.ps1 -InstallDir "$env:DOTNET_INSTALL_DIR" -NoPath -Version 1.0.0'
- - ps: $env:Path = "$env:DOTNET_INSTALL_DIR;$env:Path"
build_script:
- ps: ./Build.ps1
test: off
diff --git a/sample/Sample/Sample.csproj b/sample/Sample/Sample.csproj
index 611634e..ca990e4 100644
--- a/sample/Sample/Sample.csproj
+++ b/sample/Sample/Sample.csproj
@@ -32,4 +32,8 @@
+
+
+
+
diff --git a/src/Serilog.Sinks.Seq/SeqLoggerConfigurationExtensions.cs b/src/Serilog.Sinks.Seq/SeqLoggerConfigurationExtensions.cs
index ff6b3c9..e8c43be 100644
--- a/src/Serilog.Sinks.Seq/SeqLoggerConfigurationExtensions.cs
+++ b/src/Serilog.Sinks.Seq/SeqLoggerConfigurationExtensions.cs
@@ -42,9 +42,10 @@ public static class SeqLoggerConfigurationExtensions
/// The time to wait between checking for event batches.
/// Path for a set of files that will be used to buffer events until they
/// can be successfully transmitted across the network. Individual files will be created using the
- /// pattern -{Date}.json.
+ /// pattern *.json, which should not clash with any other filenames
+ /// in the same directory.
/// A Seq API key that authenticates the client to the Seq server.
- /// The maximum size, in bytes, to which the buffer
+ /// The maximum amount of data, in bytes, to which the buffer
/// log file for a specific date will be allowed to grow. By default no limit will be applied.
/// The maximum size, in bytes, that the JSON representation of
/// an event may take before it is dropped rather than being sent to the Seq server. Specify null for no limit.
@@ -72,7 +73,7 @@ public static LoggerConfiguration Seq(
TimeSpan? period = null,
string apiKey = null,
string bufferBaseFilename = null,
- long? bufferFileSizeLimitBytes = null,
+ long? bufferSizeLimitBytes = null,
long? eventBodyLimitBytes = 256*1024,
LoggingLevelSwitch controlLevelSwitch = null,
HttpMessageHandler messageHandler = null,
@@ -82,8 +83,8 @@ public static LoggerConfiguration Seq(
{
if (loggerSinkConfiguration == null) throw new ArgumentNullException(nameof(loggerSinkConfiguration));
if (serverUrl == null) throw new ArgumentNullException(nameof(serverUrl));
- if (bufferFileSizeLimitBytes.HasValue && bufferFileSizeLimitBytes < 0)
- throw new ArgumentOutOfRangeException(nameof(bufferFileSizeLimitBytes), "Negative value provided; file size limit must be non-negative.");
+ if (bufferSizeLimitBytes.HasValue && bufferSizeLimitBytes < 0)
+ throw new ArgumentOutOfRangeException(nameof(bufferSizeLimitBytes), "Negative value provided; buffer size limit must be non-negative.");
if (queueSizeLimit < 0)
throw new ArgumentOutOfRangeException(nameof(queueSizeLimit), "Queue size limit must be non-zero.");
@@ -113,7 +114,7 @@ public static LoggerConfiguration Seq(
apiKey,
batchPostingLimit,
defaultedPeriod,
- bufferFileSizeLimitBytes,
+ bufferSizeLimitBytes,
eventBodyLimitBytes,
controlLevelSwitch,
messageHandler,
diff --git a/src/Serilog.Sinks.Seq/Serilog.Sinks.Seq.csproj b/src/Serilog.Sinks.Seq/Serilog.Sinks.Seq.csproj
index bd90f1d..b600815 100644
--- a/src/Serilog.Sinks.Seq/Serilog.Sinks.Seq.csproj
+++ b/src/Serilog.Sinks.Seq/Serilog.Sinks.Seq.csproj
@@ -2,7 +2,7 @@
Serilog sink that writes to the Seq log server over HTTP/HTTPS.
- 3.4.1
+ 4.0.0
Serilog Contributors
Copyright © Serilog Contributors 2013-2017
netstandard1.1;netstandard1.3;net45
@@ -44,8 +44,7 @@
-
-
+
diff --git a/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/BookmarkFile.cs b/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/BookmarkFile.cs
index 8b72f0b..fc1925b 100644
--- a/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/BookmarkFile.cs
+++ b/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/BookmarkFile.cs
@@ -33,13 +33,14 @@ public FileSetPosition TryReadBookmark()
{
if (_bookmark.Length != 0)
{
+ _bookmark.Position = 0;
+
// Important not to dispose this StreamReader as the stream must remain open.
var reader = new StreamReader(_bookmark, Encoding.UTF8, false, 128);
var current = reader.ReadLine();
if (current != null)
{
- _bookmark.Position = 0;
var parts = current.Split(new[] { ":::" }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 2)
{
@@ -56,10 +57,14 @@ public void WriteBookmark(FileSetPosition bookmark)
if (bookmark.File == null)
return;
- using (var writer = new StreamWriter(_bookmark))
- {
- writer.WriteLine("{0}:::{1}", bookmark.NextLineStart, bookmark.File);
- }
+ // Don't need to truncate, since we only ever read a single line and
+ // writes are always newline-terminated
+ _bookmark.Position = 0;
+
+ // Cannot dispose, as `leaveOpen` is not available on all target platforms
+ var writer = new StreamWriter(_bookmark);
+ writer.WriteLine("{0}:::{1}", bookmark.NextLineStart, bookmark.File);
+ writer.Flush();
}
public void Dispose()
diff --git a/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/DurableSeqSink.cs b/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/DurableSeqSink.cs
index b842e5b..d33742d 100644
--- a/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/DurableSeqSink.cs
+++ b/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/DurableSeqSink.cs
@@ -17,7 +17,6 @@
using System;
using Serilog.Core;
using Serilog.Events;
-using Serilog.Sinks.RollingFile;
using System.Net.Http;
using System.Text;
@@ -26,7 +25,7 @@ namespace Serilog.Sinks.Seq.Durable
class DurableSeqSink : ILogEventSink, IDisposable
{
readonly HttpLogShipper _shipper;
- readonly RollingFileSink _sink;
+ readonly Logger _sink;
public DurableSeqSink(
string serverUrl,
@@ -34,7 +33,7 @@ public DurableSeqSink(
string apiKey,
int batchPostingLimit,
TimeSpan period,
- long? bufferFileSizeLimitBytes,
+ long? bufferSizeLimitBytes,
long? eventBodyLimitBytes,
LoggingLevelSwitch levelControlSwitch,
HttpMessageHandler messageHandler,
@@ -43,23 +42,30 @@ public DurableSeqSink(
if (serverUrl == null) throw new ArgumentNullException(nameof(serverUrl));
if (bufferBaseFilename == null) throw new ArgumentNullException(nameof(bufferBaseFilename));
+
_shipper = new HttpLogShipper(
serverUrl,
bufferBaseFilename,
apiKey,
batchPostingLimit,
period,
- eventBodyLimitBytes,
+ eventBodyLimitBytes,
levelControlSwitch,
messageHandler,
- retainedInvalidPayloadsLimitBytes);
+ retainedInvalidPayloadsLimitBytes,
+ bufferSizeLimitBytes);
- _sink = new RollingFileSink(
- bufferBaseFilename + "-{Date}.json",
- new RawJsonFormatter(),
- bufferFileSizeLimitBytes,
- null,
- encoding: Encoding.UTF8);
+ const long individualFileSizeLimitBytes = 100L * 1024 * 1024;
+ _sink = new LoggerConfiguration()
+ .MinimumLevel.Verbose()
+ .WriteTo.File(new RawJsonFormatter(),
+ bufferBaseFilename + "-.json",
+ rollingInterval: RollingInterval.Day,
+ fileSizeLimitBytes: individualFileSizeLimitBytes,
+ rollOnFileSizeLimit: true,
+ retainedFileCountLimit: null,
+ encoding: Encoding.UTF8)
+ .CreateLogger();
}
public void Dispose()
@@ -74,7 +80,7 @@ public void Emit(LogEvent logEvent)
// are worth the ambiguity.
if (_shipper.IsIncluded(logEvent))
{
- _sink.Emit(logEvent);
+ _sink.Write(logEvent);
}
}
}
diff --git a/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/FileSet.cs b/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/FileSet.cs
index 7be1e7e..6ae74af 100644
--- a/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/FileSet.cs
+++ b/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/FileSet.cs
@@ -19,6 +19,7 @@
using System.IO;
using System.Linq;
using System.Net;
+using System.Text.RegularExpressions;
using Serilog.Debugging;
namespace Serilog.Sinks.Seq.Durable
@@ -28,6 +29,7 @@ class FileSet
readonly string _bookmarkFilename;
readonly string _candidateSearchPath;
readonly string _logFolder;
+ readonly Regex _filenameMatcher;
const string InvalidPayloadFilePrefix = "invalid-";
@@ -37,7 +39,8 @@ public FileSet(string bufferBaseFilename)
_bookmarkFilename = Path.GetFullPath(bufferBaseFilename + ".bookmark");
_logFolder = Path.GetDirectoryName(_bookmarkFilename);
- _candidateSearchPath = Path.GetFileName(bufferBaseFilename) + "*.json";
+ _candidateSearchPath = Path.GetFileName(bufferBaseFilename) + "-*.json";
+ _filenameMatcher = new Regex("^" + Regex.Escape(Path.GetFileName(bufferBaseFilename)) + "-(?\\d{8})(?_[0-9]{3,}){0,1}\\.json$");
}
public BookmarkFile OpenBookmarkFile()
@@ -45,67 +48,78 @@ public BookmarkFile OpenBookmarkFile()
return new BookmarkFile(_bookmarkFilename);
}
- public string MakeInvalidPayloadFilename(HttpStatusCode statusCode)
+ public string[] GetBufferFiles()
{
- var invalidPayloadFilename = $"{InvalidPayloadFilePrefix}{statusCode}-{Guid.NewGuid():n}.json";
- return Path.Combine(_logFolder, invalidPayloadFilename);
+ return Directory.GetFiles(_logFolder, _candidateSearchPath)
+ .Select(n => new KeyValuePair(n, _filenameMatcher.Match(Path.GetFileName(n))))
+ .Where(nm => nm.Value.Success)
+ .OrderBy(nm => nm.Value.Groups["date"].Value, StringComparer.OrdinalIgnoreCase)
+ .ThenBy(nm => int.Parse("0" + nm.Value.Groups["sequence"].Value.Replace("_", "")))
+ .Select(nm => nm.Key)
+ .ToArray();
}
- public void CleanUpInvalidPayloadFiles(long maxNumberOfBytesToRetain)
+ public void CleanUpBufferFiles(long bufferSizeLimitBytes, int alwaysRetainCount)
{
try
{
- var candiateFiles = Directory.EnumerateFiles(_logFolder, $"{InvalidPayloadFilePrefix}*.json");
- DeleteOldFiles(maxNumberOfBytesToRetain, candiateFiles);
+ var bufferFiles = GetBufferFiles();
+ Array.Reverse(bufferFiles);
+ DeleteExceedingCumulativeSize(bufferFiles.Select(f => new FileInfo(f)), bufferSizeLimitBytes, 2);
}
catch (Exception ex)
{
- SelfLog.WriteLine("Exception thrown while trying to clean up invalid payload files: {0}", ex);
+ SelfLog.WriteLine("Exception thrown while cleaning up buffer files: {0}", ex);
}
}
- public string[] GetFiles()
+ public string MakeInvalidPayloadFilename(HttpStatusCode statusCode)
{
- return Directory.GetFiles(_logFolder, _candidateSearchPath)
- .OrderBy(n => n)
- .ToArray();
+ var invalidPayloadFilename = $"{InvalidPayloadFilePrefix}{statusCode}-{Guid.NewGuid():n}.json";
+ return Path.Combine(_logFolder, invalidPayloadFilename);
}
- static void DeleteOldFiles(long maxNumberOfBytesToRetain, IEnumerable files)
+ public void CleanUpInvalidPayloadFiles(long maxNumberOfBytesToRetain)
{
- var orderedFileInfos = from candiateFile in files
- let candiateFileInfo = new FileInfo(candiateFile)
- orderby candiateFileInfo.LastAccessTimeUtc descending
- select candiateFileInfo;
-
- var invalidPayloadFilesToDelete = WhereCumulativeSizeGreaterThan(orderedFileInfos, maxNumberOfBytesToRetain);
+ try
+ {
+ var candidateFiles = from file in Directory.EnumerateFiles(_logFolder, $"{InvalidPayloadFilePrefix}*.json")
+ let candiateFileInfo = new FileInfo(file)
+ orderby candiateFileInfo.LastWriteTimeUtc descending
+ select candiateFileInfo;
- foreach (var fileToDelete in invalidPayloadFilesToDelete)
+ DeleteExceedingCumulativeSize(candidateFiles, maxNumberOfBytesToRetain, 0);
+ }
+ catch (Exception ex)
{
- try
- {
- fileToDelete.Delete();
- }
- catch (Exception ex)
- {
- SelfLog.WriteLine("Exception '{0}' thrown while trying to delete file {1}", ex.Message, fileToDelete.FullName);
- }
+ SelfLog.WriteLine("Exception thrown while cleaning up invalid payload files: {0}", ex);
}
}
-
- static IEnumerable WhereCumulativeSizeGreaterThan(IEnumerable files, long maxCumulativeSize)
+
+ static void DeleteExceedingCumulativeSize(IEnumerable files, long maxNumberOfBytesToRetain, int alwaysRetainCount)
{
long cumulative = 0;
+ var i = 0;
foreach (var file in files)
{
cumulative += file.Length;
- if (cumulative > maxCumulativeSize)
+
+ if (i++ < alwaysRetainCount)
+ continue;
+
+ if (cumulative <= maxNumberOfBytesToRetain)
+ continue;
+
+ try
{
- yield return file;
+ file.Delete();
+ }
+ catch (Exception ex)
+ {
+ SelfLog.WriteLine("Exception thrown while trying to delete file {0}: {1}", file.FullName, ex);
}
}
}
-
}
}
diff --git a/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/FileSetPosition.cs b/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/FileSetPosition.cs
index b427099..bd0c73f 100644
--- a/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/FileSetPosition.cs
+++ b/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/FileSetPosition.cs
@@ -18,16 +18,14 @@ namespace Serilog.Sinks.Seq.Durable
{
struct FileSetPosition
{
- readonly string _file;
- readonly long _nextLineStart;
+ public string File { get; }
- public string File => _file;
- public long NextLineStart => _nextLineStart;
+ public long NextLineStart { get; }
public FileSetPosition(long nextLineStart, string file)
{
- _nextLineStart = nextLineStart;
- _file = file;
+ NextLineStart = nextLineStart;
+ File = file;
}
public static readonly FileSetPosition None = default(FileSetPosition);
diff --git a/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/HttpLogShipper.cs b/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/HttpLogShipper.cs
index 0da7a9c..b707d49 100644
--- a/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/HttpLogShipper.cs
+++ b/src/Serilog.Sinks.Seq/Sinks/Seq/Durable/HttpLogShipper.cs
@@ -41,6 +41,7 @@ class HttpLogShipper : IDisposable
readonly long? _eventBodyLimitBytes;
readonly FileSet _fileSet;
readonly long? _retainedInvalidPayloadsLimitBytes;
+ readonly long? _bufferSizeLimitBytes;
// Timer thread only
readonly HttpClient _httpClient;
@@ -67,7 +68,8 @@ public HttpLogShipper(
long? eventBodyLimitBytes,
LoggingLevelSwitch levelControlSwitch,
HttpMessageHandler messageHandler,
- long? retainedInvalidPayloadsLimitBytes)
+ long? retainedInvalidPayloadsLimitBytes,
+ long? bufferSizeLimitBytes)
{
_apiKey = apiKey;
_batchPostingLimit = batchPostingLimit;
@@ -75,6 +77,7 @@ public HttpLogShipper(
_controlledSwitch = new ControlledLevelSwitch(levelControlSwitch);
_connectionSchedule = new ExponentialBackoffConnectionSchedule(period);
_retainedInvalidPayloadsLimitBytes = retainedInvalidPayloadsLimitBytes;
+ _bufferSizeLimitBytes = bufferSizeLimitBytes;
_httpClient = messageHandler != null ? new HttpClient(messageHandler) : new HttpClient();
_httpClient.BaseAddress = new Uri(SeqApi.NormalizeServerBaseAddress(serverUrl));
_fileSet = new FileSet(bufferBaseFilename);
@@ -124,13 +127,10 @@ async Task OnTick()
{
count = 0;
- // Locking the bookmark ensures that though there may be multiple instances of this
- // class running, only one will ship logs at a time.
-
using (var bookmarkFile = _fileSet.OpenBookmarkFile())
{
var position = bookmarkFile.TryReadBookmark();
- var files = _fileSet.GetFiles();
+ var files = _fileSet.GetBufferFiles();
if (position.File == null || !IOFile.Exists(position.File))
{
@@ -180,6 +180,10 @@ async Task OnTick()
_connectionSchedule.MarkFailure();
SelfLog.WriteLine("Received failed HTTP shipping result {0}: {1}", result.StatusCode,
await result.Content.ReadAsStringAsync().ConfigureAwait(false));
+
+ if (_bufferSizeLimitBytes.HasValue)
+ _fileSet.CleanUpBufferFiles(_bufferSizeLimitBytes.Value, 2);
+
break;
}
}
@@ -195,7 +199,6 @@ async Task OnTick()
// Only advance the bookmark if no other process has the
// current file locked, and its length is as we found it.
-
if (files.Length == 2 && files.First() == position.File &&
FileIsUnlockedAndUnextended(position))
{
@@ -204,10 +207,8 @@ async Task OnTick()
if (files.Length > 2)
{
- // Once there's a third file waiting to ship, we do our
- // best to move on, though a lock on the current file
- // will delay this.
-
+ // By this point, we expect writers to have relinquished locks
+ // on the oldest file.
IOFile.Delete(files[0]);
}
}
@@ -216,8 +217,11 @@ async Task OnTick()
}
catch (Exception ex)
{
- SelfLog.WriteLine("Exception while emitting periodic batch from {0}: {1}", this, ex);
_connectionSchedule.MarkFailure();
+ SelfLog.WriteLine("Exception while emitting periodic batch from {0}: {1}", this, ex);
+
+ if (_bufferSizeLimitBytes.HasValue)
+ _fileSet.CleanUpBufferFiles(_bufferSizeLimitBytes.Value, 2);
}
finally
{
diff --git a/test/Serilog.Sinks.Seq.Tests/Durable/BookmarkFileTests.cs b/test/Serilog.Sinks.Seq.Tests/Durable/BookmarkFileTests.cs
new file mode 100644
index 0000000..e10a33f
--- /dev/null
+++ b/test/Serilog.Sinks.Seq.Tests/Durable/BookmarkFileTests.cs
@@ -0,0 +1,25 @@
+using Serilog.Sinks.Seq.Durable;
+using Serilog.Sinks.Seq.Tests.Support;
+using Xunit;
+
+namespace Serilog.Sinks.Seq.Tests.Durable
+{
+ public class BookmarkFileTests
+ {
+ [Fact]
+ public void BookmarkPersistenceCanBeRoundTripped()
+ {
+ using (var tmp = new TempFolder())
+ {
+ var position = new FileSetPosition(1234, Some.String());
+
+ var bookmark = new BookmarkFile(tmp.AllocateFilename("bookmark"));
+ bookmark.WriteBookmark(position);
+
+ var read = bookmark.TryReadBookmark();
+ Assert.Equal(position.NextLineStart, read.NextLineStart);
+ Assert.Equal(position.File, read.File);
+ }
+ }
+ }
+}
diff --git a/test/Serilog.Sinks.Seq.Tests/Durable/FileSetTests.cs b/test/Serilog.Sinks.Seq.Tests/Durable/FileSetTests.cs
new file mode 100644
index 0000000..5c5936e
--- /dev/null
+++ b/test/Serilog.Sinks.Seq.Tests/Durable/FileSetTests.cs
@@ -0,0 +1,40 @@
+using System.IO;
+using Serilog.Sinks.Seq.Durable;
+using Serilog.Sinks.Seq.Tests.Support;
+using Xunit;
+
+namespace Serilog.Sinks.Seq.Tests.Durable
+{
+ public class FileSetTests
+ {
+ [Fact]
+ public void MatchingBufferFilenamesAreFoundAndOrdered()
+ {
+ using (var tmp = new TempFolder())
+ {
+ var bbf = Path.GetFullPath(Path.Combine(tmp.Path, "buffer"));
+ var fakeContent = "{}";
+
+ // Matching
+ var shouldMatch = new[] {bbf + "-20180101.json", bbf + "-20180102.json", bbf + "-20180102_001.json"};
+ foreach (var fn in shouldMatch)
+ System.IO.File.WriteAllText(fn, fakeContent);
+
+ // Ignores bookmark
+ System.IO.File.WriteAllText(bbf + ".bookmark", fakeContent);
+
+ // Ignores file with name suffix
+ System.IO.File.WriteAllText(bbf + "similar-20180101.json", fakeContent);
+
+ // Ignores file from unrelated set
+ System.IO.File.WriteAllText(Path.Combine(Path.GetDirectoryName(bbf), "unrelated-20180101.json"), fakeContent);
+
+ var fileSet = new FileSet(bbf);
+ var files = fileSet.GetBufferFiles();
+
+ Assert.Equal(3, files.Length);
+ Assert.Equal(shouldMatch, files);
+ }
+ }
+ }
+}
diff --git a/test/Serilog.Sinks.Seq.Tests/Durable/PayloadReaderTests.cs b/test/Serilog.Sinks.Seq.Tests/Durable/PayloadReaderTests.cs
new file mode 100644
index 0000000..4e7c5ba
--- /dev/null
+++ b/test/Serilog.Sinks.Seq.Tests/Durable/PayloadReaderTests.cs
@@ -0,0 +1,44 @@
+using System;
+using System.IO;
+using System.Text;
+using Newtonsoft.Json;
+using Serilog.Sinks.Seq.Durable;
+using Serilog.Sinks.Seq.Tests.Support;
+using Xunit;
+using IOFile = System.IO.File;
+
+namespace Serilog.Sinks.Seq.Tests.Durable
+{
+ public class PayloadReaderTests
+ {
+ [Fact]
+ public void ReadsEventsFromBufferFiles()
+ {
+ using (var tmp = new TempFolder())
+ {
+ var fn = tmp.AllocateFilename("json");
+ var lines = IOFile.ReadAllText(Path.Combine("Resources", "ThreeBufferedEvents.json.txt"), Encoding.UTF8).Split(new [] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
+ using (var f = IOFile.Create(fn))
+ using (var fw = new StreamWriter(f, Encoding.UTF8))
+ {
+ foreach (var line in lines)
+ {
+ fw.WriteLine(line);
+ }
+ }
+ var position = new FileSetPosition(0, fn);
+ var count = 0;
+ var payload = PayloadReader.ReadPayload(1000, null, ref position, ref count);
+
+ Assert.Equal(3, count);
+ Assert.Equal(576 + 3 * (Environment.NewLine.Length - 1), position.NextLineStart);
+ Assert.Equal(fn, position.File);
+
+ var data = JsonConvert.DeserializeObject(payload);
+ var events = data["Events"];
+ Assert.NotNull(events);
+ Assert.Equal(3, events.Count);
+ }
+ }
+ }
+}
diff --git a/test/Serilog.Sinks.Seq.Tests/Resources/ThreeBufferedEvents.json.txt b/test/Serilog.Sinks.Seq.Tests/Resources/ThreeBufferedEvents.json.txt
new file mode 100644
index 0000000..e6bb860
--- /dev/null
+++ b/test/Serilog.Sinks.Seq.Tests/Resources/ThreeBufferedEvents.json.txt
@@ -0,0 +1,3 @@
+{"Timestamp":"2017-10-19T12:04:39.9775056+10:00","Level":"Information","MessageTemplate":"Running loop {Counter}, switch is at {Level}","Properties":{"Counter":700019,"Level":"Information"}}
+{"Timestamp":"2017-10-19T12:04:39.9792156+10:00","Level":"Information","MessageTemplate":"Running loop {Counter}, switch is at {Level}","Properties":{"Counter":700020,"Level":"Information"}}
+{"Timestamp":"2017-10-19T12:04:39.9792575+10:00","Level":"Information","MessageTemplate":"Running loop {Counter}, switch is at {Level}","Properties":{"Counter":700021,"Level":"Information"}}
diff --git a/test/Serilog.Sinks.Seq.Tests/Serilog.Sinks.Seq.Tests.csproj b/test/Serilog.Sinks.Seq.Tests/Serilog.Sinks.Seq.Tests.csproj
index 0fdfd3f..7ea48eb 100644
--- a/test/Serilog.Sinks.Seq.Tests/Serilog.Sinks.Seq.Tests.csproj
+++ b/test/Serilog.Sinks.Seq.Tests/Serilog.Sinks.Seq.Tests.csproj
@@ -7,6 +7,14 @@
true
true
+
+
+
+
+
+ PreserveNewest
+
+
diff --git a/test/Serilog.Sinks.Seq.Tests/Support/Some.cs b/test/Serilog.Sinks.Seq.Tests/Support/Some.cs
index 8b44714..cf9515c 100644
--- a/test/Serilog.Sinks.Seq.Tests/Support/Some.cs
+++ b/test/Serilog.Sinks.Seq.Tests/Support/Some.cs
@@ -45,5 +45,10 @@ public static LogEvent ErrorEvent()
{
return LogEvent(LogEventLevel.Error, null, "Error event");
}
+
+ public static string String()
+ {
+ return Guid.NewGuid().ToString("n");
+ }
}
}
diff --git a/test/Serilog.Sinks.Seq.Tests/Support/TempFolder.cs b/test/Serilog.Sinks.Seq.Tests/Support/TempFolder.cs
new file mode 100644
index 0000000..c32a9e6
--- /dev/null
+++ b/test/Serilog.Sinks.Seq.Tests/Support/TempFolder.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.CompilerServices;
+
+namespace Serilog.Sinks.Seq.Tests.Support
+{
+ class TempFolder : IDisposable
+ {
+ static readonly Guid Session = Guid.NewGuid();
+
+ readonly string _tempFolder;
+
+ public TempFolder([CallerMemberName] string name = null)
+ {
+ _tempFolder = System.IO.Path.Combine(
+ Environment.GetEnvironmentVariable("TMP") ?? Environment.GetEnvironmentVariable("TMPDIR") ?? "/tmp",
+ "Serilog.Sinks.Seq.Tests",
+ Session.ToString("n"),
+ name ?? Guid.NewGuid().ToString("n"));
+
+ Directory.CreateDirectory(_tempFolder);
+ }
+
+ public string Path => _tempFolder;
+
+ public void Dispose()
+ {
+ try
+ {
+ if (Directory.Exists(_tempFolder))
+ Directory.Delete(_tempFolder, true);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine(ex);
+ }
+ }
+
+ public string AllocateFilename(string ext = null)
+ {
+ return System.IO.Path.Combine(Path, Guid.NewGuid().ToString("n") + "." + (ext ?? "tmp"));
+ }
+ }
+}