From 36c9b7d2f27ea7fbf22d63dc6276f0e4a8af0c9a Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 23 Mar 2022 12:08:29 +1000 Subject: [PATCH 1/3] Target net5.0+, C#9/nullable, update dependencies --- .../{ => Data}/JsonDataExtractor.cs | 24 +++++----- .../Data/SeqSyntaxNameResolver.cs | 46 +++++++++++++++++++ src/Seq.Input.HealthCheck/HealthCheckInput.cs | 20 ++++---- .../HealthCheckResult.cs | 26 ++++++----- src/Seq.Input.HealthCheck/HttpHealthCheck.cs | 37 +++++++-------- .../Properties/AssemblyInfo.cs | 3 -- .../Seq.Input.HealthCheck.csproj | 20 +++++--- .../{Util => Settings}/HeaderSettingFormat.cs | 17 +++---- src/Seq.Input.HealthCheck/Util/Nonce.cs | 20 ++++---- .../{ => Data}/JsonDataExtractorTests.cs | 1 + .../Seq.Input.HealthCheck.Tests.csproj | 2 +- .../Settings/HeaderSettingFormatTests.cs | 34 ++++++++++++++ .../Util/NonceTests.cs | 2 +- 13 files changed, 163 insertions(+), 89 deletions(-) rename src/Seq.Input.HealthCheck/{ => Data}/JsonDataExtractor.cs (77%) create mode 100644 src/Seq.Input.HealthCheck/Data/SeqSyntaxNameResolver.cs delete mode 100644 src/Seq.Input.HealthCheck/Properties/AssemblyInfo.cs rename src/Seq.Input.HealthCheck/{Util => Settings}/HeaderSettingFormat.cs (59%) rename test/Seq.Input.HealthCheck.Tests/{ => Data}/JsonDataExtractorTests.cs (97%) create mode 100644 test/Seq.Input.HealthCheck.Tests/Settings/HeaderSettingFormatTests.cs diff --git a/src/Seq.Input.HealthCheck/JsonDataExtractor.cs b/src/Seq.Input.HealthCheck/Data/JsonDataExtractor.cs similarity index 77% rename from src/Seq.Input.HealthCheck/JsonDataExtractor.cs rename to src/Seq.Input.HealthCheck/Data/JsonDataExtractor.cs index 73c8260..3927344 100644 --- a/src/Seq.Input.HealthCheck/JsonDataExtractor.cs +++ b/src/Seq.Input.HealthCheck/Data/JsonDataExtractor.cs @@ -16,34 +16,33 @@ using System.IO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Serilog.Events; -using Serilog.Filters.Expressions; +using Serilog.Expressions; using Serilog.Formatting.Compact.Reader; using Serilog.Formatting.Json; -namespace Seq.Input.HealthCheck +namespace Seq.Input.HealthCheck.Data { public class JsonDataExtractor { - static readonly JsonValueFormatter ValueFormatter = new JsonValueFormatter("$type"); + static readonly JsonValueFormatter ValueFormatter = new("$type"); static readonly JsonSerializer Serializer = JsonSerializer.Create(new JsonSerializerSettings { DateParseHandling = DateParseHandling.None }); - readonly Func _extract; + readonly Func _extract; public JsonDataExtractor(string expression) { if (expression == "@Properties") { - _extract = v => v; + _extract = v => v ?? JValue.CreateNull(); } else { - var expr = FilterLanguage.CreateFilter(expression); + var expr = SerilogExpression.Compile(expression, nameResolver: new SeqSyntaxNameResolver()); _extract = v => { - if (!(v is JObject obj)) + if (v is not JObject obj) throw new ArgumentException("Data value extraction requires a JSON object response."); if (!obj.ContainsKey("@t")) @@ -52,16 +51,15 @@ public JsonDataExtractor(string expression) var le = LogEventReader.ReadFromJObject(obj); var value = expr(le); + + // `null` here means "undefined", but for most purposes this substitution is convenient. if (value == null) return JValue.CreateNull(); - if (!(value is LogEventPropertyValue lepv)) - return JToken.FromObject(value); - var sw = new StringWriter(); - ValueFormatter.Format(lepv, sw); + ValueFormatter.Format(value, sw); return Serializer.Deserialize( - new JsonTextReader(new StringReader(sw.ToString()))); + new JsonTextReader(new StringReader(sw.ToString())))!; }; } } diff --git a/src/Seq.Input.HealthCheck/Data/SeqSyntaxNameResolver.cs b/src/Seq.Input.HealthCheck/Data/SeqSyntaxNameResolver.cs new file mode 100644 index 0000000..503b81c --- /dev/null +++ b/src/Seq.Input.HealthCheck/Data/SeqSyntaxNameResolver.cs @@ -0,0 +1,46 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using Serilog.Events; +using Serilog.Expressions; + +namespace Seq.Input.HealthCheck.Data +{ + public class SeqSyntaxNameResolver: NameResolver + { + // ReSharper disable once UnusedMember.Global + // ReSharper disable once ReturnTypeCanBeNotNullable + public static LogEventPropertyValue? Has(LogEventPropertyValue? value) + { + return new ScalarValue(value != null); + } + + public override bool TryResolveFunctionName(string name, [NotNullWhen(true)] out MethodInfo? implementation) + { + if ("Has".Equals(name, StringComparison.OrdinalIgnoreCase)) + { + implementation = GetType().GetMethod("Has", BindingFlags.Static | BindingFlags.Public)!; + return true; + } + + implementation = null; + return false; + } + + public override bool TryResolveBuiltInPropertyName(string alias, [NotNullWhen(true)] out string? target) + { + target = alias switch + { + "Exception" => "x", + "Level" => "l", + "Message" => "m", + "MessageTemplate" => "mt", + "Properties" => "p", + "Timestamp" => "t", + _ => null + }; + + return target != null; + } + } +} \ No newline at end of file diff --git a/src/Seq.Input.HealthCheck/HealthCheckInput.cs b/src/Seq.Input.HealthCheck/HealthCheckInput.cs index 239bd6f..fe08f39 100644 --- a/src/Seq.Input.HealthCheck/HealthCheckInput.cs +++ b/src/Seq.Input.HealthCheck/HealthCheckInput.cs @@ -17,7 +17,11 @@ using System.IO; using System.Net.Http; using Seq.Apps; +using Seq.Input.HealthCheck.Data; using Seq.Input.HealthCheck.Util; +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global namespace Seq.Input.HealthCheck { @@ -26,22 +30,22 @@ namespace Seq.Input.HealthCheck public class HealthCheckInput : SeqApp, IPublishJson, IDisposable { readonly List _healthCheckTasks = new List(); - HttpClient _httpClient; + HttpClient? _httpClient; [SeqAppSetting( DisplayName = "Target URLs", HelpText = "The HTTP or HTTPS URL that the health check will periodically GET. Multiple URLs " + "can be checked; enter one per line.", InputType = SettingInputType.LongText)] - public string TargetUrl { get; set; } + public string TargetUrl { get; set; } = null!; [SeqAppSetting(InputType = SettingInputType.Password, IsOptional = true, DisplayName = "Authentication Header", HelpText = "An optional `Name: Value` header, stored as sensitive data, for authentication purposes.")] - public string AuthenticationHeader { get; set; } + public string? AuthenticationHeader { get; set; } [SeqAppSetting(InputType = SettingInputType.LongText, IsOptional = true, DisplayName = "Other Headers", HelpText = "Additional headers to send with the request, one per line in `Name: Value` format.")] - public string OtherHeaders { get; set; } + public string? OtherHeaders { get; set; } [SeqAppSetting( DisplayName = "Bypass HTTP caching", @@ -64,16 +68,14 @@ public class HealthCheckInput : SeqApp, IPublishJson, IDisposable "The expression will be evaluated against the response to produce a `Data` property" + " on the resulting event. Use the special value `@Properties` to capture the whole " + "response. The response must be UTF-8 `application/json` for this to be applied.")] - public string DataExtractionExpression { get; set; } - - - + public string? DataExtractionExpression { get; set; } + public void Start(TextWriter inputWriter) { _httpClient = HttpHealthCheckClient.Create(); var reporter = new HealthCheckReporter(inputWriter); - JsonDataExtractor extractor = null; + JsonDataExtractor? extractor = null; if (!string.IsNullOrWhiteSpace(DataExtractionExpression)) extractor = new JsonDataExtractor(DataExtractionExpression); diff --git a/src/Seq.Input.HealthCheck/HealthCheckResult.cs b/src/Seq.Input.HealthCheck/HealthCheckResult.cs index c3bf3a2..2296935 100644 --- a/src/Seq.Input.HealthCheck/HealthCheckResult.cs +++ b/src/Seq.Input.HealthCheck/HealthCheckResult.cs @@ -16,6 +16,8 @@ using System.Globalization; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable MemberCanBePrivate.Global namespace Seq.Input.HealthCheck { @@ -25,14 +27,14 @@ class HealthCheckResult public DateTime UtcTimestamp { get; } [JsonProperty("@x", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string Exception { get; } + public string? Exception { get; } [JsonProperty("@mt")] public string MessageTemplate { get; } = "Health check {Method} {TargetUrl} {Outcome} with status code {StatusCode} in {Elapsed:0.000} ms"; [JsonProperty("@l", DefaultValueHandling = DefaultValueHandling.Ignore)] - public string Level { get; } + public string? Level { get; } [JsonProperty("@r")] public string[] Renderings => new[] {Elapsed.ToString("0.000", CultureInfo.InvariantCulture)}; @@ -43,18 +45,18 @@ class HealthCheckResult public string Outcome { get; } public double Elapsed { get; } public int? StatusCode { get; } - public string ContentType { get; } + public string? ContentType { get; } public long? ContentLength { get; } public string ProbeId { get; } [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string InitialContent { get; } + public string? InitialContent { get; } [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public JToken Data { get; } + public JToken? Data { get; } [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] - public string ProbedUrl { get; } + public string? ProbedUrl { get; } public HealthCheckResult( DateTime utcTimestamp, @@ -63,15 +65,15 @@ public HealthCheckResult( string targetUrl, string outcome, string probeId, - string level, + string? level, double elapsed, int? statusCode, - string contentType, + string? contentType, long? contentLength, - string initialContent, - Exception exception, - JToken data, - string probedUrl) + string? initialContent, + Exception? exception, + JToken? data, + string? probedUrl) { if (utcTimestamp.Kind != DateTimeKind.Utc) throw new ArgumentException("The timestamp must be UTC.", nameof(utcTimestamp)); diff --git a/src/Seq.Input.HealthCheck/HttpHealthCheck.cs b/src/Seq.Input.HealthCheck/HttpHealthCheck.cs index 88ef7e5..c6e6915 100644 --- a/src/Seq.Input.HealthCheck/HttpHealthCheck.cs +++ b/src/Seq.Input.HealthCheck/HttpHealthCheck.cs @@ -22,6 +22,7 @@ using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json.Linq; +using Seq.Input.HealthCheck.Data; using Seq.Input.HealthCheck.Util; namespace Seq.Input.HealthCheck @@ -31,18 +32,18 @@ class HttpHealthCheck readonly string _title; readonly string _targetUrl; readonly List<(string, string)> _headers; - readonly JsonDataExtractor _extractor; + readonly JsonDataExtractor? _extractor; readonly bool _bypassHttpCaching; readonly HttpClient _httpClient; readonly byte[] _buffer = new byte[2048]; public const string ProbeIdParameterName = "__probe"; - static readonly UTF8Encoding ForgivingEncoding = new UTF8Encoding(false, false); + static readonly UTF8Encoding ForgivingEncoding = new(false, false); const int InitialContentChars = 16; const string OutcomeSucceeded = "succeeded", OutcomeFailed = "failed"; - public HttpHealthCheck(HttpClient httpClient, string title, string targetUrl, List<(string, string)> headers, JsonDataExtractor extractor, bool bypassHttpCaching) + public HttpHealthCheck(HttpClient httpClient, string title, string targetUrl, List<(string, string)> headers, JsonDataExtractor? extractor, bool bypassHttpCaching) { _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); _title = title ?? throw new ArgumentNullException(nameof(title)); @@ -56,12 +57,12 @@ public async Task CheckNow(CancellationToken cancel) { string outcome; - Exception exception = null; + Exception? exception = null; int? statusCode = null; - string contentType = null; + string? contentType = null; long? contentLength = null; - string initialContent = null; - JToken data = null; + string? initialContent = null; + JToken? data = null; var probeId = Nonce.Generate(12); var probedUrl = _bypassHttpCaching ? @@ -75,17 +76,11 @@ public async Task CheckNow(CancellationToken cancel) { var request = new HttpRequestMessage(HttpMethod.Get, probedUrl); request.Headers.Add("X-Correlation-ID", probeId); - if (_headers != null) + + foreach (var (name, value) in _headers) { - foreach (var (name, value) in _headers) - { - // the api does not allow overwriting, therefore removal has to happen first. - if (request.Headers.Contains(name)) - { - request.Headers.Remove(name); - } - request.Headers.Add(name, value); - } + // This will throw if a header is duplicated (better for the user to detect this configuration problem). + request.Headers.Add(name, value); } if (_bypassHttpCaching) @@ -97,7 +92,7 @@ public async Task CheckNow(CancellationToken cancel) contentType = response.Content.Headers.ContentType?.ToString(); contentLength = response.Content.Headers.ContentLength; - var content = await response.Content.ReadAsStreamAsync(); + var content = await response.Content.ReadAsStreamAsync(cancel); (initialContent, data) = await DownloadContent(content, contentType, contentLength); outcome = response.IsSuccessStatusCode ? OutcomeSucceeded : OutcomeFailed; @@ -133,18 +128,18 @@ public async Task CheckNow(CancellationToken cancel) } // Either initial content, or extracted data - async Task<(string initialContent, JToken data)> DownloadContent(Stream body, string contentType, long? contentLength) + async Task<(string? initialContent, JToken? data)> DownloadContent(Stream body, string? contentType, long? contentLength) { if (_extractor == null || contentLength == 0 || contentType != "application/json; charset=utf-8" && contentType != "application/json") { - var read = await body.ReadAsync(_buffer, 0, _buffer.Length); + var read = await body.ReadAsync(_buffer); var initial = ForgivingEncoding.GetString(_buffer, 0, Math.Min(read, InitialContentChars)); // Drain the response to avoid dropped connection errors on the server. while (read > 0) - read = await body.ReadAsync(_buffer, 0, _buffer.Length); + read = await body.ReadAsync(_buffer); return (initial, null); } diff --git a/src/Seq.Input.HealthCheck/Properties/AssemblyInfo.cs b/src/Seq.Input.HealthCheck/Properties/AssemblyInfo.cs deleted file mode 100644 index 07b8df9..0000000 --- a/src/Seq.Input.HealthCheck/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Seq.Input.HealthCheck.Tests")] diff --git a/src/Seq.Input.HealthCheck/Seq.Input.HealthCheck.csproj b/src/Seq.Input.HealthCheck/Seq.Input.HealthCheck.csproj index fb33d54..a319dd4 100644 --- a/src/Seq.Input.HealthCheck/Seq.Input.HealthCheck.csproj +++ b/src/Seq.Input.HealthCheck/Seq.Input.HealthCheck.csproj @@ -1,8 +1,8 @@ - netstandard2.0 - 1.2.2 + net5.0 + 2.0.0 Seq Health Check: periodically GET an HTTP resource and publish response metrics to Seq. Datalust and Contributors seq-app @@ -13,15 +13,21 @@ True LICENSE + 9 + enable + + + + - - - - - + + + + + diff --git a/src/Seq.Input.HealthCheck/Util/HeaderSettingFormat.cs b/src/Seq.Input.HealthCheck/Settings/HeaderSettingFormat.cs similarity index 59% rename from src/Seq.Input.HealthCheck/Util/HeaderSettingFormat.cs rename to src/Seq.Input.HealthCheck/Settings/HeaderSettingFormat.cs index 377a148..b794e6f 100644 --- a/src/Seq.Input.HealthCheck/Util/HeaderSettingFormat.cs +++ b/src/Seq.Input.HealthCheck/Settings/HeaderSettingFormat.cs @@ -3,11 +3,9 @@ using System.IO; namespace Seq.Input.HealthCheck.Util { - // adapted from: https://github.com/datalust/seq-app-httprequest/blob/e23b2328141352f5b4851c7502801f5bfe6511f7/src/Seq.App.HttpRequest/Settings/HeaderSettingFormat.cs - // changes to parse for C# 8.0 language level static class HeaderSettingFormat { - public static List<(string, string)> FromSettings(string authenticationHeader, string otherHeaders) + public static List<(string, string)> FromSettings(string? authenticationHeader, string? otherHeaders) { var headers = new List<(string, string)>(); @@ -27,16 +25,13 @@ static class HeaderSettingFormat return headers; } - + internal static (string, string) Parse(string header) { - var components = header.Split(new[] { ':' }, 2, StringSplitOptions.RemoveEmptyEntries); - if (components.Length != 2) - { - throw new ArgumentException($"The header must be specified in `Name: Value` format. (Affected line was '{header}')"); - } - - return (components[0].Trim(), components[1].Trim()); + var colon = header.IndexOf(":", StringComparison.Ordinal); + if (colon is 0 or -1) + throw new ArgumentException("The header must be specified in `Name: Value` format."); + return (header[..colon].Trim(), header[(colon + 1)..].Trim()); } } } diff --git a/src/Seq.Input.HealthCheck/Util/Nonce.cs b/src/Seq.Input.HealthCheck/Util/Nonce.cs index 7b68d77..4aa5e18 100644 --- a/src/Seq.Input.HealthCheck/Util/Nonce.cs +++ b/src/Seq.Input.HealthCheck/Util/Nonce.cs @@ -31,20 +31,18 @@ static IEnumerable GenerateChars(int count) { var rem = count; var buf = new byte[128]; - using (var rng = RandomNumberGenerator.Create()) + using var rng = RandomNumberGenerator.Create(); + while (rem > 0) { - while (rem > 0) + rng.GetBytes(buf); + var b64 = Convert.ToBase64String(buf); + for (var i = 0; i < b64.Length && rem > 0; ++i) { - rng.GetBytes(buf); - var b64 = Convert.ToBase64String(buf); - for (var i = 0; i < b64.Length && rem > 0; ++i) + var c = b64[i]; + if (char.IsLetterOrDigit(c)) { - var c = b64[i]; - if (char.IsLetterOrDigit(c)) - { - yield return c; - rem--; - } + yield return c; + rem--; } } } diff --git a/test/Seq.Input.HealthCheck.Tests/JsonDataExtractorTests.cs b/test/Seq.Input.HealthCheck.Tests/Data/JsonDataExtractorTests.cs similarity index 97% rename from test/Seq.Input.HealthCheck.Tests/JsonDataExtractorTests.cs rename to test/Seq.Input.HealthCheck.Tests/Data/JsonDataExtractorTests.cs index eeabdee..eca4e33 100644 --- a/test/Seq.Input.HealthCheck.Tests/JsonDataExtractorTests.cs +++ b/test/Seq.Input.HealthCheck.Tests/Data/JsonDataExtractorTests.cs @@ -1,6 +1,7 @@ using System.IO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Seq.Input.HealthCheck.Data; using Xunit; namespace Seq.Input.HealthCheck.Tests diff --git a/test/Seq.Input.HealthCheck.Tests/Seq.Input.HealthCheck.Tests.csproj b/test/Seq.Input.HealthCheck.Tests/Seq.Input.HealthCheck.Tests.csproj index 3d785ea..103c03e 100644 --- a/test/Seq.Input.HealthCheck.Tests/Seq.Input.HealthCheck.Tests.csproj +++ b/test/Seq.Input.HealthCheck.Tests/Seq.Input.HealthCheck.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp2.1 + net5.0 false diff --git a/test/Seq.Input.HealthCheck.Tests/Settings/HeaderSettingFormatTests.cs b/test/Seq.Input.HealthCheck.Tests/Settings/HeaderSettingFormatTests.cs new file mode 100644 index 0000000..e9d1126 --- /dev/null +++ b/test/Seq.Input.HealthCheck.Tests/Settings/HeaderSettingFormatTests.cs @@ -0,0 +1,34 @@ +using System; +using Seq.Input.HealthCheck.Util; +using Xunit; + +namespace Seq.Input.HealthCheck.Tests.Settings +{ + + public class HeaderSettingFormatTests + { + [Theory] + [InlineData("")] + [InlineData("Name")] + [InlineData("Name Value")] + [InlineData(":Value")] + public void InvalidHeaderSettingsAreNotParsed(string setting) + { + Assert.Throws(() => HeaderSettingFormat.Parse(setting)); + } + + [Theory] + [InlineData("Name: Value", "Name", "Value")] + [InlineData("Name:Value", "Name", "Value")] + [InlineData("Name: Value ", "Name", "Value")] + [InlineData("Name:", "Name", "")] + [InlineData("Dashed-Name: Value", "Dashed-Name", "Value")] + [InlineData("Name: Value: Value", "Name", "Value: Value")] + public void ValidHeaderSettingsAreParsed(string setting, string expectedName, string expectedValue) + { + var (name, value) = HeaderSettingFormat.Parse(setting); + Assert.Equal(expectedName, name); + Assert.Equal(expectedValue, value); + } + } +} \ No newline at end of file diff --git a/test/Seq.Input.HealthCheck.Tests/Util/NonceTests.cs b/test/Seq.Input.HealthCheck.Tests/Util/NonceTests.cs index 00ee0cd..845e11c 100644 --- a/test/Seq.Input.HealthCheck.Tests/Util/NonceTests.cs +++ b/test/Seq.Input.HealthCheck.Tests/Util/NonceTests.cs @@ -7,7 +7,7 @@ namespace Seq.Input.HealthCheck.Tests.Util { public class NonceTests { - public static IEnumerable CharacterCounts = Enumerable.Range(0, 100).Select(n => new [] { (object)n }); + public static readonly IEnumerable CharacterCounts = Enumerable.Range(0, 100).Select(n => new [] { (object)n }); [Theory] [MemberData(nameof(CharacterCounts))] From 83b2d38da76a937e1bba8942db91d5734169e93e Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 26 Apr 2022 16:48:17 +1000 Subject: [PATCH 2/3] Update build worker image; publish from main --- Build.ps1 | 2 +- appveyor.yml | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Build.ps1 b/Build.ps1 index 34a2dc5..dc24499 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -14,7 +14,7 @@ if($LASTEXITCODE -ne 0) { exit 1 } $branch = @{ $true = $env:APPVEYOR_REPO_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$env:APPVEYOR_REPO_BRANCH -ne $NULL]; $revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $env:APPVEYOR_BUILD_NUMBER, 10); $false = "local" }[$env:APPVEYOR_BUILD_NUMBER -ne $NULL]; -$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)))-$revision"}[$branch -eq "master" -and $revision -ne "local"] +$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)))-$revision"}[$branch -eq "main" -and $revision -ne "local"] echo "build: Version suffix is $suffix" diff --git a/appveyor.yml b/appveyor.yml index 9931ad4..3ee795a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,8 +1,6 @@ version: '{build}' skip_tags: true -image: Visual Studio 2017 -configuration: Release -install: +image: Visual Studio 2022 build_script: - ps: ./Build.ps1 test: off @@ -14,7 +12,7 @@ deploy: secure: ABsZ0uLbAvoUFXnkUJ/DZwQwGQ1EIKzvSOm3aFSUZ0kY4lTBIQhI38KxrNFahQRB skip_symbols: true on: - branch: /^(master|dev)$/ + branch: /^(main|dev)$/ - provider: GitHub auth_token: secure: hX+cZmW+9BCXy7vyH8myWsYdtQHyzzil9K5yvjJv7dK9XmyrGYYDj/DPzMqsXSjo From 704a638fca8aae2671f2d9962cda3d65d4b3c981 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Tue, 26 Apr 2022 16:53:31 +1000 Subject: [PATCH 3/3] Update CSPROJ format --- asset/seq-input-healthcheck-2.png | Bin 7870 -> 0 bytes .../Seq.Input.HealthCheck.csproj | 3 ++- 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 asset/seq-input-healthcheck-2.png diff --git a/asset/seq-input-healthcheck-2.png b/asset/seq-input-healthcheck-2.png deleted file mode 100644 index 9f2fcf445d2976d19ec0bd28074f0de17c21571d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7870 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 KYk$Seq Health Check: periodically GET an HTTP resource and publish response metrics to Seq. Datalust and Contributors seq-app - https://raw.githubusercontent.com/datalust/seq-input-healthcheck/dev/asset/seq-input-healthcheck-2.png https://github.com/datalust/seq-input-healthcheck https://github.com/datalust/seq-input-healthcheck git True + seq-input-healthcheck.png LICENSE 9 enable @@ -31,6 +31,7 @@ +