Skip to content

Commit

Permalink
Merge pull request #16 from datalust/dev
Browse files Browse the repository at this point in the history
1.2.0 Release
  • Loading branch information
nblumhardt authored Dec 19, 2019
2 parents 3fafb08 + c3c23cc commit dfd2a52
Show file tree
Hide file tree
Showing 14 changed files with 300 additions and 17 deletions.
3 changes: 3 additions & 0 deletions seq-input-healthcheck.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Datalust/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=lepv/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
27 changes: 25 additions & 2 deletions src/Seq.Input.HealthCheck/HealthCheckInput.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
using System;
// Copyright 2019 Datalust and 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.Collections.Generic;
using System.IO;
using System.Net.Http;
Expand Down Expand Up @@ -35,6 +49,14 @@ public class HealthCheckInput : SeqApp, IPublishJson, IDisposable
"response. The response must be UTF-8 `application/json` for this to be applied.")]
public string DataExtractionExpression { get; set; }

[SeqAppSetting(
DisplayName = "Bypass HTTP caching",
IsOptional = true,
HelpText = "If selected, the unique probe id will be appended to the target URL query string as " +
"`" + HttpHealthCheck.ProbeIdParameterName + "`, in order to disable any " +
"intermediary HTTP caching. The `Cache-Control: no-store` header will also be sent.")]
public bool BypassHttpCaching { get; set; }

public void Start(TextWriter inputWriter)
{
_httpClient = HttpHealthCheckClient.Create();
Expand All @@ -51,7 +73,8 @@ public void Start(TextWriter inputWriter)
_httpClient,
App.Title,
targetUrl,
extractor);
extractor,
BypassHttpCaching);

