├── xunit.runner.json
├── global.json
├── tools
└── packages.config
├── sandbox
└── HealthSandbox
│ ├── appsettings.json
│ ├── SampleHealthStatusReporter.cs
│ ├── HealthChecks
│ ├── SampleHealthCheck.cs
│ ├── SampleCachedHealthCheck.cs
│ └── SampleQuiteTimeHealthCheck.cs
│ └── HealthSandbox.csproj
├── ISSUE_TEMPLATE.md
├── NuGet.config
├── version.props
├── test
├── App.Metrics.Health.Formatters.Json.Facts
│ ├── JsonFiles
│ │ ├── healthstatus_null_healthy.json
│ │ ├── healthstatus_null_unhealthy.json
│ │ └── healthstatus.json
│ ├── App.Metrics.Health.Formatters.Json.Facts.csproj
│ └── Helpers
│ │ └── TestHelperExtensions.cs
├── App.Metrics.Health.Facts
│ ├── TestHelpers
│ │ ├── IDatabase.cs
│ │ ├── Database.cs
│ │ ├── SampleHealthCheck.cs
│ │ ├── IgnoreAttributeHealthCheck.cs
│ │ ├── TestIgnoreAttributeHealthCheck.cs
│ │ ├── DatabaseHealthCheck.cs
│ │ └── TestReporter.cs
│ ├── App.Metrics.Health.Facts.csproj
│ ├── App.Metrics.Health.Facts.csproj.DotSettings
│ ├── HealthCheckResultTests.cs
│ ├── Builders
│ │ ├── HealthReportingBuilderTests.cs
│ │ ├── HealthBuilderTests.cs
│ │ ├── HealthConfigurationBuilderTests.cs
│ │ └── HealthCheckBuilderTests.cs
│ └── HealthCheckRunnerTests.cs
├── App.Metrics.Health.Formatters.Ascii.Facts
│ ├── App.Metrics.Health.Formatters.Ascii.Facts.csproj
│ └── HealthStatusTextWriterTests.cs
├── App.Metrics.Health.Checks.Process.Facts
│ └── App.Metrics.Health.Checks.Process.Facts.csproj
└── Directory.Build.props
├── src
├── Directory.Build.props
├── App.Metrics.Health.Abstractions
│ ├── IHealth.cs
│ ├── App.Metrics.Health.Abstractions.csproj.DotSettings
│ ├── Internal
│ │ ├── ExcludeFromCodeCoverageAttribute.cs
│ │ ├── AppMetricsHealthTaskHelper.cs
│ │ └── HealthReporterCollection.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Serialization
│ │ ├── IHealthStatusWriter.cs
│ │ └── HealthStatusSerializer.cs
│ ├── Formatters
│ │ ├── IHealthOutputFormatter.cs
│ │ ├── HealthMediaTypeValue.cs
│ │ └── HealthFormatterCollection.cs
│ ├── IReportHealthStatus.cs
│ ├── HealthCheckStatusExtensions.cs
│ ├── HealthCheckStatus.cs
│ ├── IRunHealthChecks.cs
│ ├── Builder
│ │ ├── IHealthCheckBuilder.cs
│ │ ├── Extensions
│ │ │ ├── CacheHealthCheckBuilderExtensions.cs
│ │ │ └── QuiteTimeHealthCheckBuilderExtensions.cs
│ │ ├── IHealthBuilder.cs
│ │ ├── IHealthReportingBuilder.cs
│ │ ├── IHealthConfigurationBuilder.cs
│ │ └── IHealthOutputFormattingBuilder.cs
│ ├── App.Metrics.Health.Abstractions.csproj
│ ├── HealthOptions.cs
│ ├── HealthStatus.cs
│ ├── IHealthRoot.cs
│ ├── HealthConstants.cs
│ └── HealthCheckCached.cs
├── App.Metrics.Health.Core
│ ├── App.Metrics.Health.Core.csproj.DotSettings
│ ├── Internal
│ │ ├── DefaultHealth.cs
│ │ ├── Extensions
│ │ │ ├── HealthOptionsExtensions.cs
│ │ │ └── AppMetricsHealthLoggerExtensions.cs
│ │ ├── NoOp
│ │ │ └── NoOpHealthCheckRunner.cs
│ │ ├── DefaultHealthCheckRunner.cs
│ │ └── KeyValuePairHealthOptions.cs
│ ├── App.Metrics.Health.Core.csproj
│ ├── HealthRoot.cs
│ └── Builder
│ │ ├── HealthReportingBuilder.cs
│ │ ├── HealthCheckBuilder.cs
│ │ ├── HealthOutputFormattingBuilder.cs
│ │ ├── HealthConfigurationBuilder.cs
│ │ └── HealthBuilder.cs
├── App.Metrics.Health.Formatters.Ascii
│ ├── App.Metrics.Health.Formatters.Ascii.csproj.DotSettings
│ ├── Internal
│ │ └── HealthStatusFormatterConstants.cs
│ ├── App.Metrics.Health.Formatters.Ascii.csproj
│ ├── HealthTextOptions.cs
│ ├── Builder
│ │ └── HealthTextOutputFormatterBuilder.cs
│ ├── HealthStatusTextOutputFormatter.cs
│ └── HealthStatusTextWriter.cs
├── App.Metrics.Health.Formatters.Json
│ ├── App.Metrics.Health.Formatters.Json.csproj.DotSettings
│ ├── HealthJsonOptions.cs
│ ├── HealthStatusData.cs
│ ├── App.Metrics.Health.Formatters.Json.csproj
│ ├── DefaultJsonSerializerSettings.cs
│ ├── Builder
│ │ └── HealthJsonOutputFormatterBuilder.cs
│ ├── HealthStatusJsonOutputFormatter.cs
│ └── Converters
│ │ └── HealthStatusConverter.cs
├── App.Metrics.Health.Reporting.Metrics
│ ├── Internal
│ │ ├── HealthReportingConstants.cs
│ │ └── ApplicationHealthMetricRegistry.cs
│ ├── HealthAsMetricsOptions.cs
│ ├── App.Metrics.Health.Reporting.Metrics.csproj
│ ├── Builder
│ │ └── HealthResultsAsMetricsBuilderExtensions.cs
│ └── HealthResultsAsMetricsReporter.cs
├── App.Metrics.Health.Reporting.Slack
│ ├── Internal
│ │ ├── SlackAttachmentFields.cs
│ │ ├── SlackPayload.cs
│ │ ├── SlackAttachment.cs
│ │ ├── DefaultJsonSerializerSettings.cs
│ │ └── JsonContent.cs
│ ├── App.Metrics.Health.Reporting.Slack.csproj
│ ├── Builder
│ │ └── SlackHealthAlerterBuilderExtensions.cs
│ └── SlackHealthAlertOptions.cs
├── App.Metrics.Health.Checks.Http
│ └── App.Metrics.Health.Checks.Http.csproj
├── App.Metrics.Health
│ ├── App.Metrics.Health.csproj
│ └── AppMetricsHealth.cs
├── App.Metrics.Health.Checks.Sql
│ └── App.Metrics.Health.Checks.Sql.csproj
├── App.Metrics.Health.Checks.Network
│ ├── App.Metrics.Health.Checks.Network.csproj
│ └── PingHealthCheckBuilderExtensions.cs
└── App.Metrics.Health.Checks.Process
│ └── App.Metrics.Health.Checks.Process.csproj
├── benchmarks
├── App.Metrics.Health.Benchmarks
│ ├── Support
│ │ ├── BenchmarksAssemblyMarker.cs
│ │ ├── TestConfigs.cs
│ │ ├── BenchmarkTestRunner.cs
│ │ ├── OutputLogger.cs
│ │ ├── SimpleBenchmarkRunner.cs
│ │ └── BenchmarkTestExecutor.cs
│ ├── Configs
│ │ └── DefaultConfig.cs
│ ├── DefaultBenchmarkBase.cs
│ ├── BenchmarkDotNetBenchmarks
│ │ └── MeasureReadHealthStatusBenchmark.cs
│ ├── XunitHarness
│ │ └── Health.cs
│ ├── Fixtures
│ │ └── HealthTestFixture.cs
│ ├── Facts
│ │ └── HealthCheck.cs
│ └── App.Metrics.Health.Benchmarks.csproj
└── App.Metrics.Benchmarks.Runner
│ ├── BenchmarkDotNet.Artifacts
│ └── results
│ │ └── MeasureReadHealthStatusBenchmark-report-github.md
│ ├── App.Metrics.Benchmarks.Runner.csproj
│ └── Program.cs
├── GitReleaseManager.yaml
├── run-benchmarks.ps1
├── app-metrics.licenseheader
├── stylecop.json
├── PULL_REQUEST_TEMPLATE.md
├── .travis.yml
├── appveyor.yml
├── .gitignore
├── CONTRIBUTING.md
└── .gitattributes
/xunit.runner.json:
--------------------------------------------------------------------------------
1 | {
2 | }
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "projects": [ "src", "test", "benchmarks", "sandbox" ],
3 | "sdk": {
4 | "version": "2.1.300"
5 | }
6 | }
--------------------------------------------------------------------------------
/tools/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/sandbox/HealthSandbox/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "HealthOptions": {
3 | "Enabled": true,
4 | "ApplicationName": "http://health.local.com"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Before logging a new issue, please have a quick read through the [contribution guidlines](https://github.com/alhardy/AppMetrics/blob/master/CONTRIBUTING.md).
2 |
--------------------------------------------------------------------------------
/NuGet.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/version.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 2.1.0
5 |
6 |
7 |
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Formatters.Json.Facts/JsonFiles/healthstatus_null_healthy.json:
--------------------------------------------------------------------------------
1 | {
2 | "healthy": {
3 | "test_one_healthy": "first check was good",
4 | "test_two_healthy": "second check was good"
5 | },
6 | "status": "Healthy"
7 | }
--------------------------------------------------------------------------------
/src/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Formatters.Json.Facts/JsonFiles/healthstatus_null_unhealthy.json:
--------------------------------------------------------------------------------
1 | {
2 | "status": "Unhealthy",
3 | "unhealthy": {
4 | "test_three_unhealthy": "something failed",
5 | "test_four_unhealthy": "something else failed"
6 | }
7 | }
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/TestHelpers/IDatabase.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | namespace App.Metrics.Health.Facts.TestHelpers
6 | {
7 | public interface IDatabase
8 | {
9 | void Ping();
10 | }
11 | }
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/App.Metrics.Health.Facts.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(StandardTest)
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/TestHelpers/Database.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | namespace App.Metrics.Health.Facts.TestHelpers
6 | {
7 | public class Database : IDatabase
8 | {
9 | public void Ping() { }
10 | }
11 | }
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Health.Benchmarks/Support/BenchmarksAssemblyMarker.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | namespace App.Metrics.Health.Benchmarks.Support
6 | {
7 | public class BenchmarksAssemblyMarker
8 | {
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/IHealth.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Collections.Generic;
6 |
7 | namespace App.Metrics.Health
8 | {
9 | public interface IHealth
10 | {
11 | IEnumerable Checks { get; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Formatters.Json.Facts/JsonFiles/healthstatus.json:
--------------------------------------------------------------------------------
1 | {
2 | "degraded": {
3 | "test_five_degraded": "degrading service"
4 | },
5 | "healthy": {
6 | "test_one_healthy": "first check was good",
7 | "test_two_healthy": "second check was good"
8 | },
9 | "status": "Unhealthy",
10 | "unhealthy": {
11 | "test_three_unhealthy": "something failed",
12 | "test_four_unhealthy": "something else failed"
13 | }
14 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/App.Metrics.Health.Core.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | CSharp71
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/App.Metrics.Health.Facts.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | CSharp71
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/App.Metrics.Health.Abstractions.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | CSharp71
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Ascii/App.Metrics.Health.Formatters.Ascii.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | CSharp71
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Json/App.Metrics.Health.Formatters.Json.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | CSharp71
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Json/HealthJsonOptions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using Newtonsoft.Json;
6 |
7 | namespace App.Metrics.Health.Formatters.Json
8 | {
9 | public class HealthJsonOptions
10 | {
11 | public JsonSerializerSettings SerializerSettings { get; } =
12 | DefaultJsonSerializerSettings.CreateSerializerSettings();
13 | }
14 | }
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Formatters.Ascii.Facts/App.Metrics.Health.Formatters.Ascii.Facts.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(StandardTest)
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Checks.Process.Facts/App.Metrics.Health.Checks.Process.Facts.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(StandardTest)
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Metrics/Internal/HealthReportingConstants.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | namespace App.Metrics.Health.Reporting.Metrics.Internal
6 | {
7 | public static class HealthReportingConstants
8 | {
9 | public static class TagKeys
10 | {
11 | public const string HealthCheckName = "health_check_name";
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/GitReleaseManager.yaml:
--------------------------------------------------------------------------------
1 | create:
2 | include-footer: false
3 | footer-heading:
4 | footer-content:
5 | footer-includes-milestone: false
6 | milestone-replace-text:
7 | export:
8 | include-created-date-in-title: false
9 | created-date-string-format:
10 | perform-regex-removal: false
11 | regex-text:
12 | multiline-regex: false
13 | issue-labels-include:
14 | - bug
15 | - new feature
16 | - enhancement
17 | - breaking change
18 | - documentation
19 | - refactoring
20 | - investigation
21 | issue-labels-exclude:
22 | - wontfix
23 | - duplicate
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Internal/ExcludeFromCodeCoverageAttribute.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | #if NETSTANDARD1_1 || NETSTANDARD1_3 || NETSTANDARD1_6
6 |
7 | // ReSharper disable CheckNamespace
8 | namespace System.Diagnostics.CodeAnalysis
9 | // ReSharper restore CheckNamespace
10 | {
11 | internal class ExcludeFromCodeCoverageAttribute : Attribute { }
12 | }
13 |
14 | #endif
15 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Ascii/Internal/HealthStatusFormatterConstants.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | namespace App.Metrics.Health.Formatters.Ascii.Internal
6 | {
7 | public class HealthStatusFormatterConstants
8 | {
9 | public static class OutputFormatting
10 | {
11 | public const int Padding = 20;
12 | public const string Separator = "=";
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/run-benchmarks.ps1:
--------------------------------------------------------------------------------
1 | Push-Location $PSScriptRoot
2 |
3 | #.\build.cmd
4 |
5 | Remove-Item .\benchmark-results -Force -Recurse
6 |
7 | foreach ($test in ls benchmarks/*.Benchmarks) {
8 | Push-Location $test
9 |
10 | Remove-Item BenchmarkDotNet.Artifacts -Force -Recurse
11 |
12 | echo "perf: Running benchmark test project in $test"
13 |
14 | & dotnet test -c Release
15 | if($LASTEXITCODE -ne 0) { exit 2 }
16 |
17 | Pop-Location
18 |
19 | Copy-Item $test\BenchmarkDotNet.Artifacts\results -Destination .\benchmark-results -Recurse -Force -Filter "*.md"
20 | }
21 |
22 |
23 | Pop-Location
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Runtime.CompilerServices;
6 |
7 | [assembly: InternalsVisibleTo("App.Metrics.Health.Core, PublicKey=00240000048000009400000006020000002400005253413100040000010001000961061aa9f970163db728a9792c1cfb7be51f2f986054c676345f9e43f95af5f3114b1962d10888a3ea1dff99bf56bce565f887cb4b004fc44ccb7335700260012a65b9cdd090e6b60c8c67c434ca49563c82c66695f8dc0776770bfaf481ef816767b7dd67d083960a6fcfe33c3c0e1cc198fe7a13b3283133d21b3435ebbb")]
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Serialization/IHealthStatusWriter.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 |
7 | namespace App.Metrics.Health.Serialization
8 | {
9 | public interface IHealthStatusWriter : IDisposable
10 | {
11 | ///
12 | /// Writes the specified .
13 | ///
14 | /// The health status to write.
15 | void Write(HealthStatus healthStatus);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/Internal/DefaultHealth.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Collections.Generic;
6 | using System.Linq;
7 |
8 | namespace App.Metrics.Health.Internal
9 | {
10 | public class DefaultHealth : IHealth
11 | {
12 | public DefaultHealth(IEnumerable checks)
13 | {
14 | Checks = checks ?? Enumerable.Empty();
15 | }
16 |
17 | ///
18 | public IEnumerable Checks { get; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Formatters/IHealthOutputFormatter.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.IO;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace App.Metrics.Health.Formatters
10 | {
11 | public interface IHealthOutputFormatter
12 | {
13 | HealthMediaTypeValue MediaType { get; }
14 |
15 | Task WriteAsync(
16 | Stream output,
17 | HealthStatus healthStatus,
18 | CancellationToken cancellationToken = default);
19 | }
20 | }
--------------------------------------------------------------------------------
/app-metrics.licenseheader:
--------------------------------------------------------------------------------
1 | extensions: designer.cs generated.cs
2 | extensions: .cs .cpp .h
3 | // Copyright (c) Allan hardy. All rights reserved.
4 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
5 |
6 | extensions: .cshtml
7 | @*
8 | // Copyright (c) Allan hardy. All rights reserved.
9 | // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
10 | *@
11 |
12 | extensions: .xml .config .xsd
13 |
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Health.Benchmarks/Configs/DefaultConfig.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using BenchmarkDotNet.Configs;
6 | using BenchmarkDotNet.Diagnosers;
7 | using BenchmarkDotNet.Exporters;
8 | using BenchmarkDotNet.Jobs;
9 |
10 | namespace App.Metrics.Health.Benchmarks.Configs
11 | {
12 | public class DefaultConfig : ManualConfig
13 | {
14 | public DefaultConfig()
15 | {
16 | Add(Job.Core);
17 | Add(MarkdownExporter.GitHub);
18 | Add(new MemoryDiagnoser());
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Ascii/App.Metrics.Health.Formatters.Ascii.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | App Metrics Formatters for health check results to plain text.
5 | netstandard1.6
6 | $(TargetFrameworks);net452
7 | true
8 | appmetrics;healthchecks;ascii
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Benchmarks.Runner/BenchmarkDotNet.Artifacts/results/MeasureReadHealthStatusBenchmark-report-github.md:
--------------------------------------------------------------------------------
1 | ``` ini
2 |
3 | BenchmarkDotNet=v0.10.8, OS=Windows 10 Redstone 2 (10.0.15063)
4 | Processor=Intel Core i7-2600 CPU 3.40GHz (Sandy Bridge), ProcessorCount=8
5 | Frequency=3312790 Hz, Resolution=301.8604 ns, Timer=TSC
6 | dotnet cli version=2.0.0
7 | [Host] : .NET Core 4.6.00001.0, 64bit RyuJIT
8 | Core : .NET Core 4.6.00001.0, 64bit RyuJIT
9 |
10 | Job=Core Runtime=Core
11 |
12 | ```
13 | | Method | Mean | Error | StdDev | Gen 0 | Allocated |
14 | |----------- |---------:|----------:|----------:|-------:|----------:|
15 | | ReadHealth | 1.441 us | 0.0069 us | 0.0061 us | 0.1984 | 840 B |
16 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Internal/AppMetricsHealthTaskHelper.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | #if !NETSTANDARD1_6
6 |
7 | using System.Threading.Tasks;
8 |
9 | namespace App.Metrics.Health.Internal
10 | {
11 | public static class AppMetricsHealthTaskHelper
12 | {
13 | private static Task _completedTask;
14 |
15 | private struct VoidTaskResult { }
16 |
17 | public static Task CompletedTask()
18 | {
19 | return _completedTask ?? (_completedTask = Task.FromResult(default(VoidTaskResult)));
20 | }
21 | }
22 | }
23 | #endif
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Slack/Internal/SlackAttachmentFields.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | namespace App.Metrics.Health.Reporting.Slack.Internal
6 | {
7 | internal class SlackAttachmentFields
8 | {
9 | public string Title { get; set; }
10 |
11 | public string Value { get; set; }
12 |
13 | public bool Short { get; set; }
14 |
15 | public string ImageUrl { get; set; }
16 |
17 | public string ThumbUrl { get; set; }
18 |
19 | public string Footer { get; set; }
20 |
21 | public string FooterIcon { get; set; }
22 | }
23 | }
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/TestHelpers/SampleHealthCheck.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | namespace App.Metrics.Health.Facts.TestHelpers
9 | {
10 | public class SampleHealthCheck : HealthCheck
11 | {
12 | public SampleHealthCheck()
13 | : base("SampleHealthCheck") { }
14 |
15 | protected override ValueTask CheckAsync(CancellationToken token = default)
16 | {
17 | return new ValueTask(HealthCheckResult.Healthy("OK"));
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/stylecop.json:
--------------------------------------------------------------------------------
1 | {
2 | // ACTION REQUIRED: This file was automatically added to your project, but it
3 | // will not take effect until additional steps are taken to enable it. See the
4 | // following page for additional information:
5 | //
6 | // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md
7 | "$schema":
8 | "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
9 | "settings": {
10 | "documentationRules": {
11 | "companyName": "App Metrics Contributors",
12 | "copyrightText": "Copyright (c) {companyName}. All rights reserved.",
13 | "xmlHeader": true
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Health.Benchmarks/DefaultBenchmarkBase.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using App.Metrics.Health.Benchmarks.Configs;
6 | using App.Metrics.Health.Benchmarks.Fixtures;
7 | using BenchmarkDotNet.Attributes;
8 |
9 | namespace App.Metrics.Health.Benchmarks
10 | {
11 | [Config(typeof(DefaultConfig))]
12 | public abstract class DefaultBenchmarkBase
13 | {
14 | protected HealthTestFixture FixtureWithHealth { get; private set; }
15 |
16 | [GlobalSetup]
17 | public virtual void Setup()
18 | {
19 | FixtureWithHealth = new HealthTestFixture();
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Formatters.Json.Facts/App.Metrics.Health.Formatters.Json.Facts.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(StandardTest)
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Health.Benchmarks/BenchmarkDotNetBenchmarks/MeasureReadHealthStatusBenchmark.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Diagnostics;
6 | using BenchmarkDotNet.Attributes;
7 |
8 | namespace App.Metrics.Health.Benchmarks.BenchmarkDotNetBenchmarks
9 | {
10 | public class MeasureReadHealthStatusBenchmark : DefaultBenchmarkBase
11 | {
12 | [Benchmark]
13 | public void ReadHealth()
14 | {
15 | var result = FixtureWithHealth.HealthCheckRunner.ReadAsync().GetAwaiter().GetResult();
16 | Debug.WriteLine("Health status: " + result.Status);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Slack/Internal/SlackPayload.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Collections.Generic;
6 |
7 | namespace App.Metrics.Health.Reporting.Slack.Internal
8 | {
9 | internal class SlackPayload
10 | {
11 | public SlackPayload()
12 | {
13 | Attachments = new List();
14 | }
15 |
16 | public string Text { get; set; }
17 |
18 | public string Channel { get; set; }
19 |
20 | public string UserName { get; set; }
21 |
22 | public string IconEmoji { get; set; }
23 |
24 | public List Attachments { get; set; }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/IReportHealthStatus.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace App.Metrics.Health
10 | {
11 | public interface IReportHealthStatus
12 | {
13 | ///
14 | /// Gets interval to flush metrics values. Defaults to
15 | /// .
16 | ///
17 | TimeSpan ReportInterval { get; set; }
18 |
19 | Task ReportAsync(HealthOptions options, HealthStatus status, CancellationToken cancellationToken = default);
20 | }
21 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/Internal/Extensions/HealthOptionsExtensions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Collections.Generic;
6 |
7 | namespace App.Metrics.Health.Internal.Extensions
8 | {
9 | public static class HealthOptionsExtensions
10 | {
11 | public static IEnumerable> ToKeyValue(this HealthOptions options)
12 | {
13 | var result = new Dictionary
14 | {
15 | [KeyValuePairHealthOptions.EnabledDirective] = options.Enabled.ToString()
16 | };
17 |
18 | return result;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/TestHelpers/IgnoreAttributeHealthCheck.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace App.Metrics.Health.Facts.TestHelpers
10 | {
11 | [Obsolete]
12 | public class IgnoreAttributeHealthCheck : HealthCheck
13 | {
14 | public IgnoreAttributeHealthCheck()
15 | : base("Referencing Assembly - Sample Healthy") { }
16 |
17 | protected override ValueTask CheckAsync(CancellationToken token = default)
18 | {
19 | return new ValueTask(HealthCheckResult.Healthy("OK"));
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Health.Benchmarks/XunitHarness/Health.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using App.Metrics.Health.Benchmarks.BenchmarkDotNetBenchmarks;
6 | using App.Metrics.Health.Benchmarks.Support;
7 | using Xunit;
8 | using Xunit.Abstractions;
9 |
10 | namespace App.Metrics.Health.Benchmarks.XunitHarness
11 | {
12 | [Trait("Benchmark-Health", "ReadStatus")]
13 | public class Health
14 | {
15 | private readonly ITestOutputHelper _output;
16 |
17 | public Health(ITestOutputHelper output) { _output = output; }
18 |
19 | [Fact]
20 | public void CostOfReadingHealth() { BenchmarkTestRunner.CanCompileAndRun(_output); }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/TestHelpers/TestIgnoreAttributeHealthCheck.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace App.Metrics.Health.Facts.TestHelpers
10 | {
11 | [Obsolete]
12 | public class TestIgnoreAttributeHealthCheck : HealthCheck
13 | {
14 | public TestIgnoreAttributeHealthCheck()
15 | : base("Referencing Assembly - Sample Healthy") { }
16 |
17 | protected override ValueTask CheckAsync(CancellationToken token = default)
18 | {
19 | return new ValueTask(HealthCheckResult.Healthy("OK"));
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/HealthCheckStatusExtensions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | namespace App.Metrics.Health
6 | {
7 | public static class HealthCheckStatusExtensions
8 | {
9 | public static bool IsDegraded(this HealthCheckStatus status) { return status == HealthCheckStatus.Degraded; }
10 |
11 | public static bool IsHealthy(this HealthCheckStatus status) { return status == HealthCheckStatus.Healthy; }
12 |
13 | public static bool IsIgnored(this HealthCheckStatus status) { return status == HealthCheckStatus.Ignored; }
14 |
15 | public static bool IsUnhealthy(this HealthCheckStatus status) { return status == HealthCheckStatus.Unhealthy; }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Json/HealthStatusData.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Collections.Generic;
6 |
7 | namespace App.Metrics.Health.Formatters.Json
8 | {
9 | public sealed class HealthStatusData
10 | {
11 | public Dictionary Healthy { get; set; } = new Dictionary();
12 |
13 | public Dictionary Degraded { get; set; } = new Dictionary();
14 |
15 | public Dictionary Unhealthy { get; set; } = new Dictionary();
16 |
17 | public Dictionary Ignored { get; set; } = new Dictionary();
18 |
19 | public string Status { get; set; }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Json/App.Metrics.Health.Formatters.Json.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | App Metrics Formatters for health check results to JSON using Newtonsoft.Json.
5 | netstandard1.6
6 | $(TargetFrameworks);net452
7 | true
8 | appmetrics;healthcheck;json
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/sandbox/HealthSandbox/SampleHealthStatusReporter.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using App.Metrics.Health;
9 |
10 | namespace HealthSandbox
11 | {
12 | public class SampleHealthStatusReporter : IReportHealthStatus
13 | {
14 | ///
15 | public TimeSpan ReportInterval { get; set; }
16 |
17 | ///
18 | public Task ReportAsync(HealthOptions options, HealthStatus status, CancellationToken cancellationToken = default)
19 | {
20 | Console.WriteLine($"{options.ApplicationName} - Overall status: {status.Status}");
21 |
22 | return Task.CompletedTask;
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Health.Benchmarks/Fixtures/HealthTestFixture.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Threading.Tasks;
7 | using App.Metrics.Health.Builder;
8 |
9 | namespace App.Metrics.Health.Benchmarks.Fixtures
10 | {
11 | public class HealthTestFixture : IDisposable
12 | {
13 | public HealthTestFixture()
14 | {
15 | var health = new HealthBuilder()
16 | .HealthChecks.AddCheck("test", () => new ValueTask(HealthCheckResult.Healthy()))
17 | .Build();
18 |
19 | HealthCheckRunner = health.HealthCheckRunner;
20 | }
21 |
22 | public IRunHealthChecks HealthCheckRunner { get; }
23 |
24 | public void Dispose() { }
25 | }
26 | }
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Benchmarks.Runner/App.Metrics.Benchmarks.Runner.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | exe
7 | netcoreapp2.1
8 | $(TargetFrameworks);net461
9 | latest
10 | true
11 | true
12 | false
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Thanks for helping out :+1:
2 |
3 | Before submitting a pull request, please have a quick read through the [contribution guidlines](https://github.com/alhardy/AppMetrics/blob/master/CONTRIBUTING.md) and provide the following information, where appropriate replace the `[ ]` with a `[X]`
4 |
5 | ### The issue or feature being addressed
6 |
7 | - link to the issue/feature using it's github issue number #number (if an issue/feature does not exist, please create it first)
8 |
9 | ### Details on the issue fix or feature implementation
10 |
11 | - provide some details here
12 |
13 |
14 | ### Confirm the following
15 |
16 | - [ ] I have ensured that I have merged the latest changes from the dev branch
17 | - [ ] I have successfully run a [local build](https://github.com/alhardy/AppMetrics#how-to-build)
18 | - [ ] I have included unit tests for the issue/feature
19 | - [ ] I have included the github issue number in my commits
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/HealthCheckStatus.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | namespace App.Metrics.Health
6 | {
7 | ///
8 | /// Possible status values of a health check result
9 | ///
10 | public enum HealthCheckStatus
11 | {
12 | ///
13 | /// The check is healthy
14 | ///
15 | Healthy,
16 |
17 | ///
18 | /// The check is degraded, failing but not critical
19 | ///
20 | Degraded,
21 |
22 | ///
23 | /// The check is unhealthy
24 | ///
25 | Unhealthy,
26 |
27 | ///
28 | /// The check was ignored
29 | ///
30 | Ignored
31 | }
32 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Metrics/HealthAsMetricsOptions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 |
7 | namespace App.Metrics.Health.Reporting.Metrics
8 | {
9 | public class HealthAsMetricsOptions
10 | {
11 | ///
12 | /// Gets or sets the health status reporting interval.
13 | ///
14 | ///
15 | /// If not set reporting interval will be set to the .
16 | ///
17 | ///
18 | /// The to wait between reporting health status.
19 | ///
20 | public TimeSpan ReportInterval { get; set; }
21 |
22 | public bool Enabled { get; set; } = true;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Checks.Http/App.Metrics.Health.Checks.Http.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | App Metrics HTTP health checks.
5 | netstandard1.6
6 | $(TargetFrameworks);net452
7 | true
8 | appmetrics;healthchecks;http
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/test/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | netcoreapp2.1
6 | $(StandardTest);net461
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health/App.Metrics.Health.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | App Metrics Health is an open-source .NET Standard library used to define and report results on user defined health checks.
5 | netstandard1.6
6 | $(TargetFrameworks);net452
7 | true
8 | appmetrics;healthchecks
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/TestHelpers/DatabaseHealthCheck.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | namespace App.Metrics.Health.Facts.TestHelpers
9 | {
10 | public class DatabaseHealthCheck : HealthCheck
11 | {
12 | private readonly IDatabase _database;
13 |
14 | public DatabaseHealthCheck(IDatabase database)
15 | : base("DatabaseCheck")
16 | {
17 | _database = database;
18 | }
19 |
20 | protected override ValueTask CheckAsync(CancellationToken token = default)
21 | {
22 | // exceptions will be caught and the result will be unhealthy
23 | _database.Ping();
24 |
25 | return new ValueTask(HealthCheckResult.Healthy());
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | notifications:
2 | email:
3 | recipients:
4 | - al_hardy@live.com.au
5 |
6 | language: csharp
7 | dotnet: 2.0.0-preview1-005977
8 | os:
9 | # - osx
10 | - linux
11 | before_script:
12 | - chmod a+x ./build.sh
13 | script:
14 | - ./build.sh
15 |
16 | # .NET CLI require Ubuntu 14.04
17 | sudo: required
18 | dist: trusty
19 | env:
20 | global:
21 | - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
22 | - DOTNET_CLI_TELEMETRY_OPTOUT: 1
23 | addons:
24 | apt:
25 | packages:
26 | - gettext
27 | - libcurl4-openssl-dev
28 | - libicu-dev
29 | - libssl-dev
30 | - libunwind8
31 |
32 | # .NET CLI require OSX 10.10
33 | # osx_image: xcode7.1
34 | # before_install:
35 | # - if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; fi
36 |
37 | mono:
38 | - 4.6.0
39 |
40 | cache:
41 | directories:
42 | - src/packages
43 | - tools
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Health.Benchmarks/Facts/HealthCheck.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Diagnostics;
6 | using App.Metrics.Health.Benchmarks.Fixtures;
7 | using App.Metrics.Health.Benchmarks.Support;
8 | using Xunit;
9 |
10 | namespace App.Metrics.Health.Benchmarks.Facts
11 | {
12 | public class HealthCheck : IClassFixture
13 | {
14 | private readonly HealthTestFixture _fixture;
15 |
16 | public HealthCheck(HealthTestFixture fixture) { _fixture = fixture; }
17 |
18 | [Fact]
19 | public void ReadHealthStatus()
20 | {
21 | SimpleBenchmarkRunner.Run(
22 | async () =>
23 | {
24 | var result = await _fixture.HealthCheckRunner.ReadAsync();
25 | Debug.WriteLine("Health status: " + result.Status);
26 | });
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Serialization/HealthStatusSerializer.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | namespace App.Metrics.Health.Serialization
6 | {
7 | ///
8 | /// Serializes into the different formats.
9 | ///
10 | public class HealthStatusSerializer
11 | {
12 | ///
13 | /// Serializes the specified and writes the health status using the specified
14 | /// .
15 | ///
16 | /// The used to write the health status.
17 | /// The to serilize.
18 | public void Serialize(IHealthStatusWriter writer, HealthStatus healthStatus) { writer.Write(healthStatus); }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Benchmarks.Runner/Program.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Reflection;
7 | using App.Metrics.Health.Benchmarks.Support;
8 | using BenchmarkDotNet.Running;
9 |
10 | namespace App.Metrics.Benchmarks.Runner
11 | {
12 | // ReSharper disable UnusedMember.Global
13 | public class Program
14 | {
15 | public static void Main(string[] args)
16 | {
17 | ConsoleKeyInfo keyInfo;
18 |
19 | do
20 | {
21 | BenchmarkSwitcher.FromAssembly(typeof(BenchmarksAssemblyMarker).GetTypeInfo().Assembly).Run(args);
22 |
23 | Console.WriteLine("Press ESC to quit, otherwise any key to continue...");
24 |
25 | keyInfo = Console.ReadKey(true);
26 | }
27 | while (keyInfo.Key != ConsoleKey.Escape);
28 | }
29 | }
30 |
31 | // ReSharper restore UnusedMember.Global
32 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/IRunHealthChecks.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | namespace App.Metrics.Health
9 | {
10 | ///
11 | /// Provides access to the current health status of the application by executing registered s
12 | ///
13 | public interface IRunHealthChecks
14 | {
15 | ///
16 | /// Executes all registered health checks within the application
17 | ///
18 | /// The cancellation token.
19 | ///
20 | /// The current health status of the application. A single health check failure will result in an un-healthy
21 | /// result
22 | ///
23 | ValueTask ReadAsync(CancellationToken cancellationToken = default);
24 | }
25 | }
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Health.Benchmarks/App.Metrics.Health.Benchmarks.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | netcoreapp2.1
7 | $(TargetFrameworks);net461
8 | latest
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Checks.Sql/App.Metrics.Health.Checks.Sql.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | App Metrics SQL health checks.
5 | netstandard1.6
6 | $(TargetFrameworks);net452
7 | true
8 | appmetrics;healthchecks;SQL
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/App.Metrics.Health.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | App Metrics health core components.
5 | netstandard1.6
6 | $(TargetFrameworks);net452
7 | App.Metrics.Health
8 | true
9 | appmetrics;healthchecks
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Checks.Network/App.Metrics.Health.Checks.Network.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | App Metrics network health checks.
5 | netstandard1.6
6 | $(TargetFrameworks);net452
7 | true
8 | appmetrics;healthchecks;http
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Checks.Process/App.Metrics.Health.Checks.Process.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | App Metrics process health checks.
5 | netstandard1.6
6 | $(TargetFrameworks);net452
7 | true
8 | appmetrics;healthchecks;http
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Slack/Internal/SlackAttachment.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Collections.Generic;
6 |
7 | namespace App.Metrics.Health.Reporting.Slack.Internal
8 | {
9 | internal class SlackAttachment
10 | {
11 | public SlackAttachment()
12 | {
13 | Fields = new List();
14 | }
15 |
16 | public string Fallback { get; set; }
17 |
18 | public string Color { get; set; }
19 |
20 | public string PreText { get; set; }
21 |
22 | public string AuthorName { get; set; }
23 |
24 | public string AuthorLink { get; set; }
25 |
26 | public string AuthorIcon { get; set; }
27 |
28 | public string Title { get; set; }
29 |
30 | public string TitleLink { get; set; }
31 |
32 | public string Text { get; set; }
33 |
34 | public double Ts { get; set; }
35 |
36 | public List Fields { get; set; }
37 | }
38 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Slack/App.Metrics.Health.Reporting.Slack.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Reports App Metrics Health Status Alerts to Slack.
5 | netstandard1.6
6 | $(TargetFrameworks);net452
7 | true
8 | appmetrics;healthchecks;slack;alerts;reporting
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Metrics/App.Metrics.Health.Reporting.Metrics.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Report App Metrics Health Checks as metrics allowing results to be flush for example to the configured metrics reporter.
5 | netstandard1.6
6 | $(TargetFrameworks);net452
7 | true
8 | appmetrics;healthchecks;metrics;reporting
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/Internal/NoOp/NoOpHealthCheckRunner.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Diagnostics.CodeAnalysis;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 | #if !NETSTANDARD1_6
11 |
12 | #endif
13 |
14 | namespace App.Metrics.Health.Internal.NoOp
15 | {
16 | [ExcludeFromCodeCoverage]
17 | public sealed class NoOpHealthCheckRunner : IRunHealthChecks
18 | {
19 | ///
20 | public ValueTask ReadAsync(CancellationToken cancellationToken = default)
21 | {
22 | return new ValueTask(new HealthStatus(Enumerable.Empty()));
23 | }
24 |
25 | public Task FormatAsync(Stream output, HealthStatus status, CancellationToken cancellationToken = default)
26 | {
27 | #if NETSTANDARD1_6
28 | return Task.CompletedTask;
29 | #else
30 | return AppMetricsHealthTaskHelper.CompletedTask();
31 | #endif
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/sandbox/HealthSandbox/HealthChecks/SampleHealthCheck.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using App.Metrics.Health;
9 |
10 | namespace HealthSandbox.HealthChecks
11 | {
12 | public class SampleHealthCheck : HealthCheck
13 | {
14 | public SampleHealthCheck()
15 | : base("Random Health Check")
16 | {
17 | }
18 |
19 | ///
20 | protected override ValueTask CheckAsync(CancellationToken cancellationToken = default)
21 | {
22 | if (DateTime.UtcNow.Second <= 20)
23 | {
24 | return new ValueTask(HealthCheckResult.Degraded());
25 | }
26 |
27 | if (DateTime.UtcNow.Second >= 40)
28 | {
29 | return new ValueTask(HealthCheckResult.Unhealthy());
30 | }
31 |
32 | return new ValueTask(HealthCheckResult.Healthy());
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Builder/IHealthCheckBuilder.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | // ReSharper disable CheckNamespace
11 | namespace App.Metrics.Health
12 | // ReSharper restore CheckNamespace
13 | {
14 | public interface IHealthCheckBuilder
15 | {
16 | ///
17 | /// Gets the where App Metrics Health is configured.
18 | ///
19 | IHealthBuilder Builder { get; }
20 |
21 | IHealthBuilder AddCheck(string name, Func> check);
22 |
23 | IHealthBuilder AddCheck(string name, Func> check);
24 |
25 | IHealthBuilder AddCheck(THealthCheck check)
26 | where THealthCheck : HealthCheck;
27 |
28 | IHealthBuilder AddCheck()
29 | where THealthCheck : HealthCheck, new();
30 |
31 | IHealthBuilder AddChecks(IEnumerable checks);
32 | }
33 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Ascii/HealthTextOptions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Text;
6 |
7 | namespace App.Metrics.Health.Formatters.Ascii
8 | {
9 | public class HealthTextOptions
10 | {
11 | public HealthTextOptions()
12 | {
13 | Padding = 20;
14 | Separator = "=";
15 | Encoding = Encoding.UTF8;
16 | }
17 |
18 | ///
19 | /// Gets or sets the padding to apply on health check labels and messages
20 | ///
21 | ///
22 | /// The padding to apply on health check labels and messages
23 | ///
24 | public int Padding { get; set; }
25 |
26 | ///
27 | /// Gets or sets the separator to use between on health check labels and messages/status
28 | ///
29 | ///
30 | /// The separator to apply between on health check labels and messages/status
31 | ///
32 | public string Separator { get; set; }
33 |
34 | public Encoding Encoding { get; set; }
35 | }
36 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Metrics/Builder/HealthResultsAsMetricsBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using App.Metrics.Health.Reporting.Metrics;
6 |
7 | // ReSharper disable CheckNamespace
8 | namespace App.Metrics.Health
9 | // ReSharper restore CheckNamespace
10 | {
11 | public static class HealthResultsAsMetricsBuilderExtensions
12 | {
13 | public static IHealthBuilder ToMetrics(
14 | this IHealthReportingBuilder healthReportingBuilder,
15 | IMetrics metrics)
16 | {
17 | healthReportingBuilder.Using(new HealthResultsAsMetricsReporter(metrics));
18 |
19 | return healthReportingBuilder.Builder;
20 | }
21 |
22 | public static IHealthBuilder ToMetrics(
23 | this IHealthReportingBuilder healthReportingBuilder,
24 | IMetrics metrics,
25 | HealthAsMetricsOptions options)
26 | {
27 | healthReportingBuilder.Using(new HealthResultsAsMetricsReporter(metrics, options));
28 |
29 | return healthReportingBuilder.Builder;
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/sandbox/HealthSandbox/HealthChecks/SampleCachedHealthCheck.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using App.Metrics.Health;
9 |
10 | namespace HealthSandbox.HealthChecks
11 | {
12 | public class SampleCachedHealthCheck : HealthCheck
13 | {
14 | public SampleCachedHealthCheck()
15 | : base("Random Health Check - 1 Min Cache", cacheDuration: TimeSpan.FromMinutes(1))
16 | {
17 | }
18 |
19 | ///
20 | protected override ValueTask CheckAsync(CancellationToken cancellationToken = default)
21 | {
22 | if (DateTime.UtcNow.Second <= 20)
23 | {
24 | return new ValueTask(HealthCheckResult.Degraded());
25 | }
26 |
27 | if (DateTime.UtcNow.Second >= 40)
28 | {
29 | return new ValueTask(HealthCheckResult.Unhealthy());
30 | }
31 |
32 | return new ValueTask(HealthCheckResult.Healthy());
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Builder/Extensions/CacheHealthCheckBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | // ReSharper disable CheckNamespace
10 | namespace App.Metrics.Health
11 | // ReSharper restore CheckNamespace
12 | {
13 | public static class CacheHealthCheckBuilderExtensions
14 | {
15 | public static IHealthBuilder AddCachedCheck(
16 | this IHealthCheckBuilder builder,
17 | string name,
18 | Func> check,
19 | TimeSpan cacheDuration)
20 | {
21 | builder.AddCheck(new HealthCheck(name, check, cacheDuration));
22 |
23 | return builder.Builder;
24 | }
25 |
26 | public static IHealthBuilder AddCachedCheck(
27 | this IHealthCheckBuilder builder,
28 | string name,
29 | Func> check,
30 | TimeSpan cacheDuration)
31 | {
32 | builder.AddCheck(new HealthCheck(name, check, cacheDuration));
33 |
34 | return builder.Builder;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Slack/Builder/SlackHealthAlerterBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using App.Metrics.Health.Reporting.Slack;
7 |
8 | // ReSharper disable CheckNamespace
9 | namespace App.Metrics.Health
10 | // ReSharper restore CheckNamespace
11 | {
12 | public static class SlackHealthAlerterBuilderExtensions
13 | {
14 | public static IHealthBuilder ToSlack(
15 | this IHealthReportingBuilder healthReportingBuilder,
16 | Action optionsSetup)
17 | {
18 | var options = new SlackHealthAlertOptions();
19 |
20 | optionsSetup(options);
21 |
22 | healthReportingBuilder.Using(new SlackIncomingWebHookHealthAlerter(options));
23 |
24 | return healthReportingBuilder.Builder;
25 | }
26 |
27 | public static IHealthBuilder ToSlack(
28 | this IHealthReportingBuilder healthReportingBuilder,
29 | SlackHealthAlertOptions options)
30 | {
31 | healthReportingBuilder.Using(new SlackIncomingWebHookHealthAlerter(options));
32 |
33 | return healthReportingBuilder.Builder;
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Builder/Extensions/QuiteTimeHealthCheckBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | // ReSharper disable CheckNamespace
10 | namespace App.Metrics.Health
11 | // ReSharper restore CheckNamespace
12 | {
13 | public static class QuiteTimeHealthCheckBuilderExtensions
14 | {
15 | public static IHealthBuilder AddQuiteTimeCheck(
16 | this IHealthCheckBuilder builder,
17 | string name,
18 | Func> check,
19 | HealthCheck.QuiteTime quiteTime)
20 | {
21 | builder.AddCheck(new HealthCheck(name, check, quiteTime));
22 |
23 | return builder.Builder;
24 | }
25 |
26 | public static IHealthBuilder AddQuiteTimeCheck(
27 | this IHealthCheckBuilder builder,
28 | string name,
29 | Func> check,
30 | HealthCheck.QuiteTime quiteTime)
31 | {
32 | builder.AddCheck(new HealthCheck(name, check, quiteTime));
33 |
34 | return builder.Builder;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/sandbox/HealthSandbox/HealthChecks/SampleQuiteTimeHealthCheck.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using App.Metrics.Health;
9 |
10 | namespace HealthSandbox.HealthChecks
11 | {
12 | public class SampleQuiteTimeHealthCheck : HealthCheck
13 | {
14 | private static readonly QuiteTime QuiteAt = new QuiteTime(new TimeSpan(11, 0, 0), new TimeSpan(13, 0, 0), new[] { DayOfWeek.Monday });
15 |
16 | public SampleQuiteTimeHealthCheck()
17 | : base("Random Health Check - Quite Time", quiteTime: QuiteAt)
18 | {
19 | }
20 |
21 | ///
22 | protected override ValueTask CheckAsync(CancellationToken cancellationToken = default)
23 | {
24 | if (DateTime.UtcNow.Second <= 20)
25 | {
26 | return new ValueTask(HealthCheckResult.Degraded());
27 | }
28 |
29 | if (DateTime.UtcNow.Second >= 40)
30 | {
31 | return new ValueTask(HealthCheckResult.Unhealthy());
32 | }
33 |
34 | return new ValueTask(HealthCheckResult.Healthy());
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Health.Benchmarks/Support/TestConfigs.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using BenchmarkDotNet.Configs;
6 | using BenchmarkDotNet.Engines;
7 | using BenchmarkDotNet.Jobs;
8 |
9 | // ReSharper disable UnusedMember.Global
10 | namespace App.Metrics.Health.Benchmarks.Support
11 | {
12 | #pragma warning disable SA1649, SA1402
13 | public class SingleRunFastConfig : ManualConfig
14 | {
15 | public SingleRunFastConfig() { Add(Job.Dry); }
16 | }
17 |
18 | public class SingleRunMediumConfig : ManualConfig
19 | {
20 | public SingleRunMediumConfig() { Add(new Job(Job.Dry) { Run = { IterationCount = 5 } }); }
21 | }
22 |
23 | public class ThroughputFastConfig : ManualConfig
24 | {
25 | public ThroughputFastConfig() { Add(new Job(Job.Dry) { Run = { RunStrategy = RunStrategy.Throughput, IterationCount = 1 } }); }
26 | }
27 |
28 | public class DiagnoserConfig : ManualConfig
29 | {
30 | public DiagnoserConfig()
31 | {
32 | // Diagnosers need enough runs to collects the statistics!
33 | Add(new Job { Run = { LaunchCount = 1, WarmupCount = 1, IterationCount = 50 } });
34 | }
35 | }
36 | #pragma warning restore SA1649, SA1402
37 | // ReSharper restore UnusedMember.Global
38 | }
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Health.Benchmarks/Support/BenchmarkTestRunner.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Linq;
6 | using BenchmarkDotNet.Configs;
7 | using BenchmarkDotNet.Jobs;
8 | using BenchmarkDotNet.Loggers;
9 | using BenchmarkDotNet.Running;
10 | using Xunit;
11 | using Xunit.Abstractions;
12 |
13 | namespace App.Metrics.Health.Benchmarks.Support
14 | {
15 | internal static class BenchmarkTestRunner
16 | {
17 | internal static void CanCompileAndRun(ITestOutputHelper output)
18 | {
19 | var summary = BenchmarkRunner.Run(new SingleRunFastConfig(output));
20 |
21 | Assert.True(summary.Reports.Any());
22 | Assert.True(summary.Reports.All(report => report.ExecuteResults.All(executeResult => executeResult.FoundExecutable)));
23 | Assert.True(summary.Reports.All(report => report.AllMeasurements.Any()), "There are no available measurements");
24 | }
25 |
26 | private class SingleRunFastConfig : ManualConfig
27 | {
28 | internal SingleRunFastConfig(ITestOutputHelper output)
29 | {
30 | Add(Job.Dry);
31 | Add(ConsoleLogger.Default);
32 | Add(new OutputLogger(output));
33 | }
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Metrics/Internal/ApplicationHealthMetricRegistry.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using App.Metrics.Gauge;
6 |
7 | namespace App.Metrics.Health.Reporting.Metrics.Internal
8 | {
9 | public static class ApplicationHealthMetricRegistry
10 | {
11 | // ReSharper disable MemberCanBePrivate.Global
12 | public static readonly string Context = "Application.Health";
13 | // ReSharper restore MemberCanBePrivate.Global
14 |
15 | public static GaugeOptions Checks => new GaugeOptions
16 | {
17 | Context = Context,
18 | Name = "Results",
19 | MeasurementUnit = Unit.Items
20 | };
21 |
22 | public static GaugeOptions HealthGauge => new GaugeOptions
23 | {
24 | Context = Context,
25 | Name = "Score",
26 | MeasurementUnit = Unit.Custom("Health Score")
27 | };
28 | }
29 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/App.Metrics.Health.Abstractions.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | App Metrics Health abstractions and interfaces.
5 | netstandard1.6
6 | $(TargetFrameworks);net452
7 | App.Metrics.Health
8 | true
9 | appmetrics;healthchecks
10 |
11 |
12 |
13 | TRACE;DEBUG;NETSTANDARD2_0;LIBLOG_PORTABLE;LIBLOG_PUBLIC
14 |
15 |
16 |
17 | TRACE;RELEASE;NETSTANDARD2_0;LIBLOG_PORTABLE;LIBLOG_PUBLIC
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: 1.0.{build}
2 | image: Visual Studio 2017
3 | init:
4 | - git config --global core.autocrlf input
5 | environment:
6 | COVERALLS_REPO_TOKEN:
7 | secure: 9C17ZvEksrTRexM6zsRiNkr599sd9q4oNFacSsMvyLhdME6G+FC1SqlLXJMYW2KD
8 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
9 | DOTNET_CLI_TELEMETRY_OPTOUT: true
10 | CoverWith: OpenCover
11 | PreReleaseSuffix: alpha
12 | GitUser:
13 | secure: zAdAOUUof3XGDsdOBRYg7J7wZS44iL4VjI/MVGw+JnU=
14 | GitOwner:
15 | secure: n6W5JZ7Q/xfZC7b2k3+ORA==
16 | GitPassword:
17 | secure: fHangGPAk14u3V1eP3kg5EdekQoKdOsKC0F/A1mvh9IODHCZ92FGjfWb0U2aQ8bu
18 | build_script:
19 | - ps: .\build.ps1 -Target AppVeyor
20 | test: off
21 | artifacts:
22 | - path: artifacts/packages/*.nupkg
23 | - path: artifacts/coverage/*.xml
24 | - path: artifacts/test-results/*.trx
25 | - path: artifacts/resharper-reports/*.xml
26 | - path: artifacts/resharper-reports/*.html
27 | - path: artifacts/coverage/coverage.dcvr
28 | deploy:
29 | - provider: NuGet
30 | server: https://www.myget.org/F/appmetrics/api/v2/package
31 | api_key:
32 | secure: gIAiACgNj+JzXyLLTe3rLxZyrAB9RpC8Lw81xEjdOLXqotprqEwGiFWRipEqkpps
33 | skip_symbols: true
34 | symbol_server: https://www.myget.org/F/appmetrics/symbol
35 | install:
36 | - cmd: curl -O https://download.microsoft.com/download/8/8/5/88544F33-836A-49A5-8B67-451C24709A8F/dotnet-sdk-2.1.300-win-x64.exe
37 | - cmd: dotnet-sdk-2.1.300-win-x64.exe /install /quiet /norestart /log install.log
38 | skip_commits:
39 | files:
40 | - '**/*.md'
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/TestHelpers/TestReporter.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 |
9 | namespace App.Metrics.Health.Facts.TestHelpers
10 | {
11 | public class TestReporter : IReportHealthStatus
12 | {
13 | private readonly bool _pass;
14 | private readonly Exception _throwEx;
15 |
16 | public TestReporter()
17 | {
18 | }
19 |
20 | public TestReporter(bool pass = true, Exception throwEx = null)
21 | {
22 | _pass = throwEx == null && pass;
23 | _throwEx = throwEx;
24 | ReportInterval = TimeSpan.FromMilliseconds(10);
25 | }
26 |
27 | public TestReporter(TimeSpan interval, bool pass = true, Exception throwEx = null)
28 | {
29 | ReportInterval = interval;
30 | _pass = throwEx == null && pass;
31 | _throwEx = throwEx;
32 | }
33 |
34 | public TimeSpan ReportInterval { get; set; }
35 |
36 | ///
37 | public Task ReportAsync(HealthOptions options, HealthStatus status, CancellationToken cancellationToken = default)
38 | {
39 | if (_throwEx != null)
40 | {
41 | throw _throwEx;
42 | }
43 |
44 | return Task.FromResult(_pass);
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/HealthOptions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | namespace App.Metrics.Health
6 | {
7 | ///
8 | /// Top level container for all configuration settings of Health
9 | ///
10 | public class HealthOptions
11 | {
12 | public HealthOptions()
13 | {
14 | Enabled = true;
15 | ReportingEnabled = true;
16 | }
17 |
18 | ///
19 | /// Gets or sets the application name used when reporting health status for example.
20 | ///
21 | public string ApplicationName { get; set; }
22 |
23 | ///
24 | /// Gets or sets a value indicating whether [health checks enabled]. This will also avoid registering all health
25 | /// middleware if using App.Metrics.Health.Middleware.
26 | ///
27 | ///
28 | /// true if [health enabled]; otherwise, false.
29 | ///
30 | public bool Enabled { get; set; }
31 |
32 | ///
33 | /// Gets or sets a value indicating whether [reporting enabled].
34 | ///
35 | ///
36 | /// true if [reporting enabled]; otherwise, false.
37 | ///
38 | public bool ReportingEnabled { get; set; } = true;
39 | }
40 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health/AppMetricsHealth.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using App.Metrics.Health.Builder;
6 |
7 | namespace App.Metrics.Health
8 | {
9 | ///
10 | /// Provides convenience methods for creating instances of and with pre-configured defaults.
11 | ///
12 | public static class AppMetricsHealth
13 | {
14 | ///
15 | /// Initializes a new instance of the class with pre-configured defaults.
16 | ///
17 | ///
18 | /// The following defaults are applied to the returned :
19 | /// use, JSON and Plain Text health check result formatting.
20 | ///
21 | /// The initialized .
22 | public static IHealthBuilder CreateDefaultBuilder()
23 | {
24 | var builder = new HealthBuilder()
25 | .Configuration
26 | .Configure(
27 | options =>
28 | {
29 | options.Enabled = true;
30 | })
31 | .OutputHealth.AsJson()
32 | .OutputHealth.AsPlainText();
33 |
34 | return builder;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/HealthStatus.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Collections.Generic;
6 | using System.Linq;
7 |
8 | namespace App.Metrics.Health
9 | {
10 | ///
11 | /// Structure describing the status of executing all the health checks operations.
12 | ///
13 | public struct HealthStatus
14 | {
15 | public HealthStatus(IEnumerable results)
16 | {
17 | Results = results;
18 | }
19 |
20 | ///
21 | /// Gets result of each health check operation
22 | ///
23 | ///
24 | /// The health check results.
25 | ///
26 | public IEnumerable Results { get; }
27 |
28 | ///
29 | /// Gets all health checks passed.
30 | ///
31 | ///
32 | /// The status.
33 | ///
34 | public HealthCheckStatus Status
35 | {
36 | get
37 | {
38 | if (Results.Any(r => r.Check.Status.IsUnhealthy()))
39 | {
40 | return HealthCheckStatus.Unhealthy;
41 | }
42 |
43 | return Results.Any(r => r.Check.Status.IsDegraded())
44 | ? HealthCheckStatus.Degraded
45 | : HealthCheckStatus.Healthy;
46 | }
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/IHealthRoot.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Collections.Generic;
6 | using App.Metrics.Health.Formatters;
7 |
8 | namespace App.Metrics.Health
9 | {
10 | public interface IHealthRoot : IHealth
11 | {
12 | ///
13 | /// Gets the default to use when health checks are attempted to be formatted.
14 | ///
15 | ///
16 | /// The default s that is used by this application.
17 | ///
18 | IHealthOutputFormatter DefaultOutputHealthFormatter { get; }
19 |
20 | IRunHealthChecks HealthCheckRunner { get; }
21 |
22 | HealthOptions Options { get; }
23 |
24 | ///
25 | /// Gets a list of s that are used by this application to format health
26 | /// results.
27 | ///
28 | ///
29 | /// A list of s that are used by this application.
30 | ///
31 | IReadOnlyCollection OutputHealthFormatters { get; }
32 |
33 | ///
34 | /// Gets a readonly collection of s which provide the ability to report on health
35 | /// check results.
36 | ///
37 | IReadOnlyCollection Reporters { get; }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Builder/IHealthBuilder.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | // ReSharper disable CheckNamespace
6 | namespace App.Metrics.Health
7 | // ReSharper restore CheckNamespace
8 | {
9 | public interface IHealthBuilder
10 | {
11 | bool CanReport();
12 |
13 | ///
14 | /// Builder for configuring core App Metrics Health options.
15 | ///
16 | IHealthConfigurationBuilder Configuration { get; }
17 |
18 | IHealthCheckBuilder HealthChecks { get; }
19 |
20 | ///
21 | ///
22 | /// Builder for configuring health check output formatting for reporting.
23 | ///
24 | ///
25 | /// Multiple formatters can be used, in which case the
26 | /// will be set to the first configured formatter.
27 | ///
28 | ///
29 | IHealthOutputFormattingBuilder OutputHealth { get; }
30 |
31 | ///
32 | /// Builder for configuring health status reporters.
33 | ///
34 | IHealthReportingBuilder Report { get; }
35 |
36 | ///
37 | /// Builds an with the services configured via an .
38 | ///
39 | /// An with services configured via an .
40 | IHealthRoot Build();
41 | }
42 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/HealthConstants.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Collections.ObjectModel;
8 |
9 | namespace App.Metrics.Health
10 | {
11 | public static class HealthConstants
12 | {
13 | public const string DegradedStatusDisplay = "Degraded";
14 | public const string HealthyStatusDisplay = "Healthy";
15 | public const string UnhealthyStatusDisplay = "Unhealthy";
16 | public const string IgnoredStatusDisplay = "Ignored";
17 |
18 | #pragma warning disable SA1008
19 | public static readonly (double healthy, double degraded, double unhealthy) HealthScore = (1.0, 0.5, 0.0);
20 | #pragma warning restore SA1008
21 |
22 | public static ReadOnlyDictionary HealthStatusDisplay =>
23 | new ReadOnlyDictionary(
24 | new Dictionary
25 | {
26 | { HealthCheckStatus.Healthy, HealthyStatusDisplay },
27 | { HealthCheckStatus.Unhealthy, UnhealthyStatusDisplay },
28 | { HealthCheckStatus.Degraded, DegradedStatusDisplay },
29 | { HealthCheckStatus.Ignored, IgnoredStatusDisplay }
30 | });
31 |
32 | public static class Reporting
33 | {
34 | public static readonly TimeSpan DefaultReportInterval = TimeSpan.FromSeconds(10);
35 | public static readonly int DefaultNumberOfRunsBeforeReAlerting = 6; // Every 60secs
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Formatters.Ascii.Facts/HealthStatusTextWriterTests.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.IO;
6 | using System.Threading.Tasks;
7 | using App.Metrics.Health.Builder;
8 | using App.Metrics.Health.Serialization;
9 | using FluentAssertions;
10 | using Xunit;
11 |
12 | namespace App.Metrics.Health.Formatters.Ascii.Facts
13 | {
14 | public class HealthStatusTextWriterTests
15 | {
16 | [Fact]
17 | public async Task Can_apply_ascii_health_formatting()
18 | {
19 | // Arrange
20 | var health = new HealthBuilder()
21 | .OutputHealth.AsPlainText()
22 | .HealthChecks.AddCheck("test", () => new ValueTask(HealthCheckResult.Healthy()))
23 | .Build();
24 |
25 | var serializer = new HealthStatusSerializer();
26 |
27 | // Act
28 | var healthStatus = await health.HealthCheckRunner.ReadAsync();
29 |
30 | using (var sw = new StringWriter())
31 | {
32 | using (var writer = new HealthStatusTextWriter(sw))
33 | {
34 | serializer.Serialize(writer, healthStatus);
35 | }
36 |
37 | // Assert
38 | sw.ToString().Should().Be(
39 | "# OVERALL STATUS: Healthy\n--------------------------------------------------------------\n# CHECK: test\n\n MESSAGE = OK\n STATUS = Healthy\n--------------------------------------------------------------\n");
40 | }
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Health.Benchmarks/Support/OutputLogger.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using BenchmarkDotNet.Loggers;
7 | using Xunit.Abstractions;
8 |
9 | namespace App.Metrics.Health.Benchmarks.Support
10 | {
11 | public class OutputLogger : AccumulationLogger
12 | {
13 | private readonly ITestOutputHelper _testOutputHelper;
14 | private string _currentLine = string.Empty;
15 |
16 | public OutputLogger(ITestOutputHelper testOutputHelper)
17 | {
18 | _testOutputHelper = testOutputHelper ?? throw new ArgumentNullException(nameof(testOutputHelper));
19 | }
20 |
21 | public override void Write(LogKind logKind, string text)
22 | {
23 | _currentLine += RemoveInvalidChars(text);
24 | base.Write(logKind, text);
25 | }
26 |
27 | public override void WriteLine()
28 | {
29 | _testOutputHelper.WriteLine(_currentLine);
30 | _currentLine = string.Empty;
31 | base.WriteLine();
32 | }
33 |
34 | public override void WriteLine(LogKind logKind, string text)
35 | {
36 | _testOutputHelper.WriteLine(_currentLine + RemoveInvalidChars(text));
37 | _currentLine = string.Empty;
38 | base.WriteLine(logKind, text);
39 | }
40 |
41 | private static string RemoveInvalidChars(string text)
42 | {
43 | if (string.IsNullOrEmpty(text))
44 | {
45 | return string.Empty;
46 | }
47 |
48 | return text.Replace((char)0x1B, ' ');
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Slack/SlackHealthAlertOptions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 |
7 | namespace App.Metrics.Health.Reporting.Slack
8 | {
9 | public class SlackHealthAlertOptions
10 | {
11 | public bool AlertOnDegradedChecks { get; set; } = true;
12 |
13 | public string Channel { get; set; }
14 |
15 | public string EmojiIcon { get; set; }
16 |
17 | public bool Enabled { get; set; } = true;
18 |
19 | ///
20 | /// Gets or sets the health status reporting interval.
21 | ///
22 | ///
23 | /// If not set reporting interval will be set to the .
24 | ///
25 | ///
26 | /// The to wait between reporting health status.
27 | ///
28 | public TimeSpan ReportInterval { get; set; }
29 |
30 | ///
31 | /// Gets or sets the number of report runs before re-alerting checks that have re-failed. If set to 0, failed checks will only be reported once until healthy again.
32 | ///
33 | ///
34 | /// If not set number of runs will be set to the .
35 | ///
36 | public int RunsBeforeReportExistingFailures { get; set; } = HealthConstants.Reporting.DefaultNumberOfRunsBeforeReAlerting;
37 |
38 | public string Username { get; set; }
39 |
40 | public string WebhookUrl { get; set; }
41 | }
42 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #OS junk files
2 | [Tt]humbs.db
3 | *.DS_Store
4 | *.orig
5 |
6 | #Visual Studio files
7 | *.[Oo]bj
8 | *.user
9 | *.aps
10 | *.pch
11 | *.vspscc
12 | *.vssscc
13 | *_i.c
14 | *_p.c
15 | *.ncb
16 | *.suo
17 | *.tlb
18 | *.tlh
19 | *.bak
20 | *.[Cc]ache
21 | *.ilk
22 | *.log
23 | *.lib
24 | *.sbr
25 | *.sdf
26 | *.opensdf
27 | *.unsuccessfulbuild
28 | ipch/
29 | [Oo]bj/
30 | [Bb]in
31 | Ankh.NoLoad
32 | *.vsmdi
33 | *.lock.json
34 |
35 | #Tooling
36 | .idea
37 | _ReSharper*/
38 | *.resharper
39 | [Tt]est[Rr]esult*
40 | *.sass-cache
41 |
42 | #Subversion files
43 | .svn
44 |
45 | # Office Temp Files
46 | ~$*
47 |
48 | #ncrunch
49 | *ncrunch*
50 | *crunch*.local.xml
51 |
52 | #Test files
53 | *.testsettings
54 |
55 | #Build
56 | BuildOutput
57 | BuildOutput/*
58 | WebPublish
59 | WebPublish/*
60 |
61 | #Cake
62 | tools/*
63 | !tools/packages.config
64 |
65 | .vs
66 |
67 | # visual studio database projects
68 | *.dbmdl
69 | logs/*
70 | .vs/*
71 | *.project.lock.json
72 |
73 | #Temp folders
74 | temp/
75 |
76 | #Tools and packages
77 | .nuget
78 | .nuget/*
79 | artifacts
80 | artifacts/*
81 | packages
82 | packages/*
83 | *.lock.json
84 |
85 | #BenchMarkDotNet
86 | BDN.Generated
87 | BDN.Generated/*
88 | scripts/BenchMarkDotNet.Artifacts
89 | scripts/BenchMarkDotNet.Artifacts/*
90 | **/BenchMarkDotNet.Artifacts/*
91 | !/benchmarks/App.Metrics.Benchmarks.Runner/BenchMarkDotNet.Artifacts/*
92 | /benchmarks/App.Metrics.Benchmarks.Runner/BenchMarkDotNet.Artifacts/*.log
93 | /benchmarks/App.Metrics.Benchmarks.Runner/BenchMarkDotNet.Artifacts/results/*.csv
94 | /benchmarks/App.Metrics.Benchmarks.Runner/BenchMarkDotNet.Artifacts/results/*.html
95 |
96 | #NDepend
97 | *.ndproj
98 | NDependOut/*
99 |
100 | #DotCover
101 | Session/*
102 | Session.html
103 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/Internal/DefaultHealthCheckRunner.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Collections.Generic;
6 | using System.Diagnostics;
7 | using System.Linq;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 | using App.Metrics.Health.Logging;
11 |
12 | namespace App.Metrics.Health.Internal
13 | {
14 | public sealed class DefaultHealthCheckRunner : IRunHealthChecks
15 | {
16 | private static readonly ILog Logger = LogProvider.For();
17 | private readonly IEnumerable _checks;
18 |
19 | ///
20 | /// Initializes a new instance of the class.
21 | ///
22 | /// The registered health checks.
23 | public DefaultHealthCheckRunner(IEnumerable checks)
24 | {
25 | _checks = checks ?? Enumerable.Empty();
26 | }
27 |
28 | ///
29 | public async ValueTask ReadAsync(CancellationToken cancellationToken = default)
30 | {
31 | if (!_checks.Any())
32 | {
33 | return default;
34 | }
35 |
36 | var startTimestamp = Logger.IsTraceEnabled() ? Stopwatch.GetTimestamp() : 0;
37 |
38 | Logger.HealthCheckGetStatusExecuting();
39 |
40 | var results = await Task.WhenAll(_checks.OrderBy(v => v.Name).Select(v => v.ExecuteAsync(cancellationToken).AsTask()));
41 |
42 | var healthStatus = new HealthStatus(results);
43 |
44 | Logger.HealthCheckGetStatusExecuted(healthStatus, startTimestamp);
45 |
46 | return healthStatus;
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Health.Benchmarks/Support/SimpleBenchmarkRunner.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Threading;
9 |
10 | namespace App.Metrics.Health.Benchmarks.Support
11 | {
12 | public static class SimpleBenchmarkRunner
13 | {
14 | private const int NumberOfRuns = 10000;
15 | private const int WarmupRuns = 10;
16 | private static readonly int ThreadCount = Environment.ProcessorCount;
17 |
18 | public static void Run(Action action, bool warmup = false)
19 | {
20 | if (warmup)
21 | {
22 | Warmup(action);
23 | }
24 |
25 | var thread = new List();
26 |
27 | for (var i = 0; i < ThreadCount; i++)
28 | {
29 | thread.Add(
30 | new Thread(
31 | () =>
32 | {
33 | for (long j = 0; j < NumberOfRuns; j++)
34 | {
35 | action();
36 | }
37 | }));
38 | }
39 |
40 | thread.ForEach(t => t.Start());
41 | thread.ForEach(t => t.Join());
42 | }
43 |
44 | private static void PerformCollection()
45 | {
46 | GC.Collect();
47 | GC.WaitForPendingFinalizers();
48 | GC.Collect();
49 | Thread.Sleep(200);
50 | }
51 |
52 | private static void Warmup(Action action)
53 | {
54 | foreach (var unused in Enumerable.Range(1, WarmupRuns))
55 | {
56 | action();
57 | }
58 |
59 | PerformCollection();
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Json/DefaultJsonSerializerSettings.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using App.Metrics.Health.Formatters.Json.Converters;
6 | using Newtonsoft.Json;
7 | using Newtonsoft.Json.Serialization;
8 |
9 | namespace App.Metrics.Health.Formatters.Json
10 | {
11 | public static class DefaultJsonSerializerSettings
12 | {
13 | private const int DefaultMaxDepth = 32;
14 |
15 | private static readonly DefaultContractResolver SharedContractResolver = new DefaultContractResolver
16 | {
17 | NamingStrategy = new CamelCaseNamingStrategy()
18 | };
19 |
20 | ///
21 | /// Creates default .
22 | ///
23 | /// Default .
24 | public static JsonSerializerSettings CreateSerializerSettings()
25 | {
26 | var settings = new JsonSerializerSettings
27 | {
28 | ContractResolver = SharedContractResolver,
29 | NullValueHandling = NullValueHandling.Ignore,
30 | MissingMemberHandling = MissingMemberHandling.Ignore,
31 | Formatting = Formatting.Indented,
32 | MaxDepth = DefaultMaxDepth,
33 | TypeNameHandling = TypeNameHandling.None
34 | };
35 |
36 | settings.Converters.Add(new HealthStatusConverter());
37 |
38 | return settings;
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/HealthRoot.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using App.Metrics.Health.Formatters;
8 | using App.Metrics.Health.Internal;
9 |
10 | namespace App.Metrics.Health
11 | {
12 | public class HealthRoot : IHealthRoot
13 | {
14 | private readonly IHealth _health;
15 |
16 | public HealthRoot(
17 | IHealth health,
18 | HealthOptions options,
19 | HealthFormatterCollection metricsOutputFormatters,
20 | IHealthOutputFormatter defaultMetricsOutputFormatter,
21 | IRunHealthChecks healthCheckRunner,
22 | HealthReporterCollection reporterCollection)
23 | {
24 | Options = options ?? throw new ArgumentNullException(nameof(options));
25 | _health = health ?? throw new ArgumentNullException(nameof(health));
26 | OutputHealthFormatters = metricsOutputFormatters ?? new HealthFormatterCollection();
27 | DefaultOutputHealthFormatter = defaultMetricsOutputFormatter;
28 | HealthCheckRunner = healthCheckRunner;
29 | Reporters = reporterCollection ?? new HealthReporterCollection();
30 | }
31 |
32 | ///
33 | public IReadOnlyCollection OutputHealthFormatters { get; }
34 |
35 | ///
36 | public IHealthOutputFormatter DefaultOutputHealthFormatter { get; }
37 |
38 | ///
39 | public HealthOptions Options { get; }
40 |
41 | ///
42 | public IRunHealthChecks HealthCheckRunner { get; }
43 |
44 | ///
45 | public IReadOnlyCollection Reporters { get; }
46 |
47 | ///
48 | public IEnumerable Checks => _health.Checks;
49 | }
50 | }
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## How to contribute to App Metrics
2 |
3 | Thanks for taking the time to contribute to App Metrics :+1:
4 |
5 | ### **Coding Style**
6 |
7 | Coding style is enforced using StyleCop. To automate the clean up of most rules, the solution includes a "team-shared" [ReSharper DotSettings](AppMetrics.sln.DotSettings), the ReSharper Code Cleanup profile is named `AppMetrics`.
8 |
9 | If your not familiar with ReSharper Code Cleanup, see the [code cleanup docs](https://www.jetbrains.com/help/resharper/2016.3/Code_Cleanup__Running_Code_Cleanup.html) and [settings layer docs](https://www.jetbrains.com/help/resharper/2016.3/Reference__Settings_Layers.html).
10 |
11 | ### **Have you found a bug?**
12 |
13 | **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/alhardy/AppMetrics/issues).
14 |
15 | If you're unable to find an open issue related to the bug you've found go ahead and [open a new issue](https://github.com/alhardy/AppMetrics/issues/new). Be sure to include:
16 |
17 | 1. A **title and clear description**
18 | 2. As much relevant information as possible including the exact steps to reproduce the bug.
19 | 3. If possible provide a **code sample** or **unit test** demonstrating the bug.
20 |
21 | ### **Did you write a patch that fixes a bug?**
22 |
23 | * Open a new [GitHub pull request](https://help.github.com/articles/about-pull-requests/) on the `dev` branch.
24 |
25 | * Ensure the pull request description clearly describes the problem and solution. Include the relevant issue number in the commit message e.g. `git commit -m '#1 {message}`
26 |
27 | ### **Requesting a new feature?**
28 |
29 | * Suggest your feature as a [new issue](https://github.com/alhardy/AppMetrics/issues/new) to start a discussion.
30 |
31 | ### **Contributing to the documentation**
32 |
33 | App Metrics documentation is built using [Hugo](https://gohugo.io/documentation/), you can find the github repo [here](https://github.com/AppMetrics/Docs.V2.Hugo) and create a new pull request on the `master` branch.
34 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Json/Builder/HealthJsonOutputFormatterBuilder.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using App.Metrics.Health.Formatters.Json;
7 |
8 | // ReSharper disable CheckNamespace
9 | namespace App.Metrics.Health
10 | // ReSharper restore CheckNamespace
11 | {
12 | ///
13 | /// Builder for configuring metric JSON output formatting using an
14 | /// .
15 | ///
16 | public static class HealthJsonOutputFormatterBuilder
17 | {
18 | ///
19 | /// Add the allowing health check results to optionally be reported as JSON.
20 | ///
21 | ///
22 | /// The used to configure JSON formatting
23 | /// options.
24 | ///
25 | /// The JSON formatting options to use.
26 | ///
27 | /// An that can be used to further configure App Metrics Health.
28 | ///
29 | public static IHealthBuilder AsJson(
30 | this IHealthOutputFormattingBuilder metricFormattingBuilder,
31 | Action setupAction = null)
32 | {
33 | if (metricFormattingBuilder == null)
34 | {
35 | throw new ArgumentNullException(nameof(metricFormattingBuilder));
36 | }
37 |
38 | var options = new HealthJsonOptions();
39 |
40 | setupAction?.Invoke(options);
41 |
42 | var formatter = new HealthStatusJsonOutputFormatter(options.SerializerSettings);
43 |
44 | return metricFormattingBuilder.Using(formatter);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Ascii/Builder/HealthTextOutputFormatterBuilder.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using App.Metrics.Health.Formatters;
7 | using App.Metrics.Health.Formatters.Ascii;
8 |
9 | // ReSharper disable CheckNamespace
10 | namespace App.Metrics.Health
11 | // ReSharper restore CheckNamespace
12 | {
13 | ///
14 | /// Builder for configuring metric plain text output formatting using an
15 | /// .
16 | ///
17 | public static class HealthTextOutputFormatterBuilder
18 | {
19 | ///
20 | /// Add the allowing health checks to optionally be reported as plain text.
21 | ///
22 | ///
23 | /// The used to configure formatting
24 | /// options.
25 | ///
26 | /// The plain text formatting options to use.
27 | ///
28 | /// An that can be used to further configure App Metrics Health.
29 | ///
30 | public static IHealthBuilder AsPlainText(
31 | this IHealthOutputFormattingBuilder healthFormattingBuilder,
32 | Action setupAction = null)
33 | {
34 | if (healthFormattingBuilder == null)
35 | {
36 | throw new ArgumentNullException(nameof(healthFormattingBuilder));
37 | }
38 |
39 | var options = new HealthTextOptions();
40 |
41 | setupAction?.Invoke(options);
42 |
43 | var formatter = new HealthStatusTextOutputFormatter();
44 |
45 | return healthFormattingBuilder.Using(formatter);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Ascii/HealthStatusTextOutputFormatter.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.IO;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using App.Metrics.Health.Serialization;
10 | #if !NETSTANDARD1_6
11 | using App.Metrics.Health.Internal;
12 | #endif
13 |
14 | namespace App.Metrics.Health.Formatters.Ascii
15 | {
16 | public class HealthStatusTextOutputFormatter : IHealthOutputFormatter
17 | {
18 | private readonly HealthTextOptions _options;
19 |
20 | public HealthStatusTextOutputFormatter()
21 | {
22 | _options = new HealthTextOptions();
23 | }
24 |
25 | public HealthStatusTextOutputFormatter(HealthTextOptions options) { _options = options ?? throw new ArgumentNullException(nameof(options)); }
26 |
27 | public HealthMediaTypeValue MediaType => new HealthMediaTypeValue("text", "vnd.appmetrics.health", "v1", "plain");
28 |
29 | public Task WriteAsync(
30 | Stream output,
31 | HealthStatus healthStatus,
32 | CancellationToken cancellationToken = default)
33 | {
34 | if (output == null)
35 | {
36 | throw new ArgumentNullException(nameof(output));
37 | }
38 |
39 | var serializer = new HealthStatusSerializer();
40 |
41 | using (var stringWriter = new StreamWriter(output, _options.Encoding))
42 | {
43 | using (var textWriter = new HealthStatusTextWriter(stringWriter, _options.Separator, _options.Padding))
44 | {
45 | serializer.Serialize(textWriter, healthStatus);
46 | }
47 | }
48 |
49 | #if !NETSTANDARD1_6
50 | return AppMetricsHealthTaskHelper.CompletedTask();
51 | #else
52 | return Task.CompletedTask;
53 | #endif
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Json/HealthStatusJsonOutputFormatter.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.IO;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using Newtonsoft.Json;
10 | #if !NETSTANDARD1_6
11 | using App.Metrics.Health.Internal;
12 | #endif
13 |
14 | namespace App.Metrics.Health.Formatters.Json
15 | {
16 | public class HealthStatusJsonOutputFormatter : IHealthOutputFormatter
17 | {
18 | private readonly JsonSerializerSettings _serializerSettings;
19 |
20 | public HealthStatusJsonOutputFormatter() { _serializerSettings = DefaultJsonSerializerSettings.CreateSerializerSettings(); }
21 |
22 | public HealthStatusJsonOutputFormatter(JsonSerializerSettings serializerSettings)
23 | {
24 | _serializerSettings = serializerSettings ?? throw new ArgumentNullException(nameof(serializerSettings));
25 | }
26 |
27 | public HealthMediaTypeValue MediaType => new HealthMediaTypeValue("application", "vnd.appmetrics.health", "v1", "json");
28 |
29 | public Task WriteAsync(
30 | Stream output,
31 | HealthStatus healthStatus,
32 | CancellationToken cancellationToken = default)
33 | {
34 | if (output == null)
35 | {
36 | throw new ArgumentNullException(nameof(output));
37 | }
38 |
39 | var serilizer = JsonSerializer.Create(_serializerSettings);
40 |
41 | using (var streamWriter = new StreamWriter(output))
42 | {
43 | using (var textWriter = new JsonTextWriter(streamWriter))
44 | {
45 | serilizer.Serialize(textWriter, healthStatus);
46 | }
47 | }
48 |
49 | #if NETSTANDARD1_6
50 | return Task.CompletedTask;
51 | #else
52 | return AppMetricsHealthTaskHelper.CompletedTask();
53 | #endif
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/sandbox/HealthSandbox/HealthSandbox.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | exe
7 | netcoreapp2.1
8 | $(TargetFrameworks);net461
9 | latest
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | Always
38 | PreserveNewest
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Slack/Internal/DefaultJsonSerializerSettings.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using Newtonsoft.Json;
7 | using Newtonsoft.Json.Converters;
8 | using Newtonsoft.Json.Serialization;
9 |
10 | namespace App.Metrics.Health.Reporting.Slack.Internal
11 | {
12 | internal static class DefaultJsonSerializerSettings
13 | {
14 | private const int DefaultMaxDepth = 32;
15 |
16 | private static readonly DefaultContractResolver DefaultContractResolver = new DefaultContractResolver
17 | {
18 | NamingStrategy = new SnakeCaseNamingStrategy()
19 | };
20 |
21 | private static readonly Lazy Lazy =
22 | new Lazy(
23 | () =>
24 | {
25 | var settings = new JsonSerializerSettings
26 | {
27 | DateParseHandling = DateParseHandling.DateTimeOffset,
28 | MaxDepth = DefaultMaxDepth,
29 | ContractResolver = DefaultContractResolver,
30 | NullValueHandling = NullValueHandling.Ignore,
31 | MissingMemberHandling = MissingMemberHandling.Ignore,
32 | Formatting = Formatting.Indented,
33 | TypeNameHandling = TypeNameHandling.None
34 | };
35 |
36 | settings.Converters.Add(new StringEnumConverter());
37 |
38 | return settings;
39 | });
40 |
41 | public static JsonSerializerSettings Instance => Lazy.Value;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/HealthCheckResultTests.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using FluentAssertions;
7 | using Xunit;
8 |
9 | namespace App.Metrics.Health.Facts
10 | {
11 | public class HealthCheckResultTests
12 | {
13 | [Fact]
14 | public void Can_create_degraded()
15 | {
16 | var result = HealthCheckResult.Degraded("degrading api");
17 |
18 | result.Message.Should().Be("degrading api");
19 | result.Status.Should().Be(HealthCheckStatus.Degraded);
20 | }
21 |
22 | [Fact]
23 | public void Can_create_degraded_with_exception_info()
24 | {
25 | var exception = new InvalidOperationException();
26 | var exceptionString = $"EXCEPTION: {exception.GetType().Name} - {exception.Message}";
27 | var result = HealthCheckResult.Degraded(exception);
28 |
29 | result.Message.Should().StartWith(exceptionString);
30 | result.Status.Should().Be(HealthCheckStatus.Degraded);
31 | }
32 |
33 | [Fact]
34 | public void Can_create_degraded_with_formatted_values()
35 | {
36 | var id = Guid.NewGuid();
37 | var result = HealthCheckResult.Degraded("degrading api {0}", id);
38 |
39 | result.Message.Should().Be($"degrading api {id}");
40 | result.Status.Should().Be(HealthCheckStatus.Degraded);
41 | }
42 |
43 | [Fact]
44 | public void Can_create_ignored()
45 | {
46 | var result = HealthCheckResult.Ignore();
47 |
48 | result.Message.Should().Be("ignored check");
49 | result.Status.Should().Be(HealthCheckStatus.Ignored);
50 | }
51 |
52 | [Fact]
53 | public void Message_defaults_to_degraded()
54 | {
55 | var unused = Guid.NewGuid();
56 | var result = HealthCheckResult.Degraded();
57 |
58 | result.Message.Should().Be("DEGRADED");
59 | result.Status.Should().Be(HealthCheckStatus.Degraded);
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/Builder/HealthReportingBuilder.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 |
7 | namespace App.Metrics.Health.Builder
8 | {
9 | public class HealthReportingBuilder : IHealthReportingBuilder
10 | {
11 | private readonly Action _reporters;
12 |
13 | public HealthReportingBuilder(
14 | IHealthBuilder healthBuilder,
15 | Action reporters)
16 | {
17 | Builder = healthBuilder ?? throw new ArgumentNullException(nameof(healthBuilder));
18 | _reporters = reporters ?? throw new ArgumentNullException(nameof(reporters));
19 | }
20 |
21 | ///
22 | public IHealthBuilder Builder { get; }
23 |
24 | ///
25 | public IHealthBuilder Using(IReportHealthStatus reporter)
26 | {
27 | if (reporter == null)
28 | {
29 | throw new ArgumentNullException(nameof(reporter));
30 | }
31 |
32 | EnsureRequiredProperties(reporter);
33 |
34 | _reporters(reporter);
35 |
36 | return Builder;
37 | }
38 |
39 | ///
40 | public IHealthBuilder Using()
41 | where TReportHealth : IReportHealthStatus, new()
42 | {
43 | var reporter = new TReportHealth();
44 |
45 | return Using(reporter);
46 | }
47 |
48 | public IHealthBuilder Using(TimeSpan reportInterval)
49 | where TReportHealth : IReportHealthStatus, new()
50 | {
51 | var reporter = new TReportHealth { ReportInterval = reportInterval };
52 |
53 | return Using(reporter);
54 | }
55 |
56 | private static void EnsureRequiredProperties(IReportHealthStatus reporter)
57 | {
58 | reporter.ReportInterval = reporter.ReportInterval <= TimeSpan.Zero
59 | ? HealthConstants.Reporting.DefaultReportInterval
60 | : reporter.ReportInterval;
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Internal/HealthReporterCollection.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Collections.ObjectModel;
8 |
9 | namespace App.Metrics.Health.Internal
10 | {
11 | public class HealthReporterCollection : Collection
12 | {
13 | public HealthReporterCollection() { }
14 |
15 | public HealthReporterCollection(IList list)
16 | : base(list)
17 | {
18 | }
19 |
20 | public IReportHealthStatus GetType()
21 | where TReporter : IReportHealthStatus
22 | {
23 | return GetType(typeof(TReporter));
24 | }
25 |
26 | public IReportHealthStatus GetType(Type reporterType)
27 | {
28 | for (var i = Count - 1; i >= 0; i--)
29 | {
30 | var reporter = this[i];
31 | if (reporter.GetType() == reporterType)
32 | {
33 | return reporter;
34 | }
35 | }
36 |
37 | return default;
38 | }
39 |
40 | public void RemoveType()
41 | where TReporter : IReportHealthStatus
42 | {
43 | RemoveType(typeof(TReporter));
44 | }
45 |
46 | public void RemoveType(Type reporterType)
47 | {
48 | for (var i = Count - 1; i >= 0; i--)
49 | {
50 | var reporter = this[i];
51 | if (reporter.GetType() == reporterType)
52 | {
53 | RemoveAt(i);
54 | }
55 | }
56 | }
57 |
58 | public void TryAdd(IReportHealthStatus reporter)
59 | where TReporter : IReportHealthStatus
60 | {
61 | RemoveType();
62 | Add(reporter);
63 | }
64 |
65 | public void TryAdd(IReportHealthStatus reporter)
66 | {
67 | RemoveType(reporter.GetType());
68 | Add(reporter);
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/Builders/HealthReportingBuilderTests.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using App.Metrics.Health.Builder;
7 | using App.Metrics.Health.Facts.TestHelpers;
8 | using App.Metrics.Health.Internal;
9 | using FluentAssertions;
10 | using Xunit;
11 |
12 | namespace App.Metrics.Health.Facts.Builders
13 | {
14 | public class HealthReportingBuilderTests
15 | {
16 | [Fact]
17 | public void Cannot_set_null_reporter()
18 | {
19 | // Arrange
20 | Action action = () =>
21 | {
22 | // Act
23 | var unused = new HealthBuilder().Report.Using(reporter: null);
24 | };
25 |
26 | // Assert
27 | action.Should().Throw();
28 | }
29 |
30 | [Fact]
31 | public void Can_use_reporter_instance()
32 | {
33 | // Arrange
34 | var reporter = new TestReporter();
35 | var builder = new HealthBuilder().Report.Using(reporter);
36 |
37 | // Act
38 | var metrics = builder.Build();
39 |
40 | // Assert
41 | metrics.Reporters.Should().Contain(reportMetrics => reportMetrics is TestReporter);
42 | }
43 |
44 | [Fact]
45 | public void Can_use_reporter_of_type()
46 | {
47 | // Arrange
48 | var builder = new HealthBuilder().Report.Using();
49 |
50 | // Act
51 | var metrics = builder.Build();
52 |
53 | // Assert
54 | metrics.Reporters.Should().Contain(reportMetrics => reportMetrics is TestReporter);
55 | }
56 |
57 | [Fact]
58 | public void Can_use_reporter_of_type_and_override_flushinterval()
59 | {
60 | // Arrange
61 | var builder = new HealthBuilder().Report.Using(reportInterval: TimeSpan.FromDays(1));
62 |
63 | // Act
64 | var metrics = builder.Build();
65 | var reporter = (metrics.Reporters as HealthReporterCollection)?.GetType();
66 |
67 | // Assert
68 | reporter?.ReportInterval.Should().Be(TimeSpan.FromDays(1));
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Builder/IHealthReportingBuilder.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 |
7 | // ReSharper disable CheckNamespace
8 | namespace App.Metrics.Health
9 | // ReSharper restore CheckNamespace
10 | {
11 | public interface IHealthReportingBuilder
12 | {
13 | ///
14 | /// Gets the where App Metrics Health is configured.
15 | ///
16 | IHealthBuilder Builder { get; }
17 |
18 | ///
19 | /// Reports health status using the specified .
20 | ///
21 | /// An instance used to report health status.
22 | ///
23 | /// An that can be used to further configure App Metrics Health.
24 | ///
25 | IHealthBuilder Using(IReportHealthStatus reporter);
26 |
27 | ///
28 | /// Reports metrics using the specified .
29 | ///
30 | ///
31 | /// An type used to report health status.
32 | ///
33 | ///
34 | /// An that can be used to further configure App Metrics Health.
35 | ///
36 | IHealthBuilder Using()
37 | where TReportHealth : IReportHealthStatus, new();
38 |
39 | ///
40 | /// Reports metrics using the specified .
41 | ///
42 | /// The interval used to schedule health status reporting.
43 | ///
44 | /// An type used to report health status.
45 | ///
46 | ///
47 | /// An that can be used to further configure App Metrics Health.
48 | ///
49 | IHealthBuilder Using(TimeSpan reportInterval)
50 | where TReportHealth : IReportHealthStatus, new();
51 | }
52 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/Builder/HealthCheckBuilder.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace App.Metrics.Health.Builder
11 | {
12 | public class HealthCheckBuilder : IHealthCheckBuilder
13 | {
14 | private readonly Action _healthCheck;
15 |
16 | internal HealthCheckBuilder(
17 | IHealthBuilder healthBuilder,
18 | Action healthCheck)
19 | {
20 | Builder = healthBuilder ?? throw new ArgumentNullException(nameof(healthBuilder));
21 | _healthCheck = healthCheck ?? throw new ArgumentNullException(nameof(healthCheck));
22 | }
23 |
24 | ///
25 | public IHealthBuilder Builder { get; }
26 |
27 | ///
28 | public IHealthBuilder AddChecks(IEnumerable checks)
29 | {
30 | Register(checks);
31 |
32 | return Builder;
33 | }
34 |
35 | ///
36 | public IHealthBuilder AddCheck(THealthCheck check)
37 | where THealthCheck : HealthCheck
38 | {
39 | Register(check);
40 |
41 | return Builder;
42 | }
43 |
44 | ///
45 | public IHealthBuilder AddCheck()
46 | where THealthCheck : HealthCheck, new()
47 | {
48 | var check = new THealthCheck();
49 |
50 | Register(check);
51 |
52 | return Builder;
53 | }
54 |
55 | ///
56 | public IHealthBuilder AddCheck(string name, Func> check)
57 | {
58 | Register(new HealthCheck(name, check));
59 |
60 | return Builder;
61 | }
62 |
63 | ///
64 | public IHealthBuilder AddCheck(string name, Func> check)
65 | {
66 | Register(new HealthCheck(name, check));
67 |
68 | return Builder;
69 | }
70 |
71 | internal void Register(IEnumerable healthChecks)
72 | {
73 | foreach (var check in healthChecks)
74 | {
75 | _healthCheck(check);
76 | }
77 | }
78 |
79 | internal void Register(HealthCheck healthCheck)
80 | {
81 | _healthCheck(healthCheck);
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/Builder/HealthOutputFormattingBuilder.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using App.Metrics.Health.Formatters;
7 |
8 | // ReSharper disable CheckNamespace
9 | namespace App.Metrics.Health
10 | // ReSharper restore CheckNamespace
11 | {
12 | ///
13 | /// Builder for configuring s used for formatting s when
14 | /// they are reported.
15 | ///
16 | public class HealthOutputFormattingBuilder : IHealthOutputFormattingBuilder
17 | {
18 | private readonly Action _metricsFormatter;
19 |
20 | internal HealthOutputFormattingBuilder(
21 | IHealthBuilder healthBuilder,
22 | Action metricsFormatter)
23 | {
24 | Builder = healthBuilder ?? throw new ArgumentNullException(nameof(healthBuilder));
25 | _metricsFormatter = metricsFormatter ?? throw new ArgumentNullException(nameof(metricsFormatter));
26 | }
27 |
28 | ///
29 | public IHealthBuilder Builder { get; }
30 |
31 | ///
32 | public IHealthBuilder Using(IHealthOutputFormatter formatter)
33 | {
34 | if (formatter == null)
35 | {
36 | throw new ArgumentNullException(nameof(formatter));
37 | }
38 |
39 | _metricsFormatter(true, formatter);
40 |
41 | return Builder;
42 | }
43 |
44 | ///
45 | public IHealthBuilder Using()
46 | where TMetricsOutputFormatter : IHealthOutputFormatter, new()
47 | {
48 | _metricsFormatter(true, new TMetricsOutputFormatter());
49 |
50 | return Builder;
51 | }
52 |
53 | ///
54 | public IHealthBuilder Using(IHealthOutputFormatter formatter, bool replaceExisting)
55 | {
56 | if (formatter == null)
57 | {
58 | throw new ArgumentNullException(nameof(formatter));
59 | }
60 |
61 | _metricsFormatter(replaceExisting, formatter);
62 |
63 | return Builder;
64 | }
65 |
66 | ///
67 | public IHealthBuilder Using(bool replaceExisting)
68 | where THealthsOutputFormatter : IHealthOutputFormatter, new()
69 | {
70 | _metricsFormatter(replaceExisting, new THealthsOutputFormatter());
71 |
72 | return Builder;
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/Builders/HealthBuilderTests.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using App.Metrics.Health.Builder;
8 | using App.Metrics.Health.Formatters.Ascii;
9 | using App.Metrics.Health.Internal.NoOp;
10 | using FluentAssertions;
11 | using Xunit;
12 |
13 | namespace App.Metrics.Health.Facts.Builders
14 | {
15 | public class HealthBuilderTests
16 | {
17 | [Fact]
18 | public void When_no_checks_registered_use_noop_runner()
19 | {
20 | // Arrange
21 | var builder = new HealthBuilder();
22 |
23 | // Act
24 | var health = builder.OutputHealth.AsPlainText().Build();
25 |
26 | // Assert
27 | health.HealthCheckRunner.Should().BeOfType();
28 | }
29 |
30 | [Fact]
31 | public void When_checks_registered_should_not_use_noop_runner()
32 | {
33 | // Arrange
34 | var builder = new HealthBuilder();
35 |
36 | // Act
37 | var health = builder.HealthChecks.AddCheck("test", () => new ValueTask(HealthCheckResult.Healthy())).Build();
38 |
39 | // Assert
40 | health.HealthCheckRunner.Should().NotBeOfType();
41 | }
42 |
43 | [Fact]
44 | public void Options_should_not_be_null_when_configuration_not_used()
45 | {
46 | // Arrange
47 | var builder = new HealthBuilder();
48 |
49 | // Act
50 | var health = builder.Build();
51 |
52 | // Assert
53 | health.Options.Should().NotBeNull();
54 | }
55 |
56 | [Fact]
57 | public void Formatter_should_default_to_text_if_not_configured()
58 | {
59 | // Arrange
60 | var builder = new HealthBuilder();
61 |
62 | // Act
63 | var health = builder.Build();
64 |
65 | // Assert
66 | health.OutputHealthFormatters.Should().NotBeNull();
67 | health.OutputHealthFormatters.Single().Should().BeOfType();
68 | }
69 |
70 | [Fact]
71 | public void Default_formatter_should_default_to_text_if_not_configured()
72 | {
73 | // Arrange
74 | var builder = new HealthBuilder();
75 |
76 | // Act
77 | var health = builder.Build();
78 |
79 | // Assert
80 | health.DefaultOutputHealthFormatter.Should().NotBeNull();
81 | health.DefaultOutputHealthFormatter.Should().BeOfType();
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Formatters/HealthMediaTypeValue.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 |
7 | namespace App.Metrics.Health.Formatters
8 | {
9 | public struct HealthMediaTypeValue
10 | {
11 | public HealthMediaTypeValue(string type, string subType, string version, string format)
12 | {
13 | if (string.IsNullOrWhiteSpace(type))
14 | {
15 | throw new ArgumentException(nameof(type));
16 | }
17 |
18 | if (string.IsNullOrWhiteSpace(subType))
19 | {
20 | throw new ArgumentException(nameof(subType));
21 | }
22 |
23 | if (string.IsNullOrWhiteSpace(version))
24 | {
25 | throw new ArgumentException(nameof(version));
26 | }
27 |
28 | if (string.IsNullOrWhiteSpace(format))
29 | {
30 | throw new ArgumentException(nameof(format));
31 | }
32 |
33 | Type = type;
34 | SubType = subType;
35 | Format = format;
36 | Version = version;
37 | }
38 |
39 | public string ContentType => $"{Type}/{Format}";
40 |
41 | public string Format { get; }
42 |
43 | public string SubType { get; }
44 |
45 | public string Type { get; }
46 |
47 | public string Version { get; }
48 |
49 | public static bool operator ==(HealthMediaTypeValue left, HealthMediaTypeValue right) { return left.Equals(right); }
50 |
51 | public static bool operator !=(HealthMediaTypeValue left, HealthMediaTypeValue right) { return !left.Equals(right); }
52 |
53 | public override bool Equals(object obj)
54 | {
55 | if (ReferenceEquals(null, obj))
56 | {
57 | return false;
58 | }
59 |
60 | return obj is HealthMediaTypeValue && Equals((HealthMediaTypeValue)obj);
61 | }
62 |
63 | public override int GetHashCode()
64 | {
65 | unchecked
66 | {
67 | var hashCode = Format.GetHashCode();
68 | hashCode = (hashCode * 397) ^ Type.GetHashCode();
69 | hashCode = (hashCode * 397) ^ SubType.GetHashCode();
70 | hashCode = (hashCode * 397) ^ Version.GetHashCode();
71 | return hashCode;
72 | }
73 | }
74 |
75 | public override string ToString() { return $"{Type}/{SubType}-{Version}+{Format}"; }
76 |
77 | public bool Equals(HealthMediaTypeValue other)
78 | {
79 | return string.Equals(Format, other.Format) && string.Equals(Type, other.Type) && string.Equals(SubType, other.SubType) &&
80 | string.Equals(Version, other.Version);
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/Builders/HealthConfigurationBuilderTests.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Collections.Generic;
6 | using App.Metrics.Health.Builder;
7 | using FluentAssertions;
8 | using Xunit;
9 |
10 | namespace App.Metrics.Health.Facts.Builders
11 | {
12 | public class HealthConfigurationBuilderTests
13 | {
14 | [Fact]
15 | public void Can_override_option_instance_values_with_key_value_pair_options()
16 | {
17 | // Arrange
18 | var options = new HealthOptions
19 | {
20 | Enabled = true,
21 | };
22 |
23 | var keyValuePairs = new Dictionary
24 | {
25 | { "HealthOptions:Enabled", "false" }
26 | };
27 |
28 | // Act
29 | var health = new HealthBuilder().Configuration.Configure(options, keyValuePairs).Build();
30 |
31 | // Assert
32 | health.Options.Enabled.Should().BeFalse();
33 | }
34 |
35 | [Fact]
36 | public void Can_set_options_via_key_value_pairs()
37 | {
38 | // Arrange
39 | var keyValuePairs = new Dictionary
40 | {
41 | { "HealthOptions:Enabled", "false" },
42 | { "HealthOptions:ReportingEnabled", "false" },
43 | { "HealthOptions:ApplicationName", "test_app" }
44 | };
45 |
46 | // Act
47 | var health = new HealthBuilder().Configuration.Configure(keyValuePairs).Build();
48 |
49 | // Assert
50 | health.Options.Enabled.Should().BeFalse();
51 | health.Options.ReportingEnabled.Should().BeFalse();
52 | health.Options.ApplicationName.Should().Be("test_app");
53 | }
54 |
55 | [Fact]
56 | public void Can_set_options_via_setup_action()
57 | {
58 | // Arrange
59 | void SetupAction(HealthOptions options)
60 | {
61 | options.Enabled = false;
62 | }
63 |
64 | // Act
65 | var health = new HealthBuilder().Configuration.Configure(SetupAction).Build();
66 |
67 | // Assert
68 | health.Options.Enabled.Should().BeFalse();
69 | }
70 |
71 | [Fact]
72 | public void Can_set_options_with_instance()
73 | {
74 | // Arrange
75 | var options = new HealthOptions { Enabled = true };
76 |
77 | // Act
78 | var health = new HealthBuilder().Configuration.Configure(options).Build();
79 |
80 | // Assert
81 | health.Options.Enabled.Should().BeTrue();
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Formatters/HealthFormatterCollection.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Collections.ObjectModel;
8 |
9 | namespace App.Metrics.Health.Formatters
10 | {
11 | public class HealthFormatterCollection : Collection
12 | {
13 | public HealthFormatterCollection() { }
14 |
15 | public HealthFormatterCollection(IList list)
16 | : base(list)
17 | {
18 | }
19 |
20 | public IHealthOutputFormatter GetType()
21 | where T : IHealthOutputFormatter
22 | {
23 | return GetType(typeof(T));
24 | }
25 |
26 | public IHealthOutputFormatter GetType(Type formatterType)
27 | {
28 | for (var i = Count - 1; i >= 0; i--)
29 | {
30 | var formatter = this[i];
31 | if (formatter.GetType() == formatterType)
32 | {
33 | return formatter;
34 | }
35 | }
36 |
37 | return default;
38 | }
39 |
40 | public IHealthOutputFormatter GetType(HealthMediaTypeValue mediaTypeValue)
41 | {
42 | for (var i = Count - 1; i >= 0; i--)
43 | {
44 | var formatter = this[i];
45 | if (formatter.MediaType == mediaTypeValue)
46 | {
47 | return formatter;
48 | }
49 | }
50 |
51 | return default;
52 | }
53 |
54 | public void RemoveType()
55 | where T : IHealthOutputFormatter
56 | {
57 | RemoveType(typeof(T));
58 | }
59 |
60 | public void RemoveType(Type formatterType)
61 | {
62 | for (var i = Count - 1; i >= 0; i--)
63 | {
64 | var formatter = this[i];
65 | if (formatter.GetType() == formatterType)
66 | {
67 | RemoveAt(i);
68 | }
69 | }
70 | }
71 |
72 | public void RemoveType(HealthMediaTypeValue mediaTypeValue)
73 | {
74 | for (var i = Count - 1; i >= 0; i--)
75 | {
76 | var formatter = this[i];
77 | if (formatter.MediaType == mediaTypeValue)
78 | {
79 | RemoveAt(i);
80 | }
81 | }
82 | }
83 |
84 | public void TryAdd(IHealthOutputFormatter formatter)
85 | where TFormatter : IHealthOutputFormatter
86 | {
87 | RemoveType();
88 | Add(formatter);
89 | }
90 |
91 | public void TryAdd(IHealthOutputFormatter formatter)
92 | {
93 | RemoveType(formatter.GetType());
94 | Add(formatter);
95 | }
96 | }
97 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/Internal/Extensions/AppMetricsHealthLoggerExtensions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Diagnostics;
7 | using System.Diagnostics.CodeAnalysis;
8 | using System.Linq;
9 |
10 | // ReSharper disable CheckNamespace
11 | namespace App.Metrics.Health.Logging
12 | // ReSharper restore CheckNamespace
13 | {
14 | [ExcludeFromCodeCoverage]
15 | internal static class AppMetricsHealthLoggerExtensions
16 | {
17 | internal static void HealthCheckGetStatusExecuted(
18 | this ILog logger,
19 | HealthStatus healthStatus,
20 | long startTimestamp)
21 | {
22 | if (!logger.IsTraceEnabled())
23 | {
24 | return;
25 | }
26 |
27 | if (startTimestamp == 0)
28 | {
29 | return;
30 | }
31 |
32 | var currentTimestamp = Stopwatch.GetTimestamp();
33 | var elapsed = new TimeSpan((long)(TimestampToTicks * (currentTimestamp - startTimestamp)));
34 |
35 | if (healthStatus.Results.Any())
36 | {
37 | if (healthStatus.Status.IsHealthy())
38 | {
39 | logger.Debug(
40 | "Executed HealthStatus, in {ElapsedMilliseconds}ms, IsHealthy: True. {ChecksPassed} health check results passed.",
41 | elapsed.TotalMilliseconds,
42 | healthStatus.Results.Count());
43 | return;
44 | }
45 |
46 | var checksFailed = healthStatus.Results.Count(x => x.Check.Status.IsUnhealthy());
47 | var checksDegraded = healthStatus.Results.Count(x => x.Check.Status.IsDegraded());
48 | var checksPassed = healthStatus.Results.Count(x => x.Check.Status.IsHealthy());
49 | var failedChecks = healthStatus.Results.Where(h => h.Check.Status.IsUnhealthy()).Select(h => h.Name);
50 | var degradedChecks = healthStatus.Results.Where(h => h.Check.Status.IsDegraded()).Select(h => h.Name);
51 |
52 | logger.Debug(
53 | "Executed HealthStatus, in {ElapsedMilliseconds}ms, IsHealthy: False. {ChecksPassed} health check results passed. {ChecksFailed} health check results failed. Failed Checks: {FailedChecks}. {ChecksDegraded} health check results degredated. Degraded Checks: {DegredatedChecks}",
54 | elapsed.TotalMilliseconds,
55 | checksFailed,
56 | checksDegraded,
57 | checksPassed,
58 | degradedChecks,
59 | failedChecks);
60 |
61 | return;
62 | }
63 |
64 | logger.Debug("Executed HealthStatus, 0 health check results.");
65 | }
66 |
67 | internal static void HealthCheckGetStatusExecuting(this ILog logger)
68 | {
69 | logger.Trace("Executing HealthCheck Get Status");
70 | }
71 |
72 | private static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency;
73 | }
74 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/Internal/KeyValuePairHealthOptions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 |
9 | namespace App.Metrics.Health.Internal
10 | {
11 | internal class KeyValuePairHealthOptions
12 | {
13 | internal static readonly string EnabledDirective = $"{nameof(HealthOptions)}:{nameof(HealthOptions.Enabled)}";
14 | internal static readonly string ApplicationNameDirective = $"{nameof(HealthOptions)}:{nameof(HealthOptions.ApplicationName)}";
15 | internal static readonly string ReportingEnabledDirective = $"{nameof(HealthOptions)}:{nameof(HealthOptions.ReportingEnabled)}";
16 |
17 | private readonly HealthOptions _options;
18 |
19 | private readonly Dictionary _optionValues;
20 |
21 | public KeyValuePairHealthOptions(HealthOptions options, IEnumerable> optionValues)
22 | {
23 | if (optionValues == null)
24 | {
25 | throw new ArgumentNullException(nameof(optionValues));
26 | }
27 |
28 | _options = options;
29 | _optionValues = optionValues.ToDictionary(o => o.Key, o => o.Value);
30 | }
31 |
32 | public KeyValuePairHealthOptions(IEnumerable> optionValues)
33 | {
34 | if (optionValues == null)
35 | {
36 | throw new ArgumentNullException(nameof(optionValues));
37 | }
38 |
39 | _optionValues = optionValues.ToDictionary(o => o.Key, o => o.Value);
40 | }
41 |
42 | public HealthOptions AsOptions()
43 | {
44 | var options = _options ?? new HealthOptions();
45 |
46 | foreach (var key in _optionValues.Keys)
47 | {
48 | if (string.Compare(key, EnabledDirective, StringComparison.CurrentCultureIgnoreCase) == 0)
49 | {
50 | if (!bool.TryParse(_optionValues[key], out var metricsEnabled))
51 | {
52 | throw new InvalidCastException($"Attempted to bind {key} to {EnabledDirective} but it's not a boolean");
53 | }
54 |
55 | options.Enabled = metricsEnabled;
56 | }
57 | else if (string.Compare(key, ApplicationNameDirective, StringComparison.CurrentCultureIgnoreCase) == 0)
58 | {
59 | options.ApplicationName = _optionValues[key];
60 | }
61 | else if (string.Compare(key, ReportingEnabledDirective, StringComparison.CurrentCultureIgnoreCase) == 0)
62 | {
63 | if (!bool.TryParse(_optionValues[key], out var reportingEnabled))
64 | {
65 | throw new InvalidCastException($"Attempted to bind {key} to {ReportingEnabledDirective} but it's not a boolean");
66 | }
67 |
68 | options.ReportingEnabled = reportingEnabled;
69 | }
70 | }
71 |
72 | return options;
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Json/Converters/HealthStatusConverter.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using Newtonsoft.Json;
9 |
10 | namespace App.Metrics.Health.Formatters.Json.Converters
11 | {
12 | public class HealthStatusConverter : JsonConverter
13 | {
14 | public override bool CanConvert(Type objectType) { return typeof(HealthStatus) == objectType; }
15 |
16 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
17 | {
18 | var source = serializer.Deserialize(reader);
19 | var healthy = source.Healthy.Keys.Select(k => new HealthCheck.Result(k, HealthCheckResult.Healthy(source.Healthy[k])));
20 | var unhealthy = source.Unhealthy.Keys.Select(k => new HealthCheck.Result(k, HealthCheckResult.Unhealthy(source.Unhealthy[k])));
21 | var degraded = source.Degraded.Keys.Select(k => new HealthCheck.Result(k, HealthCheckResult.Degraded(source.Degraded[k])));
22 | var ignored = source.Ignored.Keys.Select(k => new HealthCheck.Result(k, HealthCheckResult.Ignore(source.Ignored[k])));
23 | var target = new HealthStatus(healthy.Concat(unhealthy).Concat(degraded).Concat(ignored));
24 | return target;
25 | }
26 |
27 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
28 | {
29 | var source = (HealthStatus)value;
30 |
31 | var target = new HealthStatusData
32 | {
33 | Status = HealthConstants.HealthStatusDisplay[source.Status]
34 | };
35 |
36 | var healthy = source.Results.Where(r => r.Check.Status.IsHealthy())
37 | .Select(c => new KeyValuePair(c.Name, CheckMessage(c)))
38 | .ToDictionary(pair => pair.Key, pair => pair.Value);
39 |
40 | var unhealthy = source.Results.Where(r => r.Check.Status.IsUnhealthy())
41 | .Select(c => new KeyValuePair(c.Name, CheckMessage(c)))
42 | .ToDictionary(pair => pair.Key, pair => pair.Value);
43 |
44 | var degraded = source.Results.Where(r => r.Check.Status.IsDegraded())
45 | .Select(c => new KeyValuePair(c.Name, CheckMessage(c)))
46 | .ToDictionary(pair => pair.Key, pair => pair.Value);
47 |
48 | var ignored = source.Results.Where(r => r.Check.Status.IsIgnored())
49 | .Select(c => new KeyValuePair(c.Name, CheckMessage(c)))
50 | .ToDictionary(pair => pair.Key, pair => pair.Value);
51 |
52 | target.Healthy = healthy.Any() ? healthy : null;
53 | target.Unhealthy = unhealthy.Any() ? unhealthy : null;
54 | target.Degraded = degraded.Any() ? degraded : null;
55 | target.Ignored = ignored.Any() ? ignored : null;
56 |
57 | serializer.Serialize(writer, target);
58 | }
59 |
60 | private static string CheckMessage(HealthCheck.Result c)
61 | {
62 | return c.IsFromCache ? $"[Cached] {c.Check.Message}" : c.Check.Message;
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/HealthCheckRunnerTests.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using App.Metrics.Health.Internal;
8 | using FluentAssertions;
9 | using Xunit;
10 |
11 | namespace App.Metrics.Health.Facts
12 | {
13 | public class HealthCheckRunnerTests
14 | {
15 | [Fact]
16 | public async Task Status_is_degraded_if_one_check_is_degraded()
17 | {
18 | // Arrange
19 | var checks = new[]
20 | {
21 | new HealthCheck("ok", () => new ValueTask(HealthCheckResult.Healthy())),
22 | new HealthCheck("degraded", () => new ValueTask(HealthCheckResult.Degraded()))
23 | };
24 | var runner = new DefaultHealthCheckRunner(checks);
25 |
26 | // Act
27 | var status = await runner.ReadAsync();
28 |
29 | // Assert
30 | status.Status.Should().Be(HealthCheckStatus.Degraded);
31 | status.Results.Count().Should().Be(2);
32 | }
33 |
34 | [Fact]
35 | public async Task Status_is_failed_if_one_check_fails()
36 | {
37 | // Arrange
38 | var checks = new[]
39 | {
40 | new HealthCheck("ok", () => new ValueTask(HealthCheckResult.Healthy())),
41 | new HealthCheck("bad", () => new ValueTask(HealthCheckResult.Unhealthy()))
42 | };
43 | var runner = new DefaultHealthCheckRunner(checks);
44 |
45 | // Act
46 | var status = await runner.ReadAsync();
47 |
48 | // Assert
49 | status.Status.Should().Be(HealthCheckStatus.Unhealthy);
50 | status.Results.Count().Should().Be(2);
51 | }
52 |
53 | [Fact]
54 | public async Task Status_is_healthy_if_all_checks_are_healthy()
55 | {
56 | // Arrange
57 | var checks = new[]
58 | {
59 | new HealthCheck("ok", () => new ValueTask(HealthCheckResult.Healthy())),
60 | new HealthCheck("another", () => new ValueTask(HealthCheckResult.Healthy()))
61 | };
62 | var runner = new DefaultHealthCheckRunner(checks);
63 |
64 | // Act
65 | var status = await runner.ReadAsync();
66 |
67 | // Assert
68 | status.Status.Should().Be(HealthCheckStatus.Healthy);
69 | status.Results.Count().Should().Be(2);
70 | }
71 |
72 | [Fact]
73 | public async Task Status_is_unhealthy_if_any_one_check_fails_even_when_degraded()
74 | {
75 | // Arrange
76 | var checks = new[]
77 | {
78 | new HealthCheck("ok", () => new ValueTask(HealthCheckResult.Healthy())),
79 | new HealthCheck("bad", () => new ValueTask(HealthCheckResult.Unhealthy())),
80 | new HealthCheck("degraded", () => new ValueTask(HealthCheckResult.Degraded()))
81 | };
82 | var runner = new DefaultHealthCheckRunner(checks);
83 |
84 | // Act
85 | var status = await runner.ReadAsync();
86 |
87 | // Assert
88 | status.Status.Should().Be(HealthCheckStatus.Unhealthy);
89 | status.Results.Count().Should().Be(3);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Checks.Network/PingHealthCheckBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Net.NetworkInformation;
7 | using System.Threading.Tasks;
8 |
9 | // ReSharper disable CheckNamespace
10 | namespace App.Metrics.Health
11 | // ReSharper restore CheckNamespace
12 | {
13 | public static class PingHealthCheckBuilderExtensions
14 | {
15 | public static IHealthBuilder AddPingCheck(
16 | this IHealthCheckBuilder healthCheckBuilder,
17 | string name,
18 | string host,
19 | TimeSpan timeout,
20 | bool degradedOnError = false)
21 | {
22 | healthCheckBuilder.AddCheck(
23 | name,
24 | async () => await ExecutePingCheckAsync(host, timeout, degradedOnError));
25 |
26 | return healthCheckBuilder.Builder;
27 | }
28 |
29 | public static IHealthBuilder AddPingCheck(
30 | this IHealthCheckBuilder healthCheckBuilder,
31 | string name,
32 | string host,
33 | TimeSpan timeout,
34 | TimeSpan cacheDuration,
35 | bool degradedOnError = false)
36 | {
37 | healthCheckBuilder.AddCachedCheck(
38 | name,
39 | async () => await ExecutePingCheckAsync(host, timeout, degradedOnError),
40 | cacheDuration);
41 |
42 | return healthCheckBuilder.Builder;
43 | }
44 |
45 | public static IHealthBuilder AddPingCheck(
46 | this IHealthCheckBuilder healthCheckBuilder,
47 | string name,
48 | string host,
49 | TimeSpan timeout,
50 | HealthCheck.QuiteTime quiteTime,
51 | bool degradedOnError = false)
52 | {
53 | healthCheckBuilder.AddQuiteTimeCheck(
54 | name,
55 | async () => await ExecutePingCheckAsync(host, timeout, degradedOnError),
56 | quiteTime);
57 |
58 | return healthCheckBuilder.Builder;
59 | }
60 |
61 | private static async Task ExecutePingCheckAsync(string host, TimeSpan timeout, bool degradedOnError)
62 | {
63 | try
64 | {
65 | var ping = new Ping();
66 | var result = await ping.SendPingAsync(host, (int)timeout.TotalMilliseconds).ConfigureAwait(false);
67 |
68 | return result.Status == IPStatus.Success
69 | ? HealthCheckResult.Healthy($"OK. {host}")
70 | : HealthCheckResultOnError($"FAILED. {host} ping result was {result.Status}", degradedOnError);
71 | }
72 | catch (Exception ex)
73 | {
74 | return degradedOnError
75 | ? HealthCheckResult.Degraded(ex)
76 | : HealthCheckResult.Unhealthy(ex);
77 | }
78 | }
79 |
80 | ///
81 | /// Create a failure (degraded or unhealthy) status response.
82 | ///
83 | /// Status message.
84 | ///
85 | /// If true, create a degraded status response.
86 | /// Otherwise create an unhealthy status response. (default: false)
87 | ///
88 | /// Failure status response.
89 | private static HealthCheckResult HealthCheckResultOnError(string message, bool degradedOnError)
90 | {
91 | return degradedOnError
92 | ? HealthCheckResult.Degraded(message)
93 | : HealthCheckResult.Unhealthy(message);
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/HealthCheckCached.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using App.Metrics.Concurrency;
9 |
10 | namespace App.Metrics.Health
11 | {
12 | ///
13 | /// Cached Health Check - Allows caching of Health Check results given a duration to cache
14 | ///
15 | public partial class HealthCheck
16 | {
17 | private readonly TimeSpan _cacheDuration = TimeSpan.Zero;
18 | private Result _cachedResult;
19 | private AtomicLong _reCheckAt = new AtomicLong(0);
20 |
21 | ///
22 | /// Initializes a new instance of the class.
23 | ///
24 | /// A descriptive name for the health check.
25 | /// A function returning either a healthy or un-healthy result.
26 | /// The duration of which to cache the health check result.
27 | public HealthCheck(
28 | string name,
29 | Func> check,
30 | TimeSpan cacheDuration)
31 | {
32 | EnsureValidCacheDuration(cacheDuration);
33 |
34 | Name = name;
35 |
36 | ValueTask CheckWithToken(CancellationToken token) => check();
37 |
38 | _check = CheckWithToken;
39 | _cacheDuration = cacheDuration;
40 | }
41 |
42 | ///
43 | /// Initializes a new instance of the class.
44 | ///
45 | /// A descriptive name for the health check.
46 | /// A function returning either a healthy or un-healthy result.
47 | /// The duration of which to cache the health check result.
48 | public HealthCheck(
49 | string name,
50 | Func> check,
51 | TimeSpan cacheDuration)
52 | {
53 | EnsureValidCacheDuration(cacheDuration);
54 |
55 | Name = name;
56 |
57 | ValueTask CheckWithToken(CancellationToken token) => check(token);
58 |
59 | _check = CheckWithToken;
60 | _cacheDuration = cacheDuration;
61 | }
62 |
63 | protected HealthCheck(string name, TimeSpan cacheDuration)
64 | {
65 | EnsureValidCacheDuration(cacheDuration);
66 |
67 | Name = name;
68 | _cacheDuration = cacheDuration;
69 | _check = token => new ValueTask(HealthCheckResult.Healthy());
70 | }
71 |
72 | private static void EnsureValidCacheDuration(TimeSpan cacheDuration)
73 | {
74 | if (cacheDuration <= TimeSpan.Zero)
75 | {
76 | throw new ArgumentException("Must be greater than zero", nameof(cacheDuration));
77 | }
78 | }
79 |
80 | private async Task ExecuteWithCachingAsync(CancellationToken cancellationToken)
81 | {
82 | if (_reCheckAt.GetValue() >= DateTime.UtcNow.Ticks)
83 | {
84 | return _cachedResult;
85 | }
86 |
87 | var checkResult = await CheckAsync(cancellationToken);
88 | _cachedResult = new Result(Name, checkResult, true);
89 |
90 | _reCheckAt.SetValue(DateTime.UtcNow.Ticks + _cacheDuration.Ticks);
91 |
92 | return new Result(Name, checkResult);
93 | }
94 |
95 | private bool HasCacheDuration() { return _cacheDuration > TimeSpan.Zero; }
96 | }
97 | }
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Formatters.Json.Facts/Helpers/TestHelperExtensions.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Collections.Generic;
6 | using System.IO;
7 | using System.Reflection;
8 | using Newtonsoft.Json;
9 | using Newtonsoft.Json.Linq;
10 |
11 | namespace App.Metrics.Health.Formatters.Json.Facts.Helpers
12 | {
13 | public enum HealthStatusSamples
14 | {
15 | Valid,
16 | NullHealthy,
17 | NullUnhealthy
18 | }
19 |
20 | public static class TestHelperExtensions
21 | {
22 | private static readonly Dictionary HealthStatusFileSampleMapping = new Dictionary
23 | {
24 | {
25 | HealthStatusSamples.Valid,
26 | "healthstatus"
27 | },
28 | {
29 | HealthStatusSamples.NullHealthy,
30 | "healthstatus_null_healthy"
31 | },
32 | {
33 | HealthStatusSamples.NullUnhealthy,
34 | "healthstatus_null_unhealthy"
35 | }
36 | };
37 |
38 | public static JToken ParseAsJson(this string json)
39 | {
40 | return JToken.Parse(json, new JsonLoadSettings { LineInfoHandling = LineInfoHandling.Ignore, CommentHandling = CommentHandling.Ignore });
41 | }
42 |
43 | public static JToken SampleJson(this HealthStatusSamples sample) { return sample.ExtractHealthStatusSampleFromResourceFile(); }
44 |
45 | private static JToken ExtractHealthStatusSampleFromResourceFile(this HealthStatusSamples sample)
46 | {
47 | return ExtractJsonFromEmbeddedResource(HealthStatusFileSampleMapping[sample]);
48 | }
49 |
50 | private static JToken ExtractJsonFromEmbeddedResource(string key)
51 | {
52 | var assemblyName = new AssemblyName("App.Metrics.Health.Formatters.Json.Facts");
53 | using (
54 | var fileStream =
55 | Assembly.Load(assemblyName).GetManifestResourceStream($"App.Metrics.Health.Formatters.Json.Facts.JsonFiles.{key}.json"))
56 | {
57 | if (fileStream == null)
58 | {
59 | return null;
60 | }
61 |
62 | using (var textReader = new StreamReader(fileStream))
63 | {
64 | using (var jsonReader = new JsonTextReader(textReader))
65 | {
66 | return JToken.ReadFrom(jsonReader);
67 | }
68 | }
69 | }
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Slack/Internal/JsonContent.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System.Net.Http;
6 | using System.Text;
7 | using Newtonsoft.Json;
8 |
9 | namespace App.Metrics.Health.Reporting.Slack.Internal
10 | {
11 | ///
12 | /// Provides HTTP content based on a json string.
13 | ///
14 | internal class JsonContent : StringContent
15 | {
16 | ///
17 | /// Initializes a new instance of the class.
18 | ///
19 | /// The content used to initialize the .
20 | public JsonContent(object content)
21 | : base(JsonConvert.SerializeObject(content, DefaultJsonSerializerSettings.Instance), Encoding.UTF8, "application/json")
22 | {
23 | }
24 |
25 | ///
26 | /// Initializes a new instance of the class.
27 | ///
28 | ///
29 | /// The content used to initialize the .
30 | ///
31 | /// The encoding to use for the content.
32 | public JsonContent(object content, Encoding encoding)
33 | : base(JsonConvert.SerializeObject(content, DefaultJsonSerializerSettings.Instance), encoding, "application/json")
34 | {
35 | }
36 |
37 | ///
38 | /// Initializes a new instance of the class.
39 | ///
40 | /// The content.
41 | /// The encoding.
42 | /// Type of the media.
43 | /// The JSON serializer setting to use
44 | public JsonContent(object content, Encoding encoding, string mediaType, JsonSerializerSettings setting)
45 | : base(JsonConvert.SerializeObject(content, setting), encoding, mediaType)
46 | {
47 | }
48 |
49 | ///
50 | /// Initializes a new instance of the class.
51 | ///
52 | /// The content used to initialize the .
53 | /// The JSON serializer setting to use
54 | public JsonContent(object content, JsonSerializerSettings setting)
55 | : base(JsonConvert.SerializeObject(content, setting), Encoding.UTF8, "application/json")
56 | {
57 | }
58 |
59 | ///
60 | /// Initializes a new instance of the class.
61 | ///
62 | ///
63 | /// The content used to initialize the .
64 | ///
65 | /// The encoding to use for the content.
66 | /// The JSON serializer setting to use
67 | public JsonContent(object content, Encoding encoding, JsonSerializerSettings setting)
68 | : base(JsonConvert.SerializeObject(content, setting), encoding, "application/json")
69 | {
70 | }
71 |
72 | ///
73 | /// Initializes a new instance of the class.
74 | ///
75 | /// The content.
76 | /// The encoding.
77 | /// Type of the media.
78 | public JsonContent(object content, Encoding encoding, string mediaType)
79 | : base(JsonConvert.SerializeObject(content, DefaultJsonSerializerSettings.Instance), encoding, mediaType)
80 | {
81 | }
82 | }
83 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Reporting.Metrics/HealthResultsAsMetricsReporter.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using App.Metrics.Health.Logging;
9 | #if !NETSTANDARD1_6
10 | using App.Metrics.Health.Internal;
11 | #endif
12 | using App.Metrics.Health.Reporting.Metrics.Internal;
13 |
14 | namespace App.Metrics.Health.Reporting.Metrics
15 | {
16 | public class HealthResultsAsMetricsReporter : IReportHealthStatus
17 | {
18 | private static readonly ILog Logger = LogProvider.For();
19 | private readonly IMetrics _metrics;
20 | private readonly HealthAsMetricsOptions _healthAsMetricsOptions;
21 |
22 | public HealthResultsAsMetricsReporter(IMetrics metrics)
23 | : this(metrics, new HealthAsMetricsOptions())
24 | {
25 | }
26 |
27 | public HealthResultsAsMetricsReporter(IMetrics metrics, HealthAsMetricsOptions healthAsMetricsOptions)
28 | {
29 | _healthAsMetricsOptions = healthAsMetricsOptions ?? throw new ArgumentNullException(nameof(healthAsMetricsOptions));
30 | _metrics = metrics ?? throw new ArgumentNullException(nameof(metrics));
31 |
32 | ReportInterval = healthAsMetricsOptions.ReportInterval > TimeSpan.Zero
33 | ? healthAsMetricsOptions.ReportInterval
34 | : HealthConstants.Reporting.DefaultReportInterval;
35 |
36 | Logger.Trace($"Using Metrics Reporter {this}. ReportInterval: {ReportInterval}");
37 | }
38 |
39 | ///
40 | public TimeSpan ReportInterval { get; set; }
41 |
42 | ///
43 | public Task ReportAsync(HealthOptions options, HealthStatus status, CancellationToken cancellationToken = default)
44 | {
45 | if (!_healthAsMetricsOptions.Enabled || !options.Enabled)
46 | {
47 | Logger.Trace($"Health Status Reporter `{this}` disabled, not reporting.");
48 |
49 | #if NETSTANDARD1_6
50 | return Task.CompletedTask;
51 | #else
52 | return AppMetricsHealthTaskHelper.CompletedTask();
53 | #endif
54 | }
55 |
56 | Logger.Trace($"Health Status Reporter `{this}` reporting health status.");
57 |
58 | foreach (var healthResult in status.Results)
59 | {
60 | var tags = new MetricTags(HealthReportingConstants.TagKeys.HealthCheckName, healthResult.Name);
61 |
62 | if (healthResult.Check.Status == HealthCheckStatus.Degraded)
63 | {
64 | _metrics.Measure.Gauge.SetValue(ApplicationHealthMetricRegistry.Checks, tags, HealthConstants.HealthScore.degraded);
65 | }
66 | else if (healthResult.Check.Status == HealthCheckStatus.Unhealthy)
67 | {
68 | _metrics.Measure.Gauge.SetValue(ApplicationHealthMetricRegistry.Checks, tags, HealthConstants.HealthScore.unhealthy);
69 | }
70 | else if (healthResult.Check.Status == HealthCheckStatus.Healthy)
71 | {
72 | _metrics.Measure.Gauge.SetValue(ApplicationHealthMetricRegistry.Checks, tags, HealthConstants.HealthScore.healthy);
73 | }
74 | }
75 |
76 | var overallHealthStatus = HealthConstants.HealthScore.healthy;
77 |
78 | if (status.Status == HealthCheckStatus.Unhealthy)
79 | {
80 | overallHealthStatus = HealthConstants.HealthScore.unhealthy;
81 | }
82 | else if (status.Status == HealthCheckStatus.Degraded)
83 | {
84 | overallHealthStatus = HealthConstants.HealthScore.degraded;
85 | }
86 |
87 | _metrics.Measure.Gauge.SetValue(ApplicationHealthMetricRegistry.HealthGauge, overallHealthStatus);
88 |
89 | Logger.Trace($"Health Status Reporter `{this}` successfully reported health status.");
90 |
91 | #if NETSTANDARD1_6
92 | return Task.CompletedTask;
93 | #else
94 | return AppMetricsHealthTaskHelper.CompletedTask();
95 | #endif
96 | }
97 | }
98 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Formatters.Ascii/HealthStatusTextWriter.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using System.Linq;
9 | using App.Metrics.Health.Formatters.Ascii.Internal;
10 | using App.Metrics.Health.Serialization;
11 |
12 | namespace App.Metrics.Health.Formatters.Ascii
13 | {
14 | public class HealthStatusTextWriter : IHealthStatusWriter
15 | {
16 | private readonly int _padding;
17 | private readonly string _separator;
18 | private readonly TextWriter _textWriter;
19 |
20 | public HealthStatusTextWriter(
21 | TextWriter textWriter,
22 | string separator = HealthStatusFormatterConstants.OutputFormatting.Separator,
23 | int padding = HealthStatusFormatterConstants.OutputFormatting.Padding)
24 | {
25 | _textWriter = textWriter ?? throw new ArgumentNullException(nameof(textWriter));
26 |
27 | _padding = padding;
28 | _separator = separator;
29 | }
30 |
31 | ///
32 | public void Dispose()
33 | {
34 | Dispose(true);
35 | GC.SuppressFinalize(this);
36 | }
37 |
38 | public void Write(HealthStatus healthStatus)
39 | {
40 | var status = GetOverallStatus(healthStatus.Results);
41 |
42 | _textWriter.Write($"# OVERALL STATUS: {status}");
43 | _textWriter.Write("\n--------------------------------------------------------------\n");
44 |
45 | foreach (var result in healthStatus.Results.OrderBy(r => (int)r.Check.Status))
46 | {
47 | WriteCheckResult(result);
48 | }
49 | }
50 |
51 | ///
52 | /// Releases unmanaged and - optionally - managed resources.
53 | ///
54 | ///
55 | /// true to release both managed and unmanaged resources; false to release only
56 | /// unmanaged resources.
57 | ///
58 | protected virtual void Dispose(bool disposing)
59 | {
60 | if (disposing)
61 | {
62 | #if NET452
63 | _textWriter?.Close();
64 | #endif
65 | _textWriter?.Dispose();
66 | }
67 | }
68 |
69 | private string FormatReadable(string label, string value)
70 | {
71 | var pad = string.Empty;
72 |
73 | if (label.Length + 2 + _separator.Length < _padding)
74 | {
75 | pad = new string(' ', _padding - label.Length - 1 - _separator.Length);
76 | }
77 |
78 | return $"{pad}{label} {_separator} {value}";
79 | }
80 |
81 | private string GetOverallStatus(IEnumerable results)
82 | {
83 | var status = HealthConstants.DegradedStatusDisplay;
84 |
85 | var enumerable = results as HealthCheck.Result[] ?? results.ToArray();
86 | var failed = enumerable.Any(c => c.Check.Status == HealthCheckStatus.Unhealthy);
87 | var degraded = enumerable.Any(c => c.Check.Status == HealthCheckStatus.Degraded);
88 |
89 | if (!degraded && !failed)
90 | {
91 | status = HealthConstants.HealthyStatusDisplay;
92 | }
93 |
94 | if (failed)
95 | {
96 | status = HealthConstants.UnhealthyStatusDisplay;
97 | }
98 |
99 | return status;
100 | }
101 |
102 | private void WriteCheckResult(HealthCheck.Result checkResult)
103 | {
104 | _textWriter.Write("# CHECK: ");
105 | _textWriter.Write(checkResult.Name);
106 | _textWriter.Write('\n');
107 | _textWriter.Write('\n');
108 | _textWriter.Write(FormatReadable("MESSAGE", checkResult.IsFromCache ? $"[Cached] {checkResult.Check.Message}" : checkResult.Check.Message));
109 | _textWriter.Write('\n');
110 | _textWriter.Write(FormatReadable("STATUS", HealthConstants.HealthStatusDisplay[checkResult.Check.Status]));
111 | _textWriter.Write("\n--------------------------------------------------------------");
112 | _textWriter.Write('\n');
113 | }
114 | }
115 | }
--------------------------------------------------------------------------------
/test/App.Metrics.Health.Facts/Builders/HealthCheckBuilderTests.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Linq;
7 | using System.Threading.Tasks;
8 | using App.Metrics.Health.Builder;
9 | using App.Metrics.Health.Facts.TestHelpers;
10 | using FluentAssertions;
11 | using Xunit;
12 |
13 | namespace App.Metrics.Health.Facts.Builders
14 | {
15 | public class HealthCheckBuilderTests
16 | {
17 | private static readonly ValueTask HealthyResult = new ValueTask(HealthCheckResult.Healthy());
18 |
19 | [Fact]
20 | public void Can_register_health_check_with_cancellation_token()
21 | {
22 | // Arrange
23 | const string check = "check with token";
24 | var builder = new HealthBuilder();
25 |
26 | // Act
27 | var health = builder.HealthChecks.AddCheck(check, token => new ValueTask(HealthCheckResult.Degraded())).Build();
28 |
29 | // Assert
30 | health.Checks.Count().Should().Be(1);
31 | health.Checks.First(c => c.Name == check).Should().NotBeNull();
32 | }
33 |
34 | [Fact]
35 | public void Can_register_health_check_without_cancellation_token()
36 | {
37 | // Arrange
38 | const string check = "check";
39 | var builder = new HealthBuilder();
40 |
41 | // Act
42 | var health = builder.HealthChecks.AddCheck(check, () => new ValueTask(HealthCheckResult.Degraded())).Build();
43 |
44 | // Assert
45 | health.Checks.Count().Should().Be(1);
46 | health.Checks.First(c => c.Name == check).Should().NotBeNull();
47 | }
48 |
49 | [Fact]
50 | public void Can_register_instance_health_checks()
51 | {
52 | // Arrange
53 | var builder = new HealthBuilder();
54 | var check = new DatabaseHealthCheck(new Database());
55 |
56 | // Act
57 | var health = builder.HealthChecks.AddCheck(check).Build();
58 |
59 | // Assert
60 | health.Checks.Count().Should().Be(1);
61 | health.Checks.Single().Name.Should().Be("DatabaseCheck");
62 | }
63 |
64 | [Fact]
65 | public void Can_register_type_health_checks()
66 | {
67 | // Arrange
68 | var builder = new HealthBuilder();
69 |
70 | // Act
71 | var health = builder.HealthChecks.AddCheck().Build();
72 |
73 | // Assert
74 | health.Checks.Count().Should().Be(1);
75 | health.Checks.Single().Name.Should().Be("SampleHealthCheck");
76 | }
77 |
78 | [Fact]
79 | public void Can_register_mulitple_health_checks()
80 | {
81 | // Arrange
82 | var builder = new HealthBuilder();
83 | var checks = new[]
84 | {
85 | new HealthCheck("first", () => HealthyResult),
86 | new HealthCheck("second", () => HealthyResult)
87 | };
88 |
89 | // Act
90 | var health = builder.HealthChecks.AddChecks(checks).Build();
91 |
92 | // Assert
93 | health.Checks.Count().Should().Be(2);
94 | }
95 |
96 | [Fact]
97 | public void Cannot_set_null_health_check()
98 | {
99 | // Arrange
100 | Action action = () =>
101 | {
102 | // Act
103 | var unused = new HealthBuilder().HealthChecks.AddCheck(null, () => HealthyResult);
104 | };
105 |
106 | // Assert
107 | action.Should().Throw();
108 | }
109 |
110 | [Fact]
111 | public void Throws_on_duplicate_registration()
112 | {
113 | // Arrange
114 | var builder = new HealthBuilder().HealthChecks.AddCheck("test", () => HealthyResult);
115 |
116 | Action action = () =>
117 | {
118 | // Act
119 | builder.HealthChecks.AddCheck("test", () => HealthyResult);
120 | var unused = builder.Build();
121 | };
122 |
123 | // Assert
124 | action.Should().Throw();
125 | }
126 | }
127 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/Builder/HealthConfigurationBuilder.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using App.Metrics.Health.Internal;
8 | using App.Metrics.Health.Internal.Extensions;
9 |
10 | // ReSharper disable CheckNamespace
11 | namespace App.Metrics.Health
12 | // ReSharper restore CheckNamespace
13 | {
14 | ///
15 | /// Builder for configuring the .
16 | ///
17 | public class HealthConfigurationBuilder : IHealthConfigurationBuilder
18 | {
19 | private readonly Action _setupAction;
20 | private HealthOptions _options;
21 |
22 | internal HealthConfigurationBuilder(
23 | IHealthBuilder healthBuilder,
24 | HealthOptions currentOptions,
25 | Action setupAction)
26 | {
27 | Builder = healthBuilder ?? throw new ArgumentNullException(nameof(healthBuilder));
28 | _setupAction = setupAction ?? throw new ArgumentNullException(nameof(setupAction));
29 | _options = currentOptions ?? new HealthOptions();
30 | }
31 |
32 | ///
33 | public IHealthBuilder Builder { get; }
34 |
35 | ///
36 | public IHealthBuilder Configure(HealthOptions options)
37 | {
38 | if (options == null)
39 | {
40 | throw new ArgumentNullException(nameof(options));
41 | }
42 |
43 | _setupAction(options);
44 |
45 | _options = options;
46 |
47 | return Builder;
48 | }
49 |
50 | ///
51 | public IHealthBuilder Configure(IEnumerable> optionValues)
52 | {
53 | if (optionValues == null)
54 | {
55 | throw new ArgumentNullException(nameof(optionValues));
56 | }
57 |
58 | var mergedOptions = new KeyValuePairHealthOptions(_options, optionValues).AsOptions();
59 |
60 | _setupAction(mergedOptions);
61 |
62 | _options = mergedOptions;
63 |
64 | return Builder;
65 | }
66 |
67 | ///
68 | public IHealthBuilder Configure(HealthOptions options, IEnumerable> optionValues)
69 | {
70 | if (options == null)
71 | {
72 | throw new ArgumentNullException(nameof(options));
73 | }
74 |
75 | if (optionValues == null)
76 | {
77 | throw new ArgumentNullException(nameof(optionValues));
78 | }
79 |
80 | _setupAction(new KeyValuePairHealthOptions(options, optionValues).AsOptions());
81 |
82 | _options = options;
83 |
84 | return Builder;
85 | }
86 |
87 | ///
88 | public IHealthBuilder Configure(Action setupAction)
89 | {
90 | if (setupAction == null)
91 | {
92 | throw new ArgumentNullException(nameof(setupAction));
93 | }
94 |
95 | setupAction(_options);
96 |
97 | _setupAction(_options);
98 |
99 | return Builder;
100 | }
101 |
102 | ///
103 | public IHealthBuilder Extend(IEnumerable> optionValues)
104 | {
105 | if (optionValues == null)
106 | {
107 | throw new ArgumentNullException(nameof(optionValues));
108 | }
109 |
110 | var mergedOptions = new KeyValuePairHealthOptions(_options, optionValues).AsOptions();
111 |
112 | _setupAction(mergedOptions);
113 |
114 | _options = mergedOptions;
115 |
116 | return Builder;
117 | }
118 |
119 | ///
120 | public IHealthBuilder Extend(HealthOptions options)
121 | {
122 | if (options == null)
123 | {
124 | throw new ArgumentNullException(nameof(options));
125 | }
126 |
127 | var optionsValuesToExtend = options.ToKeyValue();
128 | var extendedOptions = new KeyValuePairHealthOptions(_options, optionsValuesToExtend).AsOptions();
129 |
130 | _setupAction(extendedOptions);
131 |
132 | _options = extendedOptions;
133 |
134 | return Builder;
135 | }
136 | }
137 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Builder/IHealthConfigurationBuilder.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Collections.Generic;
7 |
8 | // ReSharper disable CheckNamespace
9 | namespace App.Metrics.Health
10 | // ReSharper restore CheckNamespace
11 | {
12 | public interface IHealthConfigurationBuilder
13 | {
14 | ///
15 | /// Gets the where App Metrics Helath is configured.
16 | ///
17 | IHealthBuilder Builder { get; }
18 |
19 | ///
20 | /// Uses the specifed instance for App Metrics Health core configuration.
21 | ///
22 | /// An instance used to configure core App Metrics Health options.
23 | ///
24 | /// An that can be used to further configure App Metrics Health.
25 | ///
26 | IHealthBuilder Configure(HealthOptions options);
27 |
28 | ///
29 | ///
30 | /// Uses the specifed key value pairs to configure an instance for App Metrics Health core
31 | /// configuration.
32 | ///
33 | ///
34 | /// Keys match the s property names.
35 | ///
36 | ///
37 | /// Key value pairs for configuring App Metrics Health
38 | ///
39 | /// An that can be used to further configure App Metrics Health.
40 | ///
41 | IHealthBuilder Configure(IEnumerable> optionValues);
42 |
43 | ///
44 | ///
45 | /// Uses the specifed key value pairs to configure an instance for App Metrics Health core
46 | /// configuration.
47 | ///
48 | ///
49 | /// Keys match the s property names. Any make key will override the
50 | /// value configured.
51 | ///
52 | ///
53 | /// An instance used to configure core App Metrics Health options.
54 | /// Key value pairs for configuring App Metrics
55 | ///
56 | /// An that can be used to further configure App Metrics Health.
57 | ///
58 | IHealthBuilder Configure(HealthOptions options, IEnumerable> optionValues);
59 |
60 | ///
61 | ///
62 | /// Uses the specifed key value pairs to configure an instance for App Metrics Health core
63 | /// configuration.
64 | ///
65 | ///
66 | /// Keys match the s property names. Any make key will override the
67 | /// value configured.
68 | ///
69 | ///
70 | /// An setup action used to configure core App Metrics Health options.
71 | ///
72 | /// An that can be used to further configure App Metrics Health.
73 | ///
74 | IHealthBuilder Configure(Action setupAction);
75 |
76 | ///
77 | /// Merges the specifed instance with any previously configured options.
78 | ///
79 | /// An instance used to configure core App Metrics Health options.
80 | ///
81 | /// An that can be used to further configure App Metrics Health.
82 | ///
83 | IHealthBuilder Extend(HealthOptions options);
84 |
85 | ///
86 | /// Merges the specifed instance with any previously configured options.
87 | ///
88 | /// An used to configure core App Metrics Health options.
89 | ///
90 | /// An that can be used to further configure App Metrics Health.
91 | ///
92 | IHealthBuilder Extend(IEnumerable> optionValues);
93 | }
94 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Abstractions/Builder/IHealthOutputFormattingBuilder.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using App.Metrics.Health.Formatters;
6 |
7 | // ReSharper disable CheckNamespace
8 | namespace App.Metrics.Health
9 | // ReSharper restore CheckNamespace
10 | {
11 | public interface IHealthOutputFormattingBuilder
12 | {
13 | ///
14 | /// Gets the where App Metrics Health is configured.
15 | ///
16 | IHealthBuilder Builder { get; }
17 |
18 | ///
19 | ///
20 | /// Uses the specifed as one of the available formatters when reporting
21 | /// metric values.
22 | ///
23 | ///
24 | /// Multiple formatters can be used, in which case the
25 | /// will be set to the first configured formatter.
26 | ///
27 | ///
28 | /// An instance used to format metric values when reporting.
29 | ///
30 | /// An that can be used to further configure App Metrics.
31 | ///
32 | IHealthBuilder Using(IHealthOutputFormatter formatter);
33 |
34 | ///
35 | ///
36 | /// Uses the specifed as one of the available formatters when reporting
37 | /// metric values.
38 | ///
39 | ///
40 | /// Multiple formatters can be used, in which case the
41 | /// will be set to the first configured formatter.
42 | ///
43 | ///
44 | ///
45 | /// An type used to format metric values
46 | /// when reporting.
47 | ///
48 | ///
49 | /// An that can be used to further configure App Metrics.
50 | ///
51 | IHealthBuilder Using()
52 | where THealthOutputFormatter : IHealthOutputFormatter, new();
53 |
54 | ///
55 | ///
56 | /// Uses the specifed as one of the available formatters when reporting
57 | /// metric values.
58 | ///
59 | ///
60 | /// Multiple formatters can be used, in which case the
61 | /// will be set to the first configured formatter.
62 | ///
63 | ///
64 | /// An instance used to format metric values when reporting.
65 | ///
66 | /// If [true] replaces matching formatter type with the formatter instance, otherwise the
67 | /// existing formatter instance of matching type.
68 | ///
69 | ///
70 | /// An that can be used to further configure App Metrics.
71 | ///
72 | IHealthBuilder Using(IHealthOutputFormatter formatter, bool replaceExisting);
73 |
74 | ///
75 | ///
76 | /// Uses the specifed as one of the available formatters when reporting
77 | /// metric values.
78 | ///
79 | ///
80 | /// Multiple formatters can be used, in which case the
81 | /// will be set to the first configured formatter.
82 | ///
83 | ///
84 | ///
85 | /// An type used to format metric values
86 | /// when reporting.
87 | ///
88 | ///
89 | /// If [true] replaces matching formatter type with the formatter instance, otherwise the
90 | /// existing formatter instance of matching type.
91 | ///
92 | ///
93 | /// An that can be used to further configure App Metrics.
94 | ///
95 | IHealthBuilder Using(bool replaceExisting)
96 | where THealthOutputFormatter : IHealthOutputFormatter, new();
97 | }
98 | }
--------------------------------------------------------------------------------
/src/App.Metrics.Health.Core/Builder/HealthBuilder.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Reflection;
9 | using App.Metrics.Health.Formatters;
10 | using App.Metrics.Health.Formatters.Ascii;
11 | using App.Metrics.Health.Internal;
12 | using App.Metrics.Health.Internal.NoOp;
13 | using App.Metrics.Health.Logging;
14 |
15 | namespace App.Metrics.Health.Builder
16 | {
17 | public class HealthBuilder : IHealthBuilder
18 | {
19 | private static readonly ILog Logger = LogProvider.For();
20 | private readonly Dictionary _checks = new Dictionary(StringComparer.OrdinalIgnoreCase);
21 | private readonly HealthFormatterCollection _healthFormatterCollection = new HealthFormatterCollection();
22 | private readonly HealthReporterCollection _healthStatusReporters = new HealthReporterCollection();
23 | private IHealthOutputFormatter _defaultMetricsHealthFormatter;
24 | private HealthOptions _options;
25 |
26 | ///
27 | public bool CanReport() { return _options.Enabled && _options.ReportingEnabled && _healthStatusReporters.Any(); }
28 |
29 | ///
30 | public IHealthConfigurationBuilder Configuration
31 | {
32 | get
33 | {
34 | return new HealthConfigurationBuilder(
35 | this,
36 | _options,
37 | options => { _options = options; });
38 | }
39 | }
40 |
41 | public IHealthCheckBuilder HealthChecks
42 | {
43 | get
44 | {
45 | return new HealthCheckBuilder(
46 | this,
47 | healthCheck =>
48 | {
49 | try
50 | {
51 | _checks.Add(healthCheck.Name, healthCheck);
52 | }
53 | catch (ArgumentException ex)
54 | {
55 | Logger.Error(ex, $"Attempted to add health checks with duplicates names: {healthCheck.Name}");
56 | throw;
57 | }
58 | });
59 | }
60 | }
61 |
62 | ///
63 | public IHealthOutputFormattingBuilder OutputHealth => new HealthOutputFormattingBuilder(
64 | this,
65 | (replaceExisting, formatter) =>
66 | {
67 | if (_defaultMetricsHealthFormatter == null)
68 | {
69 | _defaultMetricsHealthFormatter = formatter;
70 | }
71 |
72 | if (replaceExisting)
73 | {
74 | _healthFormatterCollection.TryAdd(formatter);
75 | }
76 | else
77 | {
78 | if (_healthFormatterCollection.GetType(formatter.GetType()) == null)
79 | {
80 | _healthFormatterCollection.Add(formatter);
81 | }
82 | }
83 | });
84 |
85 | ///
86 | public IHealthReportingBuilder Report => new HealthReportingBuilder(
87 | this,
88 | reporter => { _healthStatusReporters.TryAdd(reporter); });
89 |
90 | public IHealthRoot Build()
91 | {
92 | if (_options == null)
93 | {
94 | _options = new HealthOptions();
95 | }
96 |
97 | if (_healthFormatterCollection.Count == 0)
98 | {
99 | _healthFormatterCollection.Add(new HealthStatusTextOutputFormatter());
100 | }
101 |
102 | IRunHealthChecks healthCheckRunner;
103 |
104 | var health = new DefaultHealth(_checks.Values);
105 | var defaultMetricsOutputFormatter = _defaultMetricsHealthFormatter ?? _healthFormatterCollection.FirstOrDefault();
106 |
107 | if (_options.Enabled && health.Checks.Any())
108 | {
109 | healthCheckRunner = new DefaultHealthCheckRunner(health.Checks);
110 | }
111 | else
112 | {
113 | healthCheckRunner = new NoOpHealthCheckRunner();
114 | }
115 |
116 | if (string.IsNullOrWhiteSpace(_options.ApplicationName))
117 | {
118 | var entryAssembly = Assembly.GetEntryAssembly();
119 |
120 | _options.ApplicationName = entryAssembly?.GetName()?.Name?.Trim();
121 | }
122 |
123 | return new HealthRoot(
124 | health,
125 | _options,
126 | _healthFormatterCollection,
127 | defaultMetricsOutputFormatter,
128 | healthCheckRunner,
129 | _healthStatusReporters);
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/benchmarks/App.Metrics.Health.Benchmarks/Support/BenchmarkTestExecutor.cs:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) App Metrics Contributors. All rights reserved.
3 | //
4 |
5 | using System;
6 | using System.Linq;
7 | using BenchmarkDotNet.Columns;
8 | using BenchmarkDotNet.Configs;
9 | using BenchmarkDotNet.Loggers;
10 | using BenchmarkDotNet.Reports;
11 | using BenchmarkDotNet.Running;
12 | using Xunit;
13 | using Xunit.Abstractions;
14 |
15 | namespace App.Metrics.Health.Benchmarks.Support
16 | {
17 | // ReSharper disable UnusedMember.Global
18 | public class BenchmarkTestExecutor
19 | // ReSharper restore UnusedMember.Global
20 | {
21 | private readonly ITestOutputHelper _output;
22 |
23 | public BenchmarkTestExecutor() { }
24 |
25 | protected BenchmarkTestExecutor(ITestOutputHelper output) { _output = output; }
26 |
27 | ///
28 | /// Runs Benchmarks with the most simple config (SingleRunFastConfig)
29 | /// combined with any benchmark config applied to TBenchmark (via an attribute)
30 | /// By default will verify if every benchmark was successfully executed
31 | ///
32 | /// type that defines Benchmarks
33 | /// Optional custom config to be used instead of the default
34 | /// Optional: disable validation (default = true/enabled)
35 | /// The summary from the benchmark run
36 | // ReSharper disable UnusedMember.Global
37 | internal Summary CanExecute(IConfig config = null, bool fullValidation = true)
38 | // ReSharper restore UnusedMember.Global
39 | {
40 | return CanExecute(typeof(TBenchmark), config, fullValidation);
41 | }
42 |
43 | ///
44 | /// Runs Benchmarks with the most simple config (SingleRunFastConfig)
45 | /// combined with any benchmark config applied to Type (via an attribute)
46 | /// By default will verify if every benchmark was successfully executed
47 | ///
48 | /// type that defines Benchmarks
49 | /// Optional custom config to be used instead of the default
50 | /// Optional: disable validation (default = true/enabled)
51 | /// The summary from the benchmark run
52 | private Summary CanExecute(Type type, IConfig config = null, bool fullValidation = true)
53 | {
54 | // Add logging, so the Benchmark execution is in the TestRunner output (makes Debugging easier)
55 | if (config == null)
56 | {
57 | config = CreateSimpleConfig();
58 | }
59 |
60 | if (!config.GetLoggers().OfType().Any())
61 | {
62 | config = config.With(_output != null ? new OutputLogger(_output) : ConsoleLogger.Default);
63 | }
64 |
65 | if (!config.GetColumnProviders().Any())
66 | {
67 | config = config.With(DefaultColumnProviders.Instance);
68 | }
69 |
70 | // Make sure we ALWAYS combine the Config (default or passed in) with any Config applied to the Type/Class
71 | var summary = BenchmarkRunner.Run(type, BenchmarkConverter.GetFullConfig(type, config));
72 |
73 | if (!fullValidation)
74 | {
75 | return summary;
76 | }
77 |
78 | Assert.False(summary.HasCriticalValidationErrors, "The \"Summary\" should have NOT \"HasCriticalValidationErrors\"");
79 |
80 | Assert.True(summary.Reports.Any(), "The \"Summary\" should contain at least one \"BenchmarkReport\" in the \"Reports\" collection");
81 |
82 | Assert.True(
83 | summary.Reports.All(r => r.BuildResult.IsBuildSuccess),
84 | "The following benchmarks are failed to build: " + string.Join(", ", summary.Reports.Where(r => !r.BuildResult.IsBuildSuccess).Select(r => r.BenchmarkCase.DisplayInfo)));
85 |
86 | Assert.True(
87 | summary.Reports.All(r => r.ExecuteResults.Any(er => er.FoundExecutable && er.Data.Any())),
88 | "All reports should have at least one \"ExecuteResult\" with \"FoundExecutable\" = true and at least one \"Data\" item");
89 |
90 | Assert.True(
91 | summary.Reports.All(report => report.AllMeasurements.Any()),
92 | "All reports should have at least one \"Measurement\" in the \"AllMeasurements\" collection");
93 |
94 | return summary;
95 | }
96 |
97 | private IConfig CreateSimpleConfig(OutputLogger logger = null)
98 | {
99 | return new SingleRunFastConfig()
100 | .With(logger ?? (_output != null ? new OutputLogger(_output) : ConsoleLogger.Default))
101 | .With(DefaultColumnProviders.Instance);
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------