diff --git a/.circleci/config.yml b/.circleci/config.yml index a62505e..95f2391 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ version: 2.1 orbs: go: circleci/go@0.2.0 - prometheus: prometheus/prometheus@0.11.0 + prometheus: prometheus/prometheus@0.15.0 jobs: test: parameters: @@ -63,6 +63,8 @@ workflows: name: go-1-16 go_version: "1.16" run_lint: true - # Style and unused/missing packages are only checked against - # the latest supported Go version. + - test: + name: go-1-17 + go_version: "1.17" + run_lint: true run_style_and_unused: true diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..f96c76a --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,31 @@ +name: golangci-lint +on: + push: + paths: + - "go.sum" + - "go.mod" + - "**.go" + - "scripts/errcheck_excludes.txt" + - ".github/workflows/golangci-lint.yml" + - ".golangci.yml" + pull_request: + paths: + - "go.sum" + - "go.mod" + - "**.go" + - "scripts/errcheck_excludes.txt" + - ".github/workflows/golangci-lint.yml" + - ".golangci.yml" + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Lint + uses: golangci/golangci-lint-action@v2 + with: + version: v1.42.0 diff --git a/Makefile.common b/Makefile.common index a1b1ca4..bf7304e 100644 --- a/Makefile.common +++ b/Makefile.common @@ -78,17 +78,23 @@ ifneq ($(shell which gotestsum),) endif endif -PROMU_VERSION ?= 0.12.0 +PROMU_VERSION ?= 0.13.0 PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v1.39.0 +GOLANGCI_LINT_VERSION ?= v1.42.0 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386)) - GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint + # If we're in CI and there is an Actions file, that means the linter + # is being run in Actions, so we don't need to run it here. + ifeq (,$(CIRCLE_JOB)) + GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint + else ifeq (,$(wildcard .github/workflows/golangci-lint.yml)) + GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint + endif endif endif @@ -154,7 +160,7 @@ endif update-go-deps: @echo ">> updating Go dependencies" @for m in $$($(GO) list -mod=readonly -m -f '{{ if and (not .Indirect) (not .Main)}}{{.Path}}{{end}}' all); do \ - $(GO) get $$m; \ + $(GO) get -d $$m; \ done GO111MODULE=$(GO111MODULE) $(GO) mod tidy ifneq (,$(wildcard vendor)) diff --git a/api/prometheus/v1/api.go b/api/prometheus/v1/api.go index 0c8de07..857512f 100644 --- a/api/prometheus/v1/api.go +++ b/api/prometheus/v1/api.go @@ -139,6 +139,7 @@ const ( epBuildinfo = apiPrefix + "/status/buildinfo" epRuntimeinfo = apiPrefix + "/status/runtimeinfo" epTSDB = apiPrefix + "/status/tsdb" + epWalReplay = apiPrefix + "/status/walreplay" ) // AlertState models the state of an alert. @@ -261,6 +262,8 @@ type API interface { Metadata(ctx context.Context, metric string, limit string) (map[string][]Metadata, error) // TSDB returns the cardinality statistics. TSDB(ctx context.Context) (TSDBResult, error) + // WalReplay returns the current replay status of the wal. + WalReplay(ctx context.Context) (WalReplayStatus, error) } // AlertsResult contains the result from querying the alerts endpoint. @@ -303,8 +306,6 @@ type RuntimeinfoResult struct { CWD string `json:"CWD"` ReloadConfigSuccess bool `json:"reloadConfigSuccess"` LastConfigTime time.Time `json:"lastConfigTime"` - ChunkCount int `json:"chunkCount"` - TimeSeriesCount int `json:"timeSeriesCount"` CorruptionCount int `json:"corruptionCount"` GoroutineCount int `json:"goroutineCount"` GOMAXPROCS int `json:"GOMAXPROCS"` @@ -431,10 +432,27 @@ type queryResult struct { // TSDBResult contains the result from querying the tsdb endpoint. type TSDBResult struct { - SeriesCountByMetricName []Stat `json:"seriesCountByMetricName"` - LabelValueCountByLabelName []Stat `json:"labelValueCountByLabelName"` - MemoryInBytesByLabelName []Stat `json:"memoryInBytesByLabelName"` - SeriesCountByLabelValuePair []Stat `json:"seriesCountByLabelValuePair"` + HeadStats TSDBHeadStats `json:"headStats"` + SeriesCountByMetricName []Stat `json:"seriesCountByMetricName"` + LabelValueCountByLabelName []Stat `json:"labelValueCountByLabelName"` + MemoryInBytesByLabelName []Stat `json:"memoryInBytesByLabelName"` + SeriesCountByLabelValuePair []Stat `json:"seriesCountByLabelValuePair"` +} + +// TSDBHeadStats contains TSDB stats +type TSDBHeadStats struct { + NumSeries int `json:"numSeries"` + NumLabelPairs int `json:"numLabelPairs"` + ChunkCount int `json:"chunkCount"` + MinTime int `json:"minTime"` + MaxTime int `json:"maxTime"` +} + +// WalReplayStatus represents the wal replay status. +type WalReplayStatus struct { + Min int `json:"min"` + Max int `json:"max"` + Current int `json:"current"` } // Stat models information about statistic value. @@ -984,6 +1002,23 @@ func (h *httpAPI) TSDB(ctx context.Context) (TSDBResult, error) { return res, json.Unmarshal(body, &res) } +func (h *httpAPI) WalReplay(ctx context.Context) (WalReplayStatus, error) { + u := h.client.URL(epWalReplay, nil) + + req, err := http.NewRequest(http.MethodGet, u.String(), nil) + if err != nil { + return WalReplayStatus{}, err + } + + _, body, _, err := h.client.Do(ctx, req) + if err != nil { + return WalReplayStatus{}, err + } + + var res WalReplayStatus + return res, json.Unmarshal(body, &res) +} + func (h *httpAPI) QueryExemplars(ctx context.Context, query string, startTime time.Time, endTime time.Time) ([]ExemplarQueryResult, error) { u := h.client.URL(epQueryExemplars, nil) q := u.Query() diff --git a/api/prometheus/v1/api_test.go b/api/prometheus/v1/api_test.go index 4197d42..e7d0b4c 100644 --- a/api/prometheus/v1/api_test.go +++ b/api/prometheus/v1/api_test.go @@ -230,6 +230,13 @@ func TestAPIs(t *testing.T) { } } + doWalReply := func() func() (interface{}, Warnings, error) { + return func() (interface{}, Warnings, error) { + v, err := promAPI.WalReplay(context.Background()) + return v, nil, err + } + } + doQueryExemplars := func(query string, startTime time.Time, endTime time.Time) func() (interface{}, Warnings, error) { return func() (interface{}, Warnings, error) { v, err := promAPI.QueryExemplars(context.Background(), query, startTime, endTime) @@ -696,8 +703,6 @@ func TestAPIs(t *testing.T) { "CWD": "/prometheus", "reloadConfigSuccess": true, "lastConfigTime": "2020-05-18T15:52:56Z", - "chunkCount": 72692, - "timeSeriesCount": 18476, "corruptionCount": 0, "goroutineCount": 217, "GOMAXPROCS": 2, @@ -710,8 +715,6 @@ func TestAPIs(t *testing.T) { CWD: "/prometheus", ReloadConfigSuccess: true, LastConfigTime: time.Date(2020, 5, 18, 15, 52, 56, 0, time.UTC), - ChunkCount: 72692, - TimeSeriesCount: 18476, CorruptionCount: 0, GoroutineCount: 217, GOMAXPROCS: 2, @@ -1145,6 +1148,13 @@ func TestAPIs(t *testing.T) { reqMethod: "GET", reqPath: "/api/v1/status/tsdb", inRes: map[string]interface{}{ + "headStats": map[string]interface{}{ + "numSeries": 18476, + "numLabelPairs": 4301, + "chunkCount": 72692, + "minTime": 1634644800304, + "maxTime": 1634650590304, + }, "seriesCountByMetricName": []interface{}{ map[string]interface{}{ "name": "kubelet_http_requests_duration_seconds_bucket", @@ -1171,6 +1181,13 @@ func TestAPIs(t *testing.T) { }, }, res: TSDBResult{ + HeadStats: TSDBHeadStats{ + NumSeries: 18476, + NumLabelPairs: 4301, + ChunkCount: 72692, + MinTime: 1634644800304, + MaxTime: 1634650590304, + }, SeriesCountByMetricName: []Stat{ { Name: "kubelet_http_requests_duration_seconds_bucket", @@ -1198,6 +1215,30 @@ func TestAPIs(t *testing.T) { }, }, + { + do: doWalReply(), + reqMethod: "GET", + reqPath: "/api/v1/status/walreplay", + inErr: fmt.Errorf("some error"), + err: fmt.Errorf("some error"), + }, + + { + do: doWalReply(), + reqMethod: "GET", + reqPath: "/api/v1/status/walreplay", + inRes: map[string]interface{}{ + "min": 2, + "max": 5, + "current": 40, + }, + res: WalReplayStatus{ + Min: 2, + Max: 5, + Current: 40, + }, + }, + { do: doQueryExemplars("tns_request_duration_seconds_bucket", testTime.Add(-1*time.Minute), testTime), reqMethod: "GET", diff --git a/examples/random/main.go b/examples/random/main.go index 334fd8a..e721e91 100644 --- a/examples/random/main.go +++ b/examples/random/main.go @@ -29,48 +29,46 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) -var ( - addr = flag.String("listen-address", ":8080", "The address to listen on for HTTP requests.") - uniformDomain = flag.Float64("uniform.domain", 0.0002, "The domain for the uniform distribution.") - normDomain = flag.Float64("normal.domain", 0.0002, "The domain for the normal distribution.") - normMean = flag.Float64("normal.mean", 0.00001, "The mean for the normal distribution.") - oscillationPeriod = flag.Duration("oscillation-period", 10*time.Minute, "The duration of the rate oscillation period.") -) - -var ( - // Create a summary to track fictional interservice RPC latencies for three - // distinct services with different latency distributions. These services are - // differentiated via a "service" label. - rpcDurations = prometheus.NewSummaryVec( - prometheus.SummaryOpts{ - Name: "rpc_durations_seconds", - Help: "RPC latency distributions.", - Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, - }, - []string{"service"}, +func main() { + var ( + addr = flag.String("listen-address", ":8080", "The address to listen on for HTTP requests.") + uniformDomain = flag.Float64("uniform.domain", 0.0002, "The domain for the uniform distribution.") + normDomain = flag.Float64("normal.domain", 0.0002, "The domain for the normal distribution.") + normMean = flag.Float64("normal.mean", 0.00001, "The mean for the normal distribution.") + oscillationPeriod = flag.Duration("oscillation-period", 10*time.Minute, "The duration of the rate oscillation period.") + ) + + flag.Parse() + + var ( + // Create a summary to track fictional interservice RPC latencies for three + // distinct services with different latency distributions. These services are + // differentiated via a "service" label. + rpcDurations = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Name: "rpc_durations_seconds", + Help: "RPC latency distributions.", + Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, + }, + []string{"service"}, + ) + // The same as above, but now as a histogram, and only for the normal + // distribution. The buckets are targeted to the parameters of the + // normal distribution, with 20 buckets centered on the mean, each + // half-sigma wide. + rpcDurationsHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{ + Name: "rpc_durations_histogram_seconds", + Help: "RPC latency distributions.", + Buckets: prometheus.LinearBuckets(*normMean-5**normDomain, .5**normDomain, 20), + SparseBucketsFactor: 1.1, + }) ) - // The same as above, but now as a histogram, and only for the normal - // distribution. The buckets are targeted to the parameters of the - // normal distribution, with 20 buckets centered on the mean, each - // half-sigma wide. - rpcDurationsHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "rpc_durations_histogram_seconds", - Help: "RPC latency distributions.", - Buckets: prometheus.LinearBuckets(*normMean-5**normDomain, .5**normDomain, 20), - SparseBucketsFactor: 1.1, - }) -) -func init() { // Register the summary and the histogram with Prometheus's default registry. prometheus.MustRegister(rpcDurations) prometheus.MustRegister(rpcDurationsHistogram) // Add Go module build info. prometheus.MustRegister(prometheus.NewBuildInfoCollector()) -} - -func main() { - flag.Parse() start := time.Now() diff --git a/go.mod b/go.mod index baf3fc9..a676a4f 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/prometheus/client_golang require ( github.com/beorn7/perks v1.0.1 - github.com/cespare/xxhash/v2 v2.1.1 + github.com/cespare/xxhash/v2 v2.1.2 github.com/golang/protobuf v1.4.3 github.com/json-iterator/go v1.1.11 github.com/prometheus/client_model v0.2.1-0.20210624201024-61b6c1aac064 diff --git a/go.sum b/go.sum index 7afad65..16eddee 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,9 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= diff --git a/prometheus/collectors/dbstats_collector_go115.go b/prometheus/collectors/dbstats_collector_go115.go index a6e6268..6d152fb 100644 --- a/prometheus/collectors/dbstats_collector_go115.go +++ b/prometheus/collectors/dbstats_collector_go115.go @@ -11,6 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build go1.15 // +build go1.15 package collectors diff --git a/prometheus/collectors/dbstats_collector_pre_go115.go b/prometheus/collectors/dbstats_collector_pre_go115.go index 0568aff..6523506 100644 --- a/prometheus/collectors/dbstats_collector_pre_go115.go +++ b/prometheus/collectors/dbstats_collector_pre_go115.go @@ -11,6 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !go1.15 // +build !go1.15 package collectors diff --git a/prometheus/process_collector_other.go b/prometheus/process_collector_other.go index 3117461..2dc3660 100644 --- a/prometheus/process_collector_other.go +++ b/prometheus/process_collector_other.go @@ -11,6 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !windows // +build !windows package prometheus diff --git a/prometheus/process_collector_test.go b/prometheus/process_collector_test.go index 7b19c5e..3a604ab 100644 --- a/prometheus/process_collector_test.go +++ b/prometheus/process_collector_test.go @@ -11,6 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build linux // +build linux package prometheus diff --git a/prometheus/push/push_test.go b/prometheus/push/push_test.go index 99155b0..e55e966 100644 --- a/prometheus/push/push_test.go +++ b/prometheus/push/push_test.go @@ -199,7 +199,7 @@ func TestPush(t *testing.T) { Collector(metric1). Collector(metric2). Push(); err == nil { - t.Error("push with empty job succeded") + t.Error("push with empty job succeeded") } else { if got, want := err, errJobEmpty; got != want { t.Errorf("got error %q, want %q", got, want)