-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update module github.com/prometheus/client_golang to v1.20.5 #259
base: master
Are you sure you want to change the base?
Conversation
bcaca70
to
394ff04
Compare
394ff04
to
7e811d6
Compare
839500b
to
02f9d8e
Compare
01fd60a
to
0ad4b6a
Compare
78dd2dd
to
fa1dc8e
Compare
fa1dc8e
to
66d535a
Compare
ℹ Artifact update noticeFile name: go.modIn order to perform the update(s) described in the table above, Renovate ran the
Details:
|
66d535a
to
43c8c94
Compare
[puLL-Merge] - prometheus/client_golang@v1.19.0..v1.20.0 DescriptionThis PR updates the client_golang library with various improvements and new features. The main changes include updates to Go collector metrics, enhancements to HTTP compression support, and improvements to testing and documentation. ChangesChanges
Possible Issues
Security HotspotsNo significant security issues were identified in this change. |
b47f6cc
to
d289dd4
Compare
[puLL-Merge] - prometheus/client_golang@v1.19.0..v1.20.1 DescriptionThis PR updates the client_golang repository with several improvements and changes. The main motivations appear to be updating Go versions, improving test coverage, adding new features, and general maintenance. ChangesChanges
Possible Issues
Security Hotspots
|
d289dd4
to
fecb333
Compare
[puLL-Merge] - prometheus/client_golang@v1.19.0..v1.20.2 DescriptionThis PR updates various aspects of the client_golang project, including dependency updates, code improvements, and the addition of new features. The main changes include updating Go versions, improving the Go collector, adding support for zstd compression, and enhancing the test suite. ChangesChanges
Possible Issues
Security HotspotsNo significant security issues were identified in this change. However, the introduction of new dependencies and compression algorithms (zstd) should be thoroughly tested to ensure they don't introduce any vulnerabilities. |
fecb333
to
677b0f1
Compare
[puLL-Merge] - prometheus/client_golang@v1.19.0..v1.20.3 DescriptionThis PR makes several significant changes to the client_golang repository, including updating dependencies, adding new features, refactoring existing code, and improving test coverage. The changes span across multiple files and introduce new functionality while also addressing some existing issues. ChangesChanges
Possible Issues
Security Hotspots
|
677b0f1
to
fb7031f
Compare
fb7031f
to
cae9fea
Compare
cae9fea
to
2117bb7
Compare
[puLL-Merge] - prometheus/client_golang@v1.19.0..v1.20.4 DescriptionThis PR introduces several significant changes to the ChangesChanges
Possible Issues
Security Hotspots
Overall, this PR represents a significant update to the |
2117bb7
to
4c9abac
Compare
4c9abac
to
7293f3f
Compare
095d2f7
to
07c3e83
Compare
81363d2
to
449fb2b
Compare
449fb2b
to
ef2e099
Compare
[puLL-Merge] - prometheus/client_golang@v1.19.0..v1.20.5 Diffdiff --git .github/workflows/automerge-dependabot.yml .github/workflows/automerge-dependabot.yml
index fc664a8a6..7b2687ec3 100644
--- .github/workflows/automerge-dependabot.yml
+++ .github/workflows/automerge-dependabot.yml
@@ -1,6 +1,10 @@
name: Dependabot auto-merge
on: pull_request
+concurrency:
+ group: ${{ github.workflow }}-${{ (github.event.pull_request && github.event.pull_request.number) || github.ref || github.run_id }}
+ cancel-in-progress: true
+
permissions:
contents: write
pull-requests: write
@@ -12,7 +16,7 @@ jobs:
steps:
- name: Dependabot metadata
id: metadata
- uses: dependabot/fetch-metadata@c9c4182bf1b97f5224aee3906fd373f6b61b4526 # v1.6.0
+ uses: dependabot/fetch-metadata@dbb049abf0d677abbd7f7eee0375145b417fdd34 # v2.2.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Enable auto-merge for Dependabot PRs
diff --git .github/workflows/codeql-analysis.yml .github/workflows/codeql-analysis.yml
index 79a71ad3f..4260139cb 100644
--- .github/workflows/codeql-analysis.yml
+++ .github/workflows/codeql-analysis.yml
@@ -20,6 +20,10 @@ on:
schedule:
- cron: '31 21 * * 6'
+concurrency:
+ group: ${{ github.workflow }}-${{ (github.event.pull_request && github.event.pull_request.number) || github.ref || github.run_id }}
+ cancel-in-progress: true
+
# Minimal permissions to be inherited by any job that don't declare it's own permissions
permissions:
contents: read
@@ -46,7 +50,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3.23.2
+ uses: github/codeql-action/init@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -57,7 +61,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3.23.2
+ uses: github/codeql-action/autobuild@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -71,4 +75,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@b7bf0a3ed3ecfa44160715d7c442788f65f0f923 # v3.23.2
+ uses: github/codeql-action/analyze@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15
diff --git a/.github/workflows/container_description.yml b/.github/workflows/container_description.yml
new file mode 100644
index 000000000..8ddbc34ae
--- /dev/null
+++ .github/workflows/container_description.yml
@@ -0,0 +1,57 @@
+---
+name: Push README to Docker Hub
+on:
+ push:
+ paths:
+ - "README.md"
+ - "README-containers.md"
+ - ".github/workflows/container_description.yml"
+ branches: [ main, master ]
+
+permissions:
+ contents: read
+
+jobs:
+ PushDockerHubReadme:
+ runs-on: ubuntu-latest
+ name: Push README to Docker Hub
+ if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks.
+ steps:
+ - name: git checkout
+ uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
+ - name: Set docker hub repo name
+ run: echo "DOCKER_REPO_NAME=$(make docker-repo-name)" >> $GITHUB_ENV
+ - name: Push README to Dockerhub
+ uses: christian-korneck/update-container-description-action@d36005551adeaba9698d8d67a296bd16fa91f8e8 # v1
+ env:
+ DOCKER_USER: ${{ secrets.DOCKER_HUB_LOGIN }}
+ DOCKER_PASS: ${{ secrets.DOCKER_HUB_PASSWORD }}
+ with:
+ destination_container_repo: ${{ env.DOCKER_REPO_NAME }}
+ provider: dockerhub
+ short_description: ${{ env.DOCKER_REPO_NAME }}
+ # Empty string results in README-containers.md being pushed if it
+ # exists. Otherwise, README.md is pushed.
+ readme_file: ''
+
+ PushQuayIoReadme:
+ runs-on: ubuntu-latest
+ name: Push README to quay.io
+ if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks.
+ steps:
+ - name: git checkout
+ uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
+ - name: Set quay.io org name
+ run: echo "DOCKER_REPO=$(echo quay.io/${GITHUB_REPOSITORY_OWNER} | tr -d '-')" >> $GITHUB_ENV
+ - name: Set quay.io repo name
+ run: echo "DOCKER_REPO_NAME=$(make docker-repo-name)" >> $GITHUB_ENV
+ - name: Push README to quay.io
+ uses: christian-korneck/update-container-description-action@d36005551adeaba9698d8d67a296bd16fa91f8e8 # v1
+ env:
+ DOCKER_APIKEY: ${{ secrets.QUAY_IO_API_TOKEN }}
+ with:
+ destination_container_repo: ${{ env.DOCKER_REPO_NAME }}
+ provider: quay
+ # Empty string results in README-containers.md being pushed if it
+ # exists. Otherwise, README.md is pushed.
+ readme_file: ''
diff --git a/.github/workflows/dagger-golangci-lint.yml b/.github/workflows/dagger-golangci-lint.yml
new file mode 100644
index 000000000..0e9fa3cf0
--- /dev/null
+++ .github/workflows/dagger-golangci-lint.yml
@@ -0,0 +1,32 @@
+---
+name: dagger-golangci-lint
+on:
+ push:
+ paths:
+ - "go.sum"
+ - "go.mod"
+ - "**.go"
+ - "scripts/errcheck_excludes.txt"
+ - ".github/workflows/golangci-lint.yml"
+ - ".golangci.yml"
+ pull_request:
+
+permissions: # added using https://github.com/step-security/secure-repo
+ contents: read
+
+jobs:
+ golangci:
+ permissions:
+ contents: read # for actions/checkout to fetch code
+ pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
+ name: lint
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
+ - name: Lint
+ uses: dagger/dagger-for-github@v5
+ with:
+ version: "latest"
+ verb: call
+ args: -vvv --src . make --args lint
diff --git .github/workflows/go.yml .github/workflows/go.yml
index c72b96cc7..b9be9db3a 100644
--- .github/workflows/go.yml
+++ .github/workflows/go.yml
@@ -1,46 +1,61 @@
----
-name: Go
-on:
- pull_request:
- push:
- branches:
- - main
- - "release-*"
-
-# Minimal permissions to be inherited by any job that don't declare it's own permissions
-permissions:
- contents: read
-
-jobs:
- test:
- name: Tests
- runs-on: ubuntu-latest
-
- strategy:
- matrix:
- go_version: ["1.20", "1.21", "1.22"]
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v4.1.1
-
- - name: Set up Go ${{ matrix.go_version }}
- uses: actions/setup-go@v5.0.0
- with:
- go-version: ${{ matrix.go_version }}
-
- - name: Cache Go modules
- id: cache
- uses: actions/cache@v4
- with:
- path: ~/go/pkg/mod
- key: v1-go${{ matrix.go_version }}
-
- - name: Run tests and check license
- run: make check_license test
- env:
- CI: true
-
- - name: Run style and unused
- if: ${{ matrix.go_version == '1.20' }}
- run: make style unused
+---
+name: Go
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+ - "release-*"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ (github.event.pull_request && github.event.pull_request.number) || github.ref || github.run_id }}
+ cancel-in-progress: true
+
+# Minimal permissions to be inherited by any job that don't declare it's own permissions
+permissions:
+ contents: read
+
+jobs:
+
+ supportedVersions:
+ name: Fetch supported Go versions
+ runs-on: ubuntu-latest
+ outputs:
+ supported_versions: ${{ steps.matrix.outputs.supported_versions }}
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - name: Read supported_go_versions.txt
+ id: matrix
+ run: |
+ versions=$(cat supported_go_versions.txt)
+ matrix="[$(echo "$versions" | sed 's/\(.*\)/"\1"/' | paste -s -d,)]"
+ echo "supported_versions=$matrix" >> $GITHUB_OUTPUT
+
+ test:
+ name: Tests
+ runs-on: ubuntu-latest
+ needs: supportedVersions
+
+ strategy:
+ matrix:
+ go_version: ${{ fromJSON(needs.supportedVersions.outputs.supported_versions) }}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4.1.1
+
+ - name: Run tests and check license
+ uses: dagger/dagger-for-github@v5
+ with:
+ version: "latest"
+ verb: call
+ args: -vvv --src . make --go-version ${{matrix.go_version}} --args 'check_license test'
+
+ - name: Run style and unused
+ uses: dagger/dagger-for-github@v6
+ if: ${{ matrix.go_version == '1.20' }}
+ with:
+ version: "latest"
+ verb: call
+ args: -vvv --src . make --args 'check_license test'
diff --git .github/workflows/golangci-lint.yml .github/workflows/golangci-lint.yml
index fe63ff3fa..746831a86 100644
--- .github/workflows/golangci-lint.yml
+++ .github/workflows/golangci-lint.yml
@@ -24,15 +24,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- - name: install Go
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ - name: Install Go
+ uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
with:
- go-version: 1.20.x
+ go-version: 1.22.x
- name: Install snmp_exporter/generator dependencies
run: sudo apt-get update && sudo apt-get -y install libsnmp-dev
if: github.repository == 'prometheus/snmp_exporter'
- name: Lint
- uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0
+ uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0
with:
- version: v1.54.2
+ args: --verbose
+ version: v1.59.1
diff --git a/.github/workflows/update-go-versions.yml b/.github/workflows/update-go-versions.yml
new file mode 100644
index 000000000..f560564f2
--- /dev/null
+++ .github/workflows/update-go-versions.yml
@@ -0,0 +1,33 @@
+---
+name: Generate Metric files for new Go version
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: '0 0 1 * *'
+
+jobs:
+ update-go-versions:
+ name: Update Go Versions and Generate Tests
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Execute bash script
+ run: bash update-go-version.bash
+
+ # If there are no changes (i.e. no diff exists with the checked-out base branch),
+ # no pull request will be created and the action exits silently.
+ - name: Create a Pull Request
+ if: github.event_name != 'pull_request'
+ uses: peter-evans/create-pull-request@v6
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ commit-message: "Update Go Collector metrics for new Go version"
+ title: "chore: Update metrics for new Go version"
+ branch: update-metrics-for-new-go-version
+ base: main
+ draft: false
+ delete-branch: true
diff --git .golangci.yml .golangci.yml
index 8043d57b4..d2580cccc 100644
--- .golangci.yml
+++ .golangci.yml
@@ -1,9 +1,13 @@
---
run:
- deadline: 5m
+ timeout: 5m
skip-files:
# Skip autogenerated files.
- ^.*\.(pb|y)\.go$
+ - dagger/dagger.gen.go
+ skip-dirs:
+ - dagger/internal
+
output:
sort-results: true
diff --git CHANGELOG.md CHANGELOG.md
index a4574779c..654a8d039 100644
--- CHANGELOG.md
+++ CHANGELOG.md
@@ -1,8 +1,44 @@
## Unreleased
-## 1.19.0 / 2023-02-27
+## 1.20.5 / 2024-10-15
-The module `prometheus/common v0.48.0` introduced a bug when used together with client_golang. If your project uses client_golang and you want to use `prometheus/common v0.48.0` or higher, please update client_golang to v1.19.0.
+* [BUGFIX] testutil: Reverted #1424; functions using compareMetricFamilies are (again) only failing if filtered metricNames are in the expected input.
+
+## 1.20.4 / 2024-09-07
+
+* [BUGFIX] histograms: Fix possible data race when appending exemplars vs metrics gather. #1623
+
+## 1.20.3 / 2024-09-05
+
+* [BUGFIX] histograms: Fix possible data race when appending exemplars. #1608
+
+## 1.20.2 / 2024-08-23
+
+* [BUGFIX] promhttp: Unset Content-Encoding header when data is uncompressed. #1596
+
+## 1.20.1 / 2024-08-20
+
+* [BUGFIX] process-collector: Fixed unregistered descriptor error when using process collector with `PedanticRegistry` on linux machines. #1587
+
+## 1.20.0 / 2024-08-14
+
+* [CHANGE] :warning: go-collector: Remove `go_memstat_lookups_total` metric which was always 0; Go runtime stopped sharing pointer lookup statistics. #1577
+* [FEATURE] :warning: go-collector: Add 3 default metrics: `go_gc_gogc_percent`, `go_gc_gomemlimit_bytes` and `go_sched_gomaxprocs_threads` as those are recommended by the Go team. #1559
+* [FEATURE] go-collector: Add more information to all metrics' HELP e.g. the exact `runtime/metrics` sourcing each metric (if relevant). #1568 #1578
+* [FEATURE] testutil: Add CollectAndFormat method. #1503
+* [FEATURE] histograms: Add support for exemplars in native histograms. #1471
+* [FEATURE] promhttp: Add experimental support for `zstd` on scrape, controlled by the request `Accept-Encoding` header. #1496
+* [FEATURE] api/v1: Add `WithLimit` parameter to all API methods that supports it. #1544
+* [FEATURE] prometheus: Add support for created timestamps in constant histograms and constant summaries. #1537
+* [FEATURE] process-collector: Add network usage metrics: `process_network_receive_bytes_total` and `process_network_transmit_bytes_total`. #1555
+* [FEATURE] promlint: Add duplicated metric lint rule. #1472
+* [BUGFIX] promlint: Relax metric type in name linter rule. #1455
+* [BUGFIX] promhttp: Make sure server instrumentation wrapping supports new and future extra responseWriter methods. #1480
+* [BUGFIX] **breaking** testutil: Functions using compareMetricFamilies are now failing if filtered metricNames are not in the input. #1424 (reverted in 1.20.5)
+
+## 1.19.0 / 2024-02-27
+
+The module `prometheus/common v0.48.0` introduced an incompatibility when used together with client_golang (See https://github.com/prometheus/client_golang/pull/1448 for more details). If your project uses client_golang and you want to use `prometheus/common v0.48.0` or higher, please update client_golang to v1.19.0.
* [CHANGE] Minimum required go version is now 1.20 (we also test client_golang against new 1.22 version). #1445 #1449
* [FEATURE] collectors: Add version collector. #1422 #1427
diff --git CONTRIBUTING.md CONTRIBUTING.md
index e015a85cb..abc101b18 100644
--- CONTRIBUTING.md
+++ CONTRIBUTING.md
@@ -1,5 +1,7 @@
# Contributing
+Thank you for contributing to our project! Here are the steps and guidelines to follow when creating a pull request (PR).
+
Prometheus uses GitHub to manage reviews of pull requests.
* If you have a trivial fix or improvement, go ahead and create a pull request,
diff --git Makefile Makefile
index 1c5eb709d..2af8c5270 100644
--- Makefile
+++ Makefile
@@ -20,8 +20,22 @@ test: deps common-test
.PHONY: test-short
test-short: deps common-test-short
+# Overriding Makefile.common check_license target to add
+# dagger paths
+.PHONY: common-check_license
+common-check_license:
+ @echo ">> checking license header"
+ @licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*' ! -path './dagger/internal/*') ; do \
+ awk 'NR<=3' $$file | grep -Eq "(Copyright|generated|GENERATED)" || echo $$file; \
+ done); \
+ if [ -n "$${licRes}" ]; then \
+ echo "license header checking failed:"; echo "$${licRes}"; \
+ exit 1; \
+ fi
+
.PHONY: generate-go-collector-test-files
-VERSIONS := 1.20 1.21 1.22
+file := supported_go_versions.txt
+VERSIONS := $(shell cat ${file})
generate-go-collector-test-files:
for GO_VERSION in $(VERSIONS); do \
docker run \
diff --git Makefile.common Makefile.common
index 062a28185..e3da72ab4 100644
--- Makefile.common
+++ Makefile.common
@@ -49,23 +49,23 @@ endif
GOTEST := $(GO) test
GOTEST_DIR :=
ifneq ($(CIRCLE_JOB),)
-ifneq ($(shell command -v gotestsum > /dev/null),)
+ifneq ($(shell command -v gotestsum 2> /dev/null),)
GOTEST_DIR := test-results
GOTEST := gotestsum --junitfile $(GOTEST_DIR)/unit-tests.xml --
endif
endif
-PROMU_VERSION ?= 0.15.0
+PROMU_VERSION ?= 0.17.0
PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz
SKIP_GOLANGCI_LINT :=
GOLANGCI_LINT :=
GOLANGCI_LINT_OPTS ?=
-GOLANGCI_LINT_VERSION ?= v1.54.2
-# golangci-lint only supports linux, darwin and windows platforms on i386/amd64.
+GOLANGCI_LINT_VERSION ?= v1.59.1
+# golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64.
# windows isn't included here because of the path separator being different.
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin))
- ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386))
+ ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386 arm64))
# 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.
ifneq (,$(SKIP_GOLANGCI_LINT))
@@ -169,16 +169,20 @@ common-vet:
common-lint: $(GOLANGCI_LINT)
ifdef GOLANGCI_LINT
@echo ">> running golangci-lint"
-# 'go list' needs to be executed before staticcheck to prepopulate the modules cache.
-# Otherwise staticcheck might fail randomly for some reason not yet explained.
- $(GO) list -e -compiled -test=true -export=false -deps=true -find=false -tags= -- ./... > /dev/null
$(GOLANGCI_LINT) run $(GOLANGCI_LINT_OPTS) $(pkgs)
endif
+.PHONY: common-lint-fix
+common-lint-fix: $(GOLANGCI_LINT)
+ifdef GOLANGCI_LINT
+ @echo ">> running golangci-lint fix"
+ $(GOLANGCI_LINT) run --fix $(GOLANGCI_LINT_OPTS) $(pkgs)
+endif
+
.PHONY: common-yamllint
common-yamllint:
@echo ">> running yamllint on all YAML files in the repository"
-ifeq (, $(shell command -v yamllint > /dev/null))
+ifeq (, $(shell command -v yamllint 2> /dev/null))
@echo "yamllint not installed so skipping"
else
yamllint .
@@ -204,6 +208,10 @@ common-tarball: promu
@echo ">> building release tarball"
$(PROMU) tarball --prefix $(PREFIX) $(BIN_DIR)
+.PHONY: common-docker-repo-name
+common-docker-repo-name:
+ @echo "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)"
+
.PHONY: common-docker $(BUILD_DOCKER_ARCHS)
common-docker: $(BUILD_DOCKER_ARCHS)
$(BUILD_DOCKER_ARCHS): common-docker-%:
diff --git NOTICE NOTICE
index dd878a30e..b9cc55abb 100644
--- NOTICE
+++ NOTICE
@@ -16,8 +16,3 @@ Go support for Protocol Buffers - Google's data interchange format
http://github.com/golang/protobuf/
Copyright 2010 The Go Authors
See source code for license details.
-
-Support for streaming Protocol Buffer messages for the Go language (golang).
-https://github.com/matttproud/golang_protobuf_extensions
-Copyright 2013 Matt T. Proud
-Licensed under the Apache License, Version 2.0
diff --git README.md README.md
index f1019d0e2..d83a30cf6 100644
--- README.md
+++ README.md
@@ -24,12 +24,10 @@ CHANGELOG.md.
Features that require breaking changes in the stable parts of the repository
are being batched up and tracked in the [v2
-milestone](https://github.com/prometheus/client_golang/milestone/2). The v2
-development happens in a [separate
-branch](https://github.com/prometheus/client_golang/tree/dev-v2) for the time
-being. v2 releases off that branch will happen once sufficient stability is
-reached. In view of the widespread use of this repository, v1 and v2 will
-coexist for a while to enable a convenient transition.
+milestone](https://github.com/prometheus/client_golang/milestone/2), but plans for further development of v2 at the moment.
+
+> NOTE: The initial v2 attempt is in a [separate branch](https://github.com/prometheus/client_golang/tree/dev-v2). We also started
+experimenting on a new `prometheus.V2.*` APIs in [the 1.x's V2 struct](https://github.com/prometheus/client_golang/blob/main/prometheus/vnext.go#L23). Help wanted!
## Instrumenting applications
diff --git VERSION VERSION
index 815d5ca06..7bf9455f0 100644
--- VERSION
+++ VERSION
@@ -1 +1 @@
-1.19.0
+1.20.5
diff --git api/prometheus/v1/api.go api/prometheus/v1/api.go
index 1cfe8d863..cddf027fd 100644
--- api/prometheus/v1/api.go
+++ api/prometheus/v1/api.go
@@ -475,9 +475,9 @@ type API interface {
// Flags returns the flag values that Prometheus was launched with.
Flags(ctx context.Context) (FlagsResult, error)
// LabelNames returns the unique label names present in the block in sorted order by given time range and matchers.
- LabelNames(ctx context.Context, matches []string, startTime, endTime time.Time) ([]string, Warnings, error)
+ LabelNames(ctx context.Context, matches []string, startTime, endTime time.Time, opts ...Option) ([]string, Warnings, error)
// LabelValues performs a query for the values of the given label, time range and matchers.
- LabelValues(ctx context.Context, label string, matches []string, startTime, endTime time.Time) (model.LabelValues, Warnings, error)
+ LabelValues(ctx context.Context, label string, matches []string, startTime, endTime time.Time, opts ...Option) (model.LabelValues, Warnings, error)
// Query performs a query for the given time.
Query(ctx context.Context, query string, ts time.Time, opts ...Option) (model.Value, Warnings, error)
// QueryRange performs a query for the given range.
@@ -489,7 +489,7 @@ type API interface {
// Runtimeinfo returns the various runtime information properties about the Prometheus server.
Runtimeinfo(ctx context.Context) (RuntimeinfoResult, error)
// Series finds series by label matchers.
- Series(ctx context.Context, matches []string, startTime, endTime time.Time) ([]model.LabelSet, Warnings, error)
+ Series(ctx context.Context, matches []string, startTime, endTime time.Time, opts ...Option) ([]model.LabelSet, Warnings, error)
// Snapshot creates a snapshot of all current data into snapshots/<datetime>-<rand>
// under the TSDB's data directory and returns the directory as response.
Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, error)
@@ -502,7 +502,7 @@ type API interface {
// Metadata returns metadata about metrics currently scraped by the metric name.
Metadata(ctx context.Context, metric, limit string) (map[string][]Metadata, error)
// TSDB returns the cardinality statistics.
- TSDB(ctx context.Context) (TSDBResult, error)
+ TSDB(ctx context.Context, opts ...Option) (TSDBResult, error)
// WalReplay returns the current replay status of the wal.
WalReplay(ctx context.Context) (WalReplayStatus, error)
}
@@ -1024,9 +1024,10 @@ func (h *httpAPI) Runtimeinfo(ctx context.Context) (RuntimeinfoResult, error) {
return res, err
}
-func (h *httpAPI) LabelNames(ctx context.Context, matches []string, startTime, endTime time.Time) ([]string, Warnings, error) {
+func (h *httpAPI) LabelNames(ctx context.Context, matches []string, startTime, endTime time.Time, opts ...Option) ([]string, Warnings, error) {
u := h.client.URL(epLabels, nil)
- q := u.Query()
+ q := addOptionalURLParams(u.Query(), opts)
+
if !startTime.IsZero() {
q.Set("start", formatTime(startTime))
}
@@ -1046,9 +1047,10 @@ func (h *httpAPI) LabelNames(ctx context.Context, matches []string, startTime, e
return labelNames, w, err
}
-func (h *httpAPI) LabelValues(ctx context.Context, label string, matches []string, startTime, endTime time.Time) (model.LabelValues, Warnings, error) {
+func (h *httpAPI) LabelValues(ctx context.Context, label string, matches []string, startTime, endTime time.Time, opts ...Option) (model.LabelValues, Warnings, error) {
u := h.client.URL(epLabelValues, map[string]string{"name": label})
- q := u.Query()
+ q := addOptionalURLParams(u.Query(), opts)
+
if !startTime.IsZero() {
q.Set("start", formatTime(startTime))
}
@@ -1076,6 +1078,7 @@ func (h *httpAPI) LabelValues(ctx context.Context, label string, matches []strin
type apiOptions struct {
timeout time.Duration
+ limit uint64
}
type Option func(c *apiOptions)
@@ -1088,20 +1091,35 @@ func WithTimeout(timeout time.Duration) Option {
}
}
-func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time, opts ...Option) (model.Value, Warnings, error) {
- u := h.client.URL(epQuery, nil)
- q := u.Query()
+// WithLimit provides an optional maximum number of returned entries for APIs that support limit parameter
+// e.g. https://prometheus.io/docs/prometheus/latest/querying/api/#instant-querie:~:text=%3A%20End%20timestamp.-,limit%3D%3Cnumber%3E,-%3A%20Maximum%20number%20of
+func WithLimit(limit uint64) Option {
+ return func(o *apiOptions) {
+ o.limit = limit
+ }
+}
+func addOptionalURLParams(q url.Values, opts []Option) url.Values {
opt := &apiOptions{}
for _, o := range opts {
o(opt)
}
- d := opt.timeout
- if d > 0 {
- q.Set("timeout", d.String())
+ if opt.timeout > 0 {
+ q.Set("timeout", opt.timeout.String())
}
+ if opt.limit > 0 {
+ q.Set("limit", strconv.FormatUint(opt.limit, 10))
+ }
+
+ return q
+}
+
+func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time, opts ...Option) (model.Value, Warnings, error) {
+ u := h.client.URL(epQuery, nil)
+ q := addOptionalURLParams(u.Query(), opts)
+
q.Set("query", query)
if !ts.IsZero() {
q.Set("time", formatTime(ts))
@@ -1118,36 +1136,25 @@ func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time, opts ..
func (h *httpAPI) QueryRange(ctx context.Context, query string, r Range, opts ...Option) (model.Value, Warnings, error) {
u := h.client.URL(epQueryRange, nil)
- q := u.Query()
+ q := addOptionalURLParams(u.Query(), opts)
q.Set("query", query)
q.Set("start", formatTime(r.Start))
q.Set("end", formatTime(r.End))
q.Set("step", strconv.FormatFloat(r.Step.Seconds(), 'f', -1, 64))
- opt := &apiOptions{}
- for _, o := range opts {
- o(opt)
- }
-
- d := opt.timeout
- if d > 0 {
- q.Set("timeout", d.String())
- }
-
_, body, warnings, err := h.client.DoGetFallback(ctx, u, q)
if err != nil {
return nil, warnings, err
}
var qres queryResult
-
return qres.v, warnings, json.Unmarshal(body, &qres)
}
-func (h *httpAPI) Series(ctx context.Context, matches []string, startTime, endTime time.Time) ([]model.LabelSet, Warnings, error) {
+func (h *httpAPI) Series(ctx context.Context, matches []string, startTime, endTime time.Time, opts ...Option) ([]model.LabelSet, Warnings, error) {
u := h.client.URL(epSeries, nil)
- q := u.Query()
+ q := addOptionalURLParams(u.Query(), opts)
for _, m := range matches {
q.Add("match[]", m)
@@ -1166,8 +1173,7 @@ func (h *httpAPI) Series(ctx context.Context, matches []string, startTime, endTi
}
var mset []model.LabelSet
- err = json.Unmarshal(body, &mset)
- return mset, warnings, err
+ return mset, warnings, json.Unmarshal(body, &mset)
}
func (h *httpAPI) Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, error) {
@@ -1278,8 +1284,10 @@ func (h *httpAPI) Metadata(ctx context.Context, metric, limit string) (map[strin
return res, err
}
-func (h *httpAPI) TSDB(ctx context.Context) (TSDBResult, error) {
+func (h *httpAPI) TSDB(ctx context.Context, opts ...Option) (TSDBResult, error) {
u := h.client.URL(epTSDB, nil)
+ q := addOptionalURLParams(u.Query(), opts)
+ u.RawQuery = q.Encode()
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
diff --git api/prometheus/v1/api_test.go api/prometheus/v1/api_test.go
index e87e48311..01ddf45ca 100644
--- api/prometheus/v1/api_test.go
+++ api/prometheus/v1/api_test.go
@@ -154,15 +154,15 @@ func TestAPIs(t *testing.T) {
}
}
- doLabelNames := func(matches []string, startTime, endTime time.Time) func() (interface{}, Warnings, error) {
+ doLabelNames := func(matches []string, startTime, endTime time.Time, opts ...Option) func() (interface{}, Warnings, error) {
return func() (interface{}, Warnings, error) {
- return promAPI.LabelNames(context.Background(), matches, startTime, endTime)
+ return promAPI.LabelNames(context.Background(), matches, startTime, endTime, opts...)
}
}
- doLabelValues := func(matches []string, label string, startTime, endTime time.Time) func() (interface{}, Warnings, error) {
+ doLabelValues := func(matches []string, label string, startTime, endTime time.Time, opts ...Option) func() (interface{}, Warnings, error) {
return func() (interface{}, Warnings, error) {
- return promAPI.LabelValues(context.Background(), label, matches, startTime, endTime)
+ return promAPI.LabelValues(context.Background(), label, matches, startTime, endTime, opts...)
}
}
@@ -178,9 +178,9 @@ func TestAPIs(t *testing.T) {
}
}
- doSeries := func(matcher string, startTime, endTime time.Time) func() (interface{}, Warnings, error) {
+ doSeries := func(matcher string, startTime, endTime time.Time, opts ...Option) func() (interface{}, Warnings, error) {
return func() (interface{}, Warnings, error) {
- return promAPI.Series(context.Background(), []string{matcher}, startTime, endTime)
+ return promAPI.Series(context.Background(), []string{matcher}, startTime, endTime, opts...)
}
}
@@ -219,9 +219,9 @@ func TestAPIs(t *testing.T) {
}
}
- doTSDB := func() func() (interface{}, Warnings, error) {
+ doTSDB := func(opts ...Option) func() (interface{}, Warnings, error) {
return func() (interface{}, Warnings, error) {
- v, err := promAPI.TSDB(context.Background())
+ v, err := promAPI.TSDB(context.Background(), opts...)
return v, nil, err
}
}
diff --git api/prometheus/v1/example_test.go api/prometheus/v1/example_test.go
index 04cfac690..1247587cb 100644
--- api/prometheus/v1/example_test.go
+++ api/prometheus/v1/example_test.go
@@ -135,7 +135,11 @@ func ExampleAPI_queryRangeWithBasicAuth() {
client, err := api.NewClient(api.Config{
Address: "http://demo.robustperception.io:9090",
// We can use amazing github.com/prometheus/common/config helper!
- RoundTripper: config.NewBasicAuthRoundTripper("me", "definitely_me", "", "", api.DefaultRoundTripper),
+ RoundTripper: config.NewBasicAuthRoundTripper(
+ config.NewInlineSecret("me"),
+ config.NewInlineSecret("definitely_me"),
+ api.DefaultRoundTripper,
+ ),
})
if err != nil {
fmt.Printf("Error creating client: %v\n", err)
@@ -165,7 +169,11 @@ func ExampleAPI_queryRangeWithAuthBearerToken() {
client, err := api.NewClient(api.Config{
Address: "http://demo.robustperception.io:9090",
// We can use amazing github.com/prometheus/common/config helper!
- RoundTripper: config.NewAuthorizationCredentialsRoundTripper("Bearer", "secret_token", api.DefaultRoundTripper),
+ RoundTripper: config.NewAuthorizationCredentialsRoundTripper(
+ "Bearer",
+ config.NewInlineSecret("secret_token"),
+ api.DefaultRoundTripper,
+ ),
})
if err != nil {
fmt.Printf("Error creating client: %v\n", err)
diff --git a/dagger.json b/dagger.json
new file mode 100644
index 000000000..e1ed52ff0
--- /dev/null
+++ dagger.json
@@ -0,0 +1,12 @@
+{
+ "name": "client_golang",
+ "sdk": "go",
+ "dependencies": [
+ {
+ "name": "golang",
+ "source": "github.com/kpenfound/dagger-modules/golang@fd1a6e75721454d8ee3bcb0bd1a94d6bb4be1737"
+ }
+ ],
+ "source": "dagger",
+ "engineVersion": "v0.12.0"
+}
diff --git a/dagger/.gitattributes b/dagger/.gitattributes
new file mode 100644
index 000000000..3a454933c
--- /dev/null
+++ dagger/.gitattributes
@@ -0,0 +1,4 @@
+/dagger.gen.go linguist-generated
+/internal/dagger/** linguist-generated
+/internal/querybuilder/** linguist-generated
+/internal/telemetry/** linguist-generated
diff --git a/dagger/.gitignore b/dagger/.gitignore
new file mode 100644
index 000000000..7ebabcc14
--- /dev/null
+++ dagger/.gitignore
@@ -0,0 +1,4 @@
+/dagger.gen.go
+/internal/dagger
+/internal/querybuilder
+/internal/telemetry
diff --git a/dagger/go.mod b/dagger/go.mod
new file mode 100644
index 000000000..be916ea5d
--- /dev/null
+++ dagger/go.mod
@@ -0,0 +1,40 @@
+module dagger
+
+go 1.21.7
+
+require (
+ github.com/99designs/gqlgen v0.17.49
+ github.com/Khan/genqlient v0.7.0
+ github.com/vektah/gqlparser/v2 v2.5.16
+ go.opentelemetry.io/otel v1.27.0
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0
+ go.opentelemetry.io/otel/sdk v1.27.0
+ go.opentelemetry.io/otel/trace v1.27.0
+ golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
+ golang.org/x/sync v0.7.0
+ google.golang.org/grpc v1.64.0
+)
+
+require (
+ github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+ github.com/go-logr/logr v1.4.1 // indirect
+ github.com/go-logr/stdr v1.2.2 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
+ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
+ github.com/sosodev/duration v1.3.1 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88
+ go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect
+ go.opentelemetry.io/otel/log v0.3.0
+ go.opentelemetry.io/otel/metric v1.27.0 // indirect
+ go.opentelemetry.io/otel/sdk/log v0.3.0
+ go.opentelemetry.io/proto/otlp v1.3.1
+ golang.org/x/net v0.26.0 // indirect
+ golang.org/x/sys v0.21.0 // indirect
+ golang.org/x/text v0.16.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect
+ google.golang.org/protobuf v1.34.1 // indirect
+)
diff --git a/dagger/go.sum b/dagger/go.sum
new file mode 100644
index 000000000..6fea81b9c
--- /dev/null
+++ dagger/go.sum
@@ -0,0 +1,87 @@
+github.com/99designs/gqlgen v0.17.49 h1:b3hNGexHd33fBSAd4NDT/c3NCcQzcAVkknhN9ym36YQ=
+github.com/99designs/gqlgen v0.17.49/go.mod h1:tC8YFVZMed81x7UJ7ORUwXF4Kn6SXuucFqQBhN8+BU0=
+github.com/Khan/genqlient v0.7.0 h1:GZ1meyRnzcDTK48EjqB8t3bcfYvHArCUUvgOwpz1D4w=
+github.com/Khan/genqlient v0.7.0/go.mod h1:HNyy3wZvuYwmW3Y7mkoQLZsa/R5n5yIRajS1kPBvSFM=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
+github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
+github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
+github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/vektah/gqlparser/v2 v2.5.16 h1:1gcmLTvs3JLKXckwCwlUagVn/IlV2bwqle0vJ0vy5p8=
+github.com/vektah/gqlparser/v2 v2.5.16/go.mod h1:1lz1OeCqgQbQepsGxPVywrjdBHW2T08PUS3pJqepRww=
+go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg=
+go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ=
+go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 h1:oM0GTNKGlc5qHctWeIGTVyda4iFFalOzMZ3Ehj5rwB4=
+go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88/go.mod h1:JGG8ebaMO5nXOPnvKEl+DiA4MGwFjCbjsxT1WHIEBPY=
+go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 h1:ccBrA8nCY5mM0y5uO7FT0ze4S0TuFcWdDB2FxGMTjkI=
+go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0/go.mod h1:/9pb6634zi2Lk8LYg9Q0X8Ar6jka4dkFOylBLbVQPCE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY=
+go.opentelemetry.io/otel/log v0.3.0 h1:kJRFkpUFYtny37NQzL386WbznUByZx186DpEMKhEGZs=
+go.opentelemetry.io/otel/log v0.3.0/go.mod h1:ziCwqZr9soYDwGNbIL+6kAvQC+ANvjgG367HVcyR/ys=
+go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik=
+go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak=
+go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI=
+go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A=
+go.opentelemetry.io/otel/sdk/log v0.3.0 h1:GEjJ8iftz2l+XO1GF2856r7yYVh74URiF9JMcAacr5U=
+go.opentelemetry.io/otel/sdk/log v0.3.0/go.mod h1:BwCxtmux6ACLuys1wlbc0+vGBd+xytjmjajwqqIul2g=
+go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw=
+go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4=
+go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
+go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
+golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 h1:P8OJ/WCl/Xo4E4zoe4/bifHpSmmKwARqyqE4nW6J2GQ=
+google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5/go.mod h1:RGnPtTG7r4i8sPlNyDeikXF99hMM+hN6QMm4ooG9g2g=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 h1:AgADTJarZTBqgjiUzRgfaBchgYB3/WFTC80GPwsMcRI=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/dagger/main.go b/dagger/main.go
new file mode 100644
index 000000000..7a608af90
--- /dev/null
+++ dagger/main.go
@@ -0,0 +1,84 @@
+// Copyright 2024 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// A minimal example of how to include Prometheus instrumentation.
+
+package main
+
+import (
+ "context"
+ "dagger/internal/dagger"
+ "strings"
+
+ "golang.org/x/sync/errgroup"
+)
+
+type ClientGolang struct {
+ Source *dagger.Directory // +private
+}
+
+func New(src *dagger.Directory) *ClientGolang {
+ return &ClientGolang{Source: src}
+}
+
+// runs `make` with the given arguments
+func (m *ClientGolang) Make(
+ // +optional
+ args string,
+ // +default="1.20"
+ goVersion string,
+ // +optional
+ env []string,
+) (string, error) {
+ return dag.Golang().
+ Base(goVersion).
+ Container().
+ WithMountedDirectory("/src", m.Source).
+ WithWorkdir("/src").
+ WithMountedCache("/go/bin", dag.CacheVolume("gobincache")).
+ WithExec([]string{"sh", "-c", "make " + args}).
+ Stdout(context.Background())
+}
+
+// runs `make` with the given arguments for all supported go versions
+func (m *ClientGolang) MakeRun(
+ ctx context.Context,
+ // +optional,
+ args string,
+) error {
+ c, err := m.Source.File("supported_go_versions.txt").Contents(ctx)
+ if err != nil {
+ return err
+ }
+ goVersions := strings.Split(c, "\n")
+
+ eg := new(errgroup.Group)
+
+ for _, version := range goVersions {
+ version := version
+ if len(version) > 0 {
+ eg.Go(func() error {
+ _, err := dag.Golang().
+ Base(version).
+ Container().
+ WithMountedDirectory("/src", m.Source).
+ WithWorkdir("/src").
+ WithMountedCache("/go/bin", dag.CacheVolume("gobincache")).
+ WithExec([]string{"sh", "-c", "make " + args}).Sync(ctx)
+ return err
+ })
+ }
+ }
+
+ return eg.Wait()
+}
diff --git examples/middleware/go.mod examples/middleware/go.mod
deleted file mode 100644
index 0e6b185d9..000000000
--- examples/middleware/go.mod
+++ /dev/null
@@ -1,16 +0,0 @@
-module github.com/jessicalins/instrumentation-practices-examples/middleware
-
-go 1.19
-
-require github.com/prometheus/client_golang v1.18.0
-
-require (
- github.com/beorn7/perks v1.0.1 // indirect
- github.com/cespare/xxhash/v2 v2.2.0 // indirect
- github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
- github.com/prometheus/client_model v0.5.0 // indirect
- github.com/prometheus/common v0.45.0 // indirect
- github.com/prometheus/procfs v0.12.0 // indirect
- golang.org/x/sys v0.15.0 // indirect
- google.golang.org/protobuf v1.31.0 // indirect
-)
diff --git examples/middleware/go.sum examples/middleware/go.sum
deleted file mode 100644
index 361dab08b..000000000
--- examples/middleware/go.sum
+++ /dev/null
@@ -1,24 +0,0 @@
-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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
-github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
-github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
-github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
-github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
-github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
-github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
-github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
-github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
-github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
-github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
diff --git examples/middleware/main.go examples/middleware/main.go
index 227ed7ce6..67136cf42 100644
--- examples/middleware/main.go
+++ examples/middleware/main.go
@@ -19,11 +19,10 @@ import (
"log"
"net/http"
+ "github.com/prometheus/client_golang/examples/middleware/httpmiddleware"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
-
- "github.com/jessicalins/instrumentation-practices-examples/middleware/httpmiddleware"
)
func main() {
diff --git generate-go-collector.bash generate-go-collector.bash
index 3fa822aab..a9e2fa55f 100644
--- generate-go-collector.bash
+++ generate-go-collector.bash
@@ -5,3 +5,5 @@ set -e
go get github.com/hashicorp/go-version@v1.6.0
go run prometheus/gen_go_collector_metrics_set.go
mv -f go_collector_metrics_* prometheus
+go run prometheus/collectors/gen_go_collector_set.go
+mv -f go_collector_* prometheus/collectors
diff --git go.mod go.mod
index 662608543..918344c7f 100644
--- go.mod
+++ go.mod
@@ -4,27 +4,28 @@ go 1.20
require (
github.com/beorn7/perks v1.0.1
- github.com/cespare/xxhash/v2 v2.2.0
- github.com/davecgh/go-spew v1.1.1
+ github.com/cespare/xxhash/v2 v2.3.0
+ github.com/google/go-cmp v0.6.0
github.com/json-iterator/go v1.1.12
- github.com/prometheus/client_model v0.5.0
- github.com/prometheus/common v0.48.0
- github.com/prometheus/procfs v0.12.0
- golang.org/x/sys v0.16.0
- google.golang.org/protobuf v1.32.0
+ github.com/klauspost/compress v1.17.9
+ github.com/kylelemons/godebug v1.1.0
+ github.com/prometheus/client_model v0.6.1
+ github.com/prometheus/common v0.55.0
+ github.com/prometheus/procfs v0.15.1
+ golang.org/x/sys v0.22.0
+ google.golang.org/protobuf v1.34.2
)
require (
- github.com/golang/protobuf v1.5.3 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
- golang.org/x/net v0.20.0 // indirect
- golang.org/x/oauth2 v0.16.0 // indirect
- golang.org/x/text v0.14.0 // indirect
- google.golang.org/appengine v1.6.7 // indirect
+ golang.org/x/net v0.26.0 // indirect
+ golang.org/x/oauth2 v0.21.0 // indirect
+ golang.org/x/text v0.16.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
diff --git go.sum go.sum
index 450a27317..6102926ed 100644
--- go.sum
+++ go.sum
@@ -1,69 +1,61 @@
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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
-github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
+github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
-github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
-github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
-github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
-github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
-github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
+github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
+github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
+github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
+github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
+github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
+github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
-golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
-golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
-golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
-golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
-google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
-google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
+golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
+golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/internal/github.com/golang/gddo/LICENSE b/internal/github.com/golang/gddo/LICENSE
new file mode 100644
index 000000000..65d761bc9
--- /dev/null
+++ internal/github.com/golang/gddo/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013 The Go Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/internal/github.com/golang/gddo/README.md b/internal/github.com/golang/gddo/README.md
new file mode 100644
index 000000000..69af39a33
--- /dev/null
+++ internal/github.com/golang/gddo/README.md
@@ -0,0 +1 @@
+This source code is a stripped down version from the archived repository https://github.com/golang/gddo and licensed under BSD.
diff --git a/internal/github.com/golang/gddo/httputil/header/header.go b/internal/github.com/golang/gddo/httputil/header/header.go
new file mode 100644
index 000000000..8547c8dfd
--- /dev/null
+++ internal/github.com/golang/gddo/httputil/header/header.go
@@ -0,0 +1,145 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+// Package header provides functions for parsing HTTP headers.
+package header
+
+import (
+ "net/http"
+ "strings"
+)
+
+// Octet types from RFC 2616.
+var octetTypes [256]octetType
+
+type octetType byte
+
+const (
+ isToken octetType = 1 << iota
+ isSpace
+)
+
+func init() {
+ // OCTET = <any 8-bit sequence of data>
+ // CHAR = <any US-ASCII character (octets 0 - 127)>
+ // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
+ // CR = <US-ASCII CR, carriage return (13)>
+ // LF = <US-ASCII LF, linefeed (10)>
+ // SP = <US-ASCII SP, space (32)>
+ // HT = <US-ASCII HT, horizontal-tab (9)>
+ // <"> = <US-ASCII double-quote mark (34)>
+ // CRLF = CR LF
+ // LWS = [CRLF] 1*( SP | HT )
+ // TEXT = <any OCTET except CTLs, but including LWS>
+ // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
+ // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
+ // token = 1*<any CHAR except CTLs or separators>
+ // qdtext = <any TEXT except <">>
+
+ for c := 0; c < 256; c++ {
+ var t octetType
+ isCtl := c <= 31 || c == 127
+ isChar := 0 <= c && c <= 127
+ isSeparator := strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune(c))
+ if strings.ContainsRune(" \t\r\n", rune(c)) {
+ t |= isSpace
+ }
+ if isChar && !isCtl && !isSeparator {
+ t |= isToken
+ }
+ octetTypes[c] = t
+ }
+}
+
+// AcceptSpec describes an Accept* header.
+type AcceptSpec struct {
+ Value string
+ Q float64
+}
+
+// ParseAccept parses Accept* headers.
+func ParseAccept(header http.Header, key string) (specs []AcceptSpec) {
+loop:
+ for _, s := range header[key] {
+ for {
+ var spec AcceptSpec
+ spec.Value, s = expectTokenSlash(s)
+ if spec.Value == "" {
+ continue loop
+ }
+ spec.Q = 1.0
+ s = skipSpace(s)
+ if strings.HasPrefix(s, ";") {
+ s = skipSpace(s[1:])
+ if !strings.HasPrefix(s, "q=") {
+ continue loop
+ }
+ spec.Q, s = expectQuality(s[2:])
+ if spec.Q < 0.0 {
+ continue loop
+ }
+ }
+ specs = append(specs, spec)
+ s = skipSpace(s)
+ if !strings.HasPrefix(s, ",") {
+ continue loop
+ }
+ s = skipSpace(s[1:])
+ }
+ }
+ return
+}
+
+func skipSpace(s string) (rest string) {
+ i := 0
+ for ; i < len(s); i++ {
+ if octetTypes[s[i]]&isSpace == 0 {
+ break
+ }
+ }
+ return s[i:]
+}
+
+func expectTokenSlash(s string) (token, rest string) {
+ i := 0
+ for ; i < len(s); i++ {
+ b := s[i]
+ if (octetTypes[b]&isToken == 0) && b != '/' {
+ break
+ }
+ }
+ return s[:i], s[i:]
+}
+
+func expectQuality(s string) (q float64, rest string) {
+ switch {
+ case len(s) == 0:
+ return -1, ""
+ case s[0] == '0':
+ q = 0
+ case s[0] == '1':
+ q = 1
+ default:
+ return -1, ""
+ }
+ s = s[1:]
+ if !strings.HasPrefix(s, ".") {
+ return q, s
+ }
+ s = s[1:]
+ i := 0
+ n := 0
+ d := 1
+ for ; i < len(s); i++ {
+ b := s[i]
+ if b < '0' || b > '9' {
+ break
+ }
+ n = n*10 + int(b) - '0'
+ d *= 10
+ }
+ return q + float64(n)/float64(d), s[i:]
+}
diff --git a/internal/github.com/golang/gddo/httputil/header/,header_test.go b/internal/github.com/golang/gddo/httputil/header/header_test.go
new file mode 100644
index 000000000..e26eb6c30
--- /dev/null
+++ internal/github.com/golang/gddo/httputil/header/header_test.go
@@ -0,0 +1,49 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+package header
+
+import (
+ "net/http"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+)
+
+var parseAcceptTests = []struct {
+ s string
+ expected []AcceptSpec
+}{
+ {"text/html", []AcceptSpec{{"text/html", 1}}},
+ {"text/html; q=0", []AcceptSpec{{"text/html", 0}}},
+ {"text/html; q=0.0", []AcceptSpec{{"text/html", 0}}},
+ {"text/html; q=1", []AcceptSpec{{"text/html", 1}}},
+ {"text/html; q=1.0", []AcceptSpec{{"text/html", 1}}},
+ {"text/html; q=0.1", []AcceptSpec{{"text/html", 0.1}}},
+ {"text/html;q=0.1", []AcceptSpec{{"text/html", 0.1}}},
+ {"text/html, text/plain", []AcceptSpec{{"text/html", 1}, {"text/plain", 1}}},
+ {"text/html; q=0.1, text/plain", []AcceptSpec{{"text/html", 0.1}, {"text/plain", 1}}},
+ {"iso-8859-5, unicode-1-1;q=0.8,iso-8859-1", []AcceptSpec{{"iso-8859-5", 1}, {"unicode-1-1", 0.8}, {"iso-8859-1", 1}}},
+ {"iso-8859-1", []AcceptSpec{{"iso-8859-1", 1}}},
+ {"*", []AcceptSpec{{"*", 1}}},
+ {"da, en-gb;q=0.8, en;q=0.7", []AcceptSpec{{"da", 1}, {"en-gb", 0.8}, {"en", 0.7}}},
+ {"da, q, en-gb;q=0.8", []AcceptSpec{{"da", 1}, {"q", 1}, {"en-gb", 0.8}}},
+ {"image/png, image/*;q=0.5", []AcceptSpec{{"image/png", 1}, {"image/*", 0.5}}},
+
+ // bad cases
+ {"value1; q=0.1.2", []AcceptSpec{{"value1", 0.1}}},
+ {"da, en-gb;q=foo", []AcceptSpec{{"da", 1}}},
+}
+
+func TestParseAccept(t *testing.T) {
+ for _, tt := range parseAcceptTests {
+ header := http.Header{"Accept": {tt.s}}
+ actual := ParseAccept(header, "Accept")
+ if !cmp.Equal(actual, tt.expected) {
+ t.Errorf("ParseAccept(h, %q)=%v, want %v", tt.s, actual, tt.expected)
+ }
+ }
+}
diff --git a/internal/github.com/golang/gddo/httputil/negotiate.go b/internal/github.com/golang/gddo/httputil/negotiate.go
new file mode 100644
index 000000000..2e45780b7
--- /dev/null
+++ internal/github.com/golang/gddo/httputil/negotiate.go
@@ -0,0 +1,36 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+package httputil
+
+import (
+ "net/http"
+
+ "github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil/header"
+)
+
+// NegotiateContentEncoding returns the best offered content encoding for the
+// request's Accept-Encoding header. If two offers match with equal weight and
+// then the offer earlier in the list is preferred. If no offers are
+// acceptable, then "" is returned.
+func NegotiateContentEncoding(r *http.Request, offers []string) string {
+ bestOffer := "identity"
+ bestQ := -1.0
+ specs := header.ParseAccept(r.Header, "Accept-Encoding")
+ for _, offer := range offers {
+ for _, spec := range specs {
+ if spec.Q > bestQ &&
+ (spec.Value == "*" || spec.Value == offer) {
+ bestQ = spec.Q
+ bestOffer = offer
+ }
+ }
+ }
+ if bestQ == 0 {
+ bestOffer = ""
+ }
+ return bestOffer
+}
diff --git a/internal/github.com/golang/gddo/httputil/negotiate_test.go b/internal/github.com/golang/gddo/httputil/negotiate_test.go
new file mode 100644
index 000000000..cdd5807ca
--- /dev/null
+++ internal/github.com/golang/gddo/httputil/negotiate_test.go
@@ -0,0 +1,40 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+package httputil
+
+import (
+ "net/http"
+ "testing"
+)
+
+var negotiateContentEncodingTests = []struct {
+ s string
+ offers []string
+ expect string
+}{
+ {"", []string{"identity", "gzip"}, "identity"},
+ {"*;q=0", []string{"identity", "gzip"}, ""},
+ {"gzip", []string{"identity", "gzip"}, "gzip"},
+ {"gzip,zstd", []string{"identity", "zstd"}, "zstd"},
+ {"zstd,gzip", []string{"gzip", "zstd"}, "gzip"},
+ {"gzip,zstd", []string{"gzip", "zstd"}, "gzip"},
+ {"gzip,zstd", []string{"zstd", "gzip"}, "zstd"},
+ {"gzip;q=0.1,zstd;q=0.5", []string{"gzip", "zstd"}, "zstd"},
+ {"gzip;q=1.0, identity; q=0.5, *;q=0", []string{"identity", "gzip"}, "gzip"},
+ {"gzip;q=1.0, identity; q=0.5, *;q=0", []string{"identity", "zstd"}, "identity"},
+ {"zstd", []string{"identity", "gzip"}, "identity"},
+}
+
+func TestNegotiateContentEncoding(t *testing.T) {
+ for _, tt := range negotiateContentEncodingTests {
+ r := &http.Request{Header: http.Header{"Accept-Encoding": {tt.s}}}
+ actual := NegotiateContentEncoding(r, tt.offers)
+ if actual != tt.expect {
+ t.Errorf("NegotiateContentEncoding(%q, %#v)=%q, want %q", tt.s, tt.offers, actual, tt.expect)
+ }
+ }
+}
diff --git prometheus/collectors/dbstats_collector_test.go prometheus/collectors/dbstats_collector_test.go
index 0698bb289..057e24384 100644
--- prometheus/collectors/dbstats_collector_test.go
+++ prometheus/collectors/dbstats_collector_test.go
@@ -21,7 +21,7 @@ import (
)
func TestDBStatsCollector(t *testing.T) {
- reg := prometheus.NewRegistry()
+ reg := prometheus.NewPedanticRegistry()
{
db := new(sql.DB)
if err := reg.Register(NewDBStatsCollector(db, "db_A")); err != nil {
diff --git a/prometheus/collectors/gen_go_collector_set.go b/prometheus/collectors/gen_go_collector_set.go
new file mode 100644
index 000000000..6d1ff7e71
--- /dev/null
+++ prometheus/collectors/gen_go_collector_set.go
@@ -0,0 +1,189 @@
+// Copyright 2021 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:build ignore
+// +build ignore
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "go/format"
+ "log"
+ "os"
+ "regexp"
+ "runtime"
+ "runtime/metrics"
+ "sort"
+ "strings"
+ "text/template"
+
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/internal"
+
+ version "github.com/hashicorp/go-version"
+)
+
+type metricGroup struct {
+ Name string
+ Regex *regexp.Regexp
+ Metrics []string
+}
+
+var metricGroups = []metricGroup{
+ {"withAllMetrics", nil, nil},
+ {"withGCMetrics", regexp.MustCompile("^go_gc_.*"), nil},
+ {"withMemoryMetrics", regexp.MustCompile("^go_memory_classes_.*"), nil},
+ {"withSchedulerMetrics", regexp.MustCompile("^go_sched_.*"), nil},
+ {"withDebugMetrics", regexp.MustCompile("^go_godebug_non_default_behavior_.*"), nil},
+}
+
+func main() {
+ var givenVersion string
+ toolVersion := runtime.Version()
+ if len(os.Args) != 2 {
+ log.Printf("requires Go version (e.g. go1.17) as an argument. Since it is not specified, assuming %s.", toolVersion)
+ givenVersion = toolVersion
+ } else {
+ givenVersion = os.Args[1]
+ }
+ log.Printf("given version for Go: %s", givenVersion)
+ log.Printf("tool version for Go: %s", toolVersion)
+
+ tv, err := version.NewVersion(strings.TrimPrefix(givenVersion, "go"))
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ toolVersion = strings.Split(strings.TrimPrefix(toolVersion, "go"), " ")[0]
+ gv, err := version.NewVersion(toolVersion)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if !gv.Equal(tv) {
+ log.Fatalf("using Go version %q but expected Go version %q", tv, gv)
+ }
+
+ v := goVersion(gv.Segments()[1])
+ log.Printf("generating metrics for Go version %q", v)
+
+ descriptions := computeMetricsList()
+ groupedMetrics := groupMetrics(descriptions)
+
+ // Generate code.
+ var buf bytes.Buffer
+ err = testFile.Execute(&buf, struct {
+ GoVersion goVersion
+ Groups []metricGroup
+ }{
+ GoVersion: v,
+ Groups: groupedMetrics,
+ })
+ if err != nil {
+ log.Fatalf("executing template: %v", err)
+ }
+
+ // Format it.
+ result, err := format.Source(buf.Bytes())
+ if err != nil {
+ log.Fatalf("formatting code: %v", err)
+ }
+
+ // Write it to a file.
+ fname := fmt.Sprintf("go_collector_%s_test.go", v.Abbr())
+ if err := os.WriteFile(fname, result, 0o644); err != nil {
+ log.Fatalf("writing file: %v", err)
+ }
+}
+
+func computeMetricsList() []string {
+ var metricsList []string
+ for _, d := range metrics.All() {
+ if trans := rm2prom(d); trans != "" {
+ metricsList = append(metricsList, trans)
+ }
+ }
+ return metricsList
+}
+
+func rm2prom(d metrics.Description) string {
+ ns, ss, n, ok := internal.RuntimeMetricsToProm(&d)
+ if !ok {
+ return ""
+ }
+ return prometheus.BuildFQName(ns, ss, n)
+}
+
+func groupMetrics(metricsList []string) []metricGroup {
+ var groupedMetrics []metricGroup
+ for _, group := range metricGroups {
+ matchedMetrics := make([]string, 0)
+ for _, metric := range metricsList {
+ if group.Regex == nil || group.Regex.MatchString(metric) {
+ matchedMetrics = append(matchedMetrics, metric)
+ }
+ }
+
+ sort.Strings(matchedMetrics)
+ groupedMetrics = append(groupedMetrics, metricGroup{
+ Name: group.Name,
+ Regex: group.Regex,
+ Metrics: matchedMetrics,
+ })
+ }
+ return groupedMetrics
+}
+
+type goVersion int
+
+func (g goVersion) String() string {
+ return fmt.Sprintf("go1.%d", g)
+}
+
+func (g goVersion) Abbr() string {
+ return fmt.Sprintf("go1%d", g)
+}
+
+var testFile = template.Must(template.New("testFile").Funcs(map[string]interface{}{
+ "nextVersion": func(version goVersion) string {
+ return (version + goVersion(1)).String()
+ },
+}).Parse(`// Copyright 2022 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:build {{.GoVersion}} && !{{nextVersion .GoVersion}}
+// +build {{.GoVersion}},!{{nextVersion .GoVersion}}
+
+package collectors
+
+{{- range .Groups }}
+func {{ .Name }}() []string {
+ return withBaseMetrics([]string{
+ {{- range $metric := .Metrics }}
+ {{ $metric | printf "%q" }},
+ {{- end }}
+ })
+}
+{{ end }}
+`))
diff --git prometheus/collectors/go_collector_go117_test.go prometheus/collectors/go_collector_go117_test.go
index 370758da7..f378d4097 100644
--- prometheus/collectors/go_collector_go117_test.go
+++ prometheus/collectors/go_collector_go117_test.go
@@ -98,3 +98,13 @@ func withSchedulerMetrics() []string {
"go_threads",
}
}
+
+func withDebugMetrics() []string {
+ return withBaseMetrics([]string{})
+}
+
+var defaultRuntimeMetrics = []string{}
+
+func withDefaultRuntimeMetrics(metricNames []string, withoutGC, withoutSched bool) []string {
+ return metricNames
+}
diff --git prometheus/collectors/go_collector_go119_test.go prometheus/collectors/go_collector_go119_test.go
index 4577a219c..4febad2ce 100644
--- prometheus/collectors/go_collector_go119_test.go
+++ prometheus/collectors/go_collector_go119_test.go
@@ -16,6 +16,8 @@
package collectors
+import "sort"
+
func withAllMetrics() []string {
return withBaseMetrics([]string{
"go_cgo_go_to_c_calls_calls_total",
@@ -105,3 +107,22 @@ func withSchedulerMetrics() []string {
"go_threads",
}
}
+
+func withDebugMetrics() []string {
+ return withBaseMetrics([]string{})
+}
+
+var defaultRuntimeMetrics = []string{
+ "go_sched_gomaxprocs_threads",
+}
+
+func withDefaultRuntimeMetrics(metricNames []string, withoutGC, withoutSched bool) []string {
+ // If withoutSched is true, exclude "go_sched_gomaxprocs_threads".
+ if withoutSched {
+ return metricNames
+ }
+ metricNames = append(metricNames, defaultRuntimeMetrics...)
+ // sorting is required
+ sort.Strings(metricNames)
+ return metricNames
+}
diff --git prometheus/collectors/go_collector_go120_test.go prometheus/collectors/go_collector_go120_test.go
index 3bdb4d576..968a016ab 100644
--- prometheus/collectors/go_collector_go120_test.go
+++ prometheus/collectors/go_collector_go120_test.go
@@ -16,12 +16,11 @@
package collectors
+import "sort"
+
func withAllMetrics() []string {
return withBaseMetrics([]string{
"go_cgo_go_to_c_calls_calls_total",
- "go_gc_cycles_automatic_gc_cycles_total",
- "go_gc_cycles_forced_gc_cycles_total",
- "go_gc_cycles_total_gc_cycles_total",
"go_cpu_classes_gc_mark_assist_cpu_seconds_total",
"go_cpu_classes_gc_mark_dedicated_cpu_seconds_total",
"go_cpu_classes_gc_mark_idle_cpu_seconds_total",
@@ -33,6 +32,9 @@ func withAllMetrics() []string {
"go_cpu_classes_scavenge_total_cpu_seconds_total",
"go_cpu_classes_total_cpu_seconds_total",
"go_cpu_classes_user_cpu_seconds_total",
+ "go_gc_cycles_automatic_gc_cycles_total",
+ "go_gc_cycles_forced_gc_cycles_total",
+ "go_gc_cycles_total_gc_cycles_total",
"go_gc_heap_allocs_by_size_bytes",
"go_gc_heap_allocs_bytes_total",
"go_gc_heap_allocs_objects_total",
@@ -106,14 +108,28 @@ func withMemoryMetrics() []string {
}
func withSchedulerMetrics() []string {
- return []string{
- "go_gc_duration_seconds",
- "go_goroutines",
- "go_info",
- "go_memstats_last_gc_time_seconds",
+ return withBaseMetrics([]string{
"go_sched_gomaxprocs_threads",
"go_sched_goroutines_goroutines",
"go_sched_latencies_seconds",
- "go_threads",
+ })
+}
+
+func withDebugMetrics() []string {
+ return withBaseMetrics([]string{})
+}
+
+var defaultRuntimeMetrics = []string{
+ "go_sched_gomaxprocs_threads",
+}
+
+func withDefaultRuntimeMetrics(metricNames []string, withoutGC, withoutSched bool) []string {
+ // If withoutSched is true, exclude "go_sched_gomaxprocs_threads".
+ if withoutSched {
+ return metricNames
}
+ metricNames = append(metricNames, defaultRuntimeMetrics...)
+ // sorting is required
+ sort.Strings(metricNames)
+ return metricNames
}
diff --git prometheus/collectors/go_collector_go121_test.go prometheus/collectors/go_collector_go121_test.go
index 6facf6798..f9cc318cc 100644
--- prometheus/collectors/go_collector_go121_test.go
+++ prometheus/collectors/go_collector_go121_test.go
@@ -16,6 +16,8 @@
package collectors
+import "sort"
+
func withAllMetrics() []string {
return withBaseMetrics([]string{
"go_cgo_go_to_c_calls_calls_total",
@@ -63,6 +65,7 @@ func withAllMetrics() []string {
"go_godebug_non_default_behavior_multipartmaxheaders_events_total",
"go_godebug_non_default_behavior_multipartmaxparts_events_total",
"go_godebug_non_default_behavior_multipathtcp_events_total",
+ "go_godebug_non_default_behavior_netedns0_events_total",
"go_godebug_non_default_behavior_panicnil_events_total",
"go_godebug_non_default_behavior_randautoseed_events_total",
"go_godebug_non_default_behavior_tarinsecurepath_events_total",
@@ -138,20 +141,15 @@ func withMemoryMetrics() []string {
}
func withSchedulerMetrics() []string {
- return []string{
- "go_gc_duration_seconds",
- "go_goroutines",
- "go_info",
- "go_memstats_last_gc_time_seconds",
+ return withBaseMetrics([]string{
"go_sched_gomaxprocs_threads",
"go_sched_goroutines_goroutines",
"go_sched_latencies_seconds",
- "go_threads",
- }
+ })
}
func withDebugMetrics() []string {
- return []string{
+ return withBaseMetrics([]string{
"go_godebug_non_default_behavior_execerrdot_events_total",
"go_godebug_non_default_behavior_gocachehash_events_total",
"go_godebug_non_default_behavior_gocachetest_events_total",
@@ -163,6 +161,7 @@ func withDebugMetrics() []string {
"go_godebug_non_default_behavior_multipartmaxheaders_events_total",
"go_godebug_non_default_behavior_multipartmaxparts_events_total",
"go_godebug_non_default_behavior_multipathtcp_events_total",
+ "go_godebug_non_default_behavior_netedns0_events_total",
"go_godebug_non_default_behavior_panicnil_events_total",
"go_godebug_non_default_behavior_randautoseed_events_total",
"go_godebug_non_default_behavior_tarinsecurepath_events_total",
@@ -170,5 +169,30 @@ func withDebugMetrics() []string {
"go_godebug_non_default_behavior_x509sha1_events_total",
"go_godebug_non_default_behavior_x509usefallbackroots_events_total",
"go_godebug_non_default_behavior_zipinsecurepath_events_total",
+ })
+}
+
+var defaultRuntimeMetrics = []string{
+ "go_gc_gogc_percent",
+ "go_gc_gomemlimit_bytes",
+ "go_sched_gomaxprocs_threads",
+}
+
+func withDefaultRuntimeMetrics(metricNames []string, withoutGC, withoutSched bool) []string {
+ if withoutGC && withoutSched {
+ // If both flags are true, return the metricNames as is.
+ return metricNames
+ } else if withoutGC && !withoutSched {
+ // If only withoutGC is true, include "go_sched_gomaxprocs_threads" only.
+ metricNames = append(metricNames, []string{"go_sched_gomaxprocs_threads"}...)
+ } else if withoutSched && !withoutGC {
+ // If only withoutSched is true, exclude "go_sched_gomaxprocs_threads".
+ metricNames = append(metricNames, []string{"go_gc_gogc_percent", "go_gc_gomemlimit_bytes"}...)
+ } else {
+ // If neither flag is true, use the default metrics.
+ metricNames = append(metricNames, defaultRuntimeMetrics...)
}
+ // sorting is required
+ sort.Strings(metricNames)
+ return metricNames
}
diff --git prometheus/collectors/go_collector_go122_test.go prometheus/collectors/go_collector_go122_test.go
index 0b3c37adf..2413c2750 100644
--- prometheus/collectors/go_collector_go122_test.go
+++ prometheus/collectors/go_collector_go122_test.go
@@ -16,6 +16,8 @@
package collectors
+import "sort"
+
func withAllMetrics() []string {
return withBaseMetrics([]string{
"go_cgo_go_to_c_calls_calls_total",
@@ -66,6 +68,7 @@ func withAllMetrics() []string {
"go_godebug_non_default_behavior_multipartmaxheaders_events_total",
"go_godebug_non_default_behavior_multipartmaxparts_events_total",
"go_godebug_non_default_behavior_multipathtcp_events_total",
+ "go_godebug_non_default_behavior_netedns0_events_total",
"go_godebug_non_default_behavior_panicnil_events_total",
"go_godebug_non_default_behavior_randautoseed_events_total",
"go_godebug_non_default_behavior_tarinsecurepath_events_total",
@@ -149,11 +152,7 @@ func withMemoryMetrics() []string {
}
func withSchedulerMetrics() []string {
- return []string{
- "go_gc_duration_seconds",
- "go_goroutines",
- "go_info",
- "go_memstats_last_gc_time_seconds",
+ return withBaseMetrics([]string{
"go_sched_gomaxprocs_threads",
"go_sched_goroutines_goroutines",
"go_sched_latencies_seconds",
@@ -161,12 +160,11 @@ func withSchedulerMetrics() []string {
"go_sched_pauses_stopping_other_seconds",
"go_sched_pauses_total_gc_seconds",
"go_sched_pauses_total_other_seconds",
- "go_threads",
- }
+ })
}
func withDebugMetrics() []string {
- return []string{
+ return withBaseMetrics([]string{
"go_godebug_non_default_behavior_execerrdot_events_total",
"go_godebug_non_default_behavior_gocachehash_events_total",
"go_godebug_non_default_behavior_gocachetest_events_total",
@@ -181,6 +179,7 @@ func withDebugMetrics() []string {
"go_godebug_non_default_behavior_multipartmaxheaders_events_total",
"go_godebug_non_default_behavior_multipartmaxparts_events_total",
"go_godebug_non_default_behavior_multipathtcp_events_total",
+ "go_godebug_non_default_behavior_netedns0_events_total",
"go_godebug_non_default_behavior_panicnil_events_total",
"go_godebug_non_default_behavior_randautoseed_events_total",
"go_godebug_non_default_behavior_tarinsecurepath_events_total",
@@ -192,5 +191,30 @@ func withDebugMetrics() []string {
"go_godebug_non_default_behavior_x509usefallbackroots_events_total",
"go_godebug_non_default_behavior_x509usepolicies_events_total",
"go_godebug_non_default_behavior_zipinsecurepath_events_total",
+ })
+}
+
+var defaultRuntimeMetrics = []string{
+ "go_gc_gogc_percent",
+ "go_gc_gomemlimit_bytes",
+ "go_sched_gomaxprocs_threads",
+}
+
+func withDefaultRuntimeMetrics(metricNames []string, withoutGC, withoutSched bool) []string {
+ if withoutGC && withoutSched {
+ // If both flags are true, return the metricNames as is.
+ return metricNames
+ } else if withoutGC && !withoutSched {
+ // If only withoutGC is true, include "go_sched_gomaxprocs_threads" only.
+ metricNames = append(metricNames, []string{"go_sched_gomaxprocs_threads"}...)
+ } else if withoutSched && !withoutGC {
+ // If only withoutSched is true, exclude "go_sched_gomaxprocs_threads".
+ metricNames = append(metricNames, []string{"go_gc_gogc_percent", "go_gc_gomemlimit_bytes"}...)
+ } else {
+ // If neither flag is true, use the default metrics.
+ metricNames = append(metricNames, defaultRuntimeMetrics...)
}
+ // sorting is required
+ sort.Strings(metricNames)
+ return metricNames
}
diff --git prometheus/collectors/go_collector_latest.go prometheus/collectors/go_collector_latest.go
index bcfa4fa10..cc4ef1077 100644
--- prometheus/collectors/go_collector_latest.go
+++ prometheus/collectors/go_collector_latest.go
@@ -37,6 +37,9 @@ var (
// MetricsScheduler allows only scheduler metrics to be collected from Go runtime.
// e.g. go_sched_goroutines_goroutines
MetricsScheduler = GoRuntimeMetricsRule{regexp.MustCompile(`^/sched/.*`)}
+ // MetricsDebug allows only debug metrics to be collected from Go runtime.
+ // e.g. go_godebug_non_default_behavior_gocachetest_events_total
+ MetricsDebug = GoRuntimeMetricsRule{regexp.MustCompile(`^/godebug/.*`)}
)
// WithGoCollectorMemStatsMetricsDisabled disables metrics that is gathered in runtime.MemStats structure such as:
@@ -44,7 +47,6 @@ var (
// go_memstats_alloc_bytes
// go_memstats_alloc_bytes_total
// go_memstats_sys_bytes
-// go_memstats_lookups_total
// go_memstats_mallocs_total
// go_memstats_frees_total
// go_memstats_heap_alloc_bytes
diff --git prometheus/collectors/go_collector_latest_test.go prometheus/collectors/go_collector_latest_test.go
index ebde89118..2f5440abb 100644
--- prometheus/collectors/go_collector_latest_test.go
+++ prometheus/collectors/go_collector_latest_test.go
@@ -20,11 +20,12 @@ import (
"encoding/json"
"log"
"net/http"
- "reflect"
"regexp"
"sort"
"testing"
+ "github.com/google/go-cmp/cmp"
+
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
@@ -37,8 +38,32 @@ var baseMetrics = []string{
"go_threads",
}
+var memstatMetrics = []string{
+ "go_memstats_alloc_bytes",
+ "go_memstats_alloc_bytes_total",
+ "go_memstats_buck_hash_sys_bytes",
+ "go_memstats_frees_total",
+ "go_memstats_gc_sys_bytes",
+ "go_memstats_heap_alloc_bytes",
+ "go_memstats_heap_idle_bytes",
+ "go_memstats_heap_inuse_bytes",
+ "go_memstats_heap_objects",
+ "go_memstats_heap_released_bytes",
+ "go_memstats_heap_sys_bytes",
+ "go_memstats_mallocs_total",
+ "go_memstats_mcache_inuse_bytes",
+ "go_memstats_mcache_sys_bytes",
+ "go_memstats_mspan_inuse_bytes",
+ "go_memstats_mspan_sys_bytes",
+ "go_memstats_next_gc_bytes",
+ "go_memstats_other_sys_bytes",
+ "go_memstats_stack_inuse_bytes",
+ "go_memstats_stack_sys_bytes",
+ "go_memstats_sys_bytes",
+}
+
func TestGoCollectorMarshalling(t *testing.T) {
- reg := prometheus.NewRegistry()
+ reg := prometheus.NewPedanticRegistry()
reg.MustRegister(NewGoCollector(
WithGoCollectorRuntimeMetrics(GoRuntimeMetricsRule{
Matcher: regexp.MustCompile("/.*"),
@@ -54,8 +79,28 @@ func TestGoCollectorMarshalling(t *testing.T) {
}
}
+func TestWithGoCollectorDefault(t *testing.T) {
+ reg := prometheus.NewPedanticRegistry()
+ reg.MustRegister(NewGoCollector())
+ result, err := reg.Gather()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ got := []string{}
+ for _, r := range result {
+ got = append(got, r.GetName())
+ }
+
+ expected := append(withBaseMetrics(memstatMetrics), defaultRuntimeMetrics...)
+ sort.Strings(expected)
+ if diff := cmp.Diff(got, expected); diff != "" {
+ t.Errorf("[IMPORTANT, those are default metrics, can't change in 1.x] missmatch (-want +got):\n%s", diff)
+ }
+}
+
func TestWithGoCollectorMemStatsMetricsDisabled(t *testing.T) {
- reg := prometheus.NewRegistry()
+ reg := prometheus.NewPedanticRegistry()
reg.MustRegister(NewGoCollector(
WithGoCollectorMemStatsMetricsDisabled(),
))
@@ -69,8 +114,8 @@ func TestWithGoCollectorMemStatsMetricsDisabled(t *testing.T) {
got = append(got, r.GetName())
}
- if !reflect.DeepEqual(got, baseMetrics) {
- t.Errorf("got %v, want %v", got, baseMetrics)
+ if diff := cmp.Diff(got, withBaseMetrics(defaultRuntimeMetrics)); diff != "" {
+ t.Errorf("missmatch (-want +got):\n%s", diff)
}
}
@@ -83,7 +128,7 @@ func TestGoCollectorAllowList(t *testing.T) {
{
name: "Without any rules",
rules: nil,
- expected: baseMetrics,
+ expected: withBaseMetrics(defaultRuntimeMetrics),
},
{
name: "allow all",
@@ -93,21 +138,26 @@ func TestGoCollectorAllowList(t *testing.T) {
{
name: "allow GC",
rules: []GoRuntimeMetricsRule{MetricsGC},
- expected: withGCMetrics(),
+ expected: withDefaultRuntimeMetrics(withGCMetrics(), true, false),
},
{
name: "allow Memory",
rules: []GoRuntimeMetricsRule{MetricsMemory},
- expected: withMemoryMetrics(),
+ expected: withDefaultRuntimeMetrics(withMemoryMetrics(), false, false),
},
{
name: "allow Scheduler",
rules: []GoRuntimeMetricsRule{MetricsScheduler},
- expected: withSchedulerMetrics(),
+ expected: withDefaultRuntimeMetrics(withSchedulerMetrics(), false, true),
+ },
+ {
+ name: "allow debug",
+ rules: []GoRuntimeMetricsRule{MetricsDebug},
+ expected: withDefaultRuntimeMetrics(withDebugMetrics(), false, false),
},
} {
t.Run(test.name, func(t *testing.T) {
- reg := prometheus.NewRegistry()
+ reg := prometheus.NewPedanticRegistry()
reg.MustRegister(NewGoCollector(
WithGoCollectorMemStatsMetricsDisabled(),
WithGoCollectorRuntimeMetrics(test.rules...),
@@ -122,8 +172,8 @@ func TestGoCollectorAllowList(t *testing.T) {
got = append(got, r.GetName())
}
- if !reflect.DeepEqual(got, test.expected) {
- t.Errorf("got %v, want %v", got, test.expected)
+ if diff := cmp.Diff(got, test.expected); diff != "" {
+ t.Errorf("missmatch (-want +got):\n%s", diff)
}
})
}
@@ -144,7 +194,7 @@ func TestGoCollectorDenyList(t *testing.T) {
{
name: "Without any matchers",
matchers: nil,
- expected: baseMetrics,
+ expected: withBaseMetrics(defaultRuntimeMetrics),
},
{
name: "deny all",
@@ -157,11 +207,19 @@ func TestGoCollectorDenyList(t *testing.T) {
regexp.MustCompile("^/gc/.*"),
regexp.MustCompile("^/sched/latencies:.*"),
},
+ expected: withDefaultRuntimeMetrics(baseMetrics, true, false),
+ },
+ {
+ name: "deny gc and scheduler",
+ matchers: []*regexp.Regexp{
+ regexp.MustCompile("^/gc/.*"),
+ regexp.MustCompile("^/sched/.*"),
+ },
expected: baseMetrics,
},
} {
t.Run(test.name, func(t *testing.T) {
- reg := prometheus.NewRegistry()
+ reg := prometheus.NewPedanticRegistry()
reg.MustRegister(NewGoCollector(
WithGoCollectorMemStatsMetricsDisabled(),
WithoutGoCollectorRuntimeMetrics(test.matchers...),
@@ -176,17 +234,17 @@ func TestGoCollectorDenyList(t *testing.T) {
got = append(got, r.GetName())
}
- if !reflect.DeepEqual(got, test.expected) {
- t.Errorf("got %v, want %v", got, test.expected)
+ if diff := cmp.Diff(got, test.expected); diff != "" {
+ t.Errorf("missmatch (-want +got):\n%s", diff)
}
})
}
}
func ExampleGoCollector() {
- reg := prometheus.NewRegistry()
+ reg := prometheus.NewPedanticRegistry()
- // Register the GoCollector with the default options. Only the base metrics will be enabled.
+ // Register the GoCollector with the default options. Only the base metrics, default runtime metrics and memstats are enabled.
reg.MustRegister(NewGoCollector())
http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
@@ -194,7 +252,7 @@ func ExampleGoCollector() {
}
func ExampleGoCollector_WithAdvancedGoMetrics() {
- reg := prometheus.NewRegistry()
+ reg := prometheus.NewPedanticRegistry()
// Enable Go metrics with pre-defined rules. Or your custom rules.
reg.MustRegister(
diff --git prometheus/examples_test.go prometheus/examples_test.go
index e4fed3e95..fdbb6f747 100644
--- prometheus/examples_test.go
+++ prometheus/examples_test.go
@@ -405,6 +405,35 @@ func ExampleNewConstSummary() {
// {"label":[{"name":"code","value":"200"},{"name":"method","value":"get"},{"name":"owner","value":"example"}],"summary":{"sampleCount":"4711","sampleSum":403.34,"quantile":[{"quantile":0.5,"value":42.3},{"quantile":0.9,"value":323.3}]}}
}
+func ExampleNewConstSummaryWithCreatedTimestamp() {
+ desc := prometheus.NewDesc(
+ "http_request_duration_seconds",
+ "A summary of the HTTP request durations.",
+ []string{"code", "method"},
+ prometheus.Labels{"owner": "example"},
+ )
+
+ // Create a constant summary with created timestamp set
+ createdTs := time.Unix(1719670764, 123)
+ s := prometheus.MustNewConstSummaryWithCreatedTimestamp(
+ desc,
+ 4711, 403.34,
+ map[float64]float64{0.5: 42.3, 0.9: 323.3},
+ createdTs,
+ "200", "get",
+ )
+
+ // Just for demonstration, let's check the state of the summary by
+ // (ab)using its Write method (which is usually only used by Prometheus
+ // internally).
+ metric := &dto.Metric{}
+ s.Write(metric)
+ fmt.Println(toNormalizedJSON(metric))
+
+ // Output:
+ // {"label":[{"name":"code","value":"200"},{"name":"method","value":"get"},{"name":"owner","value":"example"}],"summary":{"sampleCount":"4711","sampleSum":403.34,"quantile":[{"quantile":0.5,"value":42.3},{"quantile":0.9,"value":323.3}],"createdTimestamp":"2024-06-29T14:19:24.000000123Z"}}
+}
+
func ExampleHistogram() {
temps := prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "pond_temperature_celsius",
@@ -456,6 +485,34 @@ func ExampleNewConstHistogram() {
// {"label":[{"name":"code","value":"200"},{"name":"method","value":"get"},{"name":"owner","value":"example"}],"histogram":{"sampleCount":"4711","sampleSum":403.34,"bucket":[{"cumulativeCount":"121","upperBound":25},{"cumulativeCount":"2403","upperBound":50},{"cumulativeCount":"3221","upperBound":100},{"cumulativeCount":"4233","upperBound":200}]}}
}
+func ExampleNewConstHistogramWithCreatedTimestamp() {
+ desc := prometheus.NewDesc(
+ "http_request_duration_seconds",
+ "A histogram of the HTTP request durations.",
+ []string{"code", "method"},
+ prometheus.Labels{"owner": "example"},
+ )
+
+ createdTs := time.Unix(1719670764, 123)
+ h := prometheus.MustNewConstHistogramWithCreatedTimestamp(
+ desc,
+ 4711, 403.34,
+ map[float64]uint64{25: 121, 50: 2403, 100: 3221, 200: 4233},
+ createdTs,
+ "200", "get",
+ )
+
+ // Just for demonstration, let's check the state of the histogram by
+ // (ab)using its Write method (which is usually only used by Prometheus
+ // internally).
+ metric := &dto.Metric{}
+ h.Write(metric)
+ fmt.Println(toNormalizedJSON(metric))
+
+ // Output:
+ // {"label":[{"name":"code","value":"200"},{"name":"method","value":"get"},{"name":"owner","value":"example"}],"histogram":{"sampleCount":"4711","sampleSum":403.34,"bucket":[{"cumulativeCount":"121","upperBound":25},{"cumulativeCount":"2403","upperBound":50},{"cumulativeCount":"3221","upperBound":100},{"cumulativeCount":"4233","upperBound":200}],"createdTimestamp":"2024-06-29T14:19:24.000000123Z"}}
+}
+
func ExampleNewConstHistogram_WithExemplar() {
desc := prometheus.NewDesc(
"http_request_duration_seconds",
diff --git prometheus/gen_go_collector_metrics_set.go prometheus/gen_go_collector_metrics_set.go
index 2622b71e4..f1db3d8d1 100644
--- prometheus/gen_go_collector_metrics_set.go
+++ prometheus/gen_go_collector_metrics_set.go
@@ -63,16 +63,29 @@ func main() {
v := goVersion(gv.Segments()[1])
log.Printf("generating metrics for Go version %q", v)
+ allDesc := metrics.All()
+
+ // Find default metrics.
+ var defaultDesc []metrics.Description
+ for _, d := range allDesc {
+ if !internal.GoCollectorDefaultRuntimeMetrics.MatchString(d.Name) {
+ continue
+ }
+ defaultDesc = append(defaultDesc, d)
+ }
+
// Generate code.
var buf bytes.Buffer
err = testFile.Execute(&buf, struct {
- Descriptions []metrics.Description
- GoVersion goVersion
- Cardinality int
+ AllDescriptions []metrics.Description
+ DefaultDescriptions []metrics.Description
+ GoVersion goVersion
+ Cardinality int
}{
- Descriptions: metrics.All(),
- GoVersion: v,
- Cardinality: rmCardinality(),
+ AllDescriptions: allDesc,
+ DefaultDescriptions: defaultDesc,
+ GoVersion: v,
+ Cardinality: rmCardinality(),
})
if err != nil {
log.Fatalf("executing template: %v", err)
@@ -167,14 +180,25 @@ var testFile = template.Must(template.New("testFile").Funcs(map[string]interface
package prometheus
-var expectedRuntimeMetrics = map[string]string{
-{{- range .Descriptions -}}
+var (
+ expectedRuntimeMetrics = map[string]string{
+{{- range .AllDescriptions -}}
{{- $trans := rm2prom . -}}
{{- if ne $trans "" }}
{{.Name | printf "%q"}}: {{$trans | printf "%q"}},
{{- end -}}
{{end}}
-}
+ }
+
+ expMetrics = map[string]string{
+{{- range .DefaultDescriptions -}}
+ {{- $trans := rm2prom . -}}
+ {{- if ne $trans "" }}
+ {{.Name | printf "%q"}}: {{$trans | printf "%q"}},
+ {{- end -}}
+{{end}}
+ }
+)
const expectedRuntimeMetricsCardinality = {{.Cardinality}}
`))
diff --git prometheus/go_collector.go prometheus/go_collector.go
index ad9a71a5e..520cbd7d4 100644
--- prometheus/go_collector.go
+++ prometheus/go_collector.go
@@ -22,13 +22,13 @@ import (
// goRuntimeMemStats provides the metrics initially provided by runtime.ReadMemStats.
// From Go 1.17 those similar (and better) statistics are provided by runtime/metrics, so
// while eval closure works on runtime.MemStats, the struct from Go 1.17+ is
-// populated using runtime/metrics.
+// populated using runtime/metrics. Those are the defaults we can't alter.
func goRuntimeMemStats() memStatsMetrics {
return memStatsMetrics{
{
desc: NewDesc(
memstatNamespace("alloc_bytes"),
- "Number of bytes allocated and still in use.",
+ "Number of bytes allocated in heap and currently in use. Equals to /memory/classes/heap/objects:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Alloc) },
@@ -36,7 +36,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("alloc_bytes_total"),
- "Total number of bytes allocated, even if freed.",
+ "Total number of bytes allocated in heap until now, even if released already. Equals to /gc/heap/allocs:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.TotalAlloc) },
@@ -44,23 +44,16 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("sys_bytes"),
- "Number of bytes obtained from system.",
+ "Number of bytes obtained from system. Equals to /memory/classes/total:byte.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Sys) },
valType: GaugeValue,
- }, {
- desc: NewDesc(
- memstatNamespace("lookups_total"),
- "Total number of pointer lookups.",
- nil, nil,
- ),
- eval: func(ms *runtime.MemStats) float64 { return float64(ms.Lookups) },
- valType: CounterValue,
}, {
desc: NewDesc(
memstatNamespace("mallocs_total"),
- "Total number of mallocs.",
+ // TODO(bwplotka): We could add go_memstats_heap_objects, probably useful for discovery. Let's gather more feedback, kind of a waste of bytes for everybody for compatibility reasons to keep both, and we can't really rename/remove useful metric.
+ "Total number of heap objects allocated, both live and gc-ed. Semantically a counter version for go_memstats_heap_objects gauge. Equals to /gc/heap/allocs:objects + /gc/heap/tiny/allocs:objects.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Mallocs) },
@@ -68,7 +61,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("frees_total"),
- "Total number of frees.",
+ "Total number of heap objects frees. Equals to /gc/heap/frees:objects + /gc/heap/tiny/allocs:objects.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.Frees) },
@@ -76,7 +69,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("heap_alloc_bytes"),
- "Number of heap bytes allocated and still in use.",
+ "Number of heap bytes allocated and currently in use, same as go_memstats_alloc_bytes. Equals to /memory/classes/heap/objects:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapAlloc) },
@@ -84,7 +77,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("heap_sys_bytes"),
- "Number of heap bytes obtained from system.",
+ "Number of heap bytes obtained from system. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes + /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapSys) },
@@ -92,7 +85,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("heap_idle_bytes"),
- "Number of heap bytes waiting to be used.",
+ "Number of heap bytes waiting to be used. Equals to /memory/classes/heap/released:bytes + /memory/classes/heap/free:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapIdle) },
@@ -100,7 +93,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("heap_inuse_bytes"),
- "Number of heap bytes that are in use.",
+ "Number of heap bytes that are in use. Equals to /memory/classes/heap/objects:bytes + /memory/classes/heap/unused:bytes",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapInuse) },
@@ -108,7 +101,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("heap_released_bytes"),
- "Number of heap bytes released to OS.",
+ "Number of heap bytes released to OS. Equals to /memory/classes/heap/released:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) },
@@ -116,7 +109,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("heap_objects"),
- "Number of allocated objects.",
+ "Number of currently allocated objects. Equals to /gc/heap/objects:objects.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapObjects) },
@@ -124,7 +117,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("stack_inuse_bytes"),
- "Number of bytes in use by the stack allocator.",
+ "Number of bytes obtained from system for stack allocator in non-CGO environments. Equals to /memory/classes/heap/stacks:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackInuse) },
@@ -132,7 +125,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("stack_sys_bytes"),
- "Number of bytes obtained from system for stack allocator.",
+ "Number of bytes obtained from system for stack allocator. Equals to /memory/classes/heap/stacks:bytes + /memory/classes/os-stacks:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackSys) },
@@ -140,7 +133,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("mspan_inuse_bytes"),
- "Number of bytes in use by mspan structures.",
+ "Number of bytes in use by mspan structures. Equals to /memory/classes/metadata/mspan/inuse:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanInuse) },
@@ -148,7 +141,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("mspan_sys_bytes"),
- "Number of bytes used for mspan structures obtained from system.",
+ "Number of bytes used for mspan structures obtained from system. Equals to /memory/classes/metadata/mspan/inuse:bytes + /memory/classes/metadata/mspan/free:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanSys) },
@@ -156,7 +149,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("mcache_inuse_bytes"),
- "Number of bytes in use by mcache structures.",
+ "Number of bytes in use by mcache structures. Equals to /memory/classes/metadata/mcache/inuse:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheInuse) },
@@ -164,7 +157,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("mcache_sys_bytes"),
- "Number of bytes used for mcache structures obtained from system.",
+ "Number of bytes used for mcache structures obtained from system. Equals to /memory/classes/metadata/mcache/inuse:bytes + /memory/classes/metadata/mcache/free:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheSys) },
@@ -172,7 +165,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("buck_hash_sys_bytes"),
- "Number of bytes used by the profiling bucket hash table.",
+ "Number of bytes used by the profiling bucket hash table. Equals to /memory/classes/profiling/buckets:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.BuckHashSys) },
@@ -180,7 +173,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("gc_sys_bytes"),
- "Number of bytes used for garbage collection system metadata.",
+ "Number of bytes used for garbage collection system metadata. Equals to /memory/classes/metadata/other:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.GCSys) },
@@ -188,7 +181,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("other_sys_bytes"),
- "Number of bytes used for other system allocations.",
+ "Number of bytes used for other system allocations. Equals to /memory/classes/other:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.OtherSys) },
@@ -196,7 +189,7 @@ func goRuntimeMemStats() memStatsMetrics {
}, {
desc: NewDesc(
memstatNamespace("next_gc_bytes"),
- "Number of heap bytes when next garbage collection will take place.",
+ "Number of heap bytes when next garbage collection will take place. Equals to /gc/heap/goal:bytes.",
nil, nil,
),
eval: func(ms *runtime.MemStats) float64 { return float64(ms.NextGC) },
@@ -225,7 +218,7 @@ func newBaseGoCollector() baseGoCollector {
nil, nil),
gcDesc: NewDesc(
"go_gc_duration_seconds",
- "A summary of the pause duration of garbage collection cycles.",
+ "A summary of the wall-time pause (stop-the-world) duration in garbage collection cycles.",
nil, nil),
gcLastTimeDesc: NewDesc(
"go_memstats_last_gc_time_seconds",
diff --git prometheus/go_collector_latest.go prometheus/go_collector_latest.go
index 2d8d9f64f..511746417 100644
--- prometheus/go_collector_latest.go
+++ prometheus/go_collector_latest.go
@@ -17,6 +17,7 @@
package prometheus
import (
+ "fmt"
"math"
"runtime"
"runtime/metrics"
@@ -153,7 +154,8 @@ func defaultGoCollectorOptions() internal.GoCollectorOptions {
"/gc/heap/frees-by-size:bytes": goGCHeapFreesBytes,
},
RuntimeMetricRules: []internal.GoCollectorRule{
- //{Matcher: regexp.MustCompile("")},
+ // Recommended metrics we want by default from runtime/metrics.
+ {Matcher: internal.GoCollectorDefaultRuntimeMetrics},
},
}
}
@@ -203,6 +205,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
// to fail here. This condition is tested in TestExpectedRuntimeMetrics.
continue
}
+ help := attachOriginalName(d.Description.Description, d.Name)
sampleBuf = append(sampleBuf, metrics.Sample{Name: d.Name})
sampleMap[d.Name] = &sampleBuf[len(sampleBuf)-1]
@@ -214,7 +217,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
m = newBatchHistogram(
NewDesc(
BuildFQName(namespace, subsystem, name),
- d.Description.Description,
+ help,
nil,
nil,
),
@@ -226,7 +229,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
Namespace: namespace,
Subsystem: subsystem,
Name: name,
- Help: d.Description.Description,
+ Help: help,
},
)
} else {
@@ -234,7 +237,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
Namespace: namespace,
Subsystem: subsystem,
Name: name,
- Help: d.Description.Description,
+ Help: help,
})
}
metricSet = append(metricSet, m)
@@ -284,6 +287,10 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector {
}
}
+func attachOriginalName(desc, origName string) string {
+ return fmt.Sprintf("%s Sourced from %s", desc, origName)
+}
+
// Describe returns all descriptions of the collector.
func (c *goCollector) Describe(ch chan<- *Desc) {
c.base.Describe(ch)
@@ -376,13 +383,13 @@ func unwrapScalarRMValue(v metrics.Value) float64 {
//
// This should never happen because we always populate our metric
// set from the runtime/metrics package.
- panic("unexpected unsupported metric")
+ panic("unexpected bad kind metric")
default:
// Unsupported metric kind.
//
// This should never happen because we check for this during initialization
// and flag and filter metrics whose kinds we don't understand.
- panic("unexpected unsupported metric kind")
+ panic(fmt.Sprintf("unexpected unsupported metric: %v", v.Kind()))
}
}
diff --git prometheus/go_collector_latest_test.go prometheus/go_collector_latest_test.go
index 551f49797..2fbe01bae 100644
--- prometheus/go_collector_latest_test.go
+++ prometheus/go_collector_latest_test.go
@@ -74,6 +74,13 @@ func addExpectedRuntimeMetrics(metrics map[string]struct{}) map[string]struct{}
return metrics
}
+func addExpectedDefaultRuntimeMetrics(metrics map[string]struct{}) map[string]struct{} {
+ for _, e := range expMetrics {
+ metrics[e] = struct{}{}
+ }
+ return metrics
+}
+
func TestGoCollector_ExposedMetrics(t *testing.T) {
for _, tcase := range []struct {
opts internal.GoCollectorOptions
@@ -86,8 +93,9 @@ func TestGoCollector_ExposedMetrics(t *testing.T) {
expectedFQNameSet: expectedBaseMetrics(),
},
{
- // Default, only MemStats.
- expectedFQNameSet: addExpectedRuntimeMemStats(expectedBaseMetrics()),
+ // Default, only MemStats and default Runtime metrics.
+ opts: defaultGoCollectorOptions(),
+ expectedFQNameSet: addExpectedDefaultRuntimeMetrics(addExpectedRuntimeMemStats(expectedBaseMetrics())),
},
{
// Get all runtime/metrics without MemStats.
diff --git prometheus/go_collector_metrics_go120_test.go prometheus/go_collector_metrics_go120_test.go
index 60517c4c2..4c1ca38d3 100644
--- prometheus/go_collector_metrics_go120_test.go
+++ prometheus/go_collector_metrics_go120_test.go
@@ -6,52 +6,58 @@
package prometheus
-var expectedRuntimeMetrics = map[string]string{
- "/cgo/go-to-c-calls:calls": "go_cgo_go_to_c_calls_calls_total",
- "/cpu/classes/gc/mark/assist:cpu-seconds": "go_cpu_classes_gc_mark_assist_cpu_seconds_total",
- "/cpu/classes/gc/mark/dedicated:cpu-seconds": "go_cpu_classes_gc_mark_dedicated_cpu_seconds_total",
- "/cpu/classes/gc/mark/idle:cpu-seconds": "go_cpu_classes_gc_mark_idle_cpu_seconds_total",
- "/cpu/classes/gc/pause:cpu-seconds": "go_cpu_classes_gc_pause_cpu_seconds_total",
- "/cpu/classes/gc/total:cpu-seconds": "go_cpu_classes_gc_total_cpu_seconds_total",
- "/cpu/classes/idle:cpu-seconds": "go_cpu_classes_idle_cpu_seconds_total",
- "/cpu/classes/scavenge/assist:cpu-seconds": "go_cpu_classes_scavenge_assist_cpu_seconds_total",
- "/cpu/classes/scavenge/background:cpu-seconds": "go_cpu_classes_scavenge_background_cpu_seconds_total",
- "/cpu/classes/scavenge/total:cpu-seconds": "go_cpu_classes_scavenge_total_cpu_seconds_total",
- "/cpu/classes/total:cpu-seconds": "go_cpu_classes_total_cpu_seconds_total",
- "/cpu/classes/user:cpu-seconds": "go_cpu_classes_user_cpu_seconds_total",
- "/gc/cycles/automatic:gc-cycles": "go_gc_cycles_automatic_gc_cycles_total",
- "/gc/cycles/forced:gc-cycles": "go_gc_cycles_forced_gc_cycles_total",
- "/gc/cycles/total:gc-cycles": "go_gc_cycles_total_gc_cycles_total",
- "/gc/heap/allocs-by-size:bytes": "go_gc_heap_allocs_by_size_bytes",
- "/gc/heap/allocs:bytes": "go_gc_heap_allocs_bytes_total",
- "/gc/heap/allocs:objects": "go_gc_heap_allocs_objects_total",
- "/gc/heap/frees-by-size:bytes": "go_gc_heap_frees_by_size_bytes",
- "/gc/heap/frees:bytes": "go_gc_heap_frees_bytes_total",
- "/gc/heap/frees:objects": "go_gc_heap_frees_objects_total",
- "/gc/heap/goal:bytes": "go_gc_heap_goal_bytes",
- "/gc/heap/objects:objects": "go_gc_heap_objects_objects",
- "/gc/heap/tiny/allocs:objects": "go_gc_heap_tiny_allocs_objects_total",
- "/gc/limiter/last-enabled:gc-cycle": "go_gc_limiter_last_enabled_gc_cycle",
- "/gc/pauses:seconds": "go_gc_pauses_seconds",
- "/gc/stack/starting-size:bytes": "go_gc_stack_starting_size_bytes",
- "/memory/classes/heap/free:bytes": "go_memory_classes_heap_free_bytes",
- "/memory/classes/heap/objects:bytes": "go_memory_classes_heap_objects_bytes",
- "/memory/classes/heap/released:bytes": "go_memory_classes_heap_released_bytes",
- "/memory/classes/heap/stacks:bytes": "go_memory_classes_heap_stacks_bytes",
- "/memory/classes/heap/unused:bytes": "go_memory_classes_heap_unused_bytes",
- "/memory/classes/metadata/mcache/free:bytes": "go_memory_classes_metadata_mcache_free_bytes",
- "/memory/classes/metadata/mcache/inuse:bytes": "go_memory_classes_metadata_mcache_inuse_bytes",
- "/memory/classes/metadata/mspan/free:bytes": "go_memory_classes_metadata_mspan_free_bytes",
- "/memory/classes/metadata/mspan/inuse:bytes": "go_memory_classes_metadata_mspan_inuse_bytes",
- "/memory/classes/metadata/other:bytes": "go_memory_classes_metadata_other_bytes",
- "/memory/classes/os-stacks:bytes": "go_memory_classes_os_stacks_bytes",
- "/memory/classes/other:bytes": "go_memory_classes_other_bytes",
- "/memory/classes/profiling/buckets:bytes": "go_memory_classes_profiling_buckets_bytes",
- "/memory/classes/total:bytes": "go_memory_classes_total_bytes",
- "/sched/gomaxprocs:threads": "go_sched_gomaxprocs_threads",
- "/sched/goroutines:goroutines": "go_sched_goroutines_goroutines",
- "/sched/latencies:seconds": "go_sched_latencies_seconds",
- "/sync/mutex/wait/total:seconds": "go_sync_mutex_wait_total_seconds_total",
-}
+var (
+ expectedRuntimeMetrics = map[string]string{
+ "/cgo/go-to-c-calls:calls": "go_cgo_go_to_c_calls_calls_total",
+ "/cpu/classes/gc/mark/assist:cpu-seconds": "go_cpu_classes_gc_mark_assist_cpu_seconds_total",
+ "/cpu/classes/gc/mark/dedicated:cpu-seconds": "go_cpu_classes_gc_mark_dedicated_cpu_seconds_total",
+ "/cpu/classes/gc/mark/idle:cpu-seconds": "go_cpu_classes_gc_mark_idle_cpu_seconds_total",
+ "/cpu/classes/gc/pause:cpu-seconds": "go_cpu_classes_gc_pause_cpu_seconds_total",
+ "/cpu/classes/gc/total:cpu-seconds": "go_cpu_classes_gc_total_cpu_seconds_total",
+ "/cpu/classes/idle:cpu-seconds": "go_cpu_classes_idle_cpu_seconds_total",
+ "/cpu/classes/scavenge/assist:cpu-seconds": "go_cpu_classes_scavenge_assist_cpu_seconds_total",
+ "/cpu/classes/scavenge/background:cpu-seconds": "go_cpu_classes_scavenge_background_cpu_seconds_total",
+ "/cpu/classes/scavenge/total:cpu-seconds": "go_cpu_classes_scavenge_total_cpu_seconds_total",
+ "/cpu/classes/total:cpu-seconds": "go_cpu_classes_total_cpu_seconds_total",
+ "/cpu/classes/user:cpu-seconds": "go_cpu_classes_user_cpu_seconds_total",
+ "/gc/cycles/automatic:gc-cycles": "go_gc_cycles_automatic_gc_cycles_total",
+ "/gc/cycles/forced:gc-cycles": "go_gc_cycles_forced_gc_cycles_total",
+ "/gc/cycles/total:gc-cycles": "go_gc_cycles_total_gc_cycles_total",
+ "/gc/heap/allocs-by-size:bytes": "go_gc_heap_allocs_by_size_bytes",
+ "/gc/heap/allocs:bytes": "go_gc_heap_allocs_bytes_total",
+ "/gc/heap/allocs:objects": "go_gc_heap_allocs_objects_total",
+ "/gc/heap/frees-by-size:bytes": "go_gc_heap_frees_by_size_bytes",
+ "/gc/heap/frees:bytes": "go_gc_heap_frees_bytes_total",
+ "/gc/heap/frees:objects": "go_gc_heap_frees_objects_total",
+ "/gc/heap/goal:bytes": "go_gc_heap_goal_bytes",
+ "/gc/heap/objects:objects": "go_gc_heap_objects_objects",
+ "/gc/heap/tiny/allocs:objects": "go_gc_heap_tiny_allocs_objects_total",
+ "/gc/limiter/last-enabled:gc-cycle": "go_gc_limiter_last_enabled_gc_cycle",
+ "/gc/pauses:seconds": "go_gc_pauses_seconds",
+ "/gc/stack/starting-size:bytes": "go_gc_stack_starting_size_bytes",
+ "/memory/classes/heap/free:bytes": "go_memory_classes_heap_free_bytes",
+ "/memory/classes/heap/objects:bytes": "go_memory_classes_heap_objects_bytes",
+ "/memory/classes/heap/released:bytes": "go_memory_classes_heap_released_bytes",
+ "/memory/classes/heap/stacks:bytes": "go_memory_classes_heap_stacks_bytes",
+ "/memory/classes/heap/unused:bytes": "go_memory_classes_heap_unused_bytes",
+ "/memory/classes/metadata/mcache/free:bytes": "go_memory_classes_metadata_mcache_free_bytes",
+ "/memory/classes/metadata/mcache/inuse:bytes": "go_memory_classes_metadata_mcache_inuse_bytes",
+ "/memory/classes/metadata/mspan/free:bytes": "go_memory_classes_metadata_mspan_free_bytes",
+ "/memory/classes/metadata/mspan/inuse:bytes": "go_memory_classes_metadata_mspan_inuse_bytes",
+ "/memory/classes/metadata/other:bytes": "go_memory_classes_metadata_other_bytes",
+ "/memory/classes/os-stacks:bytes": "go_memory_classes_os_stacks_bytes",
+ "/memory/classes/other:bytes": "go_memory_classes_other_bytes",
+ "/memory/classes/profiling/buckets:bytes": "go_memory_classes_profiling_buckets_bytes",
+ "/memory/classes/total:bytes": "go_memory_classes_total_bytes",
+ "/sched/gomaxprocs:threads": "go_sched_gomaxprocs_threads",
+ "/sched/goroutines:goroutines": "go_sched_goroutines_goroutines",
+ "/sched/latencies:seconds": "go_sched_latencies_seconds",
+ "/sync/mutex/wait/total:seconds": "go_sync_mutex_wait_total_seconds_total",
+ }
+
+ expMetrics = map[string]string{
+ "/sched/gomaxprocs:threads": "go_sched_gomaxprocs_threads",
+ }
+)
const expectedRuntimeMetricsCardinality = 89
diff --git prometheus/go_collector_metrics_go121_test.go prometheus/go_collector_metrics_go121_test.go
index 90d05bd0e..217b04fc7 100644
--- prometheus/go_collector_metrics_go121_test.go
+++ prometheus/go_collector_metrics_go121_test.go
@@ -6,77 +6,86 @@
package prometheus
-var expectedRuntimeMetrics = map[string]string{
- "/cgo/go-to-c-calls:calls": "go_cgo_go_to_c_calls_calls_total",
- "/cpu/classes/gc/mark/assist:cpu-seconds": "go_cpu_classes_gc_mark_assist_cpu_seconds_total",
- "/cpu/classes/gc/mark/dedicated:cpu-seconds": "go_cpu_classes_gc_mark_dedicated_cpu_seconds_total",
- "/cpu/classes/gc/mark/idle:cpu-seconds": "go_cpu_classes_gc_mark_idle_cpu_seconds_total",
- "/cpu/classes/gc/pause:cpu-seconds": "go_cpu_classes_gc_pause_cpu_seconds_total",
- "/cpu/classes/gc/total:cpu-seconds": "go_cpu_classes_gc_total_cpu_seconds_total",
- "/cpu/classes/idle:cpu-seconds": "go_cpu_classes_idle_cpu_seconds_total",
- "/cpu/classes/scavenge/assist:cpu-seconds": "go_cpu_classes_scavenge_assist_cpu_seconds_total",
- "/cpu/classes/scavenge/background:cpu-seconds": "go_cpu_classes_scavenge_background_cpu_seconds_total",
- "/cpu/classes/scavenge/total:cpu-seconds": "go_cpu_classes_scavenge_total_cpu_seconds_total",
- "/cpu/classes/total:cpu-seconds": "go_cpu_classes_total_cpu_seconds_total",
- "/cpu/classes/user:cpu-seconds": "go_cpu_classes_user_cpu_seconds_total",
- "/gc/cycles/automatic:gc-cycles": "go_gc_cycles_automatic_gc_cycles_total",
- "/gc/cycles/forced:gc-cycles": "go_gc_cycles_forced_gc_cycles_total",
- "/gc/cycles/total:gc-cycles": "go_gc_cycles_total_gc_cycles_total",
- "/gc/gogc:percent": "go_gc_gogc_percent",
- "/gc/gomemlimit:bytes": "go_gc_gomemlimit_bytes",
- "/gc/heap/allocs-by-size:bytes": "go_gc_heap_allocs_by_size_bytes",
- "/gc/heap/allocs:bytes": "go_gc_heap_allocs_bytes_total",
- "/gc/heap/allocs:objects": "go_gc_heap_allocs_objects_total",
- "/gc/heap/frees-by-size:bytes": "go_gc_heap_frees_by_size_bytes",
- "/gc/heap/frees:bytes": "go_gc_heap_frees_bytes_total",
- "/gc/heap/frees:objects": "go_gc_heap_frees_objects_total",
- "/gc/heap/goal:bytes": "go_gc_heap_goal_bytes",
- "/gc/heap/live:bytes": "go_gc_heap_live_bytes",
- "/gc/heap/objects:objects": "go_gc_heap_objects_objects",
- "/gc/heap/tiny/allocs:objects": "go_gc_heap_tiny_allocs_objects_total",
- "/gc/limiter/last-enabled:gc-cycle": "go_gc_limiter_last_enabled_gc_cycle",
- "/gc/pauses:seconds": "go_gc_pauses_seconds",
- "/gc/scan/globals:bytes": "go_gc_scan_globals_bytes",
- "/gc/scan/heap:bytes": "go_gc_scan_heap_bytes",
- "/gc/scan/stack:bytes": "go_gc_scan_stack_bytes",
- "/gc/scan/total:bytes": "go_gc_scan_total_bytes",
- "/gc/stack/starting-size:bytes": "go_gc_stack_starting_size_bytes",
- "/godebug/non-default-behavior/execerrdot:events": "go_godebug_non_default_behavior_execerrdot_events_total",
- "/godebug/non-default-behavior/gocachehash:events": "go_godebug_non_default_behavior_gocachehash_events_total",
- "/godebug/non-default-behavior/gocachetest:events": "go_godebug_non_default_behavior_gocachetest_events_total",
- "/godebug/non-default-behavior/gocacheverify:events": "go_godebug_non_default_behavior_gocacheverify_events_total",
- "/godebug/non-default-behavior/http2client:events": "go_godebug_non_default_behavior_http2client_events_total",
- "/godebug/non-default-behavior/http2server:events": "go_godebug_non_default_behavior_http2server_events_total",
- "/godebug/non-default-behavior/installgoroot:events": "go_godebug_non_default_behavior_installgoroot_events_total",
- "/godebug/non-default-behavior/jstmpllitinterp:events": "go_godebug_non_default_behavior_jstmpllitinterp_events_total",
- "/godebug/non-default-behavior/multipartmaxheaders:events": "go_godebug_non_default_behavior_multipartmaxheaders_events_total",
- "/godebug/non-default-behavior/multipartmaxparts:events": "go_godebug_non_default_behavior_multipartmaxparts_events_total",
- "/godebug/non-default-behavior/multipathtcp:events": "go_godebug_non_default_behavior_multipathtcp_events_total",
- "/godebug/non-default-behavior/panicnil:events": "go_godebug_non_default_behavior_panicnil_events_total",
- "/godebug/non-default-behavior/randautoseed:events": "go_godebug_non_default_behavior_randautoseed_events_total",
- "/godebug/non-default-behavior/tarinsecurepath:events": "go_godebug_non_default_behavior_tarinsecurepath_events_total",
- "/godebug/non-default-behavior/tlsmaxrsasize:events": "go_godebug_non_default_behavior_tlsmaxrsasize_events_total",
- "/godebug/non-default-behavior/x509sha1:events": "go_godebug_non_default_behavior_x509sha1_events_total",
- "/godebug/non-default-behavior/x509usefallbackroots:events": "go_godebug_non_default_behavior_x509usefallbackroots_events_total",
- "/godebug/non-default-behavior/zipinsecurepath:events": "go_godebug_non_default_behavior_zipinsecurepath_events_total",
- "/memory/classes/heap/free:bytes": "go_memory_classes_heap_free_bytes",
- "/memory/classes/heap/objects:bytes": "go_memory_classes_heap_objects_bytes",
- "/memory/classes/heap/released:bytes": "go_memory_classes_heap_released_bytes",
- "/memory/classes/heap/stacks:bytes": "go_memory_classes_heap_stacks_bytes",
- "/memory/classes/heap/unused:bytes": "go_memory_classes_heap_unused_bytes",
- "/memory/classes/metadata/mcache/free:bytes": "go_memory_classes_metadata_mcache_free_bytes",
- "/memory/classes/metadata/mcache/inuse:bytes": "go_memory_classes_metadata_mcache_inuse_bytes",
- "/memory/classes/metadata/mspan/free:bytes": "go_memory_classes_metadata_mspan_free_bytes",
- "/memory/classes/metadata/mspan/inuse:bytes": "go_memory_classes_metadata_mspan_inuse_bytes",
- "/memory/classes/metadata/other:bytes": "go_memory_classes_metadata_other_bytes",
- "/memory/classes/os-stacks:bytes": "go_memory_classes_os_stacks_bytes",
- "/memory/classes/other:bytes": "go_memory_classes_other_bytes",
- "/memory/classes/profiling/buckets:bytes": "go_memory_classes_profiling_buckets_bytes",
- "/memory/classes/total:bytes": "go_memory_classes_total_bytes",
- "/sched/gomaxprocs:threads": "go_sched_gomaxprocs_threads",
- "/sched/goroutines:goroutines": "go_sched_goroutines_goroutines",
- "/sched/latencies:seconds": "go_sched_latencies_seconds",
- "/sync/mutex/wait/total:seconds": "go_sync_mutex_wait_total_seconds_total",
-}
+var (
+ expectedRuntimeMetrics = map[string]string{
+ "/cgo/go-to-c-calls:calls": "go_cgo_go_to_c_calls_calls_total",
+ "/cpu/classes/gc/mark/assist:cpu-seconds": "go_cpu_classes_gc_mark_assist_cpu_seconds_total",
+ "/cpu/classes/gc/mark/dedicated:cpu-seconds": "go_cpu_classes_gc_mark_dedicated_cpu_seconds_total",
+ "/cpu/classes/gc/mark/idle:cpu-seconds": "go_cpu_classes_gc_mark_idle_cpu_seconds_total",
+ "/cpu/classes/gc/pause:cpu-seconds": "go_cpu_classes_gc_pause_cpu_seconds_total",
+ "/cpu/classes/gc/total:cpu-seconds": "go_cpu_classes_gc_total_cpu_seconds_total",
+ "/c,pu/classes/idle:cpu-seconds": "go_cpu_classes_idle_cpu_seconds_total",
+ "/cpu/classes/scavenge/assist:cpu-seconds": "go_cpu_classes_scavenge_assist_cpu_seconds_total",
+ "/cpu/classes/scavenge/background:cpu-seconds": "go_cpu_classes_scavenge_background_cpu_seconds_total",
+ "/cpu/classes/scavenge/total:cpu-seconds": "go_cpu_classes_scavenge_total_cpu_seconds_total",
+ "/cpu/classes/total:cpu-seconds": "go_cpu_classes_total_cpu_seconds_total",
+ "/cpu/classes/user:cpu-seconds": "go_cpu_classes_user_cpu_seconds_total",
+ "/gc/cycles/automatic:gc-cycles": "go_gc_cycles_automatic_gc_cycles_total",
+ "/gc/cycles/forced:gc-cycles": "go_gc_cycles_forced_gc_cycles_total",
+ "/gc/cycles/total:gc-cycles": "go_gc_cycles_total_gc_cycles_total",
+ "/gc/gogc:percent": "go_gc_gogc_percent",
+ "/gc/gomemlimit:bytes": "go_gc_gomemlimit_bytes",
+ "/gc/heap/allocs-by-size:bytes": "go_gc_heap_allocs_by_size_bytes",
+ "/gc/heap/allocs:bytes": "go_gc_heap_allocs_bytes_total",
+ "/gc/heap/allocs:objects": "go_gc_heap_allocs_objects_total",
+ "/gc/heap/frees-by-size:bytes": "go_gc_heap_frees_by_size_bytes",
+ "/gc/heap/frees:bytes": "go_gc_heap_frees_bytes_total",
+ "/gc/heap/frees:objects": "go_gc_heap_frees_objects_total",
+ "/gc/heap/goal:bytes": "go_gc_heap_goal_bytes",
+ "/gc/heap/live:bytes": "go_gc_heap_live_bytes",
+ "/gc/heap/objects:objects": "go_gc_heap_objects_objects",
+ "/gc/heap/tiny/allocs:objects": "go_gc_heap_tiny_allocs_objects_total",
+ "/gc/limiter/last-enabled:gc-cycle": "go_gc_limiter_last_enabled_gc_cycle",
+ "/gc/pauses:seconds": "go_gc_pauses_seconds",
+ "/gc/scan/globals:bytes": "go_gc_scan_globals_bytes",
+ "/gc/scan/heap:bytes": "go_gc_scan_heap_bytes",
+ "/gc/scan/stack:bytes": "go_gc_scan_stack_bytes",
+ "/gc/scan/total:bytes": "go_gc_scan_total_bytes",
+ "/gc/stack/starting-size:bytes": "go_gc_stack_starting_size_bytes",
+ "/godebug/non-default-behavior/execerrdot:events": "go_godebug_non_default_behavior_execerrdot_events_total",
+ "/godebug/non-default-behavior/gocachehash:events": "go_godebug_non_default_behavior_gocachehash_events_total",
+ "/godebug/non-default-behavior/gocachetest:events": "go_godebug_non_default_behavior_gocachetest_events_total",
+ "/godebug/non-default-behavior/gocacheverify:events": "go_godebug_non_default_behavior_gocacheverify_events_total",
+ "/godebug/non-default-behavior/http2client:events": "go_godebug_non_default_behavior_http2client_events_total",
+ "/godebug/non-default-behavior/http2server:events": "go_godebug_non_default_behavior_http2server_events_total",
+ "/godebug/non-default-behavior/installgoroot:events": "go_godebug_non_default_behavior_installgoroot_events_total",
+ "/godebug/non-default-behavior/jstmpllitinterp:events": "go_godebug_non_default_behavior_jstmpllitinterp_events_total",
+ "/godebug/non-default-behavior/multipartmaxheaders:events": "go_godebug_non_default_behavior_multipartmaxheaders_events_total",
+ "/godebug/non-default-behavior/multipartmaxparts:events": "go_godebug_non_default_behavior_multipartmaxparts_events_total",
+ "/godebug/non-default-behavior/multipathtcp:events": "go_godebug_non_default_behavior_multipathtcp_events_total",
+ "/godebug/non-default-behavior/netedns0:events": "go_godebug_non_default_behavior_netedns0_events_total",
+ "/godebug/non-default-behavior/panicnil:events": "go_godebug_non_default_behavior_panicnil_events_total",
+ "/godebug/non-default-behavior/randautoseed:events": "go_godebug_non_default_behavior_randautoseed_events_total",
+ "/godebug/non-default-behavior/tarinsecurepath:events": "go_godebug_non_default_behavior_tarinsecurepath_events_total",
+ "/godebug/non-default-behavior/tlsmaxrsasize:events": "go_godebug_non_default_behavior_tlsmaxrsasize_events_total",
+ "/godebug/non-default-behavior/x509sha1:events": "go_godebug_non_default_behavior_x509sha1_events_total",
+ "/godebug/non-default-behavior/x509usefallbackroots:events": "go_godebug_non_default_behavior_x509usefallbackroots_events_total",
+ "/godebug/non-default-behavior/zipinsecurepath:events": "go_godebug_non_default_behavior_zipinsecurepath_events_total",
+ "/memory/classes/heap/free:bytes": "go_memory_classes_heap_free_bytes",
+ "/memory/classes/heap/objects:bytes": "go_memory_classes_heap_objects_bytes",
+ "/memory/classes/heap/released:bytes": "go_memory_classes_heap_released_bytes",
+ "/memory/classes/heap/stacks:bytes": "go_memory_classes_heap_stacks_bytes",
+ "/memory/classes/heap/unused:bytes": "go_memory_classes_heap_unused_bytes",
+ "/memory/classes/metadata/mcache/free:bytes": "go_memory_classes_metadata_mcache_free_bytes",
+ "/memory/classes/metadata/mcache/inuse:bytes": "go_memory_classes_metadata_mcache_inuse_bytes",
+ "/memory/classes/metadata/mspan/free:bytes": "go_memory_classes_metadata_mspan_free_bytes",
+ "/memory/classes/metadata/mspan/inuse:bytes": "go_memory_classes_metadata_mspan_inuse_bytes",
+ "/memory/classes/metadata/other:bytes": "go_memory_classes_metadata_other_bytes",
+ "/memory/classes/os-stacks:bytes": "go_memory_classes_os_stacks_bytes",
+ "/memory/classes/other:bytes": "go_memory_classes_other_bytes",
+ "/memory/classes/profiling/buckets:bytes": "go_memory_classes_profiling_buckets_bytes",
+ "/memory/classes/total:bytes": "go_memory_classes_total_bytes",
+ "/sched/gomaxprocs:threads": "go_sched_gomaxprocs_threads",
+ "/sched/goroutines:goroutines": "go_sched_goroutines_goroutines",
+ "/sched/latencies:seconds": "go_sched_latencies_seconds",
+ "/sync/mutex/wait/total:seconds": "go_sync_mutex_wait_total_seconds_total",
+ }
-const expectedRuntimeMetricsCardinality = 114
+ expMetrics = map[string]string{
+ "/gc/gogc:percent": "go_gc_gogc_percent",
+ "/gc/gomemlimit:bytes": "go_gc_gomemlimit_bytes",
+ "/sched/gomaxprocs:threads": "go_sched_gomaxprocs_threads",
+ }
+)
+
+const expectedRuntimeMetricsCardinality = 115
diff --git prometheus/go_collector_metrics_go122_test.go prometheus/go_collector_metrics_go122_test.go
index 86998b8a6..6aa3a9f7e 100644
--- prometheus/go_collector_metrics_go122_test.go
+++ prometheus/go_collector_metrics_go122_test.go
@@ -6,88 +6,97 @@
package prometheus
-var expectedRuntimeMetrics = map[string]string{
- "/cgo/go-to-c-calls:calls": "go_cgo_go_to_c_calls_calls_total",
- "/cpu/classes/gc/mark/assist:cpu-seconds": "go_cpu_classes_gc_mark_assist_cpu_seconds_total",
- "/cpu/classes/gc/mark/dedicated:cpu-seconds": "go_cpu_classes_gc_mark_dedicated_cpu_seconds_total",
- "/cpu/classes/gc/mark/idle:cpu-seconds": "go_cpu_classes_gc_mark_idle_cpu_seconds_total",
- "/cpu/classes/gc/pause:cpu-seconds": "go_cpu_classes_gc_pause_cpu_seconds_total",
- "/cpu/classes/gc/total:cpu-seconds": "go_cpu_classes_gc_total_cpu_seconds_total",
- "/cpu/classes/idle:cpu-seconds": "go_cpu_classes_idle_cpu_seconds_total",
- "/cpu/classes/scavenge/assist:cpu-seconds": "go_cpu_classes_scavenge_assist_cpu_seconds_total",
- "/cpu/classes/scavenge/background:cpu-seconds": "go_cpu_classes_scavenge_background_cpu_seconds_total",
- "/cpu/classes/scavenge/total:cpu-seconds": "go_cpu_classes_scavenge_total_cpu_seconds_total",
- "/cpu/classes/total:cpu-seconds": "go_cpu_classes_total_cpu_seconds_total",
- "/cpu/classes/user:cpu-seconds": "go_cpu_classes_user_cpu_seconds_total",
- "/gc/cycles/automatic:gc-cycles": "go_gc_cycles_automatic_gc_cycles_total",
- "/gc/cycles/forced:gc-cycles": "go_gc_cycles_forced_gc_cycles_total",
- "/gc/cycles/total:gc-cycles": "go_gc_cycles_total_gc_cycles_total",
- "/gc/gogc:percent": "go_gc_gogc_percent",
- "/gc/gomemlimit:bytes": "go_gc_gomemlimit_bytes",
- "/gc/heap/allocs-by-size:bytes": "go_gc_heap_allocs_by_size_bytes",
- "/gc/heap/allocs:bytes": "go_gc_heap_allocs_bytes_total",
- "/gc/heap/allocs:objects": "go_gc_heap_allocs_objects_total",
- "/gc/heap/frees-by-size:bytes": "go_gc_heap_frees_by_size_bytes",
- "/gc/heap/frees:bytes": "go_gc_heap_frees_bytes_total",
- "/gc/heap/frees:objects": "go_gc_heap_frees_objects_total",
- "/gc/heap/goal:bytes": "go_gc_heap_goal_bytes",
- "/gc/heap/live:bytes": "go_gc_heap_live_bytes",
- "/gc/heap/objects:objects": "go_gc_heap_objects_objects",
- "/gc/heap/tiny/allocs:objects": "go_gc_heap_tiny_allocs_objects_total",
- "/gc/limiter/last-enabled:gc-cycle": "go_gc_limiter_last_enabled_gc_cycle",
- "/gc/pauses:seconds": "go_gc_pauses_seconds",
- "/gc/scan/globals:bytes": "go_gc_scan_globals_bytes",
- "/gc/scan/heap:bytes": "go_gc_scan_heap_bytes",
- "/gc/scan/stack:bytes": "go_gc_scan_stack_bytes",
- "/gc/scan/total:bytes": "go_gc_scan_total_bytes",
- "/gc/stack/starting-size:bytes": "go_gc_stack_starting_size_bytes",
- "/godebug/non-default-behavior/execerrdot:events": "go_godebug_non_default_behavior_execerrdot_events_total",
- "/godebug/non-default-behavior/gocachehash:events": "go_godebug_non_default_behavior_gocachehash_events_total",
- "/godebug/non-default-behavior/gocachetest:events": "go_godebug_non_default_behavior_gocachetest_events_total",
- "/godebug/non-default-behavior/gocacheverify:events": "go_godebug_non_default_behavior_gocacheverify_events_total",
- "/godebug/non-default-behavior/gotypesalias:events": "go_godebug_non_default_behavior_gotypesalias_events_total",
- "/godebug/non-default-behavior/http2client:events": "go_godebug_non_default_behavior_http2client_events_total",
- "/godebug/non-default-behavior/http2server:events": "go_godebug_non_default_behavior_http2server_events_total",
- "/godebug/non-default-behavior/httplaxcontentlength:events": "go_godebug_non_default_behavior_httplaxcontentlength_events_total",
- "/godebug/non-default-behavior/httpmuxgo121:events": "go_godebug_non_default_behavior_httpmuxgo121_events_total",
- "/godebug/non-default-behavior/installgoroot:events": "go_godebug_non_default_behavior_installgoroot_events_total",
- "/godebug/non-default-behavior/jstmpllitinterp:events": "go_godebug_non_default_behavior_jstmpllitinterp_events_total",
- "/godebug/non-default-behavior/multipartmaxheaders:events": "go_godebug_non_default_behavior_multipartmaxheaders_events_total",
- "/godebug/non-default-behavior/multipartmaxparts:events": "go_godebug_non_default_behavior_multipartmaxparts_events_total",
- "/godebug/non-default-behavior/multipathtcp:events": "go_godebug_non_default_behavior_multipathtcp_events_total",
- "/godebug/non-default-behavior/panicnil:events": "go_godebug_non_default_behavior_panicnil_events_total",
- "/godebug/non-default-behavior/randautoseed:events": "go_godebug_non_default_behavior_randautoseed_events_total",
- "/godebug/non-default-behavior/tarinsecurepath:events": "go_godebug_non_default_behavior_tarinsecurepath_events_total",
- "/godebug/non-default-behavior/tls10server:events": "go_godebug_non_default_behavior_tls10server_events_total",
- "/godebug/non-default-behavior/tlsmaxrsasize:events": "go_godebug_non_default_behavior_tlsmaxrsasize_events_total",
- "/godebug/non-default-behavior/tlsrsakex:events": "go_godebug_non_default_behavior_tlsrsakex_events_total",
- "/godebug/non-default-behavior/tlsunsafeekm:events": "go_godebug_non_default_behavior_tlsunsafeekm_events_total",
- "/godebug/non-default-behavior/x509sha1:events": "go_godebug_non_default_behavior_x509sha1_events_total",
- "/godebug/non-default-behavior/x509usefallbackroots:events": "go_godebug_non_default_behavior_x509usefallbackroots_events_total",
- "/godebug/non-default-behavior/x509usepolicies:events": "go_godebug_non_default_behavior_x509usepolicies_events_total",
- "/godebug/non-default-behavior/zipinsecurepath:events": "go_godebug_non_default_behavior_zipinsecurepath_events_total",
- "/memory/classes/heap/free:bytes": "go_memory_classes_heap_free_bytes",
- "/memory/classes/heap/objects:bytes": "go_memory_classes_heap_objects_bytes",
- "/memory/classes/heap/released:bytes": "go_memory_classes_heap_released_bytes",
- "/memory/classes/heap/stacks:bytes": "go_memory_classes_heap_stacks_bytes",
- "/memory/classes/heap/unused:bytes": "go_memory_classes_heap_unused_bytes",
- "/memory/classes/metadata/mcache/free:bytes": "go_memory_classes_metadata_mcache_free_bytes",
- "/memory/classes/metadata/mcache/inuse:bytes": "go_memory_classes_metadata_mcache_inuse_bytes",
- "/memory/classes/metadata/mspan/free:bytes": "go_memory_classes_metadata_mspan_free_bytes",
- "/memory/classes/metadata/mspan/inuse:bytes": "go_memory_classes_metadata_mspan_inuse_bytes",
- "/memory/classes/metadata/other:bytes": "go_memory_classes_metadata_other_bytes",
- "/memory/classes/os-stacks:bytes": "go_memory_classes_os_stacks_bytes",
- "/memory/classes/other:bytes": "go_memory_classes_other_bytes",
- "/memory/classes/profiling/buckets:bytes": "go_memory_classes_profiling_buckets_bytes",
- "/memory/classes/total:bytes": "go_memory_classes_total_bytes",
- "/sched/gomaxprocs:threads": "go_sched_gomaxprocs_threads",
- "/sched/goroutines:goroutines": "go_sched_goroutines_goroutines",
- "/sched/latencies:seconds": "go_sched_latencies_seconds",
- "/sched/pauses/stopping/gc:seconds": "go_sched_pauses_stopping_gc_seconds",
- "/sched/pauses/stopping/other:seconds": "go_sched_pauses_stopping_other_seconds",
- "/sched/pauses/total/gc:seconds": "go_sched_pauses_total_gc_seconds",
- "/sched/pauses/total/other:seconds": "go_sched_pauses_total_other_seconds",
- "/sync/mutex/wait/total:seconds": "go_sync_mutex_wait_total_seconds_total",
-}
+var (
+ expectedRuntimeMetrics = map[string]string{
+ "/cgo/go-to-c-calls:calls": "go_cgo_go_to_c_calls_calls_total",
+ "/cpu/classes/gc/mark/assist:cpu-seconds": "go_cpu_classes_gc_mark_assist_cpu_seconds_total",
+ "/cpu/classes/gc/mark/dedicated:cpu-seconds": "go_cpu_classes_gc_mark_dedicated_cpu_seconds_total",
+ "/cpu/classes/gc/mark/idle:cpu-seconds": "go_cpu_classes_gc_mark_idle_cpu_seconds_total",
+ "/cpu/classes/gc/pause:cpu-seconds": "go_cpu_classes_gc_pause_cpu_seconds_total",
+ "/cpu/classes/gc/total:cpu-seconds": "go_cpu_classes_gc_total_cpu_seconds_total",
+ "/cpu/classes/idle:cpu-seconds": "go_cpu_classes_idle_cpu_seconds_total",
+ "/cpu/classes/scavenge/assist:cpu-seconds": "go_cpu_classes_scavenge_assist_cpu_seconds_total",
+ "/cpu/classes/scavenge/background:cpu-seconds": "go_cpu_classes_scavenge_background_cpu_seconds_total",
+ "/cpu/classes/scavenge/total:cpu-seconds": "go_cpu_classes_scavenge_total_cpu_seconds_total",
+ "/cpu/classes/total:cpu-seconds": "go_cpu_classes_total_cpu_seconds_total",
+ "/cpu/classes/user:cpu-seconds": "go_cpu_classes_user_cpu_seconds_total",
+ "/gc/cycles/automatic:gc-cycles": "go_gc_cycles_automatic_gc_cycles_total",
+ "/gc/cycles/forced:gc-cycles": "go_gc_cycles_forced_gc_cycles_total",
+ "/gc/cycles/total:gc-cycles": "go_gc_cycles_total_gc_cycles_total",
+ "/gc/gogc:percent": "go_gc_gogc_percent",
+ "/gc/gomemlimit:bytes": "go_gc_gomemlimit_bytes",
+ "/gc/heap/allocs-by-size:bytes": "go_gc_heap_allocs_by_size_bytes",
+ "/gc/heap/allocs:bytes": "go_gc_heap_allocs_bytes_total",
+ "/gc/heap/allocs:objects": "go_gc_heap_allocs_objects_total",
+ "/gc/heap/frees-by-size:bytes": "go_gc_heap_frees_by_size_bytes",
+ "/gc/heap/frees:bytes": "go_gc_heap_frees_bytes_total",
+ "/gc/heap/frees:objects": "go_gc_heap_frees_objects_total",
+ "/gc/heap/goal:bytes": "go_gc_heap_goal_bytes",
+ "/gc/heap/live:bytes": "go_gc_heap_live_bytes",
+ "/gc/heap/objects:objects": "go_gc_heap_objects_objects",
+ "/gc/heap/tiny/allocs:objects": "go_gc_heap_tiny_allocs_objects_total",
+ "/gc/limiter/last-enabled:gc-cycle": "go_gc_limiter_last_enabled_gc_cycle",
+ "/gc/pauses:seconds": "go_gc_pauses_seconds",
+ "/gc/scan/globals:bytes": "go_gc_scan_globals_bytes",
+ "/gc/scan/heap:bytes": "go_gc_scan_heap_bytes",
+ "/gc/scan/stack:bytes": "go_gc_scan_stack_bytes",
+ "/gc/scan/total:bytes": "go_gc_scan_total_bytes",
+ "/gc/stack/starting-size:bytes": "go_gc_stack_starting_size_bytes",
+ "/godebug/non-default-behavior/execerrdot:events": "go_godebug_non_default_behavior_execerrdot_events_total",
+ "/godebug/non-default-behavior/gocachehash:events": "go_godebug_non_default_behavior_gocachehash_events_total",
+ "/godebug/non-default-behavior/gocachetest:events": "go_godebug_non_default_behavior_gocachetest_events_total",
+ "/godebug/non-default-behavior/gocacheverify:events": "go_godebug_non_default_behavior_gocacheverify_events_total",
+ "/godebug/non-default-behavior/gotypesalias:events": "go_godebug_non_default_behavior_gotypesalias_events_total",
+ "/godebug/non-default-behavior/http2client:events": "go_godebug_non_default_behavior_http2client_events_total",
+ "/godebug/non-default-behavior/http2server:events": "go_godebug_non_default_behavior_http2server_events_total",
+ "/godebug/non-default-behavior/httplaxcontentlength:events": "go_godebug_non_default_behavior_httplaxcontentlength_events_total",
+ "/godebug/non-default-behavior/httpmuxgo121:events": "go_godebug_non_default_behavior_httpmuxgo121_events_total",
+ "/godebug/non-default-behavior/installgoroot:events": "go_godebug_non_default_behavior_installgoroot_events_total",
+ "/godebug/non-default-behavior/jstmpllitinterp:events": "go_godebug_non_default_behavior_jstmpllitinterp_events_total",
+ "/godebug/non-default-behavior/multipartmaxheaders:events": "go_godebug_non_default_behavior_multipartmaxheaders_events_total",
+ "/godebug/non-default-behavior/multipartmaxparts:events": "go_godebug_non_default_behavior_multipartmaxparts_events_total",
+ "/godebug/non-default-behavior/multipathtcp:events": "go_godebug_non_default_behavior_multipathtcp_events_total",
+ "/godebug/non-default-behavior/netedns0:events": "go_godebug_non_default_behavior_netedns0_events_total",
+ "/godebug/non-default-behavior/panicnil:events": "go_godebug_non_default_behavior_panicnil_events_total",
+ "/godebug/non-default-behavior/randautoseed:events": "go_godebug_non_default_behavior_randautoseed_events_total",
+ "/godebug/non-default-behavior/tarinsecurepath:events": "go_godebug_non_default_behavior_tarinsecurepath_events_total",
+ "/godebug/non-default-behavior/tls10server:events": "go_godebug_non_default_behavior_tls10server_events_total",
+ "/godebug/non-default-behavior/tlsmaxrsasize:events": "go_godebug_non_default_behavior_tlsmaxrsasize_events_total",
+ "/godebug/non-default-behavior/tlsrsakex:events": "go_godebug_non_default_behavior_tlsrsakex_events_total",
+ "/godebug/non-default-behavior/tlsunsafeekm:events": "go_godebug_non_default_behavior_tlsunsafeekm_events_total",
+ "/godebug/non-default-behavior/x509sha1:events": "go_godebug_non_default_behavior_x509sha1_events_total",
+ "/godebug/non-default-behavior/x509usefallbackroots:events": "go_godebug_non_default_behavior_x509usefallbackroots_events_total",
+ "/godebug/non-default-behavior/x509usepolicies:events": "go_godebug_non_default_behavior_x509usepolicies_events_total",
+ "/godebug/non-default-behavior/zipinsecurepath:events": "go_godebug_non_default_behavior_zipinsecurepath_events_total",
+ "/memory/classes/heap/free:bytes": "go_memory_classes_heap_free_bytes",
+ "/memory/classes/heap/objects:bytes": "go_memory_classes_heap_objects_bytes",
+ "/memory/classes/heap/released:bytes": "go_memory_classes_heap_released_bytes",
+ "/memory/classes/heap/stacks:bytes": "go_memory_classes_heap_stacks_bytes",
+ "/memory/classes/heap/unused:bytes": "go_memory_classes_heap_unused_bytes",
+ "/memory/classes/metadata/mcache/free:bytes": "go_memory_classes_metadata_mcache_free_bytes",
+ "/memory/classes/metadata/mcache/inuse:bytes": "go_memory_classes_metadata_mcache_inuse_bytes",
+ "/memory/classes/metadata/mspan/free:bytes": "go_memory_classes_metadata_mspan_free_bytes",
+ "/memory/classes/metadata/mspan/inuse:bytes": "go_memory_classes_metadata_mspan_inuse_bytes",
+ "/memory/classes/metadata/other:bytes": "go_memory_classes_metadata_other_bytes",
+ "/memory/classes/os-stacks:bytes": "go_memory_classes_os_stacks_bytes",
+ "/memory/classes/other:bytes": "go_memory_classes_other_bytes",
+ "/memory/classes/profiling/buckets:bytes": "go_memory_classes_profiling_buckets_bytes",
+ "/memory/classes/total:bytes": "go_memory_classes_total_bytes",
+ "/sched/gomaxprocs:threads": "go_sched_gomaxprocs_threads",
+ "/sched/goroutines:goroutines": "go_sched_goroutines_goroutines",
+ "/sched/latencies:seconds": "go_sched_latencies_seconds",
+ "/sched/pauses/stopping/gc:seconds": "go_sched_pauses_stopping_gc_seconds",
+ "/sched/pauses/stopping/other:seconds": "go_sched_pauses_stopping_other_seconds",
+ "/sched/pauses/total/gc:seconds": "go_sched_pauses_total_gc_seconds",
+ "/sched/pauses/total/other:seconds": "go_sched_pauses_total_other_seconds",
+ "/sync/mutex/wait/total:seconds": "go_sync_mutex_wait_total_seconds_total",
+ }
-const expectedRuntimeMetricsCardinality = 161
+ expMetrics = map[string]string{
+ "/gc/gogc:percent": "go_gc_gogc_percent",
+ "/gc/gomemlimit:bytes": "go_gc_gomemlimit_bytes",
+ "/sched/gomaxprocs:threads": "go_sched_gomaxprocs_threads",
+ }
+)
+
+const expectedRuntimeMetricsCardinality = 162
diff --git prometheus/histogram.go prometheus/histogram.go
index b5c8bcb39..519db348a 100644
--- prometheus/histogram.go
+++ prometheus/histogram.go
@@ -440,7 +440,7 @@ type HistogramOpts struct {
// constant (or any negative float value).
NativeHistogramZeroThreshold float64
- // The remaining fields define a strategy to limit the number of
+ // The next three fields define a strategy to limit the number of
// populated sparse buckets. If NativeHistogramMaxBucketNumber is left
// at zero, the number of buckets is not limited. (Note that this might
// lead to unbounded memory consumption if the values observed by the
@@ -473,6 +473,22 @@ type HistogramOpts struct {
NativeHistogramMinResetDuration time.Duration
NativeHistogramMaxZeroThreshold float64
+ // NativeHistogramMaxExemplars limits the number of exemplars
+ // that are kept in memory for each native histogram. If you leave it at
+ // zero, a default value of 10 is used. If no exemplars should be kept specifically
+ // for native histograms, set it to a negative value. (Scrapers can
+ // still use the exemplars exposed for classic buckets, which are managed
+ // independently.)
+ NativeHistogramMaxExemplars int
+ // NativeHistogramExemplarTTL is only checked once
+ // NativeHistogramMaxExemplars is exceeded. In that case, the
+ // oldest exemplar is removed if it is older than NativeHistogramExemplarTTL.
+ // Otherwise, the older exemplar in the pair of exemplars that are closest
+ // together (on an exponential scale) is removed.
+ // If NativeHistogramExemplarTTL is left at its zero value, a default value of
+ // 5m is used. To always delete the oldest exemplar, set it to a negative value.
+ NativeHistogramExemplarTTL time.Duration
+
// now is for testing purposes, by default it's time.Now.
now func() time.Time
@@ -532,6 +548,7 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
if opts.afterFunc == nil {
opts.afterFunc = time.AfterFunc
}
+
h := &histogram{
desc: desc,
upperBounds: opts.Buckets,
@@ -556,6 +573,7 @@ func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogr
h.nativeHistogramZeroThreshold = DefNativeHistogramZeroThreshold
} // Leave h.nativeHistogramZeroThreshold at 0 otherwise.
h.nativeHistogramSchema = pickSchema(opts.NativeHistogramBucketFactor)
+ h.nativeExemplars = makeNativeExemplars(opts.NativeHistogramExemplarTTL, opts.NativeHistogramMaxExemplars)
}
for i, upperBound := range h.upperBounds {
if i < len(h.upperBounds)-1 {
@@ -725,7 +743,8 @@ type histogram struct {
// resetScheduled is protected by mtx. It is true if a reset is
// scheduled for a later time (when nativeHistogramMinResetDuration has
// passed).
- resetScheduled bool
+ resetScheduled bool
+ nativeExemplars nativeExemplars
// now is for testing purposes, by default it's time.Now.
now func() time.Time
@@ -742,6 +761,9 @@ func (h *histogram) Observe(v float64) {
h.observe(v, h.findBucket(v))
}
+// ObserveWithExemplar should not be called in a high-frequency setting
+// for a native histogram with configured exemplars. For this case,
+// the implementation isn't lock-free and might suffer from lock contention.
func (h *histogram) ObserveWithExemplar(v float64, e Labels) {
i := h.findBucket(v)
h.observe(v, i)
@@ -821,6 +843,13 @@ func (h *histogram) Write(out *dto.Metric) error {
Length: proto.Uint32(0),
}}
}
+
+ if h.nativeExemplars.isEnabled() {
+ h.nativeExemplars.Lock()
+ his.Exemplars = append(his.Exemplars, h.nativeExemplars.exemplars...)
+ h.nativeExemplars.Unlock()
+ }
+
}
addAndResetCounts(hotCounts, coldCounts)
return nil
@@ -1091,8 +1120,10 @@ func (h *histogram) resetCounts(counts *histogramCounts) {
deleteSyncMap(&counts.nativeHistogramBucketsPositive)
}
-// updateExemplar replaces the exemplar for the provided bucket. With empty
-// labels, it's a no-op. It panics if any of the labels is invalid.
+// updateExemplar replaces the exemplar for the provided classic bucket.
+// With empty labels, it's a no-op. It panics if any of the labels is invalid.
+// If histogram is native, the exemplar will be cached into nativeExemplars,
+// which has a limit, and will remove one exemplar when limit is reached.
func (h *histogram) updateExemplar(v float64, bucket int, l Labels) {
if l == nil {
return
@@ -1102,6 +1133,10 @@ func (h *histogram) updateExemplar(v float64, bucket int, l Labels) {
panic(err)
}
h.exemplars[bucket].Store(e)
+ doSparse := h.nativeHistogramSchema > math.MinInt32 && !math.IsNaN(v)
+ if doSparse {
+ h.nativeExemplars.addExemplar(e)
+ }
}
// HistogramVec is a Collector that bundles a set of Histograms that all share the
@@ -1336,6 +1371,48 @@ func MustNewConstHistogram(
return m
}
+// NewConstHistogramWithCreatedTimestamp does the same thing as NewConstHistogram but sets the created timestamp.
+func NewConstHistogramWithCreatedTimestamp(
+ desc *Desc,
+ count uint64,
+ sum float64,
+ buckets map[float64]uint64,
+ ct time.Time,
+ labelValues ...string,
+) (Metric, error) {
+ if desc.err != nil {
+ return nil, desc.err
+ }
+ if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
+ return nil, err
+ }
+ return &constHistogram{
+ desc: desc,
+ count: count,
+ sum: sum,
+ buckets: buckets,
+ labelPairs: MakeLabelPairs(desc, labelValues),
+ createdTs: timestamppb.New(ct),
+ }, nil
+}
+
+// MustNewConstHistogramWithCreatedTimestamp is a version of NewConstHistogramWithCreatedTimestamp that panics where
+// NewConstHistogramWithCreatedTimestamp would have returned an error.
+func MustNewConstHistogramWithCreatedTimestamp(
+ desc *Desc,
+ count uint64,
+ sum float64,
+ buckets map[float64]uint64,
+ ct time.Time,
+ labelValues ...string,
+) Metric {
+ m, err := NewConstHistogramWithCreatedTimestamp(desc, count, sum, buckets, ct, labelValues...)
+ if err != nil {
+ panic(err)
+ }
+ return m
+}
+
type buckSort []*dto.Bucket
func (s buckSort) Len() int {
@@ -1575,3 +1652,186 @@ func addAndResetCounts(hot, cold *histogramCounts) {
atomic.AddUint64(&hot.nativeHistogramZeroBucket, atomic.LoadUint64(&cold.nativeHistogramZeroBucket))
atomic.StoreUint64(&cold.nativeHistogramZeroBucket, 0)
}
+
+type nativeExemplars struct {
+ sync.Mutex
+
+ // Time-to-live for exemplars, it is set to -1 if exemplars are disabled, that is NativeHistogramMaxExemplars is below 0.
+ // The ttl is used on insertion to remove an exemplar that is older than ttl, if present.
+ ttl time.Duration
+
+ exemplars []*dto.Exemplar
+}
+
+func (n *nativeExemplars) isEnabled() bool {
+ return n.ttl != -1
+}
+
+func makeNativeExemplars(ttl time.Duration, maxCount int) nativeExemplars {
+ if ttl == 0 {
+ ttl = 5 * time.Minute
+ }
+
+ if maxCount == 0 {
+ maxCount = 10
+ }
+
+ if maxCount < 0 {
+ maxCount = 0
+ ttl = -1
+ }
+
+ return nativeExemplars{
+ ttl: ttl,
+ exemplars: make([]*dto.Exemplar, 0, maxCount),
+ }
+}
+
+func (n *nativeExemplars) addExemplar(e *dto.Exemplar) {
+ if !n.isEnabled() {
+ return
+ }
+
+ n.Lock()
+ defer n.Unlock()
+
+ // When the number of exemplars has not yet exceeded or
+ // is equal to cap(n.exemplars), then
+ // insert the new exemplar directly.
+ if len(n.exemplars) < cap(n.exemplars) {
+ var nIdx int
+ for nIdx = 0; nIdx < len(n.exemplars); nIdx++ {
+ if *e.Value < *n.exemplars[nIdx].Value {
+ break
+ }
+ }
+ n.exemplars = append(n.exemplars[:nIdx], append([]*dto.Exemplar{e}, n.exemplars[nIdx:]...)...)
+ return
+ }
+
+ if len(n.exemplars) == 1 {
+ // When the number of exemplars is 1, then
+ // replace the existing exemplar with the new exemplar.
+ n.exemplars[0] = e
+ return
+ }
+ // From this point on, the number of exemplars is greater than 1.
+
+ // When the number of exemplars exceeds the limit, remove one exemplar.
+ var (
+ ot = time.Time{} // Oldest timestamp seen. Initial value doesn't matter as we replace it due to otIdx == -1 in the loop.
+ otIdx = -1 // Index of the exemplar with the oldest timestamp.
+
+ md = -1.0 // Logarithm of the delta of the closest pair of exemplars.
+
+ // The insertion point of the new exemplar in the exemplars slice after insertion.
+ // This is calculated purely based on the order of the exemplars by value.
+ // nIdx == len(n.exemplars) means the new exemplar is to be inserted after the end.
+ nIdx = -1
+
+ // rIdx is ultimately the index for the exemplar that we are replacing with the new exemplar.
+ // The aim is to keep a good spread of exemplars by value and not let them bunch up too much.
+ // It is calculated in 3 steps:
+ // 1. First we set rIdx to the index of the older exemplar within the closest pair by value.
+ // That is the following will be true (on log scale):
+ // either the exemplar pair on index (rIdx-1, rIdx) or (rIdx, rIdx+1) will have
+ // the closest values to each other from all pairs.
+ // For example, suppose the values are distributed like this:
+ // |-----------x-------------x----------------x----x-----|
+ // ^--rIdx as this is older.
+ // Or like this:
+ // |-----------x-------------x----------------x----x-----|
+ // ^--rIdx as this is older.
+ // 2. If there is an exemplar that expired, then we simple reset rIdx to that index.
+ // 3. We check if by inserting the new exemplar we would create a closer pair at
+ // (nIdx-1, nIdx) or (nIdx, nIdx+1) and set rIdx to nIdx-1 or nIdx accordingly to
+ // keep the spread of exemplars by value; otherwise we keep rIdx as it is.
+ rIdx = -1
+ cLog float64 // Logarithm of the current exemplar.
+ pLog float64 // Logarithm of the previous exemplar.
+ )
+
+ for i, exemplar := range n.exemplars {
+ // Find the exemplar with the oldest timestamp.
+ if otIdx == -1 || exemplar.Timestamp.AsTime().Before(ot) {
+ ot = exemplar.Timestamp.AsTime()
+ otIdx = i
+ }
+
+ // Find the index at which to insert new the exemplar.
+ if nIdx == -1 && *e.Value <= *exemplar.Value {
+ nIdx = i
+ }
+
+ // Find the two closest exemplars and pick the one the with older timestamp.
+ pLog = cLog
+ cLog = math.Log(exemplar.GetValue())
+ if i == 0 {
+ continue
+ }
+ diff := math.Abs(cLog - pLog)
+ if md == -1 || diff < md {
+ // The closest exemplar pair is at index: i-1, i.
+ // Choose the exemplar with the older timestamp for replacement.
+ md = diff
+ if n.exemplars[i].Timestamp.AsTime().Before(n.exemplars[i-1].Timestamp.AsTime()) {
+ rIdx = i
+ } else {
+ rIdx = i - 1
+ }
+ }
+
+ }
+
+ // If all existing exemplar are smaller than new exemplar,
+ // then the exemplar should be inserted at the end.
+ if nIdx == -1 {
+ nIdx = len(n.exemplars)
+ }
+ // Here, we have the following relationships:
+ // n.exemplars[nIdx-1].Value < e.Value (if nIdx > 0)
+ // e.Value <= n.exemplars[nIdx].Value (if nIdx < len(n.exemplars))
+
+ if otIdx != -1 && e.Timestamp.AsTime().Sub(ot) > n.ttl {
+ // If the oldest exemplar has expired, then replace it with the new exemplar.
+ rIdx = otIdx
+ } else {
+ // In the previous for loop, when calculating the closest pair of exemplars,
+ // we did not take into account the newly inserted exemplar.
+ // So we need to calculate with the newly inserted exemplar again.
+ elog := math.Log(e.GetValue())
+ if nIdx > 0 {
+ diff := math.Abs(elog - math.Log(n.exemplars[nIdx-1].GetValue()))
+ if diff < md {
+ // The value we are about to insert is closer to the previous exemplar at the insertion point than what we calculated before in rIdx.
+ // v--rIdx
+ // |-----------x-n-----------x----------------x----x-----|
+ // nIdx-1--^ ^--new exemplar value
+ // Do not make the spread worse, replace nIdx-1 and not rIdx.
+ md = diff
+ rIdx = nIdx - 1
+ }
+ }
+ if nIdx < len(n.exemplars) {
+ diff := math.Abs(math.Log(n.exemplars[nIdx].GetValue()) - elog)
+ if diff < md {
+ // The value we are about to insert is closer to the next exemplar at the insertion point than what we calculated before in rIdx.
+ // v--rIdx
+ // |-----------x-----------n-x----------------x----x-----|
+ // new exemplar value--^ ^--nIdx
+ // Do not make the spread worse, replace nIdx-1 and not rIdx.
+ rIdx = nIdx
+ }
+ }
+ }
+
+ // Adjust the slice according to rIdx and nIdx.
+ switch {
+ case rIdx == nIdx:
+ n.exemplars[nIdx] = e
+ case rIdx < nIdx:
+ n.exemplars = append(n.exemplars[:rIdx], append(n.exemplars[rIdx+1:nIdx], append([]*dto.Exemplar{e}, n.exemplars[nIdx:]...)...)...)
+ case rIdx > nIdx:
+ n.exemplars = append(n.exemplars[:nIdx], append([]*dto.Exemplar{e}, append(n.exemplars[nIdx:rIdx], n.exemplars[rIdx+1:]...)...)...)
+ }
+}
diff --git prometheus/histogram_test.go prometheus/histogram_test.go
index 39bb0dcd4..c2a14ae72 100644
--- prometheus/histogram_test.go
+++ prometheus/histogram_test.go
@@ -1049,10 +1049,14 @@ func TestNativeHistogramConcurrency(t *testing.T) {
go func(vals []float64) {
start.Wait()
- for _, v := range vals {
+ for i, v := range vals {
// An observation every 1 to 10 seconds.
atomic.AddInt64(&ts, rand.Int63n(10)+1)
- his.Observe(v)
+ if i%2 == 0 {
+ his.Observe(v)
+ } else {
+ his.(ExemplarObserver).ObserveWithExemplar(v, Labels{"foo": "bar"})
+ }
}
end.Done()
}(vals)
@@ -1271,3 +1275,183 @@ func TestHistogramVecCreatedTimestampWithDeletes(t *testing.T) {
now = now.Add(1 * time.Hour)
expectCTsForMetricVecValues(t, histogramVec.MetricVec, dto.MetricType_HISTOGRAM, expected)
}
+
+func TestNewConstHistogramWithCreatedTimestamp(t *testing.T) {
+ metricDesc := NewDesc(
+ "sample_value",
+ "sample value",
+ nil,
+ nil,
+ )
+ buckets := map[float64]uint64{25: 100, 50: 200}
+ createdTs := time.Unix(1719670764, 123)
+
+ h, err := NewConstHistogramWithCreatedTimestamp(metricDesc, 100, 200, buckets, createdTs)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var metric dto.Metric
+ if err := h.Write(&metric); err != nil {
+ t.Fatal(err)
+ }
+
+ if metric.Histogram.CreatedTimestamp.AsTime().UnixMicro() != createdTs.UnixMicro() {
+ t.Errorf("Expected created timestamp %v, got %v", createdTs, &metric.Histogram.CreatedTimestamp)
+ }
+}
+
+func TestNativeHistogramExemplar(t *testing.T) {
+ // Test the histogram with positive NativeHistogramExemplarTTL and NativeHistogramMaxExemplars
+ h := NewHistogram(HistogramOpts{
+ Name: "test",
+ Help: "test help",
+ Buckets: []float64{1, 2, 3, 4},
+ NativeHistogramBucketFactor: 1.1,
+ NativeHistogramMaxExemplars: 3,
+ NativeHistogramExemplarTTL: 10 * time.Second,
+ }).(*histogram)
+
+ tcs := []struct {
+ name string
+ addFunc func(*histogram)
+ expectedValues []float64
+ }{
+ {
+ name: "add exemplars to the limit",
+ addFunc: func(h *histogram) {
+ h.ObserveWithExemplar(1, Labels{"id": "1"})
+ h.ObserveWithExemplar(3, Labels{"id": "1"})
+ h.ObserveWithExemplar(5, Labels{"id": "1"})
+ },
+ expectedValues: []float64{1, 3, 5},
+ },
+ {
+ name: "remove exemplar in closest pair, the removed index equals to inserted index",
+ addFunc: func(h *histogram) {
+ h.ObserveWithExemplar(4, Labels{"id": "1"})
+ },
+ expectedValues: []float64{1, 3, 4},
+ },
+ {
+ name: "remove exemplar in closest pair, the removed index is bigger than inserted index",
+ addFunc: func(h *histogram) {
+ h.ObserveWithExemplar(0, Labels{"id": "1"})
+ },
+ expectedValues: []float64{0, 1, 4},
+ },
+ {
+ name: "remove exemplar with oldest timestamp, the removed index is smaller than inserted index",
+ addFunc: func(h *histogram) {
+ h.now = func() time.Time { return time.Now().Add(time.Second * 11) }
+ h.ObserveWithExemplar(6, Labels{"id": "1"})
+ },
+ expectedValues: []float64{0, 4, 6},
+ },
+ }
+
+ for _, tc := range tcs {
+ t.Run(tc.name, func(t *testing.T) {
+ tc.addFunc(h)
+ compareNativeExemplarValues(t, h.nativeExemplars.exemplars, tc.expectedValues)
+ })
+ }
+
+ // Test the histogram with negative NativeHistogramExemplarTTL
+ h = NewHistogram(HistogramOpts{
+ Name: "test",
+ Help: "test help",
+ Buckets: []float64{1, 2, 3, 4},
+ NativeHistogramBucketFactor: 1.1,
+ NativeHistogramMaxExemplars: 3,
+ NativeHistogramExemplarTTL: -1 * time.Second,
+ }).(*histogram)
+
+ tcs = []struct {
+ name string
+ addFunc func(*histogram)
+ expectedValues []float64
+ }{
+ {
+ name: "add exemplars to the limit",
+ addFunc: func(h *histogram) {
+ h.ObserveWithExemplar(1, Labels{"id": "1"})
+ h.ObserveWithExemplar(3, Labels{"id": "1"})
+ h.ObserveWithExemplar(5, Labels{"id": "1"})
+ },
+ expectedValues: []float64{1, 3, 5},
+ },
+ {
+ name: "remove exemplar with oldest timestamp, the removed index is smaller than inserted index",
+ addFunc: func(h *histogram) {
+ h.ObserveWithExemplar(4, Labels{"id": "1"})
+ },
+ expectedValues: []float64{3, 4, 5},
+ },
+ {
+ name: "remove exemplar with oldest timestamp, the removed index equals to inserted index",
+ addFunc: func(h *histogram) {
+ h.ObserveWithExemplar(0, Labels{"id": "1"})
+ },
+ expectedValues: []float64{0, 4, 5},
+ },
+ {
+ name: "remove exemplar with oldest timestamp, the removed index is bigger than inserted index",
+ addFunc: func(h *histogram) {
+ h.ObserveWithExemplar(3, Labels{"id": "1"})
+ },
+ expectedValues: []float64{0, 3, 4},
+ },
+ }
+
+ for _, tc := range tcs {
+ t.Run(tc.name, func(t *testing.T) {
+ tc.addFunc(h)
+ compareNativeExemplarValues(t, h.nativeExemplars.exemplars, tc.expectedValues)
+ })
+ }
+
+ // Test the histogram with negative NativeHistogramMaxExemplars
+ h = NewHistogram(HistogramOpts{
+ Name: "test",
+ Help: "test help",
+ Buckets: []float64{1, 2, 3, 4},
+ NativeHistogramBucketFactor: 1.1,
+ NativeHistogramMaxExemplars: -1,
+ NativeHistogramExemplarTTL: -1 * time.Second,
+ }).(*histogram)
+
+ tcs = []struct {
+ name string
+ addFunc func(*histogram)
+ expectedValues []float64
+ }{
+ {
+ name: "add exemplars to the limit, but no effect",
+ addFunc: func(h *histogram) {
+ h.ObserveWithExemplar(1, Labels{"id": "1"})
+ h.ObserveWithExemplar(3, Labels{"id": "1"})
+ h.ObserveWithExemplar(5, Labels{"id": "1"})
+ },
+ expectedValues: []float64{},
+ },
+ }
+
+ for _, tc := range tcs {
+ t.Run(tc.name, func(t *testing.T) {
+ tc.addFunc(h)
+ compareNativeExemplarValues(t, h.nativeExemplars.exemplars, tc.expectedValues)
+ })
+ }
+}
+
+func compareNativeExemplarValues(t *testing.T, exps []*dto.Exemplar, values []float64) {
+ if len(exps) != len(values) {
+ t.Errorf("the count of exemplars is not %d", len(values))
+ }
+ for i, e := range exps {
+ if e.GetValue() != values[i] {
+ t.Errorf("the %dth exemplar value %v is not as expected: %v", i, e.GetValue(), values[i])
+ }
+ }
+}
diff --git prometheus/internal/go_collector_options.go prometheus/internal/go_collector_options.go
index 723b45d64..a4fa6eabd 100644
--- prometheus/internal/go_collector_options.go
+++ prometheus/internal/go_collector_options.go
@@ -30,3 +30,5 @@ type GoCollectorOptions struct {
RuntimeMetricSumForHist map[string]string
RuntimeMetricRules []GoCollectorRule
}
+
+var GoCollectorDefaultRuntimeMetrics = regexp.MustCompile(`/gc/gogc:percent|/gc/gomemlimit:bytes|/sched/gomaxprocs:threads`)
diff --git prometheus/metric.go prometheus/metric.go
index f018e5723..9d9b81ab4 100644
--- prometheus/metric.go
+++ prometheus/metric.go
@@ -234,7 +234,7 @@ func NewMetricWithExemplars(m Metric, exemplars ...Exemplar) (Metric, error) {
)
for i, e := range exemplars {
ts := e.Timestamp
- if ts == (time.Time{}) {
+ if ts.IsZero() {
ts = now
}
exs[i], err = newExemplar(e.Value, ts, e.Labels)
diff --git prometheus/process_collector.go prometheus/process_collector.go
index 8548dd18e..62a4e7ad9 100644
--- prometheus/process_collector.go
+++ prometheus/process_collector.go
@@ -22,14 +22,15 @@ import (
)
type processCollector struct {
- collectFn func(chan<- Metric)
- pidFn func() (int, error)
- reportErrors bool
- cpuTotal *Desc
- openFDs, maxFDs *Desc
- vsize, maxVsize *Desc
- rss *Desc
- startTime *Desc
+ collectFn func(chan<- Metric)
+ pidFn func() (int, error)
+ reportErrors bool
+ cpuTotal *Desc
+ openFDs, maxFDs *Desc
+ vsize, maxVsize *Desc
+ rss *Desc
+ startTime *Desc
+ inBytes, outBytes *Desc
}
// ProcessCollectorOpts defines the behavior of a process metrics collector
@@ -100,6 +101,16 @@ func NewProcessCollector(opts ProcessCollectorOpts) Collector {
"Start time of the process since unix epoch in seconds.",
nil, nil,
),
+ inBytes: NewDesc(
+ ns+"process_network_receive_bytes_total",
+ "Number of bytes received by the process over the network.",
+ nil, nil,
+ ),
+ outBytes: NewDesc(
+ ns+"process_network_transmit_bytes_total",
+ "Number of bytes sent by the process over the network.",
+ nil, nil,
+ ),
}
if opts.PidFn == nil {
@@ -129,6 +140,8 @@ func (c *processCollector) Describe(ch chan<- *Desc) {
ch <- c.maxVsize
ch <- c.rss
ch <- c.startTime
+ ch <- c.inBytes
+ ch <- c.outBytes
}
// Collect returns the current state of all metrics of the collector.
diff --git prometheus/process_collector_other.go prometheus/process_collector_other.go
index 8c1136cee..14d56d2d0 100644
--- prometheus/process_collector_other.go
+++ prometheus/process_collector_other.go
@@ -63,4 +63,18 @@ func (c *processCollector) processCollect(ch chan<- Metric) {
} else {
c.reportError(ch, nil, err)
}
+
+ if netstat, err := p.Netstat(); err == nil {
+ var inOctets, outOctets float64
+ if netstat.IpExt.InOctets != nil {
+ inOctets = *netstat.IpExt.InOctets
+ }
+ if netstat.IpExt.OutOctets != nil {
+ outOctets = *netstat.IpExt.OutOctets
+ }
+ ch <- MustNewConstMetric(c.inBytes, CounterValue, inOctets)
+ ch <- MustNewConstMetric(c.outBytes, CounterValue, outOctets)
+ } else {
+ c.reportError(ch, nil, err)
+ }
}
diff --git prometheus/process_collector_test.go prometheus/process_collector_test.go
index 3a604aba9..0d62d41e0 100644
--- prometheus/process_collector_test.go
+++ prometheus/process_collector_test.go
@@ -37,7 +37,7 @@ func TestProcessCollector(t *testing.T) {
t.Skipf("skipping TestProcessCollector, procfs not available: %s", err)
}
- registry := NewRegistry()
+ registry := NewPedanticRegistry()
if err := registry.Register(NewProcessCollector(ProcessCollectorOpts{})); err != nil {
t.Fatal(err)
}
@@ -69,6 +69,8 @@ func TestProcessCollector(t *testing.T) {
regexp.MustCompile("\nprocess_virtual_memory_bytes [1-9]"),
regexp.MustCompile("\nprocess_resident_memory_bytes [1-9]"),
regexp.MustCompile("\nprocess_start_time_seconds [0-9.]{10,}"),
+ regexp.MustCompile("\nprocess_network_receive_bytes_total [0-9]+"),
+ regexp.MustCompile("\nprocess_network_transmit_bytes_total [0-9]+"),
regexp.MustCompile("\nfoobar_process_cpu_seconds_total [0-9]"),
regexp.MustCompile("\nfoobar_process_max_fds [1-9]"),
regexp.MustCompile("\nfoobar_process_open_fds [1-9]"),
@@ -76,6 +78,8 @@ func TestProcessCollector(t *testing.T) {
regexp.MustCompile("\nfoobar_process_virtual_memory_bytes [1-9]"),
regexp.MustCompile("\nfoobar_process_resident_memory_bytes [1-9]"),
regexp.MustCompile("\nfoobar_process_start_time_seconds [0-9.]{10,}"),
+ regexp.MustCompile("\nfoobar_process_network_receive_bytes_total [0-9]+"),
+ regexp.MustCompile("\nfoobar_process_network_transmit_bytes_total [0-9]+"),
} {
if !re.Match(buf.Bytes()) {
t.Errorf("want body to match %s\n%s", re, buf.String())
diff --git prometheus/promhttp/delegator.go prometheus/promhttp/delegator.go
index 9819917b8..315eab5f1 100644
--- prometheus/promhttp/delegator.go
+++ prometheus/promhttp/delegator.go
@@ -76,6 +76,12 @@ func (r *responseWriterDelegator) Write(b []byte) (int, error) {
return n, err
}
+// Unwrap lets http.ResponseController get the underlying http.ResponseWriter,
+// by implementing the [rwUnwrapper](https://cs.opensource.google/go/go/+/refs/tags/go1.21.4:src/net/http/responsecontroller.go;l=42-44) interface.
+func (r *responseWriterDelegator) Unwrap() http.ResponseWriter {
+ return r.ResponseWriter
+}
+
type (
closeNotifierDelegator struct{ *responseWriterDelegator }
flusherDelegator struct{ *responseWriterDelegator }
diff --git a/prometheus/promhttp/delegator_test.go b/prometheus/promhttp/delegator_test.go
new file mode 100644
index 000000000..4576ae7c0
--- /dev/null
+++ prometheus/promhttp/delegator_test.go
@@ -0,0 +1,78 @@
+// Copyright 2024 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package promhttp
+
+import (
+ "net/http"
+ "testing"
+ "time"
+)
+
+type responseWriter struct {
+ flushErrorCalled bool
+ setWriteDeadlineCalled time.Time
+ setReadDeadlineCalled time.Time
+}
+
+func (rw *responseWriter) Header() http.Header {
+ return nil
+}
+
+func (rw *responseWriter) Write(p []byte) (int, error) {
+ return 0, nil
+}
+
+func (rw *responseWriter) WriteHeader(statusCode int) {
+}
+
+func (rw *responseWriter) FlushError() error {
+ rw.flushErrorCalled = true
+
+ return nil
+}
+
+func (rw *responseWriter) SetWriteDeadline(deadline time.Time) error {
+ rw.setWriteDeadlineCalled = deadline
+
+ return nil
+}
+
+func (rw *responseWriter) SetReadDeadline(deadline time.Time) error {
+ rw.setReadDeadlineCalled = deadline
+
+ return nil
+}
+
+func TestResponseWriterDelegatorUnwrap(t *testing.T) {
+ w := &responseWriter{}
+ rwd := &responseWriterDelegator{ResponseWriter: w}
+
+ if rwd.Unwrap() != w {
+ t.Error("unwrapped responsewriter must equal to the original responsewriter")
+ }
+
+ controller := http.NewResponseController(rwd)
+ if err := controller.Flush(); err != nil || !w.flushErrorCalled {
+ t.Error("FlushError must be propagated to the original responsewriter")
+ }
+
+ timeNow := time.Now()
+ if err := controller.SetWriteDeadline(timeNow); err != nil || w.setWriteDeadlineCalled != timeNow {
+ t.Error("SetWriteDeadline must be propagated to the original responsewriter")
+ }
+
+ if err := controller.SetReadDeadline(timeNow); err != nil || w.setReadDeadlineCalled != timeNow {
+ t.Error("SetReadDeadline must be propagated to the original responsewriter")
+ }
+}
diff --git prometheus/promhttp/http.go prometheus/promhttp/http.go
index 09b8d2fbe..e598e66e6 100644
--- prometheus/promhttp/http.go
+++ prometheus/promhttp/http.go
@@ -38,12 +38,13 @@ import (
"io"
"net/http"
"strconv"
- "strings"
"sync"
"time"
+ "github.com/klauspost/compress/zstd"
"github.com/prometheus/common/expfmt"
+ "github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil"
"github.com/prometheus/client_golang/prometheus"
)
@@ -54,6 +55,18 @@ const (
processStartTimeHeader = "Process-Start-Time-Unix"
)
+// Compression represents the content encodings handlers support for the HTTP
+// responses.
+type Compression string
+
+const (
+ Identity Compression = "identity"
+ Gzip Compression = "gzip"
+ Zstd Compression = "zstd"
+)
+
+var defaultCompressionFormats = []Compression{Identity, Gzip, Zstd}
+
var gzipPool = sync.Pool{
New: func() interface{} {
return gzip.NewWriter(nil)
@@ -122,6 +135,18 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO
}
}
+ // Select compression formats to offer based on default or user choice.
+ var compressions []string
+ if !opts.DisableCompression {
+ offers := defaultCompressionFormats
+ if len(opts.OfferedCompressions) > 0 {
+ offers = opts.OfferedCompressions
+ }
+ for _, comp := range offers {
+ compressions = append(compressions, string(comp))
+ }
+ }
+
h := http.HandlerFunc(func(rsp http.ResponseWriter, req *http.Request) {
if !opts.ProcessStartTime.IsZero() {
rsp.Header().Set(processStartTimeHeader, strconv.FormatInt(opts.ProcessStartTime.Unix(), 10))
@@ -165,21 +190,23 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO
} else {
contentType = expfmt.Negotiate(req.Header)
}
- header := rsp.Header()
- header.Set(contentTypeHeader, string(contentType))
+ rsp.Header().Set(contentTypeHeader, string(contentType))
- w := io.Writer(rsp)
- if !opts.DisableCompression && gzipAccepted(req.Header) {
- header.Set(contentEncodingHeader, "gzip")
- gz := gzipPool.Get().(*gzip.Writer)
- defer gzipPool.Put(gz)
+ w, encodingHeader, closeWriter, err := negotiateEncodingWriter(req, rsp, compressions)
+ if err != nil {
+ if opts.ErrorLog != nil {
+ opts.ErrorLog.Println("error getting writer", err)
+ }
+ w = io.Writer(rsp)
+ encodingHeader = string(Identity)
+ }
- gz.Reset(w)
- defer gz.Close()
+ defer closeWriter()
- w = gz
+ // Set Content-Encoding only when data is compressed
+ if encodingHeader != string(Identity) {
+ rsp.Header().Set(contentEncodingHeader, encodingHeader)
}
-
enc := expfmt.NewEncoder(w, contentType)
// handleError handles the error according to opts.ErrorHandling
@@ -343,9 +370,19 @@ type HandlerOpts struct {
// no effect on the HTTP status code because ErrorHandling is set to
// ContinueOnError.
Registry prometheus.Registerer
- // If DisableCompression is true, the handler will never compress the
- // response, even if requested by the client.
+ // DisableCompression disables the response encoding (compression) and
+ // encoding negotiation. If true, the handler will
+ // never compress the response, even if requested
+ // by the client and the OfferedCompressions field is set.
DisableCompression bool
+ // OfferedCompressions is a set of encodings (compressions) handler will
+ // try to offer when negotiating with the client. This defaults to identity, gzip
+ // and zstd.
+ // NOTE: If handler can't agree with the client on the encodings or
+ // unsupported or empty encodings are set in OfferedCompressions,
+ // handler always fallbacks to no compression (identity), for
+ // compatibility reasons. In such cases ErrorLog will be used if set.
+ OfferedCompressions []Compression
// The number of concurrent HTTP requests is limited to
// MaxRequestsInFlight. Additional requests are responded to with 503
// Service Unavailable and a suitable message in the body. If
@@ -381,19 +418,6 @@ type HandlerOpts struct {
ProcessStartTime time.Time
}
-// gzipAccepted returns whether the client will accept gzip-encoded content.
-func gzipAccepted(header http.Header) bool {
- a := header.Get(acceptEncodingHeader)
- parts := strings.Split(a, ",")
- for _, part := range parts {
- part = strings.TrimSpace(part)
- if part == "gzip" || strings.HasPrefix(part, "gzip;") {
- return true
- }
- }
- return false
-}
-
// httpError removes any content-encoding header and then calls http.Error with
// the provided error and http.StatusInternalServerError. Error contents is
// supposed to be uncompressed plain text. Same as with a plain http.Error, this
@@ -406,3 +430,38 @@ func httpError(rsp http.ResponseWriter, err error) {
http.StatusInternalServerError,
)
}
+
+// negotiateEncodingWriter reads the Accept-Encoding header from a request and
+// selects the right compression based on an allow-list of supported
+// compressions. It returns a writer implementing the compression and an the
+// correct value that the caller can set in the response header.
+func negotiateEncodingWriter(r *http.Request, rw io.Writer, compressions []string) (_ io.Writer, encodingHeaderValue string, closeWriter func(), _ error) {
+ if len(compressions) == 0 {
+ return rw, string(Identity), func() {}, nil
+ }
+
+ // TODO(mrueg): Replace internal/github.com/gddo once https://github.com/golang/go/issues/19307 is implemented.
+ selected := httputil.NegotiateContentEncoding(r, compressions)
+
+ switch selected {
+ case "zstd":
+ // TODO(mrueg): Replace klauspost/compress with stdlib implementation once https://github.com/golang/go/issues/62513 is implemented.
+ z, err := zstd.NewWriter(rw, zstd.WithEncoderLevel(zstd.SpeedFastest))
+ if err != nil {
+ return nil, "", func() {}, err
+ }
+
+ z.Reset(rw)
+ return z, selected, func() { _ = z.Close() }, nil
+ case "gzip":
+ gz := gzipPool.Get().(*gzip.Writer)
+ gz.Reset(rw)
+ return gz, selected, func() { _ = gz.Close(); gzipPool.Put(gz) }, nil
+ case "identity":
+ // This means the content is not compressed.
+ return rw, selected, func() {}, nil
+ default:
+ // The content encoding was not implemented yet.
+ return nil, "", func() {}, fmt.Errorf("content compression format not recognized: %s. Valid formats are: %s", selected, defaultCompressionFormats)
+ }
+}
diff --git prometheus/promhttp/http_test.go prometheus/promhttp/http_test.go
index 8ca192748..3ad2d1da8 100644
--- prometheus/promhttp/http_test.go
+++ prometheus/promhttp/http_test.go
@@ -15,8 +15,10 @@ package promhttp
import (
"bytes"
+ "compress/gzip"
"errors"
"fmt"
+ "io"
"log"
"net/http"
"net/http/httptest"
@@ -24,6 +26,7 @@ import (
"testing"
"time"
+ "github.com/klauspost/compress/zstd"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/client_golang/prometheus"
@@ -31,6 +34,11 @@ import (
type errorCollector struct{}
+const (
+ acceptHeader = "Accept"
+ acceptTextPlain = "text/plain"
+)
+
func (e errorCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- prometheus.NewDesc("invalid_metric", "not helpful", nil, nil)
}
@@ -71,6 +79,28 @@ func (g *mockTransactionGatherer) Gather() (_ []*dto.MetricFamily, done func(),
return mfs, func() { g.doneInvoked++ }, err
}
+func readCompressedBody(r io.Reader, comp Compression) (string, error) {
+ switch comp {
+ case Gzip:
+ reader, err := gzip.NewReader(r)
+ if err != nil {
+ return "", err
+ }
+ defer reader.Close()
+ got, err := io.ReadAll(reader)
+ return string(got), err
+ case Zstd:
+ reader, err := zstd.NewReader(r)
+ if err != nil {
+ return "", err
+ }
+ defer reader.Close()
+ got, err := io.ReadAll(reader)
+ return string(got), err
+ }
+ return "", fmt.Errorf("Unsupported compression")
+}
+
func TestHandlerErrorHandling(t *testing.T) {
// Create a registry that collects a MetricFamily with two elements,
// another with one, and reports an error. Further down, we'll use the
@@ -223,7 +253,7 @@ func TestInstrumentMetricHandler(t *testing.T) {
InstrumentMetricHandler(reg, HandlerForTransactional(mReg, HandlerOpts{}))
writer := httptest.NewRecorder()
request, _ := http.NewRequest("GET", "/", nil)
- request.Header.Add("Accept", "test/plain")
+ request.Header.Add(acceptHeader, acceptTextPlain)
handler.ServeHTTP(writer, request)
if got := mReg.gatherInvoked; got != 1 {
@@ -237,6 +267,10 @@ func TestInstrumentMetricHandler(t *testing.T) {
t.Errorf("got HTTP status code %d, want %d", got, want)
}
+ if got, want := writer.Header().Get(contentEncodingHeader), ""; got != want {
+ t.Errorf("got HTTP content encoding header %s, want %s", got, want)
+ }
+
want := "promhttp_metric_handler_requests_in_flight 1\n"
if got := writer.Body.String(); !strings.Contains(got, want) {
t.Errorf("got body %q, does not contain %q", got, want)
@@ -278,7 +312,7 @@ func TestHandlerMaxRequestsInFlight(t *testing.T) {
w2 := httptest.NewRecorder()
w3 := httptest.NewRecorder()
request, _ := http.NewRequest("GET", "/", nil)
- request.Header.Add("Accept", "test/plain")
+ request.Header.Add(acceptHeader, acceptTextPlain)
c := blockingCollector{Block: make(chan struct{}), CollectStarted: make(chan struct{}, 1)}
reg.MustRegister(c)
@@ -331,3 +365,277 @@ func TestHandlerTimeout(t *testing.T) {
close(c.Block) // To not leak a goroutine.
}
+
+func TestInstrumentMetricHandlerWithCompression(t *testing.T) {
+ reg := prometheus.NewRegistry()
+ mReg := &mockTransactionGatherer{g: reg}
+ handler := InstrumentMetricHandler(reg, HandlerForTransactional(mReg, HandlerOpts{DisableCompression: false}))
+ compression := Zstd
+ writer := httptest.NewRecorder()
+ request, _ := http.NewRequest("GET", "/", nil)
+ request.Header.Add(acceptHeader, acceptTextPlain)
+ request.Header.Add(acceptEncodingHeader, string(compression))
+
+ handler.ServeHTTP(writer, request)
+ if got := mReg.gatherInvoked; got != 1 {
+ t.Fatalf("unexpected number of gather invokes, want 1, got %d", got)
+ }
+ if got := mReg.doneInvoked; got != 1 {
+ t.Fatalf("unexpected number of done invokes, want 1, got %d", got)
+ }
+
+ if got, want := writer.Code, http.StatusOK; got != want {
+ t.Errorf("got HTTP status code %d, want %d", got, want)
+ }
+
+ if got, want := writer.Header().Get(contentEncodingHeader), string(compression); got != want {
+ t.Errorf("got HTTP content encoding header %s, want %s", got, want)
+ }
+
+ body, err := readCompressedBody(writer.Body, compression)
+ want := "promhttp_metric_handler_requests_in_flight 1\n"
+ if got := body; !strings.Contains(got, want) {
+ t.Errorf("got body %q, does not contain %q, err: %v", got, want, err)
+ }
+
+ want = "promhttp_metric_handler_requests_total{code=\"200\"} 0\n"
+ if got := body; !strings.Contains(got, want) {
+ t.Errorf("got body %q, does not contain %q, err: %v", got, want, err)
+ }
+
+ for i := 0; i < 100; i++ {
+ writer.Body.Reset()
+ handler.ServeHTTP(writer, request)
+
+ if got, want := mReg.gatherInvoked, i+2; got != want {
+ t.Fatalf("unexpected number of gather invokes, want %d, got %d", want, got)
+ }
+ if got, want := mReg.doneInvoked, i+2; got != want {
+ t.Fatalf("unexpected number of done invokes, want %d, got %d", want, got)
+ }
+ if got, want := writer.Code, http.StatusOK; got != want {
+ t.Errorf("got HTTP status code %d, want %d", got, want)
+ }
+ body, err := readCompressedBody(writer.Body, compression)
+
+ want := "promhttp_metric_handler_requests_in_flight 1\n"
+ if got := body; !strings.Contains(got, want) {
+ t.Errorf("got body %q, does not contain %q, err: %v", got, want, err)
+ }
+
+ want = fmt.Sprintf("promhttp_metric_handler_requests_total{code=\"200\"} %d\n", i+1)
+ if got := body; !strings.Contains(got, want) {
+ t.Errorf("got body %q, does not contain %q, err: %v", got, want, err)
+ }
+ }
+
+ // Test with Zstd
+ compression = Zstd
+ request.Header.Set(acceptEncodingHeader, string(compression))
+
+ handler.ServeHTTP(writer, request)
+
+ if got, want := writer.Code, http.StatusOK; got != want {
+ t.Errorf("got HTTP status code %d, want %d", got, want)
+ }
+
+ if got, want := writer.Header().Get(contentEncodingHeader), string(compression); got != want {
+ t.Errorf("got HTTP content encoding header %s, want %s", got, want)
+ }
+
+ body, err = readCompressedBody(writer.Body, compression)
+ want, = "promhttp_metric_handler_requests_in_flight 1\n"
+ if got := body; !strings.Contains(got, want) {
+ t.Errorf("got body %q, does not contain %q, err: %v", got, want, err)
+ }
+
+ want = "promhttp_metric_handler_requests_total{code=\"200\"} 101\n"
+ if got := body; !strings.Contains(got, want) {
+ t.Errorf("got body %q, does not contain %q, err: %v", got, want, err)
+ }
+
+ for i := 101; i < 201; i++ {
+ writer.Body.Reset()
+ handler.ServeHTTP(writer, request)
+
+ if got, want := mReg.gatherInvoked, i+2; got != want {
+ t.Fatalf("unexpected number of gather invokes, want %d, got %d", want, got)
+ }
+ if got, want := mReg.doneInvoked, i+2; got != want {
+ t.Fatalf("unexpected number of done invokes, want %d, got %d", want, got)
+ }
+ if got, want := writer.Code, http.StatusOK; got != want {
+ t.Errorf("got HTTP status code %d, want %d", got, want)
+ }
+ body, err := readCompressedBody(writer.Body, compression)
+
+ want := "promhttp_metric_handler_requests_in_flight 1\n"
+ if got := body; !strings.Contains(got, want) {
+ t.Errorf("got body %q, does not contain %q, err: %v", got, want, err)
+ }
+
+ want = fmt.Sprintf("promhttp_metric_handler_requests_total{code=\"200\"} %d\n", i+1)
+ if got := body; !strings.Contains(got, want) {
+ t.Errorf("got body %q, does not contain %q, err: %v", got, want, err)
+ }
+ }
+}
+
+func TestNegotiateEncodingWriter(t *testing.T) {
+ var defaultCompressions []string
+
+ for _, comp := range defaultCompressionFormats {
+ defaultCompressions = append(defaultCompressions, string(comp))
+ }
+
+ testCases := []struct {
+ name string
+ offeredCompressions []string
+ acceptEncoding string
+ expectedCompression string
+ err error
+ }{
+ {
+ name: "test without compression enabled",
+ offeredCompressions: []string{},
+ acceptEncoding: "",
+ expectedCompression: "identity",
+ err: nil,
+ },
+ {
+ name: "test with compression enabled with empty accept-encoding header",
+ offeredCompressions: defaultCompressions,
+ acceptEncoding: "",
+ expectedCompression: "identity",
+ err: nil,
+ },
+ {
+ name: "test with gzip compression requested",
+ offeredCompressions: defaultCompressions,
+ acceptEncoding: "gzip",
+ expectedCompression: "gzip",
+ err: nil,
+ },
+ {
+ name: "test with gzip, zstd compression requested",
+ offeredCompressions: defaultCompressions,
+ acceptEncoding: "gzip,zstd",
+ expectedCompression: "gzip",
+ err: nil,
+ },
+ {
+ name: "test with zstd, gzip compression requested",
+ offeredCompressions: defaultCompressions,
+ acceptEncoding: "zstd,gzip",
+ expectedCompression: "gzip",
+ err: nil,
+ },
+ }
+
+ for _, test := range testCases {
+ request, _ := http.NewRequest("GET", "/", nil)
+ request.Header.Add(acceptEncodingHeader, test.acceptEncoding)
+ rr := httptest.NewRecorder()
+ _, encodingHeader, _, err := negotiateEncodingWriter(request, rr, test.offeredCompressions)
+
+ if !errors.Is(err, test.err) {
+ t.Errorf("got error: %v, expected: %v", err, test.err)
+ }
+
+ if encodingHeader != test.expectedCompression {
+ t.Errorf("got different compression type: %v, expected: %v", encodingHeader, test.expectedCompression)
+ }
+ }
+}
+
+func BenchmarkCompression(b *testing.B) {
+ benchmarks := []struct {
+ name string
+ compressionType string
+ }{
+ {
+ name: "test with gzip compression",
+ compressionType: "gzip",
+ },
+ {
+ name: "test with zstd compression",
+ compressionType: "zstd",
+ },
+ {
+ name: "test with no compression",
+ compressionType: "identity",
+ },
+ }
+ sizes := []struct {
+ name string
+ metricCount int
+ labelCount int
+ labelLength int
+ metricLength int
+ }{
+ {
+ name: "small",
+ metricCount: 50,
+ labelCount: 5,
+ labelLength: 5,
+ metricLength: 5,
+ },
+ {
+ name: "medium",
+ metricCount: 500,
+ labelCount: 10,
+ labelLength: 5,
+ metricLength: 10,
+ },
+ {
+ name: "large",
+ metricCount: 5000,
+ labelCount: 10,
+ labelLength: 5,
+ metricLength: 10,
+ },
+ {
+ name: "extra-large",
+ metricCount: 50000,
+ labelCount: 20,
+ labelLength: 5,
+ metricLength: 10,
+ },
+ }
+
+ for _, size := range sizes {
+ reg := prometheus.NewRegistry()
+ handler := HandlerFor(reg, HandlerOpts{})
+
+ // Generate Metrics
+ // Original source: https://github.com/prometheus-community/avalanche/blob/main/metrics/serve.go
+ labelKeys := make([]string, size.labelCount)
+ for idx := 0; idx < size.labelCount; idx++ {
+ labelKeys[idx] = fmt.Sprintf("label_key_%s_%v", strings.Repeat("k", size.labelLength), idx)
+ }
+ labelValues := make([]string, size.labelCount)
+ for idx := 0; idx < size.labelCount; idx++ {
+ labelValues[idx] = fmt.Sprintf("label_val_%s_%v", strings.Repeat("v", size.labelLength), idx)
+ }
+ metrics := make([]*prometheus.GaugeVec, size.metricCount)
+ for idx := 0; idx < size.metricCount; idx++ {
+ gauge := prometheus.NewGaugeVec(prometheus.GaugeOpts{
+ Name: fmt.Sprintf("avalanche_metric_%s_%v_%v", strings.Repeat("m", size.metricLength), 0, idx),
+ Help: "A tasty metric morsel",
+ }, append([]string{"series_id", "cycle_id"}, labelKeys...))
+ reg.MustRegister(gauge)
+ metrics[idx] = gauge
+ }
+
+ for _, benchmark := range benchmarks {
+ b.Run(benchmark.name+"_"+size.name, func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ writer := httptest.NewRecorder()
+ request, _ := http.NewRequest("GET", "/", nil)
+ request.Header.Add(acceptEncodingHeader, benchmark.compressionType)
+ handler.ServeHTTP(writer, request)
+ }
+ })
+ }
+ }
+}
diff --git prometheus/registry.go prometheus/registry.go
index 5e2ced25a..c6fd2f58b 100644
--- prometheus/registry.go
+++ prometheus/registry.go
@@ -314,16 +314,17 @@ func (r *Registry) Register(c Collector) error {
if dimHash != desc.dimHash {
return fmt.Errorf("a previously registered descriptor with the same fully-qualified name as %s has different label names or a different help string", desc)
}
- } else {
- // ...then check the new descriptors already seen.
- if dimHash, exists := newDimHashesByName[desc.fqName]; exists {
- if dimHash != desc.dimHash {
- return fmt.Errorf("descriptors reported by collector have inconsistent label names or help strings for the same fully-qualified name, offender is %s", desc)
- }
- } else {
- newDimHashesByName[desc.fqName] = desc.dimHash
+ continue
+ }
+
+ // ...then check the new descriptors already seen.
+ if dimHash, exists := newDimHashesByName[desc.fqName]; exists {
+ if dimHash != desc.dimHash {
+ return fmt.Errorf("descriptors reported by collector have inconsistent label names or help strings for the same fully-qualified name, offender is %s", desc)
}
+ continue
}
+ newDimHashesByName[desc.fqName] = desc.dimHash
}
// A Collector yielding no Desc at all is considered unchecked.
if len(newDescIDs) == 0 {
diff --git prometheus/summary.go prometheus/summary.go
index 146270444..1ab0e4796 100644
--- prometheus/summary.go
+++ prometheus/summary.go
@@ -783,3 +783,45 @@ func MustNewConstSummary(
}
return m
}
+
+// NewConstSummaryWithCreatedTimestamp does the same thing as NewConstSummary but sets the created timestamp.
+func NewConstSummaryWithCreatedTimestamp(
+ desc *Desc,
+ count uint64,
+ sum float64,
+ quantiles map[float64]float64,
+ ct time.Time,
+ labelValues ...string,
+) (Metric, error) {
+ if desc.err != nil {
+ return nil, desc.err
+ }
+ if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
+ return nil, err
+ }
+ return &constSummary{
+ desc: desc,
+ count: count,
+ sum: sum,
+ quantiles: quantiles,
+ labelPairs: MakeLabelPairs(desc, labelValues),
+ createdTs: timestamppb.New(ct),
+ }, nil
+}
+
+// MustNewConstSummaryWithCreatedTimestamp is a version of NewConstSummaryWithCreatedTimestamp that panics where
+// NewConstSummaryWithCreatedTimestamp would have returned an error.
+func MustNewConstSummaryWithCreatedTimestamp(
+ desc *Desc,
+ count uint64,
+ sum float64,
+ quantiles map[float64]float64,
+ ct time.Time,
+ labelValues ...string,
+) Metric {
+ m, err := NewConstSummaryWithCreatedTimestamp(desc, count, sum, quantiles, ct, labelValues...)
+ if err != nil {
+ panic(err)
+ }
+ return m
+}
diff --git prometheus/summary_test.go prometheus/summary_test.go
index d1ea07257..9c961e9b5 100644
--- prometheus/summary_test.go
+++ prometheus/summary_test.go
@@ -474,3 +474,28 @@ func TestSummaryVecCreatedTimestampWithDeletes(t *testing.T) {
})
}
}
+
+func TestNewConstSummaryWithCreatedTimestamp(t *testing.T) {
+ metricDesc := NewDesc(
+ "sample_value",
+ "sample value",
+ nil,
+ nil,
+ )
+ quantiles := map[float64]float64{50: 200.12, 99: 500.342}
+ createdTs := time.Unix(1719670764, 123)
+
+ s, err := NewConstSummaryWithCreatedTimestamp(metricDesc, 100, 200, quantiles, createdTs)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var metric dto.Metric
+ if err := s.Write(&metric); err != nil {
+ t.Fatal(err)
+ }
+
+ if metric.Summary.CreatedTimestamp.AsTime().UnixMicro() != createdTs.UnixMicro() {
+ t.Errorf("Expected created timestamp %v, got %v", createdTs, &metric.Summary.CreatedTimestamp)
+ }
+}
diff --git prometheus/testutil/promlint/promlint_test.go prometheus/testutil/promlint/promlint_test.go
index 3617ed84c..c60507c19 100644
--- prometheus/testutil/promlint/promlint_test.go
+++ prometheus/testutil/promlint/promlint_test.go
@@ -668,7 +668,6 @@ func TestLintMetricTypeInName(t *testing.T) {
twoProbTest,
genTest("instance_memory_limit_bytes_gauge", "gauge", "gauge"),
genTest("request_duration_seconds_summary", "summary", "summary"),
- genTest("request_duration_seconds_summary", "histogram", "summary"),
genTest("request_duration_seconds_histogram", "histogram", "histogram"),
genTest("request_duration_seconds_HISTOGRAM", "histogram", "histogram"),
@@ -840,3 +839,27 @@ mc_something_total 10
lintAndVerify(l2, cv)
})
}
+
+func TestLintDuplicateMetric(t *testing.T) {
+ const msg = "metric not unique"
+
+ tests := []test{
+ {
+ name: "metric not unique",
+ in: `
+# HELP not_unique_total the helptext
+# TYPE not_unique_total counter
+not_unique_total{bar="abc", spam="xyz"} 1
+not_unique_total{bar="abc", spam="xyz"} 2
+`,
+ problems: []promlint.Problem{
+ {
+ Metric: "not_unique_total",
+ Text: msg,
+ },
+ },
+ },
+ }
+
+ runTests(t, tests)
+}
diff --git prometheus/testutil/promlint/validation.go prometheus/testutil/promlint/validation.go
index f52ad9eab..e1441598d 100644
--- prometheus/testutil/promlint/validation.go
+++ prometheus/testutil/promlint/validation.go
@@ -30,4 +30,5 @@ var defaultValidations = []Validation{
validations.LintReservedChars,
validations.LintCamelCase,
validations.LintUnitAbbreviations,
+ validations.LintDuplicateMetric,
}
diff --git a/prometheus/testutil/promlint/validations/duplicate_validations.go b/prometheus/testutil/promlint/validations/duplicate_validations.go
new file mode 100644
index 000000000..fdc1e6239
--- /dev/null
+++ prometheus/testutil/promlint/validations/duplicate_validations.go
@@ -0,0 +1,37 @@
+// Copyright 2024 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package validations
+
+import (
+ "fmt"
+ "reflect"
+
+ dto "github.com/prometheus/client_model/go"
+)
+
+// LintDuplicateMetric detects duplicate metric.
+func LintDuplicateMetric(mf *dto.MetricFamily) []error {
+ var problems []error
+
+ for i, m := range mf.Metric {
+ for _, k := range mf.Metric[i+1:] {
+ if reflect.DeepEqual(m.Label, k.Label) {
+ problems = append(problems, fmt.Errorf("metric not unique"))
+ break
+ }
+ }
+ }
+
+ return problems
+}
diff --git prometheus/testutil/promlint/validations/generic_name_validations.go prometheus/testutil/promlint/validations/generic_name_validations.go
index bc8dbd1e1..de52cfee4 100644
--- prometheus/testutil/promlint/validations/generic_name_validations.go
+++ prometheus/testutil/promlint/validations/generic_name_validations.go
@@ -44,21 +44,21 @@ func LintMetricUnits(mf *dto.MetricFamily) []error {
return problems
}
-// LintMetricTypeInName detects when metric types are included in the metric name.
+// LintMetricTypeInName detects when the metric type is included in the metric name.
func LintMetricTypeInName(mf *dto.MetricFamily) []error {
+ if mf.GetType() == dto.MetricType_UNTYPED {
+ return nil
+ }
+
var problems []error
- n := strings.ToLower(mf.GetName())
- for i, t := range dto.MetricType_name {
- if i == int32(dto.MetricType_UNTYPED) {
- continue
- }
+ n := strings.ToLower(mf.GetName())
+ typename := strings.ToLower(mf.GetType().String())
- typename := strings.ToLower(t)
- if strings.Contains(n, "_"+typename+"_") || strings.HasSuffix(n, "_"+typename) {
- problems = append(problems, fmt.Errorf(`metric name should not include type '%s'`, typename))
- }
+ if strings.Contains(n, "_"+typename+"_") || strings.HasSuffix(n, "_"+typename) {
+ problems = append(problems, fmt.Errorf(`metric name should not include type '%s'`, typename))
}
+
return problems
}
diff --git prometheus/testutil/testutil.go prometheus/testutil/testutil.go
index 9dce15eaf..6f1200180 100644
--- prometheus/testutil/testutil.go
+++ prometheus/testutil/testutil.go
@@ -42,9 +42,8 @@ import (
"fmt"
"io"
"net/http"
- "reflect"
- "github.com/davecgh/go-spew/spew"
+ "github.com/kylelemons/godebug/diff"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"google.golang.org/protobuf/proto"
@@ -159,6 +158,9 @@ func GatherAndCount(g prometheus.Gatherer, metricNames ...string) (int, error) {
// ScrapeAndCompare calls a remote exporter's endpoint which is expected to return some metrics in
// plain text format. Then it compares it with the results that the `expected` would return.
// If the `metricNames` is not empty it would filter the comparison only to the given metric names.
+//
+// NOTE: Be mindful of accidental discrepancies between expected and metricNames; metricNames filter
+// both expected and scraped metrics. See https://github.com/prometheus/client_golang/issues/1351.
func ScrapeAndCompare(url string, expected io.Reader, metricNames ...string) error {
resp, err := http.Get(url)
if err != nil {
@@ -184,9 +186,11 @@ func ScrapeAndCompare(url string, expected io.Reader, metricNames ...string) err
return compareMetricFamilies(scraped, wanted, metricNames...)
}
-// CollectAndCompare registers the provided Collector with a newly created
-// pedantic Registry. It then calls GatherAndCompare with that Registry and with
-// the provided metricNames.
+// CollectAndCompare collects the metrics identified by `metricNames` and compares them in the Prometheus text
+// exposition format to the data read from expected.
+//
+// NOTE: Be mindful of accidental discrepancies between expected and metricNames; metricNames filter
+// both expected and collected metrics. See https://github.com/prometheus/client_golang/issues/1351.
func CollectAndCompare(c prometheus.Collector, expected io.Reader, metricNames ...string) error {
reg := prometheus.NewPedanticRegistry()
if err := reg.Register(c); err != nil {
@@ -199,6 +203,9 @@ func CollectAndCompare(c prometheus.Collector, expected io.Reader, metricNames .
// it to an expected output read from the provided Reader in the Prometheus text
// exposition format. If any metricNames are provided, only metrics with those
// names are compared.
+//
+// NOTE: Be mindful of accidental discrepancies between expected and metricNames; metricNames filter
+// both expected and gathered metrics. See https://github.com/prometheus/client_golang/issues/1351.
func GatherAndCompare(g prometheus.Gatherer, expected io.Reader, metricNames ...string) error {
return TransactionalGatherAndCompare(prometheus.ToTransactionalGatherer(g), expected, metricNames...)
}
@@ -207,6 +214,9 @@ func GatherAndCompare(g prometheus.Gatherer, expected io.Reader, metricNames ...
// it to an expected output read from the provided Reader in the Prometheus text
// exposition format. If any metricNames are provided, only metrics with those
// names are compared.
+//
+// NOTE: Be mindful of accidental discrepancies between expected and metricNames; metricNames filter
+// both expected and gathered metrics. See https://github.com/prometheus/client_golang/issues/1351.
func TransactionalGatherAndCompare(g prometheus.TransactionalGatherer, expected io.Reader, metricNames ...string) error {
got, done, err := g.Gather()
defer done()
@@ -222,6 +232,31 @@ func TransactionalGatherAndCompare(g prometheus.TransactionalGatherer, expected
return compareMetricFamilies(got, wanted, metricNames...)
}
+// CollectAndFormat collects the metrics identified by `metricNames` and returns them in the given format.
+func CollectAndFormat(c prometheus.Collector, format expfmt.FormatType, metricNames ...string) ([]byte, error) {
+ reg := prometheus.NewPedanticRegistry()
+ if err := reg.Register(c); err != nil {
+ return nil, fmt.Errorf("registering collector failed: %w", err)
+ }
+
+ gotFiltered, err := reg.Gather()
+ if err != nil {
+ return nil, fmt.Errorf("gathering metrics failed: %w", err)
+ }
+
+ gotFiltered = filterMetrics(gotFiltered, metricNames)
+
+ var gotFormatted bytes.Buffer
+ enc := expfmt.NewEncoder(&gotFormatted, expfmt.NewFormat(format))
+ for _, mf := range gotFiltered {
+ if err := enc.Encode(mf); err != nil {
+ return nil, fmt.Errorf("encoding gathered metrics failed: %w", err)
+ }
+ }
+
+ return gotFormatted.Bytes(), nil
+}
+
// convertReaderToMetricFamily would read from a io.Reader object and convert it to a slice of
// dto.MetricFamily.
func convertReaderToMetricFamily(reader io.Reader) ([]*dto.MetricFamily, error) {
@@ -277,73 +312,12 @@ func compare(got, want []*dto.MetricFamily) error {
return fmt.Errorf("encoding expected metrics failed: %w", err)
}
}
- if diffErr := diff(wantBuf, gotBuf); diffErr != "" {
+ if diffErr := diff.Diff(gotBuf.String(), wantBuf.String()); diffErr != "" {
return fmt.Errorf(diffErr)
}
return nil
}
-// diff returns a diff of both values as long as both are of the same type and
-// are a struct, map, slice, array or string. Otherwise it returns an empty string.
-func diff(expected, actual interface{}) string {
- if expected == nil || actual == nil {
- return ""
- }
-
- et, ek := typeAndKind(expected)
- at, _ := typeAndKind(actual)
- if et != at {
- return ""
- }
-
- if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array && ek != reflect.String {
- return ""
- }
-
- var e, a string
- c := spew.ConfigState{
- Indent: " ",
- DisablePointerAddresses: true,
- DisableCapacities: true,
- SortKeys: true,
- }
- if et != reflect.TypeOf("") {
- e = c.Sdump(expected)
- a = c.Sdump(actual)
- } else {
- e = reflect.ValueOf(expected).String()
- a = reflect.ValueOf(actual).String()
- }
-
- diff, _ := internal.GetUnifiedDiffString(internal.UnifiedDiff{
- A: internal.SplitLines(e),
- B: internal.SplitLines(a),
- FromFile: "metric output does not match expectation; want",
- FromDate: "",
- ToFile: "got:",
- ToDate: "",
- Context: 1,
- })
-
- if diff == "" {
- return ""
- }
-
- return "\n\nDiff:\n" + diff
-}
-
-// typeAndKind returns the type and kind of the given interface{}
-func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) {
- t := reflect.TypeOf(v)
- k := t.Kind()
-
- if k == reflect.Ptr {
- t = t.Elem()
- k = t.Kind()
- }
- return t, k
-}
-
func fil,terMetrics(metrics []*dto.MetricFamily, names []string) []*dto.MetricFamily {
var filtered []*dto.MetricFamily
for _, m := range metrics {
diff --git prometheus/testutil/testutil_test.go prometheus/testutil/testutil_test.go
index f2e1cbaff..06e367744 100644
--- prometheus/testutil/testutil_test.go
+++ prometheus/testutil/testutil_test.go
@@ -20,6 +20,8 @@ import (
"strings"
"testing"
+ "github.com/prometheus/common/expfmt"
+
"github.com/prometheus/client_golang/prometheus"
)
@@ -300,26 +302,20 @@ func TestMetricNotFound(t *testing.T) {
"label1": "value1",
},
})
+
c.Inc()
expected := `
some_other_metric{label1="value1"} 1
`
- expectedError := `
-
-Diff:
---- metric output does not match expectation; want
-+++ got:
-@@ -1,4 +1,4 @@
--(bytes.Buffer) # HELP some_other_metric A value that represents a counter.
--# TYPE some_other_metric counter
--some_other_metric{label1="value1"} 1
-+(bytes.Buffer) # HELP some_total A value that represents a counter.
-+# TYPE some_total counter
-+some_total{label1="value1"} 1
-
-`
+ expectedError := `-# HELP some_total A value that represents a counter.
+-# TYPE some_total counter
+-some_total{label1="value1"} 1
++# HELP some_other_metric A value that represents a counter.
++# TYPE some_other_metric counter
++some_other_metric{label1="value1"} 1
+ `
err := CollectAndCompare(c, strings.NewReader(metadata+expected))
if err == nil {
@@ -437,3 +433,32 @@ func TestCollectAndCount(t *testing.T) {
t.Errorf("unexpected metric count, got %d, want %d", got, want)
}
}
+
+func TestCollectAndFormat(t *testing.T) {
+ const expected = `# HELP foo_bar A value that represents the number of bars in foo.
+# TYPE foo_bar counter
+foo_bar{fizz="bang"} 1
+`
+ c := prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "foo_bar",
+ Help: "A value that represents the number of bars in foo.",
+ },
+ []string{"fizz"},
+ )
+ c.WithLabelValues("bang").Inc()
+
+ got, err := CollectAndFormat(c, expfmt.TypeTextPlain, "foo_bar")
+ if err != nil {
+ t.Errorf("unexpected error: %s", err.Error())
+ }
+
+ gotS := string(got)
+ if err != nil {
+ t.Errorf("unexpected error: %s", err.Error())
+ }
+
+ if gotS != expected {
+ t.Errorf("unexpected metric output, got %q, expected %q", gotS, expected)
+ }
+}
diff --git prometheus/vec.go prometheus/vec.go
index 955cfd59f..2c808eece 100644
--- prometheus/vec.go
+++ prometheus/vec.go
@@ -507,7 +507,7 @@ func (m *metricMap) getOrCreateMetricWithLabelValues(
return metric
}
-// getOrCreateMetricWithLabelValues retrieves the metric by hash and label value
+// getOrCreateMetricWithLabels retrieves the metric by hash and label value
// or creates it and returns the new one.
//
// This function holds the mutex.
diff --git a/supported_go_versions.txt b/supported_go_versions.txt
new file mode 100644
index 000000000..321c2b6b7
--- /dev/null
+++ supported_go_versions.txt
@@ -0,0 +1,3 @@
+1.22
+1.21
+1.20
\ No newline at end of file
diff --git a/tutorial/whatsup/.gitignore b/tutorials/whatsup/.gitignore
similarity index 100%
rename from tutorial/whatsup/.gitignore
rename to tutorials/whatsup/.gitignore
diff --git a/tutorial/whatsup/ContribFest.pdf b/tutorials/whatsup/ContribFest.pdf
diff --git a/tutorial/whatsup/ContribFest.pdf b/tutorials/whatsup/ContribFest.pdf
similarity index 100%
rename from tutorial/whatsup/ContribFest.pdf
rename to tutorials/whatsup/ContribFest.pdf
diff --git a/tutorial/whatsup/Makefile b/tutorials/whatsup/Makefile
diff --git a/tutorial/whatsup/Makefile b/tutorials/whatsup/Makefile
similarity index 100%
rename from tutorial/whatsup/Makefile
rename to tutorials/whatsup/Makefile
diff --git a/tutorial/whatsup/README.md b/tutorials/whatsup/README.md
diff --git a/tutorial/whatsup/README.md b/tutorials/whatsup/README.md
similarity index 100%
rename from tutorial/whatsup/README.md
rename to tutorials/whatsup/README.md
diff --git a/tutorial/whatsup/go.mod b/tutorials/whatsup/go.mod
diff --git tutorial/whatsup/go.mod tutorials/whatsup/go.mod
similarity index 76%
rename from tutorial/whatsup/go.mod
rename to tutorials/whatsup/go.mod
index 93878d708..a3311154d 100644
--- tutorial/whatsup/go.mod
+++ tutorials/whatsup/go.mod
@@ -1,4 +1,4 @@
-module github.com/prometheus/client_golang/tutorial
+module github.com/prometheus/client_golang/tutorials/whatsup
go 1.20
@@ -7,8 +7,8 @@ require (
github.com/efficientgo/core v1.0.0-rc.2
github.com/efficientgo/e2e v0.14.1-0.20230421070206-d72d43f3b937
github.com/oklog/run v1.1.0
- github.com/prometheus/client_golang v1.18.0
- github.com/prometheus/common v0.46.0
+ github.com/prometheus/client_golang v1.19.1
+ github.com/prometheus/common v0.55.0
gopkg.in/yaml.v2 v2.4.0
)
@@ -22,16 +22,17 @@ require (
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
- github.com/google/go-cmp v0.5.9 // indirect
+ github.com/google/go-cmp v0.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/pkg/errors v0.9.1 // indirect
- github.com/prometheus/client_model v0.5.0 // indirect
- github.com/prometheus/procfs v0.12.0 // indirect
+ github.com/prometheus/client_model v0.6.1 // indirect
+ github.com/prometheus/procfs v0.15.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.32.0 // indirect
go.opentelemetry.io/otel v1.7.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.3 // indirect
@@ -42,12 +43,11 @@ require (
go.opentelemetry.io/otel/sdk v1.6.3 // indirect
go.opentelemetry.io/otel/trace v1.7.0 // indirect
go.opentelemetry.io/proto/otlp v0.15.0 // indirect
- golang.org/x/net v0.20.0 // indirect
- golang.org/x/oauth2 v0.16.0 // indirect
- golang.org/x/sys v0.16.0 // indirect
- golang.org/x/text v0.14.0 // indirect
- google.golang.org/appengine v1.6.7 // indirect
+ golang.org/x/net v0.26.0 // indirect
+ golang.org/x/oauth2 v0.21.0 // indirect
+ golang.org/x/sys v0.21.0 // indirect
+ golang.org/x/text v0.16.0 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.56.3 // indirect
- google.golang.org/protobuf v1.32.0 // indirect
+ google.golang.org/protobuf v1.34.2 // indirect
)
diff --git tutorial/whatsup/go.sum tutorials/whatsup/go.sum
similarity index 95%
rename from tutorial/whatsup/go.sum
rename to tutorials/whatsup/go.sum
index 63dc245be..7a05b05e3 100644
--- tutorial/whatsup/go.sum
+++ tutorials/whatsup/go.sum
@@ -128,8 +128,8 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -173,6 +173,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
@@ -181,15 +183,15 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
-github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
+github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
+github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
-github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
-github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y=
-github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ=
-github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
-github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
+github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
+github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
+github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
+github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
+github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
+github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
@@ -201,8 +203,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -244,7 +246,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
+golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -303,16 +305,16 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
-golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
-golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
+golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
+golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -351,8 +353,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
-golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -360,8 +362,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -433,8 +435,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
-google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -501,8 +501,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
-google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
@@ -512,8 +512,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/tutorial/whatsup/internal/acceptance_test.go b/tutorials/whatsup/internal/acceptance_test.go
similarity index 100%
rename from tutorial/whatsup/internal/acceptance_test.go
rename to tutorials/whatsup/internal/acceptance_test.go
diff --git a/tutorial/whatsup/internal/common.go b/tutorials/whatsup/internal/common.go
diff --git a/tutorial/whatsup/internal/common.go b/tutorials/whatsup/internal/common.go
similarity index 100%
rename from tutorial/whatsup/internal/common.go
rename to tutorials/whatsup/internal/common.go
diff --git a/tutorial/whatsup/internal/playground_test.go b/tutorials/whatsup/internal/playground_test.go
diff --git a/tutorial/whatsup/internal/playground_test.go b/tutorials/whatsup/internal/playground_test.go
similarity index 100%
rename from tutorial/whatsup/internal/playground_test.go
rename to tutorials/whatsup/internal/playground_test.go
diff --git a/tutorial/whatsup/main.go b/tutorials/whatsup/main.go
diff --git tutorial/whatsup/main.go tutorials/whatsup/main.go
similarity index 98%
rename from tutorial/whatsup/main.go
rename to tutorials/whatsup/main.go
index b4252f3ba..5943eefb0 100644
--- tutorial/whatsup/main.go
+++ tutorials/whatsup/main.go
@@ -36,7 +36,7 @@ import (
"github.com/prometheus/client_golang/api"
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
"github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/tutorial/internal"
+ "github.com/prometheus/client_golang/tutorials/whatsup/internal"
)
func main() {
diff --git tutorial/whatsup/reference/main.go tutorials/whatsup/reference/main.go
similarity index 99%
rename from tutorial/whatsup/reference/main.go
rename to tutorials/whatsup/reference/main.go
index 3c4115149..d7ac1c7b7 100644
--- tutorial/whatsup/reference/main.go
+++ tutorials/whatsup/reference/main.go
@@ -39,7 +39,7 @@ import (
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
- "github.com/prometheus/client_golang/tutorial/internal"
+ "github.com/prometheus/client_golang/tutorials/whatsup/internal"
)
func main() {
diff --git a/update-go-version.bash b/update-go-version.bash
new file mode 100644
index 000000000..02e6aa6c4
--- /dev/null
+++ update-go-version.bash
@@ -0,0 +1,23 @@
+#!/bin/env bash
+
+set -e
+
+get_latest_versions() {
+ curl -s https://go.dev/VERSION?m=text | sed -E -n 's/go([0-9]+\.[0-9]+|\.[0-9]+).*/\1/p'
+}
+
+current_version=$(cat supported_go_versions.txt | head -n 1)
+latest_version=$(get_latest_versions)
+
+# Check for new version of Go, and generate go collector test files
+# Add new Go version to supported_go_versions.txt, and remove the oldest version
+if [[ ! $current_version =~ $latest_version ]]; then
+ echo "New Go version available: $latest_version"
+ echo "Updating supported_go_versions.txt and generating Go Collector test files"
+ sed -i "1i $latest_version" supported_go_versions.txt
+ sed -i '$d' supported_go_versions.txt
+ make generate-go-collector-test-files
+else
+ echo "No new Go version detected. Current Go version is: $current_version"
+fi
+
DescriptionThis PR updates the client_golang library with several improvements and new features. The main changes include adding support for new Go versions, updating dependencies, improving metrics collection and handling, enhancing compression support, and various bug fixes and performance improvements. ChangesChanges
sequenceDiagram
participant Client
participant PrometheusClient
participant Collector
participant Registry
participant HTTPHandler
Client->>PrometheusClient: Initialize
PrometheusClient->>Collector: Create Collectors
Collector->>Registry: Register
Client->>HTTPHandler: Request Metrics
HTTPHandler->>Registry: Gather Metrics
Registry->>Collector: Collect Metrics
Collector-->>Registry: Return Metrics
Registry-->>HTTPHandler: Return Gathered Metrics
HTTPHandler->>HTTPHandler: Compress Response (if supported)
HTTPHandler-->>Client: Return Metrics (compressed if applicable)
Possible Issues
Security HotspotsNo significant security issues were identified in this change. However, as with any major update, it's important to thoroughly test the library in your specific use case to ensure no unexpected behaviors arise. |
This PR contains the following updates:
v1.19.0
->v1.20.5
Release Notes
prometheus/client_golang (github.com/prometheus/client_golang)
v1.20.5
: / 2024-10-15Compare Source
We decided to revert the
testutil
change that made our util functions less error-prone, but created a lot of work for our downstream users. Apologies for the pain! This revert should not cause any major breaking change, even if you already did the work--unless you depend on the exact error message.Going forward, we plan to reinforce our release testing strategy [1],[2] and deliver an enhanced
testutil
package/module with more flexible and safer APIs.Thanks to @dashpole @dgrisonnet @kakkoyun @ArthurSens @vesari @logicalhan @krajorama @bwplotka who helped in this patch release! 🤗
Changelog
[BUGFIX] testutil: Reverted #1424; functions using compareMetricFamilies are (again) only failing if filtered metricNames are in the expected input. #1645
v1.20.4
Compare Source
v1.20.3
Compare Source
v1.20.2
Compare Source
v1.20.1
Compare Source
v1.20.0
Compare Source
Thanks everyone for contributions!
collectors.NewGoCollector()
collector. Given its popular usage, expect your binary to expose two additional metric.Changes
go_memstat_lookups_total
metric which was always 0; Go runtime stopped sharing pointer lookup statistics. #1577go_gc_gogc_percent
,go_gc_gomemlimit_bytes
andgo_sched_gomaxprocs_threads
as those are recommended by the Go team. #1559runtime/metrics
sourcing each metric (if relevant). #1568 #1578zstd
on scrape, controlled by the requestAccept-Encoding
header. #1496WithLimit
parameter to all API methods that supports it. #1544process_network_receive_bytes_total
andprocess_network_transmit_bytes_total
. #1555instrumentation wrapping supports new and future extra responseWriter methods. #1480
All commits
New Contributors
Full Changelog: prometheus/client_golang@v1.19.1...v1.20.0
v1.19.1
Compare Source
What's Changed
golang.org/x/sys
andgoogle.golang.org/protobuf
New Contributors
Full Changelog: prometheus/client_golang@v1.19.0...v1.19.1
Configuration
📅 Schedule: Branch creation - "* 0-4 * * 3" (UTC), Automerge - At any time (no schedule defined).
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.