├── .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
4 | [](http://app-metrics.io/getting-started/intro.html) [](https://opensource.org/licenses/Apache-2.0) [](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|[](https://ci.appveyor.com/project/alhardy/aspnetcorehealth/branch/dev)|[](https://travis-ci.org/alhardy/aspnetcorehealth)|[](https://coveralls.io/github/AppMetrics/AspNetCoreHealth?branch=dev)
15 | |master|[](https://ci.appveyor.com/project/alhardy/aspnetcorehealth/branch/master)| [](https://travis-ci.org/alhardy/aspnetcorehealth)| [](https://coveralls.io/github/AppMetrics/AspNetCoreHealth?branch=master)|
16 |
17 | |Package|Dev Release|Pre-Release|Release|
18 | |------|:--------:|:--------:|:--------:|
19 | |App.Metrics.AspNetCore.Health|[](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.AspNetCore.Health)|[](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health/)|[](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health/)
20 | |App.Metrics.AspNetCore.Health.Core|[](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.AspNetCore.Health.Core)|[](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health.Core/)|[](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health.Core/)
21 | |App.Metrics.AspNetCore.Health.Endpoints|[](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.AspNetCore.Health.Endpoints)|[](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health.Endpoints/)|[](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health.Endpoints/)
22 | |App.Metrics.AspNetCore.Health.Hosting|[](https://www.myget.org/feed/appmetrics/package/nuget/App.Metrics.AspNetCore.Health.Hosting)|[](https://www.nuget.org/packages/App.Metrics.AspNetCore.Health.Hosting/)|[](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 | [](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 | }
--------------------------------------------------------------------------------