├── .gitattributes ├── .gitignore ├── .travis.yml ├── AppMetrics.ruleset ├── AspNetCoreHealth.sln ├── AspNetCoreHealth.sln.DotSettings ├── CONTRIBUTING.md ├── GitReleaseManager.yaml ├── ISSUE_TEMPLATE.md ├── LICENSE ├── NuGet.config ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── app-metrics.licenseheader ├── appveyor.yml ├── build.cake ├── build.ps1 ├── build.sh ├── build ├── Key.snk ├── common.props └── dependencies.props ├── global.json ├── sandbox ├── AdditionalChecks │ ├── AdditionalCheck.cs │ └── AdditionalChecks.csproj └── HealthSandboxMvc │ ├── Controllers │ └── TestController.cs │ ├── GlobalSuppressions.cs │ ├── HealthChecks │ └── SlowHealthCheck.cs │ ├── HealthSandboxMvc.csproj │ ├── HealthStartupFilter.cs │ ├── Host.cs │ ├── Properties │ └── launchSettings.json │ ├── Startup.cs │ └── appsettings.json ├── src ├── App.Metrics.AspNetCore.Health.Core │ ├── App.Metrics.AspNetCore.Health.Core.csproj │ ├── DefaultHealthResponseWriter.cs │ ├── Extensions │ │ ├── MediaTypeHeaderValueExtensions.cs │ │ └── RequestHeaderExtensions.cs │ ├── IHealthResponseWriter.cs │ ├── Internal │ │ ├── Extensions │ │ │ ├── AppMetricsMiddlewareHealthChecksLoggerExtensions.cs │ │ │ ├── HttpContextExtensions.cs │ │ │ └── StringExtensions.cs │ │ └── NoOp │ │ │ └── NoOpHealthStatusResponseWriter.cs │ └── Properties │ │ └── AssemblyInfo.cs ├── App.Metrics.AspNetCore.Health.Endpoints │ ├── App.Metrics.AspNetCore.Health.Endpoints.csproj │ ├── Builder │ │ ├── DefaultEndpointsApplicationBuilderExtensions.cs │ │ ├── HealthApplicationBuilderExtensions.cs │ │ ├── HealthAspNetEndpointWebHostBuilderExtensions.cs │ │ └── PingApplicationBuilderExtensions.cs │ ├── DefaultHealthEndpointsStartupFilter.cs │ ├── DependencyInjection │ │ └── HealthAspNetCoreHealthEndpointsServiceCollectionExtensions.cs │ ├── HealthEndpointsHostingOptions.cs │ ├── HealthEndpointsOptions.cs │ ├── Internal │ │ ├── HealthEndpointsOptionsSetup.cs │ │ └── HealthMiddlewareConstants.cs │ └── Middleware │ │ ├── HealthCheckEndpointMiddleware.cs │ │ └── PingEndpointMiddleware.cs ├── App.Metrics.AspNetCore.Health.Hosting │ ├── App.Metrics.AspNetCore.Health.Hosting.csproj │ └── HealthAspNetWebHostBuilderExtensions.cs ├── App.Metrics.AspNetCore.Health │ ├── App.Metrics.AspNetCore.Health.csproj │ ├── DefaultHealthAspNetWebHostBuilderExtensions.cs │ ├── DefaultHealthStartupFilter.cs │ ├── HealthWebHostOptions.cs │ └── Properties │ │ └── AssemblyInfo.cs └── Directory.Build.props ├── stylecop.json ├── test ├── App.Metrics.AspNetCore.Health.Integration.Facts │ ├── App.Metrics.AspNetCore.Health.Integration.Facts.csproj │ ├── DependencyInjection │ │ ├── MiddlewareHealthChecksAppMetricsBuilderExtensionsTests.cs │ │ └── TestConfiguration │ │ │ └── appsettings.json │ ├── Middleware │ │ ├── HealthCheckEndpointDegradedMiddlewareTests.cs │ │ ├── HealthCheckEndpointDisabledMiddlewareTests.cs │ │ ├── HealthCheckEndpointMiddlewareTests.cs │ │ ├── HealthCheckEndpointUnhealthyMiddlewareTests.cs │ │ ├── PingEndpointDisabledMiddlewareTests.cs │ │ └── PingEndpointMiddlewareTests.cs │ ├── Startup │ │ ├── DefaultTestStartup.cs │ │ ├── DegradedHealthTestStartup.cs │ │ ├── DisabledHealthCheckTestStartup.cs │ │ ├── HealthyHealthTestStartup.cs │ │ ├── PingDisabledTestStartup.cs │ │ ├── PingTestStartup.cs │ │ ├── TestStartup.cs │ │ └── UnhealthyHealthTestStartup.cs │ ├── StartupTestFixture{TStartup}.cs │ ├── TestHealthCheck.cs │ └── appsettings.json └── Directory.Build.props ├── tools └── packages.config ├── version.props └── xunit.runner.json /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /AppMetrics.ruleset: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 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 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /AspNetCoreHealth.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2027 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2D805782-756E-4C98-B22E-F502BEE95318}" 7 | ProjectSection(SolutionItems) = preProject 8 | src\Directory.Build.props = src\Directory.Build.props 9 | EndProjectSection 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{31A4DDB1-952E-4EED-96EF-29C669279A86}" 12 | ProjectSection(SolutionItems) = preProject 13 | .travis.yml = .travis.yml 14 | AppMetrics.ruleset = AppMetrics.ruleset 15 | appveyor.yml = appveyor.yml 16 | global.json = global.json 17 | stylecop.json = stylecop.json 18 | version.props = version.props 19 | EndProjectSection 20 | EndProject 21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{DD2E4647-7125-4CE3-9BA5-B45A26113A31}" 22 | ProjectSection(SolutionItems) = preProject 23 | test\Directory.Build.props = test\Directory.Build.props 24 | EndProjectSection 25 | EndProject 26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cake", "Cake", "{99DA9961-5DF1-4EA9-B37A-BE346449F512}" 27 | ProjectSection(SolutionItems) = preProject 28 | build.cake = build.cake 29 | build.ps1 = build.ps1 30 | build.sh = build.sh 31 | tools\packages.config = tools\packages.config 32 | EndProjectSection 33 | EndProject 34 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Git", "Git", "{3B3721C0-41F4-48D5-92AC-0AE97D94031D}" 35 | ProjectSection(SolutionItems) = preProject 36 | CONTRIBUTING.md = CONTRIBUTING.md 37 | GitReleaseManager.yaml = GitReleaseManager.yaml 38 | ISSUE_TEMPLATE.md = ISSUE_TEMPLATE.md 39 | PULL_REQUEST_TEMPLATE.md = PULL_REQUEST_TEMPLATE.md 40 | README.md = README.md 41 | EndProjectSection 42 | EndProject 43 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sandbox", "sandbox", "{C0BC96C5-46E1-4323-9C5D-58D9A96549CD}" 44 | EndProject 45 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sanbox", "Sanbox", "{F1ECA197-26EE-4A52-8026-D906C14685A2}" 46 | ProjectSection(SolutionItems) = preProject 47 | sandbox\generic_grafana_dashboard_demo.gif = sandbox\generic_grafana_dashboard_demo.gif 48 | EndProjectSection 49 | EndProject 50 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{07876CE3-350D-45E9-8BAD-89D35A3E1C58}" 51 | ProjectSection(SolutionItems) = preProject 52 | build\common.props = build\common.props 53 | build\dependencies.props = build\dependencies.props 54 | EndProjectSection 55 | EndProject 56 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App.Metrics.AspNetCore.Health", "src\App.Metrics.AspNetCore.Health\App.Metrics.AspNetCore.Health.csproj", "{AD27983B-4B95-44BB-A75B-C68520158C06}" 57 | EndProject 58 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App.Metrics.AspNetCore.Health.Integration.Facts", "test\App.Metrics.AspNetCore.Health.Integration.Facts\App.Metrics.AspNetCore.Health.Integration.Facts.csproj", "{951AA943-DB5A-4765-9923-C713DDC8FB5C}" 59 | EndProject 60 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App.Metrics.AspNetCore.Health.Core", "src\App.Metrics.AspNetCore.Health.Core\App.Metrics.AspNetCore.Health.Core.csproj", "{9EFA13CB-ED71-4921-8134-21CB2D0E2292}" 61 | EndProject 62 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthSandboxMvc", "sandbox\HealthSandboxMvc\HealthSandboxMvc.csproj", "{D3A46A93-5920-4EFC-8A93-A9499AC271CB}" 63 | EndProject 64 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App.Metrics.AspNetCore.Health.Hosting", "src\App.Metrics.AspNetCore.Health.Hosting\App.Metrics.AspNetCore.Health.Hosting.csproj", "{750C2D49-C0AB-4FDE-930C-208E477DBA44}" 65 | EndProject 66 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "App.Metrics.AspNetCore.Health.Endpoints", "src\App.Metrics.AspNetCore.Health.Endpoints\App.Metrics.AspNetCore.Health.Endpoints.csproj", "{F2EA63C0-A819-48FB-B0E5-5CBF879F6D01}" 67 | EndProject 68 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdditionalChecks", "sandbox\AdditionalChecks\AdditionalChecks.csproj", "{C0652D0D-2EAD-4E09-AF23-C679773F77A1}" 69 | EndProject 70 | Global 71 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 72 | Debug|Any CPU = Debug|Any CPU 73 | Release|Any CPU = Release|Any CPU 74 | EndGlobalSection 75 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 76 | {AD27983B-4B95-44BB-A75B-C68520158C06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 77 | {AD27983B-4B95-44BB-A75B-C68520158C06}.Debug|Any CPU.Build.0 = Debug|Any CPU 78 | {AD27983B-4B95-44BB-A75B-C68520158C06}.Release|Any CPU.ActiveCfg = Release|Any CPU 79 | {AD27983B-4B95-44BB-A75B-C68520158C06}.Release|Any CPU.Build.0 = Release|Any CPU 80 | {951AA943-DB5A-4765-9923-C713DDC8FB5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 81 | {951AA943-DB5A-4765-9923-C713DDC8FB5C}.Debug|Any CPU.Build.0 = Debug|Any CPU 82 | {951AA943-DB5A-4765-9923-C713DDC8FB5C}.Release|Any CPU.ActiveCfg = Release|Any CPU 83 | {951AA943-DB5A-4765-9923-C713DDC8FB5C}.Release|Any CPU.Build.0 = Release|Any CPU 84 | {9EFA13CB-ED71-4921-8134-21CB2D0E2292}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 85 | {9EFA13CB-ED71-4921-8134-21CB2D0E2292}.Debug|Any CPU.Build.0 = Debug|Any CPU 86 | {9EFA13CB-ED71-4921-8134-21CB2D0E2292}.Release|Any CPU.ActiveCfg = Release|Any CPU 87 | {9EFA13CB-ED71-4921-8134-21CB2D0E2292}.Release|Any CPU.Build.0 = Release|Any CPU 88 | {D3A46A93-5920-4EFC-8A93-A9499AC271CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 89 | {D3A46A93-5920-4EFC-8A93-A9499AC271CB}.Debug|Any CPU.Build.0 = Debug|Any CPU 90 | {D3A46A93-5920-4EFC-8A93-A9499AC271CB}.Release|Any CPU.ActiveCfg = Release|Any CPU 91 | {D3A46A93-5920-4EFC-8A93-A9499AC271CB}.Release|Any CPU.Build.0 = Release|Any CPU 92 | {750C2D49-C0AB-4FDE-930C-208E477DBA44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 93 | {750C2D49-C0AB-4FDE-930C-208E477DBA44}.Debug|Any CPU.Build.0 = Debug|Any CPU 94 | {750C2D49-C0AB-4FDE-930C-208E477DBA44}.Release|Any CPU.ActiveCfg = Release|Any CPU 95 | {750C2D49-C0AB-4FDE-930C-208E477DBA44}.Release|Any CPU.Build.0 = Release|Any CPU 96 | {F2EA63C0-A819-48FB-B0E5-5CBF879F6D01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 97 | {F2EA63C0-A819-48FB-B0E5-5CBF879F6D01}.Debug|Any CPU.Build.0 = Debug|Any CPU 98 | {F2EA63C0-A819-48FB-B0E5-5CBF879F6D01}.Release|Any CPU.ActiveCfg = Release|Any CPU 99 | {F2EA63C0-A819-48FB-B0E5-5CBF879F6D01}.Release|Any CPU.Build.0 = Release|Any CPU 100 | {C0652D0D-2EAD-4E09-AF23-C679773F77A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 101 | {C0652D0D-2EAD-4E09-AF23-C679773F77A1}.Debug|Any CPU.Build.0 = Debug|Any CPU 102 | {C0652D0D-2EAD-4E09-AF23-C679773F77A1}.Release|Any CPU.ActiveCfg = Release|Any CPU 103 | {C0652D0D-2EAD-4E09-AF23-C679773F77A1}.Release|Any CPU.Build.0 = Release|Any CPU 104 | EndGlobalSection 105 | GlobalSection(SolutionProperties) = preSolution 106 | HideSolutionNode = FALSE 107 | EndGlobalSection 108 | GlobalSection(NestedProjects) = preSolution 109 | {99DA9961-5DF1-4EA9-B37A-BE346449F512} = {31A4DDB1-952E-4EED-96EF-29C669279A86} 110 | {3B3721C0-41F4-48D5-92AC-0AE97D94031D} = {31A4DDB1-952E-4EED-96EF-29C669279A86} 111 | {F1ECA197-26EE-4A52-8026-D906C14685A2} = {31A4DDB1-952E-4EED-96EF-29C669279A86} 112 | {07876CE3-350D-45E9-8BAD-89D35A3E1C58} = {31A4DDB1-952E-4EED-96EF-29C669279A86} 113 | {AD27983B-4B95-44BB-A75B-C68520158C06} = {2D805782-756E-4C98-B22E-F502BEE95318} 114 | {951AA943-DB5A-4765-9923-C713DDC8FB5C} = {DD2E4647-7125-4CE3-9BA5-B45A26113A31} 115 | {9EFA13CB-ED71-4921-8134-21CB2D0E2292} = {2D805782-756E-4C98-B22E-F502BEE95318} 116 | {D3A46A93-5920-4EFC-8A93-A9499AC271CB} = {C0BC96C5-46E1-4323-9C5D-58D9A96549CD} 117 | {750C2D49-C0AB-4FDE-930C-208E477DBA44} = {2D805782-756E-4C98-B22E-F502BEE95318} 118 | {F2EA63C0-A819-48FB-B0E5-5CBF879F6D01} = {2D805782-756E-4C98-B22E-F502BEE95318} 119 | {C0652D0D-2EAD-4E09-AF23-C679773F77A1} = {C0BC96C5-46E1-4323-9C5D-58D9A96549CD} 120 | EndGlobalSection 121 | GlobalSection(ExtensibilityGlobals) = postSolution 122 | SolutionGuid = {A8699A1D-0BA1-403C-86E9-C789CD2645EB} 123 | EndGlobalSection 124 | EndGlobal 125 | -------------------------------------------------------------------------------- /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 [docfx](https://dotnet.github.io/docfx/), you can find the github repo [here](https://github.com/alhardy/AppMetrics.DocFx) and create a new pull request on the `master` branch. 34 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repo has been archived, source code and issues moved to [AppMetrics](https://github.com/AppMetrics/AppMetrics) 2 | 3 | # App Metrics AspNetCore Health App Metrics 4 | [![Official Site](https://img.shields.io/badge/site-appmetrics-blue.svg?style=flat-square)](http://app-metrics.io/getting-started/intro.html) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/app-metrics/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | 6 | ## What is App Metrics AspNetCore Health? 7 | 8 | [App Metrics Health](https://github.com/AppMetrics/Health) is an open-source and cross-platform .NET library used define health checks within an application, see the [Getting Started Guide](https://www.app-metrics.io/web-monitoring/aspnet-core/health/). This repository includes AspNetCore middleware and extensions to expose user-defined health checks over HTTP. 9 | 10 | ## Latest Builds, Packages & Repo Stats 11 | 12 | |Branch|AppVeyor|Travis|Coverage| 13 | |------|:--------:|:--------:|:--------:| 14 | |dev|[![AppVeyor](https://img.shields.io/appveyor/ci/alhardy/aspnetcorehealth/dev.svg?style=flat-square&label=appveyor%20build)](https://ci.appveyor.com/project/alhardy/aspnetcorehealth/branch/dev)|[![Travis](https://img.shields.io/travis/alhardy/health/dev.svg?style=flat-square&label=travis%20build)](https://travis-ci.org/alhardy/aspnetcorehealth)|[![Coveralls](https://img.shields.io/coveralls/AppMetrics/AspNetCoreHealth/dev.svg?style=flat-square)](https://coveralls.io/github/AppMetrics/AspNetCoreHealth?branch=dev) 15 | |master|[![AppVeyor](https://img.shields.io/appveyor/ci/alhardy/health/master.svg?style=flat-square&label=appveyor%20build)](https://ci.appveyor.com/project/alhardy/aspnetcorehealth/branch/master)| [![Travis](https://img.shields.io/travis/alhardy/health/master.svg?style=flat-square&label=travis%20build)](https://travis-ci.org/alhardy/aspnetcorehealth)| [![Coveralls](https://img.shields.io/coveralls/AppMetrics/AspNetCoreHealth/master.svg?style=flat-square)](https://coveralls.io/github/AppMetrics/AspNetCoreHealth?branch=master)| 16 | 17 | |Package|Dev Release|Pre-Release|Release| 18 | |------|:--------:|:--------:|:--------:| 19 | |App.Metrics.AspNetCore.Health|[![MyGet Status](https://img.shields.io/myget/appmetrics/v/App.Metrics.AspNetCore.Health.svg?style=flat-square)](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.AspNetCore.Health)|[![NuGet Status](https://img.shields.io/nuget/vpre/App.Metrics.AspNetCore.Health.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health/)|[![NuGet Status](https://img.shields.io/nuget/v/App.Metrics.AspNetCore.Health.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health/) 20 | |App.Metrics.AspNetCore.Health.Core|[![MyGet Status](https://img.shields.io/myget/appmetrics/v/App.Metrics.AspNetCore.Health.Core.svg?style=flat-square)](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.AspNetCore.Health.Core)|[![NuGet Status](https://img.shields.io/nuget/vpre/App.Metrics.AspNetCore.Health.Core.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health.Core/)|[![NuGet Status](https://img.shields.io/nuget/v/App.Metrics.AspNetCore.Health.Core.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health.Core/) 21 | |App.Metrics.AspNetCore.Health.Endpoints|[![MyGet Status](https://img.shields.io/myget/appmetrics/v/App.Metrics.AspNetCore.Health.Endpoints.svg?style=flat-square)](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.AspNetCore.Health.Endpoints)|[![NuGet Status](https://img.shields.io/nuget/vpre/App.Metrics.AspNetCore.Health.Endpoints.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health.Endpoints/)|[![NuGet Status](https://img.shields.io/nuget/v/App.Metrics.AspNetCore.Health.Endpoints.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health.Endpoints/) 22 | |App.Metrics.AspNetCore.Health.Hosting|[![MyGet Status](https://img.shields.io/myget/appmetrics/v/App.Metrics.AspNetCore.Health.Hosting.svg?style=flat-square)](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.AspNetCore.Health.Hosting)|[![NuGet Status](https://img.shields.io/nuget/vpre/App.Metrics.AspNetCore.Health.Hosting.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health.Hosting/)|[![NuGet Status](https://img.shields.io/nuget/v/App.Metrics.AspNetCore.Health.Hosting.svg?style=flat-square)](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health.Hosting/) 23 | 24 | ---------- 25 | 26 | ## How to build 27 | 28 | [AppVeyor](https://ci.appveyor.com/project/alhardy/health/branch/master) and [Travis CI](https://travis-ci.org/alhardy/health) builds are triggered on commits and PRs to `dev` and `master` branches. 29 | 30 | See the following for build arguments and running locally. 31 | 32 | |Configuration|Description|Default|Environment|Required| 33 | |------|--------|:--------:|:--------:|:--------:| 34 | |BuildConfiguration|The configuration to run the build, **Debug** or **Release** |*Release*|All|Optional| 35 | |PreReleaseSuffix|The pre-release suffix for versioning nuget package artifacts e.g. `beta`|*ci*|All|Optional| 36 | |CoverWith|**DotCover** or **OpenCover** to calculate and report code coverage, **None** to skip. When not **None**, a coverage file and html report will be generated at `./artifacts/coverage`|*OpenCover*|Windows Only|Optional| 37 | |SkipCodeInspect|**false** to run ReSharper code inspect and report results, **true** to skip. When **true**, the code inspection html report and xml output will be generated at `./artifacts/resharper-reports`|*false*|Windows Only|Optional| 38 | |BuildNumber|The build number to use for pre-release versions|*0*|All|Optional| 39 | |LinkSources|[Source link](https://github.com/ctaggart/SourceLink) support allows source code to be downloaded on demand while debugging|*true*|All|Optional| 40 | 41 | 42 | ### Windows 43 | 44 | Run `build.ps1` from the repositories root directory. 45 | 46 | ``` 47 | .\build.ps1 48 | ``` 49 | 50 | **With Arguments** 51 | 52 | ``` 53 | .\build.ps1 --ScriptArgs '-BuildConfiguration=Release -PreReleaseSuffix=beta -CoverWith=OpenCover -SkipCodeInspect=false -BuildNumber=1' 54 | ``` 55 | 56 | ### Linux & OSX 57 | 58 | Run `build.sh` from the repositories root directory. Code Coverage reports are now supported on Linux and OSX, it will be skipped running in these environments. 59 | 60 | ``` 61 | .\build.sh 62 | ``` 63 | 64 | **With Arguments** 65 | 66 | 67 | ``` 68 | .\build.sh --ScriptArgs '-BuildConfiguration=Release -PreReleaseSuffix=beta -BuildNumber=1' 69 | ``` 70 | 71 | ## Contributing 72 | 73 | See the [contribution guidlines](CONTRIBUTING.md) for details. 74 | 75 | ## Acknowledgements 76 | 77 | * [ASP.NET Core](https://github.com/aspnet) 78 | * [Grafana](https://grafana.com/) 79 | * [DocFX](https://dotnet.github.io/docfx/) 80 | * [Fluent Assertions](http://www.fluentassertions.com/) 81 | * [xUnit.net](https://xunit.github.io/) 82 | * [StyleCopAnalyzers](https://github.com/DotNetAnalyzers/StyleCopAnalyzers) 83 | * [Cake](https://github.com/cake-build/cake) 84 | * [OpenCover](https://github.com/OpenCover/opencover) 85 | 86 | ***Thanks for providing free open source licensing*** 87 | 88 | * [NDepend](http://www.ndepend.com/) 89 | * [Jetbrains](https://www.jetbrains.com/dotnet/) 90 | * [AppVeyor](https://www.appveyor.com/) 91 | * [Travis CI](https://travis-ci.org/) 92 | * [Coveralls](https://coveralls.io/) 93 | 94 | ## License 95 | 96 | This library is release under Apache 2.0 License ( see LICENSE ) Copyright (c) 2016 Allan Hardy 97 | 98 | See [LICENSE](https://github.com/alhardy/AppMetrics/blob/dev/LICENSE) 99 | 100 | ---------- 101 | [![Powered By NDepend](https://github.com/alhardy/AppMetrics.DocFx/blob/master/images/PoweredByNDepend.png)](http://www.ndepend.com/) 102 | 103 | ---------- 104 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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: mVnWU9Roa0obMO4kNFelCZW7aVOKcKTmw4WMEN0Ft8tLyAnBh8HsHLz/XF6Na+7I 8 | 9 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 10 | DOTNET_CLI_TELEMETRY_OPTOUT: true 11 | CoverWith: OpenCover 12 | PreReleaseSuffix: alpha 13 | GitUser: 14 | secure: zAdAOUUof3XGDsdOBRYg7J7wZS44iL4VjI/MVGw+JnU= 15 | GitOwner: 16 | secure: n6W5JZ7Q/xfZC7b2k3+ORA== 17 | GitPassword: 18 | secure: fHangGPAk14u3V1eP3kg5EdekQoKdOsKC0F/A1mvh9IODHCZ92FGjfWb0U2aQ8bu 19 | build_script: 20 | - ps: .\build.ps1 -Target AppVeyor 21 | test: off 22 | artifacts: 23 | - path: artifacts/packages/*.nupkg 24 | - path: artifacts/coverage/*.xml 25 | - path: artifacts/test-results/*.trx 26 | - path: artifacts/resharper-reports/*.xml 27 | - path: artifacts/resharper-reports/*.html 28 | - path: artifacts/coverage/coverage.dcvr 29 | deploy: 30 | - provider: NuGet 31 | server: https://www.myget.org/F/appmetrics/api/v2/package 32 | api_key: 33 | secure: gIAiACgNj+JzXyLLTe3rLxZyrAB9RpC8Lw81xEjdOLXqotprqEwGiFWRipEqkpps 34 | skip_symbols: true 35 | symbol_server: https://www.myget.org/F/appmetrics/symbol 36 | install: 37 | - cmd: curl -O https://download.microsoft.com/download/8/8/5/88544F33-836A-49A5-8B67-451C24709A8F/dotnet-sdk-2.1.300-win-x64.exe 38 | - cmd: dotnet-sdk-2.1.300-win-x64.exe /install /quiet /norestart /log install.log 39 | skip_commits: 40 | files: 41 | - '**/*.md' -------------------------------------------------------------------------------- /build.cake: -------------------------------------------------------------------------------- 1 | #addin Cake.Coveralls 2 | #addin Cake.ReSharperReports 3 | 4 | #tool "nuget:?package=xunit.runner.console" 5 | #tool "nuget:?package=JetBrains.dotCover.CommandLineTools" 6 | #tool "nuget:?package=OpenCover" 7 | #tool "nuget:?package=ReSharperReports" 8 | #tool "nuget:?package=JetBrains.ReSharper.CommandLineTools" 9 | #tool "nuget:?package=coveralls.io" 10 | #tool "nuget:?package=gitreleasemanager" 11 | #tool "nuget:?package=ReportGenerator" 12 | 13 | ////////////////////////////////////////////////////////////////////// 14 | // ARGUMENTS 15 | ////////////////////////////////////////////////////////////////////// 16 | var packageRelease = HasArgument("packageRelease") ? Argument("packageRelease") : 17 | EnvironmentVariable("packageRelease") != null ? bool.Parse(EnvironmentVariable("packageRelease")) : false; 18 | var target = Argument("target", "Default"); 19 | var configuration = HasArgument("BuildConfiguration") ? Argument("BuildConfiguration") : 20 | EnvironmentVariable("BuildConfiguration") != null ? EnvironmentVariable("BuildConfiguration") : "Release"; 21 | var coverWith = HasArgument("CoverWith") ? Argument("CoverWith") : 22 | EnvironmentVariable("CoverWith") != null ? EnvironmentVariable("CoverWith") : "DotCover"; // None, DotCover, OpenCover 23 | var skipReSharperCodeInspect = HasArgument("SkipCodeInspect") ? Argument("SkipCodeInspect", false) || !IsRunningOnWindows(): true; 24 | var preReleaseSuffix = HasArgument("PreReleaseSuffix") ? Argument("PreReleaseSuffix") : 25 | (AppVeyor.IsRunningOnAppVeyor && EnvironmentVariable("PreReleaseSuffix") == null) || (AppVeyor.IsRunningOnAppVeyor && AppVeyor.Environment.Repository.Tag.IsTag && !packageRelease) 26 | ? null : EnvironmentVariable("PreReleaseSuffix") != null ? EnvironmentVariable("PreReleaseSuffix") : "ci"; 27 | var buildNumber = HasArgument("BuildNumber") ? Argument("BuildNumber") : 28 | AppVeyor.IsRunningOnAppVeyor ? AppVeyor.Environment.Build.Number : 29 | TravisCI.IsRunningOnTravisCI ? TravisCI.Environment.Build.BuildNumber : 30 | EnvironmentVariable("BuildNumber") != null ? int.Parse(EnvironmentVariable("BuildNumber")) : 0; 31 | var gitUser = HasArgument("GitUser") ? Argument("GitUser") : EnvironmentVariable("GitUser"); 32 | var gitPassword = HasArgument("GitPassword") ? Argument("GitPassword") : EnvironmentVariable("GitPassword"); 33 | var skipHtmlCoverageReport = HasArgument("SkipHtmlCoverageReport") ? Argument("SkipHtmlCoverageReport", true) || !IsRunningOnWindows() : true; 34 | var linkSources = HasArgument("LinkSources") ? Argument("LinkSources") : 35 | EnvironmentVariable("LinkSources") != null ? bool.Parse(EnvironmentVariable("LinkSources")) : true; 36 | 37 | ////////////////////////////////////////////////////////////////////// 38 | // DEFINE FILES & DIRECTORIES 39 | ////////////////////////////////////////////////////////////////////// 40 | var packDirs = new [] { 41 | Directory("./src/App.Metrics.AspNetCore.Health"), 42 | Directory("./src/App.Metrics.AspNetCore.Health.Core"), 43 | Directory("./src/App.Metrics.AspNetCore.Health.Endpoints"), 44 | Directory("./src/App.Metrics.AspNetCore.Health.Hosting") 45 | }; 46 | var artifactsDir = (DirectoryPath) Directory("./artifacts"); 47 | var testResultsDir = (DirectoryPath) artifactsDir.Combine("test-results"); 48 | var coverageResultsDir = (DirectoryPath) artifactsDir.Combine("coverage"); 49 | var reSharperReportsDir = (DirectoryPath) artifactsDir.Combine("resharper-reports"); 50 | var testOCoverageOutputFilePath = coverageResultsDir.CombineWithFilePath("openCoverCoverage.xml"); 51 | var htmlCoverageReport = coverageResultsDir.FullPath + "/coverage.html"; 52 | var mergedCoverageSnapshots = coverageResultsDir.FullPath + "/coverage.dcvr"; 53 | var xmlCoverageReport = coverageResultsDir.FullPath + "/coverage.xml"; 54 | var packagesDir = artifactsDir.Combine("packages"); 55 | var resharperSettings = "./AspNetCoreHealth.sln.DotSettings"; 56 | var inspectCodeXml = string.Format("{0}/inspectCode.xml", reSharperReportsDir); 57 | var inspectCodeHtml = string.Format("{0}/inspectCode.html", reSharperReportsDir); 58 | var solutionFile = "./AspNetCoreHealth.sln"; 59 | var solution = ParseSolution(new FilePath(solutionFile)); 60 | 61 | ////////////////////////////////////////////////////////////////////// 62 | // DEFINE PARAMS 63 | ////////////////////////////////////////////////////////////////////// 64 | var coverallsToken = Context.EnvironmentVariable("COVERALLS_REPO_TOKEN"); 65 | var openCoverFilter = "+[App.Metrics*]* -[xunit.*]* -[*.Facts]*"; 66 | var openCoverExcludeFile = "*/*Designer.cs;*/*.g.cs;*/*.g.i.cs"; 67 | var coverIncludeFilter = "+:App.Metrics*"; 68 | var coverExcludeFilter = "-:*.Facts"; 69 | var excludeFromCoverage = "*.ExcludeFromCodeCoverage*"; 70 | string versionSuffix = null; 71 | 72 | if (!string.IsNullOrEmpty(preReleaseSuffix)) 73 | { 74 | if (packageRelease && AppVeyor.IsRunningOnAppVeyor && AppVeyor.Environment.Repository.Tag.IsTag) 75 | { 76 | versionSuffix = preReleaseSuffix; 77 | } 78 | else 79 | { 80 | versionSuffix = preReleaseSuffix + "-" + buildNumber.ToString("D4"); 81 | } 82 | } 83 | else if (AppVeyor.IsRunningOnAppVeyor && !AppVeyor.Environment.Repository.Tag.IsTag && !packageRelease) 84 | { 85 | versionSuffix = buildNumber.ToString("D4"); 86 | } 87 | 88 | 89 | ////////////////////////////////////////////////////////////////////// 90 | // TASKS 91 | ////////////////////////////////////////////////////////////////////// 92 | 93 | Task("Clean") 94 | .Does(() => 95 | { 96 | Context.Information("Cleaning files *.trx"); 97 | var rootDir = new System.IO.DirectoryInfo("./"); 98 | rootDir.GetFiles("*.trx", SearchOption.AllDirectories).ToList().ForEach(file=>file.Delete()); 99 | 100 | CleanDirectory(artifactsDir); 101 | CleanDirectory(coverageResultsDir); 102 | CleanDirectory(testResultsDir); 103 | }); 104 | 105 | Task("ReleaseNotes") 106 | .IsDependentOn("Clean") 107 | .WithCriteria(() => AppVeyor.IsRunningOnAppVeyor && AppVeyor.Environment.Repository.Tag.IsTag) 108 | .Does(() => 109 | { 110 | var preRelease = preReleaseSuffix != null; 111 | var milestone = AppVeyor.Environment.Repository.Tag.Name; 112 | var owner = AppVeyor.Environment.Repository.Name.Split('/')[0]; 113 | var repo = AppVeyor.Environment.Repository.Name.Split('/')[1]; 114 | var tag = AppVeyor.Environment.Repository.Tag.Name; 115 | 116 | Context.Information("Creating release notes for Milestone " + milestone); 117 | GitReleaseManagerCreate(gitUser, gitPassword, owner, repo, new GitReleaseManagerCreateSettings { 118 | Milestone = milestone, 119 | Prerelease = preRelease 120 | }); 121 | 122 | Context.Information("Publishing Release Notes for Tag " + tag); 123 | GitReleaseManagerPublish(gitUser, gitPassword, owner, repo, tag); 124 | 125 | Context.Information("Closing Milestone " + milestone); 126 | GitReleaseManagerClose(gitUser, gitPassword, owner, repo, milestone); 127 | }); 128 | 129 | Task("Restore") 130 | .IsDependentOn("Clean") 131 | .Does(() => 132 | { 133 | var settings = new DotNetCoreRestoreSettings 134 | { 135 | Sources = new [] { "https://api.nuget.org/v3/index.json", "https://www.myget.org/F/appmetrics/api/v3/index.json" } 136 | }; 137 | 138 | DotNetCoreRestore(solutionFile, settings); 139 | }); 140 | 141 | Task("Build") 142 | .IsDependentOn("Restore") 143 | .Does(() => 144 | { 145 | var settings = new DotNetCoreBuildSettings { Configuration = configuration, VersionSuffix = versionSuffix }; 146 | 147 | Context.Information("Building using preReleaseSuffix: " + preReleaseSuffix); 148 | Context.Information("Building using versionSuffix: " + versionSuffix); 149 | 150 | // Workaround to fixing pre-release version package references - https://github.com/NuGet/Home/issues/4337 151 | settings.ArgumentCustomization = args => { 152 | args.Append("/t:Restore /p:RestoreSources=https://api.nuget.org/v3/index.json;https://www.myget.org/F/appmetrics/api/v3/index.json;"); 153 | if (linkSources) { 154 | args.Append("/p:SourceLinkCreate=true"); 155 | } 156 | return args; 157 | }; 158 | 159 | 160 | if (IsRunningOnWindows()) 161 | { 162 | DotNetCoreBuild(solutionFile, settings); 163 | } 164 | else 165 | { 166 | // var projects = solution.GetProjects(); 167 | // 168 | // foreach(var project in projects) 169 | // { 170 | // var parsedProject = ParseProject(new FilePath(project.Path.ToString()), configuration); 171 | // 172 | // if (parsedProject.IsLibrary() && !project.Path.ToString().Contains(".Sandbox")&& !project.Path.ToString().Contains(".Facts") && !project.Path.ToString().Contains(".Benchmarks")) 173 | // { 174 | // settings.Framework = "netstandard2.0"; 175 | // 176 | // } 177 | // else 178 | // { 179 | // settings.Framework = "netcoreapp2.0"; 180 | // } 181 | // 182 | // Context.Information("Building as " + settings.Framework + ": " + project.Path.ToString()); 183 | // 184 | // DotNetCoreBuild(project.Path.ToString(), settings); 185 | // } 186 | 187 | } 188 | }); 189 | 190 | Task("Pack") 191 | .IsDependentOn("Restore") 192 | .IsDependentOn("Clean") 193 | .Does(() => 194 | { 195 | if (!IsRunningOnWindows()) 196 | { 197 | Context.Warning("Currently no way out-of-the-box to conditionally build & pack a project by framework, because app.metrics projects target both .NET 452 & dotnet standard skipping packages for now on non-windows environments"); 198 | return; 199 | } 200 | 201 | Context.Information("Packing using preReleaseSuffix: " + preReleaseSuffix); 202 | Context.Information("Packing using versionSuffix: " + versionSuffix); 203 | 204 | var settings = new DotNetCorePackSettings 205 | { 206 | Configuration = configuration, 207 | OutputDirectory = packagesDir, 208 | VersionSuffix = versionSuffix, 209 | NoBuild = true, 210 | // Workaround to fixing pre-release version package references - https://github.com/NuGet/Home/issues/4337 211 | ArgumentCustomization = args=>args.Append("/p:RestoreSources=https://api.nuget.org/v3/index.json;https://www.myget.org/F/appmetrics/api/v3/index.json;") 212 | }; 213 | 214 | foreach(var packDir in packDirs) 215 | { 216 | DotNetCorePack(packDir, settings); 217 | } 218 | }); 219 | 220 | Task("RunInspectCode") 221 | .WithCriteria(() => !skipReSharperCodeInspect) 222 | .Does(() => 223 | { 224 | InspectCode(solutionFile, new InspectCodeSettings { SolutionWideAnalysis = true, Profile = resharperSettings, OutputFile = inspectCodeXml }); 225 | ReSharperReports(inspectCodeXml, inspectCodeHtml); 226 | }); 227 | 228 | Task("RunTests") 229 | .WithCriteria(() => coverWith == "None" || !IsRunningOnWindows()) 230 | .IsDependentOn("Build") 231 | .Does(() => 232 | { 233 | var projects = GetFiles("./test/**/*.csproj"); 234 | 235 | CreateDirectory(coverageResultsDir); 236 | 237 | Context.Information("Found " + projects.Count() + " projects"); 238 | 239 | foreach (var project in projects) 240 | { 241 | var folderName = new System.IO.DirectoryInfo(System.IO.Path.GetDirectoryName(project.ToString())).Name; 242 | var settings = new DotNetCoreTestSettings 243 | { 244 | Configuration = configuration, 245 | // Workaround to fixing pre-release version package references - https://github.com/NuGet/Home/issues/4337 246 | ArgumentCustomization = args=>args.Append("--logger:trx /t:Restore /p:RestoreSources=https://api.nuget.org/v3/index.json;https://www.myget.org/F/appmetrics/api/v3/index.json;") 247 | }; 248 | 249 | if (!IsRunningOnWindows()) 250 | { 251 | settings.Framework = "netcoreapp2.0"; 252 | } 253 | 254 | DotNetCoreTest(project.FullPath, settings); 255 | } 256 | }); 257 | 258 | Task("HtmlCoverageReport") 259 | .WithCriteria(() => IsRunningOnWindows() && FileExists(testOCoverageOutputFilePath) && coverWith != "None" && !skipHtmlCoverageReport) 260 | .IsDependentOn("RunTests") 261 | .Does(() => 262 | { 263 | if (coverWith == "DotCover") 264 | { 265 | DotCoverReport( 266 | mergedCoverageSnapshots, 267 | htmlCoverageReport, 268 | new DotCoverReportSettings { 269 | ReportType = DotCoverReportType.HTML 270 | }); 271 | } 272 | else if (coverWith == "OpenCover") 273 | { 274 | ReportGenerator(testOCoverageOutputFilePath, coverageResultsDir); 275 | } 276 | }); 277 | 278 | Task("RunTestsWithOpenCover") 279 | .WithCriteria(() => coverWith == "OpenCover" && IsRunningOnWindows()) 280 | .IsDependentOn("Build") 281 | .Does(() => 282 | { 283 | var projects = GetFiles("./test/**/*.csproj"); 284 | 285 | CreateDirectory(coverageResultsDir); 286 | 287 | Context.Information("Found " + projects.Count() + " projects"); 288 | 289 | var settings = new DotNetCoreTestSettings 290 | { 291 | Configuration = configuration, 292 | // Workaround to fixing pre-release version package references - https://github.com/NuGet/Home/issues/4337 293 | ArgumentCustomization = args=>args.Append("--logger:trx /t:Restore /p:RestoreSources=https://api.nuget.org/v3/index.json;https://www.myget.org/F/appmetrics/api/v3/index.json;") 294 | }; 295 | 296 | foreach (var project in projects) 297 | { 298 | var folderName = new System.IO.DirectoryInfo(System.IO.Path.GetDirectoryName(project.ToString())).Name; 299 | 300 | Action testAction = tool => { tool.DotNetCoreTest(project.ToString(), settings); }; 301 | 302 | var openCoverSettings = new OpenCoverSettings { 303 | ReturnTargetCodeOffset = 1, 304 | SkipAutoProps = true, 305 | Register = "user", 306 | OldStyle = true, 307 | MergeOutput = true, 308 | ArgumentCustomization = args => args.Append(@"-safemode:off -hideskipped:All") 309 | }; 310 | 311 | openCoverSettings.WithFilter(openCoverFilter); 312 | openCoverSettings.ExcludeByAttribute(excludeFromCoverage); 313 | openCoverSettings.ExcludeByFile(openCoverExcludeFile); 314 | 315 | OpenCover(testAction, testOCoverageOutputFilePath, openCoverSettings); 316 | } 317 | }); 318 | 319 | Task("PublishTestResults") 320 | .IsDependentOn("RunTestsWithDotCover") 321 | .IsDependentOn("RunTestsWithOpenCover") 322 | .IsDependentOn("RunTests") 323 | .Does(() => 324 | { 325 | if (IsRunningOnWindows()) 326 | { 327 | CreateDirectory(testResultsDir); 328 | 329 | var projects = GetFiles("./test/**/*.csproj"); 330 | 331 | foreach (var project in projects) 332 | { 333 | var folderName = new System.IO.DirectoryInfo(System.IO.Path.GetDirectoryName(project.ToString())).Name; 334 | 335 | IEnumerable filePaths = GetFiles(System.IO.Path.GetDirectoryName(project.ToString()) + "/TestResults" + "/*.trx"); 336 | 337 | Context.Information("Found " + filePaths.Count() + " .trx files"); 338 | 339 | foreach(var filePath in filePaths) 340 | { 341 | Context.Information("Moving " + filePath.FullPath + " to " + testResultsDir); 342 | 343 | try 344 | { 345 | MoveFiles(filePath.FullPath, testResultsDir); 346 | MoveFile(testResultsDir + "/" + filePath.GetFilename(), testResultsDir + "/" + folderName + ".trx"); 347 | } 348 | catch(Exception ex) 349 | { 350 | Context.Information(ex.ToString()); 351 | } 352 | } 353 | } 354 | } 355 | }); 356 | 357 | Task("RunTestsWithDotCover") 358 | .WithCriteria(() => coverWith == "DotCover" && IsRunningOnWindows()) 359 | .IsDependentOn("Build") 360 | .Does(() => 361 | { 362 | var projects = GetFiles("./test/**/*.csproj"); 363 | 364 | CreateDirectory(coverageResultsDir); 365 | 366 | Context.Information("Found " + projects.Count() + " projects"); 367 | 368 | var settings = new DotNetCoreTestSettings 369 | { 370 | Configuration = configuration, 371 | // Workaround to fixing pre-release version package references - https://github.com/NuGet/Home/issues/4337 372 | ArgumentCustomization = args=>args.Append("--logger:trx /t:Restore /p:RestoreSources=https://api.nuget.org/v3/index.json;https://www.myget.org/F/appmetrics/api/v3/index.json;") 373 | }; 374 | 375 | foreach (var project in projects) 376 | { 377 | var folderName = new System.IO.DirectoryInfo(System.IO.Path.GetDirectoryName(project.ToString())).Name; 378 | 379 | Action testAction = tool => { tool.DotNetCoreTest(project.ToString(), settings); }; 380 | 381 | var dotCoverSettings = new DotCoverCoverSettings 382 | { 383 | ArgumentCustomization = args => args.Append(@"/HideAutoProperties") 384 | .Append(@"/AttributeFilters=System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute") 385 | .Append(@"/Filters=+:module=App.Metrics*;-:module=*.Facts*;") 386 | .Append(@"/ReturnTargetExitCode") 387 | }; 388 | 389 | dotCoverSettings.WithFilter(coverIncludeFilter).WithFilter(coverExcludeFilter); 390 | 391 | var coverageFile = coverageResultsDir.FullPath + folderName + ".dcvr"; 392 | 393 | DotCoverCover(testAction, new FilePath(coverageFile), dotCoverSettings); 394 | 395 | MoveFiles(coverageFile, coverageResultsDir); 396 | } 397 | 398 | var snapshots = GetFiles(coverageResultsDir.FullPath + "/*.dcvr"); 399 | 400 | if (snapshots != null && snapshots.Any()) 401 | { 402 | DotCoverMerge(snapshots, 403 | new FilePath(mergedCoverageSnapshots), null); 404 | 405 | DotCoverReport( 406 | mergedCoverageSnapshots, 407 | xmlCoverageReport, 408 | new DotCoverReportSettings { ReportType = DotCoverReportType.XML}); 409 | } 410 | }); 411 | 412 | 413 | Task("PublishCoverage") 414 | .WithCriteria(() => FileExists(testOCoverageOutputFilePath)) 415 | .WithCriteria(() => !BuildSystem.IsLocalBuild) 416 | .WithCriteria(() => coverWith == "OpenCover") 417 | .WithCriteria(() => !string.IsNullOrEmpty(coverallsToken)) 418 | .IsDependentOn("RunTests") 419 | .Does(() => 420 | { 421 | CoverallsIo(testOCoverageOutputFilePath, new CoverallsIoSettings() 422 | { 423 | RepoToken = coverallsToken 424 | }); 425 | }); 426 | 427 | ////////////////////////////////////////////////////////////////////// 428 | // TASK TARGETS 429 | ////////////////////////////////////////////////////////////////////// 430 | 431 | Task("Default") 432 | .IsDependentOn("Build") 433 | .IsDependentOn("PublishTestResults") 434 | .IsDependentOn("Pack") 435 | .IsDependentOn("HtmlCoverageReport") 436 | .IsDependentOn("RunInspectCode"); 437 | 438 | Task("AppVeyor") 439 | .IsDependentOn("Build") 440 | .IsDependentOn("PublishTestResults") 441 | .IsDependentOn("Pack") 442 | .IsDependentOn("HtmlCoverageReport") 443 | .IsDependentOn("RunInspectCode") 444 | .IsDependentOn("PublishCoverage") 445 | .IsDependentOn("ReleaseNotes"); 446 | 447 | ////////////////////////////////////////////////////////////////////// 448 | // EXECUTION 449 | ////////////////////////////////////////////////////////////////////// 450 | 451 | RunTarget(target); -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # This is the Cake bootstrapper script for PowerShell. 3 | # This file was downloaded from https://github.com/cake-build/resources 4 | # Feel free to change this file to fit your needs. 5 | ########################################################################## 6 | 7 | <# 8 | 9 | .SYNOPSIS 10 | This is a Powershell script to bootstrap a Cake build. 11 | 12 | .DESCRIPTION 13 | This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) 14 | and execute your Cake build script with the parameters you provide. 15 | 16 | .PARAMETER Script 17 | The build script to execute. 18 | .PARAMETER Target 19 | The build script target to run. 20 | .PARAMETER Configuration 21 | The build configuration to use. 22 | .PARAMETER Verbosity 23 | Specifies the amount of information to be displayed. 24 | .PARAMETER Experimental 25 | Tells Cake to use the latest Roslyn release. 26 | .PARAMETER WhatIf 27 | Performs a dry run of the build script. 28 | No tasks will be executed. 29 | .PARAMETER Mono 30 | Tells Cake to use the Mono scripting engine. 31 | .PARAMETER SkipToolPackageRestore 32 | Skips restoring of packages. 33 | .PARAMETER ScriptArgs 34 | Remaining arguments are added here. 35 | 36 | .LINK 37 | http://cakebuild.net 38 | 39 | #> 40 | 41 | [CmdletBinding()] 42 | Param( 43 | [string]$Script = "build.cake", 44 | [string]$Target = "Default", 45 | [ValidateSet("Release", "Debug")] 46 | [string]$Configuration = "Release", 47 | [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] 48 | [string]$Verbosity = "Verbose", 49 | [switch]$Experimental, 50 | [Alias("DryRun","Noop")] 51 | [switch]$WhatIf, 52 | [switch]$Mono, 53 | [switch]$SkipToolPackageRestore, 54 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] 55 | [string[]]$ScriptArgs 56 | ) 57 | 58 | [Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null 59 | function MD5HashFile([string] $filePath) 60 | { 61 | if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf)) 62 | { 63 | return $null 64 | } 65 | 66 | [System.IO.Stream] $file = $null; 67 | [System.Security.Cryptography.MD5] $md5 = $null; 68 | try 69 | { 70 | $md5 = [System.Security.Cryptography.MD5]::Create() 71 | $file = [System.IO.File]::OpenRead($filePath) 72 | return [System.BitConverter]::ToString($md5.ComputeHash($file)) 73 | } 74 | finally 75 | { 76 | if ($file -ne $null) 77 | { 78 | $file.Dispose() 79 | } 80 | } 81 | } 82 | 83 | Write-Host "Preparing to run build script..." 84 | 85 | if(!$PSScriptRoot){ 86 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 87 | } 88 | 89 | $TOOLS_DIR = Join-Path $PSScriptRoot "tools" 90 | $NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" 91 | $CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" 92 | $NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" 93 | $PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" 94 | $PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" 95 | 96 | # Should we use mono? 97 | $UseMono = ""; 98 | if($Mono.IsPresent) { 99 | Write-Verbose -Message "Using the Mono based scripting engine." 100 | $UseMono = "-mono" 101 | } 102 | 103 | # Should we use the new Roslyn? 104 | $UseExperimental = ""; 105 | if($Experimental.IsPresent -and !($Mono.IsPresent)) { 106 | Write-Verbose -Message "Using experimental version of Roslyn." 107 | $UseExperimental = "-experimental" 108 | } 109 | 110 | # Is this a dry run? 111 | $UseDryRun = ""; 112 | if($WhatIf.IsPresent) { 113 | $UseDryRun = "-dryrun" 114 | } 115 | 116 | # Make sure tools folder exists 117 | if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { 118 | Write-Verbose -Message "Creating tools directory..." 119 | New-Item -Path $TOOLS_DIR -Type directory | out-null 120 | } 121 | 122 | # Make sure that packages.config exist. 123 | if (!(Test-Path $PACKAGES_CONFIG)) { 124 | Write-Verbose -Message "Downloading packages.config..." 125 | try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch { 126 | Throw "Could not download packages.config." 127 | } 128 | } 129 | 130 | # Try find NuGet.exe in path if not exists 131 | if (!(Test-Path $NUGET_EXE)) { 132 | Write-Verbose -Message "Trying to find nuget.exe in PATH..." 133 | $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) } 134 | $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 135 | if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { 136 | Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." 137 | $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName 138 | } 139 | } 140 | 141 | # Try download NuGet.exe if not exists 142 | if (!(Test-Path $NUGET_EXE)) { 143 | Write-Verbose -Message "Downloading NuGet.exe..." 144 | try { 145 | (New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE) 146 | } catch { 147 | Throw "Could not download NuGet.exe." 148 | } 149 | } 150 | 151 | # Save nuget.exe path to environment to be available to child processed 152 | $ENV:NUGET_EXE = $NUGET_EXE 153 | 154 | # Restore tools from NuGet? 155 | if(-Not $SkipToolPackageRestore.IsPresent) { 156 | Push-Location 157 | Set-Location $TOOLS_DIR 158 | 159 | # Check for changes in packages.config and remove installed tools if true. 160 | [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG) 161 | if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or 162 | ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { 163 | Write-Verbose -Message "Missing or changed package.config hash..." 164 | Remove-Item * -Recurse -Exclude packages.config,nuget.exe 165 | } 166 | 167 | Write-Verbose -Message "Restoring tools from NuGet..." 168 | $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" 169 | 170 | if ($LASTEXITCODE -ne 0) { 171 | Throw "An error occured while restoring NuGet tools." 172 | } 173 | else 174 | { 175 | $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" 176 | } 177 | Write-Verbose -Message ($NuGetOutput | out-string) 178 | Pop-Location 179 | } 180 | 181 | # Make sure that Cake has been installed. 182 | if (!(Test-Path $CAKE_EXE)) { 183 | Throw "Could not find Cake.exe at $CAKE_EXE" 184 | } 185 | 186 | # Start Cake 187 | Write-Host "Running build script..." 188 | Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs" 189 | exit $LASTEXITCODE -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ########################################################################## 4 | # This is the Cake bootstrapper script for Linux and OS X. 5 | # This file was downloaded from https://github.com/cake-build/resources 6 | # Feel free to change this file to fit your needs. 7 | ########################################################################## 8 | 9 | # Define directories. 10 | SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 11 | TOOLS_DIR=$SCRIPT_DIR/tools 12 | ADDINS_DIR=$TOOLS_DIR/Addins 13 | MODULES_DIR=$TOOLS_DIR/Modules 14 | NUGET_EXE=$TOOLS_DIR/nuget.exe 15 | CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe 16 | PACKAGES_CONFIG=$TOOLS_DIR/packages.config 17 | PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum 18 | ADDINS_PACKAGES_CONFIG=$ADDINS_DIR/packages.config 19 | MODULES_PACKAGES_CONFIG=$MODULES_DIR/packages.config 20 | 21 | # Define md5sum or md5 depending on Linux/OSX 22 | MD5_EXE= 23 | if [[ "$(uname -s)" == "Darwin" ]]; then 24 | MD5_EXE="md5 -r" 25 | else 26 | MD5_EXE="md5sum" 27 | fi 28 | 29 | # Define default arguments. 30 | SCRIPT="build.cake" 31 | TARGET="Default" 32 | CONFIGURATION="Release" 33 | VERBOSITY="verbose" 34 | DRYRUN= 35 | SHOW_VERSION=false 36 | SCRIPT_ARGUMENTS=() 37 | 38 | # Parse arguments. 39 | for i in "$@"; do 40 | case $1 in 41 | -s|--script) SCRIPT="$2"; shift ;; 42 | -t|--target) TARGET="$2"; shift ;; 43 | -c|--configuration) CONFIGURATION="$2"; shift ;; 44 | -v|--verbosity) VERBOSITY="$2"; shift ;; 45 | -d|--dryrun) DRYRUN="-dryrun" ;; 46 | --version) SHOW_VERSION=true ;; 47 | --) shift; SCRIPT_ARGUMENTS+=("$@"); break ;; 48 | *) SCRIPT_ARGUMENTS+=("$1") ;; 49 | esac 50 | shift 51 | done 52 | 53 | # Make sure the tools folder exist. 54 | if [ ! -d "$TOOLS_DIR" ]; then 55 | mkdir "$TOOLS_DIR" 56 | fi 57 | 58 | # Make sure that packages.config exist. 59 | if [ ! -f "$TOOLS_DIR/packages.config" ]; then 60 | echo "Downloading packages.config..." 61 | curl -Lsfo "$TOOLS_DIR/packages.config" http://cakebuild.net/download/bootstrapper/packages 62 | if [ $? -ne 0 ]; then 63 | echo "An error occured while downloading packages.config." 64 | exit 1 65 | fi 66 | fi 67 | 68 | # Download NuGet if it does not exist. 69 | if [ ! -f "$NUGET_EXE" ]; then 70 | echo "Downloading NuGet..." 71 | curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe 72 | if [ $? -ne 0 ]; then 73 | echo "An error occured while downloading nuget.exe." 74 | exit 1 75 | fi 76 | fi 77 | 78 | # Restore tools from NuGet. 79 | pushd "$TOOLS_DIR" >/dev/null 80 | if [ ! -f "$PACKAGES_CONFIG_MD5" ] || [ "$( cat "$PACKAGES_CONFIG_MD5" | sed 's/\r$//' )" != "$( $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' )" ]; then 81 | find . -type d ! -name . | xargs rm -rf 82 | fi 83 | 84 | mono "$NUGET_EXE" install -ExcludeVersion 85 | if [ $? -ne 0 ]; then 86 | echo "Could not restore NuGet tools." 87 | exit 1 88 | fi 89 | 90 | $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' >| "$PACKAGES_CONFIG_MD5" 91 | 92 | popd >/dev/null 93 | 94 | # Restore addins from NuGet. 95 | if [ -f "$ADDINS_PACKAGES_CONFIG" ]; then 96 | pushd "$ADDINS_DIR" >/dev/null 97 | 98 | mono "$NUGET_EXE" install -ExcludeVersion 99 | if [ $? -ne 0 ]; then 100 | echo "Could not restore NuGet addins." 101 | exit 1 102 | fi 103 | 104 | popd >/dev/null 105 | fi 106 | 107 | # Restore modules from NuGet. 108 | if [ -f "$MODULES_PACKAGES_CONFIG" ]; then 109 | pushd "$MODULES_DIR" >/dev/null 110 | 111 | mono "$NUGET_EXE" install -ExcludeVersion 112 | if [ $? -ne 0 ]; then 113 | echo "Could not restore NuGet modules." 114 | exit 1 115 | fi 116 | 117 | popd >/dev/null 118 | fi 119 | 120 | # Make sure that Cake has been installed. 121 | if [ ! -f "$CAKE_EXE" ]; then 122 | echo "Could not find Cake.exe at '$CAKE_EXE'." 123 | exit 1 124 | fi 125 | 126 | # Start Cake 127 | if $SHOW_VERSION; then 128 | exec mono "$CAKE_EXE" -version 129 | else 130 | exec mono "$CAKE_EXE" $SCRIPT -verbosity=$VERBOSITY -configuration=$CONFIGURATION -target=$TARGET $DRYRUN "${SCRIPT_ARGUMENTS[@]}" 131 | fi -------------------------------------------------------------------------------- /build/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AppMetrics/AspNetCoreHealth/951575caf01ab02a2c349be80a6ef1e1b23cb467/build/Key.snk -------------------------------------------------------------------------------- /build/common.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | App Metrics 7 | Allan Hardy 2016 8 | Allan Hardy 9 | $(NoWarn);1701;1702;1705;CS1591;CS1570 10 | true 11 | true 12 | https://app-metrics.io/images/logo.png 13 | https://app-metrics.io/ 14 | https://github.com/AppMetrics/AppMetrics/blob/master/LICENSE 15 | git 16 | git://github.com/AppMetrics/AspNetCoreHealth 17 | false 18 | false 19 | false 20 | false 21 | Latest 22 | ..\..\AppMetrics.ruleset 23 | $(MSBuildThisFileDirectory)Key.snk 24 | true 25 | true 26 | embedded 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | All 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /build/dependencies.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2.1.0-* 4 | 3.0.0-* 5 | 2.1.6 6 | 2.1.3 7 | 2.1.1 8 | 4.5.0 9 | 2.1.0 10 | 4.8.3 11 | 15.9.0 12 | 2.4.1 13 | 5.5.3 14 | 5.0.0 15 | 1.0.0 16 | 17 | 18 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "projects": [ "src", "test", "sandbox" ], 3 | "sdk": { 4 | "version": "2.1.300" 5 | } 6 | } -------------------------------------------------------------------------------- /sandbox/AdditionalChecks/AdditionalCheck.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. 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 AdditionalChecks 11 | { 12 | public class AdditionalCheck : HealthCheck 13 | { 14 | public AdditionalCheck() 15 | : base("Additional 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 | } -------------------------------------------------------------------------------- /sandbox/AdditionalChecks/AdditionalChecks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | netstandard2.0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sandbox/HealthSandboxMvc/Controllers/TestController.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace HealthSandboxMvc.Controllers 8 | { 9 | [Route("api/[controller]")] 10 | public class TestController : Controller 11 | { 12 | [HttpGet] 13 | public IActionResult Get() 14 | { 15 | return Ok(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /sandbox/HealthSandboxMvc/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "Sample app", Scope = "member", Target = "~M:MetricsSandboxMvc.Host.BuildWebHost(System.String[])~Microsoft.AspNetCore.Hosting.IWebHost")] 6 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1124:Do not use regions", Justification = "Sample app", Scope = "member", Target = "~M:HealthSandboxMvc.Host.BuildWebHost(System.String[])~Microsoft.AspNetCore.Hosting.IWebHost")] 7 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1123:Do not place regions within elements", Justification = "sample app", Scope = "member", Target = "~M:HealthSandboxMvc.Host.BuildWebHost(System.String[])~Microsoft.AspNetCore.Hosting.IWebHost")] 8 | 9 | -------------------------------------------------------------------------------- /sandbox/HealthSandboxMvc/HealthChecks/SlowHealthCheck.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. 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 HealthSandboxMvc.HealthChecks 11 | { 12 | public class SlowHealthCheck : HealthCheck 13 | { 14 | public SlowHealthCheck() 15 | : base("Slow Health Check") 16 | { 17 | } 18 | 19 | /// 20 | protected override async ValueTask CheckAsync(CancellationToken cancellationToken = default) 21 | { 22 | await Task.Delay(TimeSpan.FromSeconds(6), cancellationToken); 23 | 24 | if (DateTime.UtcNow.Second <= 20) 25 | { 26 | return HealthCheckResult.Degraded(); 27 | } 28 | 29 | if (DateTime.UtcNow.Second >= 40) 30 | { 31 | return HealthCheckResult.Unhealthy(); 32 | } 33 | 34 | return HealthCheckResult.Healthy(); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /sandbox/HealthSandboxMvc/HealthSandboxMvc.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 | Always 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /sandbox/HealthSandboxMvc/HealthStartupFilter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | 9 | namespace HealthSandboxMvc 10 | { 11 | public class HealthStartupFilter : IStartupFilter 12 | { 13 | public Action Configure(Action next) 14 | { 15 | return AddHealth; 16 | 17 | void AddHealth(IApplicationBuilder builder) 18 | { 19 | builder.UsePingEndpoint(); 20 | builder.UseHealthEndpoint(); 21 | 22 | next(builder); 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /sandbox/HealthSandboxMvc/Host.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using App.Metrics.AspNetCore.Health; 7 | using App.Metrics.Health; 8 | using App.Metrics.Health.Formatters.Ascii; 9 | using App.Metrics.Health.Reporting.Slack; 10 | using Microsoft.AspNetCore; 11 | using Microsoft.AspNetCore.Hosting; 12 | using Microsoft.Extensions.Configuration; 13 | using Serilog; 14 | using Serilog.Events; 15 | 16 | namespace HealthSandboxMvc 17 | { 18 | public static class Host 19 | { 20 | public static IWebHost BuildWebHost(string[] args) 21 | { 22 | ConfigureLogging(); 23 | 24 | return WebHost.CreateDefaultBuilder(args) 25 | 26 | #region App Metrics Health configuration options 27 | 28 | // To configure ASP.NET core App Metrics hosting options: custom ports and endpoints 29 | // .ConfigureAppHealthHostingConfiguration( 30 | // options => 31 | // { 32 | // options.HealthEndpointPort = 2222; 33 | // options.HealthEndpoint = "healthchecks"; 34 | // }) 35 | 36 | // To configure App Metrics Health core where an IHealthBuilder is provided with defaults that can be overriden 37 | // .ConfigureHealthWithDefaults( 38 | // builder => 39 | // { 40 | // builder.Configuration.Configure( 41 | // options => 42 | // { 43 | // options.Enabled = true; 44 | // }); 45 | // }) 46 | 47 | // To configure App Metrics core where an IMetricsBuilder is provided without any defaults set 48 | // .ConfigureHealth( 49 | // builder => 50 | // { 51 | // builder.Configuration.Configure( 52 | // options => 53 | // { 54 | // options.Enabled = true; 55 | // }); 56 | // }) 57 | 58 | // To configure App Metrics Health endpoints explicity 59 | // .UseHealthEndpoints() 60 | 61 | // To configure all App Metrics Health Asp.Net Core extensions overriding defaults 62 | // .UseHealth(Configure()) 63 | 64 | // To confgiure all App Metrics Asp.Net core extensions using a custom startup filter providing more control over what middleware tracking is added to the request pipeline for example 65 | // .UseHealth() 66 | 67 | #endregion 68 | 69 | .ConfigureHealthWithDefaults( 70 | (context, builder) => 71 | { 72 | var slackOptions = new SlackHealthAlertOptions(); 73 | context.Configuration.GetSection(nameof(SlackHealthAlertOptions)).Bind(slackOptions); 74 | 75 | builder.HealthChecks.AddProcessPrivateMemorySizeCheck("Private Memory Size", 200); 76 | builder.HealthChecks.AddProcessVirtualMemorySizeCheck("Virtual Memory Size", 200); 77 | builder.HealthChecks.AddProcessPhysicalMemoryCheck("Working Set", 200); 78 | builder.HealthChecks.AddPingCheck("google ping", "google.com", TimeSpan.FromSeconds(10)); 79 | builder.HealthChecks.AddHttpGetCheck("github", new Uri("https://github.com/"), TimeSpan.FromSeconds(10)); 80 | builder.Report.ToSlack(slackOptions); // TODO: Edit webhook url in appsettings 81 | }) 82 | .UseHealth() 83 | .UseStartup() 84 | .Build(); 85 | } 86 | 87 | public static void Main(string[] args) { BuildWebHost(args).Run(); } 88 | 89 | // ReSharper disable UnusedMember.Local - .UseHealth(Configure()) 90 | private static Action Configure() 91 | // ReSharper restore UnusedMember.Local 92 | { 93 | return options => 94 | { 95 | options.EndpointOptions = endpointsOptions => 96 | { 97 | endpointsOptions.HealthEndpointOutputFormatter = new HealthStatusTextOutputFormatter(); 98 | }; 99 | }; 100 | } 101 | 102 | private static void ConfigureLogging() 103 | { 104 | Log.Logger = new LoggerConfiguration() 105 | .MinimumLevel.Verbose() 106 | .MinimumLevel.Override("Microsoft", LogEventLevel.Verbose) 107 | .WriteTo.LiterateConsole() 108 | .WriteTo.Seq("http://localhost:5341") 109 | .CreateLogger(); 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /sandbox/HealthSandboxMvc/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "HealthSandboxMvc": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "launchUrl": "health", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | }, 10 | "applicationUrl": "http://localhost:1111" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sandbox/HealthSandboxMvc/Startup.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | 9 | namespace HealthSandboxMvc 10 | { 11 | public class Startup 12 | { 13 | public Startup(IConfiguration configuration) { Configuration = configuration; } 14 | 15 | public IConfiguration Configuration { get; } 16 | 17 | public void Configure(IApplicationBuilder app) { app.UseMvc(); } 18 | 19 | public void ConfigureServices(IServiceCollection services) 20 | { 21 | services.AddMvc(); 22 | // To configure App Metrics Health Host options in Startup 23 | // services.TryAddEnumerable(ServiceDescriptor.Singleton, ConfigureHealthHostingOptions>()); 24 | } 25 | 26 | // To configure App Metrics Health Host options using IConfigureOptions in Startup 27 | // public class ConfigureHealthHostingOptions : IConfigureOptions 28 | // { 29 | // public void Configure(HealthEndpointsHostingOptions options) { options.HealthEndpoint = "/ucstom_health_check_url"; } 30 | // } 31 | } 32 | } -------------------------------------------------------------------------------- /sandbox/HealthSandboxMvc/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "HealthOptions": { 3 | "Enabled": true 4 | }, 5 | "HealthEndpointsOptions": { 6 | "PingEndpointEnabled": true, 7 | "HealthEndpointEnabled": true, 8 | "Timeout": "0:0:10" 9 | }, 10 | "Logging": { 11 | "IncludeScopes": false, 12 | "LogLevel": { 13 | "Default": "Warning" 14 | } 15 | }, 16 | "SlackHealthAlertOptions": { 17 | "Channel": "#general", 18 | "WebhookUrl": "https://hooks.slack.com/services/todo" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Core/App.Metrics.AspNetCore.Health.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | App Metrics Health ASP.NET Core core components. 5 | netstandard2.0 6 | true 7 | appmetrics;aspnetcore;middleware;healthchecks 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Core/DefaultHealthResponseWriter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using App.Metrics.Health; 11 | using App.Metrics.Health.Formatters; 12 | using Microsoft.AspNetCore.Http; 13 | using Microsoft.AspNetCore.Http.Headers; 14 | using Microsoft.Net.Http.Headers; 15 | 16 | namespace App.Metrics.AspNetCore.Health.Core 17 | { 18 | public class DefaultHealthResponseWriter : IHealthResponseWriter 19 | { 20 | private readonly IHealthOutputFormatter _fallbackFormatter; 21 | private readonly IHealthOutputFormatter _formatter; 22 | private readonly HealthFormatterCollection _formatters; 23 | 24 | public DefaultHealthResponseWriter( 25 | IHealthOutputFormatter fallbackFormatter, 26 | IReadOnlyCollection formatters) 27 | { 28 | if (formatters == null) 29 | { 30 | throw new ArgumentNullException(nameof(formatters)); 31 | } 32 | 33 | _formatters = new HealthFormatterCollection(formatters.ToList()); 34 | _fallbackFormatter = fallbackFormatter; 35 | } 36 | 37 | // ReSharper disable UnusedMember.Global 38 | public DefaultHealthResponseWriter(IHealthOutputFormatter formatter) 39 | // ReSharper restore UnusedMember.Global 40 | { 41 | _formatter = formatter ?? throw new ArgumentNullException(nameof(formatter)); 42 | } 43 | 44 | /// 45 | public Task WriteAsync(HttpContext context, HealthStatus healthStatus, CancellationToken token = default) 46 | { 47 | var formatter = _formatter ?? context.Request.GetTypedHeaders().ResolveFormatter( 48 | _fallbackFormatter, 49 | metricsMediaTypeValue => _formatters.GetType(metricsMediaTypeValue)); 50 | 51 | context.SetNoCacheHeaders(); 52 | 53 | if (formatter == default(IHealthOutputFormatter)) 54 | { 55 | context.Response.StatusCode = StatusCodes.Status406NotAcceptable; 56 | context.Response.Headers[HeaderNames.ContentType] = new[] { context.Request.ContentType }; 57 | return Task.CompletedTask; 58 | } 59 | 60 | SetResponseStatusCode(context, healthStatus); 61 | 62 | context.Response.Headers[HeaderNames.ContentType] = new[] { formatter.MediaType.ContentType }; 63 | 64 | return formatter.WriteAsync(context.Response.Body, healthStatus, token); 65 | } 66 | 67 | private static void SetResponseStatusCode(HttpContext context, HealthStatus healthStatus) 68 | { 69 | var responseStatusCode = StatusCodes.Status200OK; 70 | 71 | if (healthStatus.Status.IsUnhealthy()) 72 | { 73 | responseStatusCode = StatusCodes.Status503ServiceUnavailable; 74 | } 75 | 76 | if (healthStatus.Status.IsDegraded()) 77 | { 78 | responseStatusCode = StatusCodes.Status200OK; 79 | context.Response.Headers[HeaderNames.Warning] = new[] { "Warning: 100 'Degraded'" }; 80 | } 81 | 82 | context.Response.StatusCode = responseStatusCode; 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Core/Extensions/MediaTypeHeaderValueExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using App.Metrics.Health.Formatters; 7 | 8 | // ReSharper disable CheckNamespace 9 | namespace Microsoft.Net.Http.Headers 10 | // ReSharper restore CheckNamespace 11 | { 12 | public static class MediaTypeHeaderValueExtensions 13 | { 14 | public static HealthMediaTypeValue ToHealthMediaType(this MediaTypeHeaderValue mediaTypeHeaderValue) 15 | { 16 | var versionAndFormatTokens = mediaTypeHeaderValue.SubType.Value.Split('-'); 17 | 18 | if (mediaTypeHeaderValue.Type.Value.IsMissing() 19 | || mediaTypeHeaderValue.SubType.Value.IsMissing() 20 | || versionAndFormatTokens.Length != 2) 21 | { 22 | return default; 23 | } 24 | 25 | var versionAndFormat = versionAndFormatTokens[1].Split('+'); 26 | 27 | if (versionAndFormat.Length != 2) 28 | { 29 | return default; 30 | } 31 | 32 | return new HealthMediaTypeValue( 33 | mediaTypeHeaderValue.Type.Value, 34 | versionAndFormatTokens[0], 35 | versionAndFormat[0], 36 | versionAndFormat[1]); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Core/Extensions/RequestHeaderExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using App.Metrics.Health.Formatters; 7 | using Microsoft.Net.Http.Headers; 8 | 9 | // ReSharper disable CheckNamespace 10 | namespace Microsoft.AspNetCore.Http.Headers 11 | // ReSharper restore CheckNamespace 12 | { 13 | public static class RequestHeaderExtensions 14 | { 15 | public static TFormatter ResolveFormatter( 16 | this RequestHeaders headers, 17 | TFormatter defaultFormatter, 18 | Func resolveOutputFormatter) 19 | { 20 | if (headers.Accept == null) 21 | { 22 | return defaultFormatter; 23 | } 24 | 25 | var formatter = defaultFormatter; 26 | 27 | foreach (var accept in headers.Accept) 28 | { 29 | var metricsMediaTypeValue = accept.ToHealthMediaType(); 30 | 31 | if (metricsMediaTypeValue != default) 32 | { 33 | formatter = resolveOutputFormatter(metricsMediaTypeValue); 34 | } 35 | 36 | if (formatter != null) 37 | { 38 | return formatter; 39 | } 40 | } 41 | 42 | return defaultFormatter; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Core/IHealthResponseWriter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using App.Metrics.Health; 8 | using Microsoft.AspNetCore.Http; 9 | 10 | // ReSharper disable CheckNamespace 11 | namespace App.Metrics.AspNetCore.Health 12 | // ReSharper restore CheckNamespace 13 | { 14 | public interface IHealthResponseWriter 15 | { 16 | Task WriteAsync(HttpContext context, HealthStatus healthStatus, CancellationToken token = default); 17 | } 18 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Core/Internal/Extensions/AppMetricsMiddlewareHealthChecksLoggerExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | // ReSharper disable CheckNamespace 9 | namespace Microsoft.Extensions.Logging 10 | // ReSharper restore CheckNamespace 11 | { 12 | [ExcludeFromCodeCoverage] 13 | internal static class AppMetricsMiddlewareHealthChecksLoggerExtensions 14 | { 15 | public static void MiddlewareExecuted(this ILogger logger) 16 | { 17 | if (logger.IsEnabled(LogLevel.Trace)) 18 | { 19 | logger.LogTrace(AppMetricsEventIds.Middleware.MiddlewareExecutedId, $"Executed App Metrics Health Middleware {typeof(TMiddleware).FullName}"); 20 | } 21 | } 22 | 23 | public static void MiddlewareFailed(this ILogger logger, Exception e, string message) 24 | { 25 | logger.LogError(AppMetricsEventIds.Middleware.MiddlewareErrorId, e, $"[{typeof(TMiddleware).FullName}] {message}"); 26 | } 27 | 28 | public static void MiddlewareExecuting(this ILogger logger) 29 | { 30 | if (logger.IsEnabled(LogLevel.Trace)) 31 | { 32 | logger.LogTrace(AppMetricsEventIds.Middleware.MiddlewareExecutingId, $"Executing App Metrics Health Middleware {typeof(TMiddleware).FullName}"); 33 | } 34 | } 35 | 36 | private static class AppMetricsEventIds 37 | { 38 | public static class Middleware 39 | { 40 | public const int MiddlewareExecutedId = 1; 41 | public const int MiddlewareExecutingId = 2; 42 | public const int MiddlewareErrorId = 3; 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Core/Internal/Extensions/HttpContextExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | // ReSharper disable CheckNamespace 6 | namespace Microsoft.AspNetCore.Http 7 | // ReSharper restore CheckNamespace 8 | { 9 | internal static class HttpContextExtensions 10 | { 11 | public static void SetNoCacheHeaders(this HttpContext context) 12 | { 13 | context.Response.Headers["Cache-Control"] = new[] { "no-cache, no-store, must-revalidate" }; 14 | context.Response.Headers["Pragma"] = new[] { "no-cache" }; 15 | context.Response.Headers["Expires"] = new[] { "0" }; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Core/Internal/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Diagnostics; 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | // ReSharper disable CheckNamespace 9 | namespace System 10 | // ReSharper restore CheckNamespace 11 | { 12 | [ExcludeFromCodeCoverage] 13 | internal static class StringExtensions 14 | { 15 | [DebuggerStepThrough] 16 | internal static bool IsMissing(this string value) { return string.IsNullOrWhiteSpace(value); } 17 | 18 | [DebuggerStepThrough] 19 | internal static bool IsPresent(this string value) { return !string.IsNullOrWhiteSpace(value); } 20 | 21 | [DebuggerStepThrough] 22 | internal static string EnsureLeadingSlash(this string url) 23 | { 24 | if (!url.StartsWith("/")) 25 | { 26 | return "/" + url; 27 | } 28 | 29 | return url; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Core/Internal/NoOp/NoOpHealthStatusResponseWriter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using App.Metrics.Health; 9 | using Microsoft.AspNetCore.Http; 10 | 11 | namespace App.Metrics.AspNetCore.Health.Core.Internal.NoOp 12 | { 13 | [ExcludeFromCodeCoverage] 14 | public class NoOpHealthStatusResponseWriter : IHealthResponseWriter 15 | { 16 | public string ContentType => "text/plain"; 17 | 18 | /// 19 | public Task WriteAsync(HttpContext context, HealthStatus healthStatus, CancellationToken token = default) 20 | { 21 | return context.Response.WriteAsync( 22 | "No formatter has been registered. See App.Metrics.Formatters.Ascii & App.Metrics.Formatters.Json for example.", 23 | token); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Core/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Runtime.CompilerServices; 6 | 7 | [assembly: InternalsVisibleTo("App.Metrics.AspNetCore.Health, PublicKey=00240000048000009400000006020000002400005253413100040000010001000961061aa9f970163db728a9792c1cfb7be51f2f986054c676345f9e43f95af5f3114b1962d10888a3ea1dff99bf56bce565f887cb4b004fc44ccb7335700260012a65b9cdd090e6b60c8c67c434ca49563c82c66695f8dc0776770bfaf481ef816767b7dd67d083960a6fcfe33c3c0e1cc198fe7a13b3283133d21b3435ebbb")] 8 | [assembly: InternalsVisibleTo("App.Metrics.AspNetCore.Health.Endpoints, PublicKey=00240000048000009400000006020000002400005253413100040000010001000961061aa9f970163db728a9792c1cfb7be51f2f986054c676345f9e43f95af5f3114b1962d10888a3ea1dff99bf56bce565f887cb4b004fc44ccb7335700260012a65b9cdd090e6b60c8c67c434ca49563c82c66695f8dc0776770bfaf481ef816767b7dd67d083960a6fcfe33c3c0e1cc198fe7a13b3283133d21b3435ebbb")] 9 | [assembly: InternalsVisibleTo("App.Metrics.AspNetCore.Health.Integration.Facts, PublicKey=00240000048000009400000006020000002400005253413100040000010001000961061aa9f970163db728a9792c1cfb7be51f2f986054c676345f9e43f95af5f3114b1962d10888a3ea1dff99bf56bce565f887cb4b004fc44ccb7335700260012a65b9cdd090e6b60c8c67c434ca49563c82c66695f8dc0776770bfaf481ef816767b7dd67d083960a6fcfe33c3c0e1cc198fe7a13b3283133d21b3435ebbb")] -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Endpoints/App.Metrics.AspNetCore.Health.Endpoints.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | App Metrics Health ASP.NET Core Endpoints provides the ability to expose health checks as web endpoints. 5 | netstandard2.0 6 | true 7 | appmetrics;aspnetcore;healthchecks 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Endpoints/Builder/DefaultEndpointsApplicationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | // ReSharper disable CheckNamespace 6 | namespace Microsoft.AspNetCore.Builder 7 | // ReSharper restore CheckNamespace 8 | { 9 | /// 10 | /// Extension methods for to add App Metrics Hosting to the request execution pipeline. 11 | /// 12 | public static class DefaultEndpointsApplicationBuilderExtensions 13 | { 14 | /// 15 | /// Adds all available App Metrics health endpoint middleware to the request execution pipeline. 16 | /// 17 | /// The . 18 | /// A reference to this instance after the operation has completed. 19 | public static IApplicationBuilder UseHealthAllEndpoints(this IApplicationBuilder app) 20 | { 21 | app.UseHealthEndpoint(); 22 | app.UsePingEndpoint(); 23 | 24 | return app; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Endpoints/Builder/HealthApplicationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using App.Metrics.AspNetCore.Health.Endpoints; 7 | using App.Metrics.AspNetCore.Health.Endpoints.Middleware; 8 | using App.Metrics.Health; 9 | using App.Metrics.Health.Extensions.DependencyInjection.Internal; 10 | using App.Metrics.Health.Formatters; 11 | using Microsoft.AspNetCore.Http; 12 | using Microsoft.AspNetCore.Http.Features; 13 | using Microsoft.Extensions.DependencyInjection; 14 | using Microsoft.Extensions.Options; 15 | 16 | // ReSharper disable CheckNamespace 17 | namespace Microsoft.AspNetCore.Builder 18 | // ReSharper restore CheckNamespace 19 | { 20 | /// 21 | /// Extension methods for to add App Metrics to the request execution pipeline. 22 | /// 23 | public static class HealthApplicationBuilderExtensions 24 | { 25 | /// 26 | /// Adds App Metrics health endpoint middleware to the request execution pipeline. 27 | /// 28 | /// 29 | /// Uses the matching given a requests Accept header, otherwise falls back 30 | /// to HealthOptions.DefaultMetricsFormatter. 31 | /// 32 | /// The . 33 | /// A reference to this instance after the operation has completed. 34 | public static IApplicationBuilder UseHealthEndpoint(this IApplicationBuilder app) 35 | { 36 | EnsureHealthAdded(app); 37 | 38 | var healthOptions = app.ApplicationServices.GetRequiredService(); 39 | var endpointHostingOptionsAccessor = app.ApplicationServices.GetRequiredService>(); 40 | var endpointsOptionsAccessor = app.ApplicationServices.GetRequiredService>(); 41 | 42 | UseHealthMiddleware(app, endpointHostingOptionsAccessor, endpointsOptionsAccessor, healthOptions); 43 | 44 | return app; 45 | } 46 | 47 | /// 48 | /// Adds App Metrics health endpoint middleware to the request execution pipeline. 49 | /// 50 | /// The . 51 | /// 52 | /// Overrides all configured , matching on accept headers 53 | /// won't apply. 54 | /// 55 | /// A reference to this instance after the operation has completed. 56 | public static IApplicationBuilder UseHealthEndpoint(this IApplicationBuilder app, IHealthOutputFormatter formatter) 57 | { 58 | EnsureHealthAdded(app); 59 | 60 | var healthOptions = app.ApplicationServices.GetRequiredService(); 61 | var endpointHostingOptionsAccessor = app.ApplicationServices.GetRequiredService>(); 62 | var endpointsOptionsAccessor = app.ApplicationServices.GetRequiredService>(); 63 | 64 | UseHealthMiddleware(app, endpointHostingOptionsAccessor, endpointsOptionsAccessor, healthOptions, formatter); 65 | 66 | return app; 67 | } 68 | 69 | private static void EnsureHealthAdded(IApplicationBuilder app) 70 | { 71 | if (app == null) 72 | { 73 | throw new ArgumentNullException(nameof(app)); 74 | } 75 | 76 | // Verify if AddHealth was done before calling using middleware. 77 | // We use the HealthMarkerService to make sure if all the services were added. 78 | AppMetricsHealthServicesHelper.ThrowIfMetricsNotRegistered(app.ApplicationServices); 79 | } 80 | 81 | private static bool ShouldUseHealthEndpoint( 82 | IOptions endpointsHostingOptionsAccessor, 83 | IOptions endpointsOptionsAccessor, 84 | HealthOptions metricsOptions, 85 | HttpContext context) 86 | { 87 | int? port = null; 88 | 89 | if (endpointsHostingOptionsAccessor.Value.HealthEndpointPort.HasValue) 90 | { 91 | port = endpointsHostingOptionsAccessor.Value.HealthEndpointPort.Value; 92 | } 93 | 94 | return context.Request.Path == endpointsHostingOptionsAccessor.Value.HealthEndpoint && 95 | endpointsOptionsAccessor.Value.HealthEndpointEnabled && 96 | metricsOptions.Enabled && 97 | endpointsHostingOptionsAccessor.Value.HealthEndpoint.IsPresent() && 98 | (!port.HasValue || context.Features.Get()?.LocalPort == port.Value); 99 | } 100 | 101 | private static void UseHealthMiddleware( 102 | IApplicationBuilder app, 103 | IOptions endpointsHostingOptionsAccessor, 104 | IOptions endpointsOptionsAccessor, 105 | HealthOptions metricsOptions, 106 | IHealthOutputFormatter formatter = null) 107 | { 108 | formatter = formatter ?? endpointsOptionsAccessor.Value.HealthEndpointOutputFormatter; 109 | 110 | app.UseWhen( 111 | context => ShouldUseHealthEndpoint(endpointsHostingOptionsAccessor, endpointsOptionsAccessor, metricsOptions, context), 112 | appBuilder => 113 | { 114 | var responseWriter = HealthAspNetCoreHealthEndpointsServiceCollectionExtensions.ResolveHealthResponseWriter(app.ApplicationServices, formatter); 115 | appBuilder.UseMiddleware(responseWriter, endpointsOptionsAccessor.Value.Timeout); 116 | }); 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Endpoints/Builder/HealthAspNetEndpointWebHostBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using App.Metrics.AspNetCore.Health.Endpoints; 9 | using Microsoft.Extensions.Configuration; 10 | using Microsoft.Extensions.DependencyInjection; 11 | 12 | // ReSharper disable CheckNamespace 13 | namespace Microsoft.AspNetCore.Hosting 14 | // ReSharper restore CheckNamespace 15 | { 16 | public static class HealthAspNetEndpointWebHostBuilderExtensions 17 | { 18 | /// 19 | /// Adds App Metrics Helath services, configuration and middleware to the 20 | /// . 21 | /// 22 | /// The . 23 | /// A reference to this instance after the operation has completed. 24 | /// 25 | /// cannot be null 26 | /// 27 | public static IWebHostBuilder UseHealthEndpoints(this IWebHostBuilder hostBuilder) 28 | { 29 | hostBuilder.ConfigureHealth(); 30 | 31 | hostBuilder.ConfigureServices( 32 | (context, services) => 33 | { 34 | services.AddHealthEndpoints(context.Configuration); 35 | services.AddSingleton(new DefaultHealthEndpointsStartupFilter()); 36 | }); 37 | 38 | return hostBuilder; 39 | } 40 | 41 | /// 42 | /// Adds App Metrics Health services, configuration and middleware to the 43 | /// . 44 | /// 45 | /// The . 46 | /// A callback to configure . 47 | /// A reference to this instance after the operation has completed. 48 | /// 49 | /// cannot be null 50 | /// 51 | public static IWebHostBuilder UseHealthEndpoints( 52 | this IWebHostBuilder hostBuilder, 53 | Action optionsDelegate) 54 | { 55 | hostBuilder.ConfigureHealth(); 56 | 57 | hostBuilder.ConfigureServices( 58 | (context, services) => 59 | { 60 | services.AddHealthEndpoints(optionsDelegate, context.Configuration); 61 | services.AddSingleton(new DefaultHealthEndpointsStartupFilter()); 62 | }); 63 | 64 | return hostBuilder; 65 | } 66 | 67 | /// 68 | /// Adds App Metrics Health services, configuration and middleware to the 69 | /// . 70 | /// 71 | /// The . 72 | /// A callback to configure . 73 | /// A reference to this instance after the operation has completed. 74 | /// 75 | /// cannot be null 76 | /// 77 | public static IWebHostBuilder UseHealthEndpoints( 78 | this IWebHostBuilder hostBuilder, 79 | Action setupDelegate) 80 | { 81 | hostBuilder.ConfigureHealth(); 82 | 83 | hostBuilder.ConfigureServices( 84 | (context, services) => 85 | { 86 | var endpointOptions = new HealthEndpointsOptions(); 87 | services.AddHealthEndpoints( 88 | options => setupDelegate(context, endpointOptions), 89 | context.Configuration); 90 | services.AddSingleton(new DefaultHealthEndpointsStartupFilter()); 91 | }); 92 | 93 | return hostBuilder; 94 | } 95 | 96 | /// 97 | /// Adds App Metrics Health services, configuration and middleware to the 98 | /// . 99 | /// 100 | /// The . 101 | /// The containing 102 | /// A callback to configure . 103 | /// A reference to this instance after the operation has completed. 104 | /// 105 | /// cannot be null 106 | /// 107 | public static IWebHostBuilder UseHealthEndpoints( 108 | this IWebHostBuilder hostBuilder, 109 | IConfiguration configuration, 110 | Action optionsDelegate) 111 | { 112 | hostBuilder.ConfigureHealth(); 113 | 114 | hostBuilder.ConfigureServices( 115 | services => 116 | { 117 | services.AddHealthEndpoints(optionsDelegate, configuration); 118 | services.AddSingleton(new DefaultHealthEndpointsStartupFilter()); 119 | }); 120 | 121 | return hostBuilder; 122 | } 123 | 124 | public static IWebHostBuilder ConfigureAppHealthHostingConfiguration( 125 | this IWebHostBuilder hostBuilder, 126 | Action setupHostingConfiguration) 127 | { 128 | var healthEndpointHostingOptions = new HealthEndpointsHostingOptions(); 129 | setupHostingConfiguration(healthEndpointHostingOptions); 130 | 131 | var ports = new List(); 132 | 133 | if (healthEndpointHostingOptions.AllEndpointsPort.HasValue) 134 | { 135 | Console.WriteLine( 136 | $"Hosting {healthEndpointHostingOptions.HealthEndpoint} endpoint on port {healthEndpointHostingOptions.AllEndpointsPort.Value}"); 137 | Console.WriteLine( 138 | $"Hosting {healthEndpointHostingOptions.PingEndpoint} on port {healthEndpointHostingOptions.AllEndpointsPort.Value}"); 139 | 140 | ports.Add(healthEndpointHostingOptions.AllEndpointsPort.Value); 141 | } 142 | else 143 | { 144 | if (healthEndpointHostingOptions.HealthEndpointPort.HasValue) 145 | { 146 | Console.WriteLine($"Hosting {healthEndpointHostingOptions.HealthEndpoint} on port {healthEndpointHostingOptions.HealthEndpointPort.Value}"); 147 | ports.Add(healthEndpointHostingOptions.HealthEndpointPort.Value); 148 | } 149 | 150 | if (healthEndpointHostingOptions.PingEndpointPort.HasValue) 151 | { 152 | Console.WriteLine($"Hosting {healthEndpointHostingOptions.PingEndpoint} on port {healthEndpointHostingOptions.PingEndpointPort.Value}"); 153 | ports.Add(healthEndpointHostingOptions.PingEndpointPort.Value); 154 | } 155 | } 156 | 157 | if (ports.Any()) 158 | { 159 | var existingUrl = hostBuilder.GetSetting(WebHostDefaults.ServerUrlsKey); 160 | var additionalUrls = string.Join(";", ports.Distinct().Select(p => $"http://*:{p}/")); 161 | hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, $"{existingUrl};{additionalUrls}"); 162 | } 163 | 164 | hostBuilder.ConfigureServices(services => services.Configure(setupHostingConfiguration)); 165 | 166 | return hostBuilder; 167 | } 168 | } 169 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Endpoints/Builder/PingApplicationBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using App.Metrics.AspNetCore.Health.Endpoints; 7 | using App.Metrics.AspNetCore.Health.Endpoints.Middleware; 8 | using Microsoft.AspNetCore.Http; 9 | using Microsoft.AspNetCore.Http.Features; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Extensions.Options; 12 | 13 | // ReSharper disable CheckNamespace 14 | namespace Microsoft.AspNetCore.Builder 15 | // ReSharper restore CheckNamespace 16 | { 17 | /// 18 | /// Extension methods for to add App Metrics Health ping pong to the request execution 19 | /// pipeline. 20 | /// 21 | public static class PingApplicationBuilderExtensions 22 | { 23 | /// 24 | /// Adds App Metrics Health Ping middleware to the request execution pipeline. 25 | /// 26 | /// The . 27 | /// A reference to this instance after the operation has completed. 28 | public static IApplicationBuilder UsePingEndpoint(this IApplicationBuilder app) 29 | { 30 | if (app == null) 31 | { 32 | throw new ArgumentNullException(nameof(app)); 33 | } 34 | 35 | var metricsEndpointsHostingOptionsAccessor = app.ApplicationServices.GetRequiredService>(); 36 | var endpointsOptionsAccessor = app.ApplicationServices.GetRequiredService>(); 37 | 38 | UsePingMiddleware(app, metricsEndpointsHostingOptionsAccessor, endpointsOptionsAccessor); 39 | 40 | return app; 41 | } 42 | 43 | private static bool ShouldUsePing( 44 | IOptions endpointsHostingOptionsAccessor, 45 | IOptions endpointsOptionsAccessor, 46 | HttpContext context) 47 | { 48 | int? port = null; 49 | 50 | if (endpointsHostingOptionsAccessor.Value.AllEndpointsPort.HasValue) 51 | { 52 | port = endpointsHostingOptionsAccessor.Value.AllEndpointsPort.Value; 53 | } 54 | else if (endpointsHostingOptionsAccessor.Value.PingEndpointPort.HasValue) 55 | { 56 | port = endpointsHostingOptionsAccessor.Value.PingEndpointPort.Value; 57 | } 58 | 59 | return context.Request.Path == endpointsHostingOptionsAccessor.Value.PingEndpoint && 60 | endpointsOptionsAccessor.Value.PingEndpointEnabled && 61 | endpointsHostingOptionsAccessor.Value.PingEndpoint.IsPresent() && 62 | (!port.HasValue || context.Features.Get()?.LocalPort == port.Value); 63 | } 64 | 65 | private static void UsePingMiddleware( 66 | IApplicationBuilder app, 67 | IOptions metricsEndpointsHostingOptionsAccessor, 68 | IOptions endpointsOptionsAccessor) 69 | { 70 | app.UseWhen( 71 | context => ShouldUsePing(metricsEndpointsHostingOptionsAccessor, endpointsOptionsAccessor, context), 72 | appBuilder => { appBuilder.UseMiddleware(); }); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Endpoints/DefaultHealthEndpointsStartupFilter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | 9 | namespace App.Metrics.AspNetCore.Health.Endpoints 10 | { 11 | /// 12 | /// Inserts App Metrics Health Endpoints at the beginning of the pipeline. 13 | /// 14 | public class DefaultHealthEndpointsStartupFilter : IStartupFilter 15 | { 16 | /// 17 | public Action Configure(Action next) 18 | { 19 | return AddHealthEndpoints; 20 | 21 | void AddHealthEndpoints(IApplicationBuilder builder) 22 | { 23 | builder.UseHealthEndpoint(); 24 | builder.UsePingEndpoint(); 25 | 26 | next(builder); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Endpoints/DependencyInjection/HealthAspNetCoreHealthEndpointsServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Linq; 7 | using App.Metrics.AspNetCore.Health; 8 | using App.Metrics.AspNetCore.Health.Core; 9 | using App.Metrics.AspNetCore.Health.Core.Internal.NoOp; 10 | using App.Metrics.AspNetCore.Health.Endpoints; 11 | using App.Metrics.AspNetCore.Health.Endpoints.Internal; 12 | using App.Metrics.Health; 13 | using App.Metrics.Health.Formatters; 14 | using Microsoft.Extensions.Configuration; 15 | using Microsoft.Extensions.DependencyInjection.Extensions; 16 | using Microsoft.Extensions.Options; 17 | 18 | // ReSharper disable CheckNamespace 19 | namespace Microsoft.Extensions.DependencyInjection 20 | // ReSharper restore CheckNamespace 21 | { 22 | public static class HealthAspNetCoreHealthEndpointsServiceCollectionExtensions 23 | { 24 | private static readonly string DefaultConfigSection = nameof(HealthEndpointsOptions); 25 | 26 | /// 27 | /// Adds essential App Metrics Health AspNet Core metrics services to the specified . 28 | /// 29 | /// The to add services to. 30 | /// 31 | /// An that can be used to further configure services. 32 | /// 33 | public static IServiceCollection AddHealthEndpoints(this IServiceCollection services) 34 | { 35 | AddHealthEndpointsServices(services); 36 | 37 | return services; 38 | } 39 | 40 | /// 41 | /// Adds essential App Metrics Health AspNet Core metrics services to the specified . 42 | /// 43 | /// The to add services to. 44 | /// The from where to load . 45 | /// 46 | /// An that can be used to further configure services. 47 | /// 48 | public static IServiceCollection AddHealthEndpoints( 49 | this IServiceCollection services, 50 | IConfiguration configuration) 51 | { 52 | services.AddHealthEndpoints(configuration.GetSection(DefaultConfigSection)); 53 | 54 | return services; 55 | } 56 | 57 | /// 58 | /// Adds essential App Metrics Health AspNet Core metrics services to the specified . 59 | /// 60 | /// The to add services to. 61 | /// The from where to load . 62 | /// 63 | /// An that can be used to further configure services. 64 | /// 65 | public static IServiceCollection AddHealthEndpoints( 66 | this IServiceCollection services, 67 | IConfigurationSection configuration) 68 | { 69 | services.AddHealthEndpoints(); 70 | 71 | services.Configure(configuration); 72 | 73 | return services; 74 | } 75 | 76 | /// 77 | /// Adds essential App Metrics Health AspNet Core health services to the specified . 78 | /// 79 | /// The to add services to. 80 | /// The from where to load . 81 | /// 82 | /// An to configure the provided . 83 | /// 84 | /// 85 | /// An that can be used to further configure services. 86 | /// 87 | public static IServiceCollection AddHealthEndpoints( 88 | this IServiceCollection services, 89 | IConfiguration configuration, 90 | Action setupAction) 91 | { 92 | services.AddHealthEndpoints(configuration.GetSection(DefaultConfigSection), setupAction); 93 | 94 | return services; 95 | } 96 | 97 | /// 98 | /// Adds essential App Metrics Health AspNet Core metrics services to the specified . 99 | /// 100 | /// The to add services to. 101 | /// The from where to load . 102 | /// 103 | /// An to configure the provided . 104 | /// 105 | /// 106 | /// An that can be used to further configure services. 107 | /// 108 | public static IServiceCollection AddHealthEndpoints( 109 | this IServiceCollection services, 110 | IConfigurationSection configuration, 111 | Action setupAction) 112 | { 113 | services.AddHealthEndpoints(); 114 | 115 | services.Configure(configuration); 116 | services.Configure(setupAction); 117 | 118 | return services; 119 | } 120 | 121 | /// 122 | /// Adds essential App Metrics Health AspNet Core metrics services to the specified . 123 | /// 124 | /// The to add services to. 125 | /// 126 | /// An to configure the provided . 127 | /// 128 | /// The from where to load . 129 | /// 130 | /// An that can be used to further configure services. 131 | /// 132 | public static IServiceCollection AddHealthEndpoints( 133 | this IServiceCollection services, 134 | Action setupAction, 135 | IConfiguration configuration) 136 | { 137 | services.AddHealthEndpoints(setupAction, configuration.GetSection(DefaultConfigSection)); 138 | 139 | return services; 140 | } 141 | 142 | /// 143 | /// Adds essential App Metrics Health AspNet Core metrics services to the specified . 144 | /// 145 | /// The to add services to. 146 | /// 147 | /// An to configure the provided . 148 | /// 149 | /// The from where to load . 150 | /// 151 | /// An that can be used to further configure services. 152 | /// 153 | public static IServiceCollection AddHealthEndpoints( 154 | this IServiceCollection services, 155 | Action setupAction, 156 | IConfigurationSection configuration) 157 | { 158 | services.AddHealthEndpoints(); 159 | 160 | services.Configure(setupAction); 161 | services.Configure(configuration); 162 | 163 | return services; 164 | } 165 | 166 | /// 167 | /// Adds essential App Metrics Health AspNet Core metrics services to the specified . 168 | /// 169 | /// The to add services to. 170 | /// 171 | /// An to configure the provided . 172 | /// 173 | /// 174 | /// An that can be used to further configure services. 175 | /// 176 | public static IServiceCollection AddHealthEndpoints( 177 | this IServiceCollection services, 178 | Action setupAction) 179 | { 180 | services.AddHealthEndpoints(); 181 | 182 | services.Configure(setupAction); 183 | 184 | return services; 185 | } 186 | 187 | internal static void AddHealthEndpointsServices(IServiceCollection services) 188 | { 189 | var endpointOptionsDescriptor = ServiceDescriptor.Singleton, HealthEndpointsOptionsSetup>(); 190 | services.TryAddEnumerable(endpointOptionsDescriptor); 191 | 192 | services.TryAddSingleton(provider => ResolveHealthResponseWriter(provider)); 193 | } 194 | 195 | internal static IHealthResponseWriter ResolveHealthResponseWriter(IServiceProvider provider, IHealthOutputFormatter formatter = null) 196 | { 197 | var endpointOptions = provider.GetRequiredService>(); 198 | var health = provider.GetRequiredService(); 199 | 200 | if (health.Options.Enabled && endpointOptions.Value.HealthEndpointEnabled && health.OutputHealthFormatters.Any()) 201 | { 202 | if (formatter == null) 203 | { 204 | var fallbackFormatter = endpointOptions.Value.HealthEndpointOutputFormatter ?? health.DefaultOutputHealthFormatter; 205 | 206 | return new DefaultHealthResponseWriter(fallbackFormatter, health.OutputHealthFormatters); 207 | } 208 | 209 | return new DefaultHealthResponseWriter(formatter, health.OutputHealthFormatters); 210 | } 211 | 212 | return new NoOpHealthStatusResponseWriter(); 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Endpoints/HealthEndpointsHostingOptions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using App.Metrics.AspNetCore.Health.Endpoints.Internal; 7 | 8 | namespace App.Metrics.AspNetCore.Health.Endpoints 9 | { 10 | /// 11 | /// Provides programmatic configuration for metrics endpoints hosting in the App Metrics framework. 12 | /// 13 | public class HealthEndpointsHostingOptions 14 | { 15 | /// 16 | /// Gets or sets the port to host available endpoints provided by App Metrics. 17 | /// 18 | /// 19 | /// This overrides all endpoing specific port configuration allowing a the port to be specific on a single 20 | /// setting. 21 | /// 22 | /// 23 | /// The App Metrics available endpoint's port. 24 | /// 25 | public int? AllEndpointsPort { get; set; } 26 | 27 | /// 28 | /// Gets or sets the health endpoint, defaults to /health. 29 | /// 30 | /// 31 | /// The health endpoint. 32 | /// 33 | public string HealthEndpoint { get; set; } = HealthMiddlewareConstants.DefaultRoutePaths.HealthEndpoint.EnsureLeadingSlash(); 34 | 35 | /// 36 | /// Gets or sets the port to host the health endpoint. 37 | /// 38 | public int? HealthEndpointPort { get; set; } 39 | 40 | /// 41 | /// Gets or sets the ping endpoint, defaults to /ping. 42 | /// 43 | /// 44 | /// The ping endpoint. 45 | /// 46 | public string PingEndpoint { get; set; } = HealthMiddlewareConstants.DefaultRoutePaths.PingEndpoint.EnsureLeadingSlash(); 47 | 48 | /// 49 | /// Gets or sets the port to host the ping endpoint. 50 | /// 51 | /// 52 | /// The pint endpoint's port. 53 | /// 54 | public int? PingEndpointPort { get; set; } 55 | } 56 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Endpoints/HealthEndpointsOptions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using App.Metrics.Health.Formatters; 7 | using Microsoft.AspNetCore.Builder; 8 | 9 | namespace App.Metrics.AspNetCore.Health.Endpoints 10 | { 11 | public class HealthEndpointsOptions 12 | { 13 | public HealthEndpointsOptions() 14 | { 15 | HealthEndpointEnabled = true; 16 | PingEndpointEnabled = true; 17 | } 18 | 19 | /// 20 | /// Gets or sets a value indicating whether [health endpoint should be enabled], if disabled endpoint responds with 21 | /// 404. 22 | /// 23 | /// 24 | /// true if [health endpoint is enabled]; otherwise, false. 25 | /// 26 | public bool HealthEndpointEnabled { get; set; } 27 | 28 | /// 29 | /// Gets or sets the used to write the health status when the health endpoint is 30 | /// requested. 31 | /// 32 | /// 33 | /// The used to write metrics. 34 | /// 35 | public IHealthOutputFormatter HealthEndpointOutputFormatter { get; set; } 36 | 37 | /// 38 | /// Gets or sets a value indicating whether [ping endpoint should be enabled], if disabled endpoint responds with 404. 39 | /// 40 | /// Only valid if UsePingEndpoint configured on the . 41 | /// 42 | /// true if [ping endpoint enabled]; otherwise, false. 43 | /// 44 | public bool PingEndpointEnabled { get; set; } 45 | 46 | /// 47 | /// Gets or sets the timeout when reading health checks via the health endpoint. 48 | /// 49 | public TimeSpan Timeout { get; set; } 50 | } 51 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Endpoints/Internal/HealthEndpointsOptionsSetup.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using App.Metrics.Health.Formatters; 8 | using App.Metrics.Health.Formatters.Json; 9 | using Microsoft.Extensions.Options; 10 | 11 | namespace App.Metrics.AspNetCore.Health.Endpoints.Internal 12 | { 13 | /// 14 | /// Sets up default health endpoint options for . 15 | /// 16 | public class HealthEndpointsOptionsSetup : IConfigureOptions 17 | { 18 | private readonly HealthFormatterCollection _healthFormatters; 19 | 20 | public HealthEndpointsOptionsSetup(IReadOnlyCollection healthFormatters) 21 | { 22 | _healthFormatters = new HealthFormatterCollection(healthFormatters.ToList()); 23 | } 24 | 25 | /// 26 | public void Configure(HealthEndpointsOptions options) 27 | { 28 | if (options.HealthEndpointOutputFormatter == null) 29 | { 30 | options.HealthEndpointOutputFormatter = 31 | _healthFormatters.GetType() ?? _healthFormatters.LastOrDefault(); 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Endpoints/Internal/HealthMiddlewareConstants.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | namespace App.Metrics.AspNetCore.Health.Endpoints.Internal 6 | { 7 | internal static class HealthMiddlewareConstants 8 | { 9 | public static class DefaultRoutePaths 10 | { 11 | public const string HealthEndpoint = "/health"; 12 | public const string PingEndpoint = "/ping"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Endpoints/Middleware/HealthCheckEndpointMiddleware.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using App.Metrics.Health; 9 | using Microsoft.AspNetCore.Http; 10 | using Microsoft.AspNetCore.Http.Features; 11 | using Microsoft.Extensions.Logging; 12 | using Microsoft.Net.Http.Headers; 13 | 14 | namespace App.Metrics.AspNetCore.Health.Endpoints.Middleware 15 | { 16 | // ReSharper disable ClassNeverInstantiated.Global 17 | public class HealthCheckEndpointMiddleware 18 | // ReSharper restore ClassNeverInstantiated.Global 19 | { 20 | private readonly IRunHealthChecks _healthCheckRunner; 21 | private readonly IHealthResponseWriter _healthResponseWriter; 22 | private readonly ILogger _logger; 23 | private readonly TimeSpan _timeout; 24 | 25 | // ReSharper disable UnusedParameter.Local 26 | public HealthCheckEndpointMiddleware( 27 | RequestDelegate next, 28 | ILoggerFactory loggerFactory, 29 | IRunHealthChecks healthCheckRunner, 30 | IHealthResponseWriter healthResponseWriter, 31 | TimeSpan timeout) 32 | // ReSharper restore UnusedParameter.Local 33 | { 34 | _healthCheckRunner = healthCheckRunner; 35 | _logger = loggerFactory.CreateLogger(); 36 | _healthResponseWriter = healthResponseWriter ?? throw new ArgumentNullException(nameof(healthResponseWriter)); 37 | _timeout = timeout <= TimeSpan.Zero ? TimeSpan.FromSeconds(20) : timeout; 38 | } 39 | 40 | // ReSharper disable UnusedMember.Global 41 | public async Task Invoke(HttpContext context) 42 | // ReSharper restore UnusedMember.Global 43 | { 44 | _logger.MiddlewareExecuting(); 45 | 46 | var healthCheckCancellationTokenSource = new CancellationTokenSource(_timeout); 47 | 48 | using (var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(healthCheckCancellationTokenSource.Token, context.RequestAborted)) 49 | { 50 | try 51 | { 52 | var healthStatus = await _healthCheckRunner.ReadAsync(cancellationTokenSource.Token); 53 | 54 | await _healthResponseWriter.WriteAsync(context, healthStatus, cancellationTokenSource.Token); 55 | } 56 | catch (OperationCanceledException e) 57 | { 58 | var responseFeature = context.Response.HttpContext.Features.Get(); 59 | 60 | if (healthCheckCancellationTokenSource.IsCancellationRequested) 61 | { 62 | responseFeature.ReasonPhrase = "Health Check Middleware timed out."; 63 | _logger.MiddlewareFailed(e, responseFeature.ReasonPhrase); 64 | } 65 | else if (context.RequestAborted.IsCancellationRequested) 66 | { 67 | responseFeature.ReasonPhrase = "Health Check Middleware request aborted."; 68 | _logger.MiddlewareFailed(e, responseFeature.ReasonPhrase); 69 | } 70 | 71 | context.SetNoCacheHeaders(); 72 | context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; 73 | context.Response.Headers[HeaderNames.ContentType] = new[] { context.Request.ContentType }; 74 | } 75 | } 76 | 77 | _logger.MiddlewareExecuted(); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Endpoints/Middleware/PingEndpointMiddleware.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Http; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace App.Metrics.AspNetCore.Health.Endpoints.Middleware 10 | { 11 | // ReSharper disable ClassNeverInstantiated.Global 12 | public class PingEndpointMiddleware 13 | // ReSharper restore ClassNeverInstantiated.Global 14 | { 15 | private readonly ILogger _logger; 16 | 17 | // ReSharper disable UnusedParameter.Local - next required by middleware components 18 | public PingEndpointMiddleware( 19 | RequestDelegate next, 20 | ILogger logger) 21 | // ReSharper restore UnusedParameter.Local 22 | { 23 | _logger = logger; 24 | } 25 | 26 | // ReSharper disable UnusedMember.Global 27 | public async Task Invoke(HttpContext context) 28 | // ReSharper restore UnusedMember.Global 29 | { 30 | _logger.MiddlewareExecuting(); 31 | 32 | context.Response.Headers["Content-Type"] = new[] { "text/plain" }; 33 | context.SetNoCacheHeaders(); 34 | 35 | context.Response.StatusCode = StatusCodes.Status200OK; 36 | 37 | await context.Response.WriteAsync("pong"); 38 | 39 | _logger.MiddlewareExecuted(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Hosting/App.Metrics.AspNetCore.Health.Hosting.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | App Metrics Health ASP.NET Core Hosting provides extensions to configure App Metrics Health in an ASP.NET Core application. 5 | netstandard2.0 6 | true 7 | appmetrics;aspnetcore;metrics 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health.Hosting/HealthAspNetWebHostBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Reflection; 7 | using App.Metrics.Health; 8 | using App.Metrics.Health.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.DependencyModel; 11 | 12 | // ReSharper disable CheckNamespace 13 | namespace Microsoft.AspNetCore.Hosting 14 | // ReSharper restore CheckNamespace 15 | { 16 | /// 17 | /// Extension methods for setting up App Metrics Health AspNet Core services in an . 18 | /// 19 | public static class HealthAspNetWebHostBuilderExtensions 20 | { 21 | private static bool _healthBuilt; 22 | 23 | public static IWebHostBuilder ConfigureHealthWithDefaults( 24 | this IWebHostBuilder hostBuilder, 25 | Action configureHealth, 26 | DependencyContext dependencyContext = null) 27 | { 28 | if (_healthBuilt) 29 | { 30 | throw new InvalidOperationException("HealthBuilder allows creation only of a single instance of IMetrics"); 31 | } 32 | 33 | return hostBuilder.ConfigureServices( 34 | (context, services) => 35 | { 36 | var healthBuilder = AppMetricsHealth.CreateDefaultBuilder(); 37 | configureHealth(context, healthBuilder); 38 | healthBuilder.HealthChecks.RegisterFromAssembly(services, dependencyContext ?? GetDependencyContext()); 39 | healthBuilder.Configuration.ReadFrom(context.Configuration); 40 | healthBuilder.BuildAndAddTo(services); 41 | _healthBuilt = true; 42 | }); 43 | } 44 | 45 | public static IWebHostBuilder ConfigureHealthWithDefaults( 46 | this IWebHostBuilder hostBuilder, 47 | Action configureHealth, 48 | DependencyContext dependencyContext = null) 49 | { 50 | if (_healthBuilt) 51 | { 52 | throw new InvalidOperationException("HealthBuilder allows creation only of a single instance of IMetrics"); 53 | } 54 | 55 | return hostBuilder.ConfigureServices( 56 | (context, services) => 57 | { 58 | var healthBuilder = AppMetricsHealth.CreateDefaultBuilder(); 59 | configureHealth(context, services, healthBuilder); 60 | healthBuilder.HealthChecks.RegisterFromAssembly(services, dependencyContext ?? GetDependencyContext()); 61 | healthBuilder.Configuration.ReadFrom(context.Configuration); 62 | healthBuilder.BuildAndAddTo(services); 63 | _healthBuilt = true; 64 | }); 65 | } 66 | 67 | public static IWebHostBuilder ConfigureHealthWithDefaults(this IWebHostBuilder hostBuilder, Action configureHealth) 68 | { 69 | if (_healthBuilt) 70 | { 71 | throw new InvalidOperationException("HealthBuilder allows creation only of a single instance of IHealth"); 72 | } 73 | 74 | hostBuilder.ConfigureHealthWithDefaults( 75 | (context, builder) => 76 | { 77 | configureHealth(builder); 78 | }); 79 | 80 | return hostBuilder; 81 | } 82 | 83 | public static IWebHostBuilder ConfigureHealth( 84 | this IWebHostBuilder hostBuilder, 85 | Action configureHealth) 86 | { 87 | if (_healthBuilt) 88 | { 89 | throw new InvalidOperationException("HealthBuilder allows creation only of a single instance of IHealth"); 90 | } 91 | 92 | return hostBuilder.ConfigureServices( 93 | (context, services) => 94 | { 95 | services.AddHealth( 96 | healthBuilder => 97 | { 98 | configureHealth(context, healthBuilder); 99 | healthBuilder.Configuration.ReadFrom(context.Configuration); 100 | _healthBuilt = true; 101 | }); 102 | }); 103 | } 104 | 105 | public static IWebHostBuilder ConfigureHealth( 106 | this IWebHostBuilder hostBuilder, 107 | Action configureHealth) 108 | { 109 | if (_healthBuilt) 110 | { 111 | throw new InvalidOperationException("HealthBuilder allows creation only of a single instance of IHealth"); 112 | } 113 | 114 | return hostBuilder.ConfigureServices( 115 | (context, services) => 116 | { 117 | services.AddHealth( 118 | healthBuilder => 119 | { 120 | configureHealth(context, services, healthBuilder); 121 | healthBuilder.Configuration.ReadFrom(context.Configuration); 122 | _healthBuilt = true; 123 | }); 124 | }); 125 | } 126 | 127 | public static IWebHostBuilder ConfigureHealth(this IWebHostBuilder hostBuilder, Action configureHealth) 128 | { 129 | if (_healthBuilt) 130 | { 131 | throw new InvalidOperationException("HealthBuilder allows creation only of a single instance of IHealth"); 132 | } 133 | 134 | hostBuilder.ConfigureHealth( 135 | (context, healthBuilder) => 136 | { 137 | configureHealth(healthBuilder); 138 | }); 139 | 140 | return hostBuilder; 141 | } 142 | 143 | public static IWebHostBuilder ConfigureHealth( 144 | this IWebHostBuilder hostBuilder, 145 | DependencyContext dependencyContext = null) 146 | { 147 | if (_healthBuilt) 148 | { 149 | return hostBuilder; 150 | } 151 | 152 | return hostBuilder.ConfigureServices( 153 | (context, services) => 154 | { 155 | if (!_healthBuilt) 156 | { 157 | AppMetricsHealth.CreateDefaultBuilder() 158 | .Configuration.ReadFrom(context.Configuration) 159 | .HealthChecks.RegisterFromAssembly(services, dependencyContext ?? GetDependencyContext()) 160 | .BuildAndAddTo(services); 161 | 162 | _healthBuilt = true; 163 | } 164 | }); 165 | } 166 | 167 | internal static DependencyContext GetDependencyContext() 168 | { 169 | return Assembly.GetEntryAssembly() != null ? DependencyContext.Default : null; 170 | } 171 | } 172 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health/App.Metrics.AspNetCore.Health.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | App Metrics ASP.NET Core Health provides health checking capabilities to web applications and allows you to expose health check results over HTTP. 5 | netstandard2.0 6 | true 7 | appmetrics;aspnetcore;middleware;healthchecks 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health/DefaultHealthAspNetWebHostBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.DependencyInjection; 8 | 9 | namespace App.Metrics.AspNetCore.Health 10 | { 11 | public static class DefaultHealthAspNetWebHostBuilderExtensions 12 | { 13 | /// 14 | /// Adds App Metrics Health services, configuration and middleware to the 15 | /// . 16 | /// 17 | /// The . 18 | /// A reference to this instance after the operation has completed. 19 | /// 20 | /// cannot be null 21 | /// 22 | public static IWebHostBuilder UseHealth(this IWebHostBuilder hostBuilder) 23 | { 24 | hostBuilder.ConfigureServices( 25 | (context, services) => 26 | { 27 | services.AddHealthReportingHostedService(); 28 | services.AddHealthEndpoints(context.Configuration); 29 | }); 30 | 31 | hostBuilder.UseHealthEndpoints(); 32 | 33 | return hostBuilder; 34 | } 35 | 36 | /// 37 | /// Adds App Metrics Health services, configuration and middleware to the 38 | /// . 39 | /// 40 | /// The . 41 | /// A callback to configure . 42 | /// A reference to this instance after the operation has completed. 43 | /// 44 | /// cannot be null 45 | /// 46 | public static IWebHostBuilder UseHealth( 47 | this IWebHostBuilder hostBuilder, 48 | Action optionsDelegate) 49 | { 50 | var options = new HealthWebHostOptions(); 51 | 52 | hostBuilder.ConfigureServices( 53 | services => 54 | { 55 | optionsDelegate(options); 56 | 57 | services.AddHealthReportingHostedService(options.UnobservedTaskExceptionHandler); 58 | services.AddHealthEndpoints(options.EndpointOptions); 59 | }); 60 | 61 | hostBuilder.UseHealthEndpoints(); 62 | 63 | return hostBuilder; 64 | } 65 | 66 | /// 67 | /// Adds App Metrics Health services, configuration and middleware to the 68 | /// . 69 | /// 70 | /// The . 71 | /// A callback to configure . 72 | /// A reference to this instance after the operation has completed. 73 | /// 74 | /// cannot be null 75 | /// 76 | public static IWebHostBuilder UseHealth( 77 | this IWebHostBuilder hostBuilder, 78 | Action optionsDelegate) 79 | { 80 | var options = new HealthWebHostOptions(); 81 | 82 | hostBuilder.ConfigureServices( 83 | (context, services) => 84 | { 85 | optionsDelegate(context, options); 86 | 87 | services.AddHealthReportingHostedService(options.UnobservedTaskExceptionHandler); 88 | services.AddHealthEndpoints(options.EndpointOptions, context.Configuration); 89 | }); 90 | 91 | hostBuilder.UseHealthEndpoints(); 92 | 93 | return hostBuilder; 94 | } 95 | 96 | /// 97 | /// Adds App Metrics Health services, configuration and middleware to the 98 | /// . 99 | /// 100 | /// The type of the used to configure metrics middleware. 101 | /// The . 102 | /// A callback to configure . 103 | /// A reference to this instance after the operation has completed. 104 | public static IWebHostBuilder UseHealth( 105 | this IWebHostBuilder hostBuilder, 106 | Action optionsDelegate) 107 | where TStartup : IStartupFilter, new() 108 | { 109 | var options = new HealthWebHostOptions(); 110 | 111 | hostBuilder.ConfigureHealth(); 112 | 113 | hostBuilder.ConfigureServices( 114 | (context, services) => 115 | { 116 | optionsDelegate(options); 117 | 118 | services.AddHealthReportingHostedService(options.UnobservedTaskExceptionHandler); 119 | services.AddHealthEndpoints(options.EndpointOptions, context.Configuration); 120 | services.AddSingleton(new TStartup()); 121 | }); 122 | 123 | return hostBuilder; 124 | } 125 | 126 | /// 127 | /// Adds App Metrics Health services, configuration and middleware to the 128 | /// . 129 | /// 130 | /// The type of the used to configure metrics middleware. 131 | /// The . 132 | /// A reference to this instance after the operation has completed. 133 | public static IWebHostBuilder UseHealth( 134 | this IWebHostBuilder hostBuilder) 135 | where TStartup : IStartupFilter, new() 136 | { 137 | hostBuilder.ConfigureHealth(); 138 | 139 | hostBuilder.ConfigureServices( 140 | (context, services) => 141 | { 142 | services.AddHealthReportingHostedService(); 143 | services.AddHealthEndpoints(context.Configuration); 144 | services.AddSingleton(new TStartup()); 145 | }); 146 | 147 | return hostBuilder; 148 | } 149 | } 150 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health/DefaultHealthStartupFilter.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | 9 | namespace App.Metrics.AspNetCore.Health 10 | { 11 | /// 12 | /// Inserts the App Metrics Health Middleware at the request pipeline 13 | /// 14 | public class DefaultHealthStartupFilter : IStartupFilter 15 | { 16 | /// 17 | public Action Configure(Action next) 18 | { 19 | return AddAllHealthEndpoints; 20 | 21 | void AddAllHealthEndpoints(IApplicationBuilder app) 22 | { 23 | app.UseHealthAllEndpoints(); 24 | app.UsePingEndpoint(); 25 | 26 | next(app); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health/HealthWebHostOptions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Threading.Tasks; 7 | using App.Metrics.AspNetCore.Health.Endpoints; 8 | using App.Metrics.Health; 9 | 10 | namespace App.Metrics.AspNetCore.Health 11 | { 12 | /// 13 | /// Provides programmatic configuration for health and health endpoints in the App Metrics framework. 14 | /// 15 | public class HealthWebHostOptions 16 | { 17 | public HealthWebHostOptions() 18 | { 19 | HealthOptions = options => { }; 20 | EndpointOptions = options => { }; 21 | } 22 | 23 | /// 24 | /// Gets or sets to configure the provided . 25 | /// 26 | public Action HealthOptions { get; set; } 27 | 28 | /// 29 | /// Gets or sets to configure the provided . 30 | /// 31 | public Action EndpointOptions { get; set; } 32 | 33 | /// 34 | /// Gets or sets the registered with an exception is thrown. 35 | /// 36 | public EventHandler UnobservedTaskExceptionHandler { get; set; } 37 | } 38 | } -------------------------------------------------------------------------------- /src/App.Metrics.AspNetCore.Health/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Runtime.CompilerServices; 6 | 7 | [assembly: InternalsVisibleTo("App.Metrics.AspNetCore.Health.Integration.Facts, PublicKey=00240000048000009400000006020000002400005253413100040000010001000961061aa9f970163db728a9792c1cfb7be51f2f986054c676345f9e43f95af5f3114b1962d10888a3ea1dff99bf56bce565f887cb4b004fc44ccb7335700260012a65b9cdd090e6b60c8c67c434ca49563c82c66695f8dc0776770bfaf481ef816767b7dd67d083960a6fcfe33c3c0e1cc198fe7a13b3283133d21b3435ebbb")] -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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": "Allan Hardy", 12 | "copyrightText": "Copyright (c) {companyName}. All rights reserved.", 13 | "xmlHeader": true 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/App.Metrics.AspNetCore.Health.Integration.Facts.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(StandardTest) 5 | 6 | 7 | 8 | 9 | PreserveNewest 10 | 11 | 12 | 13 | 14 | 15 | Always 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/DependencyInjection/MiddlewareHealthChecksAppMetricsBuilderExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using App.Metrics.AspNetCore.Health.Endpoints; 7 | using App.Metrics.Health; 8 | using App.Metrics.Health.Extensions.Configuration; 9 | using FluentAssertions; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using Microsoft.Extensions.Options; 13 | using Xunit; 14 | 15 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.DependencyInjection 16 | { 17 | public class MiddlewareHealthChecksAppMetricsBuilderExtensionsTests 18 | { 19 | [Fact] 20 | public void Can_load_settings_from_configuration() 21 | { 22 | var endpointOptions = new HealthEndpointsOptions(); 23 | 24 | var provider = SetupServicesAndConfiguration(); 25 | Action resolveEndpointsOptions = () => { endpointOptions = provider.GetRequiredService>().Value; }; 26 | 27 | resolveEndpointsOptions.Should().NotThrow(); 28 | 29 | endpointOptions.HealthEndpointEnabled.Should().Be(false); 30 | } 31 | 32 | [Fact] 33 | public void Can_override_settings_from_configuration() 34 | { 35 | var options = new HealthEndpointsOptions(); 36 | var provider = SetupServicesAndConfiguration( 37 | o => 38 | { 39 | o.HealthEndpointEnabled = true; 40 | o.Timeout = TimeSpan.FromDays(1); 41 | }); 42 | 43 | Action resolveOptions = () => { options = provider.GetRequiredService>().Value; }; 44 | 45 | resolveOptions.Should().NotThrow(); 46 | options.HealthEndpointEnabled.Should().Be(true); 47 | options.PingEndpointEnabled.Should().Be(false); 48 | options.Timeout.Should().Be(TimeSpan.FromDays(1)); 49 | } 50 | 51 | private IServiceProvider SetupServicesAndConfiguration( 52 | Action setupEndpointAction = null) 53 | { 54 | var services = new ServiceCollection(); 55 | services.AddOptions(); 56 | 57 | var builder = new ConfigurationBuilder() 58 | .SetBasePath(AppContext.BaseDirectory) 59 | .AddJsonFile("DependencyInjection/TestConfiguration/appsettings.json", optional: true, reloadOnChange: true); 60 | 61 | var configuration = builder.Build(); 62 | 63 | var healthBuilder = AppMetricsHealth.CreateDefaultBuilder() 64 | .Configuration.ReadFrom(configuration); 65 | services.AddHealth(healthBuilder); 66 | 67 | if (setupEndpointAction == null) 68 | { 69 | services.AddHealthEndpoints(configuration.GetSection(nameof(HealthEndpointsOptions))); 70 | } 71 | else 72 | { 73 | services.AddHealthEndpoints(configuration.GetSection(nameof(HealthEndpointsOptions)), setupEndpointAction); 74 | } 75 | 76 | return services.BuildServiceProvider(); 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/DependencyInjection/TestConfiguration/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "HealthOptions": { 3 | "Enabled": false 4 | }, 5 | "HealthEndpointsOptions": { 6 | "HealthEndpointEnabled": false, 7 | "PingEndpointEnabled": false, 8 | "Timeout": "0:0:10" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Middleware/HealthCheckEndpointDegradedMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | using App.Metrics.AspNetCore.Health.Integration.Facts.Startup; 9 | using FluentAssertions; 10 | using Xunit; 11 | 12 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Middleware 13 | { 14 | public class HealthCheckEndpointDegradedMiddlewareTests : IClassFixture> 15 | { 16 | public HealthCheckEndpointDegradedMiddlewareTests(StartupTestFixture fixture) 17 | { 18 | Client = fixture.Client; 19 | } 20 | 21 | private HttpClient Client { get; } 22 | 23 | [Fact] 24 | public async Task Can_count_errors_per_endpoint_and_also_get_a_total_error_count() 25 | { 26 | var result = await Client.GetAsync("/health"); 27 | 28 | result.StatusCode.Should().Be(HttpStatusCode.OK); 29 | result.Headers.Warning.ToString().Should().StartWith("Warning: 100 "); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Middleware/HealthCheckEndpointDisabledMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | using App.Metrics.AspNetCore.Health.Integration.Facts.Startup; 9 | using FluentAssertions; 10 | using Xunit; 11 | 12 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Middleware 13 | { 14 | public class HealthCheckEndpointDisabledMiddlewareTests : IClassFixture> 15 | { 16 | public HealthCheckEndpointDisabledMiddlewareTests(StartupTestFixture fixture) { Client = fixture.Client; } 17 | 18 | private HttpClient Client { get; } 19 | 20 | [Fact] 21 | public async Task Can_disable_health_checks() 22 | { 23 | var result = await Client.GetAsync("/health"); 24 | 25 | result.StatusCode.Should().Be(HttpStatusCode.NotFound); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Middleware/HealthCheckEndpointMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | using App.Metrics.AspNetCore.Health.Integration.Facts.Startup; 9 | using FluentAssertions; 10 | using Xunit; 11 | 12 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Middleware 13 | { 14 | public class HealthCheckEndpointMiddlewareTests : IClassFixture> 15 | { 16 | public HealthCheckEndpointMiddlewareTests(StartupTestFixture fixture) 17 | { 18 | Client = fixture.Client; 19 | } 20 | 21 | private HttpClient Client { get; } 22 | 23 | [Fact] 24 | public async Task Returns_correct_response_headers() 25 | { 26 | var result = await Client.GetAsync("/health"); 27 | 28 | result.Headers.CacheControl.NoCache.Should().Be(true); 29 | result.Headers.CacheControl.NoStore.Should().Be(true); 30 | result.Headers.CacheControl.MustRevalidate.Should().Be(true); 31 | result.Headers.Pragma.ToString().Should().Be("no-cache"); 32 | result.StatusCode.Should().Be(HttpStatusCode.OK); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Middleware/HealthCheckEndpointUnhealthyMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | using App.Metrics.AspNetCore.Health.Integration.Facts.Startup; 9 | using FluentAssertions; 10 | using Xunit; 11 | 12 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Middleware 13 | { 14 | public class HealthCheckEndpointUnhealthyMiddlewareTests : IClassFixture> 15 | { 16 | public HealthCheckEndpointUnhealthyMiddlewareTests(StartupTestFixture fixture) 17 | { 18 | Client = fixture.Client; 19 | } 20 | 21 | private HttpClient Client { get; } 22 | 23 | [Fact] 24 | public async Task Can_count_errors_per_endpoint_and_also_get_a_total_error_count() 25 | { 26 | var result = await Client.GetAsync("/health"); 27 | 28 | result.StatusCode.Should().Be(HttpStatusCode.ServiceUnavailable); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Middleware/PingEndpointDisabledMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | using App.Metrics.AspNetCore.Health.Integration.Facts.Startup; 9 | using FluentAssertions; 10 | using Xunit; 11 | 12 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Middleware 13 | { 14 | public class PingEndpointDisabledMiddlewareTests : IClassFixture> 15 | { 16 | public PingEndpointDisabledMiddlewareTests(StartupTestFixture fixture) 17 | { 18 | Client = fixture.Client; 19 | } 20 | 21 | private HttpClient Client { get; } 22 | 23 | [Fact] 24 | public async Task When_enabled_returns_pong() 25 | { 26 | var result = await Client.GetAsync("/ping"); 27 | 28 | result.StatusCode.Should().Be(HttpStatusCode.NotFound); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Middleware/PingEndpointMiddlewareTests.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | using App.Metrics.AspNetCore.Health.Integration.Facts.Startup; 9 | using FluentAssertions; 10 | using Xunit; 11 | 12 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Middleware 13 | { 14 | public class PingEndpointMiddlewareTests : IClassFixture> 15 | { 16 | public PingEndpointMiddlewareTests(StartupTestFixture fixture) 17 | { 18 | Client = fixture.Client; 19 | } 20 | 21 | private HttpClient Client { get; } 22 | 23 | [Fact] 24 | public async Task Resposne_is_plain_text_content_type() 25 | { 26 | var result = await Client.GetAsync("/ping"); 27 | 28 | result.StatusCode.Should().Be(HttpStatusCode.OK); 29 | result.Content.Headers.ContentType.ToString().Should().Match(s => s == "text/plain"); 30 | } 31 | 32 | [Fact] 33 | public async Task Returns_correct_response_headers() 34 | { 35 | var result = await Client.GetAsync("/ping"); 36 | 37 | result.Headers.CacheControl.NoCache.Should().Be(true); 38 | result.Headers.CacheControl.NoStore.Should().Be(true); 39 | result.Headers.CacheControl.MustRevalidate.Should().Be(true); 40 | result.Headers.Pragma.ToString().Should().Be("no-cache"); 41 | } 42 | 43 | [Fact] 44 | public async Task When_enabled_returns_pong() 45 | { 46 | var result = await Client.GetAsync("/ping"); 47 | var response = await result.Content.ReadAsStringAsync(); 48 | 49 | result.StatusCode.Should().Be(HttpStatusCode.OK); 50 | response.Should().Be("pong"); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Startup/DefaultTestStartup.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using App.Metrics.AspNetCore.Health.Endpoints; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Startup 12 | { 13 | // ReSharper disable UnusedMember.Global 14 | public class DefaultTestStartup : TestStartup 15 | // ReSharper restore UnusedMember.Global 16 | { 17 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 18 | { 19 | app.UseHealthEndpoint(); 20 | 21 | SetupAppBuilder(app, env, loggerFactory); 22 | } 23 | 24 | public void ConfigureServices(IServiceCollection services) 25 | { 26 | var appMetricsMiddlewareHealthChecksOptions = new HealthEndpointsOptions { HealthEndpointEnabled = true }; 27 | 28 | SetupServices(services, appMetricsMiddlewareHealthChecksOptions); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Startup/DegradedHealthTestStartup.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using App.Metrics.AspNetCore.Health.Endpoints; 6 | using App.Metrics.Health; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Startup 13 | { 14 | // ReSharper disable ClassNeverInstantiated.Global 15 | public class DegradedHealthTestStartup : TestStartup 16 | // ReSharper restore ClassNeverInstantiated.Global 17 | { 18 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 19 | { 20 | app.UseHealthEndpoint(); 21 | 22 | SetupAppBuilder(app, env, loggerFactory); 23 | } 24 | 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | SetupServices( 28 | services, 29 | new HealthEndpointsOptions(), 30 | healthChecks: new[] { HealthCheckResult.Healthy(), HealthCheckResult.Degraded() }); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Startup/DisabledHealthCheckTestStartup.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using App.Metrics.AspNetCore.Health.Endpoints; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Startup 12 | { 13 | // ReSharper disable ClassNeverInstantiated.Global 14 | public class DisabledHealthCheckTestStartup : TestStartup 15 | // ReSharper restore ClassNeverInstantiated.Global 16 | { 17 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 18 | { 19 | app.UseHealthEndpoint(); 20 | 21 | SetupAppBuilder(app, env, loggerFactory); 22 | } 23 | 24 | public void ConfigureServices(IServiceCollection services) 25 | { 26 | var appMetricsMiddlewareHelathCheckOptions = new HealthEndpointsOptions { HealthEndpointEnabled = false }; 27 | 28 | SetupServices(services, appMetricsMiddlewareHelathCheckOptions); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Startup/HealthyHealthTestStartup.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using App.Metrics.AspNetCore.Health.Endpoints; 6 | using App.Metrics.Health; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Startup 13 | { 14 | // ReSharper disable ClassNeverInstantiated.Global 15 | public class HealthyHealthTestStartup : TestStartup 16 | // ReSharper restore ClassNeverInstantiated.Global 17 | { 18 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 19 | { 20 | app.UseHealthEndpoint(); 21 | 22 | SetupAppBuilder(app, env, loggerFactory); 23 | } 24 | 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | var appMetricsMiddlewareHelathCheckOptions = new HealthEndpointsOptions(); 28 | 29 | SetupServices( 30 | services, 31 | appMetricsMiddlewareHelathCheckOptions, 32 | healthChecks: new[] { HealthCheckResult.Healthy() }); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Startup/PingDisabledTestStartup.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using App.Metrics.AspNetCore.Health.Endpoints; 6 | using App.Metrics.Health; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Startup 13 | { 14 | public class PingDisabledTestStartup : TestStartup 15 | { 16 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 17 | { 18 | app.UsePingEndpoint(); 19 | 20 | SetupAppBuilder(app, env, loggerFactory); 21 | } 22 | 23 | public void ConfigureServices(IServiceCollection services) 24 | { 25 | var healthEndpointsOptions = new HealthEndpointsOptions 26 | { 27 | PingEndpointEnabled = false 28 | }; 29 | 30 | SetupServices(services, healthEndpointsOptions, healthChecks: new[] { HealthCheckResult.Healthy() }); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Startup/PingTestStartup.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using App.Metrics.AspNetCore.Health.Endpoints; 6 | using App.Metrics.Health; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Startup 13 | { 14 | public class PingTestStartup : TestStartup 15 | { 16 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 17 | { 18 | app.UsePingEndpoint(); 19 | 20 | SetupAppBuilder(app, env, loggerFactory); 21 | } 22 | 23 | public void ConfigureServices(IServiceCollection services) 24 | { 25 | var healthEndpointsOptions = new HealthEndpointsOptions 26 | { 27 | PingEndpointEnabled = true 28 | }; 29 | 30 | SetupServices(services, healthEndpointsOptions, healthChecks: new[] { HealthCheckResult.Healthy() }); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Startup/TestStartup.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using App.Metrics.AspNetCore.Health.Endpoints; 9 | using App.Metrics.Health; 10 | using App.Metrics.Health.Builder; 11 | using Microsoft.AspNetCore.Builder; 12 | using Microsoft.AspNetCore.Hosting; 13 | using Microsoft.Extensions.DependencyInjection; 14 | using Microsoft.Extensions.Logging; 15 | 16 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Startup 17 | { 18 | public abstract class TestStartup 19 | { 20 | protected void SetupAppBuilder(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 21 | { 22 | app.UseHealthEndpoint(); 23 | } 24 | 25 | protected void SetupServices( 26 | IServiceCollection services, 27 | HealthEndpointsOptions healthMiddlewareCoreChecksOptions, 28 | IEnumerable healthChecks = null) 29 | { 30 | services.AddOptions(); 31 | services.AddLogging(); 32 | 33 | // TODO: scan for healthchecks 34 | // var startupAssemblyName = typeof(TestStartup).Assembly.GetName().Name; 35 | 36 | var builder = new HealthBuilder() 37 | .Configuration.Configure(options => options.Enabled = true) 38 | .OutputHealth.AsPlainText() 39 | .OutputHealth.AsJson(); 40 | 41 | var checks = healthChecks?.ToList() ?? new List(); 42 | 43 | for (var i = 0; i < checks.Count; i++) 44 | { 45 | var check = checks[i]; 46 | builder.HealthChecks.AddCheck("Check" + i, () => new ValueTask(check)); 47 | } 48 | 49 | services.AddHealth(builder) 50 | .AddHealthEndpoints( 51 | options => 52 | { 53 | options.HealthEndpointEnabled = healthMiddlewareCoreChecksOptions.HealthEndpointEnabled; 54 | options.PingEndpointEnabled = healthMiddlewareCoreChecksOptions.PingEndpointEnabled; 55 | options.Timeout = healthMiddlewareCoreChecksOptions.Timeout; 56 | }); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/Startup/UnhealthyHealthTestStartup.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using App.Metrics.AspNetCore.Health.Endpoints; 6 | using App.Metrics.Health; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace App.Metrics.AspNetCore.Health.Integration.Facts.Startup 13 | { 14 | // ReSharper disable ClassNeverInstantiated.Global 15 | public class UnhealthyHealthTestStartup : TestStartup 16 | // ReSharper restore ClassNeverInstantiated.Global 17 | { 18 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 19 | { 20 | app.UseHealthEndpoint(); 21 | 22 | SetupAppBuilder(app, env, loggerFactory); 23 | } 24 | 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | var appMetricsMiddlewareHelathCheckOptions = new HealthEndpointsOptions { HealthEndpointEnabled = true }; 28 | 29 | SetupServices( 30 | services, 31 | appMetricsMiddlewareHelathCheckOptions, 32 | healthChecks: new[] { HealthCheckResult.Healthy(), HealthCheckResult.Degraded(), HealthCheckResult.Unhealthy() }); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/StartupTestFixture{TStartup}.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System; 6 | using System.Net.Http; 7 | using App.Metrics.Health; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.AspNetCore.TestHost; 10 | using Microsoft.Extensions.DependencyInjection; 11 | 12 | namespace App.Metrics.AspNetCore.Health.Integration.Facts 13 | { 14 | // ReSharper disable ClassNeverInstantiated.Global 15 | public class StartupTestFixture : IDisposable 16 | // ReSharper restore ClassNeverInstantiated.Global 17 | where TStartup : class 18 | { 19 | private readonly TestServer _server; 20 | 21 | public StartupTestFixture() 22 | { 23 | var builder = new WebHostBuilder().UseStartup(); 24 | 25 | _server = new TestServer(builder); 26 | 27 | Client = _server.CreateClient(); 28 | Health = _server.Host.Services.GetRequiredService(); 29 | } 30 | 31 | public HttpClient Client { get; } 32 | 33 | public IHealth Health { get; } 34 | 35 | public void Dispose() 36 | { 37 | Client.Dispose(); 38 | _server.Dispose(); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/TestHealthCheck.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Allan Hardy. All rights reserved. 3 | // 4 | 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using App.Metrics.Health; 8 | 9 | namespace App.Metrics.AspNetCore.Health.Integration.Facts 10 | { 11 | // ReSharper disable UnusedMember.Global this is automatically registered 12 | public class TestHealthCheck : HealthCheck 13 | // ReSharper restore UnusedMember.Global 14 | { 15 | public TestHealthCheck() 16 | : base("Test Health Check") { } 17 | 18 | protected override ValueTask CheckAsync(CancellationToken token = default) 19 | { 20 | return new ValueTask(HealthCheckResult.Healthy("OK")); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /test/App.Metrics.AspNetCore.Health.Integration.Facts/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "HealthEndpointOptions": { 3 | "Enabled": false, 4 | "Endpoint": "/health-test" 5 | } 6 | } -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | netcoreapp2.1 6 | $(DeveloperBuildTest) 7 | $(StandardTest);netcoreapp2.1 8 | $(StandardTest);net461 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tools/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /version.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2.1.0 5 | 6 | 7 | -------------------------------------------------------------------------------- /xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | } --------------------------------------------------------------------------------