_healthCheckTasks.Add(new HealthCheckTask(
healthCheck,
Expand Down
16 changes: 15 additions & 1 deletion src/Seq.Input.HealthCheck/HealthCheckReporter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
using System;
// Copyright 2019 Datalust and 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.IO;
using Newtonsoft.Json;

Expand Down
26 changes: 24 additions & 2 deletions src/Seq.Input.HealthCheck/HealthCheckResult.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
using System;
// Copyright 2019 Datalust and 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.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Expand Down Expand Up @@ -31,27 +45,33 @@ class HealthCheckResult
public int? StatusCode { get; }
public string ContentType { get; }
public long? ContentLength { get; }
public string ProbeId { get; }

[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string InitialContent { get; }

[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public JToken Data { get; }

[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public string ProbedUrl { get; }

public HealthCheckResult(
DateTime utcTimestamp,
string healthCheckTitle,
string method,
string targetUrl,
string outcome,
string probeId,
string level,
double elapsed,
int? statusCode,
string contentType,
long? contentLength,
string initialContent,
Exception exception,
JToken data)
JToken data,
string probedUrl)
{
if (utcTimestamp.Kind != DateTimeKind.Utc)
throw new ArgumentException("The timestamp must be UTC.", nameof(utcTimestamp));
Expand All @@ -62,6 +82,7 @@ public HealthCheckResult(
Method = method ?? throw new ArgumentNullException(nameof(method));
TargetUrl = targetUrl ?? throw new ArgumentNullException(nameof(targetUrl));
Outcome = outcome ?? throw new ArgumentNullException(nameof(outcome));
ProbeId = probeId ?? throw new ArgumentNullException(nameof(probeId));

Level = level;
Elapsed = elapsed;
Expand All @@ -71,6 +92,7 @@ public HealthCheckResult(
InitialContent = initialContent;
Exception = exception?.ToString();
Data = data;
ProbedUrl = probedUrl;
}
}
}
16 changes: 15 additions & 1 deletion src/Seq.Input.HealthCheck/HealthCheckTask.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
using System;
// Copyright 2019 Datalust and 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.Threading;
using System.Threading.Tasks;
using Serilog;
Expand Down
42 changes: 38 additions & 4 deletions src/Seq.Input.HealthCheck/HttpHealthCheck.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
using System;
// Copyright 2019 Datalust and 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.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Seq.Input.HealthCheck.Util;

namespace Seq.Input.HealthCheck
{
Expand All @@ -14,19 +30,23 @@ class HttpHealthCheck
readonly string _title;
readonly string _targetUrl;
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);
const int InitialContentChars = 16;
const string OutcomeSucceeded = "succeeded", OutcomeFailed = "failed";

public HttpHealthCheck(HttpClient httpClient, string title, string targetUrl, JsonDataExtractor extractor)
public HttpHealthCheck(HttpClient httpClient, string title, string targetUrl, JsonDataExtractor extractor, bool bypassHttpCaching)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_title = title ?? throw new ArgumentNullException(nameof(title));
_targetUrl = targetUrl ?? throw new ArgumentNullException(nameof(targetUrl));
_extractor = extractor;
_bypassHttpCaching = bypassHttpCaching;
}

public async Task<HealthCheckResult> CheckNow(CancellationToken cancel)
Expand All @@ -40,12 +60,24 @@ public async Task<HealthCheckResult> CheckNow(CancellationToken cancel)
string initialContent = null;
JToken data = null;

var probeId = Nonce.Generate(12);
var probedUrl = _bypassHttpCaching ?
UrlHelper.AppendParameter(_targetUrl, ProbeIdParameterName, probeId) :
_targetUrl;

var utcTimestamp = DateTime.UtcNow;
var sw = Stopwatch.StartNew();

try
{
var response = await _httpClient.GetAsync(_targetUrl, cancel);
var request = new HttpRequestMessage(HttpMethod.Get, probedUrl);
request.Headers.Add("X-Correlation-ID", probeId);

if (_bypassHttpCaching)
request.Headers.CacheControl = new CacheControlHeaderValue { NoStore = true };

var response = await _httpClient.SendAsync(request, cancel);

statusCode = (int)response.StatusCode;
contentType = response.Content.Headers.ContentType?.ToString();
contentLength = response.Content.Headers.ContentLength;
Expand Down Expand Up @@ -73,14 +105,16 @@ public async Task<HealthCheckResult> CheckNow(CancellationToken cancel)
"GET",
_targetUrl,
outcome,
probeId,
level,
sw.Elapsed.TotalMilliseconds,
statusCode,
contentType,
contentLength,
initialContent,
exception,
data);
data,
_targetUrl == probedUrl ? null : probedUrl);
}

// Either initial content, or extracted data
Expand Down
16 changes: 15 additions & 1 deletion src/Seq.Input.HealthCheck/HttpHealthCheckClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
using System.Net.Http;
// Copyright 2019 Datalust and 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.Net.Http;

namespace Seq.Input.HealthCheck
{
Expand Down
18 changes: 16 additions & 2 deletions src/Seq.Input.HealthCheck/JsonDataExtractor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
using System;
// Copyright 2019 Datalust and 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.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Expand All @@ -11,7 +25,7 @@ namespace Seq.Input.HealthCheck
{
public class JsonDataExtractor
{
static JsonValueFormatter ValueFormatter = new JsonValueFormatter("$type");
static readonly JsonValueFormatter ValueFormatter = new JsonValueFormatter("$type");
static readonly JsonSerializer Serializer = JsonSerializer.Create(new JsonSerializerSettings
{
DateParseHandling = DateParseHandling.None
Expand Down
3 changes: 3 additions & 0 deletions src/Seq.Input.HealthCheck/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Seq.Input.HealthCheck.Tests")]
9 changes: 5 additions & 4 deletions src/Seq.Input.HealthCheck/Seq.Input.HealthCheck.csproj
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<VersionPrefix>1.1.0</VersionPrefix>
<VersionPrefix>1.2.0</VersionPrefix>
<Description>Seq Health Check: periodically GET an HTTP resource and publish response metrics to Seq.</Description>
<Authors>Datalust and Contributors</Authors>
<PackageTags>seq-app</PackageTags>
Expand All @@ -16,10 +16,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
<PackageReference Include="newtonsoft.json" Version="12.0.1" />
<PackageReference Include="Seq.Apps" Version="5.1.0" />
<PackageReference Include="Serilog" Version="2.8.0" />
<PackageReference Include="Serilog.Filters.Expressions" Version="2.0.1-dev-00046" />
<PackageReference Include="Serilog" Version="2.9.0" />
<PackageReference Include="Serilog.Filters.Expressions" Version="2.1.0" />
<PackageReference Include="Serilog.Formatting.Compact.Reader" Version="1.0.3" />
</ItemGroup>

Expand Down
53 changes: 53 additions & 0 deletions src/Seq.Input.HealthCheck/Util/Nonce.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2019 Datalust and 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.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;

namespace Seq.Input.HealthCheck.Util
{
static class Nonce
{
public static string Generate(int chars)
{
if (chars < 0) throw new ArgumentOutOfRangeException(nameof(chars));
return new string(GenerateChars(chars).ToArray());
}

static IEnumerable<char> GenerateChars(int count)
{
var rem = count;
var buf = new byte[128];
using (var rng = RandomNumberGenerator.Create())
{
while (rem > 0)
{
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))
{
yield return c;
rem--;
}
}
}
}
}
}
}
Loading

0 comments on commit dfd2a52

Please sign in to comment.