-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
47 changed files
with
5,356 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
report.xml | ||
cover.out | ||
*.iml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
linters-settings: | ||
goconst: | ||
min-len: 3 | ||
min-occurrences: 3 | ||
gocritic: | ||
enabled-tags: | ||
- diagnostic | ||
- experimental | ||
- opinionated | ||
- performance | ||
- style | ||
disabled-checks: | ||
- dupImport # https://github.com/go-critic/go-critic/issues/845 | ||
- ifElseChain | ||
- octalLiteral | ||
- whyNoLint | ||
- wrapperFunc | ||
- importShadow | ||
- unnamedResult | ||
- unnecessaryBlock | ||
settings: | ||
rangeValCopy: | ||
sizeThreshold: 256 | ||
gocyclo: | ||
min-complexity: 15 | ||
goimports: | ||
local-prefixes: github.com/golangci/golangci-lint | ||
golint: | ||
min-confidence: 0 | ||
govet: | ||
check-shadowing: true | ||
lll: | ||
line-length: 300 | ||
maligned: | ||
suggest-new: true | ||
misspell: | ||
locale: US | ||
nolintlint: | ||
allow-leading-space: true # don't require machine-readable nolint directives (i.e. with no leading space) | ||
allow-unused: false # report any unused nolint directives | ||
require-explanation: false # don't require an explanation for nolint directives | ||
require-specific: false # don't require nolint directives to be specific about which linter is being skipped | ||
|
||
linters: | ||
disable-all: true | ||
enable: | ||
- bodyclose | ||
- deadcode | ||
- depguard | ||
- dogsled | ||
- errcheck | ||
- gochecknoinits | ||
- goconst | ||
- gocritic | ||
- gocyclo | ||
- gofmt | ||
- goimports | ||
- golint | ||
- goprintffuncname | ||
- gosec | ||
- gosimple | ||
- govet | ||
- ineffassign | ||
- interfacer | ||
- lll | ||
- misspell | ||
- nakedret | ||
- nolintlint | ||
- rowserrcheck | ||
- scopelint | ||
- staticcheck | ||
- structcheck | ||
- stylecheck | ||
- typecheck | ||
- unconvert | ||
- unparam | ||
- unused | ||
- varcheck | ||
- whitespace |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
language: go | ||
|
||
go: | ||
- 1.15.x | ||
|
||
before_script: | ||
- make lint-install |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
MAKEFLAGS += --warn-undefined-variables | ||
SHELL := bash | ||
.SHELLFLAGS := -euo pipefail -c | ||
.DEFAULT_GOAL := all | ||
|
||
BIN_DIR ?= $(shell go env GOPATH)/bin | ||
export PATH := $(PATH):$(BIN_DIR) | ||
|
||
.PHONY: deps | ||
deps: ## download go modules | ||
go mod download | ||
|
||
.PHONY: fmt | ||
fmt: lint/check ## ensure consistent code style | ||
go run oss.indeed.com/go/go-groups -w . | ||
gofmt -s -w . | ||
golangci-lint run --fix > /dev/null 2>&1 || true | ||
go mod tidy | ||
|
||
.PHONY: lint/check | ||
lint/check: | ||
@if ! golangci-lint --version > /dev/null 2>&1; then \ | ||
echo -e "\033[0;33mgolangci-lint is not installed: run \`\033[0;32mmake lint-install\033[0m\033[0;33m\` or install it from https://golangci-lint.run\033[0m"; \ | ||
exit 1; \ | ||
fi | ||
|
||
.PHONY: lint-install | ||
lint-install: ## installs golangci-lint to the go bin dir | ||
@if ! golangci-lint --version > /dev/null 2>&1; then \ | ||
echo "Installing golangci-lint"; \ | ||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(BIN_DIR) v1.32.0; \ | ||
fi | ||
|
||
.PHONY: lint | ||
lint: lint/check ## run golangci-lint | ||
golangci-lint run | ||
@if [ -n "$$(gofmt -s -l .)" ] || [ -n "$$(go run oss.indeed.com/go/go-groups -d .)" ]; then \ | ||
echo -e "\033[0;33mdetected fmt problems: run \`\033[0;32mmake fmt\033[0m\033[0;33m\`\033[0m"; \ | ||
exit 1; \ | ||
fi | ||
|
||
.PHONY: test | ||
test: lint ## run go tests | ||
go run oss.indeed.com/go/go-opine test -coverprofile=cover.out -junit=report.xml | ||
|
||
.PHONY: all | ||
all: test | ||
|
||
.PHONY: help | ||
help: ## displays this help message | ||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_\/-]+:.*?## / {printf "\033[34m%-12s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) | \ | ||
sort | \ | ||
grep -v '#' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
osslifecycle=experimental | ||
osslifecycle=active |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,134 @@ | ||
# Indeed Open Source Repository Template | ||
libhealth | ||
========= | ||
[](https://goreportcard.com/report/oss.indeed.com/go/libhealth) | ||
[](https://travis-ci.com/indeedeng/libhealth) | ||
[](https://godoc.org/oss.indeed.com/go/libhealth) | ||
[](OSSMETADATA) | ||
[](LICENSE) | ||
|
||
 | ||
libhealth is a Golang library that provides flexible components to report the current state of external systems | ||
that an application depends on, as well as the current health of any internal aspects of the application. | ||
|
||
## components | ||
### dependency set | ||
Most applications have a set of dependencies whose health state needs to be tracked. A dependency set can | ||
compute the health state of an application from health monitors and their associated urgency levels. | ||
|
||
A default template repository we can use to bootstrap new open source projects. Replace this text with an overview of your project and what it does. | ||
The final health state of an application is computed from their status and urgency. A weakly coupled component | ||
in a complete outage results in a MINOR healthcheck outage state. A more strongly coupled or required | ||
component in an OUTAGE results in correspondingly higher outage states. The matrix below illustrates | ||
the relationship between the "urgency" of a component and computed status. | ||
|
||
Update the OSSLifeCycle shield above with the name of your repository (i.e. replace `default-template.svg` with `YOUR-REPOSITORY-NAME.svg`) | ||
| Status \ Urgency | REQUIRED | STRONG | WEAK | NONE | | ||
| ---------------- | -------- | ------ | ------ | ---- | | ||
| OUTAGE | OUTAGE | MAJOR | MINOR | OK | | ||
| MAJOR | MAJOR | MAJOR | MINOR | OK | | ||
| MINOR | MINOR | MINOR | MINOR | OK | | ||
| OK | OK | OK | OK | OK | | ||
|
||
Your README.md should contain the following sections: | ||
|
||
## Getting Started | ||
While applications can implement a dependency set of their own, a basic dependency set is provided which fits | ||
most use cases. Applications typically need one basic dependency set, and libhealth will update monitors | ||
and track their state in background goroutines. | ||
|
||
How does a user get started using this project? | ||
Example: | ||
```go | ||
import "oss.indeed.com/go/libhealth" | ||
|
||
## Getting Help | ||
func setupHealth() { | ||
deps := libhealth.NewBasicDependencySet() | ||
deps.Register(libhealth.NewMonitor( | ||
"health-monitor-name", | ||
"monitor description", | ||
"https://docs/to/your/monitor", | ||
libhealth.WEAK, | ||
func(ctx context.Context) libhealth.Health { | ||
// calculate monitor health here | ||
return libhealth.NewHealth(libhealth.OK, "everything is fine") | ||
})) | ||
} | ||
``` | ||
|
||
How does a user ask questions if they are stuck? | ||
### healthcheck endpoints | ||
Typical applications expose several healthcheck endpoints to an HTTP server for tracking their state. | ||
libhealth provides two classes of endpoints: public "info" and private healthcheck endpoints. The | ||
public endpoints are typically consumed by other software (e.g loadbalancers, HAProxy, nginx, etc). | ||
They return a 200 or 500 status code and very simple json payload indicating the source of the response. | ||
|
||
## How To Contribute | ||
An example response to the /info endpoints is shown below: | ||
```json | ||
{ | ||
"condition" : "OK", | ||
"duration" : 0, | ||
"hostname" : "aus-worker11" | ||
} | ||
``` | ||
|
||
What does a user need to know if they want to start contributing? If this information is extensive, capture it in a CONTRIBUTING.md file and link to that file here. | ||
The private healthcheck endpoints expose significantly more information about the runtime and environment | ||
of the process to aid debugging outages, however these should only be exposed to whitelisted ips. | ||
If this is not possible, consider only exposing endpoints for the less verbose public endpoints. | ||
|
||
## Project Maintainers | ||
A common pattern is to expose the following endpoints: | ||
``` | ||
/info/healthcheck | ||
/info/healthcheck/live | ||
/private/healthcheck | ||
/private/healthcheck/live | ||
``` | ||
|
||
Who are the project maintainers, and how can they be reached? | ||
HTTP handlers are provided by libhealth for serving each of these routes: | ||
```go | ||
import "oss.indeed.com/go/libhealth" | ||
|
||
## Code of Conduct | ||
This project is governed by the [Contributor Covenant v 1.4.1](CODE_OF_CONDUCT.md). (Review the Code of Conduct and remove this sentence before publishing your project.) | ||
func healthRouter(d libhealth.DependencySet) *http.ServeMux { | ||
router := http.NewServeMux() | ||
router.Handle("/info/healthcheck", libhealth.NewInfo(d)) | ||
router.Handle("/info/healthcheck/live", libhealth.NewInfo(d)) | ||
router.Handle("/private/healthcheck", libhealth.NewPrivate("my-app-name", d)) | ||
router.Handle("/private/healthcheck/live", libhealth.NewPrivate("my-app-name", d)) | ||
return router | ||
} | ||
``` | ||
|
||
Alternatively, you can use the helper function `WrapServeMux`, which will register all these handlers for you: | ||
```go | ||
import "oss.indeed.com/go/libhealth" | ||
|
||
... | ||
router := http.NewServeMux() | ||
libhealth.WrapServeMux(router, "my-app-name", dependencies) | ||
``` | ||
|
||
# Contributing | ||
|
||
We welcome contributions! Feel free to help make `libhealth` better. | ||
|
||
### Process | ||
|
||
- Open an issue and describe the desired feature / bug fix before making | ||
changes. It's useful to get a second pair of eyes before investing development | ||
effort. | ||
- Make the change. If adding a new feature, remember to provide tests that | ||
demonstrate the new feature works, including any error paths. If contributing | ||
a bug fix, add tests that demonstrate the erroneous behavior is fixed. | ||
- Open a pull request. Automated CI tests will run. If the tests fail, please | ||
make changes to fix the behavior, and repeat until the tests pass. | ||
- Once everything looks good, one of the indeedeng members will review the | ||
PR and provide feedback. | ||
|
||
# Maintainers | ||
|
||
The `oss.indeed.com/go/libhealth` project is maintained by Indeed Engineering. | ||
|
||
While we are always busy helping people get jobs, we will try to respond to | ||
GitHub issues, pull requests, and questions within a couple of business days. | ||
|
||
# Code of Conduct | ||
|
||
`oss.indeed.com/go/libhealth` is governed by the [Contributer Covenant v1.4.1](CODE_OF_CONDUCT.md) | ||
|
||
For more information please contact [email protected]. | ||
|
||
## License | ||
This project uses the [Apache 2.0](LICENSE) license. (Update this and the LICENSE file if your project uses a different license.) | ||
|
||
The `oss.indeed.com/go/libhealth` project is open source under the [Apache 2.0](LICENSE) license. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
count | ||
===== | ||
|
||
About | ||
----- | ||
A count is used to keep track of accumulating values over time intervals, | ||
and export them to /private/v as a list of numbers. Thresholds can be | ||
defined to be evaluated over the values, and plugged into the healthcheck | ||
framework. | ||
|
||
Example | ||
------- | ||
|
||
Create a count.IntCounter, and set a name for the varexp name, | ||
with 20 buckets representing 5 minutes of time each. | ||
|
||
```go | ||
counter, err := count.Ints("my-counter", libhealth.SizeFiveMinutes, 20) | ||
``` | ||
|
||
Call .Increment to increment current value. | ||
|
||
```go | ||
for range time.Tick(30 * time.Second) { | ||
nodes, err := fetchLiveNodes(e.maxNodeAge, e.session) | ||
if err != nil { | ||
counter.Increment(1) | ||
log.Warnf("session fetchLiveNodes error: %v", err) | ||
} | ||
... | ||
} | ||
``` | ||
|
||
Define a Healthcheck using count.Threshold. | ||
|
||
```go | ||
// DependencySet creates the set of healthchecks for background fetch task | ||
func DependencySet(counter counts.IntCounter) libhealth.DependencySet { | ||
// thresholds for rate of fetch errors (1 fetch per 30 seconds, buckets of 5 minutes) | ||
counter.Set(count.MaxIntThreshold{ | ||
Threshold: 1, | ||
Severity: health.MINOR, | ||
Description: "failed to fetch data once in the last 5 minutes", | ||
}).Set(count.MaxSumIntThreshold{ | ||
Threshold: 5, | ||
Severity: health.MAJOR, | ||
Description: "failed to fetch data>= 5 times in 5 minutes", | ||
}).Set(count.MaxSumIntThreshold{ | ||
Threshold: 10, | ||
Severity: health.OUTAGE, | ||
Description: "failed to fetch data >= 10 times in 5 minutes", | ||
}) | ||
|
||
return libhealth.NewBasicDependencySet( | ||
libhealth.NewMonitor( | ||
"rate-of-data-fetch-errors", | ||
"if this is non-zero we cannot talk to the service", | ||
"https://example.com/TODO", | ||
libhealth.STRONG, | ||
counter.Health, | ||
), | ||
) | ||
} | ||
``` |
Oops, something went wrong.