mirror of
https://github.com/prometheus/statsd_exporter.git
synced 2025-10-03 22:19:42 +00:00
Compare commits
91 commits
v0.27.2-pr
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
d9e9dcd39c | ||
|
c349fbcb34 | ||
|
93a61f6927 | ||
|
34fff57424 | ||
|
144bc6bdb5 | ||
|
3eff3c0cc7 | ||
|
fa4ca034b6 | ||
|
0dbbd7e2a4 | ||
|
9fbaeeeaaf | ||
|
848039ff9e | ||
|
c2c7fb083c | ||
|
c223653967 | ||
|
eaeb9287f9 | ||
|
f1f841310f | ||
|
e8b902793d | ||
|
530ac73d7f | ||
|
e94c23c18f | ||
|
d0fd88fea4 | ||
|
a1eb47f5cb | ||
|
7d00675ec9 | ||
|
05caab2de4 | ||
|
ab1f7e9fa7 | ||
|
66b02179b4 | ||
|
3464f277df | ||
|
62ccc8c4a4 | ||
|
4feb411e09 | ||
|
cbadf49755 | ||
|
919fad636f | ||
|
58bae426f1 | ||
|
743c7f91b8 | ||
|
0fc3a29d69 | ||
|
8ef8c1e933 | ||
|
4f0e8416e0 | ||
|
c5785482d9 | ||
|
ccc44de63f | ||
|
c2d5d18cdf | ||
|
343a50a3d1 | ||
|
dbf1dba353 | ||
|
edd97953e3 | ||
|
e1cef5e7a7 | ||
|
c0758e1c1a | ||
|
267753cdaa | ||
|
e1d5b04928 | ||
|
3b86000e17 | ||
|
a94ccca200 | ||
|
4d9b92568d | ||
|
34a445d689 | ||
|
45044cbc48 | ||
|
cc1fcb45af | ||
|
ce77e77d0e | ||
|
f37cc2b683 | ||
|
2f34dcbf06 | ||
|
91cc6182b1 | ||
|
a7bc1e8652 | ||
|
4014d84fff | ||
|
0d54d0ed32 | ||
|
493392ecf0 | ||
|
7f8da59baa | ||
|
37d4cfa364 | ||
|
96ed54b157 | ||
|
d45b0e3e36 | ||
|
a125dac85b | ||
|
e5c2d40581 | ||
|
46cb41614e | ||
|
32fee3f94b | ||
|
ffbede602e | ||
|
1093a661ea | ||
|
98de69fdda | ||
|
fa11c20fd1 | ||
|
0e8f597c40 | ||
|
285293f587 | ||
|
7b20c1c6b0 | ||
|
8cf95221d1 | ||
|
3a4875414b | ||
|
e103da1d09 | ||
|
3082cf590c | ||
|
5d59712795 | ||
|
9803afaa8d | ||
|
009fcca35f | ||
|
3e8c235b06 | ||
|
c0a390a2c4 | ||
|
9cf8734c97 | ||
|
211680b98c | ||
|
bcd2cd8862 | ||
|
3cd0545478 | ||
|
64cc00e019 | ||
|
8aac1cabf3 | ||
|
e3378f8027 | ||
|
c18857b71b | ||
|
a20db6eec6 | ||
|
2a6c1a326b |
35 changed files with 782 additions and 567 deletions
|
@ -5,7 +5,7 @@ executors:
|
|||
# Whenever the Go version is updated here, .promu.yml should also be updated.
|
||||
golang:
|
||||
docker:
|
||||
- image: cimg/go:1.22
|
||||
- image: cimg/go:1.24
|
||||
jobs:
|
||||
test:
|
||||
executor: golang
|
||||
|
|
8
.github/workflows/container_description.yml
vendored
8
.github/workflows/container_description.yml
vendored
|
@ -18,7 +18,9 @@ jobs:
|
|||
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
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set docker hub repo name
|
||||
run: echo "DOCKER_REPO_NAME=$(make docker-repo-name)" >> $GITHUB_ENV
|
||||
- name: Push README to Dockerhub
|
||||
|
@ -40,7 +42,9 @@ jobs:
|
|||
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
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- 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
|
||||
|
|
14
.github/workflows/golangci-lint.yml
vendored
14
.github/workflows/golangci-lint.yml
vendored
|
@ -24,16 +24,18 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
persist-credentials: false
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: 1.24.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@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0
|
||||
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
|
||||
with:
|
||||
args: --verbose
|
||||
version: v1.60.1
|
||||
version: v2.2.1
|
||||
|
|
|
@ -1,26 +1,40 @@
|
|||
version: "2"
|
||||
run:
|
||||
issues-exit-code: 1
|
||||
tests: true
|
||||
linters:
|
||||
disable:
|
||||
- errcheck
|
||||
enable:
|
||||
- goimports
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- nakedret
|
||||
- staticcheck
|
||||
- unused
|
||||
- whitespace
|
||||
linters-settings:
|
||||
govet:
|
||||
enable-all: true
|
||||
disable:
|
||||
- fieldalignment
|
||||
disable:
|
||||
- errcheck
|
||||
settings:
|
||||
govet:
|
||||
disable:
|
||||
- fieldalignment
|
||||
enable-all: true
|
||||
staticcheck:
|
||||
checks:
|
||||
- all
|
||||
- -ST1000
|
||||
- -ST1003
|
||||
- -ST1020
|
||||
exclusions:
|
||||
rules:
|
||||
- path: (.+)\.go$
|
||||
text: 'shadow: declaration of "err" shadows declaration at line (\d+)'
|
||||
warn-unused: true
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
exclude:
|
||||
- 'shadow: declaration of "err" shadows declaration at line (\d+)'
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
formatters:
|
||||
enable:
|
||||
- gofumpt
|
||||
- goimports
|
||||
settings:
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
goimports:
|
||||
local-prefixes:
|
||||
- github.com/prometheus/statsd_exporter
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
go:
|
||||
# Whenever the Go version is updated here, .circle/config.yml should also
|
||||
# be updated.
|
||||
version: 1.22
|
||||
version: 1.24
|
||||
repository:
|
||||
path: github.com/prometheus/statsd_exporter
|
||||
build:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
extends: default
|
||||
ignore: |
|
||||
ui/react-app/node_modules
|
||||
**/node_modules
|
||||
|
||||
rules:
|
||||
braces:
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
## 0.27.2-pr579 / 2024-09-16
|
||||
## 0.28.0 / 2024-10-25
|
||||
|
||||
* [CHANGE] Update exporter-toolkit & switch to slog ([#586](https://github.com/prometheus/statsd_exporter/pull/586))
|
||||
* [CHANGE] Update client_golang and prometheus-common
|
||||
|
||||
This is a breaking change for library users.
|
||||
|
||||
## 0.27.2 / 2024-10-07
|
||||
|
||||
* [BUGFIX] Fix panic on certain invalid lines ([#579](https://github.com/prometheus/statsd_exporter/pull/579))
|
||||
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
* Matthias Rampke <matthias@prometheus.io>
|
||||
* Pedro Tanaka [@pedro-stanaka](https://github.com/pedro-stanaka)
|
||||
|
|
|
@ -61,7 +61,8 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_
|
|||
SKIP_GOLANGCI_LINT :=
|
||||
GOLANGCI_LINT :=
|
||||
GOLANGCI_LINT_OPTS ?=
|
||||
GOLANGCI_LINT_VERSION ?= v1.60.1
|
||||
GOLANGCI_LINT_VERSION ?= v2.2.1
|
||||
GOLANGCI_FMT_OPTS ?=
|
||||
# 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))
|
||||
|
@ -138,7 +139,7 @@ common-deps:
|
|||
update-go-deps:
|
||||
@echo ">> updating Go dependencies"
|
||||
@for m in $$($(GO) list -mod=readonly -m -f '{{ if and (not .Indirect) (not .Main)}}{{.Path}}{{end}}' all); do \
|
||||
$(GO) get -d $$m; \
|
||||
$(GO) get $$m; \
|
||||
done
|
||||
$(GO) mod tidy
|
||||
|
||||
|
@ -156,9 +157,13 @@ $(GOTEST_DIR):
|
|||
@mkdir -p $@
|
||||
|
||||
.PHONY: common-format
|
||||
common-format:
|
||||
common-format: $(GOLANGCI_LINT)
|
||||
@echo ">> formatting code"
|
||||
$(GO) fmt $(pkgs)
|
||||
ifdef GOLANGCI_LINT
|
||||
@echo ">> formatting code with golangci-lint"
|
||||
$(GOLANGCI_LINT) fmt $(GOLANGCI_FMT_OPTS)
|
||||
endif
|
||||
|
||||
.PHONY: common-vet
|
||||
common-vet:
|
||||
|
@ -248,8 +253,8 @@ $(PROMU):
|
|||
cp $(PROMU_TMP)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM)/promu $(FIRST_GOPATH)/bin/promu
|
||||
rm -r $(PROMU_TMP)
|
||||
|
||||
.PHONY: proto
|
||||
proto:
|
||||
.PHONY: common-proto
|
||||
common-proto:
|
||||
@echo ">> generating code from proto files"
|
||||
@./scripts/genproto.sh
|
||||
|
||||
|
@ -275,3 +280,9 @@ $(1)_precheck:
|
|||
exit 1; \
|
||||
fi
|
||||
endef
|
||||
|
||||
govulncheck: install-govulncheck
|
||||
govulncheck ./...
|
||||
|
||||
install-govulncheck:
|
||||
command -v govulncheck > /dev/null || go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
|
|
41
README.md
41
README.md
|
@ -418,46 +418,7 @@ Setting `buckets` or `quantiles` in the defaults is deprecated in favor of `hist
|
|||
|
||||
If `summary_options` is present in a mapping config, it will only override the fields set in the mapping. Unset fields in the mapping will take the values from the defaults.
|
||||
|
||||
```yaml
|
||||
defaults:
|
||||
observer_type: histogram
|
||||
histogram_options:
|
||||
buckets: [.005, .01, .025, .05, .1, .25, .5, 1, 2.5 ]
|
||||
native_histogram_bucket_factor: 1.1
|
||||
native_histogram_max_buckets: 256
|
||||
summary_options:
|
||||
quantiles:
|
||||
- quantile: 0.99
|
||||
error: 0.001
|
||||
- quantile: 0.95
|
||||
error: 0.01
|
||||
- quantile: 0.9
|
||||
error: 0.05
|
||||
- quantile: 0.5
|
||||
error: 0.005
|
||||
max_age: 5m
|
||||
age_buckets: 2
|
||||
buf_cap: 1000
|
||||
match_type: glob
|
||||
glob_disable_ordering: false
|
||||
ttl: 0 # metrics do not expire
|
||||
mappings:
|
||||
# This will be a histogram using the buckets set in `defaults`.
|
||||
- match: "test.timing.*.*.*"
|
||||
name: "my_timer"
|
||||
labels:
|
||||
provider: "$2"
|
||||
outcome: "$3"
|
||||
job: "${1}_server"
|
||||
# This will be a summary using the summary_options set in `defaults`
|
||||
- match: "other.distribution.*.*.*"
|
||||
observer_type: summary
|
||||
name: "other_distribution"
|
||||
labels:
|
||||
provider: "$2"
|
||||
outcome: "$3"
|
||||
job: "${1}_server_other"
|
||||
```
|
||||
See [`config.exmple.yml`](config.example.yml) for an annotated example configuration.
|
||||
|
||||
### `drop` action
|
||||
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.27.2-pr579
|
||||
0.28.0
|
||||
|
|
160
bridge_test.go
160
bridge_test.go
|
@ -15,14 +15,15 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/prometheus/common/promslog"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/clock"
|
||||
"github.com/prometheus/statsd_exporter/pkg/event"
|
||||
|
@ -40,7 +41,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
name: "empty",
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "simple counter",
|
||||
in: "foo:2|c",
|
||||
out: event.Events{
|
||||
|
@ -50,7 +52,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "simple gauge",
|
||||
in: "foo:3|g",
|
||||
out: event.Events{
|
||||
|
@ -60,7 +63,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
GLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "gauge with sampling",
|
||||
in: "foo:3|g|@0.2",
|
||||
out: event.Events{
|
||||
|
@ -70,7 +74,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
GLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "gauge decrement",
|
||||
in: "foo:-10|g",
|
||||
out: event.Events{
|
||||
|
@ -81,7 +86,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
GLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "gauge increment",
|
||||
in: "foo:+10|g",
|
||||
out: event.Events{
|
||||
|
@ -92,7 +98,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
GLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "gauge set negative",
|
||||
in: "foo:0|g\nfoo:-1|g",
|
||||
out: event.Events{
|
||||
|
@ -109,7 +116,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
GLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
// Test the sequence given here https://github.com/statsd/statsd/blob/master/docs/metric_types.md#gauges
|
||||
name: "gauge up and down",
|
||||
in: "gaugor:333|g\ngaugor:-10|g\ngaugor:+4|g",
|
||||
|
@ -133,7 +141,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
GLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "simple timer",
|
||||
in: "foo:200|ms",
|
||||
out: event.Events{
|
||||
|
@ -143,7 +152,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
OLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "simple histogram",
|
||||
in: "foo:200|h",
|
||||
out: event.Events{
|
||||
|
@ -153,7 +163,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
OLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "simple distribution",
|
||||
in: "foo:200|d",
|
||||
out: event.Events{
|
||||
|
@ -163,7 +174,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
OLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "distribution with sampling",
|
||||
in: "foo:0.01|d|@0.2|#tag1:bar,#tag2:baz",
|
||||
out: event.Events{
|
||||
|
@ -193,7 +205,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
OLabels: map[string]string{"tag1": "bar", "tag2": "baz"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "librato tag extension",
|
||||
in: "foo#tag1=bar,tag2=baz:100|c",
|
||||
out: event.Events{
|
||||
|
@ -203,7 +216,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"tag1": "bar", "tag2": "baz"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "librato tag extension with tag keys unsupported by prometheus",
|
||||
in: "foo#09digits=0,tag.with.dots=1:100|c",
|
||||
out: event.Events{
|
||||
|
@ -213,7 +227,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"_09digits": "0", "tag_with_dots": "1"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "influxdb tag extension",
|
||||
in: "foo,tag1=bar,tag2=baz:100|c",
|
||||
out: event.Events{
|
||||
|
@ -223,7 +238,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"tag1": "bar", "tag2": "baz"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "SignalFx tag extension",
|
||||
in: "foo.[tag1=bar,tag2=baz]test:100|c",
|
||||
out: event.Events{
|
||||
|
@ -233,7 +249,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"tag1": "bar", "tag2": "baz"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "SignalFx tag extension, tags at end of name",
|
||||
in: "foo.test[tag1=bar,tag2=baz]:100|c",
|
||||
out: event.Events{
|
||||
|
@ -243,7 +260,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"tag1": "bar", "tag2": "baz"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "SignalFx tag extension, tags at beginning of name",
|
||||
in: "[tag1=bar,tag2=baz]foo.test:100|c",
|
||||
out: event.Events{
|
||||
|
@ -253,7 +271,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"tag1": "bar", "tag2": "baz"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "SignalFx tag extension, no tags",
|
||||
in: "foo.[]test:100|c",
|
||||
out: event.Events{
|
||||
|
@ -263,7 +282,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "SignalFx tag extension, non-kv tags",
|
||||
in: "foo.[tag1,tag2]test:100|c",
|
||||
out: event.Events{
|
||||
|
@ -273,7 +293,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "SignalFx tag extension, missing closing bracket",
|
||||
in: "[tag1=bar,tag2=bazfoo.test:100|c",
|
||||
out: event.Events{
|
||||
|
@ -283,7 +304,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "SignalFx tag extension, missing opening bracket",
|
||||
in: "tag1=bar,tag2=baz]foo.test:100|c",
|
||||
out: event.Events{
|
||||
|
@ -293,7 +315,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "influxdb tag extension with tag keys unsupported by prometheus",
|
||||
in: "foo,09digits=0,tag.with.dots=1:100|c",
|
||||
out: event.Events{
|
||||
|
@ -303,7 +326,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"_09digits": "0", "tag_with_dots": "1"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "datadog tag extension",
|
||||
in: "foo:100|c|#tag1:bar,tag2:baz",
|
||||
out: event.Events{
|
||||
|
@ -313,7 +337,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"tag1": "bar", "tag2": "baz"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "datadog tag extension with # in all keys (as sent by datadog php client)",
|
||||
in: "foo:100|c|#tag1:bar,#tag2:baz",
|
||||
out: event.Events{
|
||||
|
@ -323,7 +348,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"tag1": "bar", "tag2": "baz"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "datadog tag extension with tag keys unsupported by prometheus",
|
||||
in: "foo:100|c|#09digits:0,tag.with.dots:1",
|
||||
out: event.Events{
|
||||
|
@ -333,7 +359,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"_09digits": "0", "tag_with_dots": "1"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "datadog tag extension with valueless tags: ignored",
|
||||
in: "foo:100|c|#tag_without_a_value",
|
||||
out: event.Events{
|
||||
|
@ -343,7 +370,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "datadog tag extension with valueless tags (edge case)",
|
||||
in: "foo:100|c|#tag_without_a_value,tag:value",
|
||||
out: event.Events{
|
||||
|
@ -353,7 +381,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"tag": "value"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "datadog tag extension with empty tags (edge case)",
|
||||
in: "foo:100|c|#tag:value,,",
|
||||
out: event.Events{
|
||||
|
@ -363,7 +392,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"tag": "value"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "datadog tag extension with sampling",
|
||||
in: "foo:100|c|@0.1|#tag1:bar,#tag2:baz",
|
||||
out: event.Events{
|
||||
|
@ -373,23 +403,28 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"tag1": "bar", "tag2": "baz"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "librato/dogstatsd mixed tag styles without sampling",
|
||||
in: "foo#tag1=foo,tag3=bing:100|c|#tag1:bar,#tag2:baz",
|
||||
out: event.Events{},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "signalfx/dogstatsd mixed tag styles without sampling",
|
||||
in: "foo[tag1=foo,tag3=bing]:100|c|#tag1:bar,#tag2:baz",
|
||||
out: event.Events{},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "influxdb/dogstatsd mixed tag styles without sampling",
|
||||
in: "foo,tag1=foo,tag3=bing:100|c|#tag1:bar,#tag2:baz",
|
||||
out: event.Events{},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "mixed tag styles with sampling",
|
||||
in: "foo#tag1=foo,tag3=bing:100|c|@0.1|#tag1:bar,#tag2:baz",
|
||||
out: event.Events{},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "histogram with sampling",
|
||||
in: "foo:0.01|h|@0.2|#tag1:bar,#tag2:baz",
|
||||
out: event.Events{
|
||||
|
@ -419,7 +454,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
OLabels: map[string]string{"tag1": "bar", "tag2": "baz"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "datadog tag extension with multiple colons",
|
||||
in: "foo:100|c|@0.1|#tag1:foo:bar",
|
||||
out: event.Events{
|
||||
|
@ -429,13 +465,16 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"tag1": "foo:bar"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "datadog tag extension with invalid utf8 tag values",
|
||||
in: "foo:100|c|@0.1|#tag:\xc3\x28invalid",
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "datadog tag extension with both valid and invalid utf8 tag values",
|
||||
in: "foo:100|c|@0.1|#tag1:valid,tag2:\xc3\x28invalid",
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "multiple metrics with invalid datadog utf8 tag values",
|
||||
in: "foo:200|c|#tag:value\nfoo:300|c|#tag:\xc3\x28invalid",
|
||||
out: event.Events{
|
||||
|
@ -445,7 +484,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{"tag": "value"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "combined multiline metrics",
|
||||
in: "foo:200|ms:300|ms:5|c|@0.1:6|g\nbar:1|c:5|ms",
|
||||
out: event.Events{
|
||||
|
@ -480,7 +520,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
OLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "timings with sampling factor",
|
||||
in: "foo.timing:0.5|ms|@0.1",
|
||||
out: event.Events{
|
||||
|
@ -495,16 +536,20 @@ func TestHandlePacket(t *testing.T) {
|
|||
&event.ObserverEvent{OMetricName: "foo.timing", OValue: 0.0005, OLabels: map[string]string{}},
|
||||
&event.ObserverEvent{OMetricName: "foo.timing", OValue: 0.0005, OLabels: map[string]string{}},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "bad line",
|
||||
in: "foo",
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "bad component",
|
||||
in: "foo:1",
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "bad value",
|
||||
in: "foo:1o|c",
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "illegal sampling factor",
|
||||
in: "foo:1|c|@bar",
|
||||
out: event.Events{
|
||||
|
@ -514,7 +559,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "zero sampling factor",
|
||||
in: "foo:2|c|@0",
|
||||
out: event.Events{
|
||||
|
@ -524,7 +570,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "illegal stat type",
|
||||
in: "foo:2|t",
|
||||
},
|
||||
|
@ -550,7 +597,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
CLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "ms timer with conversion to seconds",
|
||||
in: "foo:200|ms",
|
||||
out: event.Events{
|
||||
|
@ -560,7 +608,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
OLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "histogram with no unit conversion",
|
||||
in: "foo:200|h",
|
||||
out: event.Events{
|
||||
|
@ -570,7 +619,8 @@ func TestHandlePacket(t *testing.T) {
|
|||
OLabels: map[string]string{},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: "distribution with no unit conversion",
|
||||
in: "foo:200|d",
|
||||
out: event.Events{
|
||||
|
@ -592,7 +642,7 @@ func TestHandlePacket(t *testing.T) {
|
|||
for k, l := range []statsDPacketHandler{&listener.StatsDUDPListener{
|
||||
Conn: nil,
|
||||
EventHandler: nil,
|
||||
Logger: log.NewNopLogger(),
|
||||
Logger: promslog.NewNopLogger(),
|
||||
LineParser: parser,
|
||||
UDPPackets: udpPackets,
|
||||
UDPPacketDrops: udpPacketDrops,
|
||||
|
@ -605,7 +655,7 @@ func TestHandlePacket(t *testing.T) {
|
|||
}, &mockStatsDTCPListener{listener.StatsDTCPListener{
|
||||
Conn: nil,
|
||||
EventHandler: nil,
|
||||
Logger: log.NewNopLogger(),
|
||||
Logger: promslog.NewNopLogger(),
|
||||
LineParser: parser,
|
||||
LinesReceived: linesReceived,
|
||||
EventsFlushed: eventsFlushed,
|
||||
|
@ -616,7 +666,7 @@ func TestHandlePacket(t *testing.T) {
|
|||
TCPConnections: tcpConnections,
|
||||
TCPErrors: tcpErrors,
|
||||
TCPLineTooLong: tcpLineTooLong,
|
||||
}, log.NewNopLogger()}} {
|
||||
}, promslog.NewNopLogger()}} {
|
||||
events := make(chan event.Events, 32)
|
||||
l.SetEventHandler(&event.UnbufferedEventHandler{C: events})
|
||||
for i, scenario := range scenarios {
|
||||
|
@ -649,7 +699,7 @@ type statsDPacketHandler interface {
|
|||
|
||||
type mockStatsDTCPListener struct {
|
||||
listener.StatsDTCPListener
|
||||
log.Logger
|
||||
*slog.Logger
|
||||
}
|
||||
|
||||
func (ml *mockStatsDTCPListener) HandlePacket(packet []byte) {
|
||||
|
@ -710,7 +760,7 @@ mappings:
|
|||
events := make(chan event.Events)
|
||||
defer close(events)
|
||||
go func() {
|
||||
ex := exporter.NewExporter(prometheus.DefaultRegisterer, testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := exporter.NewExporter(prometheus.DefaultRegisterer, testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Listen(events)
|
||||
}()
|
||||
|
||||
|
|
84
config.example.yml
Normal file
84
config.example.yml
Normal file
|
@ -0,0 +1,84 @@
|
|||
# This is a modern starter configuration for the statsd_exporter. It uses native
|
||||
# and classic histograms by default, as well as the fast glob matching without ordering.
|
||||
defaults:
|
||||
# Convert timers into histograms, which can be aggregated across instances. See below for an example of using a summary with pre-computed quantiles instead.
|
||||
observer_type: histogram
|
||||
histogram_options:
|
||||
# Expose native histograms.
|
||||
# A bucket spread factor of 1.1 allows for pretty good precision in
|
||||
# measuring latencies, as long as they are not spread too widely.
|
||||
native_histogram_bucket_factor: 1.1
|
||||
# If latency is spread too widely, the resolution is automatically reduced,
|
||||
# keeping within the maximum bucket count. 160 is the default maximum bucket
|
||||
# count in OpenTelemetry, and is a good default for most use cases.
|
||||
native_histogram_max_buckets: 160
|
||||
# Also expose classic histograms, with a spread of latency buckets from 5ms to 2.5s.
|
||||
buckets: [.005, .01, .025, .05, .1, .25, .5, 1, 2.5 ]
|
||||
# Metric can be optionally exposed as a summary. Set reasonable defaults for
|
||||
# that case. Only used with `observer_type: summary`.
|
||||
summary_options:
|
||||
quantiles:
|
||||
- quantile: 0.99
|
||||
error: 0.001
|
||||
- quantile: 0.95
|
||||
error: 0.01
|
||||
- quantile: 0.9
|
||||
error: 0.05
|
||||
- quantile: 0.5
|
||||
error: 0.005
|
||||
max_age: 5m
|
||||
age_buckets: 2
|
||||
buf_cap: 1000
|
||||
# Match using glob patterns by default, it is much faster than regex.
|
||||
match_type: glob
|
||||
# Sepecific matches take precedence over *, ignoring the order of the mapping rules.
|
||||
# Regex matches are always evaluated after glob matches, and do honor the
|
||||
# order of mapping rules. Avoid using regex matches where possible.
|
||||
glob_disable_ordering: true
|
||||
# Do not expire metrics by default. When deployed as a sidecar, and restarted
|
||||
# together with application deployments, this matches the behavior of native
|
||||
# in-application instrumentation. Set a reasonable TTL if the exporter has its
|
||||
# own lifecycle.
|
||||
ttl: 0
|
||||
mappings:
|
||||
# Example 1: This will be a histogram using the bucket configuration set in `defaults`.
|
||||
- match: "test.timing.*.*.*"
|
||||
name: "my_timer"
|
||||
help: "Latency of the application"
|
||||
labels:
|
||||
provider: "$2"
|
||||
outcome: "$3"
|
||||
job: "${1}_server"
|
||||
# Example 2: Use a much tighter bucket spread. Use this to track latencies in a
|
||||
# relatively narrow range. For classic histograms, we need to set the buckets
|
||||
# around this band manually; native histograms are sparse and adapt automatically.
|
||||
- match: "consistent_app.timing.*.*.*"
|
||||
name: "my_consistent_timer"
|
||||
help: "Latency of an application with a consistent latency pattern"
|
||||
histogram_options:
|
||||
native_histogram_bucket_factor: 1.005
|
||||
# For the sake of this example, set the buckets for a latency distribution
|
||||
# tightly clustered around 15ms.
|
||||
buckets: [0.01, 0.011, 0.012, 0.013, 0.014, 0.015, 0.016, 0.017, 0.018, 0.019, 0.02]
|
||||
labels:
|
||||
provider: "$2"
|
||||
outcome: "$3"
|
||||
job: "${1}_server"
|
||||
# Example 3: This will be a summary using the summary_options set in `defaults`
|
||||
#
|
||||
# Note: Summary metrics are not recommended because they can not be aggregated over multiple instances.
|
||||
- match: "other.distribution.*.*.*"
|
||||
observer_type: summary
|
||||
name: "other_distribution"
|
||||
labels:
|
||||
provider: "$2"
|
||||
outcome: "$3"
|
||||
job: "${1}_server_other"
|
||||
# Optional: drop all unmapped metrics. Keep this as the last mapping rule. Any
|
||||
# metric not matched otherwise will be dropped because "." matches all metrics.
|
||||
# Without this rule, all metrics will be exposed, with the metric name
|
||||
# automatically generated from the statsd metric name.
|
||||
# - match: "."
|
||||
# match_type: regex
|
||||
# action: drop
|
||||
# name: "dropped"
|
|
@ -17,9 +17,8 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/common/promslog"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/event"
|
||||
"github.com/prometheus/statsd_exporter/pkg/exporter"
|
||||
|
@ -43,7 +42,7 @@ func benchmarkUDPListener(times int, b *testing.B) {
|
|||
"some_very_useful_metrics_with_quite_a_log_name:13|c",
|
||||
}
|
||||
bytesInput := make([]string, len(input)*times)
|
||||
logger := log.NewNopLogger()
|
||||
logger := promslog.NewNopLogger()
|
||||
for run := 0; run < times; run++ {
|
||||
for i := 0; i < len(input); i++ {
|
||||
bytesInput[run*len(input)+i] = fmt.Sprintf("run%d%s", run, input[i])
|
||||
|
@ -92,9 +91,11 @@ func benchmarkUDPListener(times int, b *testing.B) {
|
|||
func BenchmarkUDPListener1(b *testing.B) {
|
||||
benchmarkUDPListener(1, b)
|
||||
}
|
||||
|
||||
func BenchmarkUDPListener5(b *testing.B) {
|
||||
benchmarkUDPListener(5, b)
|
||||
}
|
||||
|
||||
func BenchmarkUDPListener50(b *testing.B) {
|
||||
benchmarkUDPListener(50, b)
|
||||
}
|
||||
|
@ -175,7 +176,7 @@ mappings:
|
|||
b.Fatalf("Config load error: %s %s", config, err)
|
||||
}
|
||||
|
||||
ex := exporter.NewExporter(prometheus.DefaultRegisterer, testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := exporter.NewExporter(prometheus.DefaultRegisterer, testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
|
||||
// reset benchmark timer to not measure startup costs
|
||||
b.ResetTimer()
|
||||
|
|
31
go.mod
31
go.mod
|
@ -1,15 +1,14 @@
|
|||
module github.com/prometheus/statsd_exporter
|
||||
|
||||
go 1.21
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/alecthomas/kingpin/v2 v2.4.0
|
||||
github.com/go-kit/log v0.2.1
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
|
||||
github.com/prometheus/client_golang v1.20.3
|
||||
github.com/prometheus/client_model v0.6.1
|
||||
github.com/prometheus/common v0.57.0
|
||||
github.com/prometheus/exporter-toolkit v0.11.0
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/prometheus/client_model v0.6.2
|
||||
github.com/prometheus/common v0.65.0
|
||||
github.com/prometheus/exporter-toolkit v0.14.0
|
||||
github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
@ -19,18 +18,18 @@ require (
|
|||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/jpillora/backoff v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/mdlayher/socket v0.4.1 // indirect
|
||||
github.com/mdlayher/vsock v1.2.1 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/oauth2 v0.22.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
)
|
||||
|
|
69
go.sum
69
go.sum
|
@ -11,41 +11,41 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
|
|||
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-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
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/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
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/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
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/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||
github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=
|
||||
github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE=
|
||||
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/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.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4=
|
||||
github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
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.57.0 h1:Ro/rKjwdq9mZn1K5QPctzh+MA4Lp0BuYk5ZZEVhoNcY=
|
||||
github.com/prometheus/common v0.57.0/go.mod h1:7uRPFSUTbfZWsJ7MHY56sqt7hLQu3bxXHDnNhl8E9qI=
|
||||
github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g=
|
||||
github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q=
|
||||
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/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
|
||||
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/exporter-toolkit v0.14.0 h1:NMlswfibpcZZ+H0sZBiTjrA3/aBFHkNZqE+iCj5EmRg=
|
||||
github.com/prometheus/exporter-toolkit v0.14.0/go.mod h1:Gu5LnVvt7Nr/oqTBUC23WILZepW0nffNo10XdhQcwWA=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
@ -55,26 +55,29 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/
|
|||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
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/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807 h1:LUsDduamlucuNnWcaTbXQ6aLILFcLXADpOzeEH3U+OI=
|
||||
github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
|
||||
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
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/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
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/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
|
|
@ -16,7 +16,7 @@ package main
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus/common/promslog"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/line"
|
||||
)
|
||||
|
@ -36,7 +36,7 @@ var (
|
|||
"foo15:200|ms:300|ms:5|c|@0.1:6|g\nfoo15a:1|c:5|ms",
|
||||
"some_very_useful_metrics_with_quite_a_log_name:13|c",
|
||||
}
|
||||
nopLogger = log.NewNopLogger()
|
||||
nopLogger = promslog.NewNopLogger()
|
||||
)
|
||||
|
||||
func benchmarkLinesToEvents(times int, b *testing.B, input []string) {
|
||||
|
@ -65,9 +65,11 @@ func benchmarkLinesToEvents(times int, b *testing.B, input []string) {
|
|||
func BenchmarkLineToEventsMixed1(b *testing.B) {
|
||||
benchmarkLinesToEvents(1, b, mixedLines)
|
||||
}
|
||||
|
||||
func BenchmarkLineToEventsMixed5(b *testing.B) {
|
||||
benchmarkLinesToEvents(5, b, mixedLines)
|
||||
}
|
||||
|
||||
func BenchmarkLineToEventsMixed50(b *testing.B) {
|
||||
benchmarkLinesToEvents(50, b, mixedLines)
|
||||
}
|
||||
|
|
95
main.go
95
main.go
|
@ -16,6 +16,7 @@ package main
|
|||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
|
@ -25,20 +26,18 @@ import (
|
|||
"syscall"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
versioncollector "github.com/prometheus/client_golang/prometheus/collectors/version"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/prometheus/common/promlog"
|
||||
"github.com/prometheus/common/promlog/flag"
|
||||
"github.com/prometheus/common/promslog"
|
||||
"github.com/prometheus/common/promslog/flag"
|
||||
"github.com/prometheus/common/version"
|
||||
"github.com/prometheus/exporter-toolkit/web"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/address"
|
||||
"github.com/prometheus/statsd_exporter/pkg/event"
|
||||
"github.com/prometheus/statsd_exporter/pkg/exporter"
|
||||
"github.com/prometheus/statsd_exporter/pkg/level"
|
||||
"github.com/prometheus/statsd_exporter/pkg/line"
|
||||
"github.com/prometheus/statsd_exporter/pkg/listener"
|
||||
"github.com/prometheus/statsd_exporter/pkg/mapper"
|
||||
|
@ -149,7 +148,7 @@ var (
|
|||
Name: "statsd_exporter_events_conflict_total",
|
||||
Help: "The total number of StatsD events with conflicting names.",
|
||||
},
|
||||
[]string{"type"},
|
||||
[]string{"type", "metric_name"},
|
||||
)
|
||||
errorEventStats = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
|
@ -174,49 +173,49 @@ var (
|
|||
)
|
||||
)
|
||||
|
||||
func serveHTTP(mux http.Handler, listenAddress string, logger log.Logger) {
|
||||
level.Error(logger).Log("msg", http.ListenAndServe(listenAddress, mux))
|
||||
func serveHTTP(mux http.Handler, listenAddress string, logger *slog.Logger) {
|
||||
logger.Error(http.ListenAndServe(listenAddress, mux).Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func sighupConfigReloader(fileName string, mapper *mapper.MetricMapper, logger log.Logger) {
|
||||
func sighupConfigReloader(fileName string, mapper *mapper.MetricMapper, logger *slog.Logger) {
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, syscall.SIGHUP)
|
||||
|
||||
for s := range signals {
|
||||
if fileName == "" {
|
||||
level.Warn(logger).Log("msg", "Received signal but no mapping config to reload", "signal", s)
|
||||
logger.Warn("Received signal but no mapping config to reload", "signal", s)
|
||||
continue
|
||||
}
|
||||
|
||||
level.Info(logger).Log("msg", "Received signal, attempting reload", "signal", s)
|
||||
logger.Info("Received signal, attempting reload", "signal", s)
|
||||
|
||||
reloadConfig(fileName, mapper, logger)
|
||||
}
|
||||
}
|
||||
|
||||
func reloadConfig(fileName string, mapper *mapper.MetricMapper, logger log.Logger) {
|
||||
func reloadConfig(fileName string, mapper *mapper.MetricMapper, logger *slog.Logger) {
|
||||
err := mapper.InitFromFile(fileName)
|
||||
if err != nil {
|
||||
level.Info(logger).Log("msg", "Error reloading config", "error", err)
|
||||
logger.Info("Error reloading config", "error", err)
|
||||
configLoads.WithLabelValues("failure").Inc()
|
||||
} else {
|
||||
level.Info(logger).Log("msg", "Config reloaded successfully")
|
||||
logger.Info("Config reloaded successfully")
|
||||
configLoads.WithLabelValues("success").Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func dumpFSM(mapper *mapper.MetricMapper, dumpFilename string, logger log.Logger) error {
|
||||
func dumpFSM(mapper *mapper.MetricMapper, dumpFilename string, logger *slog.Logger) error {
|
||||
f, err := os.Create(dumpFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
level.Info(logger).Log("msg", "Start dumping FSM", "file_name", dumpFilename)
|
||||
logger.Info("Start dumping FSM", "file_name", dumpFilename)
|
||||
w := bufio.NewWriter(f)
|
||||
mapper.FSM.DumpFSM(w)
|
||||
w.Flush()
|
||||
f.Close()
|
||||
level.Info(logger).Log("msg", "Finish dumping FSM")
|
||||
logger.Info("Finish dumping FSM")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -271,17 +270,13 @@ func main() {
|
|||
udpPacketQueueSize = kingpin.Flag("statsd.udp-packet-queue-size", "Size of internal queue for processing UDP packets.").Default("10000").Int()
|
||||
)
|
||||
|
||||
promlogConfig := &promlog.Config{}
|
||||
flag.AddFlags(kingpin.CommandLine, promlogConfig)
|
||||
promslogConfig := &promslog.Config{}
|
||||
flag.AddFlags(kingpin.CommandLine, promslogConfig)
|
||||
kingpin.Version(version.Print("statsd_exporter"))
|
||||
kingpin.CommandLine.UsageWriter(os.Stdout)
|
||||
kingpin.HelpFlag.Short('h')
|
||||
kingpin.Parse()
|
||||
logger := promlog.New(promlogConfig)
|
||||
if err := level.SetLogLevel(promlogConfig.Level.String()); err != nil {
|
||||
level.Error(logger).Log("msg", "failed to set log level", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
logger := promslog.New(promslogConfig)
|
||||
prometheus.MustRegister(versioncollector.NewCollector("statsd_exporter"))
|
||||
|
||||
parser := line.NewParser()
|
||||
|
@ -298,8 +293,8 @@ func main() {
|
|||
parser.EnableSignalFXParsing()
|
||||
}
|
||||
|
||||
level.Info(logger).Log("msg", "Starting StatsD -> Prometheus Exporter", "version", version.Info())
|
||||
level.Info(logger).Log("msg", "Build context", "context", version.BuildContext())
|
||||
logger.Info("Starting StatsD -> Prometheus Exporter", "version", version.Info())
|
||||
logger.Info("Build context", "context", version.BuildContext())
|
||||
|
||||
events := make(chan event.Events, *eventQueueSize)
|
||||
defer close(events)
|
||||
|
@ -309,7 +304,7 @@ func main() {
|
|||
|
||||
cache, err := getCache(*cacheSize, *cacheType, thisMapper.Registerer)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "Unable to setup metric mapper cache", "error", err)
|
||||
logger.Error("Unable to setup metric mapper cache", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
thisMapper.UseCache(cache)
|
||||
|
@ -317,13 +312,13 @@ func main() {
|
|||
if *mappingConfig != "" {
|
||||
err := thisMapper.InitFromFile(*mappingConfig)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "error loading config", "error", err)
|
||||
logger.Error("error loading config", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if *dumpFSMPath != "" {
|
||||
err := dumpFSM(thisMapper, *dumpFSMPath, logger)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "error dumping FSM", "error", err)
|
||||
logger.Error("error dumping FSM", "error", err)
|
||||
// Failure to dump the FSM is an error (the user asked for it and it
|
||||
// didn't happen) but not fatal (the exporter is fully functional
|
||||
// afterwards).
|
||||
|
@ -334,7 +329,7 @@ func main() {
|
|||
exporter := exporter.NewExporter(prometheus.DefaultRegisterer, thisMapper, logger, eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
|
||||
if *checkConfig {
|
||||
level.Info(logger).Log("msg", "Configuration check successful, exiting")
|
||||
logger.Info("Configuration check successful, exiting")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -343,35 +338,35 @@ func main() {
|
|||
var err error
|
||||
relayTarget, err = relay.NewRelay(logger, *relayAddr, *relayPacketLen)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "Unable to create relay", "err", err)
|
||||
logger.Error("Unable to create relay", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
level.Info(logger).Log("msg", "Accepting StatsD Traffic", "udp", *statsdListenUDP, "tcp", *statsdListenTCP, "unixgram", *statsdListenUnixgram)
|
||||
level.Info(logger).Log("msg", "Accepting Prometheus Requests", "addr", *listenAddress)
|
||||
logger.Info("Accepting StatsD Traffic", "udp", *statsdListenUDP, "tcp", *statsdListenTCP, "unixgram", *statsdListenUnixgram)
|
||||
logger.Info("Accepting Prometheus Requests", "addr", *listenAddress)
|
||||
|
||||
if *statsdListenUDP == "" && *statsdListenTCP == "" && *statsdListenUnixgram == "" {
|
||||
level.Error(logger).Log("At least one of UDP/TCP/Unixgram listeners must be specified.")
|
||||
logger.Error("At least one of UDP/TCP/Unixgram listeners must be specified.")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if *statsdListenUDP != "" {
|
||||
udpListenAddr, err := address.UDPAddrFromString(*statsdListenUDP)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "invalid UDP listen address", "address", *statsdListenUDP, "error", err)
|
||||
logger.Error("invalid UDP listen address", "address", *statsdListenUDP, "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
uconn, err := net.ListenUDP("udp", udpListenAddr)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "failed to start UDP listener", "error", err)
|
||||
logger.Error("failed to start UDP listener", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if *readBuffer != 0 {
|
||||
err = uconn.SetReadBuffer(*readBuffer)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "error setting UDP read buffer", "error", err)
|
||||
logger.Error("error setting UDP read buffer", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
@ -401,12 +396,12 @@ func main() {
|
|||
if *statsdListenTCP != "" {
|
||||
tcpListenAddr, err := address.TCPAddrFromString(*statsdListenTCP)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "invalid TCP listen address", "address", *statsdListenUDP, "error", err)
|
||||
logger.Error("invalid TCP listen address", "address", *statsdListenUDP, "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
tconn, err := net.ListenTCP("tcp", tcpListenAddr)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", err)
|
||||
logger.Error("failed to start TCP listener", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer tconn.Close()
|
||||
|
@ -434,7 +429,7 @@ func main() {
|
|||
if *statsdListenUnixgram != "" {
|
||||
var err error
|
||||
if _, err = os.Stat(*statsdListenUnixgram); !os.IsNotExist(err) {
|
||||
level.Error(logger).Log("msg", "Unixgram socket already exists", "socket_name", *statsdListenUnixgram)
|
||||
logger.Error("Unixgram socket already exists", "socket_name", *statsdListenUnixgram)
|
||||
os.Exit(1)
|
||||
}
|
||||
uxgconn, err := net.ListenUnixgram("unixgram", &net.UnixAddr{
|
||||
|
@ -442,7 +437,7 @@ func main() {
|
|||
Name: *statsdListenUnixgram,
|
||||
})
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "failed to listen on Unixgram socket", "error", err)
|
||||
logger.Error("failed to listen on Unixgram socket", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
@ -451,7 +446,7 @@ func main() {
|
|||
if *readBuffer != 0 {
|
||||
err = uxgconn.SetReadBuffer(*readBuffer)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("msg", "error setting Unixgram read buffer", "error", err)
|
||||
logger.Error("error setting Unixgram read buffer", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
@ -481,11 +476,11 @@ func main() {
|
|||
// convert the string to octet
|
||||
perm, err := strconv.ParseInt("0"+string(*statsdUnixSocketMode), 8, 32)
|
||||
if err != nil {
|
||||
level.Warn(logger).Log("Bad permission %s: %v, ignoring\n", *statsdUnixSocketMode, err)
|
||||
logger.Warn("Bad permission %s: %v, ignoring\n", *statsdUnixSocketMode, err)
|
||||
} else {
|
||||
err = os.Chmod(*statsdListenUnixgram, os.FileMode(perm))
|
||||
if err != nil {
|
||||
level.Warn(logger).Log("Failed to change unixgram socket permission: %v", err)
|
||||
logger.Warn("Failed to change unixgram socket permission", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -507,7 +502,7 @@ func main() {
|
|||
}
|
||||
landingPage, err := web.NewLandingPage(landingConfig)
|
||||
if err != nil {
|
||||
level.Error(logger).Log("err", err)
|
||||
logger.Error("error creating landing page", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
mux.Handle("/", landingPage)
|
||||
|
@ -520,10 +515,10 @@ func main() {
|
|||
if r.Method == http.MethodPut || r.Method == http.MethodPost {
|
||||
fmt.Fprintf(w, "Requesting reload")
|
||||
if *mappingConfig == "" {
|
||||
level.Warn(logger).Log("msg", "Received lifecycle api reload but no mapping config to reload")
|
||||
logger.Warn("Received lifecycle api reload but no mapping config to reload")
|
||||
return
|
||||
}
|
||||
level.Info(logger).Log("msg", "Received lifecycle api reload, attempting reload")
|
||||
logger.Info("Received lifecycle api reload, attempting reload")
|
||||
reloadConfig(*mappingConfig, thisMapper, logger)
|
||||
}
|
||||
})
|
||||
|
@ -537,7 +532,7 @@ func main() {
|
|||
|
||||
mux.HandleFunc("/-/healthy", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet {
|
||||
level.Debug(logger).Log("msg", "Received health check")
|
||||
logger.Debug("Received health check")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, "Statsd Exporter is Healthy.\n")
|
||||
}
|
||||
|
@ -545,7 +540,7 @@ func main() {
|
|||
|
||||
mux.HandleFunc("/-/ready", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet {
|
||||
level.Debug(logger).Log("msg", "Received ready check")
|
||||
logger.Debug("Received ready check")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, "Statsd Exporter is Ready.\n")
|
||||
}
|
||||
|
@ -562,8 +557,8 @@ func main() {
|
|||
// quit if we get a message on either channel
|
||||
select {
|
||||
case sig := <-signals:
|
||||
level.Info(logger).Log("msg", "Received os signal, exiting", "signal", sig.String())
|
||||
logger.Info("Received os signal, exiting", "signal", sig.String())
|
||||
case <-quitChan:
|
||||
level.Info(logger).Log("msg", "Received lifecycle api quit, exiting")
|
||||
logger.Info("Received lifecycle api quit, exiting")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/clock"
|
||||
"github.com/prometheus/statsd_exporter/pkg/mapper"
|
||||
)
|
||||
|
@ -39,6 +40,7 @@ func (c *CounterEvent) MetricName() string { return c.CMetricName }
|
|||
func (c *CounterEvent) Value() float64 { return c.CValue }
|
||||
func (c *CounterEvent) Labels() map[string]string { return c.CLabels }
|
||||
func (c *CounterEvent) MetricType() mapper.MetricType { return mapper.MetricTypeCounter }
|
||||
func (c *CounterEvent) Values() []float64 { return []float64{c.CValue} }
|
||||
|
||||
type GaugeEvent struct {
|
||||
GMetricName string
|
||||
|
@ -51,6 +53,7 @@ func (g *GaugeEvent) MetricName() string { return g.GMetricName }
|
|||
func (g *GaugeEvent) Value() float64 { return g.GValue }
|
||||
func (g *GaugeEvent) Labels() map[string]string { return g.GLabels }
|
||||
func (g *GaugeEvent) MetricType() mapper.MetricType { return mapper.MetricTypeGauge }
|
||||
func (g *GaugeEvent) Values() []float64 { return []float64{g.GValue} }
|
||||
|
||||
type ObserverEvent struct {
|
||||
OMetricName string
|
||||
|
@ -62,6 +65,7 @@ func (o *ObserverEvent) MetricName() string { return o.OMetricName }
|
|||
func (o *ObserverEvent) Value() float64 { return o.OValue }
|
||||
func (o *ObserverEvent) Labels() map[string]string { return o.OLabels }
|
||||
func (o *ObserverEvent) MetricType() mapper.MetricType { return mapper.MetricTypeObserver }
|
||||
func (o *ObserverEvent) Values() []float64 { return []float64{o.OValue} }
|
||||
|
||||
type Events []Event
|
||||
|
||||
|
@ -136,3 +140,87 @@ type UnbufferedEventHandler struct {
|
|||
func (ueh *UnbufferedEventHandler) Queue(events Events) {
|
||||
ueh.C <- events
|
||||
}
|
||||
|
||||
// MultiValueEvent is an event that contains multiple values, it is going to replace the existing Event interface.
|
||||
type MultiValueEvent interface {
|
||||
MetricName() string
|
||||
Labels() map[string]string
|
||||
MetricType() mapper.MetricType
|
||||
Values() []float64
|
||||
}
|
||||
|
||||
type MultiObserverEvent struct {
|
||||
OMetricName string
|
||||
OValues []float64 // DataDog extensions allow multiple values in a single sample
|
||||
OLabels map[string]string
|
||||
SampleRate float64
|
||||
}
|
||||
|
||||
type ExpandableEvent interface {
|
||||
Expand() []Event
|
||||
}
|
||||
|
||||
func (m *MultiObserverEvent) MetricName() string { return m.OMetricName }
|
||||
func (m *MultiObserverEvent) Value() float64 { return m.OValues[0] }
|
||||
func (m *MultiObserverEvent) Labels() map[string]string { return m.OLabels }
|
||||
func (m *MultiObserverEvent) MetricType() mapper.MetricType { return mapper.MetricTypeObserver }
|
||||
func (m *MultiObserverEvent) Values() []float64 { return m.OValues }
|
||||
|
||||
// Expand returns a list of events that are the result of expanding the multi-value event.
|
||||
// This will be used as a middle-step in the pipeline to convert multi-value events to single-value events.
|
||||
// And keep the exporter code compatible with previous versions.
|
||||
func (m *MultiObserverEvent) Expand() []Event {
|
||||
if len(m.OValues) == 1 && m.SampleRate == 0 {
|
||||
return []Event{
|
||||
&ObserverEvent{
|
||||
OMetricName: m.OMetricName,
|
||||
OValue: m.OValues[0],
|
||||
OLabels: copyLabels(m.OLabels),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
events := make([]Event, 0, len(m.OValues))
|
||||
for _, value := range m.OValues {
|
||||
events = append(events, &ObserverEvent{
|
||||
OMetricName: m.OMetricName,
|
||||
OValue: value,
|
||||
OLabels: copyLabels(m.OLabels),
|
||||
})
|
||||
}
|
||||
|
||||
if m.SampleRate > 0 && m.SampleRate < 1 {
|
||||
multiplier := int(1 / m.SampleRate)
|
||||
multipliedEvents := make([]Event, 0, len(events)*multiplier)
|
||||
for i := 0; i < multiplier; i++ {
|
||||
for _, event := range events {
|
||||
e := event.(*ObserverEvent)
|
||||
multipliedEvents = append(multipliedEvents, &ObserverEvent{
|
||||
OMetricName: e.OMetricName,
|
||||
OValue: e.OValue,
|
||||
OLabels: copyLabels(e.OLabels),
|
||||
})
|
||||
}
|
||||
}
|
||||
return multipliedEvents
|
||||
}
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
// Helper function to copy labels map
|
||||
func copyLabels(labels map[string]string) map[string]string {
|
||||
newLabels := make(map[string]string, len(labels))
|
||||
for k, v := range labels {
|
||||
newLabels[k] = v
|
||||
}
|
||||
return newLabels
|
||||
}
|
||||
|
||||
var (
|
||||
_ ExpandableEvent = &MultiObserverEvent{}
|
||||
_ MultiValueEvent = &MultiObserverEvent{}
|
||||
_ MultiValueEvent = &CounterEvent{}
|
||||
_ MultiValueEvent = &GaugeEvent{}
|
||||
_ MultiValueEvent = &ObserverEvent{}
|
||||
)
|
||||
|
|
|
@ -14,11 +14,15 @@
|
|||
package event
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/clock"
|
||||
"github.com/prometheus/statsd_exporter/pkg/mapper"
|
||||
)
|
||||
|
||||
var eventsFlushed = prometheus.NewCounter(
|
||||
|
@ -85,3 +89,206 @@ func TestEventIntervalFlush(t *testing.T) {
|
|||
t.Fatal("Expected 10 events in the event channel, but got", len(events))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiValueEvent(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
event MultiValueEvent
|
||||
wantValues []float64
|
||||
wantName string
|
||||
wantType mapper.MetricType
|
||||
wantLabels map[string]string
|
||||
}{
|
||||
{
|
||||
name: "MultiObserverEvent with single value",
|
||||
event: &MultiObserverEvent{
|
||||
OMetricName: "test_metric",
|
||||
OValues: []float64{1.0},
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
SampleRate: 0,
|
||||
},
|
||||
wantValues: []float64{1.0},
|
||||
wantName: "test_metric",
|
||||
wantType: mapper.MetricTypeObserver,
|
||||
wantLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
{
|
||||
name: "MultiObserverEvent with multiple values",
|
||||
event: &MultiObserverEvent{
|
||||
OMetricName: "test_metric",
|
||||
OValues: []float64{1.0, 2.0, 3.0},
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
SampleRate: 0.5,
|
||||
},
|
||||
wantValues: []float64{1.0, 2.0, 3.0},
|
||||
wantName: "test_metric",
|
||||
wantType: mapper.MetricTypeObserver,
|
||||
wantLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
{
|
||||
name: "CounterEvent implements MultiValueEvent",
|
||||
event: &CounterEvent{
|
||||
CMetricName: "test_counter",
|
||||
CValue: 42.0,
|
||||
CLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
wantValues: []float64{42.0},
|
||||
wantName: "test_counter",
|
||||
wantType: mapper.MetricTypeCounter,
|
||||
wantLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
{
|
||||
name: "GaugeEvent implements MultiValueEvent",
|
||||
event: &GaugeEvent{
|
||||
GMetricName: "test_gauge",
|
||||
GValue: 123.0,
|
||||
GLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
wantValues: []float64{123.0},
|
||||
wantName: "test_gauge",
|
||||
wantType: mapper.MetricTypeGauge,
|
||||
wantLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
{
|
||||
name: "ObserverEvent implements MultiValueEvent",
|
||||
event: &ObserverEvent{
|
||||
OMetricName: "test_observer",
|
||||
OValue: 99.0,
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
wantValues: []float64{99.0},
|
||||
wantName: "test_observer",
|
||||
wantType: mapper.MetricTypeObserver,
|
||||
wantLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.event.Values(); !reflect.DeepEqual(got, tt.wantValues) {
|
||||
t.Errorf("MultiValueEvent.Values() = %v, want %v", got, tt.wantValues)
|
||||
}
|
||||
if got := tt.event.MetricName(); got != tt.wantName {
|
||||
t.Errorf("MultiValueEvent.MetricName() = %v, want %v", got, tt.wantName)
|
||||
}
|
||||
if got := tt.event.MetricType(); got != tt.wantType {
|
||||
t.Errorf("MultiValueEvent.MetricType() = %v, want %v", got, tt.wantType)
|
||||
}
|
||||
if got := tt.event.Labels(); !reflect.DeepEqual(got, tt.wantLabels) {
|
||||
t.Errorf("MultiValueEvent.Labels() = %v, want %v", got, tt.wantLabels)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiObserverEvent_Expand(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
event *MultiObserverEvent
|
||||
wantEvents []Event
|
||||
}{
|
||||
{
|
||||
name: "single value no sampling",
|
||||
event: &MultiObserverEvent{
|
||||
OMetricName: "test_metric",
|
||||
OValues: []float64{1.0},
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
SampleRate: 0,
|
||||
},
|
||||
wantEvents: []Event{
|
||||
&ObserverEvent{
|
||||
OMetricName: "test_metric",
|
||||
OValue: 1.0,
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple values no sampling",
|
||||
event: &MultiObserverEvent{
|
||||
OMetricName: "test_metric",
|
||||
OValues: []float64{1.0, 2.0, 3.0},
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
SampleRate: 0,
|
||||
},
|
||||
wantEvents: []Event{
|
||||
&ObserverEvent{
|
||||
OMetricName: "test_metric",
|
||||
OValue: 1.0,
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
&ObserverEvent{
|
||||
OMetricName: "test_metric",
|
||||
OValue: 2.0,
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
&ObserverEvent{
|
||||
OMetricName: "test_metric",
|
||||
OValue: 3.0,
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple values with sampling",
|
||||
event: &MultiObserverEvent{
|
||||
OMetricName: "test_metric",
|
||||
OValues: []float64{1.0, 2.0},
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
SampleRate: 0.5,
|
||||
},
|
||||
wantEvents: []Event{
|
||||
&ObserverEvent{
|
||||
OMetricName: "test_metric",
|
||||
OValue: 1.0,
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
&ObserverEvent{
|
||||
OMetricName: "test_metric",
|
||||
OValue: 2.0,
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
&ObserverEvent{
|
||||
OMetricName: "test_metric",
|
||||
OValue: 1.0,
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
&ObserverEvent{
|
||||
OMetricName: "test_metric",
|
||||
OValue: 2.0,
|
||||
OLabels: map[string]string{"label": "value"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
got := tt.event.Expand()
|
||||
if len(tt.wantEvents) != len(got) {
|
||||
t.Fatalf("Expected %d events, but got %d", len(tt.wantEvents), len(got))
|
||||
}
|
||||
|
||||
eventCount := func(events []Event) map[string]int {
|
||||
counts := make(map[string]int)
|
||||
for _, event := range events {
|
||||
oe := event.(*ObserverEvent)
|
||||
key := fmt.Sprintf("%s%f%v", oe.OMetricName, oe.OValue, oe.OLabels)
|
||||
counts[key]++
|
||||
}
|
||||
return counts
|
||||
}
|
||||
|
||||
wantMap := eventCount(tt.wantEvents)
|
||||
gotMap := eventCount(got)
|
||||
|
||||
for key, count := range wantMap {
|
||||
if gotMap[key] != count {
|
||||
t.Fatalf("Event mismatch for key %v: expected %d, got %d", key, count, gotMap[key])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,15 +14,14 @@
|
|||
package exporter
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/clock"
|
||||
"github.com/prometheus/statsd_exporter/pkg/event"
|
||||
"github.com/prometheus/statsd_exporter/pkg/level"
|
||||
"github.com/prometheus/statsd_exporter/pkg/mapper"
|
||||
"github.com/prometheus/statsd_exporter/pkg/registry"
|
||||
)
|
||||
|
@ -43,7 +42,7 @@ type Registry interface {
|
|||
type Exporter struct {
|
||||
Mapper *mapper.MetricMapper
|
||||
Registry Registry
|
||||
Logger log.Logger
|
||||
Logger *slog.Logger
|
||||
EventsActions *prometheus.CounterVec
|
||||
EventsUnmapped prometheus.Counter
|
||||
ErrorEventStats *prometheus.CounterVec
|
||||
|
@ -63,7 +62,7 @@ func (b *Exporter) Listen(e <-chan event.Events) {
|
|||
b.Registry.RemoveStaleMetrics()
|
||||
case events, ok := <-e:
|
||||
if !ok {
|
||||
level.Debug(b.Logger).Log("msg", "Channel is closed. Break out of Exporter.Listener.")
|
||||
b.Logger.Debug("Channel is closed. Break out of Exporter.Listener.")
|
||||
removeStaleMetricsTicker.Stop()
|
||||
return
|
||||
}
|
||||
|
@ -99,7 +98,7 @@ func (b *Exporter) handleEvent(thisEvent event.Event) {
|
|||
prometheusLabels := thisEvent.Labels()
|
||||
if present {
|
||||
if mapping.Name == "" {
|
||||
level.Debug(b.Logger).Log("msg", "The mapping generates an empty metric name", "metric_name", thisEvent.MetricName(), "match", mapping.Match)
|
||||
b.Logger.Debug("The mapping generates an empty metric name", "metric_name", thisEvent.MetricName(), "match", mapping.Match)
|
||||
b.ErrorEventStats.WithLabelValues("empty_metric_name").Inc()
|
||||
return
|
||||
}
|
||||
|
@ -127,7 +126,7 @@ func (b *Exporter) handleEvent(thisEvent event.Event) {
|
|||
// We don't accept negative values for counters. Incrementing the counter with a negative number
|
||||
// will cause the exporter to panic. Instead we will warn and continue to the next event.
|
||||
if eventValue < 0.0 {
|
||||
level.Debug(b.Logger).Log("msg", "counter must be non-negative value", "metric", metricName, "event_value", eventValue)
|
||||
b.Logger.Debug("counter must be non-negative value", "metric", metricName, "event_value", eventValue)
|
||||
b.ErrorEventStats.WithLabelValues("illegal_negative_counter").Inc()
|
||||
return
|
||||
}
|
||||
|
@ -137,8 +136,8 @@ func (b *Exporter) handleEvent(thisEvent event.Event) {
|
|||
counter.Add(eventValue)
|
||||
b.EventStats.WithLabelValues("counter").Inc()
|
||||
} else {
|
||||
level.Debug(b.Logger).Log("msg", regErrF, "metric", metricName, "error", err)
|
||||
b.ConflictingEventStats.WithLabelValues("counter").Inc()
|
||||
b.Logger.Debug(regErrF, "metric", metricName, "error", err)
|
||||
b.ConflictingEventStats.WithLabelValues("counter", metricName).Inc()
|
||||
}
|
||||
|
||||
case *event.GaugeEvent:
|
||||
|
@ -152,8 +151,8 @@ func (b *Exporter) handleEvent(thisEvent event.Event) {
|
|||
}
|
||||
b.EventStats.WithLabelValues("gauge").Inc()
|
||||
} else {
|
||||
level.Debug(b.Logger).Log("msg", regErrF, "metric", metricName, "error", err)
|
||||
b.ConflictingEventStats.WithLabelValues("gauge").Inc()
|
||||
b.Logger.Debug(regErrF, "metric", metricName, "error", err)
|
||||
b.ConflictingEventStats.WithLabelValues("gauge", metricName).Inc()
|
||||
}
|
||||
|
||||
case *event.ObserverEvent:
|
||||
|
@ -172,8 +171,8 @@ func (b *Exporter) handleEvent(thisEvent event.Event) {
|
|||
histogram.Observe(eventValue)
|
||||
b.EventStats.WithLabelValues("observer").Inc()
|
||||
} else {
|
||||
level.Debug(b.Logger).Log("msg", regErrF, "metric", metricName, "error", err)
|
||||
b.ConflictingEventStats.WithLabelValues("observer").Inc()
|
||||
b.Logger.Debug(regErrF, "metric", metricName, "error", err)
|
||||
b.ConflictingEventStats.WithLabelValues("observer", metricName).Inc()
|
||||
}
|
||||
|
||||
case mapper.ObserverTypeDefault, mapper.ObserverTypeSummary:
|
||||
|
@ -182,22 +181,22 @@ func (b *Exporter) handleEvent(thisEvent event.Event) {
|
|||
summary.Observe(eventValue)
|
||||
b.EventStats.WithLabelValues("observer").Inc()
|
||||
} else {
|
||||
level.Debug(b.Logger).Log("msg", regErrF, "metric", metricName, "error", err)
|
||||
b.ConflictingEventStats.WithLabelValues("observer").Inc()
|
||||
b.Logger.Debug(regErrF, "metric", metricName, "error", err)
|
||||
b.ConflictingEventStats.WithLabelValues("observer", metricName).Inc()
|
||||
}
|
||||
|
||||
default:
|
||||
level.Error(b.Logger).Log("msg", "unknown observer type", "type", t)
|
||||
b.Logger.Error("unknown observer type", "type", t)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
default:
|
||||
level.Debug(b.Logger).Log("msg", "Unsupported event type")
|
||||
b.Logger.Debug("Unsupported event type")
|
||||
b.EventStats.WithLabelValues("illegal").Inc()
|
||||
}
|
||||
}
|
||||
|
||||
func NewExporter(reg prometheus.Registerer, mapper *mapper.MetricMapper, logger log.Logger, eventsActions *prometheus.CounterVec, eventsUnmapped prometheus.Counter, errorEventStats *prometheus.CounterVec, eventStats *prometheus.CounterVec, conflictingEventStats *prometheus.CounterVec, metricsCount *prometheus.GaugeVec) *Exporter {
|
||||
func NewExporter(reg prometheus.Registerer, mapper *mapper.MetricMapper, logger *slog.Logger, eventsActions *prometheus.CounterVec, eventsUnmapped prometheus.Counter, errorEventStats, eventStats, conflictingEventStats *prometheus.CounterVec, metricsCount *prometheus.GaugeVec) *Exporter {
|
||||
return &Exporter{
|
||||
Mapper: mapper,
|
||||
Registry: registry.NewRegistry(reg, mapper),
|
||||
|
|
|
@ -15,13 +15,14 @@ package exporter
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/prometheus/common/promslog"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/clock"
|
||||
"github.com/prometheus/statsd_exporter/pkg/event"
|
||||
|
@ -116,7 +117,7 @@ var (
|
|||
Name: "statsd_exporter_events_conflict_total",
|
||||
Help: "The total number of StatsD events with conflicting names.",
|
||||
},
|
||||
[]string{"type"},
|
||||
[]string{"type", "metric_name"},
|
||||
)
|
||||
errorEventStats = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
|
@ -172,7 +173,7 @@ func TestNegativeCounter(t *testing.T) {
|
|||
|
||||
testMapper := mapper.MetricMapper{}
|
||||
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Listen(events)
|
||||
|
||||
updated := getTelemetryCounterValue(errorCounter)
|
||||
|
@ -253,7 +254,7 @@ mappings:
|
|||
t.Fatalf("Config load error: %s %s", config, err)
|
||||
}
|
||||
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Listen(events)
|
||||
|
||||
metrics, err := prometheus.DefaultGatherer.Gather()
|
||||
|
@ -316,7 +317,7 @@ mappings:
|
|||
t.Fatalf("Config load error: %s %s", config, err)
|
||||
}
|
||||
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Listen(events)
|
||||
|
||||
metrics, err := prometheus.DefaultGatherer.Gather()
|
||||
|
@ -359,14 +360,14 @@ mappings:
|
|||
honor_labels: true
|
||||
`
|
||||
testMapper := &mapper.MetricMapper{
|
||||
Logger: log.NewNopLogger(),
|
||||
Logger: promslog.NewNopLogger(),
|
||||
}
|
||||
err := testMapper.InitFromYAMLString(config)
|
||||
if err != nil {
|
||||
t.Fatalf("Config load error: %s %s", config, err)
|
||||
}
|
||||
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Listen(events)
|
||||
|
||||
metrics, err := prometheus.DefaultGatherer.Gather()
|
||||
|
@ -647,7 +648,7 @@ mappings:
|
|||
close(events)
|
||||
}()
|
||||
reg := prometheus.NewRegistry()
|
||||
ex := NewExporter(reg, testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := NewExporter(reg, testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Listen(events)
|
||||
|
||||
metrics, err := reg.Gather()
|
||||
|
@ -702,7 +703,7 @@ mappings:
|
|||
errorCounter := errorEventStats.WithLabelValues("empty_metric_name")
|
||||
prev := getTelemetryCounterValue(errorCounter)
|
||||
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Listen(events)
|
||||
|
||||
updated := getTelemetryCounterValue(errorCounter)
|
||||
|
@ -736,7 +737,7 @@ func TestInvalidUtf8InDatadogTagValue(t *testing.T) {
|
|||
for _, l := range []statsDPacketHandler{&listener.StatsDUDPListener{
|
||||
Conn: nil,
|
||||
EventHandler: nil,
|
||||
Logger: log.NewNopLogger(),
|
||||
Logger: promslog.NewNopLogger(),
|
||||
LineParser: parser,
|
||||
UDPPackets: udpPackets,
|
||||
UDPPacketDrops: udpPacketDrops,
|
||||
|
@ -749,7 +750,7 @@ func TestInvalidUtf8InDatadogTagValue(t *testing.T) {
|
|||
}, &mockStatsDTCPListener{listener.StatsDTCPListener{
|
||||
Conn: nil,
|
||||
EventHandler: nil,
|
||||
Logger: log.NewNopLogger(),
|
||||
Logger: promslog.NewNopLogger(),
|
||||
LineParser: parser,
|
||||
LinesReceived: linesReceived,
|
||||
EventsFlushed: eventsFlushed,
|
||||
|
@ -760,7 +761,7 @@ func TestInvalidUtf8InDatadogTagValue(t *testing.T) {
|
|||
TCPConnections: tcpConnections,
|
||||
TCPErrors: tcpErrors,
|
||||
TCPLineTooLong: tcpLineTooLong,
|
||||
}, log.NewNopLogger()}} {
|
||||
}, promslog.NewNopLogger()}} {
|
||||
l.SetEventHandler(ueh)
|
||||
l.HandlePacket([]byte("bar:200|c|#tag:value\nbar:200|c|#tag:\xc3\x28invalid"))
|
||||
}
|
||||
|
@ -769,7 +770,7 @@ func TestInvalidUtf8InDatadogTagValue(t *testing.T) {
|
|||
|
||||
testMapper := mapper.MetricMapper{}
|
||||
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Listen(events)
|
||||
}
|
||||
|
||||
|
@ -782,7 +783,7 @@ func TestSummaryWithQuantilesEmptyMapping(t *testing.T) {
|
|||
go func() {
|
||||
testMapper := mapper.MetricMapper{}
|
||||
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Listen(events)
|
||||
}()
|
||||
|
||||
|
@ -825,7 +826,7 @@ func TestHistogramUnits(t *testing.T) {
|
|||
events := make(chan event.Events)
|
||||
go func() {
|
||||
testMapper := mapper.MetricMapper{}
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Mapper.Defaults.ObserverType = mapper.ObserverTypeHistogram
|
||||
ex.Listen(events)
|
||||
}()
|
||||
|
@ -856,12 +857,13 @@ func TestHistogramUnits(t *testing.T) {
|
|||
t.Fatalf("Received unexpected value for histogram observation %f != .300", *value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCounterIncrement(t *testing.T) {
|
||||
// Start exporter with a synchronous channel
|
||||
events := make(chan event.Events)
|
||||
go func() {
|
||||
testMapper := mapper.MetricMapper{}
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Listen(events)
|
||||
}()
|
||||
|
||||
|
@ -908,7 +910,7 @@ func TestGaugeIncrementDecrement(t *testing.T) {
|
|||
events := make(chan event.Events)
|
||||
go func() {
|
||||
testMapper := mapper.MetricMapper{}
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Listen(events)
|
||||
}()
|
||||
|
||||
|
@ -970,7 +972,7 @@ func TestScaledMapping(t *testing.T) {
|
|||
|
||||
// Start exporter with a synchronous channel
|
||||
go func() {
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, &testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Listen(events)
|
||||
}()
|
||||
|
||||
|
@ -1018,7 +1020,7 @@ type statsDPacketHandler interface {
|
|||
|
||||
type mockStatsDTCPListener struct {
|
||||
listener.StatsDTCPListener
|
||||
log.Logger
|
||||
*slog.Logger
|
||||
}
|
||||
|
||||
func (ml *mockStatsDTCPListener) HandlePacket(packet []byte) {
|
||||
|
@ -1079,7 +1081,7 @@ mappings:
|
|||
events := make(chan event.Events)
|
||||
defer close(events)
|
||||
go func() {
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, testMapper, log.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex := NewExporter(prometheus.DefaultRegisterer, testMapper, promslog.NewNopLogger(), eventsActions, eventsUnmapped, errorEventStats, eventStats, conflictingEventStats, metricsCount)
|
||||
ex.Listen(events)
|
||||
}()
|
||||
|
||||
|
@ -1291,7 +1293,7 @@ func BenchmarkParseDogStatsDTags(b *testing.B) {
|
|||
b.Run(name, func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
labels := map[string]string{}
|
||||
parser.ParseDogStatsDTags(tags, labels, tagErrors, log.NewNopLogger())
|
||||
parser.ParseDogStatsDTags(tags, labels, tagErrors, promslog.NewNopLogger())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
// 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.
|
||||
|
||||
package level
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
)
|
||||
|
||||
var logLevel = LevelInfo
|
||||
|
||||
// A Level is a logging priority. Higher levels are more important.
|
||||
type Level int
|
||||
|
||||
const (
|
||||
// LevelDebug logs are typically voluminous, and are usually disabled in
|
||||
// production.
|
||||
LevelDebug Level = iota
|
||||
// LevelInfo is the default logging priority.
|
||||
LevelInfo
|
||||
// LevelWarn logs are more important than Info, but don't need individual
|
||||
// human review.
|
||||
LevelWarn
|
||||
// LevelError logs are high-priority. If an application is running smoothly,
|
||||
// it shouldn't generate any error-level logs.
|
||||
LevelError
|
||||
)
|
||||
|
||||
var emptyLogger = &EmptyLogger{}
|
||||
|
||||
type EmptyLogger struct{}
|
||||
|
||||
func (l *EmptyLogger) Log(keyvals ...interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLogLevel sets the log level.
|
||||
func SetLogLevel(level string) error {
|
||||
switch level {
|
||||
case "debug":
|
||||
logLevel = LevelDebug
|
||||
case "info":
|
||||
logLevel = LevelInfo
|
||||
case "warn":
|
||||
logLevel = LevelWarn
|
||||
case "error":
|
||||
logLevel = LevelError
|
||||
default:
|
||||
return fmt.Errorf("unrecognized log level %s", level)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Error returns a logger that includes a Key/ErrorValue pair.
|
||||
func Error(logger log.Logger) log.Logger {
|
||||
if logLevel <= LevelError {
|
||||
return level.Error(logger)
|
||||
}
|
||||
return emptyLogger
|
||||
}
|
||||
|
||||
// Warn returns a logger that includes a Key/WarnValue pair.
|
||||
func Warn(logger log.Logger) log.Logger {
|
||||
if logLevel <= LevelWarn {
|
||||
return level.Warn(logger)
|
||||
}
|
||||
return emptyLogger
|
||||
}
|
||||
|
||||
// Info returns a logger that includes a Key/InfoValue pair.
|
||||
func Info(logger log.Logger) log.Logger {
|
||||
if logLevel <= LevelInfo {
|
||||
return level.Info(logger)
|
||||
}
|
||||
return emptyLogger
|
||||
}
|
||||
|
||||
// Debug returns a logger that includes a Key/DebugValue pair.
|
||||
func Debug(logger log.Logger) log.Logger {
|
||||
if logLevel <= LevelDebug {
|
||||
return level.Debug(logger)
|
||||
}
|
||||
return emptyLogger
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
// 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.
|
||||
|
||||
package level
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
func TestSetLogLevel(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
level string
|
||||
logLevel Level
|
||||
wantErr bool
|
||||
}{
|
||||
{"wrong level", "foo", LevelInfo, true},
|
||||
{"level debug", "debug", LevelDebug, false},
|
||||
{"level info", "info", LevelInfo, false},
|
||||
{"level warn", "warn", LevelWarn, false},
|
||||
{"level error", "error", LevelError, false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if err := SetLogLevel(tt.level); (err != nil) != tt.wantErr {
|
||||
t.Fatalf("Expected log level to be set successfully, but got %v", err)
|
||||
}
|
||||
if tt.logLevel != logLevel {
|
||||
t.Fatalf("Expected log level %v, but got %v", tt.logLevel, logLevel)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVariousLevels(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
level string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
"level debug",
|
||||
"debug",
|
||||
strings.Join([]string{
|
||||
"level=debug log=debug",
|
||||
"level=info log=info",
|
||||
"level=warn log=warn",
|
||||
"level=error log=error",
|
||||
}, "\n"),
|
||||
},
|
||||
{
|
||||
"level info",
|
||||
"info",
|
||||
strings.Join([]string{
|
||||
"level=info log=info",
|
||||
"level=warn log=warn",
|
||||
"level=error log=error",
|
||||
}, "\n"),
|
||||
},
|
||||
{
|
||||
"level warn",
|
||||
"warn",
|
||||
strings.Join([]string{
|
||||
"level=warn log=warn",
|
||||
"level=error log=error",
|
||||
}, "\n"),
|
||||
},
|
||||
{
|
||||
"level error",
|
||||
"error",
|
||||
strings.Join([]string{
|
||||
"level=error log=error",
|
||||
}, "\n"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
logger := log.NewLogfmtLogger(&buf)
|
||||
|
||||
if err := SetLogLevel(tt.level); err != nil {
|
||||
t.Fatalf("Expected log level to be set successfully, but got %v", err)
|
||||
}
|
||||
|
||||
Debug(logger).Log("log", "debug")
|
||||
Info(logger).Log("log", "info")
|
||||
Warn(logger).Log("log", "warn")
|
||||
Error(logger).Log("log", "error")
|
||||
|
||||
got := strings.TrimSpace(buf.String())
|
||||
if tt.want != got {
|
||||
t.Fatalf("Expected log output %v, but got %v", tt.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -15,15 +15,14 @@ package line
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/event"
|
||||
"github.com/prometheus/statsd_exporter/pkg/level"
|
||||
"github.com/prometheus/statsd_exporter/pkg/mapper"
|
||||
)
|
||||
|
||||
|
@ -95,11 +94,11 @@ func buildEvent(statType, metric string, value float64, relative bool, labels ma
|
|||
}
|
||||
}
|
||||
|
||||
func parseTag(component, tag string, separator rune, labels map[string]string, tagErrors prometheus.Counter, logger log.Logger) {
|
||||
func parseTag(component, tag string, separator rune, labels map[string]string, tagErrors prometheus.Counter, logger *slog.Logger) {
|
||||
// Entirely empty tag is an error
|
||||
if len(tag) == 0 {
|
||||
tagErrors.Inc()
|
||||
level.Debug(logger).Log("msg", "Empty name tag", "component", component)
|
||||
logger.Debug("Empty name tag", "component", component)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -111,7 +110,7 @@ func parseTag(component, tag string, separator rune, labels map[string]string, t
|
|||
if len(k) == 0 || len(v) == 0 {
|
||||
// Empty key or value is an error
|
||||
tagErrors.Inc()
|
||||
level.Debug(logger).Log("msg", "Malformed name tag", "k", k, "v", v, "component", component)
|
||||
logger.Debug("Malformed name tag", "k", k, "v", v, "component", component)
|
||||
} else {
|
||||
labels[mapper.EscapeMetricName(k)] = v
|
||||
}
|
||||
|
@ -121,10 +120,10 @@ func parseTag(component, tag string, separator rune, labels map[string]string, t
|
|||
|
||||
// Missing separator (no value) is an error
|
||||
tagErrors.Inc()
|
||||
level.Debug(logger).Log("msg", "Malformed name tag", "tag", tag, "component", component)
|
||||
logger.Debug("Malformed name tag", "tag", tag, "component", component)
|
||||
}
|
||||
|
||||
func parseNameTags(component string, labels map[string]string, tagErrors prometheus.Counter, logger log.Logger) {
|
||||
func parseNameTags(component string, labels map[string]string, tagErrors prometheus.Counter, logger *slog.Logger) {
|
||||
lastTagEndIndex := 0
|
||||
for i, c := range component {
|
||||
if c == ',' {
|
||||
|
@ -148,7 +147,7 @@ func trimLeftHash(s string) string {
|
|||
return s
|
||||
}
|
||||
|
||||
func (p *Parser) ParseDogStatsDTags(component string, labels map[string]string, tagErrors prometheus.Counter, logger log.Logger) {
|
||||
func (p *Parser) ParseDogStatsDTags(component string, labels map[string]string, tagErrors prometheus.Counter, logger *slog.Logger) {
|
||||
if p.DogstatsdTagsEnabled {
|
||||
lastTagEndIndex := 0
|
||||
for i, c := range component {
|
||||
|
@ -167,7 +166,7 @@ func (p *Parser) ParseDogStatsDTags(component string, labels map[string]string,
|
|||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseNameAndTags(name string, labels map[string]string, tagErrors prometheus.Counter, logger log.Logger) string {
|
||||
func (p *Parser) parseNameAndTags(name string, labels map[string]string, tagErrors prometheus.Counter, logger *slog.Logger) string {
|
||||
if p.SignalFXTagsEnabled {
|
||||
// check for SignalFx tags first
|
||||
// `[` delimits start of tags by SignalFx
|
||||
|
@ -183,7 +182,7 @@ func (p *Parser) parseNameAndTags(name string, labels map[string]string, tagErro
|
|||
return name[:startIdx] + name[endIdx+1:]
|
||||
case (startIdx != -1) != (endIdx != -1):
|
||||
// only one bracket, return unparsed
|
||||
level.Debug(logger).Log("msg", "invalid SignalFx tags, not parsing", "metric", name)
|
||||
logger.Debug("invalid SignalFx tags, not parsing", "metric", name)
|
||||
tagErrors.Inc()
|
||||
return name
|
||||
}
|
||||
|
@ -202,7 +201,7 @@ func (p *Parser) parseNameAndTags(name string, labels map[string]string, tagErro
|
|||
return name
|
||||
}
|
||||
|
||||
func (p *Parser) LineToEvents(line string, sampleErrors prometheus.CounterVec, samplesReceived prometheus.Counter, tagErrors prometheus.Counter, tagsReceived prometheus.Counter, logger log.Logger) event.Events {
|
||||
func (p *Parser) LineToEvents(line string, sampleErrors prometheus.CounterVec, samplesReceived, tagErrors, tagsReceived prometheus.Counter, logger *slog.Logger) event.Events {
|
||||
events := event.Events{}
|
||||
if line == "" {
|
||||
return events
|
||||
|
@ -211,7 +210,7 @@ func (p *Parser) LineToEvents(line string, sampleErrors prometheus.CounterVec, s
|
|||
elements := strings.SplitN(line, ":", 2)
|
||||
if len(elements) < 2 || len(elements[0]) == 0 || !utf8.ValidString(line) {
|
||||
sampleErrors.WithLabelValues("malformed_line").Inc()
|
||||
level.Debug(logger).Log("msg", "bad line", "line", line)
|
||||
logger.Debug("bad line", "line", line)
|
||||
return events
|
||||
}
|
||||
|
||||
|
@ -223,7 +222,7 @@ func (p *Parser) LineToEvents(line string, sampleErrors prometheus.CounterVec, s
|
|||
|
||||
// don't allow mixed tagging styles
|
||||
sampleErrors.WithLabelValues("mixed_tagging_styles").Inc()
|
||||
level.Debug(logger).Log("msg", "bad line: multiple tagging styles", "line", line)
|
||||
logger.Debug("bad line: multiple tagging styles", "line", line)
|
||||
return events
|
||||
}
|
||||
|
||||
|
@ -231,7 +230,7 @@ func (p *Parser) LineToEvents(line string, sampleErrors prometheus.CounterVec, s
|
|||
lineParts := strings.SplitN(elements[1], "|", 3)
|
||||
if len(lineParts) < 2 {
|
||||
sampleErrors.WithLabelValues("not_enough_parts_after_colon").Inc()
|
||||
level.Debug(logger).Log("msg", "bad line: not enough '|'-delimited parts after first ':'", "line", line)
|
||||
logger.Debug("bad line: not enough '|'-delimited parts after first ':'", "line", line)
|
||||
return events
|
||||
}
|
||||
if strings.Contains(lineParts[0], ":") {
|
||||
|
@ -256,7 +255,7 @@ func (p *Parser) LineToEvents(line string, sampleErrors prometheus.CounterVec, s
|
|||
samples = aggLines
|
||||
} else {
|
||||
sampleErrors.WithLabelValues("invalid_extended_aggregate_type").Inc()
|
||||
level.Debug(logger).Log("msg", "bad line: invalid extended aggregate type", "line", line)
|
||||
logger.Debug("bad line: invalid extended aggregate type", "line", line)
|
||||
return events
|
||||
}
|
||||
} else if usingDogStatsDTags {
|
||||
|
@ -272,19 +271,19 @@ samples:
|
|||
components := strings.Split(sample, "|")
|
||||
if len(components) < 2 || len(components) > 4 {
|
||||
sampleErrors.WithLabelValues("malformed_component").Inc()
|
||||
level.Debug(logger).Log("msg", "bad component", "line", line)
|
||||
logger.Debug("bad component", "line", line)
|
||||
continue
|
||||
}
|
||||
valueStr, statType := components[0], components[1]
|
||||
|
||||
var relative = false
|
||||
relative := false
|
||||
if strings.Index(valueStr, "+") == 0 || strings.Index(valueStr, "-") == 0 {
|
||||
relative = true
|
||||
}
|
||||
|
||||
value, err := strconv.ParseFloat(valueStr, 64)
|
||||
if err != nil {
|
||||
level.Debug(logger).Log("msg", "bad value", "value", valueStr, "line", line)
|
||||
logger.Debug("bad value", "value", valueStr, "line", line)
|
||||
sampleErrors.WithLabelValues("malformed_value").Inc()
|
||||
continue
|
||||
}
|
||||
|
@ -293,7 +292,7 @@ samples:
|
|||
if len(components) >= 3 {
|
||||
for _, component := range components[2:] {
|
||||
if len(component) == 0 {
|
||||
level.Debug(logger).Log("msg", "Empty component", "line", line)
|
||||
logger.Debug("Empty component", "line", line)
|
||||
sampleErrors.WithLabelValues("malformed_component").Inc()
|
||||
continue samples
|
||||
}
|
||||
|
@ -305,7 +304,7 @@ samples:
|
|||
|
||||
samplingFactor, err := strconv.ParseFloat(component[1:], 64)
|
||||
if err != nil {
|
||||
level.Debug(logger).Log("msg", "Invalid sampling factor", "component", component[1:], "line", line)
|
||||
logger.Debug("Invalid sampling factor", "component", component[1:], "line", line)
|
||||
sampleErrors.WithLabelValues("invalid_sample_factor").Inc()
|
||||
}
|
||||
if samplingFactor == 0 {
|
||||
|
@ -322,7 +321,7 @@ samples:
|
|||
case '#':
|
||||
p.ParseDogStatsDTags(component[1:], labels, tagErrors, logger)
|
||||
default:
|
||||
level.Debug(logger).Log("msg", "Invalid sampling factor or tag section", "component", components[2], "line", line)
|
||||
logger.Debug("Invalid sampling factor or tag section", "component", components[2], "line", line)
|
||||
sampleErrors.WithLabelValues("invalid_sample_factor").Inc()
|
||||
continue
|
||||
}
|
||||
|
@ -336,7 +335,7 @@ samples:
|
|||
for i := 0; i < multiplyEvents; i++ {
|
||||
event, err := buildEvent(statType, metric, value, relative, labels)
|
||||
if err != nil {
|
||||
level.Debug(logger).Log("msg", "Error building event", "line", line, "error", err)
|
||||
logger.Debug("Error building event", "line", line, "error", err)
|
||||
sampleErrors.WithLabelValues("illegal_event").Inc()
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/common/promslog"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/event"
|
||||
)
|
||||
|
@ -49,7 +49,7 @@ var (
|
|||
Help: "The number of errors parsing DogStatsD tags.",
|
||||
},
|
||||
)
|
||||
nopLogger = log.NewNopLogger()
|
||||
nopLogger = promslog.NewNopLogger()
|
||||
)
|
||||
|
||||
func TestLineToEvents(t *testing.T) {
|
||||
|
|
|
@ -16,26 +16,25 @@ package listener
|
|||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/event"
|
||||
"github.com/prometheus/statsd_exporter/pkg/level"
|
||||
"github.com/prometheus/statsd_exporter/pkg/relay"
|
||||
)
|
||||
|
||||
type Parser interface {
|
||||
LineToEvents(line string, sampleErrors prometheus.CounterVec, samplesReceived prometheus.Counter, tagErrors prometheus.Counter, tagsReceived prometheus.Counter, logger log.Logger) event.Events
|
||||
LineToEvents(line string, sampleErrors prometheus.CounterVec, samplesReceived, tagErrors, tagsReceived prometheus.Counter, logger *slog.Logger) event.Events
|
||||
}
|
||||
|
||||
type StatsDUDPListener struct {
|
||||
Conn *net.UDPConn
|
||||
EventHandler event.EventHandler
|
||||
Logger log.Logger
|
||||
Logger *slog.Logger
|
||||
LineParser Parser
|
||||
UDPPackets prometheus.Counter
|
||||
UDPPacketDrops prometheus.Counter
|
||||
|
@ -64,7 +63,7 @@ func (l *StatsDUDPListener) Listen() {
|
|||
if strings.HasSuffix(err.Error(), "use of closed network connection") {
|
||||
return
|
||||
}
|
||||
level.Error(l.Logger).Log("error", err)
|
||||
l.Logger.Error("error reading from UDP connection", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -94,7 +93,7 @@ func (l *StatsDUDPListener) ProcessUdpPacketQueue() {
|
|||
func (l *StatsDUDPListener) HandlePacket(packet []byte) {
|
||||
lines := strings.Split(string(packet), "\n")
|
||||
for _, line := range lines {
|
||||
level.Debug(l.Logger).Log("msg", "Incoming line", "proto", "udp", "line", line)
|
||||
l.Logger.Debug("Incoming line", "proto", "udp", "line", line)
|
||||
l.LinesReceived.Inc()
|
||||
if l.Relay != nil && len(line) > 0 {
|
||||
l.Relay.RelayLine(line)
|
||||
|
@ -106,7 +105,7 @@ func (l *StatsDUDPListener) HandlePacket(packet []byte) {
|
|||
type StatsDTCPListener struct {
|
||||
Conn *net.TCPListener
|
||||
EventHandler event.EventHandler
|
||||
Logger log.Logger
|
||||
Logger *slog.Logger
|
||||
LineParser Parser
|
||||
LinesReceived prometheus.Counter
|
||||
EventsFlushed prometheus.Counter
|
||||
|
@ -133,7 +132,7 @@ func (l *StatsDTCPListener) Listen() {
|
|||
if strings.HasSuffix(err.Error(), "use of closed network connection") {
|
||||
return
|
||||
}
|
||||
level.Error(l.Logger).Log("msg", "AcceptTCP failed", "error", err)
|
||||
l.Logger.Error("AcceptTCP failed", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
go l.HandleConn(c)
|
||||
|
@ -151,14 +150,14 @@ func (l *StatsDTCPListener) HandleConn(c *net.TCPConn) {
|
|||
if err != nil {
|
||||
if err != io.EOF {
|
||||
l.TCPErrors.Inc()
|
||||
level.Debug(l.Logger).Log("msg", "Read failed", "addr", c.RemoteAddr(), "error", err)
|
||||
l.Logger.Debug("Read failed", "addr", c.RemoteAddr(), "error", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
level.Debug(l.Logger).Log("msg", "Incoming line", "proto", "tcp", "line", string(line))
|
||||
l.Logger.Debug("Incoming line", "proto", "tcp", "line", string(line))
|
||||
if isPrefix {
|
||||
l.TCPLineTooLong.Inc()
|
||||
level.Debug(l.Logger).Log("msg", "Read failed: line too long", "addr", c.RemoteAddr())
|
||||
l.Logger.Debug("Read failed: line too long", "addr", c.RemoteAddr())
|
||||
break
|
||||
}
|
||||
l.LinesReceived.Inc()
|
||||
|
@ -172,7 +171,7 @@ func (l *StatsDTCPListener) HandleConn(c *net.TCPConn) {
|
|||
type StatsDUnixgramListener struct {
|
||||
Conn *net.UnixConn
|
||||
EventHandler event.EventHandler
|
||||
Logger log.Logger
|
||||
Logger *slog.Logger
|
||||
LineParser Parser
|
||||
UnixgramPackets prometheus.Counter
|
||||
LinesReceived prometheus.Counter
|
||||
|
@ -198,7 +197,7 @@ func (l *StatsDUnixgramListener) Listen() {
|
|||
if strings.HasSuffix(err.Error(), "use of closed network connection") {
|
||||
return
|
||||
}
|
||||
level.Error(l.Logger).Log(err)
|
||||
l.Logger.Error("error reading from unixgram connection", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
l.HandlePacket(buf[:n])
|
||||
|
@ -209,7 +208,7 @@ func (l *StatsDUnixgramListener) HandlePacket(packet []byte) {
|
|||
l.UnixgramPackets.Inc()
|
||||
lines := strings.Split(string(packet), "\n")
|
||||
for _, line := range lines {
|
||||
level.Debug(l.Logger).Log("msg", "Incoming line", "proto", "unixgram", "line", line)
|
||||
l.Logger.Debug("Incoming line", "proto", "unixgram", "line", line)
|
||||
l.LinesReceived.Inc()
|
||||
if l.Relay != nil && len(line) > 0 {
|
||||
l.Relay.RelayLine(line)
|
||||
|
|
|
@ -31,18 +31,18 @@ func (f *FSM) DumpFSM(w io.Writer) {
|
|||
for idx < len(states) {
|
||||
for field, transition := range states[idx].transitions {
|
||||
states[len(states)] = transition
|
||||
w.Write([]byte(fmt.Sprintf("%d -> %d [label = \"%s\"];\n", idx, len(states)-1, field)))
|
||||
fmt.Fprintf(w, "%d -> %d [label = \"%s\"];\n", idx, len(states)-1, field)
|
||||
if idx == 0 {
|
||||
// color for metric types
|
||||
w.Write([]byte(fmt.Sprintf("%d [color=\"#D6B656\",fillcolor=\"#FFF2CC\"];\n", len(states)-1)))
|
||||
fmt.Fprintf(w, "%d [color=\"#D6B656\",fillcolor=\"#FFF2CC\"];\n", len(states)-1)
|
||||
} else if len(transition.transitions) == 0 {
|
||||
// color for end state
|
||||
w.Write([]byte(fmt.Sprintf("%d [color=\"#82B366\",fillcolor=\"#D5E8D4\"];\n", len(states)-1)))
|
||||
fmt.Fprintf(w, "%d [color=\"#82B366\",fillcolor=\"#D5E8D4\"];\n", len(states)-1)
|
||||
}
|
||||
}
|
||||
idx++
|
||||
}
|
||||
// color for start state
|
||||
w.Write([]byte(fmt.Sprintln("0 [color=\"#a94442\",fillcolor=\"#f2dede\"];")))
|
||||
fmt.Fprintln(w, "0 [color=\"#a94442\",fillcolor=\"#f2dede\"];")
|
||||
w.Write([]byte("}"))
|
||||
}
|
||||
|
|
|
@ -20,9 +20,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
templateReplaceCaptureRE = regexp.MustCompile(`\$\{?([a-zA-Z0-9_\$]+)\}?`)
|
||||
)
|
||||
var templateReplaceCaptureRE = regexp.MustCompile(`\$\{?([a-zA-Z0-9_\$]+)\}?`)
|
||||
|
||||
type TemplateFormatter struct {
|
||||
captureIndexes []int
|
||||
|
@ -46,9 +44,9 @@ func NewTemplateFormatter(template string, captureCount int) *TemplateFormatter
|
|||
if err != nil || idx > captureCount || idx < 1 {
|
||||
// if index larger than captured count or using unsupported named capture group,
|
||||
// replace with empty string
|
||||
valueFormatter = strings.Replace(valueFormatter, match[0], "", -1)
|
||||
valueFormatter = strings.ReplaceAll(valueFormatter, match[0], "")
|
||||
} else {
|
||||
valueFormatter = strings.Replace(valueFormatter, match[0], "%s", -1)
|
||||
valueFormatter = strings.ReplaceAll(valueFormatter, match[0], "%s")
|
||||
// note: the regex reference variable $? starts from 1
|
||||
indexes = append(indexes, idx-1)
|
||||
}
|
||||
|
|
|
@ -14,12 +14,9 @@
|
|||
package fsm
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/level"
|
||||
)
|
||||
|
||||
type mappingState struct {
|
||||
|
@ -69,7 +66,7 @@ func NewFSM(metricTypes []string, maxPossibleTransitions int, orderingDisabled b
|
|||
// AddState adds a mapping rule into the existing FSM.
|
||||
// The maxPossibleTransitions parameter sets the expected count of transitions left.
|
||||
// The result parameter sets the generic type to be returned when fsm found a match in GetMapping.
|
||||
func (f *FSM) AddState(match string, matchMetricType string, maxPossibleTransitions int, result interface{}) int {
|
||||
func (f *FSM) AddState(match, matchMetricType string, maxPossibleTransitions int, result interface{}) int {
|
||||
// first split by "."
|
||||
matchFields := strings.Split(match, ".")
|
||||
// fill into our FSM
|
||||
|
@ -128,7 +125,7 @@ func (f *FSM) AddState(match string, matchMetricType string, maxPossibleTransiti
|
|||
// GetMapping using the fsm to find matching rules according to given statsdMetric and statsdMetricType.
|
||||
// If it finds a match, the final state and the captured strings are returned;
|
||||
// if there's no match found, nil and a empty list will be returned.
|
||||
func (f *FSM) GetMapping(statsdMetric string, statsdMetricType string) (*mappingState, []string) {
|
||||
func (f *FSM) GetMapping(statsdMetric, statsdMetricType string) (*mappingState, []string) {
|
||||
matchFields := strings.Split(statsdMetric, ".")
|
||||
currentState := f.root.transitions[statsdMetricType]
|
||||
|
||||
|
@ -171,7 +168,8 @@ func (f *FSM) GetMapping(statsdMetric string, statsdMetricType string) (*mapping
|
|||
if !present || fieldsLeft > altState.maxRemainingLength || fieldsLeft < altState.minRemainingLength {
|
||||
} else {
|
||||
// push to backtracking stack
|
||||
newCursor := fsmBacktrackStackCursor{prev: backtrackCursor, state: altState,
|
||||
newCursor := fsmBacktrackStackCursor{
|
||||
prev: backtrackCursor, state: altState,
|
||||
fieldIndex: i,
|
||||
captureIndex: captureIdx, currentCapture: field,
|
||||
}
|
||||
|
@ -234,7 +232,7 @@ func (f *FSM) GetMapping(statsdMetric string, statsdMetricType string) (*mapping
|
|||
|
||||
// TestIfNeedBacktracking tests if backtrack is needed for given list of mappings
|
||||
// and whether ordering is disabled.
|
||||
func TestIfNeedBacktracking(mappings []string, orderingDisabled bool, logger log.Logger) bool {
|
||||
func TestIfNeedBacktracking(mappings []string, orderingDisabled bool, logger *slog.Logger) bool {
|
||||
backtrackingNeeded := false
|
||||
// A has * in rules, but there's other transisitions at the same state,
|
||||
// this makes A the cause of backtracking
|
||||
|
@ -246,11 +244,11 @@ func TestIfNeedBacktracking(mappings []string, orderingDisabled bool, logger log
|
|||
l := len(strings.Split(mapping, "."))
|
||||
ruleByLength[l] = append(ruleByLength[l], mapping)
|
||||
|
||||
metricRe := strings.Replace(mapping, ".", "\\.", -1)
|
||||
metricRe = strings.Replace(metricRe, "*", "([^.]*)", -1)
|
||||
metricRe := strings.ReplaceAll(mapping, ".", "\\.")
|
||||
metricRe = strings.ReplaceAll(metricRe, "*", "([^.]*)")
|
||||
regex, err := regexp.Compile("^" + metricRe + "$")
|
||||
if err != nil {
|
||||
level.Warn(logger).Log("msg", "Invalid match, cannot compile regex in mapping", "mapping", mapping, "err", err)
|
||||
logger.Warn("Invalid match, cannot compile regex in mapping", "mapping", mapping, "err", err)
|
||||
}
|
||||
// put into array no matter there's error or not, we will skip later if regex is nil
|
||||
ruleREByLength[l] = append(ruleREByLength[l], regex)
|
||||
|
@ -275,8 +273,8 @@ func TestIfNeedBacktracking(mappings []string, orderingDisabled bool, logger log
|
|||
}
|
||||
// translate the substring of r1 from 0 to the index of current * into regex
|
||||
// A.B.C.*.E.* will becomes ^A\.B\.C\. and ^A\.B\.C\.\*\.E\.
|
||||
reStr := strings.Replace(r1[:index], ".", "\\.", -1)
|
||||
reStr = strings.Replace(reStr, "*", "\\*", -1)
|
||||
reStr := strings.ReplaceAll(r1[:index], ".", "\\.")
|
||||
reStr = strings.ReplaceAll(reStr, "*", "\\*")
|
||||
re := regexp.MustCompile("^" + reStr)
|
||||
for i2, r2 := range rules {
|
||||
if i2 == i1 {
|
||||
|
@ -293,7 +291,7 @@ func TestIfNeedBacktracking(mappings []string, orderingDisabled bool, logger log
|
|||
if i2 != i1 && len(re1.FindStringSubmatchIndex(r2)) > 0 {
|
||||
// log if we care about ordering and the superset occurs before
|
||||
if !orderingDisabled && i1 < i2 {
|
||||
level.Warn(logger).Log("msg", "match is a super set of match but in a lower order, the first will never be matched", "first_match", r1, "second_match", r2)
|
||||
logger.Warn("match is a super set of match but in a lower order, the first will never be matched", "first_match", r1, "second_match", r2)
|
||||
}
|
||||
currentRuleNeedBacktrack = false
|
||||
}
|
||||
|
@ -311,7 +309,7 @@ func TestIfNeedBacktracking(mappings []string, orderingDisabled bool, logger log
|
|||
}
|
||||
|
||||
if currentRuleNeedBacktrack {
|
||||
level.Warn(logger).Log("msg", "backtracking required because of match. Performance may be degraded", "match", r1)
|
||||
logger.Warn("backtracking required because of match. Performance may be degraded", "match", r1)
|
||||
backtrackingNeeded = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,16 +15,16 @@ package mapper
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"regexp"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/common/promslog"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/level"
|
||||
"github.com/prometheus/statsd_exporter/pkg/mapper/fsm"
|
||||
)
|
||||
|
||||
|
@ -53,7 +53,7 @@ type MetricMapper struct {
|
|||
|
||||
MappingsCount prometheus.Gauge
|
||||
|
||||
Logger log.Logger
|
||||
Logger *slog.Logger
|
||||
}
|
||||
|
||||
type SummaryOptions struct {
|
||||
|
@ -174,12 +174,12 @@ func (m *MetricMapper) InitFromYAMLString(fileContents string) error {
|
|||
|
||||
if currentMapping.LegacyQuantiles != nil &&
|
||||
(currentMapping.SummaryOptions == nil || currentMapping.SummaryOptions.Quantiles != nil) {
|
||||
level.Warn(m.Logger).Log("msg", "using the top level quantiles is deprecated. Please use quantiles in the summary_options hierarchy")
|
||||
m.Logger.Warn("using the top level quantiles is deprecated. Please use quantiles in the summary_options hierarchy")
|
||||
}
|
||||
|
||||
if currentMapping.LegacyBuckets != nil &&
|
||||
(currentMapping.HistogramOptions == nil || currentMapping.HistogramOptions.Buckets != nil) {
|
||||
level.Warn(m.Logger).Log("msg", "using the top level buckets is deprecated. Please use buckets in the histogram_options hierarchy")
|
||||
m.Logger.Warn("using the top level buckets is deprecated. Please use buckets in the histogram_options hierarchy")
|
||||
}
|
||||
|
||||
if currentMapping.SummaryOptions != nil &&
|
||||
|
@ -242,7 +242,7 @@ func (m *MetricMapper) InitFromYAMLString(fileContents string) error {
|
|||
defer m.mutex.Unlock()
|
||||
|
||||
if m.Logger == nil {
|
||||
m.Logger = log.NewNopLogger()
|
||||
m.Logger = promslog.NewNopLogger()
|
||||
}
|
||||
|
||||
m.Defaults = n.Defaults
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus/common/promslog"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/mappercache/lru"
|
||||
"github.com/prometheus/statsd_exporter/pkg/mappercache/randomreplacement"
|
||||
|
@ -245,7 +245,7 @@ mappings:
|
|||
}
|
||||
|
||||
mapper := MetricMapper{
|
||||
Logger: log.NewNopLogger(),
|
||||
Logger: promslog.NewNopLogger(),
|
||||
}
|
||||
err := mapper.InitFromYAMLString(config)
|
||||
if err != nil {
|
||||
|
|
|
@ -216,7 +216,7 @@ mappings:
|
|||
},
|
||||
},
|
||||
},
|
||||
//Config with backtracking, the non-matched rule has star(s)
|
||||
// Config with backtracking, the non-matched rule has star(s)
|
||||
// A metric like full.name.anothertest will first match full.name.* and then tries
|
||||
// to match *.dummy.* and then failed.
|
||||
// This test case makes sure the captures in the non-matched later rule
|
||||
|
|
|
@ -16,24 +16,22 @@ package relay
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/clock"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prometheus/statsd_exporter/pkg/level"
|
||||
)
|
||||
|
||||
type Relay struct {
|
||||
addr *net.UDPAddr
|
||||
bufferChannel chan []byte
|
||||
conn *net.UDPConn
|
||||
logger log.Logger
|
||||
logger *slog.Logger
|
||||
packetLength uint
|
||||
|
||||
packetsTotal prometheus.Counter
|
||||
|
@ -67,7 +65,7 @@ var (
|
|||
|
||||
// NewRelay creates a statsd UDP relay. It can be used to send copies of statsd raw
|
||||
// lines to a separate service.
|
||||
func NewRelay(l log.Logger, target string, packetLength uint) (*Relay, error) {
|
||||
func NewRelay(l *slog.Logger, target string, packetLength uint) (*Relay, error) {
|
||||
addr, err := net.ResolveUDPAddr("udp", target)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to resolve target %s, err: %w", target, err)
|
||||
|
@ -110,24 +108,24 @@ func (r *Relay) relayOutput() {
|
|||
case <-relayInterval.C:
|
||||
err = r.sendPacket(buffer.Bytes())
|
||||
if err != nil {
|
||||
level.Error(r.logger).Log("msg", "Error sending UDP packet", "error", err)
|
||||
r.logger.Error("Error sending UDP packet", "error", err)
|
||||
return
|
||||
}
|
||||
// Clear out the buffer.
|
||||
buffer.Reset()
|
||||
case b := <-r.bufferChannel:
|
||||
if uint(len(b)+buffer.Len()) > r.packetLength {
|
||||
level.Debug(r.logger).Log("msg", "Buffer full, sending packet", "length", buffer.Len())
|
||||
r.logger.Debug("Buffer full, sending packet", "length", buffer.Len())
|
||||
err = r.sendPacket(buffer.Bytes())
|
||||
if err != nil {
|
||||
level.Error(r.logger).Log("msg", "Error sending UDP packet", "error", err)
|
||||
r.logger.Error("Error sending UDP packet", "error", err)
|
||||
return
|
||||
}
|
||||
// Seed the new buffer with the new line.
|
||||
buffer.Reset()
|
||||
buffer.Write(b)
|
||||
} else {
|
||||
level.Debug(r.logger).Log("msg", "Adding line to buffer", "line", string(b))
|
||||
r.logger.Debug("Adding line to buffer", "line", string(b))
|
||||
buffer.Write(b)
|
||||
}
|
||||
}
|
||||
|
@ -137,10 +135,10 @@ func (r *Relay) relayOutput() {
|
|||
// sendPacket sends a single relay line to the destination target.
|
||||
func (r *Relay) sendPacket(buf []byte) error {
|
||||
if len(buf) == 0 {
|
||||
level.Debug(r.logger).Log("msg", "Empty buffer, nothing to send")
|
||||
r.logger.Debug("Empty buffer, nothing to send")
|
||||
return nil
|
||||
}
|
||||
level.Debug(r.logger).Log("msg", "Sending packet", "length", len(buf), "data", string(buf))
|
||||
r.logger.Debug("Sending packet", "length", len(buf), "data", string(buf))
|
||||
_, err := r.conn.WriteToUDP(buf, r.addr)
|
||||
r.packetsTotal.Inc()
|
||||
return err
|
||||
|
@ -150,15 +148,15 @@ func (r *Relay) sendPacket(buf []byte) error {
|
|||
func (r *Relay) RelayLine(l string) {
|
||||
lineLength := uint(len(l))
|
||||
if lineLength == 0 {
|
||||
level.Debug(r.logger).Log("msg", "Empty line, not relaying")
|
||||
r.logger.Debug("Empty line, not relaying")
|
||||
return
|
||||
}
|
||||
if lineLength > r.packetLength-1 {
|
||||
level.Warn(r.logger).Log("msg", "line too long, not relaying", "length", lineLength, "max", r.packetLength)
|
||||
r.logger.Warn("line too long, not relaying", "length", lineLength, "max", r.packetLength)
|
||||
r.longLinesTotal.Inc()
|
||||
return
|
||||
}
|
||||
level.Debug(r.logger).Log("msg", "Relaying line", "line", string(l))
|
||||
r.logger.Debug("Relaying line", "line", string(l))
|
||||
if !strings.HasSuffix(l, "\n") {
|
||||
l = l + "\n"
|
||||
}
|
||||
|
|
|
@ -19,11 +19,12 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/prometheus/statsd_exporter/pkg/clock"
|
||||
"github.com/prometheus/common/promslog"
|
||||
"github.com/stvp/go-udp-testing"
|
||||
|
||||
"github.com/prometheus/statsd_exporter/pkg/clock"
|
||||
)
|
||||
|
||||
func TestRelay_RelayLine(t *testing.T) {
|
||||
|
@ -54,13 +55,12 @@ func TestRelay_RelayLine(t *testing.T) {
|
|||
}
|
||||
clock.ClockInstance.Instant = time.Unix(0, 0)
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
logger := promslog.NewNopLogger()
|
||||
r, err := NewRelay(
|
||||
logger,
|
||||
"localhost:1160",
|
||||
200,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Did not expect error while creating relay.")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue