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")); + } + } +}