pax_global_header00006660000000000000000000000064145614654470014532gustar00rootroot0000000000000052 comment=cd0bbe2da091463a7d87d1dd2e5bf07a71fe5ff9 golang-github-uber-go-tally-4.1.11/000077500000000000000000000000001456146544700170265ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/.github/000077500000000000000000000000001456146544700203665ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/.github/workflows/000077500000000000000000000000001456146544700224235ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/.github/workflows/test.yml000066400000000000000000000016711456146544700241320ustar00rootroot00000000000000name: Test on: push: branches: ['*'] tags: ['v*'] pull_request: branches: ['*'] jobs: test: runs-on: ubuntu-latest strategy: matrix: go: ["1.17.x", "1.18.x"] include: - go: 1.18.x latest: true steps: - name: Setup Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - name: Checkout code uses: actions/checkout@v2 - name: Load cached dependencies uses: actions/cache@v1 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - name: Download Dependencies run: | go mod download (cd tools && go mod download) - name: Lint if: matrix.latest run: make lint - name: Test run: make cover - name: Upload coverage to codecov.io uses: codecov/codecov-action@v1 golang-github-uber-go-tally-4.1.11/.gitignore000066400000000000000000000005241456146544700210170ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test vendor bin # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof *.pprof *.out *.log .DS_Store node_modules/ .idea/ cover.html golang-github-uber-go-tally-4.1.11/.travis.yml000066400000000000000000000003511456146544700211360ustar00rootroot00000000000000language: go sudo: false go: - 1.18.x - 1.19.x - 1.20.x - 1.21.x cache: directories: - vendor install: - npm i uber-licence - make dependencies script: - make test - make lint after_success: - make coveralls golang-github-uber-go-tally-4.1.11/LICENSE000066400000000000000000000020411456146544700200300ustar00rootroot00000000000000MIT License Copyright (c) 2016 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-uber-go-tally-4.1.11/Makefile000066400000000000000000000053301456146544700204670ustar00rootroot00000000000000# "go install"-ed binaries will be placed here during development. export GOBIN ?= $(shell pwd)/bin BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem GO_FILES = $(shell find . \ '(' -path '*/.*' -o -path './thirdparty/*' -prune ')' -o \ '(' -type f -a -name '*.go' ')' -print) MODULES = . ./tools LINT_IGNORE = m3/thrift\|thirdparty LICENSE_IGNORE = m3/thrift\|thirdparty STATICCHECK_IGNORE = m3/thrift\|thirdparty\|m3/resource_pool.go:.*releaseProto is unused\|m3/reporter.go:.* argument should be pointer-like to avoid allocations GOLINT = $(GOBIN)/golint STATICCHECK = $(GOBIN)/staticcheck .PHONY: all all: lint test .PHONY: lint lint: gofmt golint gomodtidy staticcheck license .PHONY: golint golint: $(GOLINT) @echo "Checking lint..." @$(eval LOG := $(shell mktemp -t log.XXXXX)) @$(GOLINT) ./... | grep -v '$(LINT_IGNORE)' > $(LOG) || true @[ ! -s "$(LOG)" ] || \ (echo "golint failed:" | \ cat - $(LOG) && false) $(GOLINT): tools/go.mod cd tools && go install golang.org/x/lint/golint .PHONY: staticcheck staticcheck: $(STATICCHECK) @echo "Checking staticcheck..." @$(eval LOG := $(shell mktemp -t log.XXXXX)) @$(STATICCHECK) ./... | grep -v '$(STATICCHECK_IGNORE)' > $(LOG) || true @[ ! -s "$(LOG)" ] || \ (echo "staticcheck failed:" | \ cat - $(LOG) && false) $(STATICCHECK): cd tools && go install honnef.co/go/tools/cmd/staticcheck .PHONY: gofmt gofmt: @echo "Checking formatting..." $(eval LOG := $(shell mktemp -t log.XXXXX)) @gofmt -e -s -l $(GO_FILES) | grep -v '$(LINT_IGNORE)' > $(LOG) || true @[ ! -s "$(LOG)" ] || \ (echo "gofmt failed. Please reformat the following files:" | \ cat - $(LOG) && false) .PHONY: gomodtidy gomodtidy: go.mod go.sum @echo "Checking go.mod and go.sum..." @$(foreach mod,$(MODULES),\ (cd $(mod) && go mod tidy) &&) true @if ! git diff --quiet $^; then \ echo "go mod tidy changed files:" && \ git status --porcelain $^ && \ false; \ fi .PHONY: license license: check_license.sh @echo "Checking for license headers..." $(eval LOG := $(shell mktemp -t log.XXXXX)) @./check_license.sh | grep -v '$(LICENSE_IGNORE)' > $(LOG) || true @[ ! -s "$(LOG)" ] || \ (echo "Missing license headers in some files:" | \ cat - $(LOG) && false) .PHONY: test test: go test -race -v ./... .PHONY: examples examples: mkdir -p ./bin go build -o ./bin/print_example ./example/ go build -o ./bin/m3_example ./m3/example/ go build -o ./bin/prometheus_example ./prometheus/example/ go build -o ./bin/statsd_example ./statsd/example/ .PHONY: cover cover: go test -cover -coverprofile=cover.out -coverpkg=./... -race -v ./... go tool cover -html=cover.out -o cover.html .PHONY: bench BENCH ?= . bench: go test -bench=$(BENCH) -run="^$$" $(BENCH_FLAGS) ./... golang-github-uber-go-tally-4.1.11/README.md000066400000000000000000000160561456146544700203150ustar00rootroot00000000000000# :heavy_check_mark: tally [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] Fast, buffered, hierarchical stats collection in Go. ## Installation `go get -u github.com/uber-go/tally` ## Abstract Tally provides a common interface for emitting metrics, while letting you not worry about the velocity of metrics emission. By default it buffers counters, gauges and histograms at a specified interval but does not buffer timer values. This is primarily so timer values can have all their values sampled if desired and if not they can be sampled as summaries or histograms independently by a reporter. ## Structure - Scope: Keeps track of metrics, and their common metadata. - Metrics: Counters, Gauges, Timers and Histograms. - Reporter: Implemented by you. Accepts aggregated values from the scope. Forwards the aggregated values to your metrics ingestion pipeline. - The reporters already available listed alphabetically are: - `github.com/uber-go/tally/m3`: Report m3 metrics, timers are not sampled and forwarded directly. - `github.com/uber-go/tally/multi`: Report to multiple reporters, you can multi-write metrics to other reporters simply. - `github.com/uber-go/tally/prometheus`: Report prometheus metrics, timers by default are made summaries with an option to make them histograms instead. - `github.com/uber-go/tally/statsd`: Report statsd metrics, no support for tags. ### Basics - Scopes created with tally provide race-safe registration and use of all metric types `Counter`, `Gauge`, `Timer`, `Histogram`. - `NewRootScope(...)` returns a `Scope` and `io.Closer`, the second return value is used to stop the scope's goroutine reporting values from the scope to it's reporter. This is to reduce the footprint of `Scope` from the public API for those implementing it themselves to use in Go packages that take a tally `Scope`. ### Acquire a Scope ### ```go reporter = NewMyStatsReporter() // Implement as you will tags := map[string]string{ "dc": "east-1", "type": "master", } reportEvery := time.Second scope := tally.NewRootScope(tally.ScopeOptions{ Tags: tags, Reporter: reporter, }, reportEvery) ``` ### Get/Create a metric, use it ### ```go // Get a counter, increment a counter reqCounter := scope.Counter("requests") // cache me reqCounter.Inc(1) queueGauge := scope.Gauge("queue_length") // cache me queueGauge.Update(42) ``` ### Report your metrics ### Use the inbuilt statsd reporter: ```go import ( "io" "github.com/cactus/go-statsd-client/v5/statsd" "github.com/uber-go/tally" tallystatsd "github.com/uber-go/tally/statsd" // ... ) func newScope() (tally.Scope, io.Closer) { statter, _ := statsd.NewBufferedClient("127.0.0.1:8125", "stats", 100*time.Millisecond, 1440) reporter := tallystatsd.NewReporter(statter, tallystatsd.Options{ SampleRate: 1.0, }) scope, closer := tally.NewRootScope(tally.ScopeOptions{ Prefix: "my-service", Tags: map[string]string{}, Reporter: reporter, }, time.Second) return scope, closer } ``` Implement your own reporter using the `StatsReporter` interface: ```go // BaseStatsReporter implements the shared reporter methods. type BaseStatsReporter interface { Capabilities() Capabilities Flush() } // StatsReporter is a backend for Scopes to report metrics to. type StatsReporter interface { BaseStatsReporter // ReportCounter reports a counter value ReportCounter( name string, tags map[string]string, value int64, ) // ReportGauge reports a gauge value ReportGauge( name string, tags map[string]string, value float64, ) // ReportTimer reports a timer value ReportTimer( name string, tags map[string]string, interval time.Duration, ) // ReportHistogramValueSamples reports histogram samples for a bucket ReportHistogramValueSamples( name string, tags map[string]string, buckets Buckets, bucketLowerBound, bucketUpperBound float64, samples int64, ) // ReportHistogramDurationSamples reports histogram samples for a bucket ReportHistogramDurationSamples( name string, tags map[string]string, buckets Buckets, bucketLowerBound, bucketUpperBound time.Duration, samples int64, ) } ``` Or implement your own metrics implementation that matches the tally `Scope` interface to use different buffering semantics: ```go type Scope interface { // Counter returns the Counter object corresponding to the name. Counter(name string) Counter // Gauge returns the Gauge object corresponding to the name. Gauge(name string) Gauge // Timer returns the Timer object corresponding to the name. Timer(name string) Timer // Histogram returns the Histogram object corresponding to the name. // To use default value and duration buckets configured for the scope // simply pass tally.DefaultBuckets or nil. // You can use tally.ValueBuckets{x, y, ...} for value buckets. // You can use tally.DurationBuckets{x, y, ...} for duration buckets. // You can use tally.MustMakeLinearValueBuckets(start, width, count) for linear values. // You can use tally.MustMakeLinearDurationBuckets(start, width, count) for linear durations. // You can use tally.MustMakeExponentialValueBuckets(start, factor, count) for exponential values. // You can use tally.MustMakeExponentialDurationBuckets(start, factor, count) for exponential durations. Histogram(name string, buckets Buckets) Histogram // Tagged returns a new child scope with the given tags and current tags. Tagged(tags map[string]string) Scope // SubScope returns a new child scope appending a further name prefix. SubScope(name string) Scope // Capabilities returns a description of metrics reporting capabilities. Capabilities() Capabilities } // Capabilities is a description of metrics reporting capabilities. type Capabilities interface { // Reporting returns whether the reporter has the ability to actively report. Reporting() bool // Tagging returns whether the reporter has the capability for tagged metrics. Tagging() bool } ``` ## Performance This stuff needs to be fast. With that in mind, we avoid locks and unnecessary memory allocations. ``` BenchmarkCounterInc-8 200000000 7.68 ns/op BenchmarkReportCounterNoData-8 300000000 4.88 ns/op BenchmarkReportCounterWithData-8 100000000 21.6 ns/op BenchmarkGaugeSet-8 100000000 16.0 ns/op BenchmarkReportGaugeNoData-8 100000000 10.4 ns/op BenchmarkReportGaugeWithData-8 50000000 27.6 ns/op BenchmarkTimerInterval-8 50000000 37.7 ns/op BenchmarkTimerReport-8 300000000 5.69 ns/op ```
Released under the [MIT License](LICENSE). [doc-img]: https://godoc.org/github.com/uber-go/tally?status.svg [doc]: https://godoc.org/github.com/uber-go/tally [ci-img]: https://travis-ci.org/uber-go/tally.svg?branch=master [ci]: https://travis-ci.org/uber-go/tally [cov-img]: https://coveralls.io/repos/github/uber-go/tally/badge.svg?branch=master [cov]: https://coveralls.io/github/uber-go/tally?branch=master [glide.lock]: https://github.com/uber-go/tally/blob/master/glide.lock [v1]: https://github.com/uber-go/tally/milestones golang-github-uber-go-tally-4.1.11/check_license.sh000077500000000000000000000004441456146544700221460ustar00rootroot00000000000000#!/bin/bash -e ERROR_COUNT=0 while read -r file do case "$(head -1 "${file}")" in *"Copyright (c) "*" Uber Technologies, Inc.") # everything's cool ;; *) echo "$file:missing license header." (( ERROR_COUNT++ )) ;; esac done < <(git ls-files "*\.go") exit "$ERROR_COUNT" golang-github-uber-go-tally-4.1.11/example/000077500000000000000000000000001456146544700204615ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/example/README.md000066400000000000000000000000521456146544700217350ustar00rootroot00000000000000# Print reporter example `go run ./*.go` golang-github-uber-go-tally-4.1.11/example/main.go000066400000000000000000000065061456146544700217430ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package main import ( "fmt" "time" tally "github.com/uber-go/tally/v4" ) type printStatsReporter struct{} func newPrintStatsReporter() tally.StatsReporter { return &printStatsReporter{} } func (r *printStatsReporter) ReportCounter(name string, _ map[string]string, value int64) { fmt.Printf("count %s %d\n", name, value) } func (r *printStatsReporter) ReportGauge(name string, _ map[string]string, value float64) { fmt.Printf("gauge %s %f\n", name, value) } func (r *printStatsReporter) ReportTimer(name string, _ map[string]string, interval time.Duration) { fmt.Printf("timer %s %s\n", name, interval.String()) } func (r *printStatsReporter) ReportHistogramValueSamples( name string, _ map[string]string, _ tally.Buckets, bucketLowerBound, bucketUpperBound float64, samples int64, ) { fmt.Printf("histogram %s bucket lower %f upper %f samples %d\n", name, bucketLowerBound, bucketUpperBound, samples) } func (r *printStatsReporter) ReportHistogramDurationSamples( name string, _ map[string]string, _ tally.Buckets, bucketLowerBound, bucketUpperBound time.Duration, samples int64, ) { fmt.Printf("histogram %s bucket lower %v upper %v samples %d\n", name, bucketLowerBound, bucketUpperBound, samples) } func (r *printStatsReporter) Capabilities() tally.Capabilities { return r } func (r *printStatsReporter) Reporting() bool { return true } func (r *printStatsReporter) Tagging() bool { return false } func (r *printStatsReporter) Flush() { fmt.Printf("flush\n") } func main() { reporter := newPrintStatsReporter() rootScope, closer := tally.NewRootScope(tally.ScopeOptions{ Reporter: reporter, }, time.Second) defer closer.Close() subScope := rootScope.SubScope("requests") bighand := time.NewTicker(time.Millisecond * 2300) littlehand := time.NewTicker(time.Millisecond * 10) hugehand := time.NewTicker(time.Millisecond * 5100) measureThing := rootScope.Gauge("thing") timings := rootScope.Timer("timings") tickCounter := subScope.Counter("ticks") // Spin forever, watch report get called go func() { for { select { case <-bighand.C: measureThing.Update(42.1) case <-littlehand.C: tickCounter.Inc(1) case <-hugehand.C: timings.Record(3200 * time.Millisecond) } } }() select {} } golang-github-uber-go-tally-4.1.11/generate.go000066400000000000000000000025021456146544700211460ustar00rootroot00000000000000// Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( //go:generate mockgen -package tallymock -destination tallymock/stats_reporter.go -imports github.com/uber-go/tally github.com/uber-go/tally StatsReporter _ "github.com/golang/mock/mockgen/model" ) golang-github-uber-go-tally-4.1.11/go.mod000066400000000000000000000007111456146544700201330ustar00rootroot00000000000000module github.com/uber-go/tally/v4 go 1.15 require ( github.com/cactus/go-statsd-client/v5 v5.0.0 github.com/golang/mock v1.6.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_model v0.2.0 github.com/stretchr/testify v1.8.0 github.com/twmb/murmur3 v1.1.5 go.uber.org/atomic v1.7.0 go.uber.org/goleak v1.2.1 gopkg.in/validator.v2 v2.0.0-20200605151824-2b28d334fa05 gopkg.in/yaml.v2 v2.4.0 ) golang-github-uber-go-tally-4.1.11/go.sum000066400000000000000000000425351456146544700201720ustar00rootroot00000000000000cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cactus/go-statsd-client/v5 v5.0.0 h1:KqvIQtc9qt34uq+nu4nd1PwingWfBt/IISgtUQ2nSJk= github.com/cactus/go-statsd-client/v5 v5.0.0/go.mod h1:COEvJ1E+/E2L4q6QE5CkjWPi4eeDw9maJBMIuMPBZbY= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/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/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/twmb/murmur3 v1.1.5 h1:i9OLS9fkuLzBXjt6dptlAEyk58fJsSTXbRg3SgVyqgk= github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/validator.v2 v2.0.0-20200605151824-2b28d334fa05 h1:l9eKDCWy9n7C5NAiQAMvDePh0vyLAweR6LcSUVXFUGg= gopkg.in/validator.v2 v2.0.0-20200605151824-2b28d334fa05/go.mod h1:o4V0GXN9/CAmCsvJ0oXYZvrZOe7syiDZSN1GWGZTGzc= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-github-uber-go-tally-4.1.11/histogram.go000066400000000000000000000234661456146544700213650ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "errors" "fmt" "math" "sort" "time" ) var ( // DefaultBuckets can be passed to specify to default buckets. DefaultBuckets Buckets errBucketsCountNeedsGreaterThanZero = errors.New("n needs to be > 0") errBucketsStartNeedsGreaterThanZero = errors.New("start needs to be > 0") errBucketsFactorNeedsGreaterThanOne = errors.New("factor needs to be > 1") _singleBucket = bucketPair{ lowerBoundDuration: time.Duration(math.MinInt64), upperBoundDuration: time.Duration(math.MaxInt64), lowerBoundValue: -math.MaxFloat64, upperBoundValue: math.MaxFloat64, } ) // ValueBuckets is a set of float64 values that implements Buckets. type ValueBuckets []float64 // Implements sort.Interface func (v ValueBuckets) Len() int { return len(v) } // Implements sort.Interface func (v ValueBuckets) Swap(i, j int) { v[i], v[j] = v[j], v[i] } // Implements sort.Interface func (v ValueBuckets) Less(i, j int) bool { return v[i] < v[j] } func (v ValueBuckets) String() string { values := make([]string, len(v)) for i := range values { values[i] = fmt.Sprintf("%f", v[i]) } return fmt.Sprint(values) } // AsValues implements Buckets. func (v ValueBuckets) AsValues() []float64 { return v } // AsDurations implements Buckets and returns time.Duration // representations of the float64 values divided by time.Second. func (v ValueBuckets) AsDurations() []time.Duration { values := make([]time.Duration, len(v)) for i := range values { values[i] = time.Duration(v[i] * float64(time.Second)) } return values } // DurationBuckets is a set of time.Duration values that implements Buckets. type DurationBuckets []time.Duration // Implements sort.Interface func (v DurationBuckets) Len() int { return len(v) } // Implements sort.Interface func (v DurationBuckets) Swap(i, j int) { v[i], v[j] = v[j], v[i] } // Implements sort.Interface func (v DurationBuckets) Less(i, j int) bool { return v[i] < v[j] } func (v DurationBuckets) String() string { values := make([]string, len(v)) for i := range values { values[i] = v[i].String() } return fmt.Sprintf("%v", values) } // AsValues implements Buckets and returns float64 // representations of the time.Duration values divided by time.Second. func (v DurationBuckets) AsValues() []float64 { values := make([]float64, len(v)) for i := range values { values[i] = float64(v[i]) / float64(time.Second) } return values } // AsDurations implements Buckets. func (v DurationBuckets) AsDurations() []time.Duration { return v } func bucketsEqual(x Buckets, y Buckets) bool { switch b1 := x.(type) { case DurationBuckets: b2, ok := y.(DurationBuckets) if !ok { return false } if len(b1) != len(b2) { return false } for i := 0; i < len(b1); i++ { if b1[i] != b2[i] { return false } } case ValueBuckets: b2, ok := y.(ValueBuckets) if !ok { return false } if len(b1) != len(b2) { return false } for i := 0; i < len(b1); i++ { if b1[i] != b2[i] { return false } } } return true } func newBucketPair( htype histogramType, durations []time.Duration, values []float64, upperBoundIndex int, prev BucketPair, ) bucketPair { var pair bucketPair switch htype { case durationHistogramType: pair = bucketPair{ lowerBoundDuration: prev.UpperBoundDuration(), upperBoundDuration: durations[upperBoundIndex], } case valueHistogramType: pair = bucketPair{ lowerBoundValue: prev.UpperBoundValue(), upperBoundValue: values[upperBoundIndex], } default: // nop } return pair } // BucketPairs creates a set of bucket pairs from a set // of buckets describing the lower and upper bounds for // each derived bucket. func BucketPairs(buckets Buckets) []BucketPair { htype := valueHistogramType if _, ok := buckets.(DurationBuckets); ok { htype = durationHistogramType } if buckets == nil || buckets.Len() < 1 { return []BucketPair{_singleBucket} } var ( values []float64 durations []time.Duration pairs = make([]BucketPair, 0, buckets.Len()+2) pair bucketPair ) switch htype { case durationHistogramType: durations = copyAndSortDurations(buckets.AsDurations()) pair.lowerBoundDuration = _singleBucket.lowerBoundDuration pair.upperBoundDuration = durations[0] case valueHistogramType: values = copyAndSortValues(buckets.AsValues()) pair.lowerBoundValue = _singleBucket.lowerBoundValue pair.upperBoundValue = values[0] default: // n.b. This branch will never be executed because htype is only ever // one of two values. panic("unsupported histogram type") } pairs = append(pairs, pair) for i := 1; i < buckets.Len(); i++ { pairs = append( pairs, newBucketPair(htype, durations, values, i, pairs[i-1]), ) } switch htype { case durationHistogramType: pair.lowerBoundDuration = pairs[len(pairs)-1].UpperBoundDuration() pair.upperBoundDuration = _singleBucket.upperBoundDuration case valueHistogramType: pair.lowerBoundValue = pairs[len(pairs)-1].UpperBoundValue() pair.upperBoundValue = _singleBucket.upperBoundValue } pairs = append(pairs, pair) return pairs } func copyAndSortValues(values []float64) []float64 { valuesCopy := make([]float64, len(values)) copy(valuesCopy, values) sort.Sort(ValueBuckets(valuesCopy)) return valuesCopy } func copyAndSortDurations(durations []time.Duration) []time.Duration { durationsCopy := make([]time.Duration, len(durations)) copy(durationsCopy, durations) sort.Sort(DurationBuckets(durationsCopy)) return durationsCopy } type bucketPair struct { lowerBoundValue float64 upperBoundValue float64 lowerBoundDuration time.Duration upperBoundDuration time.Duration } func (p bucketPair) LowerBoundValue() float64 { return p.lowerBoundValue } func (p bucketPair) UpperBoundValue() float64 { return p.upperBoundValue } func (p bucketPair) LowerBoundDuration() time.Duration { return p.lowerBoundDuration } func (p bucketPair) UpperBoundDuration() time.Duration { return p.upperBoundDuration } // LinearValueBuckets creates a set of linear value buckets. func LinearValueBuckets(start, width float64, n int) (ValueBuckets, error) { if n <= 0 { return nil, errBucketsCountNeedsGreaterThanZero } buckets := make([]float64, n) for i := range buckets { buckets[i] = start + (float64(i) * width) } return buckets, nil } // MustMakeLinearValueBuckets creates a set of linear value buckets // or panics. func MustMakeLinearValueBuckets(start, width float64, n int) ValueBuckets { buckets, err := LinearValueBuckets(start, width, n) if err != nil { panic(err) } return buckets } // LinearDurationBuckets creates a set of linear duration buckets. func LinearDurationBuckets(start, width time.Duration, n int) (DurationBuckets, error) { if n <= 0 { return nil, errBucketsCountNeedsGreaterThanZero } buckets := make([]time.Duration, n) for i := range buckets { buckets[i] = start + (time.Duration(i) * width) } return buckets, nil } // MustMakeLinearDurationBuckets creates a set of linear duration buckets. // or panics. func MustMakeLinearDurationBuckets(start, width time.Duration, n int) DurationBuckets { buckets, err := LinearDurationBuckets(start, width, n) if err != nil { panic(err) } return buckets } // ExponentialValueBuckets creates a set of exponential value buckets. func ExponentialValueBuckets(start, factor float64, n int) (ValueBuckets, error) { if n <= 0 { return nil, errBucketsCountNeedsGreaterThanZero } if start <= 0 { return nil, errBucketsStartNeedsGreaterThanZero } if factor <= 1 { return nil, errBucketsFactorNeedsGreaterThanOne } buckets := make([]float64, n) curr := start for i := range buckets { buckets[i] = curr curr *= factor } return buckets, nil } // MustMakeExponentialValueBuckets creates a set of exponential value buckets // or panics. func MustMakeExponentialValueBuckets(start, factor float64, n int) ValueBuckets { buckets, err := ExponentialValueBuckets(start, factor, n) if err != nil { panic(err) } return buckets } // ExponentialDurationBuckets creates a set of exponential duration buckets. func ExponentialDurationBuckets(start time.Duration, factor float64, n int) (DurationBuckets, error) { if n <= 0 { return nil, errBucketsCountNeedsGreaterThanZero } if start <= 0 { return nil, errBucketsStartNeedsGreaterThanZero } if factor <= 1 { return nil, errBucketsFactorNeedsGreaterThanOne } buckets := make([]time.Duration, n) curr := start for i := range buckets { buckets[i] = curr curr = time.Duration(float64(curr) * factor) } return buckets, nil } // MustMakeExponentialDurationBuckets creates a set of exponential value buckets // or panics. func MustMakeExponentialDurationBuckets(start time.Duration, factor float64, n int) DurationBuckets { buckets, err := ExponentialDurationBuckets(start, factor, n) if err != nil { panic(err) } return buckets } golang-github-uber-go-tally-4.1.11/histogram_test.go000066400000000000000000000146121456146544700224150ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "math" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestValueBucketsString(t *testing.T) { result, err := LinearValueBuckets(1, 1, 3) require.NoError(t, err) assert.Equal(t, "[1.000000 2.000000 3.000000]", Buckets(result).String()) } func TestDurationBucketsString(t *testing.T) { result, err := LinearDurationBuckets(time.Second, time.Second, 3) require.NoError(t, err) assert.Equal(t, "[1s 2s 3s]", Buckets(result).String()) } func TestBucketPairsDefaultsToNegInfinityToInfinity(t *testing.T) { pairs := BucketPairs(nil) require.Equal(t, 1, len(pairs)) assert.Equal(t, -math.MaxFloat64, pairs[0].LowerBoundValue()) assert.Equal(t, math.MaxFloat64, pairs[0].UpperBoundValue()) assert.Equal(t, time.Duration(math.MinInt64), pairs[0].LowerBoundDuration()) assert.Equal(t, time.Duration(math.MaxInt64), pairs[0].UpperBoundDuration()) } func TestBucketPairsSortsValueBuckets(t *testing.T) { pairs := BucketPairs(ValueBuckets{1.0, 3.0, 2.0}) require.Equal(t, 4, len(pairs)) assert.Equal(t, -math.MaxFloat64, pairs[0].LowerBoundValue()) assert.Equal(t, 1.0, pairs[0].UpperBoundValue()) assert.Equal(t, 1.0, pairs[1].LowerBoundValue()) assert.Equal(t, 2.0, pairs[1].UpperBoundValue()) assert.Equal(t, 2.0, pairs[2].LowerBoundValue()) assert.Equal(t, 3.0, pairs[2].UpperBoundValue()) assert.Equal(t, 3.0, pairs[3].LowerBoundValue()) assert.Equal(t, math.MaxFloat64, pairs[3].UpperBoundValue()) } func TestBucketPairsSortsDurationBuckets(t *testing.T) { pairs := BucketPairs(DurationBuckets{0 * time.Second, 2 * time.Second, 1 * time.Second}) require.Equal(t, 4, len(pairs)) assert.Equal(t, time.Duration(math.MinInt64), pairs[0].LowerBoundDuration()) assert.Equal(t, 0*time.Second, pairs[0].UpperBoundDuration()) assert.Equal(t, 0*time.Second, pairs[1].LowerBoundDuration()) assert.Equal(t, 1*time.Second, pairs[1].UpperBoundDuration()) assert.Equal(t, 1*time.Second, pairs[2].LowerBoundDuration()) assert.Equal(t, 2*time.Second, pairs[2].UpperBoundDuration()) assert.Equal(t, 2*time.Second, pairs[3].LowerBoundDuration()) assert.Equal(t, time.Duration(math.MaxInt64), pairs[3].UpperBoundDuration()) } func TestMustMakeLinearValueBuckets(t *testing.T) { assert.NotPanics(t, func() { assert.Equal(t, ValueBuckets{ 0.0, 1.0, 2.0, }, MustMakeLinearValueBuckets(0, 1, 3)) }) } func TestMustMakeLinearValueBucketsPanicsOnBadCount(t *testing.T) { assert.Panics(t, func() { MustMakeLinearValueBuckets(0, 1, 0) }) } func TestMustMakeLinearDurationBuckets(t *testing.T) { assert.NotPanics(t, func() { assert.Equal(t, DurationBuckets{ 0, time.Second, 2 * time.Second, }, MustMakeLinearDurationBuckets(0*time.Second, 1*time.Second, 3)) }) } func TestMustMakeLinearDurationBucketsPanicsOnBadCount(t *testing.T) { assert.Panics(t, func() { MustMakeLinearDurationBuckets(0*time.Second, 1*time.Second, 0) }) } func TestMustMakeExponentialValueBuckets(t *testing.T) { assert.NotPanics(t, func() { assert.Equal(t, ValueBuckets{ 2, 4, 8, }, MustMakeExponentialValueBuckets(2, 2, 3)) }) } func TestMustMakeExponentialValueBucketsPanicsOnBadCount(t *testing.T) { assert.Panics(t, func() { MustMakeExponentialValueBuckets(2, 2, 0) }) } func TestMustMakeExponentialValueBucketsPanicsOnBadStart(t *testing.T) { assert.Panics(t, func() { MustMakeExponentialValueBuckets(0, 2, 2) }) } func TestMustMakeExponentialValueBucketsPanicsOnBadFactor(t *testing.T) { assert.Panics(t, func() { MustMakeExponentialValueBuckets(2, 1, 2) }) } func TestMustMakeExponentialDurationBuckets(t *testing.T) { assert.NotPanics(t, func() { assert.Equal(t, DurationBuckets{ 2 * time.Second, 4 * time.Second, 8 * time.Second, }, MustMakeExponentialDurationBuckets(2*time.Second, 2, 3)) }) } func TestMustMakeExponentialDurationBucketsPanicsOnBadCount(t *testing.T) { assert.Panics(t, func() { MustMakeExponentialDurationBuckets(2*time.Second, 2, 0) }) } func TestMustMakeExponentialDurationBucketsPanicsOnBadStart(t *testing.T) { assert.Panics(t, func() { MustMakeExponentialDurationBuckets(0, 2, 2) }) } func TestMustMakeExponentialDurationBucketsPanicsOnBadFactor(t *testing.T) { assert.Panics(t, func() { MustMakeExponentialDurationBuckets(2*time.Second, 1, 2) }) } func TestBucketPairsNoRaceWhenSorted(t *testing.T) { buckets := DurationBuckets{} for i := 0; i < 99; i++ { buckets = append(buckets, time.Duration(i)*time.Second) } newPair := func() { pairs := BucketPairs(buckets) require.Equal(t, 100, len(pairs)) } for i := 0; i < 10; i++ { go newPair() } } func TestBucketPairsNoRaceWhenUnsorted(t *testing.T) { buckets := DurationBuckets{} for i := 100; i > 1; i-- { buckets = append(buckets, time.Duration(i)*time.Second) } newPair := func() { pairs := BucketPairs(buckets) require.Equal(t, 100, len(pairs)) } for i := 0; i < 10; i++ { go newPair() } } func BenchmarkBucketsEqual(b *testing.B) { bench := func(b *testing.B, x Buckets, y Buckets) { b.ResetTimer() for i := 0; i < b.N; i++ { bucketsEqual(x, y) } } b.Run("same 20 values", func(b *testing.B) { buckets := MustMakeLinearValueBuckets(1.0, 1.0, 20) bench(b, buckets, buckets) }) b.Run("same 20 durations", func(b *testing.B) { buckets := MustMakeLinearDurationBuckets(time.Second, time.Second, 20) bench(b, buckets, buckets) }) } golang-github-uber-go-tally-4.1.11/instrument/000077500000000000000000000000001456146544700212365ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/instrument/call.go000066400000000000000000000043001456146544700224750ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package instrument import ( tally "github.com/uber-go/tally/v4" ) const ( resultType = "result_type" resultTypeError = "error" resultTypeSuccess = "success" timingSuffix = "latency" ) // NewCall returns a Call that instruments a function using a given scope // and a label to name the metrics. // The following counters are created excluding {{ and }}: // {{name}}+result_type=success // {{name}}+result_type=error // The following timers are created excluding {{ and }} and replacing . with // the scope's separator: // {{name}}.latency func NewCall(scope tally.Scope, name string) Call { return &call{ err: scope.Tagged(map[string]string{resultType: resultTypeError}).Counter(name), success: scope.Tagged(map[string]string{resultType: resultTypeSuccess}).Counter(name), timing: scope.SubScope(name).Timer(timingSuffix), } } type call struct { success tally.Counter err tally.Counter timing tally.Timer } func (c *call) Exec(f ExecFn) error { sw := c.timing.Start() err := f() sw.Stop() if err != nil { c.err.Inc(1) return err } c.success.Inc(1) return nil } golang-github-uber-go-tally-4.1.11/instrument/call_test.go000066400000000000000000000051111456146544700235350ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package instrument import ( "errors" "testing" "time" "github.com/uber-go/tally/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestCallSuccess(t *testing.T) { s := tally.NewTestScope("", nil) sleepFor := time.Microsecond err := NewCall(s, "test_call").Exec(func() error { time.Sleep(time.Microsecond) return nil }) assert.Nil(t, err) snapshot := s.Snapshot() counters := snapshot.Counters() timers := snapshot.Timers() require.NotNil(t, counters["test_call+result_type=success"]) require.NotNil(t, timers["test_call.latency+"]) assert.Equal(t, int64(1), counters["test_call+result_type=success"].Value()) require.Equal(t, 1, len(timers["test_call.latency+"].Values())) assert.True(t, timers["test_call.latency+"].Values()[0] >= sleepFor) } func TestCallFail(t *testing.T) { s := tally.NewTestScope("", nil) sleepFor := time.Microsecond expected := errors.New("an error") err := NewCall(s, "test_call").Exec(func() error { time.Sleep(sleepFor) return expected }) assert.NotNil(t, err) assert.Equal(t, expected, err) snapshot := s.Snapshot() counters := snapshot.Counters() timers := snapshot.Timers() require.NotNil(t, counters["test_call+result_type=error"]) require.NotNil(t, timers["test_call.latency+"]) assert.Equal(t, int64(1), counters["test_call+result_type=error"].Value()) require.Equal(t, 1, len(timers["test_call.latency+"].Values())) assert.True(t, timers["test_call.latency+"].Values()[0] >= sleepFor) } golang-github-uber-go-tally-4.1.11/instrument/types.go000066400000000000000000000027021456146544700227320ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package instrument // ExecFn is an executable function that can be instrumented with a Call. type ExecFn func() error // Call allows tracking the successes, errors, and timing of functions. type Call interface { // Exec executes a function and records whether it succeeded or // failed, and the amount of time that it took. Exec(f ExecFn) error } golang-github-uber-go-tally-4.1.11/internal/000077500000000000000000000000001456146544700206425ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/internal/cache/000077500000000000000000000000001456146544700217055ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/internal/cache/string_intern.go000066400000000000000000000031571456146544700251270ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cache import ( "sync" ) // StringInterner interns strings. type StringInterner struct { entries map[string]string mtx sync.RWMutex } // NewStringInterner creates a new StringInterner. func NewStringInterner() *StringInterner { return &StringInterner{ entries: make(map[string]string), } } // Intern interns s. func (i *StringInterner) Intern(s string) string { i.mtx.RLock() x, ok := i.entries[s] i.mtx.RUnlock() if ok { return x } i.mtx.Lock() i.entries[s] = s i.mtx.Unlock() return s } golang-github-uber-go-tally-4.1.11/internal/cache/tag_cache.go000066400000000000000000000045311456146544700241350ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package cache import ( "sync" "github.com/uber-go/tally/v4/internal/identity" m3thrift "github.com/uber-go/tally/v4/m3/thrift/v2" ) // TagCache is an identity.Accumulator-based tag cache. type TagCache struct { entries map[uint64][]m3thrift.MetricTag mtx sync.RWMutex } // NewTagCache creates a new TagCache. func NewTagCache() *TagCache { return &TagCache{ entries: make(map[uint64][]m3thrift.MetricTag), } } // Get returns the cached value for key. func (c *TagCache) Get(key uint64) ([]m3thrift.MetricTag, bool) { c.mtx.RLock() defer c.mtx.RUnlock() entry, ok := c.entries[key] return entry, ok } // Set attempts to set the value of key as tslice, returning either tslice or // the pre-existing value if found. func (c *TagCache) Set(key uint64, tslice []m3thrift.MetricTag) []m3thrift.MetricTag { c.mtx.RLock() existing, ok := c.entries[key] c.mtx.RUnlock() if ok { return existing } c.mtx.Lock() defer c.mtx.Unlock() c.entries[key] = tslice return tslice } // Len returns the size of the cache, func (c *TagCache) Len() int { c.mtx.RLock() defer c.mtx.RUnlock() return len(c.entries) } // TagMapKey generates a new key based on tags. func TagMapKey(tags map[string]string) uint64 { return identity.StringStringMap(tags) } golang-github-uber-go-tally-4.1.11/internal/identity/000077500000000000000000000000001456146544700224735ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/internal/identity/accumulator.go000066400000000000000000000070471456146544700253510ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package identity import ( "math" "time" "github.com/twmb/murmur3" ) const ( _hashSeed uint64 = 23 _hashFold uint64 = 31 ) // Accumulator is a commutative folding accumulator. type Accumulator uint64 // NewAccumulator creates a new Accumulator with a default seed value. // // n.b. Here and elsewhere, we use nosplit to avoid stack size checks, which // are unnecessary as memory width is bounded to each instance of `a` (a // uint64) and, potentially, a single stack-local loop temporary while // iterating. func NewAccumulator() Accumulator { return Accumulator(_hashSeed) } // NewAccumulatorWithSeed creates a new Accumulator with the provided seed value. func NewAccumulatorWithSeed(seed uint64) Accumulator { return Accumulator(seed) } // AddString hashes str and folds it into the accumulator. func (a Accumulator) AddString(str string) Accumulator { return a + Accumulator(murmur3.StringSum64(str)*_hashFold) } // AddUint64 folds u64 into the accumulator. func (a Accumulator) AddUint64(u64 uint64) Accumulator { return a + Accumulator(u64*_hashFold) } // Value returns the accumulated value. func (a Accumulator) Value() uint64 { return uint64(a) } // Durations returns the accumulated identity of durs. func Durations(durs []time.Duration) uint64 { if len(durs) == 0 { return 0 } acc := NewAccumulator() // n.b. Wrapping due to overflow is okay here, since those values cannot be // represented by int64. for _, d := range durs { acc = acc.AddUint64(uint64(d)) } return acc.Value() } // Int64s returns the accumulated identity of i64s. func Int64s(i64s []int64) uint64 { if len(i64s) == 0 { return 0 } acc := NewAccumulator() // n.b. Wrapping due to overflow is okay here, since those values cannot be // represented by int64. for _, i := range i64s { acc = acc.AddUint64(uint64(i)) } return acc.Value() } // Float64s returns the accumulated identity of f64s. func Float64s(f64s []float64) uint64 { if len(f64s) == 0 { return 0 } // n.b. Wrapping due to overflow is okay here, since those values cannot be // represented by int64. acc := NewAccumulator() for _, f := range f64s { acc = acc.AddUint64(math.Float64bits(f)) } return acc.Value() } // StringStringMap returns the accumulated identity of m. func StringStringMap(m map[string]string) uint64 { if len(m) == 0 { return 0 } acc := NewAccumulator() for k, v := range m { acc = acc.AddString(k + "=" + v) } return acc.Value() } golang-github-uber-go-tally-4.1.11/internal/identity/accumulator_benchmark_test.go000066400000000000000000000034251456146544700304160ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package identity_test import ( "fmt" "testing" "github.com/uber-go/tally/v4/internal/identity" ) func BenchmarkAccumulator_StringStringMap(b *testing.B) { cases := []struct { keys int }{ {keys: 0}, {keys: 1}, {keys: 2}, {keys: 4}, {keys: 8}, {keys: 16}, {keys: 32}, } bench := func(b *testing.B, m map[string]string) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { identity.StringStringMap(m) } } for _, tt := range cases { b.Run(fmt.Sprintf("%d keys", tt.keys), func(b *testing.B) { m := make(map[string]string) for i := 0; i < tt.keys; i++ { s := fmt.Sprintf("abcdefghij%d", i) m[s] = s } bench(b, m) }) } } golang-github-uber-go-tally-4.1.11/key_gen.go000066400000000000000000000064051456146544700210030ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally const ( prefixSplitter = '+' keyPairSplitter = ',' keyNameSplitter = '=' nilString = "" ) // KeyForStringMap generates a unique key for a map string set combination. func KeyForStringMap( stringMap map[string]string, ) string { return KeyForPrefixedStringMap(nilString, stringMap) } // KeyForPrefixedStringMap generates a unique key for a // a prefix and a map string set combination. func KeyForPrefixedStringMap( prefix string, stringMap map[string]string, ) string { return keyForPrefixedStringMaps(prefix, stringMap) } // keyForPrefixedStringMapsAsKey writes a key using the prefix and the tags in a canonical form to // the given input byte slice and returns a reference to the byte slice. Callers of this method can // use a stack allocated byte slice to remove heap allocation. func keyForPrefixedStringMapsAsKey(buf []byte, prefix string, maps ...map[string]string) []byte { // stack allocated keys := make([]string, 0, 32) for _, m := range maps { for k := range m { keys = append(keys, k) } } insertionSort(keys) if prefix != nilString { buf = append(buf, prefix...) buf = append(buf, prefixSplitter) } var lastKey string // last key written to the buffer for _, k := range keys { if len(lastKey) > 0 { if k == lastKey { // Already wrote this key. continue } buf = append(buf, keyPairSplitter) } lastKey = k buf = append(buf, k...) buf = append(buf, keyNameSplitter) // Find and write the value for this key. Rightmost map takes // precedence. for j := len(maps) - 1; j >= 0; j-- { if v, ok := maps[j][k]; ok { buf = append(buf, v...) break } } } return buf } // keyForPrefixedStringMaps generates a unique key for a prefix and a series // of maps containing tags. // // If a key occurs in multiple maps, keys on the right take precedence. func keyForPrefixedStringMaps(prefix string, maps ...map[string]string) string { return string(keyForPrefixedStringMapsAsKey(make([]byte, 0, 256), prefix, maps...)) } func insertionSort(keys []string) { n := len(keys) for i := 1; i < n; i++ { for j := i; j > 0 && keys[j] < keys[j-1]; j-- { keys[j], keys[j-1] = keys[j-1], keys[j] } } } golang-github-uber-go-tally-4.1.11/key_gen_test.go000066400000000000000000000047511456146544700220440ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "math/rand" "sort" "testing" "github.com/stretchr/testify/assert" ) func TestKeyForPrefixedStringMaps(t *testing.T) { tests := []struct { desc string prefix string maps []map[string]string want string }{ { desc: "no maps", prefix: "foo", want: "foo+", }, { desc: "disjoint maps", prefix: "foo", maps: []map[string]string{ { "a": "foo", "b": "bar", }, { "c": "baz", "d": "qux", }, }, want: "foo+a=foo,b=bar,c=baz,d=qux", }, { desc: "map overlap", prefix: "foo", maps: []map[string]string{ { "a": "1", "b": "1", "c": "1", "d": "1", }, {"b": "2"}, {"c": "3"}, {"d": "4"}, }, want: "foo+a=1,b=2,c=3,d=4", }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { got := keyForPrefixedStringMaps(tt.prefix, tt.maps...) assert.Equal(t, tt.want, got) }) } } func TestInsertionSort(t *testing.T) { chars := []byte("abcdefghijklmnopqrstuvwxyz") n := len(chars) var expected []string var actual []string for i := 0; i < 12; i++ { s := rand.Intn(20) + 5 var key []byte for j := 0; j < s; j++ { key = append(key, chars[rand.Intn(n)]) } expected = append(expected, string(key)) actual = append(actual, string(key)) } sort.Strings(expected) insertionSort(actual) assert.Equal(t, expected, actual) } golang-github-uber-go-tally-4.1.11/m3/000077500000000000000000000000001456146544700173455ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/m3/config.go000066400000000000000000000057411456146544700211500ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package m3 // Configuration is a configuration for a M3 reporter. type Configuration struct { // HostPort is the host and port of the M3 server. HostPort string `yaml:"hostPort" validate:"nonzero"` // HostPorts are the host and port of the M3 server. HostPorts []string `yaml:"hostPorts"` // Service is the service tag to that this client emits. Service string `yaml:"service" validate:"nonzero"` // Env is the env tag to use that this client emits. Env string `yaml:"env" validate:"nonzero"` // CommonTags are tags that are common for all metrics this client emits. CommonTags map[string]string `yaml:"tags" ` // Queue is the maximum metric queue size of client. Queue int `yaml:"queue"` // PacketSize is the maximum packet size for a batch of metrics. PacketSize int32 `yaml:"packetSize"` // IncludeHost is whether or not to include host tag. IncludeHost bool `yaml:"includeHost"` // HistogramBucketTagPrecision is precision to use when formatting the metric tag // with the histogram bucket bound values. HistogramBucketTagPrecision uint `yaml:"histogramBucketTagPrecision"` // InternalTags are tags that should be added to all internal metrics // emitted by the reporter. InternalTags map[string]string `yaml:"internalTags"` } // NewReporter creates a new M3 reporter from this configuration. func (c Configuration) NewReporter() (Reporter, error) { hostPorts := c.HostPorts if len(hostPorts) == 0 { hostPorts = []string{c.HostPort} } return NewReporter(Options{ HostPorts: hostPorts, Service: c.Service, Env: c.Env, CommonTags: c.CommonTags, MaxQueueSize: c.Queue, MaxPacketSizeBytes: c.PacketSize, IncludeHost: c.IncludeHost, HistogramBucketTagPrecision: c.HistogramBucketTagPrecision, InternalTags: c.InternalTags, }) } golang-github-uber-go-tally-4.1.11/m3/config_test.go000066400000000000000000000042721456146544700222050ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package m3 import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/uber-go/tally/v4/m3/thriftudp" ) func TestConfigSimple(t *testing.T) { c := Configuration{ HostPort: "127.0.0.1:9052", Service: "my-service", Env: "test", } r, err := c.NewReporter() require.NoError(t, err) reporter := r.(*reporter) _, ok := reporter.client.Transport.(*thriftudp.TUDPTransport) assert.True(t, ok) assert.True(t, tagEquals(reporter.commonTags, "service", "my-service")) assert.True(t, tagEquals(reporter.commonTags, "env", "test")) assert.Equal(t, 0, len(c.InternalTags)) } func TestConfigMulti(t *testing.T) { c := Configuration{ HostPorts: []string{"127.0.0.1:9052", "127.0.0.1:9062"}, Service: "my-service", Env: "test", } r, err := c.NewReporter() require.NoError(t, err) reporter := r.(*reporter) _, ok := reporter.client.Transport.(*thriftudp.TMultiUDPTransport) assert.True(t, ok) assert.True(t, tagEquals(reporter.commonTags, "service", "my-service")) assert.True(t, tagEquals(reporter.commonTags, "env", "test")) } golang-github-uber-go-tally-4.1.11/m3/customtransports/000077500000000000000000000000001456146544700230175ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/m3/customtransports/buffered_read_transport.go000066400000000000000000000056201456146544700302420ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package customtransport import ( "bytes" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" ) // TBufferedReadTransport is a thrift.TTransport that reads from a buffer type TBufferedReadTransport struct { readBuf *bytes.Buffer } // NewTBufferedReadTransport creates a buffer backed TTransport func NewTBufferedReadTransport(readBuf *bytes.Buffer) (*TBufferedReadTransport, error) { return &TBufferedReadTransport{readBuf: readBuf}, nil } // IsOpen does nothing as transport is not maintaining the connection // Required to maintain thrift.TTransport interface func (p *TBufferedReadTransport) IsOpen() bool { return true } // Open does nothing as transport is not maintaining the connection // Required to maintain thrift.TTransport interface func (p *TBufferedReadTransport) Open() error { return nil } // Close does nothing as transport is not maintaining the connection // Required to maintain thrift.TTransport interface func (p *TBufferedReadTransport) Close() error { return nil } // Read reads bytes from the local buffer and puts them in the specified buf func (p *TBufferedReadTransport) Read(buf []byte) (int, error) { in, err := p.readBuf.Read(buf) return in, thrift.NewTTransportExceptionFromError(err) } // RemainingBytes returns the number of bytes left to be read from the readBuf func (p *TBufferedReadTransport) RemainingBytes() uint64 { return uint64(p.readBuf.Len()) } // Write writes bytes into the read buffer // Required to maintain thrift.TTransport interface func (p *TBufferedReadTransport) Write(buf []byte) (int, error) { p.readBuf = bytes.NewBuffer(buf) return len(buf), nil } // Flush does nothing as udp server does not write responses back // Required to maintain thrift.TTransport interface func (p *TBufferedReadTransport) Flush() error { return nil } golang-github-uber-go-tally-4.1.11/m3/customtransports/buffered_read_transport_test.go000066400000000000000000000047631456146544700313100ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package customtransport import ( "bytes" "testing" "github.com/stretchr/testify/require" ) // TestTBufferedReadTransport tests the TBufferedReadTransport func TestTBufferedReadTransport(t *testing.T) { buffer := bytes.NewBuffer([]byte("testString")) trans, err := NewTBufferedReadTransport(buffer) require.NotNil(t, trans) require.NoError(t, err) require.Equal(t, uint64(10), trans.RemainingBytes()) firstRead := make([]byte, 4) n, err := trans.Read(firstRead) require.NoError(t, err) require.Equal(t, 4, n) require.Equal(t, []byte("test"), firstRead) require.Equal(t, uint64(6), trans.RemainingBytes()) secondRead := make([]byte, 7) n, err = trans.Read(secondRead) require.Equal(t, 6, n) require.NoError(t, err) require.Equal(t, []byte("String"), secondRead[0:6]) require.Equal(t, uint64(0), trans.RemainingBytes()) } // TestTBufferedReadTransportEmptyFunctions tests the empty functions in TBufferedReadTransport func TestTBufferedReadTransportEmptyFunctions(t *testing.T) { byteArr := make([]byte, 1) trans, err := NewTBufferedReadTransport(bytes.NewBuffer(byteArr)) require.NotNil(t, trans) require.NoError(t, err) err = trans.Open() require.NoError(t, err) err = trans.Close() require.NoError(t, err) err = trans.Flush() require.NoError(t, err) n, err := trans.Write(byteArr) require.Equal(t, 1, n) require.NoError(t, err) isOpen := trans.IsOpen() require.True(t, isOpen) } golang-github-uber-go-tally-4.1.11/m3/customtransports/m3_calc_transport.go000066400000000000000000000066671456146544700270020ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package customtransport // TCalcTransport is a thrift TTransport that is used to calculate how many // bytes are used when writing a thrift element. type TCalcTransport struct { count int32 } // GetCount returns the number of bytes that would be written // Required to maintain thrift.TTransport interface func (p *TCalcTransport) GetCount() int32 { return p.count } // ResetCount resets the number of bytes written to 0 func (p *TCalcTransport) ResetCount() { p.count = 0 } // Write adds the number of bytes written to the count // Required to maintain thrift.TTransport interface func (p *TCalcTransport) Write(buf []byte) (int, error) { p.count += int32(len(buf)) return len(buf), nil } // WriteByte adds 1 to the count // Required to maintain thrift.TRichTransport interface func (p *TCalcTransport) WriteByte(byte) error { p.count++ return nil } // WriteString adds the length of the string to the count // Required to maintain thrift.TRichTransport interface func (p *TCalcTransport) WriteString(s string) (int, error) { p.count += int32(len(s)) return len(s), nil } // IsOpen does nothing as transport is not maintaining a connection // Required to maintain thrift.TTransport interface func (p *TCalcTransport) IsOpen() bool { return true } // Open does nothing as transport is not maintaining a connection // Required to maintain thrift.TTransport interface func (p *TCalcTransport) Open() error { return nil } // Close does nothing as transport is not maintaining a connection // Required to maintain thrift.TTransport interface func (p *TCalcTransport) Close() error { return nil } // Read does nothing as it's not required for calculations // Required to maintain thrift.TTransport interface func (p *TCalcTransport) Read(buf []byte) (int, error) { return 0, nil } // ReadByte does nothing as it's not required for calculations // Required to maintain thrift.TRichTransport interface func (p *TCalcTransport) ReadByte() (byte, error) { return 0, nil } // RemainingBytes returns the max number of bytes (same as Thrift's StreamTransport) as we // do not know how many bytes we have left. func (p *TCalcTransport) RemainingBytes() uint64 { const maxSize = ^uint64(0) return maxSize } // Flush does nothing as it's not required for calculations // Required to maintain thrift.TTransport interface func (p *TCalcTransport) Flush() error { return nil } golang-github-uber-go-tally-4.1.11/m3/customtransports/m3_calc_transport_test.go000066400000000000000000000053631456146544700300310ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package customtransport import ( "testing" "github.com/stretchr/testify/require" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" ) // Make sure that TCalcTransport implements TRichTransport. // We use TRichTransport instead of TTransport to avoid unnecessary allocations // when writing string fields. See tests in var _ thrift.TRichTransport = (*TCalcTransport)(nil) func TestTCalcTransport(t *testing.T) { trans := &TCalcTransport{} require.Nil(t, trans.Open()) require.True(t, trans.IsOpen()) require.EqualValues(t, 0, trans.GetCount()) testString1 := "test" testString2 := "string" n, err := trans.Write([]byte(testString1)) require.Equal(t, len(testString1), n) require.NoError(t, err) require.EqualValues(t, len(testString1), trans.GetCount()) n, err = trans.Write([]byte(testString2)) require.EqualValues(t, len(testString2), n) require.NoError(t, err) require.EqualValues(t, len(testString1)+len(testString2), trans.GetCount()) trans.ResetCount() n, err = trans.WriteString(testString1) require.EqualValues(t, len(testString1), n) require.NoError(t, err) require.EqualValues(t, len(testString1), trans.GetCount()) err = trans.WriteByte('a') require.NoError(t, err) require.EqualValues(t, len(testString1)+1, trans.GetCount()) n, err = trans.Read([]byte(testString1)) require.NoError(t, err) require.EqualValues(t, 0, n) b, err := trans.ReadByte() require.NoError(t, err) require.Equal(t, byte(0), b) require.Equal(t, ^uint64(0), trans.RemainingBytes()) trans.ResetCount() require.EqualValues(t, 0, trans.GetCount()) err = trans.Flush() require.NoError(t, err) require.Nil(t, trans.Close()) } golang-github-uber-go-tally-4.1.11/m3/example/000077500000000000000000000000001456146544700210005ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/m3/example/README.md000066400000000000000000000000471456146544700222600ustar00rootroot00000000000000# M3 reporter example `go run ./*.go` golang-github-uber-go-tally-4.1.11/m3/example/config.yaml000066400000000000000000000002031456146544700231240ustar00rootroot00000000000000m3: hostPort: 127.0.0.1:6396 service: m3example env: staging tags: cluster: local region: us-east golang-github-uber-go-tally-4.1.11/m3/example/local_server.go000066400000000000000000000062411456146544700240120ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package main import ( "bytes" "fmt" "net" "sync/atomic" "github.com/uber-go/tally/v4/m3" customtransport "github.com/uber-go/tally/v4/m3/customtransports" m3thrift "github.com/uber-go/tally/v4/m3/thrift/v1" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" ) type batchCallback func(batch *m3thrift.MetricBatch) type localM3Server struct { Service *localM3Service Addr string protocol m3.Protocol processor thrift.TProcessor conn *net.UDPConn closed int32 } func newLocalM3Server( listenAddr string, protocol m3.Protocol, fn batchCallback, ) (*localM3Server, error) { udpAddr, err := net.ResolveUDPAddr("udp", listenAddr) if err != nil { return nil, err } service := newLocalM3Service(fn) processor := m3thrift.NewM3Processor(service) conn, err := net.ListenUDP(udpAddr.Network(), udpAddr) if err != nil { return nil, err } return &localM3Server{ Service: service, Addr: conn.LocalAddr().String(), conn: conn, protocol: protocol, processor: processor, }, nil } func (f *localM3Server) Serve() error { readBuf := make([]byte, 65536) for { n, err := f.conn.Read(readBuf) if err != nil { if atomic.LoadInt32(&f.closed) == 0 { return fmt.Errorf("failed to read: %v", err) } return nil } trans, _ := customtransport.NewTBufferedReadTransport(bytes.NewBuffer(readBuf[0:n])) var proto thrift.TProtocol if f.protocol == m3.Compact { proto = thrift.NewTCompactProtocol(trans) } else { proto = thrift.NewTBinaryProtocolTransport(trans) } if _, err = f.processor.Process(proto, proto); err != nil { fmt.Println("Error processing thrift metric:", err) } } } func (f *localM3Server) Close() error { atomic.AddInt32(&f.closed, 1) return f.conn.Close() } type localM3Service struct { fn batchCallback } func newLocalM3Service(fn batchCallback) *localM3Service { return &localM3Service{fn: fn} } func (m *localM3Service) EmitMetricBatch(batch *m3thrift.MetricBatch) (err error) { m.fn(batch) return thrift.NewTTransportException(thrift.END_OF_FILE, "complete") } golang-github-uber-go-tally-4.1.11/m3/example/m3_main.go000066400000000000000000000103431456146544700226530ustar00rootroot00000000000000// Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package main import ( "flag" "fmt" "io/ioutil" "log" "math/rand" "os" "time" tally "github.com/uber-go/tally/v4" "github.com/uber-go/tally/v4/m3" m3thrift "github.com/uber-go/tally/v4/m3/thrift/v1" validator "gopkg.in/validator.v2" yaml "gopkg.in/yaml.v2" ) var configFileArg = flag.String("config", "config.yaml", "YAML config file path") type config struct { M3 m3.Configuration `yaml:"m3"` } func configFromFile(file string) (*config, error) { fd, err := os.Open(file) if err != nil { return nil, err } defer fd.Close() data, err := ioutil.ReadAll(fd) if err != nil { return nil, err } cfg := &config{} if err := yaml.Unmarshal(data, cfg); err != nil { return nil, err } if err := validator.Validate(cfg); err != nil { return nil, err } return cfg, nil } func main() { flag.Parse() configFile := *configFileArg if configFile == "" { flag.Usage() return } cfg, err := configFromFile(configFile) if err != nil { log.Fatalf("failed to read config file %s: %v", configFile, err) } r, err := cfg.M3.NewReporter() if err != nil { log.Fatalf("failed to create reporter: %v", err) } scope, closer := tally.NewRootScope(tally.ScopeOptions{ CachedReporter: r, CardinalityMetricsTags: cfg.M3.InternalTags, }, 1*time.Second) defer closer.Close() counter := scope.Tagged(map[string]string{ "foo": "bar", }).Counter("test_counter") gauge := scope.Tagged(map[string]string{ "foo": "baz", }).Gauge("test_gauge") timer := scope.Tagged(map[string]string{ "foo": "qux", }).Timer("test_timer_summary") histogram := scope.Tagged(map[string]string{ "foo": "quk", }).Histogram("test_histogram", tally.DefaultBuckets) go func() { for { counter.Inc(1) time.Sleep(time.Second) } }() go func() { for { gauge.Update(rand.Float64() * 1000) time.Sleep(time.Second) } }() go func() { for { tsw := timer.Start() hsw := histogram.Start() time.Sleep(time.Duration(rand.Float64() * float64(time.Second))) tsw.Stop() hsw.Stop() } }() srv, err := newLocalM3Server("127.0.0.1:6396", m3.Compact, func(b *m3thrift.MetricBatch) { for _, m := range b.Metrics { tags := make(map[string]string) for tag := range b.CommonTags { tags[tag.GetTagName()] = tag.GetTagValue() } for tag := range m.Tags { tags[tag.GetTagName()] = tag.GetTagValue() } metVal := m.GetMetricValue() switch { case metVal != nil && metVal.Count != nil: fmt.Printf("counter value: %d, tags: %v\n", metVal.Count.GetI64Value(), tags) case metVal != nil && metVal.Gauge != nil && metVal.Gauge.I64Value != nil: fmt.Printf("gauge value: %d, tags: %v\n", metVal.Gauge.GetI64Value(), tags) case metVal != nil && metVal.Gauge != nil && metVal.Gauge.DValue != nil: fmt.Printf("gauge value: %f, tags: %v\n", metVal.Gauge.GetDValue(), tags) case metVal != nil && metVal.Timer != nil: fmt.Printf("timer value: %v, tags: %v\n", time.Duration(metVal.Timer.GetI64Value()), tags) } } }) if err != nil { log.Fatalf("failed to create test listen server: %v", err) } if err := srv.Serve(); err != nil { log.Fatalf("failed to serve test listen server: %v", err) } } golang-github-uber-go-tally-4.1.11/m3/reporter.go000066400000000000000000000501361456146544700215430ustar00rootroot00000000000000// Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package m3 import ( "fmt" "io" "math" "os" "runtime" "sort" "strconv" "sync" "time" "github.com/pkg/errors" tally "github.com/uber-go/tally/v4" "github.com/uber-go/tally/v4/internal/cache" customtransport "github.com/uber-go/tally/v4/m3/customtransports" m3thrift "github.com/uber-go/tally/v4/m3/thrift/v2" "github.com/uber-go/tally/v4/m3/thriftudp" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" "go.uber.org/atomic" ) // Protocol describes a M3 thrift transport protocol. type Protocol int // Compact and Binary represent the compact and // binary thrift protocols respectively. const ( Compact Protocol = iota Binary ) const ( // ServiceTag is the name of the M3 service tag. ServiceTag = "service" // EnvTag is the name of the M3 env tag. EnvTag = "env" // HostTag is the name of the M3 host tag. HostTag = "host" // DefaultMaxQueueSize is the default M3 reporter queue size. DefaultMaxQueueSize = 4096 // DefaultMaxPacketSize is the default M3 reporter max packet size. DefaultMaxPacketSize = int32(32768) // DefaultHistogramBucketIDName is the default histogram bucket ID tag name DefaultHistogramBucketIDName = "bucketid" // DefaultHistogramBucketName is the default histogram bucket name tag name DefaultHistogramBucketName = "bucket" // DefaultHistogramBucketTagPrecision is the default // precision to use when formatting the metric tag // with the histogram bucket bound values. DefaultHistogramBucketTagPrecision = uint(6) _emitMetricBatchOverhead = 19 _minMetricBucketIDTagLength = 4 _timeResolution = 100 * time.Millisecond ) var ( _maxInt64 = int64(math.MaxInt64) _maxFloat64 = math.MaxFloat64 ) type metricType int const ( counterType metricType = iota + 1 timerType gaugeType ) var ( errNoHostPorts = errors.New("at least one entry for HostPorts is required") errCommonTagSize = errors.New("common tags serialized size exceeds packet size") errAlreadyClosed = errors.New("reporter already closed") ) // Reporter is an M3 reporter. type Reporter interface { tally.CachedStatsReporter io.Closer } // reporter is a metrics backend that reports metrics to a local or // remote M3 collector, metrics are batched together and emitted // via either thrift compact or binary protocol in batch UDP packets. type reporter struct { bucketIDTagName string bucketTagName string bucketValFmt string buckets []tally.BucketPair calc *customtransport.TCalcTransport calcLock sync.Mutex calcProto thrift.TProtocol client *m3thrift.M3Client commonTags []m3thrift.MetricTag done atomic.Bool donech chan struct{} freeBytes int32 metCh chan sizedMetric now atomic.Int64 overheadBytes int32 pending atomic.Uint64 resourcePool *resourcePool stringInterner *cache.StringInterner tagCache *cache.TagCache wg sync.WaitGroup batchSizeHistogram tally.CachedHistogram numBatches atomic.Int64 numBatchesCounter tally.CachedCount numMetrics atomic.Int64 numMetricsCounter tally.CachedCount numWriteErrors atomic.Int64 numWriteErrorsCounter tally.CachedCount numTagCacheCounter tally.CachedCount } // Options is a set of options for the M3 reporter. type Options struct { HostPorts []string Service string Env string CommonTags map[string]string IncludeHost bool Protocol Protocol MaxQueueSize int MaxPacketSizeBytes int32 HistogramBucketIDName string HistogramBucketName string HistogramBucketTagPrecision uint InternalTags map[string]string } // NewReporter creates a new M3 reporter. func NewReporter(opts Options) (Reporter, error) { if opts.MaxQueueSize <= 0 { opts.MaxQueueSize = DefaultMaxQueueSize } if opts.MaxPacketSizeBytes <= 0 { opts.MaxPacketSizeBytes = DefaultMaxPacketSize } if opts.HistogramBucketIDName == "" { opts.HistogramBucketIDName = DefaultHistogramBucketIDName } if opts.HistogramBucketName == "" { opts.HistogramBucketName = DefaultHistogramBucketName } if opts.HistogramBucketTagPrecision == 0 { opts.HistogramBucketTagPrecision = DefaultHistogramBucketTagPrecision } // Create M3 thrift client var trans thrift.TTransport var err error if len(opts.HostPorts) == 0 { err = errNoHostPorts } else if len(opts.HostPorts) == 1 { trans, err = thriftudp.NewTUDPClientTransport(opts.HostPorts[0], "") } else { trans, err = thriftudp.NewTMultiUDPClientTransport(opts.HostPorts, "") } if err != nil { return nil, err } var protocolFactory thrift.TProtocolFactory if opts.Protocol == Compact { protocolFactory = thrift.NewTCompactProtocolFactory() } else { protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() } var ( client = m3thrift.NewM3ClientFactory(trans, protocolFactory) resourcePool = newResourcePool(protocolFactory) tagm = make(map[string]string) tags = resourcePool.getMetricTagSlice() ) // Create common tags for k, v := range opts.CommonTags { tagm[k] = v } if opts.CommonTags[ServiceTag] == "" { if opts.Service == "" { return nil, fmt.Errorf("%s common tag is required", ServiceTag) } tagm[ServiceTag] = opts.Service } if opts.CommonTags[EnvTag] == "" { if opts.Env == "" { return nil, fmt.Errorf("%s common tag is required", EnvTag) } tagm[EnvTag] = opts.Env } if opts.IncludeHost { if opts.CommonTags[HostTag] == "" { hostname, err := os.Hostname() if err != nil { return nil, errors.WithMessage(err, "error resolving host tag") } tagm[HostTag] = hostname } } for k, v := range tagm { tags = append(tags, m3thrift.MetricTag{ Name: k, Value: v, }) } // Calculate size of common tags var ( batch = m3thrift.MetricBatch{ Metrics: resourcePool.getMetricSlice(), CommonTags: tags, } proto = resourcePool.getProto() ) if err := batch.Write(proto); err != nil { return nil, errors.WithMessage( err, "failed to write to proto for size calculation", ) } resourcePool.releaseMetricSlice(batch.Metrics) var ( calc = proto.Transport().(*customtransport.TCalcTransport) numOverheadBytes = _emitMetricBatchOverhead + calc.GetCount() freeBytes = opts.MaxPacketSizeBytes - numOverheadBytes ) calc.ResetCount() if freeBytes <= 0 { return nil, errCommonTagSize } buckets := tally.ValueBuckets(append( []float64{0.0}, tally.MustMakeExponentialValueBuckets(2.0, 2.0, 11)..., )) r := &reporter{ buckets: tally.BucketPairs(buckets), bucketIDTagName: opts.HistogramBucketIDName, bucketTagName: opts.HistogramBucketName, bucketValFmt: "%." + strconv.Itoa(int(opts.HistogramBucketTagPrecision)) + "f", calc: calc, calcProto: proto, client: client, commonTags: tags, donech: make(chan struct{}), freeBytes: freeBytes, metCh: make(chan sizedMetric, opts.MaxQueueSize), overheadBytes: numOverheadBytes, resourcePool: resourcePool, stringInterner: cache.NewStringInterner(), tagCache: cache.NewTagCache(), } internalTags := map[string]string{ "version": tally.Version, "host": tally.DefaultTagRedactValue, "instance": tally.DefaultTagRedactValue, } for k, v := range opts.InternalTags { internalTags[k] = v } r.batchSizeHistogram = r.AllocateHistogram("tally.internal.batch-size", internalTags, buckets) r.numBatchesCounter = r.AllocateCounter("tally.internal.num-batches", internalTags) r.numMetricsCounter = r.AllocateCounter("tally.internal.num-metrics", internalTags) r.numWriteErrorsCounter = r.AllocateCounter("tally.internal.num-write-errors", internalTags) r.numTagCacheCounter = r.AllocateCounter("tally.internal.num-tag-cache", internalTags) r.wg.Add(1) go func() { defer r.wg.Done() r.process() }() r.wg.Add(1) go func() { defer r.wg.Done() r.timeLoop() }() return r, nil } // AllocateCounter implements tally.CachedStatsReporter. func (r *reporter) AllocateCounter( name string, tags map[string]string, ) tally.CachedCount { return r.allocateCounter(name, tags) } func (r *reporter) allocateCounter( name string, tags map[string]string, ) cachedMetric { var ( counter = r.newMetric(name, tags, counterType) size = r.calculateSize(counter) ) return cachedMetric{ metric: counter, reporter: r, size: size, } } // AllocateGauge implements tally.CachedStatsReporter. func (r *reporter) AllocateGauge( name string, tags map[string]string, ) tally.CachedGauge { var ( gauge = r.newMetric(name, tags, gaugeType) size = r.calculateSize(gauge) ) return cachedMetric{ metric: gauge, reporter: r, size: size, } } // AllocateTimer implements tally.CachedStatsReporter. func (r *reporter) AllocateTimer( name string, tags map[string]string, ) tally.CachedTimer { var ( timer = r.newMetric(name, tags, timerType) size = r.calculateSize(timer) ) return cachedMetric{ metric: timer, reporter: r, size: size, } } // AllocateHistogram implements tally.CachedStatsReporter. func (r *reporter) AllocateHistogram( name string, tags map[string]string, buckets tally.Buckets, ) tally.CachedHistogram { var ( _, isDuration = buckets.(tally.DurationBuckets) bucketIDLen = int(math.Max( float64(ndigits(buckets.Len())), float64(_minMetricBucketIDTagLength), )) bucketIDFmt = "%0" + strconv.Itoa(bucketIDLen) + "d" cachedValueBuckets []cachedHistogramBucket cachedDurationBuckets []cachedHistogramBucket ) var ( mtags = r.convertTags(tags) prevDuration = time.Duration(math.MinInt64) prevValue = -math.MaxFloat64 ) for i, pair := range tally.BucketPairs(buckets) { var ( counter = r.allocateCounter(name, nil) hbucket = cachedHistogramBucket{ bucketID: r.stringInterner.Intern(fmt.Sprintf(bucketIDFmt, i)), valueUpperBound: pair.UpperBoundValue(), durationUpperBound: pair.UpperBoundDuration(), metric: &counter, } delta = len(r.bucketIDTagName) + len(r.bucketTagName) + len(hbucket.bucketID) ) hbucket.metric.metric.Tags = mtags hbucket.metric.size = r.calculateSize(hbucket.metric.metric) if isDuration { bname := r.stringInterner.Intern( r.durationBucketString(prevDuration) + "-" + r.durationBucketString(pair.UpperBoundDuration()), ) hbucket.bucket = bname hbucket.metric.size += int32(delta + len(bname)) cachedDurationBuckets = append(cachedDurationBuckets, hbucket) } else { bname := r.stringInterner.Intern( r.valueBucketString(prevValue) + "-" + r.valueBucketString(pair.UpperBoundValue()), ) hbucket.bucket = bname hbucket.metric.size += int32(delta + len(bname)) cachedValueBuckets = append(cachedValueBuckets, hbucket) } prevDuration = pair.UpperBoundDuration() prevValue = pair.UpperBoundValue() } return cachedHistogram{ r: r, name: name, cachedValueBuckets: cachedValueBuckets, cachedDurationBuckets: cachedDurationBuckets, } } func (r *reporter) valueBucketString(v float64) string { if v == math.MaxFloat64 { return "infinity" } if v == -math.MaxFloat64 { return "-infinity" } return fmt.Sprintf(r.bucketValFmt, v) } func (r *reporter) durationBucketString(d time.Duration) string { if d == 0 { return "0" } if d == time.Duration(math.MaxInt64) { return "infinity" } if d == time.Duration(math.MinInt64) { return "-infinity" } return d.String() } func (r *reporter) newMetric( name string, tags map[string]string, t metricType, ) m3thrift.Metric { m := m3thrift.Metric{ Name: r.stringInterner.Intern(name), Timestamp: _maxInt64, } switch t { case counterType: m.Value.MetricType = m3thrift.MetricType_COUNTER m.Value.Count = _maxInt64 case gaugeType: m.Value.MetricType = m3thrift.MetricType_GAUGE m.Value.Gauge = _maxFloat64 case timerType: m.Value.MetricType = m3thrift.MetricType_TIMER m.Value.Timer = _maxInt64 } if len(tags) == 0 { return m } m.Tags = r.convertTags(tags) return m } func (r *reporter) calculateSize(m m3thrift.Metric) int32 { r.calcLock.Lock() m.Write(r.calcProto) //nolint:errcheck size := r.calc.GetCount() r.calc.ResetCount() r.calcLock.Unlock() return size } func (r *reporter) reportCopyMetric( m m3thrift.Metric, size int32, bucket string, bucketID string, ) { r.pending.Inc() defer r.pending.Dec() if r.done.Load() { return } m.Timestamp = r.now.Load() sm := sizedMetric{ m: m, size: size, set: true, bucket: bucket, bucketID: bucketID, } select { case r.metCh <- sm: case <-r.donech: } } // Flush sends an empty sizedMetric to signal a flush. func (r *reporter) Flush() { r.pending.Inc() defer r.pending.Dec() if r.done.Load() { return } r.reportInternalMetrics() r.metCh <- sizedMetric{} } // Close waits for metrics to be flushed before closing the backend. func (r *reporter) Close() (err error) { if !r.done.CAS(false, true) { return errAlreadyClosed } // Wait for any pending reports to complete. for r.pending.Load() > 0 { runtime.Gosched() } close(r.donech) close(r.metCh) r.wg.Wait() return nil } func (r *reporter) Capabilities() tally.Capabilities { return r } func (r *reporter) Reporting() bool { return true } func (r *reporter) Tagging() bool { return true } func (r *reporter) process() { var ( extraTags = sync.Pool{ New: func() interface{} { return make([]m3thrift.MetricTag, 0, 8) }, } borrowedTags = make([][]m3thrift.MetricTag, 0, 128) mets = make([]m3thrift.Metric, 0, r.freeBytes/10) bytes int32 ) for smet := range r.metCh { flush := !smet.set && len(mets) > 0 if flush || bytes+smet.size > r.freeBytes { r.numMetrics.Add(int64(len(mets))) mets = r.flush(mets) bytes = 0 if len(borrowedTags) > 0 { for i := range borrowedTags { extraTags.Put(borrowedTags[i][:0]) } borrowedTags = borrowedTags[:0] } } if !smet.set { continue } m := smet.m if len(smet.bucket) > 0 { tags := extraTags.Get().([]m3thrift.MetricTag) tags = append(tags, m.Tags...) tags = append( tags, m3thrift.MetricTag{ Name: r.bucketIDTagName, Value: smet.bucketID, }, m3thrift.MetricTag{ Name: r.bucketTagName, Value: smet.bucket, }, ) borrowedTags = append(borrowedTags, tags) m.Tags = tags } mets = append(mets, m) bytes += smet.size } // Final flush r.flush(mets) } func (r *reporter) flush(mets []m3thrift.Metric) []m3thrift.Metric { if len(mets) == 0 { return mets } r.numBatches.Inc() err := r.client.EmitMetricBatchV2(m3thrift.MetricBatch{ Metrics: mets, CommonTags: r.commonTags, }) if err != nil { r.numWriteErrors.Inc() } // n.b. In the event that we had allocated additional tag storage in // process(), clear it so that it can be reclaimed. This does not // affect allocated metrics' tags. for i := range mets { mets[i].Tags = nil } return mets[:0] } func (r *reporter) convertTags(tags map[string]string) []m3thrift.MetricTag { key := cache.TagMapKey(tags) mtags, ok := r.tagCache.Get(key) if !ok { mtags = r.resourcePool.getMetricTagSlice() for k, v := range tags { mtags = append(mtags, m3thrift.MetricTag{ Name: r.stringInterner.Intern(k), Value: r.stringInterner.Intern(v), }) } mtags = r.tagCache.Set(key, mtags) } return mtags } func (r *reporter) reportInternalMetrics() { var ( batches = r.numBatches.Swap(0) metrics = r.numMetrics.Swap(0) writeErrors = r.numWriteErrors.Swap(0) batchSize = float64(metrics) / float64(batches) ) bucket := sort.Search(len(r.buckets), func(i int) bool { return r.buckets[i].UpperBoundValue() >= batchSize }) var value float64 if bucket < len(r.buckets) { value = r.buckets[bucket].UpperBoundValue() } else { value = math.MaxFloat64 } r.batchSizeHistogram.ValueBucket(0, value).ReportSamples(1) r.numBatchesCounter.ReportCount(batches) r.numMetricsCounter.ReportCount(metrics) r.numWriteErrorsCounter.ReportCount(writeErrors) r.numTagCacheCounter.ReportCount(int64(r.tagCache.Len())) } func (r *reporter) timeLoop() { t := time.NewTicker(_timeResolution) defer t.Stop() for !r.done.Load() { r.now.Store(time.Now().UnixNano()) select { case <-t.C: case <-r.donech: return } } } type cachedMetric struct { metric m3thrift.Metric reporter *reporter size int32 } func (c cachedMetric) ReportCount(value int64) { c.metric.Value.Count = value c.reporter.reportCopyMetric(c.metric, c.size, "", "") } func (c cachedMetric) ReportGauge(value float64) { c.metric.Value.Gauge = value c.reporter.reportCopyMetric(c.metric, c.size, "", "") } func (c cachedMetric) ReportTimer(interval time.Duration) { c.metric.Value.Timer = int64(interval) c.reporter.reportCopyMetric(c.metric, c.size, "", "") } type noopMetric struct{} func (c noopMetric) ReportCount(value int64) {} func (c noopMetric) ReportGauge(value float64) {} func (c noopMetric) ReportTimer(interval time.Duration) {} func (c noopMetric) ReportSamples(value int64) {} type cachedHistogram struct { r *reporter name string cachedValueBuckets []cachedHistogramBucket cachedDurationBuckets []cachedHistogramBucket } func (h cachedHistogram) ValueBucket( _ float64, bucketUpperBound float64, ) tally.CachedHistogramBucket { var ( n = len(h.cachedValueBuckets) idx = sort.Search(n, func(i int) bool { return h.cachedValueBuckets[i].valueUpperBound >= bucketUpperBound }) ) if idx == n { return noopMetric{} } var ( b = h.cachedValueBuckets[idx] cm = b.metric m = cm.metric size = cm.size bucket = b.bucket bucketID = b.bucketID rep = cm.reporter ) return reportSamplesFunc(func(value int64) { m.Value.Count = value rep.reportCopyMetric(m, size, bucket, bucketID) }) } func (h cachedHistogram) DurationBucket( _ time.Duration, bucketUpperBound time.Duration, ) tally.CachedHistogramBucket { var ( n = len(h.cachedDurationBuckets) idx = sort.Search(n, func(i int) bool { return h.cachedDurationBuckets[i].durationUpperBound >= bucketUpperBound }) ) if idx == n { return noopMetric{} } var ( b = h.cachedDurationBuckets[idx] cm = b.metric m = cm.metric size = cm.size bucket = b.bucket bucketID = b.bucketID rep = cm.reporter ) return reportSamplesFunc(func(value int64) { m.Value.Count = value rep.reportCopyMetric(m, size, bucket, bucketID) }) } type cachedHistogramBucket struct { metric *cachedMetric durationUpperBound time.Duration valueUpperBound float64 bucket string bucketID string } type reportSamplesFunc func(value int64) func (f reportSamplesFunc) ReportSamples(value int64) { f(value) } type sizedMetric struct { m m3thrift.Metric size int32 set bool bucket string bucketID string } func ndigits(i int) int { n := 1 for i/10 != 0 { n++ i /= 10 } return n } golang-github-uber-go-tally-4.1.11/m3/reporter_benchmark_test.go000066400000000000000000000057021456146544700246130ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package m3 import ( "fmt" "testing" "time" tally "github.com/uber-go/tally/v4" ) func BenchmarkNewMetric(b *testing.B) { r, _ := NewReporter(Options{ HostPorts: []string{"127.0.0.1:9052"}, Service: "test-service", CommonTags: defaultCommonTags, }) defer r.Close() benchReporter := r.(*reporter) b.ResetTimer() for n := 0; n < b.N; n++ { benchReporter.newMetric("foo", nil, counterType) } } func BenchmarkEmitMetrics(b *testing.B) { r, _ := NewReporter(Options{ HostPorts: []string{"127.0.0.1:9052"}, Service: "test-service", CommonTags: defaultCommonTags, }) defer r.Close() benchReporter := r.(*reporter) counters := make([]tally.CachedCount, 100) for i := range counters { counters[i] = r.AllocateCounter(fmt.Sprintf("foo-%v", i), nil /* tags */) } for n := 0; n < b.N; n++ { for _, c := range counters { c.ReportCount(1) } benchReporter.Flush() } } func BenchmarkCalulateSize(b *testing.B) { r, _ := NewReporter(Options{ HostPorts: []string{"127.0.0.1:9052"}, Service: "test-service", CommonTags: defaultCommonTags, }) defer r.Close() benchReporter := r.(*reporter) met := benchReporter.newMetric("foo", map[string]string{"domain": "foo"}, counterType) met.Value.Count = 123456 b.ResetTimer() for n := 0; n < b.N; n++ { benchReporter.calculateSize(met) } } func BenchmarkTimer(b *testing.B) { r, _ := NewReporter(Options{ HostPorts: []string{"127.0.0.1:9052"}, Service: "test-service", CommonTags: defaultCommonTags, MaxQueueSize: DefaultMaxQueueSize, }) defer r.Close() benchReporter := r.(*reporter) go func() { // Blindly consume metrics for range benchReporter.metCh { // nop } }() timer := benchReporter.AllocateTimer("foo", nil) b.ResetTimer() for n := 0; n < b.N; n++ { timer.ReportTimer(time.Duration(n) * time.Millisecond) } b.StopTimer() } golang-github-uber-go-tally-4.1.11/m3/reporter_integration_test.go000066400000000000000000000065421456146544700252070ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package m3 import ( "fmt" "io/ioutil" "os" "os/exec" "path" "path/filepath" "sync" "testing" "github.com/stretchr/testify/require" ) var mainFileFmt = ` package main import ( "time" tally "github.com/uber-go/tally/v4" "github.com/uber-go/tally/v4/m3" ) func main() { r, err := m3.NewReporter(m3.Options{ HostPorts: []string{"%s"}, Service: "test-service", Env: "test", }) if err != nil { panic(err) } scope, closer := tally.NewRootScope(tally.ScopeOptions{ CachedReporter: r, OmitCardinalityMetrics: true, }, 5 * time.Second) defer closer.Close() scope.Counter("my-counter").Inc(42) scope.Gauge("my-gauge").Update(123) scope.Timer("my-timer").Record(456 * time.Millisecond) } ` // TestIntegrationProcessFlushOnExit tests whether data is correctly flushed // when the scope is closed for shortly lived programs func TestIntegrationProcessFlushOnExit(t *testing.T) { for i := 0; i < 5; i++ { testProcessFlushOnExit(t, i) } } func testProcessFlushOnExit(t *testing.T, i int) { dir, err := os.MkdirTemp(".", "foo") require.NoError(t, err) defer os.RemoveAll(dir) dir, err = filepath.Abs(dir) require.NoError(t, err) var wg sync.WaitGroup server := newFakeM3Server(t, &wg, true, Compact) go server.Serve() defer server.Close() mainFile := path.Join(dir, "main.go") mainFileContents := fmt.Sprintf(mainFileFmt, server.Addr) fileErr := ioutil.WriteFile(mainFile, []byte(mainFileContents), 0o666) require.NoError(t, fileErr) binary := path.Join(dir, "m3testemit") // build cmd := exec.Command("go", "build", "-o", binary, mainFile) cmd.Dir = dir output, err := cmd.CombinedOutput() require.NoError(t, err, fmt.Sprintf("output:\n\n%s", output)) // run, do not sleep at end of the main program as per // main program source code wg.Add(1) require.NoError(t, exec.Command(binary).Run()) // Wait for fake M3 server to receive the batch wg.Wait() require.Equal(t, 1, len(server.Service.getBatches())) require.NotNil(t, server.Service.getBatches()[0]) // 3 metrics are emitted by mainFileFmt plus various other internal metrics. require.Equal(t, internalMetrics+3, len(server.Service.getBatches()[0].GetMetrics())) metrics := server.Service.getBatches()[0].GetMetrics() fmt.Printf("Test %d emitted:\n%v\n", i, metrics) } golang-github-uber-go-tally-4.1.11/m3/reporter_test.go000066400000000000000000000526521456146544700226070ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package m3 import ( "bytes" "math/rand" "net" "os" "strconv" "strings" "sync" "sync/atomic" "testing" "time" tally "github.com/uber-go/tally/v4" customtransport "github.com/uber-go/tally/v4/m3/customtransports" m3thrift "github.com/uber-go/tally/v4/m3/thrift/v2" "github.com/uber-go/tally/v4/m3/thriftudp" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) const ( queueSize = 1000 includeHost = true maxPacketSize = int32(1440) shortInterval = 10 * time.Millisecond ) var ( localListenAddr = &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)} defaultCommonTags = map[string]string{"env": "test", "host": "test"} ) var protocols = []Protocol{Compact, Binary} const internalMetrics = 5 // Additional metrics the reporter sends in a batch - use this, not a magic number. const cardinalityMetrics = 4 // Additional metrics emitted by the scope registry. // TestReporter tests the reporter works as expected with both compact and binary protocols func TestReporter(t *testing.T) { for _, protocol := range protocols { var wg sync.WaitGroup server := newFakeM3Server(t, &wg, true, protocol) go server.Serve() defer server.Close() commonTags = map[string]string{ "env": "development", "host": hostname(), "commonTag": "common", "commonTag2": "tag", "commonTag3": "val", } r, err := NewReporter(Options{ HostPorts: []string{server.Addr}, Service: "test-service", CommonTags: commonTags, IncludeHost: includeHost, Protocol: protocol, MaxQueueSize: queueSize, MaxPacketSizeBytes: maxPacketSize, }) require.NoError(t, err) defer func() { assert.NoError(t, r.Close()) }() tags := map[string]string{"testTag": "TestValue", "testTag2": "TestValue2"} wg.Add(2) r.AllocateCounter("my-counter", tags).ReportCount(10) r.Flush() r.AllocateTimer("my-timer", tags).ReportTimer(5 * time.Millisecond) r.Flush() wg.Wait() batches := server.Service.getBatches() require.Equal(t, 2, len(batches)) // Validate common tags for _, batch := range batches { require.NotNil(t, batch) require.True(t, batch.IsSetCommonTags()) require.Equal(t, len(commonTags)+1, len(batch.GetCommonTags())) for _, tag := range batch.GetCommonTags() { if tag.GetName() == ServiceTag { require.Equal(t, "test-service", tag.GetValue()) } else { require.Equal(t, commonTags[tag.GetName()], tag.GetValue()) } } } // Validate metrics emittedCounters := batches[0].GetMetrics() require.Equal(t, internalMetrics+1, len(emittedCounters)) emittedTimers := batches[1].GetMetrics() require.Equal(t, internalMetrics+1, len(emittedTimers)) emittedCounter, emittedTimer := emittedCounters[0], emittedTimers[0] if emittedCounter.GetName() == "my-timer" { emittedCounter, emittedTimer = emittedTimer, emittedCounter } require.Equal(t, "my-counter", emittedCounter.GetName()) require.True(t, emittedCounter.IsSetTags()) require.Equal(t, len(tags), len(emittedCounter.GetTags())) for _, tag := range emittedCounter.GetTags() { require.Equal(t, tags[tag.GetName()], tag.GetValue()) } require.True(t, emittedCounter.IsSetValue()) emittedVal := emittedCounter.GetValue() emittedCount := emittedVal.GetCount() require.EqualValues(t, int64(10), emittedCount) require.True(t, emittedTimer.IsSetValue()) emittedVal = emittedTimer.GetValue() emittedTimerVal := emittedVal.GetTimer() require.EqualValues(t, int64(5*1000*1000), emittedTimerVal) } } // TestMultiReporter tests the multi Reporter works as expected func TestMultiReporter(t *testing.T) { dests := []string{"127.0.0.1:9052", "127.0.0.1:9053"} commonTags := map[string]string{ "env": "test", "host": "test", "commonTag": "common", "commonTag2": "tag", "commonTag3": "val", } r, err := NewReporter(Options{ HostPorts: dests, Service: "test-service", CommonTags: commonTags, }) require.NoError(t, err) defer r.Close() reporter, ok := r.(*reporter) require.True(t, ok) multitransport, ok := reporter.client.Transport.(*thriftudp.TMultiUDPTransport) require.NotNil(t, multitransport) require.True(t, ok) } // TestNewReporterErrors tests for Reporter creation errors func TestNewReporterErrors(t *testing.T) { var err error // Test freeBytes (maxPacketSizeBytes - numOverheadBytes) is negative _, err = NewReporter(Options{ HostPorts: []string{"127.0.0.1"}, Service: "test-service", MaxQueueSize: 10, MaxPacketSizeBytes: 2 << 5, }) assert.Error(t, err) // Test invalid addr _, err = NewReporter(Options{ HostPorts: []string{"fakeAddress"}, Service: "test-service", }) assert.Error(t, err) } // TestReporterRaceCondition checks if therem is race condition between reporter closing // and metric reporting, when run with race detector on, this test should pass func TestReporterRaceCondition(t *testing.T) { r, err := NewReporter(Options{ HostPorts: []string{"localhost:8888"}, Service: "test-service", CommonTags: defaultCommonTags, MaxQueueSize: queueSize, MaxPacketSizeBytes: maxPacketSize, }) require.NoError(t, err) go func() { r.AllocateTimer("my-timer", nil).ReportTimer(10 * time.Millisecond) }() r.Close() } // TestReporterFinalFlush ensures the Reporter emits the last batch of metrics // after close func TestReporterFinalFlush(t *testing.T) { var wg sync.WaitGroup server := newFakeM3Server(t, &wg, true, Compact) go server.Serve() defer server.Close() r, err := NewReporter(Options{ HostPorts: []string{server.Addr}, Service: "test-service", CommonTags: defaultCommonTags, MaxQueueSize: queueSize, MaxPacketSizeBytes: maxPacketSize, }) require.NoError(t, err) wg.Add(1) r.AllocateTimer("my-timer", nil).ReportTimer(10 * time.Millisecond) r.Close() wg.Wait() require.Equal(t, 1, len(server.Service.getBatches())) require.NotNil(t, server.Service.getBatches()[0]) require.Equal(t, 1, len(server.Service.getBatches()[0].GetMetrics())) } // TestReporterNoPanicOnTimerAfterClose ensure the reporter avoids panic // after close of the reporter when emitting a timer value func TestReporterNoPanicOnTimerAfterClose(t *testing.T) { server := newFakeM3Server(t, &sync.WaitGroup{}, true, Compact) go server.Serve() defer server.Close() r, err := NewReporter(Options{ HostPorts: []string{server.Addr}, Service: "test-service", CommonTags: defaultCommonTags, MaxQueueSize: queueSize, MaxPacketSizeBytes: maxPacketSize, }) require.NoError(t, err) timer := r.AllocateTimer("my-timer", nil) r.Close() assert.NotPanics(t, func() { timer.ReportTimer(time.Millisecond) }) } // TestReporterNoPanicOnFlushAfterClose ensure the reporter avoids panic // after close of the reporter when calling flush func TestReporterNoPanicOnFlushAfterClose(t *testing.T) { server := newFakeM3Server(t, &sync.WaitGroup{}, true, Compact) go server.Serve() defer server.Close() r, err := NewReporter(Options{ HostPorts: []string{server.Addr}, Service: "test-service", CommonTags: defaultCommonTags, MaxQueueSize: queueSize, MaxPacketSizeBytes: maxPacketSize, }) require.NoError(t, err) r.Close() assert.NotPanics(t, func() { r.Flush() }) } func TestReporterHistogram(t *testing.T) { var wg sync.WaitGroup server := newFakeM3Server(t, &wg, true, Compact) go server.Serve() defer server.Close() r, err := NewReporter(Options{ HostPorts: []string{server.Addr}, Service: "test-service", CommonTags: defaultCommonTags, MaxQueueSize: queueSize, MaxPacketSizeBytes: maxPacketSize, }) require.NoError(t, err) wg.Add(1) h := r.AllocateHistogram("my-histogram", map[string]string{ "foo": "bar", }, tally.DurationBuckets{ 0 * time.Millisecond, 25 * time.Millisecond, 50 * time.Millisecond, 75 * time.Millisecond, 100 * time.Millisecond, }) b := h.DurationBucket(0*time.Millisecond, 25*time.Millisecond) b.ReportSamples(7) b = h.DurationBucket(50*time.Millisecond, 75*time.Millisecond) b.ReportSamples(3) r.Close() wg.Wait() require.Equal(t, 1, len(server.Service.getBatches())) require.NotNil(t, server.Service.getBatches()[0]) require.Equal(t, 2, len(server.Service.getBatches()[0].GetMetrics())) // Verify first bucket counter := server.Service.getBatches()[0].GetMetrics()[0] require.Equal(t, "my-histogram", counter.GetName()) require.True(t, counter.IsSetTags()) for _, tag := range counter.GetTags() { require.Equal(t, map[string]string{ "foo": "bar", "bucketid": "0001", "bucket": "0-25ms", }[tag.GetName()], tag.GetValue()) } require.Equal(t, 3, len(counter.GetTags())) require.True(t, counter.IsSetValue()) val := counter.GetValue() count := val.GetCount() require.Equal(t, int64(7), count) // Verify second bucket counter = server.Service.getBatches()[0].GetMetrics()[1] require.Equal(t, "my-histogram", counter.GetName()) require.True(t, counter.IsSetTags()) require.Equal(t, 3, len(counter.GetTags())) for _, tag := range counter.GetTags() { require.Equal(t, map[string]string{ "foo": "bar", "bucketid": "0003", "bucket": "50ms-75ms", }[tag.GetName()], tag.GetValue()) } require.True(t, counter.IsSetValue()) val = counter.GetValue() count = val.GetCount() require.Equal(t, int64(3), count) } func TestBatchSizes(t *testing.T) { server := newFakeM3Server(t, nil, false, Compact) go server.Serve() defer server.Close() commonTags := map[string]string{ "env": "test", "domain": "pod" + strconv.Itoa(rand.Intn(100)), } maxPacketSize := int32(1440) r, err := NewReporter(Options{ HostPorts: []string{server.Addr}, Service: "test-service", CommonTags: commonTags, MaxQueueSize: 10000, MaxPacketSizeBytes: maxPacketSize, }) require.NoError(t, err) rand.Seed(time.Now().UnixNano()) var stop uint32 go func() { var ( counters = make(map[string]tally.CachedCount) gauges = make(map[string]tally.CachedGauge) timers = make(map[string]tally.CachedTimer) randTags = func() map[string]string { return map[string]string{ "t1": "val" + strconv.Itoa(rand.Intn(10000)), } } ) for atomic.LoadUint32(&stop) == 0 { metTypeRand := rand.Intn(9) name := "size.test.metric.name" + strconv.Itoa(rand.Intn(50)) if metTypeRand <= 2 { _, ok := counters[name] if !ok { counters[name] = r.AllocateCounter(name, randTags()) } counters[name].ReportCount(rand.Int63n(10000)) } else if metTypeRand <= 5 { _, ok := gauges[name] if !ok { gauges[name] = r.AllocateGauge(name, randTags()) } gauges[name].ReportGauge(rand.Float64() * 10000) } else { _, ok := timers[name] if !ok { timers[name] = r.AllocateTimer(name, randTags()) } timers[name].ReportTimer(time.Duration(rand.Int63n(10000))) } } r.Close() }() for len(server.Packets()) < 100 { time.Sleep(shortInterval) } atomic.StoreUint32(&stop, 1) for _, packet := range server.Packets() { require.True(t, len(packet) < int(maxPacketSize)) } } func TestReporterSpecifyService(t *testing.T) { commonTags := map[string]string{ ServiceTag: "overrideService", EnvTag: "test", HostTag: "overrideHost", } r, err := NewReporter(Options{ HostPorts: []string{"127.0.0.1:1000"}, Service: "test-service", CommonTags: commonTags, IncludeHost: includeHost, MaxQueueSize: 10, MaxPacketSizeBytes: 100, }) require.NoError(t, err) defer r.Close() reporter, ok := r.(*reporter) require.True(t, ok) assert.Equal(t, 3, len(reporter.commonTags)) for _, tag := range reporter.commonTags { switch tag.GetName() { case ServiceTag: assert.Equal(t, "overrideService", tag.GetValue()) case EnvTag: assert.Equal(t, "test", tag.GetValue()) case HostTag: assert.Equal(t, "overrideHost", tag.GetValue()) } } } func TestIncludeHost(t *testing.T) { var wg sync.WaitGroup server := newFakeM3Server(t, &wg, true, Compact) go server.Serve() defer server.Close() commonTags := map[string]string{"env": "test"} r, err := NewReporter(Options{ HostPorts: []string{server.Addr}, Service: "test-service", CommonTags: commonTags, IncludeHost: false, }) require.NoError(t, err) defer r.Close() withoutHost, ok := r.(*reporter) require.True(t, ok) assert.False(t, tagIncluded(withoutHost.commonTags, "host")) r, err = NewReporter(Options{ HostPorts: []string{server.Addr}, Service: "test-service", CommonTags: commonTags, IncludeHost: true, }) require.NoError(t, err) defer r.Close() withHost, ok := r.(*reporter) require.True(t, ok) assert.True(t, tagIncluded(withHost.commonTags, "host")) } func TestReporterResetTagsAfterReturnToPool(t *testing.T) { var wg sync.WaitGroup server := newFakeM3Server(t, &wg, false, Compact) go server.Serve() defer server.Close() r, err := NewReporter(Options{ HostPorts: []string{server.Addr}, Service: "test-service", CommonTags: defaultCommonTags, MaxQueueSize: queueSize, MaxPacketSizeBytes: maxPacketSize, }) require.NoError(t, err) defer r.Close() // Intentionally allocate and leak counters to exhaust metric pool. for i := 0; i < metricPoolSize-2; i++ { r.AllocateCounter("placeholder", nil) } // Allocate two counter so there is only one more slot in the pool. tags := map[string]string{"tagName1": "tagValue1"} c1 := r.AllocateCounter("counterWithTags", tags) // Report the counter with tags to take the last slot. wg.Add(internalMetrics + 1) c1.ReportCount(1) r.Flush() wg.Wait() // Empty flush to ensure the copied metric is released. wg.Add(internalMetrics) r.Flush() for { rep := r.(*reporter) if len(rep.metCh) == 0 { break } time.Sleep(5 * time.Millisecond) } // Allocate a new counter with no tags reusing the metric // just released to the pool. c2 := r.AllocateCounter("counterWithNoTags", nil) // Report the counter with no tags. wg.Add(internalMetrics + 1) c2.ReportCount(1) r.Flush() wg.Wait() // Verify that first reported counter has tags and the second // reported counter has no tags. metrics := server.Service.getMetrics() require.Equal(t, 2+3*internalMetrics, len(metrics)) // 2 test metrics, 3 rounds of internal metrics var filtered []m3thrift.Metric for _, metric := range metrics { if !strings.HasPrefix(metric.Name, "tally.internal") { filtered = append(filtered, metric) } } require.Equal(t, 2, len(filtered)) require.Equal(t, len(tags), len(filtered[0].GetTags())) for _, tag := range filtered[0].GetTags() { require.Equal(t, tags[tag.GetName()], tag.GetValue()) } require.Equal(t, 0, len(filtered[1].GetTags())) } func TestReporterCommmonTagsInternal(t *testing.T) { var wg sync.WaitGroup server := newFakeM3Server(t, &wg, false, Compact) go server.Serve() defer server.Close() internalTags := map[string]string{ "internal1": "test1", "internal2": "test2", } r, err := NewReporter(Options{ HostPorts: []string{server.Addr}, Service: "test-service", CommonTags: defaultCommonTags, MaxQueueSize: queueSize, IncludeHost: true, MaxPacketSizeBytes: maxPacketSize, InternalTags: internalTags, }) require.NoError(t, err) defer r.Close() c := r.AllocateCounter("testCounter1", nil) c.ReportCount(1) wg.Add(internalMetrics + 1) r.Flush() wg.Wait() numInternalMetricsActual := 0 metrics := server.Service.getMetrics() require.Equal(t, internalMetrics+1, len(metrics)) for _, metric := range metrics { if strings.HasPrefix(metric.Name, "tally.internal") { numInternalMetricsActual++ for k, v := range internalTags { require.True(t, tagEquals(metric.Tags, k, v)) } // The following tags should be redacted. require.True(t, tagEquals(metric.Tags, "host", tally.DefaultTagRedactValue)) require.True(t, tagEquals(metric.Tags, "instance", tally.DefaultTagRedactValue)) } else { require.Equal(t, "testCounter1", metric.Name) require.False(t, tagIncluded(metric.Tags, "internal1")) require.False(t, tagIncluded(metric.Tags, "internal2")) } // The following tags should not be present as part of the individual metrics // as they are common tags. require.False(t, tagIncluded(metric.Tags, "service")) } require.Equal(t, internalMetrics, numInternalMetricsActual) } func TestReporterHasReportingAndTaggingCapability(t *testing.T) { r, err := NewReporter(Options{ HostPorts: []string{"127.0.0.1:9052"}, Service: "test-service", CommonTags: defaultCommonTags, }) require.NoError(t, err) assert.True(t, r.Capabilities().Reporting()) assert.True(t, r.Capabilities().Tagging()) } type fakeM3Server struct { t *testing.T Service *fakeM3Service Addr string protocol Protocol processor thrift.TProcessor conn *net.UDPConn closed int32 packets fakeM3ServerPackets } type fakeM3ServerPackets struct { sync.RWMutex values [][]byte } // newFakeM3Server creates a new fake M3 server that listens on a random port // and returns the server. // The server will wait for the given wait group to be done before returning. // If countBatches is true, the server will wait consider the wg.Add()s to be // representing batches and will do a eg.Done() for each encountered batch. // But if countBatches is false, the server will do the same thing but for individual // metrics instead of batches. func newFakeM3Server(t *testing.T, wg *sync.WaitGroup, countBatches bool, protocol Protocol) *fakeM3Server { service := newFakeM3Service(wg, countBatches) processor := m3thrift.NewM3Processor(service) conn, err := net.ListenUDP(localListenAddr.Network(), localListenAddr) require.NoError(t, err, "ListenUDP failed") return &fakeM3Server{ t: t, Service: service, Addr: conn.LocalAddr().String(), conn: conn, protocol: protocol, processor: processor, } } func (f *fakeM3Server) Serve() { readBuf := make([]byte, 64000) for f.conn != nil { n, err := f.conn.Read(readBuf) if err != nil { if atomic.LoadInt32(&f.closed) == 0 { f.t.Errorf("FakeM3Server failed to Read: %v", err) } return } f.packets.Lock() f.packets.values = append(f.packets.values, readBuf[0:n]) f.packets.Unlock() trans, _ := customtransport.NewTBufferedReadTransport(bytes.NewBuffer(readBuf[0:n])) var proto thrift.TProtocol if f.protocol == Compact { proto = thrift.NewTCompactProtocol(trans) } else { proto = thrift.NewTBinaryProtocolTransport(trans) } _, err = f.processor.Process(proto, proto) if terr, ok := err.(thrift.TTransportException); ok { require.Equal(f.t, thrift.END_OF_FILE, terr.TypeId()) } else { require.NoError(f.t, err) } } } func (f *fakeM3Server) Close() error { atomic.AddInt32(&f.closed, 1) return f.conn.Close() } func (f *fakeM3Server) Packets() [][]byte { f.packets.Lock() defer f.packets.Unlock() packets := make([][]byte, len(f.packets.values)) copy(packets, f.packets.values) return packets } func newFakeM3Service(wg *sync.WaitGroup, countBatches bool) *fakeM3Service { return &fakeM3Service{wg: wg, countBatches: countBatches} } type fakeM3Service struct { lock sync.RWMutex batches []m3thrift.MetricBatch metrics []m3thrift.Metric wg *sync.WaitGroup countBatches bool } func (m *fakeM3Service) getBatches() []m3thrift.MetricBatch { m.lock.RLock() defer m.lock.RUnlock() return m.batches } func (m *fakeM3Service) getMetrics() []m3thrift.Metric { m.lock.RLock() defer m.lock.RUnlock() return m.metrics } func (m *fakeM3Service) EmitMetricBatchV2(batch m3thrift.MetricBatch) (err error) { m.lock.Lock() m.batches = append(m.batches, batch) if m.wg != nil && m.countBatches { m.wg.Done() } for _, metric := range batch.Metrics { m.metrics = append(m.metrics, metric) if m.wg != nil && !m.countBatches { m.wg.Done() } } m.lock.Unlock() return thrift.NewTTransportException(thrift.END_OF_FILE, "complete") } func hostname() string { host, err := os.Hostname() if err != nil { host = "unknown" } return host } func tagIncluded(tags []m3thrift.MetricTag, tagName string) bool { for _, tag := range tags { if tag.Name == tagName { return true } } return false } func tagEquals(tags []m3thrift.MetricTag, tagName string, tagValue string) bool { for _, tag := range tags { if tag.GetName() == tagName && tag.GetValue() == tagValue { return true } } return false } golang-github-uber-go-tally-4.1.11/m3/resource_pool.go000066400000000000000000000061141456146544700225560ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package m3 import ( tally "github.com/uber-go/tally/v4" customtransport "github.com/uber-go/tally/v4/m3/customtransports" m3thrift "github.com/uber-go/tally/v4/m3/thrift/v2" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" ) const ( batchPoolSize = 10 metricPoolSize = DefaultMaxQueueSize protoPoolSize = 10 ) type resourcePool struct { metricSlicePool *tally.ObjectPool metricTagSlicePool *tally.ObjectPool protoPool *tally.ObjectPool } func newResourcePool(protoFac thrift.TProtocolFactory) *resourcePool { metricSlicePool := tally.NewObjectPool(batchPoolSize) metricSlicePool.Init(func() interface{} { return make([]m3thrift.Metric, 0, batchPoolSize) }) metricTagSlicePool := tally.NewObjectPool(DefaultMaxQueueSize) metricTagSlicePool.Init(func() interface{} { return make([]m3thrift.MetricTag, 0, batchPoolSize) }) protoPool := tally.NewObjectPool(protoPoolSize) protoPool.Init(func() interface{} { return protoFac.GetProtocol(&customtransport.TCalcTransport{}) }) return &resourcePool{ metricSlicePool: metricSlicePool, metricTagSlicePool: metricTagSlicePool, protoPool: protoPool, } } func (r *resourcePool) getMetricSlice() []m3thrift.Metric { return r.metricSlicePool.Get().([]m3thrift.Metric) } func (r *resourcePool) getMetricTagSlice() []m3thrift.MetricTag { return r.metricTagSlicePool.Get().([]m3thrift.MetricTag) } func (r *resourcePool) getProto() thrift.TProtocol { o := r.protoPool.Get() return o.(thrift.TProtocol) } //nolint:unused func (r *resourcePool) releaseProto(proto thrift.TProtocol) { calc := proto.Transport().(*customtransport.TCalcTransport) calc.ResetCount() r.protoPool.Put(proto) } func (r *resourcePool) releaseMetricSlice(metrics []m3thrift.Metric) { for i := 0; i < len(metrics); i++ { metrics[i].Tags = nil } r.metricSlicePool.Put(metrics[:0]) } //nolint:unused func (r *resourcePool) releaseMetricTagSlice(tags []m3thrift.MetricTag) { r.metricSlicePool.Put(tags[:0]) } golang-github-uber-go-tally-4.1.11/m3/resource_pool_test.go000066400000000000000000000034671456146544700236250ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package m3 import ( "testing" "github.com/stretchr/testify/require" m3thrift "github.com/uber-go/tally/v4/m3/thrift/v2" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" ) func TestM3ResourcePoolMetric(t *testing.T) { p := newResourcePool(thrift.NewTCompactProtocolFactory()) metrics := p.getMetricSlice() metrics = append(metrics, m3thrift.Metric{}) require.Equal(t, 1, len(metrics)) p.releaseMetricSlice(metrics) metrics = p.getMetricSlice() require.Equal(t, 0, len(metrics)) tags := p.getMetricTagSlice() tags = append(tags, m3thrift.MetricTag{}) require.Equal(t, 1, len(tags)) p.releaseMetricTagSlice(tags) tags = p.getMetricTagSlice() require.Equal(t, 0, len(tags)) } golang-github-uber-go-tally-4.1.11/m3/sanitize.go000066400000000000000000000033341456146544700215250ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package m3 import ( tally "github.com/uber-go/tally/v4" ) // DefaultSanitizerOpts are the options for the default M3 sanitizer. var DefaultSanitizerOpts = tally.SanitizeOptions{ NameCharacters: tally.ValidCharacters{ Ranges: tally.AlphanumericRange, Characters: tally.UnderscoreDashDotCharacters, }, KeyCharacters: tally.ValidCharacters{ Ranges: tally.AlphanumericRange, Characters: tally.UnderscoreDashCharacters, }, ValueCharacters: tally.ValidCharacters{ Ranges: tally.AlphanumericRange, Characters: tally.UnderscoreDashDotCharacters, }, ReplacementCharacter: tally.DefaultReplacementCharacter, } golang-github-uber-go-tally-4.1.11/m3/scope_test.go000066400000000000000000000142611456146544700220500ustar00rootroot00000000000000// Copyright (c) 2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package m3 import ( "sync" "testing" "time" "github.com/stretchr/testify/require" tally "github.com/uber-go/tally/v4" ) var commonTags = map[string]string{"env": "test"} type doneFn func() func newTestReporterScope( t *testing.T, addr string, scopePrefix string, scopeTags map[string]string, ) (Reporter, tally.Scope, doneFn) { r, err := NewReporter(Options{ HostPorts: []string{addr}, Service: "testService", CommonTags: commonTags, IncludeHost: includeHost, MaxQueueSize: queueSize, MaxPacketSizeBytes: maxPacketSize, }) require.NoError(t, err) scope, closer := tally.NewRootScope(tally.ScopeOptions{ Prefix: scopePrefix, Tags: scopeTags, CachedReporter: r, OmitCardinalityMetrics: false, }, shortInterval) return r, scope, func() { require.NoError(t, closer.Close()) require.True(t, r.(*reporter).done.Load()) } } // TestScope tests that scope works as expected func TestScope(t *testing.T) { var wg sync.WaitGroup server := newFakeM3Server(t, &wg, true, Compact) go server.Serve() defer server.Close() tags := map[string]string{"testTag": "TestValue", "testTag2": "TestValue2"} _, scope, close := newTestReporterScope(t, server.Addr, "honk", tags) wg.Add(1) timer := scope.Timer("dazzle") timer.Start().Stop() close() wg.Wait() require.Equal(t, 1, len(server.Service.getBatches())) require.NotNil(t, server.Service.getBatches()[0]) emittedTimers := server.Service.getBatches()[0].GetMetrics() require.Equal(t, internalMetrics+cardinalityMetrics+1, len(emittedTimers)) require.Equal(t, "honk.dazzle", emittedTimers[0].GetName()) } // TestScopeCounter tests that scope works as expected func TestScopeCounter(t *testing.T) { var wg sync.WaitGroup server := newFakeM3Server(t, &wg, true, Compact) go server.Serve() defer server.Close() tags := map[string]string{"testTag": "TestValue", "testTag2": "TestValue2"} _, scope, close := newTestReporterScope(t, server.Addr, "honk", tags) wg.Add(1) counter := scope.Counter("foobar") counter.Inc(42) close() wg.Wait() require.Equal(t, 1, len(server.Service.getBatches())) require.NotNil(t, server.Service.getBatches()[0]) emittedMetrics := server.Service.getBatches()[0].GetMetrics() require.Equal(t, internalMetrics+cardinalityMetrics+1, len(emittedMetrics)) require.Equal(t, "honk.foobar", emittedMetrics[cardinalityMetrics].GetName()) } // TestScopeGauge tests that scope works as expected func TestScopeGauge(t *testing.T) { var wg sync.WaitGroup server := newFakeM3Server(t, &wg, true, Compact) go server.Serve() defer server.Close() tags := map[string]string{"testTag": "TestValue", "testTag2": "TestValue2"} _, scope, close := newTestReporterScope(t, server.Addr, "honk", tags) wg.Add(1) gauge := scope.Gauge("foobaz") gauge.Update(42) close() wg.Wait() require.Equal(t, 1, len(server.Service.getBatches())) require.NotNil(t, server.Service.getBatches()[0]) emittedMetrics := server.Service.getBatches()[0].GetMetrics() require.Equal(t, internalMetrics+cardinalityMetrics+1, len(emittedMetrics)) require.Equal(t, "honk.foobaz", emittedMetrics[cardinalityMetrics].GetName()) } func BenchmarkScopeReportTimer(b *testing.B) { backend, err := NewReporter(Options{ HostPorts: []string{"127.0.0.1:4444"}, Service: "my-service", MaxQueueSize: 10000, MaxPacketSizeBytes: maxPacketSize, }) if err != nil { b.Error(err.Error()) return } scope, closer := tally.NewRootScope(tally.ScopeOptions{ Prefix: "bench", CachedReporter: backend, }, 1*time.Second) perEndpointScope := scope.Tagged( map[string]string{ "endpointid": "health", "handlerid": "health", }, ) timer := perEndpointScope.Timer("inbound.latency") b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { timer.Record(500) } }) b.StopTimer() closer.Close() b.StartTimer() } func BenchmarkScopeReportHistogram(b *testing.B) { backend, err := NewReporter(Options{ HostPorts: []string{"127.0.0.1:4444"}, Service: "my-service", MaxQueueSize: 10000, MaxPacketSizeBytes: maxPacketSize, Env: "test", }) if err != nil { b.Error(err.Error()) return } scope, closer := tally.NewRootScope(tally.ScopeOptions{ Prefix: "bench", CachedReporter: backend, }, 1*time.Second) perEndpointScope := scope.Tagged( map[string]string{ "endpointid": "health", "handlerid": "health", }, ) buckets := tally.DurationBuckets{ 0 * time.Millisecond, 10 * time.Millisecond, 25 * time.Millisecond, 50 * time.Millisecond, 75 * time.Millisecond, 100 * time.Millisecond, 200 * time.Millisecond, 300 * time.Millisecond, 400 * time.Millisecond, 500 * time.Millisecond, 600 * time.Millisecond, 800 * time.Millisecond, 1 * time.Second, 2 * time.Second, 5 * time.Second, } histogram := perEndpointScope.Histogram("inbound.latency", buckets) b.ReportAllocs() b.ResetTimer() bucketsLen := len(buckets) for i := 0; i < b.N; i++ { histogram.RecordDuration(buckets[i%bucketsLen]) } b.StopTimer() closer.Close() } golang-github-uber-go-tally-4.1.11/m3/thrift/000077500000000000000000000000001456146544700206455ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/m3/thrift/Makefile000066400000000000000000000007441456146544700223120ustar00rootroot00000000000000gen-thrift: @thrift --gen go:thrift_import="github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" -out . v1/v1.thrift && rm -rf v1/m3-remote @echo Generated v1 Go Thrift in metrics/m3/thrift/v1. @thrift --gen go:thrift_import="github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" -out . v2/v2.thrift && rm -rf v2/m3-remote @echo Generated v2 Go Thrift in metrics/m3/thrift/v2. @git apply thrift.diff @echo Applied thrift.diff. golang-github-uber-go-tally-4.1.11/m3/thrift/thrift.diff000066400000000000000000000146161456146544700230070ustar00rootroot00000000000000diff --git a/m3/thrift/v2/m3.go b/m3/thrift/v2/m3.go index fc46a31..f0a288c 100644 --- a/m3/thrift/v2/m3.go +++ b/m3/thrift/v2/m3.go @@ -17,7 +17,7 @@ var _ = bytes.Equal type M3 interface { // Parameters: // - Batch - EmitMetricBatchV2(batch *MetricBatch) (err error) + EmitMetricBatchV2(batch MetricBatch) (err error) } type M3Client struct { @@ -48,14 +48,14 @@ func NewM3ClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thri // Parameters: // - Batch -func (p *M3Client) EmitMetricBatchV2(batch *MetricBatch) (err error) { +func (p *M3Client) EmitMetricBatchV2(batch MetricBatch) (err error) { if err = p.sendEmitMetricBatchV2(batch); err != nil { return } return } -func (p *M3Client) sendEmitMetricBatchV2(batch *MetricBatch) (err error) { +func (p *M3Client) sendEmitMetricBatchV2(batch MetricBatch) (err error) { oprot := p.OutputProtocol if oprot == nil { oprot = p.ProtocolFactory.GetProtocol(p.Transport) @@ -145,23 +145,23 @@ func (p *m3ProcessorEmitMetricBatchV2) Process(seqId int32, iprot, oprot thrift.TP // Attributes: // - Batch type M3EmitMetricBatchV2Args struct { - Batch *MetricBatch `thrift:"batch,1" json:"batch"` + Batch MetricBatch `thrift:"batch,1" json:"batch"` } func NewM3EmitMetricBatchV2Args() *M3EmitMetricBatchV2Args { return &M3EmitMetricBatchV2Args{} } -var M3EmitMetricBatchV2Args_Batch_DEFAULT *MetricBatch +var M3EmitMetricBatchV2Args_Batch_DEFAULT MetricBatch -func (p *M3EmitMetricBatchV2Args) GetBatch() *MetricBatch { +func (p *M3EmitMetricBatchV2Args) GetBatch() MetricBatch { if !p.IsSetBatch() { return M3EmitMetricBatchV2Args_Batch_DEFAULT } return p.Batch } func (p *M3EmitMetricBatchV2Args) IsSetBatch() bool { - return p.Batch != nil + return p.Batch.Metrics != nil || p.Batch.CommonTags != nil } func (p *M3EmitMetricBatchV2Args) Read(iprot thrift.TProtocol) error { @@ -198,7 +198,7 @@ func (p *M3EmitMetricBatchV2Args) Read(iprot thrift.TProtocol) error { } func (p *M3EmitMetricBatchV2Args) readField1(iprot thrift.TProtocol) error { - p.Batch = &MetricBatch{} + p.Batch = MetricBatch{} if err := p.Batch.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err) } diff --git a/m3/thrift/v2/ttypes.go b/m3/thrift/v2/ttypes.go index 5f83caa..3e6c33b 100644 --- a/m3/thrift/v2/ttypes.go +++ b/m3/thrift/v2/ttypes.go @@ -429,10 +429,10 @@ func (p *MetricTag) String() string { // - Timestamp // - Tags type Metric struct { - Name string `thrift:"name,1,required" json:"name"` - Value *MetricValue `thrift:"value,2,required" json:"value"` - Timestamp int64 `thrift:"timestamp,3,required" json:"timestamp"` - Tags []*MetricTag `thrift:"tags,4" json:"tags,omitempty"` + Name string `thrift:"name,1,required" json:"name"` + Value MetricValue `thrift:"value,2,required" json:"value"` + Timestamp int64 `thrift:"timestamp,3,required" json:"timestamp"` + Tags []MetricTag `thrift:"tags,4" json:"tags,omitempty"` } func NewMetric() *Metric { @@ -443,9 +443,9 @@ func (p *Metric) GetName() string { return p.Name } -var Metric_Value_DEFAULT *MetricValue +var Metric_Value_DEFAULT MetricValue -func (p *Metric) GetValue() *MetricValue { +func (p *Metric) GetValue() MetricValue { if !p.IsSetValue() { return Metric_Value_DEFAULT } @@ -456,13 +456,13 @@ func (p *Metric) GetTimestamp() int64 { return p.Timestamp } -var Metric_Tags_DEFAULT []*MetricTag +var Metric_Tags_DEFAULT []MetricTag -func (p *Metric) GetTags() []*MetricTag { +func (p *Metric) GetTags() []MetricTag { return p.Tags } func (p *Metric) IsSetValue() bool { - return p.Value != nil + return p.Value.GetMetricType() != MetricType_INVALID || p.Value.Count != 0 || p.Value.Gauge != 0 || p.Value.Timer != 0 } func (p *Metric) IsSetTags() bool { @@ -540,7 +540,7 @@ func (p *Metric) readField1(iprot thrift.TProtocol) error { } func (p *Metric) readField2(iprot thrift.TProtocol) error { - p.Value = &MetricValue{} + p.Value = MetricValue{} if err := p.Value.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Value), err) } @@ -561,10 +561,10 @@ func (p *Metric) readField4(iprot thrift.TProtocol) error { if err != nil { return thrift.PrependError("error reading list begin: ", err) } - tSlice := make([]*MetricTag, 0, size) + tSlice := make([]MetricTag, 0, size) p.Tags = tSlice for i := 0; i < size; i++ { - _elem0 := &MetricTag{} + _elem0 := MetricTag{} if err := _elem0.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) } @@ -674,21 +674,21 @@ func (p *Metric) String() string { // - Metrics // - CommonTags type MetricBatch struct { - Metrics []*Metric `thrift:"metrics,1,required" json:"metrics"` - CommonTags []*MetricTag `thrift:"commonTags,2" json:"commonTags,omitempty"` + Metrics []Metric `thrift:"metrics,1,required" json:"metrics"` + CommonTags []MetricTag `thrift:"commonTags,2" json:"commonTags,omitempty"` } func NewMetricBatch() *MetricBatch { return &MetricBatch{} } -func (p *MetricBatch) GetMetrics() []*Metric { +func (p *MetricBatch) GetMetrics() []Metric { return p.Metrics } -var MetricBatch_CommonTags_DEFAULT []*MetricTag +var MetricBatch_CommonTags_DEFAULT []MetricTag -func (p *MetricBatch) GetCommonTags() []*MetricTag { +func (p *MetricBatch) GetCommonTags() []MetricTag { return p.CommonTags } func (p *MetricBatch) IsSetCommonTags() bool { @@ -743,10 +743,10 @@ func (p *MetricBatch) readField1(iprot thrift.TProtocol) error { if err != nil { return thrift.PrependError("error reading list begin: ", err) } - tSlice := make([]*Metric, 0, size) + tSlice := make([]Metric, 0, size) p.Metrics = tSlice for i := 0; i < size; i++ { - _elem1 := &Metric{} + _elem1 := Metric{} if err := _elem1.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) } @@ -763,10 +763,10 @@ func (p *MetricBatch) readField2(iprot thrift.TProtocol) error { if err != nil { return thrift.PrependError("error reading list begin: ", err) } - tSlice := make([]*MetricTag, 0, size) + tSlice := make([]MetricTag, 0, size) p.CommonTags = tSlice for i := 0; i < size; i++ { - _elem2 := &MetricTag{} + _elem2 := MetricTag{} if err := _elem2.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem2), err) } golang-github-uber-go-tally-4.1.11/m3/thrift/v1/000077500000000000000000000000001456146544700211735ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/m3/thrift/v1/constants.go000066400000000000000000000005761456146544700235460ustar00rootroot00000000000000// Autogenerated by Thrift Compiler (0.9.3) // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING package v1 import ( "bytes" "fmt" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" ) // (needed to ensure safety because of naive import list construction.) var _ = thrift.ZERO var _ = fmt.Printf var _ = bytes.Equal func init() { } golang-github-uber-go-tally-4.1.11/m3/thrift/v1/m3.go000066400000000000000000000145341456146544700220500ustar00rootroot00000000000000// Autogenerated by Thrift Compiler (0.9.3) // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING package v1 import ( "bytes" "fmt" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" ) // (needed to ensure safety because of naive import list construction.) var _ = thrift.ZERO var _ = fmt.Printf var _ = bytes.Equal type M3 interface { //M3 Metrics Service // Emits a batch of metrics. // // Parameters: // - Batch EmitMetricBatch(batch *MetricBatch) (err error) } //M3 Metrics Service type M3Client struct { Transport thrift.TTransport ProtocolFactory thrift.TProtocolFactory InputProtocol thrift.TProtocol OutputProtocol thrift.TProtocol SeqId int32 } func NewM3ClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *M3Client { return &M3Client{Transport: t, ProtocolFactory: f, InputProtocol: f.GetProtocol(t), OutputProtocol: f.GetProtocol(t), SeqId: 0, } } func NewM3ClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *M3Client { return &M3Client{Transport: t, ProtocolFactory: nil, InputProtocol: iprot, OutputProtocol: oprot, SeqId: 0, } } // Emits a batch of metrics. // // Parameters: // - Batch func (p *M3Client) EmitMetricBatch(batch *MetricBatch) (err error) { if err = p.sendEmitMetricBatch(batch); err != nil { return } return } func (p *M3Client) sendEmitMetricBatch(batch *MetricBatch) (err error) { oprot := p.OutputProtocol if oprot == nil { oprot = p.ProtocolFactory.GetProtocol(p.Transport) p.OutputProtocol = oprot } p.SeqId++ if err = oprot.WriteMessageBegin("emitMetricBatch", thrift.ONEWAY, p.SeqId); err != nil { return } args := M3EmitMetricBatchArgs{ Batch: batch, } if err = args.Write(oprot); err != nil { return } if err = oprot.WriteMessageEnd(); err != nil { return } return oprot.Flush() } type M3Processor struct { processorMap map[string]thrift.TProcessorFunction handler M3 } func (p *M3Processor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { p.processorMap[key] = processor } func (p *M3Processor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { processor, ok = p.processorMap[key] return processor, ok } func (p *M3Processor) ProcessorMap() map[string]thrift.TProcessorFunction { return p.processorMap } func NewM3Processor(handler M3) *M3Processor { self3 := &M3Processor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} self3.processorMap["emitMetricBatch"] = &m3ProcessorEmitMetricBatch{handler: handler} return self3 } func (p *M3Processor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { name, _, seqId, err := iprot.ReadMessageBegin() if err != nil { return false, err } if processor, ok := p.GetProcessorFunction(name); ok { return processor.Process(seqId, iprot, oprot) } iprot.Skip(thrift.STRUCT) iprot.ReadMessageEnd() x4 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) x4.Write(oprot) oprot.WriteMessageEnd() oprot.Flush() return false, x4 } type m3ProcessorEmitMetricBatch struct { handler M3 } func (p *m3ProcessorEmitMetricBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { args := M3EmitMetricBatchArgs{} if err = args.Read(iprot); err != nil { iprot.ReadMessageEnd() return false, err } iprot.ReadMessageEnd() var err2 error if err2 = p.handler.EmitMetricBatch(args.Batch); err2 != nil { return true, err2 } return true, nil } // HELPER FUNCTIONS AND STRUCTURES // Attributes: // - Batch type M3EmitMetricBatchArgs struct { Batch *MetricBatch `thrift:"batch,1" json:"batch"` } func NewM3EmitMetricBatchArgs() *M3EmitMetricBatchArgs { return &M3EmitMetricBatchArgs{} } var M3EmitMetricBatchArgs_Batch_DEFAULT *MetricBatch func (p *M3EmitMetricBatchArgs) GetBatch() *MetricBatch { if !p.IsSetBatch() { return M3EmitMetricBatchArgs_Batch_DEFAULT } return p.Batch } func (p *M3EmitMetricBatchArgs) IsSetBatch() bool { return p.Batch != nil } func (p *M3EmitMetricBatchArgs) Read(iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *M3EmitMetricBatchArgs) readField1(iprot thrift.TProtocol) error { p.Batch = &MetricBatch{} if err := p.Batch.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err) } return nil } func (p *M3EmitMetricBatchArgs) Write(oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin("emitMetricBatch_args"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *M3EmitMetricBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("batch", thrift.STRUCT, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err) } if err := p.Batch.Write(oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err) } return err } func (p *M3EmitMetricBatchArgs) String() string { if p == nil { return "" } return fmt.Sprintf("M3EmitMetricBatchArgs(%+v)", *p) } golang-github-uber-go-tally-4.1.11/m3/thrift/v1/ttypes.go000066400000000000000000001004141456146544700230520ustar00rootroot00000000000000// Autogenerated by Thrift Compiler (0.9.3) // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING package v1 import ( "bytes" "fmt" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" ) // (needed to ensure safety because of naive import list construction.) var _ = thrift.ZERO var _ = fmt.Printf var _ = bytes.Equal var GoUnusedProtection__ int // Different types of values that m3 emits. Each metric // must contain one of these values // // Attributes: // - Count // - Gauge // - Timer type MetricValue struct { Count *CountValue `thrift:"count,1" json:"count,omitempty"` Gauge *GaugeValue `thrift:"gauge,2" json:"gauge,omitempty"` Timer *TimerValue `thrift:"timer,3" json:"timer,omitempty"` } func NewMetricValue() *MetricValue { return &MetricValue{} } var MetricValue_Count_DEFAULT CountValue func (p *MetricValue) GetCount() CountValue { if !p.IsSetCount() { return MetricValue_Count_DEFAULT } return *p.Count } var MetricValue_Gauge_DEFAULT GaugeValue func (p *MetricValue) GetGauge() GaugeValue { if !p.IsSetGauge() { return MetricValue_Gauge_DEFAULT } return *p.Gauge } var MetricValue_Timer_DEFAULT TimerValue func (p *MetricValue) GetTimer() TimerValue { if !p.IsSetTimer() { return MetricValue_Timer_DEFAULT } return *p.Timer } func (p *MetricValue) CountSetFieldsMetricValue() int { count := 0 if p.IsSetCount() { count++ } if p.IsSetGauge() { count++ } if p.IsSetTimer() { count++ } return count } func (p *MetricValue) IsSetCount() bool { return p.Count != nil } func (p *MetricValue) IsSetGauge() bool { return p.Gauge != nil } func (p *MetricValue) IsSetTimer() bool { return p.Timer != nil } func (p *MetricValue) Read(iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } case 2: if err := p.readField2(iprot); err != nil { return err } case 3: if err := p.readField3(iprot); err != nil { return err } default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *MetricValue) readField1(iprot thrift.TProtocol) error { p.Count = &CountValue{} if err := p.Count.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Count), err) } return nil } func (p *MetricValue) readField2(iprot thrift.TProtocol) error { p.Gauge = &GaugeValue{} if err := p.Gauge.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Gauge), err) } return nil } func (p *MetricValue) readField3(iprot thrift.TProtocol) error { p.Timer = &TimerValue{} if err := p.Timer.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Timer), err) } return nil } func (p *MetricValue) Write(oprot thrift.TProtocol) error { if c := p.CountSetFieldsMetricValue(); c != 1 { return fmt.Errorf("%T write union: exactly one field must be set (%d set).", p, c) } if err := oprot.WriteStructBegin("MetricValue"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := p.writeField2(oprot); err != nil { return err } if err := p.writeField3(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *MetricValue) writeField1(oprot thrift.TProtocol) (err error) { if p.IsSetCount() { if err := oprot.WriteFieldBegin("count", thrift.STRUCT, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:count: ", p), err) } if err := p.Count.Write(oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Count), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:count: ", p), err) } } return err } func (p *MetricValue) writeField2(oprot thrift.TProtocol) (err error) { if p.IsSetGauge() { if err := oprot.WriteFieldBegin("gauge", thrift.STRUCT, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:gauge: ", p), err) } if err := p.Gauge.Write(oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Gauge), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:gauge: ", p), err) } } return err } func (p *MetricValue) writeField3(oprot thrift.TProtocol) (err error) { if p.IsSetTimer() { if err := oprot.WriteFieldBegin("timer", thrift.STRUCT, 3); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:timer: ", p), err) } if err := p.Timer.Write(oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Timer), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 3:timer: ", p), err) } } return err } func (p *MetricValue) String() string { if p == nil { return "" } return fmt.Sprintf("MetricValue(%+v)", *p) } // Different types of count values // // Attributes: // - I64Value type CountValue struct { I64Value *int64 `thrift:"i64Value,1" json:"i64Value,omitempty"` } func NewCountValue() *CountValue { return &CountValue{} } var CountValue_I64Value_DEFAULT int64 func (p *CountValue) GetI64Value() int64 { if !p.IsSetI64Value() { return CountValue_I64Value_DEFAULT } return *p.I64Value } func (p *CountValue) CountSetFieldsCountValue() int { count := 0 if p.IsSetI64Value() { count++ } return count } func (p *CountValue) IsSetI64Value() bool { return p.I64Value != nil } func (p *CountValue) Read(iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *CountValue) readField1(iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.I64Value = &v } return nil } func (p *CountValue) Write(oprot thrift.TProtocol) error { if c := p.CountSetFieldsCountValue(); c != 1 { return fmt.Errorf("%T write union: exactly one field must be set (%d set).", p, c) } if err := oprot.WriteStructBegin("CountValue"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *CountValue) writeField1(oprot thrift.TProtocol) (err error) { if p.IsSetI64Value() { if err := oprot.WriteFieldBegin("i64Value", thrift.I64, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:i64Value: ", p), err) } if err := oprot.WriteI64(int64(*p.I64Value)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.i64Value (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:i64Value: ", p), err) } } return err } func (p *CountValue) String() string { if p == nil { return "" } return fmt.Sprintf("CountValue(%+v)", *p) } // Different types of gauge values // // Attributes: // - I64Value // - DValue type GaugeValue struct { I64Value *int64 `thrift:"i64Value,1" json:"i64Value,omitempty"` DValue *float64 `thrift:"dValue,2" json:"dValue,omitempty"` } func NewGaugeValue() *GaugeValue { return &GaugeValue{} } var GaugeValue_I64Value_DEFAULT int64 func (p *GaugeValue) GetI64Value() int64 { if !p.IsSetI64Value() { return GaugeValue_I64Value_DEFAULT } return *p.I64Value } var GaugeValue_DValue_DEFAULT float64 func (p *GaugeValue) GetDValue() float64 { if !p.IsSetDValue() { return GaugeValue_DValue_DEFAULT } return *p.DValue } func (p *GaugeValue) CountSetFieldsGaugeValue() int { count := 0 if p.IsSetI64Value() { count++ } if p.IsSetDValue() { count++ } return count } func (p *GaugeValue) IsSetI64Value() bool { return p.I64Value != nil } func (p *GaugeValue) IsSetDValue() bool { return p.DValue != nil } func (p *GaugeValue) Read(iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } case 2: if err := p.readField2(iprot); err != nil { return err } default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *GaugeValue) readField1(iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.I64Value = &v } return nil } func (p *GaugeValue) readField2(iprot thrift.TProtocol) error { if v, err := iprot.ReadDouble(); err != nil { return thrift.PrependError("error reading field 2: ", err) } else { p.DValue = &v } return nil } func (p *GaugeValue) Write(oprot thrift.TProtocol) error { if c := p.CountSetFieldsGaugeValue(); c != 1 { return fmt.Errorf("%T write union: exactly one field must be set (%d set).", p, c) } if err := oprot.WriteStructBegin("GaugeValue"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := p.writeField2(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *GaugeValue) writeField1(oprot thrift.TProtocol) (err error) { if p.IsSetI64Value() { if err := oprot.WriteFieldBegin("i64Value", thrift.I64, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:i64Value: ", p), err) } if err := oprot.WriteI64(int64(*p.I64Value)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.i64Value (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:i64Value: ", p), err) } } return err } func (p *GaugeValue) writeField2(oprot thrift.TProtocol) (err error) { if p.IsSetDValue() { if err := oprot.WriteFieldBegin("dValue", thrift.DOUBLE, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:dValue: ", p), err) } if err := oprot.WriteDouble(float64(*p.DValue)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.dValue (2) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:dValue: ", p), err) } } return err } func (p *GaugeValue) String() string { if p == nil { return "" } return fmt.Sprintf("GaugeValue(%+v)", *p) } // Different types of timer values // // Attributes: // - I64Value // - DValue type TimerValue struct { I64Value *int64 `thrift:"i64Value,1" json:"i64Value,omitempty"` DValue *float64 `thrift:"dValue,2" json:"dValue,omitempty"` } func NewTimerValue() *TimerValue { return &TimerValue{} } var TimerValue_I64Value_DEFAULT int64 func (p *TimerValue) GetI64Value() int64 { if !p.IsSetI64Value() { return TimerValue_I64Value_DEFAULT } return *p.I64Value } var TimerValue_DValue_DEFAULT float64 func (p *TimerValue) GetDValue() float64 { if !p.IsSetDValue() { return TimerValue_DValue_DEFAULT } return *p.DValue } func (p *TimerValue) CountSetFieldsTimerValue() int { count := 0 if p.IsSetI64Value() { count++ } if p.IsSetDValue() { count++ } return count } func (p *TimerValue) IsSetI64Value() bool { return p.I64Value != nil } func (p *TimerValue) IsSetDValue() bool { return p.DValue != nil } func (p *TimerValue) Read(iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } case 2: if err := p.readField2(iprot); err != nil { return err } default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *TimerValue) readField1(iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.I64Value = &v } return nil } func (p *TimerValue) readField2(iprot thrift.TProtocol) error { if v, err := iprot.ReadDouble(); err != nil { return thrift.PrependError("error reading field 2: ", err) } else { p.DValue = &v } return nil } func (p *TimerValue) Write(oprot thrift.TProtocol) error { if c := p.CountSetFieldsTimerValue(); c != 1 { return fmt.Errorf("%T write union: exactly one field must be set (%d set).", p, c) } if err := oprot.WriteStructBegin("TimerValue"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := p.writeField2(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *TimerValue) writeField1(oprot thrift.TProtocol) (err error) { if p.IsSetI64Value() { if err := oprot.WriteFieldBegin("i64Value", thrift.I64, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:i64Value: ", p), err) } if err := oprot.WriteI64(int64(*p.I64Value)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.i64Value (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:i64Value: ", p), err) } } return err } func (p *TimerValue) writeField2(oprot thrift.TProtocol) (err error) { if p.IsSetDValue() { if err := oprot.WriteFieldBegin("dValue", thrift.DOUBLE, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:dValue: ", p), err) } if err := oprot.WriteDouble(float64(*p.DValue)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.dValue (2) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:dValue: ", p), err) } } return err } func (p *TimerValue) String() string { if p == nil { return "" } return fmt.Sprintf("TimerValue(%+v)", *p) } // Tags that can be applied to a metric // // Attributes: // - TagName // - TagValue type MetricTag struct { TagName string `thrift:"tagName,1" json:"tagName"` TagValue *string `thrift:"tagValue,2" json:"tagValue,omitempty"` } func NewMetricTag() *MetricTag { return &MetricTag{} } func (p *MetricTag) GetTagName() string { return p.TagName } var MetricTag_TagValue_DEFAULT string func (p *MetricTag) GetTagValue() string { if !p.IsSetTagValue() { return MetricTag_TagValue_DEFAULT } return *p.TagValue } func (p *MetricTag) IsSetTagValue() bool { return p.TagValue != nil } func (p *MetricTag) Read(iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } case 2: if err := p.readField2(iprot); err != nil { return err } default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *MetricTag) readField1(iprot thrift.TProtocol) error { if v, err := iprot.ReadString(); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.TagName = v } return nil } func (p *MetricTag) readField2(iprot thrift.TProtocol) error { if v, err := iprot.ReadString(); err != nil { return thrift.PrependError("error reading field 2: ", err) } else { p.TagValue = &v } return nil } func (p *MetricTag) Write(oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin("MetricTag"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := p.writeField2(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *MetricTag) writeField1(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("tagName", thrift.STRING, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:tagName: ", p), err) } if err := oprot.WriteString(string(p.TagName)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.tagName (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:tagName: ", p), err) } return err } func (p *MetricTag) writeField2(oprot thrift.TProtocol) (err error) { if p.IsSetTagValue() { if err := oprot.WriteFieldBegin("tagValue", thrift.STRING, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:tagValue: ", p), err) } if err := oprot.WriteString(string(*p.TagValue)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.tagValue (2) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:tagValue: ", p), err) } } return err } func (p *MetricTag) String() string { if p == nil { return "" } return fmt.Sprintf("MetricTag(%+v)", *p) } // The metric that is being emitted // // Attributes: // - Name // - MetricValue // - Timestamp // - Tags type Metric struct { Name string `thrift:"name,1" json:"name"` MetricValue *MetricValue `thrift:"metricValue,2" json:"metricValue,omitempty"` Timestamp *int64 `thrift:"timestamp,3" json:"timestamp,omitempty"` Tags map[*MetricTag]bool `thrift:"tags,4" json:"tags,omitempty"` } func NewMetric() *Metric { return &Metric{} } func (p *Metric) GetName() string { return p.Name } var Metric_MetricValue_DEFAULT *MetricValue func (p *Metric) GetMetricValue() *MetricValue { if !p.IsSetMetricValue() { return Metric_MetricValue_DEFAULT } return p.MetricValue } var Metric_Timestamp_DEFAULT int64 func (p *Metric) GetTimestamp() int64 { if !p.IsSetTimestamp() { return Metric_Timestamp_DEFAULT } return *p.Timestamp } var Metric_Tags_DEFAULT map[*MetricTag]bool func (p *Metric) GetTags() map[*MetricTag]bool { return p.Tags } func (p *Metric) IsSetMetricValue() bool { return p.MetricValue != nil } func (p *Metric) IsSetTimestamp() bool { return p.Timestamp != nil } func (p *Metric) IsSetTags() bool { return p.Tags != nil } func (p *Metric) Read(iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } case 2: if err := p.readField2(iprot); err != nil { return err } case 3: if err := p.readField3(iprot); err != nil { return err } case 4: if err := p.readField4(iprot); err != nil { return err } default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *Metric) readField1(iprot thrift.TProtocol) error { if v, err := iprot.ReadString(); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.Name = v } return nil } func (p *Metric) readField2(iprot thrift.TProtocol) error { p.MetricValue = &MetricValue{} if err := p.MetricValue.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.MetricValue), err) } return nil } func (p *Metric) readField3(iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(); err != nil { return thrift.PrependError("error reading field 3: ", err) } else { p.Timestamp = &v } return nil } func (p *Metric) readField4(iprot thrift.TProtocol) error { _, size, err := iprot.ReadSetBegin() if err != nil { return thrift.PrependError("error reading set begin: ", err) } tSet := make(map[*MetricTag]bool, size) p.Tags = tSet for i := 0; i < size; i++ { _elem0 := &MetricTag{} if err := _elem0.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) } p.Tags[_elem0] = true } if err := iprot.ReadSetEnd(); err != nil { return thrift.PrependError("error reading set end: ", err) } return nil } func (p *Metric) Write(oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin("Metric"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := p.writeField2(oprot); err != nil { return err } if err := p.writeField3(oprot); err != nil { return err } if err := p.writeField4(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *Metric) writeField1(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("name", thrift.STRING, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:name: ", p), err) } if err := oprot.WriteString(string(p.Name)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.name (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:name: ", p), err) } return err } func (p *Metric) writeField2(oprot thrift.TProtocol) (err error) { if p.IsSetMetricValue() { if err := oprot.WriteFieldBegin("metricValue", thrift.STRUCT, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:metricValue: ", p), err) } if err := p.MetricValue.Write(oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.MetricValue), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:metricValue: ", p), err) } } return err } func (p *Metric) writeField3(oprot thrift.TProtocol) (err error) { if p.IsSetTimestamp() { if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 3); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:timestamp: ", p), err) } if err := oprot.WriteI64(int64(*p.Timestamp)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.timestamp (3) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 3:timestamp: ", p), err) } } return err } func (p *Metric) writeField4(oprot thrift.TProtocol) (err error) { if p.IsSetTags() { if err := oprot.WriteFieldBegin("tags", thrift.SET, 4); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:tags: ", p), err) } if err := oprot.WriteSetBegin(thrift.STRUCT, len(p.Tags)); err != nil { return thrift.PrependError("error writing set begin: ", err) } for v, _ := range p.Tags { if err := v.Write(oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteSetEnd(); err != nil { return thrift.PrependError("error writing set end: ", err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 4:tags: ", p), err) } } return err } func (p *Metric) String() string { if p == nil { return "" } return fmt.Sprintf("Metric(%+v)", *p) } // Structure that holds a group of metrics which share // common properties like the cluster and service. // // Attributes: // - Metrics // - CommonTags type MetricBatch struct { Metrics []*Metric `thrift:"metrics,1" json:"metrics"` CommonTags map[*MetricTag]bool `thrift:"commonTags,2" json:"commonTags,omitempty"` } func NewMetricBatch() *MetricBatch { return &MetricBatch{} } func (p *MetricBatch) GetMetrics() []*Metric { return p.Metrics } var MetricBatch_CommonTags_DEFAULT map[*MetricTag]bool func (p *MetricBatch) GetCommonTags() map[*MetricTag]bool { return p.CommonTags } func (p *MetricBatch) IsSetCommonTags() bool { return p.CommonTags != nil } func (p *MetricBatch) Read(iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } case 2: if err := p.readField2(iprot); err != nil { return err } default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *MetricBatch) readField1(iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin() if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]*Metric, 0, size) p.Metrics = tSlice for i := 0; i < size; i++ { _elem1 := &Metric{} if err := _elem1.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) } p.Metrics = append(p.Metrics, _elem1) } if err := iprot.ReadListEnd(); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *MetricBatch) readField2(iprot thrift.TProtocol) error { _, size, err := iprot.ReadSetBegin() if err != nil { return thrift.PrependError("error reading set begin: ", err) } tSet := make(map[*MetricTag]bool, size) p.CommonTags = tSet for i := 0; i < size; i++ { _elem2 := &MetricTag{} if err := _elem2.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem2), err) } p.CommonTags[_elem2] = true } if err := iprot.ReadSetEnd(); err != nil { return thrift.PrependError("error reading set end: ", err) } return nil } func (p *MetricBatch) Write(oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin("MetricBatch"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := p.writeField2(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *MetricBatch) writeField1(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("metrics", thrift.LIST, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:metrics: ", p), err) } if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Metrics)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Metrics { if err := v.Write(oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:metrics: ", p), err) } return err } func (p *MetricBatch) writeField2(oprot thrift.TProtocol) (err error) { if p.IsSetCommonTags() { if err := oprot.WriteFieldBegin("commonTags", thrift.SET, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:commonTags: ", p), err) } if err := oprot.WriteSetBegin(thrift.STRUCT, len(p.CommonTags)); err != nil { return thrift.PrependError("error writing set begin: ", err) } for v, _ := range p.CommonTags { if err := v.Write(oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteSetEnd(); err != nil { return thrift.PrependError("error writing set end: ", err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:commonTags: ", p), err) } } return err } func (p *MetricBatch) String() string { if p == nil { return "" } return fmt.Sprintf("MetricBatch(%+v)", *p) } golang-github-uber-go-tally-4.1.11/m3/thrift/v1/v1.thrift000066400000000000000000000022171456146544700227450ustar00rootroot00000000000000/** * Different types of values that m3 emits. Each metric * must contain one of these values */ union MetricValue { 1: optional CountValue count 2: optional GaugeValue gauge 3: optional TimerValue timer } /** * Different types of count values */ union CountValue { 1: optional i64 i64Value } /** * Different types of gauge values */ union GaugeValue { 1: optional i64 i64Value 2: optional double dValue } /** * Different types of timer values */ union TimerValue { 1: optional i64 i64Value 2: optional double dValue } /** * Tags that can be applied to a metric */ struct MetricTag { 1: string tagName, 2: optional string tagValue } /** * The metric that is being emitted */ struct Metric { 1: string name, 2: optional MetricValue metricValue, 3: optional i64 timestamp, 4: optional set tags } /** * Structure that holds a group of metrics which share * common properties like the cluster and service. */ struct MetricBatch { 1: list metrics 2: optional set commonTags } /** * M3 Metrics Service */ service M3 { /** * Emits a batch of metrics. */ oneway void emitMetricBatch(1: MetricBatch batch) } golang-github-uber-go-tally-4.1.11/m3/thrift/v2/000077500000000000000000000000001456146544700211745ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/m3/thrift/v2/constants.go000066400000000000000000000005761456146544700235470ustar00rootroot00000000000000// Autogenerated by Thrift Compiler (0.9.3) // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING package v2 import ( "bytes" "fmt" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" ) // (needed to ensure safety because of naive import list construction.) var _ = thrift.ZERO var _ = fmt.Printf var _ = bytes.Equal func init() { } golang-github-uber-go-tally-4.1.11/m3/thrift/v2/m3.go000066400000000000000000000145031456146544700220450ustar00rootroot00000000000000// Autogenerated by Thrift Compiler (0.9.3) // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING package v2 import ( "bytes" "fmt" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" ) // (needed to ensure safety because of naive import list construction.) var _ = thrift.ZERO var _ = fmt.Printf var _ = bytes.Equal type M3 interface { // Parameters: // - Batch EmitMetricBatchV2(batch MetricBatch) (err error) } type M3Client struct { Transport thrift.TTransport ProtocolFactory thrift.TProtocolFactory InputProtocol thrift.TProtocol OutputProtocol thrift.TProtocol SeqId int32 } func NewM3ClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *M3Client { return &M3Client{Transport: t, ProtocolFactory: f, InputProtocol: f.GetProtocol(t), OutputProtocol: f.GetProtocol(t), SeqId: 0, } } func NewM3ClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *M3Client { return &M3Client{Transport: t, ProtocolFactory: nil, InputProtocol: iprot, OutputProtocol: oprot, SeqId: 0, } } // Parameters: // - Batch func (p *M3Client) EmitMetricBatchV2(batch MetricBatch) (err error) { if err = p.sendEmitMetricBatchV2(batch); err != nil { return } return } func (p *M3Client) sendEmitMetricBatchV2(batch MetricBatch) (err error) { oprot := p.OutputProtocol if oprot == nil { oprot = p.ProtocolFactory.GetProtocol(p.Transport) p.OutputProtocol = oprot } p.SeqId++ if err = oprot.WriteMessageBegin("emitMetricBatchV2", thrift.ONEWAY, p.SeqId); err != nil { return } args := M3EmitMetricBatchV2Args{ Batch: batch, } if err = args.Write(oprot); err != nil { return } if err = oprot.WriteMessageEnd(); err != nil { return } return oprot.Flush() } type M3Processor struct { processorMap map[string]thrift.TProcessorFunction handler M3 } func (p *M3Processor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { p.processorMap[key] = processor } func (p *M3Processor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { processor, ok = p.processorMap[key] return processor, ok } func (p *M3Processor) ProcessorMap() map[string]thrift.TProcessorFunction { return p.processorMap } func NewM3Processor(handler M3) *M3Processor { self3 := &M3Processor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} self3.processorMap["emitMetricBatchV2"] = &m3ProcessorEmitMetricBatchV2{handler: handler} return self3 } func (p *M3Processor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { name, _, seqId, err := iprot.ReadMessageBegin() if err != nil { return false, err } if processor, ok := p.GetProcessorFunction(name); ok { return processor.Process(seqId, iprot, oprot) } iprot.Skip(thrift.STRUCT) iprot.ReadMessageEnd() x4 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) x4.Write(oprot) oprot.WriteMessageEnd() oprot.Flush() return false, x4 } type m3ProcessorEmitMetricBatchV2 struct { handler M3 } func (p *m3ProcessorEmitMetricBatchV2) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { args := M3EmitMetricBatchV2Args{} if err = args.Read(iprot); err != nil { iprot.ReadMessageEnd() return false, err } iprot.ReadMessageEnd() var err2 error if err2 = p.handler.EmitMetricBatchV2(args.Batch); err2 != nil { return true, err2 } return true, nil } // HELPER FUNCTIONS AND STRUCTURES // Attributes: // - Batch type M3EmitMetricBatchV2Args struct { Batch MetricBatch `thrift:"batch,1" json:"batch"` } func NewM3EmitMetricBatchV2Args() *M3EmitMetricBatchV2Args { return &M3EmitMetricBatchV2Args{} } var M3EmitMetricBatchV2Args_Batch_DEFAULT MetricBatch func (p *M3EmitMetricBatchV2Args) GetBatch() MetricBatch { if !p.IsSetBatch() { return M3EmitMetricBatchV2Args_Batch_DEFAULT } return p.Batch } func (p *M3EmitMetricBatchV2Args) IsSetBatch() bool { return p.Batch.Metrics != nil || p.Batch.CommonTags != nil } func (p *M3EmitMetricBatchV2Args) Read(iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *M3EmitMetricBatchV2Args) readField1(iprot thrift.TProtocol) error { p.Batch = MetricBatch{} if err := p.Batch.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err) } return nil } func (p *M3EmitMetricBatchV2Args) Write(oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin("emitMetricBatchV2_args"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *M3EmitMetricBatchV2Args) writeField1(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("batch", thrift.STRUCT, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err) } if err := p.Batch.Write(oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err) } return err } func (p *M3EmitMetricBatchV2Args) String() string { if p == nil { return "" } return fmt.Sprintf("M3EmitMetricBatchV2Args(%+v)", *p) } golang-github-uber-go-tally-4.1.11/m3/thrift/v2/ttypes.go000066400000000000000000000564301456146544700230630ustar00rootroot00000000000000// Autogenerated by Thrift Compiler (0.9.3) // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING package v2 import ( "bytes" "fmt" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" ) // (needed to ensure safety because of naive import list construction.) var _ = thrift.ZERO var _ = fmt.Printf var _ = bytes.Equal var GoUnusedProtection__ int type MetricType int64 const ( MetricType_INVALID MetricType = 0 MetricType_COUNTER MetricType = 1 MetricType_GAUGE MetricType = 2 MetricType_TIMER MetricType = 3 ) func (p MetricType) String() string { switch p { case MetricType_INVALID: return "INVALID" case MetricType_COUNTER: return "COUNTER" case MetricType_GAUGE: return "GAUGE" case MetricType_TIMER: return "TIMER" } return "" } func MetricTypeFromString(s string) (MetricType, error) { switch s { case "INVALID": return MetricType_INVALID, nil case "COUNTER": return MetricType_COUNTER, nil case "GAUGE": return MetricType_GAUGE, nil case "TIMER": return MetricType_TIMER, nil } return MetricType(0), fmt.Errorf("not a valid MetricType string") } func MetricTypePtr(v MetricType) *MetricType { return &v } func (p MetricType) MarshalText() ([]byte, error) { return []byte(p.String()), nil } func (p *MetricType) UnmarshalText(text []byte) error { q, err := MetricTypeFromString(string(text)) if err != nil { return err } *p = q return nil } // Attributes: // - MetricType // - Count // - Gauge // - Timer type MetricValue struct { MetricType MetricType `thrift:"metricType,1,required" json:"metricType"` Count int64 `thrift:"count,2,required" json:"count"` Gauge float64 `thrift:"gauge,3,required" json:"gauge"` Timer int64 `thrift:"timer,4,required" json:"timer"` } func NewMetricValue() *MetricValue { return &MetricValue{} } func (p *MetricValue) GetMetricType() MetricType { return p.MetricType } func (p *MetricValue) GetCount() int64 { return p.Count } func (p *MetricValue) GetGauge() float64 { return p.Gauge } func (p *MetricValue) GetTimer() int64 { return p.Timer } func (p *MetricValue) Read(iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } var issetMetricType bool = false var issetCount bool = false var issetGauge bool = false var issetTimer bool = false for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } issetMetricType = true case 2: if err := p.readField2(iprot); err != nil { return err } issetCount = true case 3: if err := p.readField3(iprot); err != nil { return err } issetGauge = true case 4: if err := p.readField4(iprot); err != nil { return err } issetTimer = true default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } if !issetMetricType { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field MetricType is not set")) } if !issetCount { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Count is not set")) } if !issetGauge { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Gauge is not set")) } if !issetTimer { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Timer is not set")) } return nil } func (p *MetricValue) readField1(iprot thrift.TProtocol) error { if v, err := iprot.ReadI32(); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { temp := MetricType(v) p.MetricType = temp } return nil } func (p *MetricValue) readField2(iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(); err != nil { return thrift.PrependError("error reading field 2: ", err) } else { p.Count = v } return nil } func (p *MetricValue) readField3(iprot thrift.TProtocol) error { if v, err := iprot.ReadDouble(); err != nil { return thrift.PrependError("error reading field 3: ", err) } else { p.Gauge = v } return nil } func (p *MetricValue) readField4(iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(); err != nil { return thrift.PrependError("error reading field 4: ", err) } else { p.Timer = v } return nil } func (p *MetricValue) Write(oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin("MetricValue"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := p.writeField2(oprot); err != nil { return err } if err := p.writeField3(oprot); err != nil { return err } if err := p.writeField4(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *MetricValue) writeField1(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("metricType", thrift.I32, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:metricType: ", p), err) } if err := oprot.WriteI32(int32(p.MetricType)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.metricType (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:metricType: ", p), err) } return err } func (p *MetricValue) writeField2(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("count", thrift.I64, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:count: ", p), err) } if err := oprot.WriteI64(int64(p.Count)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.count (2) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:count: ", p), err) } return err } func (p *MetricValue) writeField3(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("gauge", thrift.DOUBLE, 3); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:gauge: ", p), err) } if err := oprot.WriteDouble(float64(p.Gauge)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.gauge (3) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 3:gauge: ", p), err) } return err } func (p *MetricValue) writeField4(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("timer", thrift.I64, 4); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:timer: ", p), err) } if err := oprot.WriteI64(int64(p.Timer)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.timer (4) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 4:timer: ", p), err) } return err } func (p *MetricValue) String() string { if p == nil { return "" } return fmt.Sprintf("MetricValue(%+v)", *p) } // Attributes: // - Name // - Value type MetricTag struct { Name string `thrift:"name,1,required" json:"name"` Value string `thrift:"value,2,required" json:"value"` } func NewMetricTag() *MetricTag { return &MetricTag{} } func (p *MetricTag) GetName() string { return p.Name } func (p *MetricTag) GetValue() string { return p.Value } func (p *MetricTag) Read(iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } var issetName bool = false var issetValue bool = false for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } issetName = true case 2: if err := p.readField2(iprot); err != nil { return err } issetValue = true default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } if !issetName { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Name is not set")) } if !issetValue { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Value is not set")) } return nil } func (p *MetricTag) readField1(iprot thrift.TProtocol) error { if v, err := iprot.ReadString(); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.Name = v } return nil } func (p *MetricTag) readField2(iprot thrift.TProtocol) error { if v, err := iprot.ReadString(); err != nil { return thrift.PrependError("error reading field 2: ", err) } else { p.Value = v } return nil } func (p *MetricTag) Write(oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin("MetricTag"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := p.writeField2(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *MetricTag) writeField1(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("name", thrift.STRING, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:name: ", p), err) } if err := oprot.WriteString(string(p.Name)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.name (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:name: ", p), err) } return err } func (p *MetricTag) writeField2(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("value", thrift.STRING, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) } if err := oprot.WriteString(string(p.Value)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) } return err } func (p *MetricTag) String() string { if p == nil { return "" } return fmt.Sprintf("MetricTag(%+v)", *p) } // Attributes: // - Name // - Value // - Timestamp // - Tags type Metric struct { Name string `thrift:"name,1,required" json:"name"` Value MetricValue `thrift:"value,2,required" json:"value"` Timestamp int64 `thrift:"timestamp,3,required" json:"timestamp"` Tags []MetricTag `thrift:"tags,4" json:"tags,omitempty"` } func NewMetric() *Metric { return &Metric{} } func (p *Metric) GetName() string { return p.Name } var Metric_Value_DEFAULT MetricValue func (p *Metric) GetValue() MetricValue { if !p.IsSetValue() { return Metric_Value_DEFAULT } return p.Value } func (p *Metric) GetTimestamp() int64 { return p.Timestamp } var Metric_Tags_DEFAULT []MetricTag func (p *Metric) GetTags() []MetricTag { return p.Tags } func (p *Metric) IsSetValue() bool { return p.Value.GetMetricType() != MetricType_INVALID || p.Value.Count != 0 || p.Value.Gauge != 0 || p.Value.Timer != 0 } func (p *Metric) IsSetTags() bool { return p.Tags != nil } func (p *Metric) Read(iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } var issetName bool = false var issetValue bool = false var issetTimestamp bool = false for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } issetName = true case 2: if err := p.readField2(iprot); err != nil { return err } issetValue = true case 3: if err := p.readField3(iprot); err != nil { return err } issetTimestamp = true case 4: if err := p.readField4(iprot); err != nil { return err } default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } if !issetName { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Name is not set")) } if !issetValue { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Value is not set")) } if !issetTimestamp { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Timestamp is not set")) } return nil } func (p *Metric) readField1(iprot thrift.TProtocol) error { if v, err := iprot.ReadString(); err != nil { return thrift.PrependError("error reading field 1: ", err) } else { p.Name = v } return nil } func (p *Metric) readField2(iprot thrift.TProtocol) error { p.Value = MetricValue{} if err := p.Value.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Value), err) } return nil } func (p *Metric) readField3(iprot thrift.TProtocol) error { if v, err := iprot.ReadI64(); err != nil { return thrift.PrependError("error reading field 3: ", err) } else { p.Timestamp = v } return nil } func (p *Metric) readField4(iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin() if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]MetricTag, 0, size) p.Tags = tSlice for i := 0; i < size; i++ { _elem0 := MetricTag{} if err := _elem0.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) } p.Tags = append(p.Tags, _elem0) } if err := iprot.ReadListEnd(); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *Metric) Write(oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin("Metric"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := p.writeField2(oprot); err != nil { return err } if err := p.writeField3(oprot); err != nil { return err } if err := p.writeField4(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *Metric) writeField1(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("name", thrift.STRING, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:name: ", p), err) } if err := oprot.WriteString(string(p.Name)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.name (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:name: ", p), err) } return err } func (p *Metric) writeField2(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("value", thrift.STRUCT, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) } if err := p.Value.Write(oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Value), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) } return err } func (p *Metric) writeField3(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 3); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:timestamp: ", p), err) } if err := oprot.WriteI64(int64(p.Timestamp)); err != nil { return thrift.PrependError(fmt.Sprintf("%T.timestamp (3) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 3:timestamp: ", p), err) } return err } func (p *Metric) writeField4(oprot thrift.TProtocol) (err error) { if p.IsSetTags() { if err := oprot.WriteFieldBegin("tags", thrift.LIST, 4); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:tags: ", p), err) } if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Tags)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Tags { if err := v.Write(oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 4:tags: ", p), err) } } return err } func (p *Metric) String() string { if p == nil { return "" } return fmt.Sprintf("Metric(%+v)", *p) } // Attributes: // - Metrics // - CommonTags type MetricBatch struct { Metrics []Metric `thrift:"metrics,1,required" json:"metrics"` CommonTags []MetricTag `thrift:"commonTags,2" json:"commonTags,omitempty"` } func NewMetricBatch() *MetricBatch { return &MetricBatch{} } func (p *MetricBatch) GetMetrics() []Metric { return p.Metrics } var MetricBatch_CommonTags_DEFAULT []MetricTag func (p *MetricBatch) GetCommonTags() []MetricTag { return p.CommonTags } func (p *MetricBatch) IsSetCommonTags() bool { return p.CommonTags != nil } func (p *MetricBatch) Read(iprot thrift.TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) } var issetMetrics bool = false for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == thrift.STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } issetMetrics = true case 2: if err := p.readField2(iprot); err != nil { return err } default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } if !issetMetrics { return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Metrics is not set")) } return nil } func (p *MetricBatch) readField1(iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin() if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]Metric, 0, size) p.Metrics = tSlice for i := 0; i < size; i++ { _elem1 := Metric{} if err := _elem1.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) } p.Metrics = append(p.Metrics, _elem1) } if err := iprot.ReadListEnd(); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *MetricBatch) readField2(iprot thrift.TProtocol) error { _, size, err := iprot.ReadListBegin() if err != nil { return thrift.PrependError("error reading list begin: ", err) } tSlice := make([]MetricTag, 0, size) p.CommonTags = tSlice for i := 0; i < size; i++ { _elem2 := MetricTag{} if err := _elem2.Read(iprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem2), err) } p.CommonTags = append(p.CommonTags, _elem2) } if err := iprot.ReadListEnd(); err != nil { return thrift.PrependError("error reading list end: ", err) } return nil } func (p *MetricBatch) Write(oprot thrift.TProtocol) error { if err := oprot.WriteStructBegin("MetricBatch"); err != nil { return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := p.writeField2(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return thrift.PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return thrift.PrependError("write struct stop error: ", err) } return nil } func (p *MetricBatch) writeField1(oprot thrift.TProtocol) (err error) { if err := oprot.WriteFieldBegin("metrics", thrift.LIST, 1); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:metrics: ", p), err) } if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Metrics)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.Metrics { if err := v.Write(oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 1:metrics: ", p), err) } return err } func (p *MetricBatch) writeField2(oprot thrift.TProtocol) (err error) { if p.IsSetCommonTags() { if err := oprot.WriteFieldBegin("commonTags", thrift.LIST, 2); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:commonTags: ", p), err) } if err := oprot.WriteListBegin(thrift.STRUCT, len(p.CommonTags)); err != nil { return thrift.PrependError("error writing list begin: ", err) } for _, v := range p.CommonTags { if err := v.Write(oprot); err != nil { return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) } } if err := oprot.WriteListEnd(); err != nil { return thrift.PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(); err != nil { return thrift.PrependError(fmt.Sprintf("%T write field end error 2:commonTags: ", p), err) } } return err } func (p *MetricBatch) String() string { if p == nil { return "" } return fmt.Sprintf("MetricBatch(%+v)", *p) } golang-github-uber-go-tally-4.1.11/m3/thrift/v2/v2.thrift000066400000000000000000000011611456146544700227440ustar00rootroot00000000000000enum MetricType { INVALID = 0 COUNTER = 1 GAUGE = 2 TIMER = 3 } struct MetricValue { 1: required MetricType metricType 2: required i64 count 3: required double gauge 4: required i64 timer } struct MetricTag { 1: required string name 2: required string value } struct Metric { 1: required string name 2: required MetricValue value 3: required i64 timestamp 4: optional list tags } struct MetricBatch { 1: required list metrics 2: optional list commonTags } service M3 { oneway void emitMetricBatchV2(1: MetricBatch batch) }golang-github-uber-go-tally-4.1.11/m3/thriftudp/000077500000000000000000000000001456146544700213565ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/m3/thriftudp/multitransport.go000066400000000000000000000072331456146544700250210ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package thriftudp import ( "fmt" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" ) // TMultiUDPTransport does multiUDP as a thrift.TTransport type TMultiUDPTransport struct { transports []thrift.TTransport } // NewTMultiUDPClientTransport creates a set of net.UDPConn-backed TTransports for Thrift clients // All writes are buffered and flushed in one UDP packet. If locHostPort is not "", it // will be used as the local address for the connection // Example: // trans, err := thriftudp.NewTMultiUDPClientTransport([]string{"192.168.1.1:9090","192.168.1.2:9090"}, "") func NewTMultiUDPClientTransport( destHostPorts []string, locHostPort string, ) (*TMultiUDPTransport, error) { var transports []thrift.TTransport for i := range destHostPorts { trans, err := NewTUDPClientTransport(destHostPorts[i], locHostPort) if err != nil { return nil, err } transports = append(transports, trans) } return &TMultiUDPTransport{transports: transports}, nil } // Open the connections of the underlying transports func (p *TMultiUDPTransport) Open() error { for _, trans := range p.transports { if err := trans.Open(); err != nil { return err } } return nil } // IsOpen returns true if the connections of the underlying transports are open func (p *TMultiUDPTransport) IsOpen() bool { for _, trans := range p.transports { if open := trans.IsOpen(); !open { return false } } return true } // Close closes the connections of the underlying transports func (p *TMultiUDPTransport) Close() error { for _, trans := range p.transports { if err := trans.Close(); err != nil { return err } } return nil } // Read is not supported for multiple underlying transports func (p *TMultiUDPTransport) Read(buf []byte) (int, error) { // Not applicable, required by TTransport however return 0, fmt.Errorf("not supported") } // RemainingBytes is not supported for multiple underlying transports func (p *TMultiUDPTransport) RemainingBytes() uint64 { // Not applicable, required by TTransport however return 0 } // Write writes specified buf to the write buffer of underlying transports func (p *TMultiUDPTransport) Write(buff []byte) (int, error) { n := 0 for _, trans := range p.transports { written, err := trans.Write(buff) if err != nil { return n, err } if written > n { n = written } } return n, nil } // Flush flushes the write buffer of the underlying transports func (p *TMultiUDPTransport) Flush() error { for _, trans := range p.transports { if err := trans.Flush(); err != nil { return err } } return nil } golang-github-uber-go-tally-4.1.11/m3/thriftudp/multitransport_test.go000066400000000000000000000131561456146544700260610ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package thriftudp import ( "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" ) func TestNewTMultiUDPClientTransport(t *testing.T) { trans, err := NewTMultiUDPClientTransport([]string{"127.0.0.1:9090", "127.0.0.1:9090"}, "") assert.NotNil(t, trans) assert.Nil(t, err) trans.Close() } func TestNewTMultiUDPClientTransportBadAddress(t *testing.T) { trans, err := NewTMultiUDPClientTransport([]string{"not an address"}, "") assert.Nil(t, trans) assert.NotNil(t, err) } func TestTMultiUDPTransportOpen(t *testing.T) { trans, err := NewTMultiUDPClientTransport([]string{}, "") assert.Nil(t, err) trans.transports = []thrift.TTransport{newMockTransport(), newMockTransport()} assert.Nil(t, trans.Open()) trans.Close() } func TestTMultiUDPTransportOpenFail(t *testing.T) { trans, err := NewTMultiUDPClientTransport([]string{}, "") assert.Nil(t, err) trans.transports = []thrift.TTransport{newMockTransport(), newMockTransport()} trans.transports[1].(*mockTransport).openError = fmt.Errorf("error") assert.NotNil(t, trans.Open()) trans.Close() } func TestTMultiUDPTransportIsOpen(t *testing.T) { trans, err := NewTMultiUDPClientTransport([]string{}, "") assert.Nil(t, err) trans.transports = []thrift.TTransport{newMockTransport(), newMockTransport()} trans.transports[0].(*mockTransport).currentlyOpen = true trans.transports[1].(*mockTransport).currentlyOpen = true assert.True(t, trans.IsOpen()) trans.Close() } func TestTMultiUDPTransportIsNotOpen(t *testing.T) { trans, err := NewTMultiUDPClientTransport([]string{}, "") assert.Nil(t, err) trans.transports = []thrift.TTransport{newMockTransport(), newMockTransport()} assert.False(t, trans.IsOpen()) trans.Close() } func TestTMultiUDPTransportReadNotSupported(t *testing.T) { trans, err := NewTMultiUDPClientTransport([]string{}, "") assert.Nil(t, err) n, err := trans.Read([]byte{}) assert.Equal(t, int(0), n) assert.NotNil(t, err) trans.Close() } func TestTMultiUDPTransportRemainingBytesNotSupported(t *testing.T) { trans, err := NewTMultiUDPClientTransport([]string{}, "") assert.Nil(t, err) n := trans.RemainingBytes() assert.Equal(t, uint64(0), n) trans.Close() } func TestTMultiUDPTransportWrite(t *testing.T) { trans, err := NewTMultiUDPClientTransport([]string{}, "") assert.Nil(t, err) trans.transports = []thrift.TTransport{newMockTransport(), newMockTransport()} trans.transports[0].(*mockTransport).onWrite = func(buf []byte) (int, error) { return len(buf), nil } trans.transports[1].(*mockTransport).onWrite = func(buf []byte) (int, error) { return len(buf), nil } b := []byte{1, 2, 3} n, err := trans.Write(b) assert.Equal(t, int(len(b)), n) assert.Nil(t, err) trans.Flush() trans.Close() } func TestTMultiUDPTransportWriteError(t *testing.T) { trans, err := NewTMultiUDPClientTransport([]string{}, "") assert.Nil(t, err) trans.transports = []thrift.TTransport{newMockTransport(), newMockTransport()} trans.transports[0].(*mockTransport).onWrite = func(buf []byte) (int, error) { return 0, fmt.Errorf("error") } trans.transports[1].(*mockTransport).onWrite = func(buf []byte) (int, error) { return len(buf), nil } b := []byte{1, 2, 3} n, err := trans.Write(b) assert.Equal(t, int(0), n) assert.NotNil(t, err) trans.Close() } type mockTransport struct { currentlyOpen bool onRead func(buf []byte) (int, error) onRemainingBytes func() uint64 onWrite func(buf []byte) (int, error) onFlush func() error openError error closeError error } func newMockTransport() *mockTransport { return &mockTransport{} } func (m *mockTransport) Open() error { if m.openError != nil { return m.openError } m.currentlyOpen = true return nil } func (m *mockTransport) IsOpen() bool { return m.currentlyOpen } func (m *mockTransport) Close() error { if m.closeError != nil { return m.closeError } m.currentlyOpen = false return nil } func (m *mockTransport) Read(buf []byte) (int, error) { if m.onRead != nil { return m.onRead(buf) } return 0, fmt.Errorf("no mock Read implementation") } func (m *mockTransport) RemainingBytes() uint64 { if m.onRemainingBytes != nil { return m.onRemainingBytes() } return 0 } func (m *mockTransport) Write(buf []byte) (int, error) { if m.onWrite != nil { return m.onWrite(buf) } return 0, fmt.Errorf("no mock Write implementation") } func (m *mockTransport) Flush() error { if m.onFlush != nil { return m.onFlush() } return fmt.Errorf("no mock Flush implementation") } golang-github-uber-go-tally-4.1.11/m3/thriftudp/transport.go000066400000000000000000000161121456146544700237420ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package thriftudp import ( "bytes" "context" "net" "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" "go.uber.org/atomic" ) // MaxLength of UDP packet const MaxLength = 65000 // Ensure TUDPTransport implements TRichTransport which avoids allocations // when writing strings. var _ thrift.TRichTransport = (*TUDPTransport)(nil) // TUDPTransport does UDP as a thrift.TTransport type TUDPTransport struct { conn *net.UDPConn addr net.Addr writeBuf bytes.Buffer readByteBuf []byte closed atomic.Bool } // NewTUDPClientTransport creates a net.UDPConn-backed TTransport for Thrift clients // All writes are buffered and flushed in one UDP packet. If locHostPort is not "", it // will be used as the local address for the connection // Example: // trans, err := thriftudp.NewTUDPClientTransport("192.168.1.1:9090", "") func NewTUDPClientTransport(destHostPort string, locHostPort string) (*TUDPTransport, error) { destAddr, err := net.ResolveUDPAddr("udp", destHostPort) if err != nil { return nil, thrift.NewTTransportException(thrift.NOT_OPEN, err.Error()) } var locAddr *net.UDPAddr if locHostPort != "" { locAddr, err = net.ResolveUDPAddr("udp", locHostPort) if err != nil { return nil, thrift.NewTTransportException(thrift.NOT_OPEN, err.Error()) } } conn, err := net.DialUDP(destAddr.Network(), locAddr, destAddr) if err != nil { return nil, thrift.NewTTransportException(thrift.NOT_OPEN, err.Error()) } return &TUDPTransport{ addr: destAddr, conn: conn, readByteBuf: make([]byte, 1), }, nil } // NewTUDPServerTransport creates a net.UDPConn-backed TTransport for Thrift servers // It will listen for incoming udp packets on the specified host/port // Example: // trans, err := thriftudp.NewTUDPClientTransport("localhost:9001") func NewTUDPServerTransport(hostPort string) (*TUDPTransport, error) { return NewTUDPServerTransportWithListenConfig(hostPort, net.ListenConfig{}) } // NewTUDPServerTransportWithListenConfig creates a net.UDPConn-backed TTransport for Thrift servers // It will listen for incoming udp packets on the specified host/port // It takes net.ListenConfig to customize socket options func NewTUDPServerTransportWithListenConfig( hostPort string, listenConfig net.ListenConfig, ) (*TUDPTransport, error) { conn, err := listenConfig.ListenPacket(context.Background(), "udp", hostPort) if err != nil { return nil, thrift.NewTTransportException(thrift.NOT_OPEN, err.Error()) } uconn, ok := conn.(*net.UDPConn) if !ok { return nil, thrift.NewTTransportException(thrift.NOT_OPEN, "not a udp connection") } return &TUDPTransport{ addr: uconn.LocalAddr(), conn: uconn, readByteBuf: make([]byte, 1), }, nil } // Open does nothing as connection is opened on creation // Required to maintain thrift.TTransport interface func (p *TUDPTransport) Open() error { return nil } // Conn retrieves the underlying net.UDPConn func (p *TUDPTransport) Conn() *net.UDPConn { return p.conn } // IsOpen returns true if the connection is open func (p *TUDPTransport) IsOpen() bool { return !p.closed.Load() } // Close closes the transport and the underlying connection. // Note: the current implementation allows Close to be called multiple times without an error. func (p *TUDPTransport) Close() error { if closed := p.closed.Swap(true); !closed { return p.conn.Close() } return nil } // Addr returns the address that the transport is listening on or writing to func (p *TUDPTransport) Addr() net.Addr { return p.addr } // Read reads one UDP packet and puts it in the specified buf func (p *TUDPTransport) Read(buf []byte) (int, error) { if !p.IsOpen() { return 0, thrift.NewTTransportException(thrift.NOT_OPEN, "Connection not open") } n, err := p.conn.Read(buf) return n, thrift.NewTTransportExceptionFromError(err) } // ReadByte reads a single byte and returns it func (p *TUDPTransport) ReadByte() (byte, error) { n, err := p.Read(p.readByteBuf) if err != nil { return 0, err } if n == 0 { return 0, thrift.NewTTransportException(thrift.PROTOCOL_ERROR, "Received empty packet") } return p.readByteBuf[0], nil } // RemainingBytes returns the max number of bytes (same as Thrift's StreamTransport) as we // do not know how many bytes we have left. func (p *TUDPTransport) RemainingBytes() uint64 { const maxSize = ^uint64(0) return maxSize } // Write writes specified buf to the write buffer func (p *TUDPTransport) Write(buf []byte) (int, error) { if !p.IsOpen() { return 0, thrift.NewTTransportException(thrift.NOT_OPEN, "Connection not open") } if p.writeBuf.Len()+len(buf) > MaxLength { return 0, thrift.NewTTransportException(thrift.INVALID_DATA, "Data does not fit within one UDP packet") } n, err := p.writeBuf.Write(buf) return n, thrift.NewTTransportExceptionFromError(err) } // WriteByte writes a single byte to the write buffer func (p *TUDPTransport) WriteByte(b byte) error { if !p.IsOpen() { return thrift.NewTTransportException(thrift.NOT_OPEN, "Connection not open") } if p.writeBuf.Len()+1 > MaxLength { return thrift.NewTTransportException(thrift.INVALID_DATA, "Data does not fit within one UDP packet") } err := p.writeBuf.WriteByte(b) return thrift.NewTTransportExceptionFromError(err) } // WriteString writes the specified string to the write buffer func (p *TUDPTransport) WriteString(s string) (int, error) { if !p.IsOpen() { return 0, thrift.NewTTransportException(thrift.NOT_OPEN, "Connection not open") } if p.writeBuf.Len()+len(s) > MaxLength { return 0, thrift.NewTTransportException(thrift.INVALID_DATA, "Data does not fit within one UDP packet") } n, err := p.writeBuf.WriteString(s) return n, thrift.NewTTransportExceptionFromError(err) } // Flush flushes the write buffer as one udp packet func (p *TUDPTransport) Flush() error { if !p.IsOpen() { return thrift.NewTTransportException(thrift.NOT_OPEN, "Connection not open") } _, err := p.conn.Write(p.writeBuf.Bytes()) p.writeBuf.Reset() // always reset the buffer, even in case of an error return err } golang-github-uber-go-tally-4.1.11/m3/thriftudp/transport_test.go000066400000000000000000000214631456146544700250060ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package thriftudp import ( "net" "strings" "sync" "syscall" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) var ( localListenAddr = &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)} listenConfig = net.ListenConfig{} ) func TestNewTUDPClientTransport(t *testing.T) { _, err := NewTUDPClientTransport("fakeAddressAndPort", "") require.Error(t, err) _, err = NewTUDPClientTransport("localhost:9090", "fakeaddressandport") require.Error(t, err) withLocalServer(t, func(addr string) { trans, err := NewTUDPClientTransport(addr, "") require.NoError(t, err) require.True(t, trans.IsOpen()) require.NotNil(t, trans.Addr()) // Check address assert.True(t, strings.HasPrefix(trans.Addr().String(), "127.0.0.1:"), "address check") require.Equal(t, "udp", trans.Addr().Network()) err = trans.Open() require.NoError(t, err) err = trans.Close() require.NoError(t, err) require.False(t, trans.IsOpen()) }) } func TestNewTUDPServerTransportWithListenConfig(t *testing.T) { _, err := NewTUDPServerTransportWithListenConfig("fakeAddressAndPort", listenConfig) require.Error(t, err) trans, err := NewTUDPServerTransportWithListenConfig(localListenAddr.String(), listenConfig) require.NoError(t, err) require.True(t, trans.IsOpen()) require.Equal(t, ^uint64(0), trans.RemainingBytes()) // Ensure a second server can't be created on the same address trans2, err := NewTUDPServerTransportWithListenConfig(trans.Addr().String(), listenConfig) if trans2 != nil { // close the second server if one got created trans2.Close() } require.Error(t, err) require.NoError(t, trans.Close()) require.False(t, trans.IsOpen()) // test if net.ListenConfig is used ch := make(chan struct{}) _, _ = NewTUDPServerTransportWithListenConfig( localListenAddr.String(), net.ListenConfig{ Control: func(_, _ string, _ syscall.RawConn) error { close(ch) return nil }, }, ) select { case <-ch: case <-time.After(time.Second): t.Fatal("expected control function to execute") } } func TestTUDPServerTransportIsOpen(t *testing.T) { _, err := NewTUDPServerTransport("fakeAddressAndPort") require.Error(t, err) trans, err := NewTUDPServerTransport(localListenAddr.String()) require.NoError(t, err) require.True(t, trans.IsOpen()) require.Equal(t, ^uint64(0), trans.RemainingBytes()) wg := sync.WaitGroup{} wg.Add(2) go func() { time.Sleep(2 * time.Millisecond) err = trans.Close() require.NoError(t, err) wg.Done() }() go func() { for i := 0; i < 4; i++ { time.Sleep(1 * time.Millisecond) trans.IsOpen() } wg.Done() }() wg.Wait() require.False(t, trans.IsOpen()) } func TestWriteRead(t *testing.T) { server, err := NewTUDPServerTransport(localListenAddr.String()) require.NoError(t, err) defer server.Close() client, err := NewTUDPClientTransport(server.Addr().String(), "") require.NoError(t, err) defer client.Close() n, err := client.Write([]byte("test")) require.NoError(t, err) require.Equal(t, 4, n) n, err = client.WriteString("string") require.NoError(t, err) require.Equal(t, 6, n) err = client.Flush() require.NoError(t, err) expected := []byte("teststring") readBuf := make([]byte, 20) n, err = server.Read(readBuf) require.NoError(t, err) require.Equal(t, len(expected), n) require.Equal(t, expected, readBuf[0:n]) } func TestWriteByteReadByte(t *testing.T) { server, err := NewTUDPServerTransport(localListenAddr.String()) require.NoError(t, err) defer server.Close() client, err := NewTUDPClientTransport(server.Addr().String(), "") require.NoError(t, err) client.Open() defer client.Close() for _, b := range []byte("test") { err := client.WriteByte(b) require.NoError(t, err) err = client.Flush() require.NoError(t, err) } want := []byte("test") for i := range want { b, err := server.ReadByte() require.NoError(t, err) assert.Equal(t, want[i], b, "byte %v mismatch", i) } } func TestReadByteEmptyPacket(t *testing.T) { server, err := NewTUDPServerTransport(localListenAddr.String()) require.NoError(t, err) defer server.Close() client, err := NewTUDPClientTransport(server.Addr().String(), "") require.NoError(t, err) defer client.Close() err = client.Flush() require.NoError(t, err) _, err = server.ReadByte() require.Error(t, err) } func TestIndirectCloseError(t *testing.T) { trans, err := NewTUDPServerTransport(localListenAddr.String()) require.NoError(t, err) require.True(t, trans.IsOpen()) // Close connection object directly conn := trans.Conn() require.NotNil(t, conn) err = conn.Close() require.NoError(t, err, "calling close directly on connection") err = trans.Close() require.Error(t, err, "calling close on transport") } // Note: this test is here merely to capture the existing functionality. // It's questionable whether multiple calls to Close() should succeed or not. func TestDoubleCloseIsOK(t *testing.T) { trans, err := NewTUDPServerTransport(localListenAddr.String()) require.NoError(t, err) require.True(t, trans.IsOpen()) conn := trans.Conn() require.NotNil(t, conn) err = trans.Close() require.NoError(t, err, "closing transport") err = trans.Close() require.NoError(t, err, "closing transport second time") } func TestConnClosedReadWrite(t *testing.T) { trans, err := NewTUDPServerTransport(localListenAddr.String()) require.NoError(t, err) require.True(t, trans.IsOpen()) trans.Close() require.False(t, trans.IsOpen()) _, err = trans.Read(make([]byte, 1)) require.Error(t, err) _, err = trans.ReadByte() require.Error(t, err) _, err = trans.Write([]byte("test")) require.Error(t, err) _, err = trans.WriteString("test") require.Error(t, err) err = trans.WriteByte('t') require.Error(t, err) } func TestHugeWrite(t *testing.T) { withLocalServer(t, func(addr string) { trans, err := NewTUDPClientTransport(addr, "") require.NoError(t, err) hugeMessage := make([]byte, 40000) _, err = trans.Write(hugeMessage) require.NoError(t, err) // expect buffer to exceed max _, err = trans.Write(hugeMessage) require.Error(t, err) _, err = trans.WriteString(string(hugeMessage)) require.Error(t, err) }) } func TestWriteByteLimit(t *testing.T) { withLocalServer(t, func(addr string) { trans, err := NewTUDPClientTransport(addr, "") require.NoError(t, err) hugeMessage := make([]byte, MaxLength) _, err = trans.Write(hugeMessage) require.NoError(t, err) // expect buffer to exceed max err = trans.WriteByte('a') require.Error(t, err) }) } func TestFlushErrors(t *testing.T) { withLocalServer(t, func(addr string) { trans, err := NewTUDPClientTransport(addr, "") require.NoError(t, err) // flushing closed transport trans.Close() err = trans.Flush() require.Error(t, err) // error when trying to write in flush trans, err = NewTUDPClientTransport(addr, "") require.NoError(t, err) trans.conn.Close() _, err = trans.Write([]byte{1, 2, 3, 4}) require.NoError(t, err) require.Error(t, trans.Flush(), "Flush with data should fail") }) } func TestResetInFlush(t *testing.T) { conn, err := net.ListenUDP(localListenAddr.Network(), localListenAddr) require.NoError(t, err, "ListenUDP failed") trans, err := NewTUDPClientTransport(conn.LocalAddr().String(), "") require.NoError(t, err) _, err = trans.Write([]byte("some nonsense")) require.NoError(t, err) trans.conn.Close() // close the transport's connection via back door require.NotNil(t, trans.Flush(), "should fail to write to closed connection") assert.Equal(t, 0, trans.writeBuf.Len(), "should reset the buffer") } func withLocalServer(t *testing.T, f func(addr string)) { conn, err := net.ListenUDP(localListenAddr.Network(), localListenAddr) require.NoError(t, err, "ListenUDP failed") f(conn.LocalAddr().String()) require.NoError(t, conn.Close(), "Close failed") } golang-github-uber-go-tally-4.1.11/multi/000077500000000000000000000000001456146544700201605ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/multi/README.md000066400000000000000000000005031456146544700214350ustar00rootroot00000000000000# A buffered multi reporter Combine reporters to emit to many backends. Multiple `tally.StatsReporter` as a single reporter: ```go reporter := NewMultiReporter(statsdReporter, ...) ``` Multiple `tally.CachedStatsReporter` as a single reporter: ```go reporter := NewMultiCachedReporter(m3Reporter, promReporter, ...) ``` golang-github-uber-go-tally-4.1.11/multi/reporter.go000066400000000000000000000150461456146544700223570ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package multi import ( "time" tally "github.com/uber-go/tally/v4" ) type multi struct { multiBaseReporters multiBaseReporters reporters []tally.StatsReporter } // NewMultiReporter creates a new multi tally.StatsReporter. func NewMultiReporter( r ...tally.StatsReporter, ) tally.StatsReporter { var baseReporters multiBaseReporters for _, r := range r { baseReporters = append(baseReporters, r) } return &multi{ multiBaseReporters: baseReporters, reporters: r, } } func (r *multi) ReportCounter( name string, tags map[string]string, value int64, ) { for _, r := range r.reporters { r.ReportCounter(name, tags, value) } } func (r *multi) ReportGauge( name string, tags map[string]string, value float64, ) { for _, r := range r.reporters { r.ReportGauge(name, tags, value) } } func (r *multi) ReportTimer( name string, tags map[string]string, interval time.Duration, ) { for _, r := range r.reporters { r.ReportTimer(name, tags, interval) } } func (r *multi) ReportHistogramValueSamples( name string, tags map[string]string, buckets tally.Buckets, bucketLowerBound, bucketUpperBound float64, samples int64, ) { for _, r := range r.reporters { r.ReportHistogramValueSamples(name, tags, buckets, bucketLowerBound, bucketUpperBound, samples) } } func (r *multi) ReportHistogramDurationSamples( name string, tags map[string]string, buckets tally.Buckets, bucketLowerBound, bucketUpperBound time.Duration, samples int64, ) { for _, r := range r.reporters { r.ReportHistogramDurationSamples(name, tags, buckets, bucketLowerBound, bucketUpperBound, samples) } } func (r *multi) Capabilities() tally.Capabilities { return r.multiBaseReporters.Capabilities() } func (r *multi) Flush() { r.multiBaseReporters.Flush() } type multiCached struct { multiBaseReporters multiBaseReporters reporters []tally.CachedStatsReporter } // NewMultiCachedReporter creates a new multi tally.CachedStatsReporter. func NewMultiCachedReporter( r ...tally.CachedStatsReporter, ) tally.CachedStatsReporter { var baseReporters multiBaseReporters for _, r := range r { baseReporters = append(baseReporters, r) } return &multiCached{ multiBaseReporters: baseReporters, reporters: r, } } func (r *multiCached) AllocateCounter( name string, tags map[string]string, ) tally.CachedCount { metrics := make([]tally.CachedCount, 0, len(r.reporters)) for _, r := range r.reporters { metrics = append(metrics, r.AllocateCounter(name, tags)) } return multiMetric{counters: metrics} } func (r *multiCached) AllocateGauge( name string, tags map[string]string, ) tally.CachedGauge { metrics := make([]tally.CachedGauge, 0, len(r.reporters)) for _, r := range r.reporters { metrics = append(metrics, r.AllocateGauge(name, tags)) } return multiMetric{gauges: metrics} } func (r *multiCached) AllocateTimer( name string, tags map[string]string, ) tally.CachedTimer { metrics := make([]tally.CachedTimer, 0, len(r.reporters)) for _, r := range r.reporters { metrics = append(metrics, r.AllocateTimer(name, tags)) } return multiMetric{timers: metrics} } func (r *multiCached) AllocateHistogram( name string, tags map[string]string, buckets tally.Buckets, ) tally.CachedHistogram { metrics := make([]tally.CachedHistogram, 0, len(r.reporters)) for _, r := range r.reporters { metrics = append(metrics, r.AllocateHistogram(name, tags, buckets)) } return multiMetric{histograms: metrics} } func (r *multiCached) Capabilities() tally.Capabilities { return r.multiBaseReporters.Capabilities() } func (r *multiCached) Flush() { r.multiBaseReporters.Flush() } type multiMetric struct { counters []tally.CachedCount gauges []tally.CachedGauge timers []tally.CachedTimer histograms []tally.CachedHistogram } func (m multiMetric) ReportCount(value int64) { for _, m := range m.counters { m.ReportCount(value) } } func (m multiMetric) ReportGauge(value float64) { for _, m := range m.gauges { m.ReportGauge(value) } } func (m multiMetric) ReportTimer(interval time.Duration) { for _, m := range m.timers { m.ReportTimer(interval) } } func (m multiMetric) ValueBucket( bucketLowerBound, bucketUpperBound float64, ) tally.CachedHistogramBucket { var multi []tally.CachedHistogramBucket for _, m := range m.histograms { multi = append(multi, m.ValueBucket(bucketLowerBound, bucketUpperBound)) } return multiHistogramBucket{multi} } func (m multiMetric) DurationBucket( bucketLowerBound, bucketUpperBound time.Duration, ) tally.CachedHistogramBucket { var multi []tally.CachedHistogramBucket for _, m := range m.histograms { multi = append(multi, m.DurationBucket(bucketLowerBound, bucketUpperBound)) } return multiHistogramBucket{multi} } type multiHistogramBucket struct { multi []tally.CachedHistogramBucket } func (m multiHistogramBucket) ReportSamples(value int64) { for _, m := range m.multi { m.ReportSamples(value) } } type multiBaseReporters []tally.BaseStatsReporter func (r multiBaseReporters) Capabilities() tally.Capabilities { c := &capabilities{reporting: true, tagging: true} for _, r := range r { c.reporting = c.reporting && r.Capabilities().Reporting() c.tagging = c.tagging && r.Capabilities().Tagging() } return c } func (r multiBaseReporters) Flush() { for _, r := range r { r.Flush() } } type capabilities struct { reporting bool tagging bool } func (c *capabilities) Reporting() bool { return c.reporting } func (c *capabilities) Tagging() bool { return c.tagging } golang-github-uber-go-tally-4.1.11/multi/reporter_test.go000066400000000000000000000262251456146544700234170ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package multi import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" tally "github.com/uber-go/tally/v4" ) func TestMultiReporter(t *testing.T) { a, b, c := newCapturingStatsReporter(), newCapturingStatsReporter(), newCapturingStatsReporter() all := []*capturingStatsReporter{a, b, c} r := NewMultiReporter(a, b, c) tags := []map[string]string{ {"foo": "bar"}, {"foo": "baz"}, {"foo": "qux"}, {"foo": "bzz"}, {"foo": "buz"}, } valueBuckets := tally.MustMakeLinearValueBuckets(0, 2, 5) durationBuckets := tally.MustMakeLinearDurationBuckets(0, 2*time.Second, 5) r.ReportCounter("foo", tags[0], 42) r.ReportCounter("foo", tags[0], 84) r.ReportGauge("baz", tags[1], 42.0) r.ReportTimer("qux", tags[2], 126*time.Millisecond) r.ReportHistogramValueSamples("bzz", tags[3], valueBuckets, 2.0, 4.0, 3) r.ReportHistogramDurationSamples("buz", tags[4], durationBuckets, 2*time.Second, 4*time.Second, 3) for _, r := range all { require.Equal(t, 2, len(r.counts)) assert.Equal(t, "foo", r.counts[0].name) assert.Equal(t, tags[0], r.counts[0].tags) assert.Equal(t, int64(42), r.counts[0].value) assert.Equal(t, "foo", r.counts[1].name) assert.Equal(t, tags[0], r.counts[1].tags) assert.Equal(t, int64(84), r.counts[1].value) assert.Equal(t, "baz", r.gauges[0].name) assert.Equal(t, tags[1], r.gauges[0].tags) assert.Equal(t, float64(42.0), r.gauges[0].value) assert.Equal(t, "qux", r.timers[0].name) assert.Equal(t, tags[2], r.timers[0].tags) assert.Equal(t, 126*time.Millisecond, r.timers[0].value) assert.Equal(t, "bzz", r.histogramValueSamples[0].name) assert.Equal(t, tags[3], r.histogramValueSamples[0].tags) assert.Equal(t, 2.0, r.histogramValueSamples[0].bucketLowerBound) assert.Equal(t, 4.0, r.histogramValueSamples[0].bucketUpperBound) assert.Equal(t, int64(3), r.histogramValueSamples[0].samples) assert.Equal(t, "buz", r.histogramDurationSamples[0].name) assert.Equal(t, tags[4], r.histogramDurationSamples[0].tags) assert.Equal(t, 2*time.Second, r.histogramDurationSamples[0].bucketLowerBound) assert.Equal(t, 4*time.Second, r.histogramDurationSamples[0].bucketUpperBound) assert.Equal(t, int64(3), r.histogramDurationSamples[0].samples) } assert.NotNil(t, r.Capabilities()) r.Flush() for _, r := range all { assert.Equal(t, 1, r.flush) } } func TestMultiCachedReporter(t *testing.T) { a, b, c := newCapturingStatsReporter(), newCapturingStatsReporter(), newCapturingStatsReporter() all := []*capturingStatsReporter{a, b, c} r := NewMultiCachedReporter(a, b, c) tags := []map[string]string{ {"foo": "bar"}, {"foo": "baz"}, {"foo": "qux"}, {"foo": "bzz"}, {"foo": "buz"}, } valueBuckets := tally.MustMakeLinearValueBuckets(0, 2, 5) durationBuckets := tally.MustMakeLinearDurationBuckets(0, 2*time.Second, 5) ctr := r.AllocateCounter("foo", tags[0]) ctr.ReportCount(42) ctr.ReportCount(84) gauge := r.AllocateGauge("baz", tags[1]) gauge.ReportGauge(42.0) tmr := r.AllocateTimer("qux", tags[2]) tmr.ReportTimer(126 * time.Millisecond) vhist := r.AllocateHistogram("bzz", tags[3], valueBuckets) vhist.ValueBucket(2.0, 4.0).ReportSamples(3) dhist := r.AllocateHistogram("buz", tags[4], durationBuckets) dhist.DurationBucket(2*time.Second, 4*time.Second).ReportSamples(3) for _, r := range all { require.Equal(t, 2, len(r.counts)) assert.Equal(t, "foo", r.counts[0].name) assert.Equal(t, tags[0], r.counts[0].tags) assert.Equal(t, int64(42), r.counts[0].value) assert.Equal(t, "foo", r.counts[1].name) assert.Equal(t, tags[0], r.counts[1].tags) assert.Equal(t, int64(84), r.counts[1].value) assert.Equal(t, "baz", r.gauges[0].name) assert.Equal(t, tags[1], r.gauges[0].tags) assert.Equal(t, float64(42.0), r.gauges[0].value) assert.Equal(t, "qux", r.timers[0].name) assert.Equal(t, tags[2], r.timers[0].tags) assert.Equal(t, 126*time.Millisecond, r.timers[0].value) assert.Equal(t, "bzz", r.histogramValueSamples[0].name) assert.Equal(t, tags[3], r.histogramValueSamples[0].tags) assert.Equal(t, 2.0, r.histogramValueSamples[0].bucketLowerBound) assert.Equal(t, 4.0, r.histogramValueSamples[0].bucketUpperBound) assert.Equal(t, int64(3), r.histogramValueSamples[0].samples) assert.Equal(t, "buz", r.histogramDurationSamples[0].name) assert.Equal(t, tags[4], r.histogramDurationSamples[0].tags) assert.Equal(t, 2*time.Second, r.histogramDurationSamples[0].bucketLowerBound) assert.Equal(t, 4*time.Second, r.histogramDurationSamples[0].bucketUpperBound) assert.Equal(t, int64(3), r.histogramDurationSamples[0].samples) } assert.NotNil(t, r.Capabilities()) r.Flush() for _, r := range all { assert.Equal(t, 1, r.flush) } } type capturingStatsReporter struct { counts []capturedCount gauges []capturedGauge timers []capturedTimer histogramValueSamples []capturedHistogramValueSamples histogramDurationSamples []capturedHistogramDurationSamples capabilities int flush int } type capturedCount struct { name string tags map[string]string value int64 } type capturedGauge struct { name string tags map[string]string value float64 } type capturedTimer struct { name string tags map[string]string value time.Duration } type capturedHistogramValueSamples struct { name string tags map[string]string bucketLowerBound float64 bucketUpperBound float64 samples int64 } type capturedHistogramDurationSamples struct { name string tags map[string]string bucketLowerBound time.Duration bucketUpperBound time.Duration samples int64 } func newCapturingStatsReporter() *capturingStatsReporter { return &capturingStatsReporter{} } func (r *capturingStatsReporter) ReportCounter( name string, tags map[string]string, value int64, ) { r.counts = append(r.counts, capturedCount{name, tags, value}) } func (r *capturingStatsReporter) ReportGauge( name string, tags map[string]string, value float64, ) { r.gauges = append(r.gauges, capturedGauge{name, tags, value}) } func (r *capturingStatsReporter) ReportTimer( name string, tags map[string]string, value time.Duration, ) { r.timers = append(r.timers, capturedTimer{name, tags, value}) } func (r *capturingStatsReporter) ReportHistogramValueSamples( name string, tags map[string]string, buckets tally.Buckets, bucketLowerBound, bucketUpperBound float64, samples int64, ) { elem := capturedHistogramValueSamples{ name, tags, bucketLowerBound, bucketUpperBound, samples, } r.histogramValueSamples = append(r.histogramValueSamples, elem) } func (r *capturingStatsReporter) ReportHistogramDurationSamples( name string, tags map[string]string, buckets tally.Buckets, bucketLowerBound, bucketUpperBound time.Duration, samples int64, ) { elem := capturedHistogramDurationSamples{ name, tags, bucketLowerBound, bucketUpperBound, samples, } r.histogramDurationSamples = append(r.histogramDurationSamples, elem) } func (r *capturingStatsReporter) AllocateCounter( name string, tags map[string]string, ) tally.CachedCount { return cachedCount{fn: func(value int64) { r.counts = append(r.counts, capturedCount{name, tags, value}) }} } func (r *capturingStatsReporter) AllocateGauge( name string, tags map[string]string, ) tally.CachedGauge { return cachedGauge{fn: func(value float64) { r.gauges = append(r.gauges, capturedGauge{name, tags, value}) }} } func (r *capturingStatsReporter) AllocateTimer( name string, tags map[string]string, ) tally.CachedTimer { return cachedTimer{fn: func(value time.Duration) { r.timers = append(r.timers, capturedTimer{name, tags, value}) }} } func (r *capturingStatsReporter) AllocateHistogram( name string, tags map[string]string, buckets tally.Buckets, ) tally.CachedHistogram { return cachedHistogram{ valueFn: func(bucketLowerBound, bucketUpperBound float64, samples int64) { elem := capturedHistogramValueSamples{ name, tags, bucketLowerBound, bucketUpperBound, samples, } r.histogramValueSamples = append(r.histogramValueSamples, elem) }, durationFn: func(bucketLowerBound, bucketUpperBound time.Duration, samples int64) { elem := capturedHistogramDurationSamples{ name, tags, bucketLowerBound, bucketUpperBound, samples, } r.histogramDurationSamples = append(r.histogramDurationSamples, elem) }, } } func (r *capturingStatsReporter) Capabilities() tally.Capabilities { r.capabilities++ return r } func (r *capturingStatsReporter) Reporting() bool { return true } func (r *capturingStatsReporter) Tagging() bool { return true } func (r *capturingStatsReporter) Flush() { r.flush++ } type cachedCount struct { fn func(value int64) } func (c cachedCount) ReportCount(value int64) { c.fn(value) } type cachedGauge struct { fn func(value float64) } func (c cachedGauge) ReportGauge(value float64) { c.fn(value) } type cachedTimer struct { fn func(value time.Duration) } func (c cachedTimer) ReportTimer(value time.Duration) { c.fn(value) } type cachedHistogram struct { valueFn func(bucketLowerBound, bucketUpperBound float64, samples int64) durationFn func(bucketLowerBound, bucketUpperBound time.Duration, samples int64) } func (h cachedHistogram) ValueBucket( bucketLowerBound, bucketUpperBound float64, ) tally.CachedHistogramBucket { return cachedHistogramValueBucket{&h, bucketLowerBound, bucketUpperBound} } func (h cachedHistogram) DurationBucket( bucketLowerBound, bucketUpperBound time.Duration, ) tally.CachedHistogramBucket { return cachedHistogramDurationBucket{&h, bucketLowerBound, bucketUpperBound} } type cachedHistogramValueBucket struct { histogram *cachedHistogram bucketLowerBound float64 bucketUpperBound float64 } func (b cachedHistogramValueBucket) ReportSamples(v int64) { b.histogram.valueFn(b.bucketLowerBound, b.bucketUpperBound, v) } type cachedHistogramDurationBucket struct { histogram *cachedHistogram bucketLowerBound time.Duration bucketUpperBound time.Duration } func (b cachedHistogramDurationBucket) ReportSamples(v int64) { b.histogram.durationFn(b.bucketLowerBound, b.bucketUpperBound, v) } golang-github-uber-go-tally-4.1.11/pool.go000066400000000000000000000036411456146544700203320ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally // ObjectPool is an minimalistic object pool to avoid // any circular dependencies on any other object pool. type ObjectPool struct { values chan interface{} alloc func() interface{} } // NewObjectPool creates a new pool. func NewObjectPool(size int) *ObjectPool { return &ObjectPool{ values: make(chan interface{}, size), } } // Init initializes the object pool. func (p *ObjectPool) Init(alloc func() interface{}) { p.alloc = alloc for i := 0; i < cap(p.values); i++ { p.values <- p.alloc() } } // Get gets an object from the pool. func (p *ObjectPool) Get() interface{} { var v interface{} select { case v = <-p.values: default: v = p.alloc() } return v } // Put puts an object back to the pool. func (p *ObjectPool) Put(obj interface{}) { select { case p.values <- obj: default: } } golang-github-uber-go-tally-4.1.11/prometheus/000077500000000000000000000000001456146544700212215ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/prometheus/README.md000066400000000000000000000066611456146544700225110ustar00rootroot00000000000000# A buffered Prometheus reporter See `examples/prometheus_main.go` for an end to end example. ## Options You can use a specific Prometheus registry, and you can use either summaries or histograms for timers. The reporter options are: ```go // Options is a set of options for the tally reporter. type Options struct { // Registerer is the prometheus registerer to register // metrics with. Use nil to specify the default registerer. Registerer prom.Registerer // DefaultTimerType is the default type timer type to create // when using timers. It's default value is a histogram timer type. DefaultTimerType TimerType // DefaultHistogramBuckets is the default histogram buckets // to use. Use nil to specify the default histogram buckets. DefaultHistogramBuckets []float64 // DefaultSummaryObjectives is the default summary objectives // to use. Use nil to specify the default summary objectives. DefaultSummaryObjectives map[float64]float64 // OnRegisterError defines a method to call to when registering // a metric with the registerer fails. Use nil to specify // to panic by default when registering a metric fails. OnRegisterError func(err error) } ``` The timer types are: ```go // TimerType describes a type of timer type TimerType int const ( // SummaryTimerType is a timer type that reports into a summary SummaryTimerType TimerType = iota // HistogramTimerType is a timer type that reports into a histogram HistogramTimerType ) ``` You can also pre-register help description text ahead of using a metric that will be named and tagged identically with `tally`. You can also access the Prometheus HTTP handler directly. The returned reporter interface: ```go // Reporter is a Prometheus backed tally reporter. type Reporter interface { tally.CachedStatsReporter // HTTPHandler provides the Prometheus HTTP scrape handler. HTTPHandler() http.Handler // RegisterCounter is a helper method to initialize a counter // in the Prometheus backend with a given help text. // If not called explicitly, the Reporter will create one for // you on first use, with a not super helpful HELP string. RegisterCounter( name string, tagKeys []string, desc string, ) (*prom.CounterVec, error) // RegisterGauge is a helper method to initialize a gauge // in the prometheus backend with a given help text. // If not called explicitly, the Reporter will create one for // you on first use, with a not super helpful HELP string. RegisterGauge( name string, tagKeys []string, desc string, ) (*prom.GaugeVec, error) // RegisterTimer is a helper method to initialize a timer // summary or histogram vector in the prometheus backend // with a given help text. // If not called explicitly, the Reporter will create one for // you on first use, with a not super helpful HELP string. // You may pass opts as nil to get the default timer type // and objectives/buckets. // You may also pass objectives/buckets as nil in opts to // get the default objectives/buckets for the specified // timer type. RegisterTimer( name string, tagKeys []string, desc string, opts *RegisterTimerOptions, ) (TimerUnion, error) } ``` The register timer options: ```go // RegisterTimerOptions provides options when registering a timer on demand. // By default you can pass nil for the options to get the reporter defaults. type RegisterTimerOptions struct { TimerType TimerType HistogramBuckets []float64 SummaryObjectives map[float64]float64 } ``` golang-github-uber-go-tally-4.1.11/prometheus/config.go000066400000000000000000000126121456146544700230170ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package prometheus import ( "fmt" "log" "net" "net/http" "os" "strings" prom "github.com/prometheus/client_golang/prometheus" ) // Configuration is a configuration for a Prometheus reporter. type Configuration struct { // HandlerPath if specified will be used instead of using the default // HTTP handler path "/metrics". HandlerPath string `yaml:"handlerPath"` // ListenNetwork if specified will be used instead of using tcp network. // Supported networks: tcp, tcp4, tcp6 and unix. ListenNetwork string `yaml:"listenNetwork"` // ListenAddress if specified will be used instead of just registering the // handler on the default HTTP serve mux without listening. ListenAddress string `yaml:"listenAddress"` // TimerType is the default Prometheus type to use for Tally timers. TimerType string `yaml:"timerType"` // DefaultHistogramBuckets if specified will set the default histogram // buckets to be used by the reporter. DefaultHistogramBuckets []HistogramObjective `yaml:"defaultHistogramBuckets"` // DefaultSummaryObjectives if specified will set the default summary // objectives to be used by the reporter. DefaultSummaryObjectives []SummaryObjective `yaml:"defaultSummaryObjectives"` // OnError specifies what to do when an error either with listening // on the specified listen address or registering a metric with the // Prometheus. By default the registerer will panic. OnError string `yaml:"onError"` } // HistogramObjective is a Prometheus histogram bucket. // See: https://godoc.org/github.com/prometheus/client_golang/prometheus#HistogramOpts type HistogramObjective struct { Upper float64 `yaml:"upper"` } // SummaryObjective is a Prometheus summary objective. // See: https://godoc.org/github.com/prometheus/client_golang/prometheus#SummaryOpts type SummaryObjective struct { Percentile float64 `yaml:"percentile"` AllowedError float64 `yaml:"allowedError"` } // ConfigurationOptions allows some programatic options, such as using a // specific registry and what error callback to register. type ConfigurationOptions struct { // Registry if not nil will specify the specific registry to use // for registering metrics. Registry *prom.Registry // OnError allows for customization of what to do when a metric // registration error fails, the default is to panic. OnError func(e error) } // NewReporter creates a new M3 reporter from this configuration. func (c Configuration) NewReporter( configOpts ConfigurationOptions, ) (Reporter, error) { var opts Options if configOpts.Registry != nil { opts.Registerer = configOpts.Registry } if configOpts.OnError != nil { opts.OnRegisterError = configOpts.OnError } else { switch c.OnError { case "stderr": opts.OnRegisterError = func(err error) { fmt.Fprintf(os.Stderr, "tally prometheus reporter error: %v\n", err) } case "log": opts.OnRegisterError = func(err error) { log.Printf("tally prometheus reporter error: %v\n", err) } case "none": opts.OnRegisterError = func(err error) {} default: opts.OnRegisterError = func(err error) { panic(err) } } } switch c.TimerType { case "summary": opts.DefaultTimerType = SummaryTimerType case "histogram": opts.DefaultTimerType = HistogramTimerType } if len(c.DefaultHistogramBuckets) > 0 { var values []float64 for _, value := range c.DefaultHistogramBuckets { values = append(values, value.Upper) } opts.DefaultHistogramBuckets = values } if len(c.DefaultSummaryObjectives) > 0 { values := make(map[float64]float64) for _, value := range c.DefaultSummaryObjectives { values[value.Percentile] = value.AllowedError } opts.DefaultSummaryObjectives = values } reporter := NewReporter(opts) path := "/metrics" if handlerPath := strings.TrimSpace(c.HandlerPath); handlerPath != "" { path = handlerPath } if addr := strings.TrimSpace(c.ListenAddress); addr == "" { http.Handle(path, reporter.HTTPHandler()) } else { mux := http.NewServeMux() mux.Handle(path, reporter.HTTPHandler()) go func() { network := c.ListenNetwork if network == "" { network = "tcp" } listener, err := net.Listen(network, addr) if err != nil { opts.OnRegisterError(err) return } defer listener.Close() if err = http.Serve(listener, mux); err != nil { opts.OnRegisterError(err) } }() } return reporter, nil } golang-github-uber-go-tally-4.1.11/prometheus/config_test.go000066400000000000000000000051251456146544700240570ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package prometheus import ( "io/ioutil" "net" "os" "path" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestListenErrorCallsOnRegisterError(t *testing.T) { // Ensure that Listen error calls default OnRegisterError to panic listener, err := net.Listen("tcp", "127.0.0.1:0") require.NoError(t, err) defer func() { _ = listener.Close() }() assert.NotPanics(t, func() { cfg := Configuration{ ListenAddress: listener.Addr().String(), OnError: "log", } _, _ = cfg.NewReporter(ConfigurationOptions{}) time.Sleep(time.Second) }) } func TestUnixDomainSocketListener(t *testing.T) { dir, err := ioutil.TempDir("", "tally-test-prometheus") require.NoError(t, err) defer os.RemoveAll(dir) uds := path.Join(dir, "test-metrics.sock") cfg := Configuration{ListenNetwork: "unix", ListenAddress: uds} _, _ = cfg.NewReporter(ConfigurationOptions{}) time.Sleep(time.Second) _, err = os.Stat(uds) require.NoError(t, err) require.NoError(t, os.Remove(uds)) } func TestTcpListener(t *testing.T) { cases := map[string]Configuration{ "127.0.0.1:0": {ListenAddress: "127.0.0.1:0"}, "tcp://127.0.0.1:0": {ListenNetwork: "tcp", ListenAddress: "127.0.0.1:0"}, "tcp4://127.0.0.1:0": {ListenNetwork: "tcp4", ListenAddress: "127.0.0.1:0"}, } for cn, cc := range cases { t.Run(cn, func(t *testing.T) { assert.NotPanics(t, func() { _, _ = cc.NewReporter(ConfigurationOptions{}) time.Sleep(time.Second) }) }) } } golang-github-uber-go-tally-4.1.11/prometheus/example/000077500000000000000000000000001456146544700226545ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/prometheus/example/README.md000066400000000000000000000000571456146544700241350ustar00rootroot00000000000000# Prometheus reporter example `go run ./*.go` golang-github-uber-go-tally-4.1.11/prometheus/example/prometheus_main.go000066400000000000000000000050261456146544700264050ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package main import ( "fmt" "math/rand" "net/http" "time" tally "github.com/uber-go/tally/v4" promreporter "github.com/uber-go/tally/v4/prometheus" ) func main() { r := promreporter.NewReporter(promreporter.Options{}) // Note: `promreporter.DefaultSeparator` is "_". // Prometheus doesnt like metrics with "." or "-" in them. scope, closer := tally.NewRootScope(tally.ScopeOptions{ Prefix: "my_service", Tags: map[string]string{}, CachedReporter: r, Separator: promreporter.DefaultSeparator, }, 1*time.Second) defer closer.Close() counter := scope.Tagged(map[string]string{ "foo": "bar", }).Counter("test_counter") gauge := scope.Tagged(map[string]string{ "foo": "baz", }).Gauge("test_gauge") timer := scope.Tagged(map[string]string{ "foo": "qux", }).Timer("test_timer_summary") histogram := scope.Tagged(map[string]string{ "foo": "quk", }).Histogram("test_histogram", tally.DefaultBuckets) go func() { for { counter.Inc(1) time.Sleep(time.Second) } }() go func() { for { gauge.Update(rand.Float64() * 1000) time.Sleep(time.Second) } }() go func() { for { tsw := timer.Start() hsw := histogram.Start() time.Sleep(time.Duration(rand.Float64() * float64(time.Second))) tsw.Stop() hsw.Stop() } }() http.Handle("/metrics", r.HTTPHandler()) fmt.Printf("Serving :8080/metrics\n") fmt.Printf("%v\n", http.ListenAndServe(":8080", nil)) select {} } golang-github-uber-go-tally-4.1.11/prometheus/reporter.go000066400000000000000000000375551456146544700234310ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package prometheus import ( "net/http" "strings" "sync" "time" "github.com/pkg/errors" prom "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" tally "github.com/uber-go/tally/v4" ) const ( // DefaultSeparator is the default separator that should be used with // a tally scope for a prometheus reporter. DefaultSeparator = "_" ) var ( errUnknownTimerType = errors.New("unknown metric timer type") ms = float64(time.Millisecond) / float64(time.Second) ) // DefaultHistogramBuckets is the default histogram buckets used when // creating a new Histogram in the prometheus registry. // See: https://godoc.org/github.com/prometheus/client_golang/prometheus#HistogramOpts func DefaultHistogramBuckets() []float64 { return []float64{ ms, 2 * ms, 5 * ms, 10 * ms, 20 * ms, 50 * ms, 100 * ms, 200 * ms, 500 * ms, 1000 * ms, 2000 * ms, 5000 * ms, 10000 * ms, } } // DefaultSummaryObjectives is the default objectives used when // creating a new Summary in the prometheus registry. // See: https://godoc.org/github.com/prometheus/client_golang/prometheus#SummaryOpts func DefaultSummaryObjectives() map[float64]float64 { return map[float64]float64{ 0.5: 0.01, 0.75: 0.001, 0.95: 0.001, 0.99: 0.001, 0.999: 0.0001, } } // Reporter is a Prometheus backed tally reporter. type Reporter interface { tally.CachedStatsReporter // HTTPHandler provides the Prometheus HTTP scrape handler. HTTPHandler() http.Handler // RegisterCounter is a helper method to initialize a counter // in the Prometheus backend with a given help text. // If not called explicitly, the Reporter will create one for // you on first use, with a not super helpful HELP string. RegisterCounter( name string, tagKeys []string, desc string, ) (*prom.CounterVec, error) // RegisterGauge is a helper method to initialize a gauge // in the prometheus backend with a given help text. // If not called explicitly, the Reporter will create one for // you on first use, with a not super helpful HELP string. RegisterGauge( name string, tagKeys []string, desc string, ) (*prom.GaugeVec, error) // RegisterTimer is a helper method to initialize a timer // summary or histogram vector in the prometheus backend // with a given help text. // If not called explicitly, the Reporter will create one for // you on first use, with a not super helpful HELP string. // You may pass opts as nil to get the default timer type // and objectives/buckets. // You may also pass objectives/buckets as nil in opts to // get the default objectives/buckets for the specified // timer type. RegisterTimer( name string, tagKeys []string, desc string, opts *RegisterTimerOptions, ) (TimerUnion, error) } // RegisterTimerOptions provides options when registering a timer on demand. // By default you can pass nil for the options to get the reporter defaults. type RegisterTimerOptions struct { TimerType TimerType HistogramBuckets []float64 SummaryObjectives map[float64]float64 } // TimerUnion is a representation of either a summary or a histogram // described by the TimerType. type TimerUnion struct { TimerType TimerType Histogram *prom.HistogramVec Summary *prom.SummaryVec } type metricID string type reporter struct { sync.RWMutex registerer prom.Registerer gatherer prom.Gatherer timerType TimerType objectives map[float64]float64 buckets []float64 onRegisterError func(e error) counters map[metricID]*prom.CounterVec gauges map[metricID]*prom.GaugeVec timers map[metricID]*promTimerVec } type promTimerVec struct { summary *prom.SummaryVec histogram *prom.HistogramVec } type cachedMetric struct { counter prom.Counter gauge prom.Gauge reportTimer func(d time.Duration) histogram prom.Observer summary prom.Observer } func (m *cachedMetric) ReportCount(value int64) { m.counter.Add(float64(value)) } func (m *cachedMetric) ReportGauge(value float64) { m.gauge.Set(value) } func (m *cachedMetric) ReportTimer(interval time.Duration) { m.reportTimer(interval) } func (m *cachedMetric) reportTimerHistogram(interval time.Duration) { m.histogram.Observe(float64(interval) / float64(time.Second)) } func (m *cachedMetric) reportTimerSummary(interval time.Duration) { m.summary.Observe(float64(interval) / float64(time.Second)) } func (m *cachedMetric) ValueBucket( bucketLowerBound, bucketUpperBound float64, ) tally.CachedHistogramBucket { return cachedHistogramBucket{m, bucketUpperBound} } func (m *cachedMetric) DurationBucket( bucketLowerBound, bucketUpperBound time.Duration, ) tally.CachedHistogramBucket { upperBound := float64(bucketUpperBound) / float64(time.Second) return cachedHistogramBucket{m, upperBound} } type cachedHistogramBucket struct { metric *cachedMetric upperBound float64 } func (b cachedHistogramBucket) ReportSamples(value int64) { for i := int64(0); i < value; i++ { b.metric.histogram.Observe(b.upperBound) } } type noopMetric struct{} func (m noopMetric) ReportCount(value int64) {} func (m noopMetric) ReportGauge(value float64) {} func (m noopMetric) ReportTimer(interval time.Duration) {} func (m noopMetric) ReportSamples(value int64) {} func (m noopMetric) ValueBucket(lower, upper float64) tally.CachedHistogramBucket { return m } func (m noopMetric) DurationBucket(lower, upper time.Duration) tally.CachedHistogramBucket { return m } func (r *reporter) HTTPHandler() http.Handler { return promhttp.HandlerFor(r.gatherer, promhttp.HandlerOpts{}) } // TimerType describes a type of timer type TimerType int const ( // SummaryTimerType is a timer type that reports into a summary SummaryTimerType TimerType = iota // HistogramTimerType is a timer type that reports into a histogram HistogramTimerType ) // Options is a set of options for the tally reporter. type Options struct { // Registerer is the prometheus registerer to register // metrics with. Use nil to specify the default registerer. Registerer prom.Registerer // Gatherer is the prometheus gatherer to gather // metrics with. Use nil to specify the default gatherer. Gatherer prom.Gatherer // DefaultTimerType is the default type timer type to create // when using timers. It's default value is a summary timer type. DefaultTimerType TimerType // DefaultHistogramBuckets is the default histogram buckets // to use. Use nil to specify the default histogram buckets. DefaultHistogramBuckets []float64 // DefaultSummaryObjectives is the default summary objectives // to use. Use nil to specify the default summary objectives. DefaultSummaryObjectives map[float64]float64 // OnRegisterError defines a method to call to when registering // a metric with the registerer fails. Use nil to specify // to panic by default when registering fails. OnRegisterError func(err error) } // NewReporter returns a new Reporter for Prometheus client backed metrics // objectives is the objectives used when creating a new Summary histogram for Timers. See // https://godoc.org/github.com/prometheus/client_golang/prometheus#SummaryOpts for more details. func NewReporter(opts Options) Reporter { if opts.Registerer == nil { opts.Registerer = prom.DefaultRegisterer } else { // A specific registerer was set, check if it's a registry and if // no gatherer was set, then use that as the gatherer if reg, ok := opts.Registerer.(*prom.Registry); ok && opts.Gatherer == nil { opts.Gatherer = reg } } if opts.Gatherer == nil { opts.Gatherer = prom.DefaultGatherer } if opts.DefaultHistogramBuckets == nil { opts.DefaultHistogramBuckets = DefaultHistogramBuckets() } if opts.DefaultSummaryObjectives == nil { opts.DefaultSummaryObjectives = DefaultSummaryObjectives() } if opts.OnRegisterError == nil { opts.OnRegisterError = func(err error) { // n.b. Because our forked Prometheus client does not actually emit // this message as a concrete error type (it uses fmt.Errorf), // we need to check the error message. if strings.Contains(err.Error(), "previously registered") { err = errors.WithMessagef( err, "potential tally.Scope() vs Prometheus usage contract mismatch: "+ "if this occurs after using Scope.Tagged(), different metric "+ "names must be used than were registered with the parent scope", ) } panic(err) } } return &reporter{ registerer: opts.Registerer, gatherer: opts.Gatherer, timerType: opts.DefaultTimerType, buckets: opts.DefaultHistogramBuckets, objectives: opts.DefaultSummaryObjectives, onRegisterError: opts.OnRegisterError, counters: make(map[metricID]*prom.CounterVec), gauges: make(map[metricID]*prom.GaugeVec), timers: make(map[metricID]*promTimerVec), } } func (r *reporter) RegisterCounter( name string, tagKeys []string, desc string, ) (*prom.CounterVec, error) { return r.counterVec(name, tagKeys, desc) } func (r *reporter) counterVec( name string, tagKeys []string, desc string, ) (*prom.CounterVec, error) { id := canonicalMetricID(name, tagKeys) r.Lock() defer r.Unlock() if ctr, ok := r.counters[id]; ok { return ctr, nil } ctr := prom.NewCounterVec( prom.CounterOpts{ Name: name, Help: desc, }, tagKeys, ) if err := r.registerer.Register(ctr); err != nil { return nil, err } r.counters[id] = ctr return ctr, nil } // AllocateCounter implements tally.CachedStatsReporter. func (r *reporter) AllocateCounter(name string, tags map[string]string) tally.CachedCount { tagKeys := keysFromMap(tags) counterVec, err := r.counterVec(name, tagKeys, name+" counter") if err != nil { r.onRegisterError(err) return noopMetric{} } return &cachedMetric{counter: counterVec.With(tags)} } func (r *reporter) RegisterGauge( name string, tagKeys []string, desc string, ) (*prom.GaugeVec, error) { return r.gaugeVec(name, tagKeys, desc) } func (r *reporter) gaugeVec( name string, tagKeys []string, desc string, ) (*prom.GaugeVec, error) { id := canonicalMetricID(name, tagKeys) r.Lock() defer r.Unlock() if g, ok := r.gauges[id]; ok { return g, nil } g := prom.NewGaugeVec( prom.GaugeOpts{ Name: name, Help: desc, }, tagKeys, ) if err := r.registerer.Register(g); err != nil { return nil, err } r.gauges[id] = g return g, nil } // AllocateGauge implements tally.CachedStatsReporter. func (r *reporter) AllocateGauge(name string, tags map[string]string) tally.CachedGauge { tagKeys := keysFromMap(tags) gaugeVec, err := r.gaugeVec(name, tagKeys, name+" gauge") if err != nil { r.onRegisterError(err) return noopMetric{} } return &cachedMetric{gauge: gaugeVec.With(tags)} } func (r *reporter) RegisterTimer( name string, tagKeys []string, desc string, opts *RegisterTimerOptions, ) (TimerUnion, error) { timerType, buckets, objectives := r.timerConfig(opts) switch timerType { case HistogramTimerType: h, err := r.histogramVec(name, tagKeys, desc, buckets) return TimerUnion{TimerType: timerType, Histogram: h}, err case SummaryTimerType: s, err := r.summaryVec(name, tagKeys, desc, objectives) return TimerUnion{TimerType: timerType, Summary: s}, err } return TimerUnion{}, errUnknownTimerType } func (r *reporter) timerConfig( opts *RegisterTimerOptions, ) ( timerType TimerType, buckets []float64, objectives map[float64]float64, ) { timerType = r.timerType objectives = r.objectives buckets = r.buckets if opts != nil { timerType = opts.TimerType if opts.SummaryObjectives != nil { objectives = opts.SummaryObjectives } if opts.HistogramBuckets != nil { buckets = opts.HistogramBuckets } } return } func (r *reporter) summaryVec( name string, tagKeys []string, desc string, objectives map[float64]float64, ) (*prom.SummaryVec, error) { id := canonicalMetricID(name, tagKeys) r.Lock() defer r.Unlock() if s, ok := r.timers[id]; ok { return s.summary, nil } s := prom.NewSummaryVec( prom.SummaryOpts{ Name: name, Help: desc, Objectives: objectives, }, tagKeys, ) if err := r.registerer.Register(s); err != nil { return nil, err } r.timers[id] = &promTimerVec{summary: s} return s, nil } func (r *reporter) histogramVec( name string, tagKeys []string, desc string, buckets []float64, ) (*prom.HistogramVec, error) { id := canonicalMetricID(name, tagKeys) r.Lock() defer r.Unlock() if h, ok := r.timers[id]; ok { return h.histogram, nil } h := prom.NewHistogramVec( prom.HistogramOpts{ Name: name, Help: desc, Buckets: buckets, }, tagKeys, ) if err := r.registerer.Register(h); err != nil { return nil, err } r.timers[id] = &promTimerVec{histogram: h} return h, nil } // AllocateTimer implements tally.CachedStatsReporter. func (r *reporter) AllocateTimer(name string, tags map[string]string) tally.CachedTimer { var ( timer tally.CachedTimer err error ) tagKeys := keysFromMap(tags) timerType, buckets, objectives := r.timerConfig(nil) switch timerType { case HistogramTimerType: var histogramVec *prom.HistogramVec histogramVec, err = r.histogramVec(name, tagKeys, name+" histogram", buckets) if err == nil { t := &cachedMetric{histogram: histogramVec.With(tags)} t.reportTimer = t.reportTimerHistogram timer = t } case SummaryTimerType: var summaryVec *prom.SummaryVec summaryVec, err = r.summaryVec(name, tagKeys, name+" summary", objectives) if err == nil { t := &cachedMetric{summary: summaryVec.With(tags)} t.reportTimer = t.reportTimerSummary timer = t } default: err = errUnknownTimerType } if err != nil { r.onRegisterError(err) return noopMetric{} } return timer } func (r *reporter) AllocateHistogram( name string, tags map[string]string, buckets tally.Buckets, ) tally.CachedHistogram { tagKeys := keysFromMap(tags) histogramVec, err := r.histogramVec(name, tagKeys, name+" histogram", buckets.AsValues()) if err != nil { r.onRegisterError(err) return noopMetric{} } return &cachedMetric{histogram: histogramVec.With(tags)} } func (r *reporter) Capabilities() tally.Capabilities { return r } func (r *reporter) Reporting() bool { return true } func (r *reporter) Tagging() bool { return true } // Flush does nothing for prometheus func (r *reporter) Flush() {} var metricIDKeyValue = "1" // NOTE: this generates a canonical MetricID for a given name+label keys, // not values. This omits label values, as we track metrics as // Vectors in order to support on-the-fly label changes. func canonicalMetricID(name string, tagKeys []string) metricID { keySet := make(map[string]string, len(tagKeys)) for _, key := range tagKeys { keySet[key] = metricIDKeyValue } return metricID(tally.KeyForPrefixedStringMap(name, keySet)) } func keysFromMap(m map[string]string) []string { labelKeys := make([]string, len(m)) i := 0 for k := range m { labelKeys[i] = k i++ } return labelKeys } golang-github-uber-go-tally-4.1.11/prometheus/reporter_test.go000066400000000000000000000362611456146544700244610ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package prometheus import ( "fmt" "testing" "time" prom "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" tally "github.com/uber-go/tally/v4" ) // NB(r): If a test is failing, you can debug what is being // gathered from Prometheus by printing the following: // proto.MarshalTextString(gather(t, registry)[0]) func TestCounter(t *testing.T) { registry := prom.NewRegistry() r := NewReporter(Options{Registerer: registry}) name := "test_counter" tags := map[string]string{ "foo": "bar", "test": "everything", } tags2 := map[string]string{ "foo": "baz", "test": "something", } count := r.AllocateCounter(name, tags) count.ReportCount(1) count.ReportCount(2) count = r.AllocateCounter(name, tags2) count.ReportCount(2) assertMetric(t, gather(t, registry), metric{ name: name, mtype: dto.MetricType_COUNTER, instances: []instance{ { labels: tags, counter: counterValue(3), }, { labels: tags2, counter: counterValue(2), }, }, }) } func TestGauge(t *testing.T) { registry := prom.NewRegistry() r := NewReporter(Options{Registerer: registry}) name := "test_gauge" tags := map[string]string{ "foo": "bar", "test": "everything", } gauge := r.AllocateGauge(name, tags) gauge.ReportGauge(15) gauge.ReportGauge(30) assertMetric(t, gather(t, registry), metric{ name: name, mtype: dto.MetricType_GAUGE, instances: []instance{ { labels: tags, gauge: gaugeValue(30), }, }, }) } func TestTimerHistogram(t *testing.T) { registry := prom.NewRegistry() r := NewReporter(Options{ Registerer: registry, DefaultTimerType: HistogramTimerType, DefaultHistogramBuckets: []float64{ 50 * ms, 250 * ms, 1000 * ms, 2500 * ms, 10000 * ms, }, }) name := "test_timer" tags := map[string]string{ "foo": "bar", "test": "everything", } tags2 := map[string]string{ "foo": "baz", "test": "something", } vals := []time.Duration{ 23 * time.Millisecond, 223 * time.Millisecond, 320 * time.Millisecond, } vals2 := []time.Duration{ 1742 * time.Millisecond, 3232 * time.Millisecond, } timer := r.AllocateTimer(name, tags) for _, v := range vals { timer.ReportTimer(v) } timer = r.AllocateTimer(name, tags2) for _, v := range vals2 { timer.ReportTimer(v) } assertMetric(t, gather(t, registry), metric{ name: name, mtype: dto.MetricType_HISTOGRAM, instances: []instance{ { labels: tags, histogram: histogramValue(histogramVal{ sampleCount: uint64(len(vals)), sampleSum: durationFloatSum(vals), buckets: []histogramValBucket{ {upperBound: 0.05, count: 1}, {upperBound: 0.25, count: 2}, {upperBound: 1.00, count: 3}, {upperBound: 2.50, count: 3}, {upperBound: 10.00, count: 3}, }, }), }, { labels: tags2, histogram: histogramValue(histogramVal{ sampleCount: uint64(len(vals2)), sampleSum: durationFloatSum(vals2), buckets: []histogramValBucket{ {upperBound: 0.05, count: 0}, {upperBound: 0.25, count: 0}, {upperBound: 1.00, count: 0}, {upperBound: 2.50, count: 1}, {upperBound: 10.00, count: 2}, }, }), }, }, }) } func TestTimerSummary(t *testing.T) { registry := prom.NewRegistry() r := NewReporter(Options{ Registerer: registry, DefaultTimerType: SummaryTimerType, DefaultSummaryObjectives: map[float64]float64{ 0.5: 0.01, 0.75: 0.001, 0.95: 0.001, 0.99: 0.001, 0.999: 0.0001, }, }) name := "test_timer" tags := map[string]string{ "foo": "bar", "test": "everything", } tags2 := map[string]string{ "foo": "baz", "test": "something", } vals := []time.Duration{ 23 * time.Millisecond, 223 * time.Millisecond, 320 * time.Millisecond, } vals2 := []time.Duration{ 1742 * time.Millisecond, 3232 * time.Millisecond, } timer := r.AllocateTimer(name, tags) for _, v := range vals { timer.ReportTimer(v) } timer = r.AllocateTimer(name, tags2) for _, v := range vals2 { timer.ReportTimer(v) } assertMetric(t, gather(t, registry), metric{ name: name, mtype: dto.MetricType_SUMMARY, instances: []instance{ { labels: tags, summary: summaryValue(summaryVal{ sampleCount: uint64(len(vals)), sampleSum: durationFloatSum(vals), quantiles: []summaryValQuantile{ {quantile: 0.50, value: 0.223}, {quantile: 0.75, value: 0.32}, {quantile: 0.95, value: 0.32}, {quantile: 0.99, value: 0.32}, {quantile: 0.999, value: 0.32}, }, }), }, { labels: tags2, summary: summaryValue(summaryVal{ sampleCount: uint64(len(vals2)), sampleSum: durationFloatSum(vals2), quantiles: []summaryValQuantile{ {quantile: 0.50, value: 1.742}, {quantile: 0.75, value: 3.232}, {quantile: 0.95, value: 3.232}, {quantile: 0.99, value: 3.232}, {quantile: 0.999, value: 3.232}, }, }), }, }, }) } func TestHistogramBucketValues(t *testing.T) { registry := prom.NewRegistry() r := NewReporter(Options{ Registerer: registry, }) buckets := tally.DurationBuckets{ 0 * time.Millisecond, 50 * time.Millisecond, 250 * time.Millisecond, 1000 * time.Millisecond, 2500 * time.Millisecond, 10000 * time.Millisecond, } name := "test_histogram" tags := map[string]string{ "foo": "bar", "test": "everything", } tags2 := map[string]string{ "foo": "baz", "test": "something", } vals := []time.Duration{ 23 * time.Millisecond, 223 * time.Millisecond, 320 * time.Millisecond, } vals2 := []time.Duration{ 1742 * time.Millisecond, 3232 * time.Millisecond, } histogram := r.AllocateHistogram(name, tags, buckets) histogram.DurationBucket(0, 50*time.Millisecond).ReportSamples(1) histogram.DurationBucket(0, 250*time.Millisecond).ReportSamples(1) histogram.DurationBucket(0, 1000*time.Millisecond).ReportSamples(1) histogram = r.AllocateHistogram(name, tags2, buckets) histogram.DurationBucket(0, 2500*time.Millisecond).ReportSamples(1) histogram.DurationBucket(0, 10000*time.Millisecond).ReportSamples(1) assertMetric(t, gather(t, registry), metric{ name: name, mtype: dto.MetricType_HISTOGRAM, instances: []instance{ { labels: tags, histogram: histogramValue(histogramVal{ sampleCount: uint64(len(vals)), sampleSum: 1.3, buckets: []histogramValBucket{ {upperBound: 0.00, count: 0}, {upperBound: 0.05, count: 1}, {upperBound: 0.25, count: 2}, {upperBound: 1.00, count: 3}, {upperBound: 2.50, count: 3}, {upperBound: 10.00, count: 3}, }, }), }, { labels: tags2, histogram: histogramValue(histogramVal{ sampleCount: uint64(len(vals2)), sampleSum: 12.5, buckets: []histogramValBucket{ {upperBound: 0.00, count: 0}, {upperBound: 0.05, count: 0}, {upperBound: 0.25, count: 0}, {upperBound: 1.00, count: 0}, {upperBound: 2.50, count: 1}, {upperBound: 10.00, count: 2}, }, }), }, }, }) } func TestOnRegisterError(t *testing.T) { var captured []error registry := prom.NewRegistry() r := NewReporter(Options{ Registerer: registry, OnRegisterError: func(err error) { captured = append(captured, err) }, }) c := r.AllocateCounter("bad-name", nil) c.ReportCount(2) c.ReportCount(4) c = r.AllocateCounter("bad.name", nil) c.ReportCount(42) c.ReportCount(84) assert.Equal(t, 2, len(captured)) } func TestAlreadyRegisteredCounter(t *testing.T) { var captured []error registry := prom.NewRegistry() r := NewReporter(Options{ Registerer: registry, OnRegisterError: func(err error) { captured = append(captured, err) }, }) // n.b. Prometheus metrics are different from M3 metrics in that they are // uniquely identified as "metric_name+label_name+label_name+..."; // additionally, given that Prometheus ingestion is pull-based, there // is only ever one reporter used regardless of tally.Scope hierarchy. // // Because of this, for a given metric "foo", only the first-registered // permutation of metric and label names will succeed because the same // registry is being used, and because Prometheus asserts that a // registered metric name has the same corresponding label names. // Subsequent registrations - such as adding or removing tags - will // return an error. // // This is a problem because Tally's API does not apply semantics or // restrictions to the combinatorics (or descendant mutations of) // metric tags. As such, we must assert that Tally's Prometheus // reporter does the right thing and indicates an error when this // happens. // // The first allocation call will succeed. This establishes the required // label names (["foo"]) for metric "foo". r.AllocateCounter("foo", map[string]string{"foo": "bar"}) // The second allocation call is okay, as it has the same label names (["foo"]). r.AllocateCounter("foo", map[string]string{"foo": "baz"}) // The third allocation call fails, because it has different label names // (["bar"], vs previously ["foo"]) for the same metric name "foo". r.AllocateCounter("foo", map[string]string{"bar": "qux"}) // The fourth allocation call fails, because while it has one of the same // label names ("foo") as was previously registered for metric "foo", it // also has an additional label name (["foo", "zork"] != ["foo"]). r.AllocateCounter("foo", map[string]string{ "foo": "bar", "zork": "derp", }) // The fifth allocation call fails, because it has no label names for the // metric "foo", which expects the label names it was originally registered // with (["foo"]). r.AllocateCounter("foo", nil) require.Equal(t, 3, len(captured)) for _, err := range captured { require.Contains(t, err.Error(), "same fully-qualified name") } } func gather(t *testing.T, r prom.Gatherer) []*dto.MetricFamily { metrics, err := r.Gather() require.NoError(t, err) return metrics } func counterValue(v float64) *dto.Counter { return &dto.Counter{Value: &v} } func gaugeValue(v float64) *dto.Gauge { return &dto.Gauge{Value: &v} } type histogramVal struct { sampleCount uint64 sampleSum float64 buckets []histogramValBucket } type histogramValBucket struct { count uint64 upperBound float64 } func histogramValue(v histogramVal) *dto.Histogram { r := &dto.Histogram{ SampleCount: &v.sampleCount, SampleSum: &v.sampleSum, } for _, b := range v.buckets { b := b // or else the addresses we take will be static r.Bucket = append(r.Bucket, &dto.Bucket{ CumulativeCount: &b.count, UpperBound: &b.upperBound, }) } return r } type summaryVal struct { sampleCount uint64 sampleSum float64 quantiles []summaryValQuantile } type summaryValQuantile struct { quantile float64 value float64 } func summaryValue(v summaryVal) *dto.Summary { r := &dto.Summary{ SampleCount: &v.sampleCount, SampleSum: &v.sampleSum, } for _, q := range v.quantiles { q := q // or else the addresses we take will be static r.Quantile = append(r.Quantile, &dto.Quantile{ Quantile: &q.quantile, Value: &q.value, }) } return r } func durationFloatSum(v []time.Duration) float64 { var sum float64 for _, d := range v { sum += durationFloat(d) } return sum } func durationFloat(d time.Duration) float64 { return float64(d) / float64(time.Second) } type metric struct { name string mtype dto.MetricType instances []instance } type instance struct { labels map[string]string counter *dto.Counter gauge *dto.Gauge histogram *dto.Histogram summary *dto.Summary } func assertMetric( t *testing.T, metrics []*dto.MetricFamily, query metric, ) { q := query msgFmt := func(msg string, v ...interface{}) string { prefix := fmt.Sprintf("assert fail for metric name=%s, type=%s: ", q.name, q.mtype.String()) return fmt.Sprintf(prefix+msg, v...) } for _, m := range metrics { if m.GetName() != q.name || m.GetType() != q.mtype { continue } if len(q.instances) == 0 { require.Fail(t, msgFmt("no instances to assert")) } for _, i := range q.instances { found := false for _, j := range m.GetMetric() { if len(i.labels) != len(j.GetLabel()) { continue } notMatched := make(map[string]string, len(i.labels)) for k, v := range i.labels { notMatched[k] = v } for _, pair := range j.GetLabel() { notMatchedValue, matches := notMatched[pair.GetName()] if matches && pair.GetValue() == notMatchedValue { delete(notMatched, pair.GetName()) } } if len(notMatched) != 0 { continue } found = true switch { case i.counter != nil: require.NotNil(t, j.GetCounter()) assert.Equal(t, i.counter.GetValue(), j.GetCounter().GetValue()) case i.gauge != nil: require.NotNil(t, j.GetGauge()) assert.Equal(t, i.gauge.GetValue(), j.GetGauge().GetValue()) case i.histogram != nil: require.NotNil(t, j.GetHistogram()) assert.Equal(t, i.histogram.GetSampleCount(), j.GetHistogram().GetSampleCount()) assert.Equal(t, i.histogram.GetSampleSum(), j.GetHistogram().GetSampleSum()) require.Equal(t, len(i.histogram.GetBucket()), len(j.GetHistogram().GetBucket())) for idx, b := range i.histogram.GetBucket() { actual := j.GetHistogram().GetBucket()[idx] assert.Equal(t, b.GetCumulativeCount(), actual.GetCumulativeCount()) assert.Equal(t, b.GetUpperBound(), actual.GetUpperBound()) } case i.summary != nil: require.NotNil(t, j.GetSummary()) assert.Equal(t, i.summary.GetSampleCount(), j.GetSummary().GetSampleCount()) assert.Equal(t, i.summary.GetSampleSum(), j.GetSummary().GetSampleSum()) require.Equal(t, len(i.summary.GetQuantile()), len(j.GetSummary().GetQuantile())) for idx, q := range i.summary.GetQuantile() { actual := j.GetSummary().GetQuantile()[idx] assert.Equal(t, q.GetQuantile(), actual.GetQuantile()) assert.Equal(t, q.GetValue(), actual.GetValue()) } } } if !found { require.Fail(t, msgFmt("instance not found labels=%v", i.labels)) } } return } require.Fail(t, msgFmt("metric not found")) } golang-github-uber-go-tally-4.1.11/prometheus/sanitize.go000066400000000000000000000033321456146544700233770ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package prometheus import ( tally "github.com/uber-go/tally/v4" ) // DefaultSanitizerOpts are the options for the default Prometheus sanitizer. var DefaultSanitizerOpts = tally.SanitizeOptions{ NameCharacters: tally.ValidCharacters{ Ranges: tally.AlphanumericRange, Characters: tally.UnderscoreCharacters, }, KeyCharacters: tally.ValidCharacters{ Ranges: tally.AlphanumericRange, Characters: tally.UnderscoreCharacters, }, ValueCharacters: tally.ValidCharacters{ Ranges: tally.AlphanumericRange, Characters: tally.UnderscoreCharacters, }, ReplacementCharacter: tally.DefaultReplacementCharacter, } golang-github-uber-go-tally-4.1.11/reporter.go000066400000000000000000000077271456146544700212340ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import "time" // BaseStatsReporter implements the shared reporter methods. type BaseStatsReporter interface { // Capabilities returns the capabilities description of the reporter. Capabilities() Capabilities // Flush asks the reporter to flush all reported values. Flush() } // StatsReporter is a backend for Scopes to report metrics to. type StatsReporter interface { BaseStatsReporter // ReportCounter reports a counter value ReportCounter( name string, tags map[string]string, value int64, ) // ReportGauge reports a gauge value ReportGauge( name string, tags map[string]string, value float64, ) // ReportTimer reports a timer value ReportTimer( name string, tags map[string]string, interval time.Duration, ) // ReportHistogramValueSamples reports histogram samples for a bucket ReportHistogramValueSamples( name string, tags map[string]string, buckets Buckets, bucketLowerBound, bucketUpperBound float64, samples int64, ) // ReportHistogramDurationSamples reports histogram samples for a bucket ReportHistogramDurationSamples( name string, tags map[string]string, buckets Buckets, bucketLowerBound, bucketUpperBound time.Duration, samples int64, ) } // CachedStatsReporter is a backend for Scopes that pre allocates all // counter, gauges, timers & histograms. This is harder to implement but more performant. type CachedStatsReporter interface { BaseStatsReporter // AllocateCounter pre allocates a counter data structure with name & tags. AllocateCounter( name string, tags map[string]string, ) CachedCount // AllocateGauge pre allocates a gauge data structure with name & tags. AllocateGauge( name string, tags map[string]string, ) CachedGauge // AllocateTimer pre allocates a timer data structure with name & tags. AllocateTimer( name string, tags map[string]string, ) CachedTimer // AllocateHistogram pre allocates a histogram data structure with name, tags, // value buckets and duration buckets. AllocateHistogram( name string, tags map[string]string, buckets Buckets, ) CachedHistogram } // CachedCount interface for reporting an individual counter type CachedCount interface { ReportCount(value int64) } // CachedGauge interface for reporting an individual gauge type CachedGauge interface { ReportGauge(value float64) } // CachedTimer interface for reporting an individual timer type CachedTimer interface { ReportTimer(interval time.Duration) } // CachedHistogram interface for reporting histogram samples to buckets type CachedHistogram interface { ValueBucket( bucketLowerBound, bucketUpperBound float64, ) CachedHistogramBucket DurationBucket( bucketLowerBound, bucketUpperBound time.Duration, ) CachedHistogramBucket } // CachedHistogramBucket interface for reporting histogram samples to a specific bucket type CachedHistogramBucket interface { ReportSamples(value int64) } golang-github-uber-go-tally-4.1.11/sanitize.go000066400000000000000000000122551456146544700212100ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "bytes" "sync" ) var ( // DefaultReplacementCharacter is the default character used for // replacements. DefaultReplacementCharacter = '_' // AlphanumericRange is the range of alphanumeric characters. AlphanumericRange = []SanitizeRange{ {rune('a'), rune('z')}, {rune('A'), rune('Z')}, {rune('0'), rune('9')}} // UnderscoreCharacters is just an underscore character. UnderscoreCharacters = []rune{ '_'} // UnderscoreDashCharacters is a slice of underscore, and // dash characters. UnderscoreDashCharacters = []rune{ '-', '_'} // UnderscoreDashDotCharacters is a slice of underscore, // dash, and dot characters. UnderscoreDashDotCharacters = []rune{ '.', '-', '_'} ) // SanitizeFn returns a sanitized version of the input string. type SanitizeFn func(string) string // SanitizeRange is a range of characters (inclusive on both ends). type SanitizeRange [2]rune // ValidCharacters is a collection of valid characters. type ValidCharacters struct { Ranges []SanitizeRange Characters []rune } // SanitizeOptions are the set of configurable options for sanitisation. type SanitizeOptions struct { NameCharacters ValidCharacters KeyCharacters ValidCharacters ValueCharacters ValidCharacters ReplacementCharacter rune } // Sanitizer sanitizes the provided input based on the function executed. type Sanitizer interface { // Name sanitizes the provided `name` string. Name(n string) string // Key sanitizes the provided `key` string. Key(k string) string // Value sanitizes the provided `value` string. Value(v string) string } // NewSanitizer returns a new sanitizer based on provided options. func NewSanitizer(opts SanitizeOptions) Sanitizer { return sanitizer{ nameFn: opts.NameCharacters.sanitizeFn(opts.ReplacementCharacter), keyFn: opts.KeyCharacters.sanitizeFn(opts.ReplacementCharacter), valueFn: opts.ValueCharacters.sanitizeFn(opts.ReplacementCharacter), } } // NoOpSanitizeFn returns the input un-touched. func NoOpSanitizeFn(v string) string { return v } // NewNoOpSanitizer returns a sanitizer which returns all inputs un-touched. func NewNoOpSanitizer() Sanitizer { return sanitizer{ nameFn: NoOpSanitizeFn, keyFn: NoOpSanitizeFn, valueFn: NoOpSanitizeFn, } } type sanitizer struct { nameFn SanitizeFn keyFn SanitizeFn valueFn SanitizeFn } func (s sanitizer) Name(n string) string { return s.nameFn(n) } func (s sanitizer) Key(k string) string { return s.keyFn(k) } func (s sanitizer) Value(v string) string { return s.valueFn(v) } var _sanitizeBuffers = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } func getSanitizeBuffer() *bytes.Buffer { return _sanitizeBuffers.Get().(*bytes.Buffer) } func putSanitizeBuffer(b *bytes.Buffer) { b.Reset() _sanitizeBuffers.Put(b) } func (c *ValidCharacters) sanitizeFn(repChar rune) SanitizeFn { return func(value string) string { var buf *bytes.Buffer for idx, ch := range value { // first check if the provided character is valid validCurr := false for i := 0; !validCurr && i < len(c.Ranges); i++ { if ch >= c.Ranges[i][0] && ch <= c.Ranges[i][1] { validCurr = true break } } for i := 0; !validCurr && i < len(c.Characters); i++ { if c.Characters[i] == ch { validCurr = true break } } // if it's valid, we can optimise allocations by avoiding copying if validCurr { if buf == nil { continue // haven't deviated from string, still no need to init buffer } buf.WriteRune(ch) // we've deviated from string, write to buffer continue } // ie the character is invalid, and the buffer has not been initialised // so we initialise buffer and backfill if buf == nil { buf = getSanitizeBuffer() if idx > 0 { buf.WriteString(value[:idx]) } } // write the replacement character buf.WriteRune(repChar) } // return input un-touched if the buffer has been not initialised if buf == nil { return value } // otherwise, return the newly constructed buffer result := buf.String() putSanitizeBuffer(buf) return result } } golang-github-uber-go-tally-4.1.11/sanitize_test.go000066400000000000000000000040671456146544700222510ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "testing" "github.com/stretchr/testify/require" ) func newTestSanitizer() SanitizeFn { c := &ValidCharacters{ Ranges: AlphanumericRange, Characters: UnderscoreDashCharacters, } return c.sanitizeFn(DefaultReplacementCharacter) } func TestSanitizeIdentifierAllValidCharacters(t *testing.T) { allValidChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" fn := newTestSanitizer() require.Equal(t, allValidChars, fn(allValidChars)) } func TestSanitizeTestCases(t *testing.T) { fn := newTestSanitizer() type testCase struct { input string output string } testCases := []testCase{ {"abcdef0AxS-s_Z", "abcdef0AxS-s_Z"}, {"a:b", "a_b"}, {"a! b", "a__b"}, {"?bZ", "_bZ"}, } for _, tc := range testCases { require.Equal(t, tc.output, fn(tc.input)) } } func BenchmarkSanitizeFn(b *testing.B) { sanitize := newTestSanitizer() b.ResetTimer() for i := 0; i < b.N; i++ { _ = sanitize("foo bar") } } golang-github-uber-go-tally-4.1.11/scope.go000066400000000000000000000440431456146544700204730ustar00rootroot00000000000000// Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "io" "sync" "time" "go.uber.org/atomic" ) const ( _defaultInitialSliceSize = 16 _defaultReportingInterval = 2 * time.Second ) var ( // NoopScope is a scope that does nothing NoopScope, _ = NewRootScope(ScopeOptions{Reporter: NullStatsReporter}, 0) // DefaultSeparator is the default separator used to join nested scopes DefaultSeparator = "." globalNow = time.Now defaultScopeBuckets = DurationBuckets{ 0 * time.Millisecond, 10 * time.Millisecond, 25 * time.Millisecond, 50 * time.Millisecond, 75 * time.Millisecond, 100 * time.Millisecond, 200 * time.Millisecond, 300 * time.Millisecond, 400 * time.Millisecond, 500 * time.Millisecond, 600 * time.Millisecond, 800 * time.Millisecond, 1 * time.Second, 2 * time.Second, 5 * time.Second, } ) type scope struct { separator string prefix string tags map[string]string reporter StatsReporter cachedReporter CachedStatsReporter baseReporter BaseStatsReporter defaultBuckets Buckets sanitizer Sanitizer registry *scopeRegistry cm sync.RWMutex gm sync.RWMutex tm sync.RWMutex hm sync.RWMutex counters map[string]*counter countersSlice []*counter gauges map[string]*gauge gaugesSlice []*gauge histograms map[string]*histogram histogramsSlice []*histogram timers map[string]*timer // nb: deliberately skipping timersSlice as we report timers immediately, // no buffering is involved. bucketCache *bucketCache closed atomic.Bool done chan struct{} wg sync.WaitGroup root bool testScope bool } // ScopeOptions is a set of options to construct a scope. type ScopeOptions struct { Tags map[string]string Prefix string Reporter StatsReporter CachedReporter CachedStatsReporter Separator string DefaultBuckets Buckets SanitizeOptions *SanitizeOptions OmitCardinalityMetrics bool CardinalityMetricsTags map[string]string testScope bool registryShardCount uint } // NewRootScope creates a new root Scope with a set of options and // a reporting interval. // Must provide either a StatsReporter or a CachedStatsReporter. func NewRootScope(opts ScopeOptions, interval time.Duration) (Scope, io.Closer) { s := newRootScope(opts, interval) return s, s } // NewRootScopeWithDefaultInterval invokes NewRootScope with the default // reporting interval of 2s. func NewRootScopeWithDefaultInterval(opts ScopeOptions) (Scope, io.Closer) { return NewRootScope(opts, _defaultReportingInterval) } // NewTestScope creates a new Scope without a stats reporter with the // given prefix and adds the ability to take snapshots of metrics emitted // to it. func NewTestScope( prefix string, tags map[string]string, ) TestScope { return newRootScope(ScopeOptions{ Prefix: prefix, Tags: tags, testScope: true, }, 0) } func newRootScope(opts ScopeOptions, interval time.Duration) *scope { sanitizer := NewNoOpSanitizer() if o := opts.SanitizeOptions; o != nil { sanitizer = NewSanitizer(*o) } if opts.Tags == nil { opts.Tags = make(map[string]string) } if opts.Separator == "" { opts.Separator = DefaultSeparator } var baseReporter BaseStatsReporter if opts.Reporter != nil { baseReporter = opts.Reporter } else if opts.CachedReporter != nil { baseReporter = opts.CachedReporter } if opts.DefaultBuckets == nil || opts.DefaultBuckets.Len() < 1 { opts.DefaultBuckets = defaultScopeBuckets } s := &scope{ baseReporter: baseReporter, bucketCache: newBucketCache(), cachedReporter: opts.CachedReporter, counters: make(map[string]*counter), countersSlice: make([]*counter, 0, _defaultInitialSliceSize), defaultBuckets: opts.DefaultBuckets, done: make(chan struct{}), gauges: make(map[string]*gauge), gaugesSlice: make([]*gauge, 0, _defaultInitialSliceSize), histograms: make(map[string]*histogram), histogramsSlice: make([]*histogram, 0, _defaultInitialSliceSize), prefix: sanitizer.Name(opts.Prefix), reporter: opts.Reporter, sanitizer: sanitizer, separator: sanitizer.Name(opts.Separator), timers: make(map[string]*timer), root: true, testScope: opts.testScope, } // NB(r): Take a copy of the tags on creation // so that it cannot be modified after set. s.tags = s.copyAndSanitizeMap(opts.Tags) // Register the root scope s.registry = newScopeRegistryWithShardCount(s, opts.registryShardCount, opts.OmitCardinalityMetrics, opts.CardinalityMetricsTags) if interval > 0 { s.wg.Add(1) go func() { defer s.wg.Done() s.reportLoop(interval) }() } return s } // report dumps all aggregated stats into the reporter. Should be called automatically by the root scope periodically. func (s *scope) report(r StatsReporter) { s.cm.RLock() for name, counter := range s.counters { counter.report(s.fullyQualifiedName(name), s.tags, r) } s.cm.RUnlock() s.gm.RLock() for name, gauge := range s.gauges { gauge.report(s.fullyQualifiedName(name), s.tags, r) } s.gm.RUnlock() // we do nothing for timers here because timers report directly to ths StatsReporter without buffering s.hm.RLock() for name, histogram := range s.histograms { histogram.report(s.fullyQualifiedName(name), s.tags, r) } s.hm.RUnlock() } func (s *scope) cachedReport() { s.cm.RLock() for _, counter := range s.countersSlice { counter.cachedReport() } s.cm.RUnlock() s.gm.RLock() for _, gauge := range s.gaugesSlice { gauge.cachedReport() } s.gm.RUnlock() // we do nothing for timers here because timers report directly to ths StatsReporter without buffering s.hm.RLock() for _, histogram := range s.histogramsSlice { histogram.cachedReport() } s.hm.RUnlock() } // reportLoop is used by the root scope for periodic reporting func (s *scope) reportLoop(interval time.Duration) { ticker := time.NewTicker(interval) defer ticker.Stop() for { select { case <-ticker.C: s.reportLoopRun() case <-s.done: return } } } func (s *scope) reportLoopRun() { if s.closed.Load() { return } s.reportRegistry() } func (s *scope) reportRegistry() { if s.reporter != nil { s.registry.Report(s.reporter) s.reporter.Flush() } else if s.cachedReporter != nil { s.registry.CachedReport() s.cachedReporter.Flush() } } func (s *scope) Counter(name string) Counter { name = s.sanitizer.Name(name) if c, ok := s.counter(name); ok { return c } s.cm.Lock() defer s.cm.Unlock() if c, ok := s.counters[name]; ok { return c } var cachedCounter CachedCount if s.cachedReporter != nil { cachedCounter = s.cachedReporter.AllocateCounter( s.fullyQualifiedName(name), s.tags, ) } c := newCounter(cachedCounter) s.counters[name] = c s.countersSlice = append(s.countersSlice, c) return c } func (s *scope) counter(sanitizedName string) (Counter, bool) { s.cm.RLock() defer s.cm.RUnlock() c, ok := s.counters[sanitizedName] return c, ok } func (s *scope) Gauge(name string) Gauge { name = s.sanitizer.Name(name) if g, ok := s.gauge(name); ok { return g } s.gm.Lock() defer s.gm.Unlock() if g, ok := s.gauges[name]; ok { return g } var cachedGauge CachedGauge if s.cachedReporter != nil { cachedGauge = s.cachedReporter.AllocateGauge( s.fullyQualifiedName(name), s.tags, ) } g := newGauge(cachedGauge) s.gauges[name] = g s.gaugesSlice = append(s.gaugesSlice, g) return g } func (s *scope) gauge(name string) (Gauge, bool) { s.gm.RLock() defer s.gm.RUnlock() g, ok := s.gauges[name] return g, ok } func (s *scope) Timer(name string) Timer { name = s.sanitizer.Name(name) if t, ok := s.timer(name); ok { return t } s.tm.Lock() defer s.tm.Unlock() if t, ok := s.timers[name]; ok { return t } var cachedTimer CachedTimer if s.cachedReporter != nil { cachedTimer = s.cachedReporter.AllocateTimer( s.fullyQualifiedName(name), s.tags, ) } t := newTimer( s.fullyQualifiedName(name), s.tags, s.reporter, cachedTimer, ) s.timers[name] = t return t } func (s *scope) timer(sanitizedName string) (Timer, bool) { s.tm.RLock() defer s.tm.RUnlock() t, ok := s.timers[sanitizedName] return t, ok } func (s *scope) Histogram(name string, b Buckets) Histogram { name = s.sanitizer.Name(name) if h, ok := s.histogram(name); ok { return h } if b == nil { b = s.defaultBuckets } htype := valueHistogramType if _, ok := b.(DurationBuckets); ok { htype = durationHistogramType } s.hm.Lock() defer s.hm.Unlock() if h, ok := s.histograms[name]; ok { return h } var cachedHistogram CachedHistogram if s.cachedReporter != nil { cachedHistogram = s.cachedReporter.AllocateHistogram( s.fullyQualifiedName(name), s.tags, b, ) } h := newHistogram( htype, s.fullyQualifiedName(name), s.tags, s.reporter, s.bucketCache.Get(htype, b), cachedHistogram, ) s.histograms[name] = h s.histogramsSlice = append(s.histogramsSlice, h) return h } func (s *scope) histogram(sanitizedName string) (Histogram, bool) { s.hm.RLock() defer s.hm.RUnlock() h, ok := s.histograms[sanitizedName] return h, ok } func (s *scope) Tagged(tags map[string]string) Scope { return s.subscope(s.prefix, tags) } func (s *scope) SubScope(prefix string) Scope { prefix = s.sanitizer.Name(prefix) return s.subscope(s.fullyQualifiedName(prefix), nil) } func (s *scope) subscope(prefix string, tags map[string]string) Scope { return s.registry.Subscope(s, prefix, tags) } func (s *scope) Capabilities() Capabilities { if s.baseReporter == nil { return capabilitiesNone } return s.baseReporter.Capabilities() } func (s *scope) Snapshot() Snapshot { snap := newSnapshot() s.registry.ForEachScope(func(ss *scope) { // NB(r): tags are immutable, no lock required to read. tags := make(map[string]string, len(s.tags)) for k, v := range ss.tags { tags[k] = v } ss.cm.RLock() for key, c := range ss.counters { name := ss.fullyQualifiedName(key) id := KeyForPrefixedStringMap(name, tags) snap.counters[id] = &counterSnapshot{ name: name, tags: tags, value: c.snapshot(), } } ss.cm.RUnlock() ss.gm.RLock() for key, g := range ss.gauges { name := ss.fullyQualifiedName(key) id := KeyForPrefixedStringMap(name, tags) snap.gauges[id] = &gaugeSnapshot{ name: name, tags: tags, value: g.snapshot(), } } ss.gm.RUnlock() ss.tm.RLock() for key, t := range ss.timers { name := ss.fullyQualifiedName(key) id := KeyForPrefixedStringMap(name, tags) snap.timers[id] = &timerSnapshot{ name: name, tags: tags, values: t.snapshot(), } } ss.tm.RUnlock() ss.hm.RLock() for key, h := range ss.histograms { name := ss.fullyQualifiedName(key) id := KeyForPrefixedStringMap(name, tags) snap.histograms[id] = &histogramSnapshot{ name: name, tags: tags, values: h.snapshotValues(), durations: h.snapshotDurations(), } } ss.hm.RUnlock() }) return snap } func (s *scope) Close() error { // n.b. Once this flag is set, the next scope report will remove it from // the registry and clear its metrics. if !s.closed.CAS(false, true) { return nil } close(s.done) if s.root { s.reportRegistry() if closer, ok := s.baseReporter.(io.Closer); ok { return closer.Close() } } return nil } func (s *scope) clearMetrics() { s.cm.Lock() s.gm.Lock() s.tm.Lock() s.hm.Lock() defer s.cm.Unlock() defer s.gm.Unlock() defer s.tm.Unlock() defer s.hm.Unlock() for k := range s.counters { delete(s.counters, k) } s.countersSlice = nil for k := range s.gauges { delete(s.gauges, k) } s.gaugesSlice = nil for k := range s.timers { delete(s.timers, k) } for k := range s.histograms { delete(s.histograms, k) } s.histogramsSlice = nil } // NB(prateek): We assume concatenation of sanitized inputs is // sanitized. If that stops being true, then we need to sanitize the // output of this function. func (s *scope) fullyQualifiedName(name string) string { if len(s.prefix) == 0 { return name } // NB: we don't need to sanitize the output of this function as we // sanitize all the the inputs (prefix, separator, name); and the // output we're creating is a concatenation of the sanitized inputs. // If we change the concatenation to involve other inputs or characters, // we'll need to sanitize them too. return s.prefix + s.separator + name } func (s *scope) copyAndSanitizeMap(tags map[string]string) map[string]string { result := make(map[string]string, len(tags)) for k, v := range tags { k = s.sanitizer.Key(k) v = s.sanitizer.Value(v) result[k] = v } return result } // TestScope is a metrics collector that has no reporting, ensuring that // all emitted values have a given prefix or set of tags type TestScope interface { Scope // Snapshot returns a copy of all values since the last report execution, // this is an expensive operation and should only be use for testing purposes Snapshot() Snapshot } // Snapshot is a snapshot of values since last report execution type Snapshot interface { // Counters returns a snapshot of all counter summations since last report execution Counters() map[string]CounterSnapshot // Gauges returns a snapshot of gauge last values since last report execution Gauges() map[string]GaugeSnapshot // Timers returns a snapshot of timer values since last report execution Timers() map[string]TimerSnapshot // Histograms returns a snapshot of histogram samples since last report execution Histograms() map[string]HistogramSnapshot } // CounterSnapshot is a snapshot of a counter type CounterSnapshot interface { // Name returns the name Name() string // Tags returns the tags Tags() map[string]string // Value returns the value Value() int64 } // GaugeSnapshot is a snapshot of a gauge type GaugeSnapshot interface { // Name returns the name Name() string // Tags returns the tags Tags() map[string]string // Value returns the value Value() float64 } // TimerSnapshot is a snapshot of a timer type TimerSnapshot interface { // Name returns the name Name() string // Tags returns the tags Tags() map[string]string // Values returns the values Values() []time.Duration } // HistogramSnapshot is a snapshot of a histogram type HistogramSnapshot interface { // Name returns the name Name() string // Tags returns the tags Tags() map[string]string // Values returns the sample values by upper bound for a valueHistogram Values() map[float64]int64 // Durations returns the sample values by upper bound for a durationHistogram Durations() map[time.Duration]int64 } // mergeRightTags merges 2 sets of tags with the tags from tagsRight overriding values from tagsLeft func mergeRightTags(tagsLeft, tagsRight map[string]string) map[string]string { if tagsLeft == nil && tagsRight == nil { return nil } if len(tagsRight) == 0 { return tagsLeft } if len(tagsLeft) == 0 { return tagsRight } result := make(map[string]string, len(tagsLeft)+len(tagsRight)) for k, v := range tagsLeft { result[k] = v } for k, v := range tagsRight { result[k] = v } return result } type snapshot struct { counters map[string]CounterSnapshot gauges map[string]GaugeSnapshot timers map[string]TimerSnapshot histograms map[string]HistogramSnapshot } func newSnapshot() *snapshot { return &snapshot{ counters: make(map[string]CounterSnapshot), gauges: make(map[string]GaugeSnapshot), timers: make(map[string]TimerSnapshot), histograms: make(map[string]HistogramSnapshot), } } func (s *snapshot) Counters() map[string]CounterSnapshot { return s.counters } func (s *snapshot) Gauges() map[string]GaugeSnapshot { return s.gauges } func (s *snapshot) Timers() map[string]TimerSnapshot { return s.timers } func (s *snapshot) Histograms() map[string]HistogramSnapshot { return s.histograms } type counterSnapshot struct { name string tags map[string]string value int64 } func (s *counterSnapshot) Name() string { return s.name } func (s *counterSnapshot) Tags() map[string]string { return s.tags } func (s *counterSnapshot) Value() int64 { return s.value } type gaugeSnapshot struct { name string tags map[string]string value float64 } func (s *gaugeSnapshot) Name() string { return s.name } func (s *gaugeSnapshot) Tags() map[string]string { return s.tags } func (s *gaugeSnapshot) Value() float64 { return s.value } type timerSnapshot struct { name string tags map[string]string values []time.Duration } func (s *timerSnapshot) Name() string { return s.name } func (s *timerSnapshot) Tags() map[string]string { return s.tags } func (s *timerSnapshot) Values() []time.Duration { return s.values } type histogramSnapshot struct { name string tags map[string]string values map[float64]int64 durations map[time.Duration]int64 } func (s *histogramSnapshot) Name() string { return s.name } func (s *histogramSnapshot) Tags() map[string]string { return s.tags } func (s *histogramSnapshot) Values() map[float64]int64 { return s.values } func (s *histogramSnapshot) Durations() map[time.Duration]int64 { return s.durations } golang-github-uber-go-tally-4.1.11/scope_benchmark_test.go000066400000000000000000000203421456146544700235400ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "fmt" "strconv" "sync/atomic" "testing" "time" ) func BenchmarkNameGeneration(b *testing.B) { root, _ := NewRootScope(ScopeOptions{ Prefix: "funkytown", Reporter: NullStatsReporter, }, 0) s := root.(*scope) for n := 0; n < b.N; n++ { s.fullyQualifiedName("take.me.to") } } func BenchmarkCounterAllocation(b *testing.B) { root, _ := NewRootScope(ScopeOptions{ Prefix: "funkytown", Reporter: NullStatsReporter, }, 0) s := root.(*scope) ids := make([]string, 0, b.N) for i := 0; i < b.N; i++ { ids = append(ids, fmt.Sprintf("take.me.to.%d", i)) } b.ResetTimer() for n := 0; n < b.N; n++ { s.Counter(ids[n]) } } func BenchmarkSanitizedCounterAllocation(b *testing.B) { root, _ := NewRootScope(ScopeOptions{ Prefix: "funkytown", Reporter: NullStatsReporter, SanitizeOptions: &alphanumericSanitizerOpts, }, 0) s := root.(*scope) ids := make([]string, 0, b.N) for i := 0; i < b.N; i++ { ids = append(ids, fmt.Sprintf("take.me.to.%d", i)) } b.ResetTimer() for n := 0; n < b.N; n++ { s.Counter(ids[n]) } } func BenchmarkNameGenerationTagged(b *testing.B) { root, _ := NewRootScope(ScopeOptions{ Prefix: "funkytown", Tags: map[string]string{ "style": "funky", "hair": "wavy", "jefferson": "starship", }, Reporter: NullStatsReporter, }, 0) s := root.(*scope) for n := 0; n < b.N; n++ { s.fullyQualifiedName("take.me.to") } } func BenchmarkScopeTaggedCachedSubscopes(b *testing.B) { root, _ := NewRootScope(ScopeOptions{ Prefix: "funkytown", Reporter: NullStatsReporter, Tags: map[string]string{ "style": "funky", "hair": "wavy", "jefferson": "starship", }, }, 0) b.ResetTimer() for n := 0; n < b.N; n++ { root.Tagged(map[string]string{ "foo": "bar", "baz": "qux", "qux": "quux", }) } } func BenchmarkScopeTaggedNoCachedSubscopes(b *testing.B) { root, _ := NewRootScope(ScopeOptions{ Prefix: "funkytown", Reporter: NullStatsReporter, Tags: map[string]string{ "style": "funky", "hair": "wavy", "jefferson": "starship", }, }, 0) values := make([]string, b.N) for i := 0; i < b.N; i++ { values[i] = strconv.Itoa(i) } b.ResetTimer() for n := 0; n < b.N; n++ { root.Tagged(map[string]string{ "foo": values[n], "baz": values[n], "qux": values[n], }) } } func BenchmarkScopeTaggedNoCachedSubscopesParallel(b *testing.B) { root, _ := NewRootScope(ScopeOptions{ Prefix: "funkytown", Reporter: NullStatsReporter, Tags: map[string]string{ "style": "funky", "hair": "wavy", "jefferson": "starship", }, }, 0) b.ResetTimer() index := int64(0) b.RunParallel(func(pb *testing.PB) { for pb.Next() { n := atomic.AddInt64(&index, 1) value := strconv.Itoa(int(n)) // Validated that the compiler is not optimizing this with a cpu profiler. // Check https://github.com/uber-go/tally/pull/184 for more details root.Tagged(map[string]string{ "foo": value, "baz": value, "qux": value, }) } }) } func BenchmarkScopeTaggedNoCachedSubscopesParallelPercentageCached(b *testing.B) { percentageCached := int64(5) root, _ := NewRootScope(ScopeOptions{ Prefix: "funkytown", Reporter: NullStatsReporter, Tags: map[string]string{ "style": "funky", "hair": "wavy", "jefferson": "starship", }, }, 0) cachedMap := map[string]string{ "foo": "any", "baz": "any", "qux": "any", } root.Tagged(cachedMap) b.ResetTimer() index := int64(-1) b.RunParallel(func(pb *testing.PB) { for pb.Next() { n := atomic.AddInt64(&index, 1) if (n % 100) < percentageCached { root.Tagged(cachedMap) continue } value := strconv.Itoa(int(n)) // Validated that the compiler is not optimizing this with a cpu profiler. // Check https://github.com/uber-go/tally/pull/184 for more details root.Tagged(map[string]string{ "foo": value, "baz": value, "qux": value, }) } }) } func BenchmarkNameGenerationNoPrefix(b *testing.B) { root, _ := NewRootScope(ScopeOptions{ Reporter: NullStatsReporter, }, 0) s := root.(*scope) for n := 0; n < b.N; n++ { s.fullyQualifiedName("im.all.alone") } } func BenchmarkHistogramAllocation(b *testing.B) { root, _ := NewRootScope(ScopeOptions{ Reporter: NullStatsReporter, }, 0) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { root.Histogram("foo"+strconv.Itoa(i), DefaultBuckets) } } func BenchmarkHistogramExisting(b *testing.B) { root, _ := NewRootScope(ScopeOptions{ Reporter: NullStatsReporter, }, 0) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { root.Histogram("foo", DefaultBuckets) } } func benchmarkScopeReportingN(b *testing.B, numElems int) { root, _ := NewRootScope(ScopeOptions{ Prefix: "funkytown", CachedReporter: noopCachedReporter{}, SanitizeOptions: &alphanumericSanitizerOpts, }, 0) s := root.(*scope) ids := make([]string, 0, numElems) for i := 0; i < numElems; i++ { id := fmt.Sprintf("take.me.to.%d", i) ids = append(ids, id) s.Counter(id) } _ = ids b.ResetTimer() for n := 0; n < b.N; n++ { s.cachedReport() } } func BenchmarkScopeReporting(b *testing.B) { for i := 1; i <= 1000000; i *= 10 { size := fmt.Sprintf("size%d", i) b.Run(size, func(b *testing.B) { benchmarkScopeReportingN(b, i) }) } } type noopStat struct{} func (s noopStat) ReportCount(value int64) {} func (s noopStat) ReportGauge(value float64) {} func (s noopStat) ReportTimer(interval time.Duration) {} func (s noopStat) ValueBucket(bucketLowerBound, bucketUpperBound float64) CachedHistogramBucket { return s } func (s noopStat) DurationBucket(bucketLowerBound, bucketUpperBound time.Duration) CachedHistogramBucket { return s } func (s noopStat) ReportSamples(value int64) {} type noopCachedReporter struct{} func (n noopCachedReporter) Capabilities() Capabilities { return n } func (n noopCachedReporter) Reporting() bool { return true } func (n noopCachedReporter) Tagging() bool { return true } func (n noopCachedReporter) Flush() {} func (n noopCachedReporter) ReportCounter(name string, tags map[string]string, value int64) {} func (n noopCachedReporter) ReportGauge(name string, tags map[string]string, value float64) {} func (n noopCachedReporter) ReportTimer(name string, tags map[string]string, interval time.Duration) { } func (n noopCachedReporter) ReportHistogramValueSamples(name string, tags map[string]string, buckets Buckets, bucketLowerBound float64, bucketUpperBound float64, samples int64) { } func (n noopCachedReporter) ReportHistogramDurationSamples(name string, tags map[string]string, buckets Buckets, bucketLowerBound time.Duration, bucketUpperBound time.Duration, samples int64) { } func (n noopCachedReporter) AllocateCounter(name string, tags map[string]string) CachedCount { return noopStat{} } func (n noopCachedReporter) AllocateGauge(name string, tags map[string]string) CachedGauge { return noopStat{} } func (n noopCachedReporter) AllocateTimer(name string, tags map[string]string) CachedTimer { return noopStat{} } func (n noopCachedReporter) AllocateHistogram(name string, tags map[string]string, buckets Buckets) CachedHistogram { return noopStat{} } golang-github-uber-go-tally-4.1.11/scope_registry.go000066400000000000000000000260751456146544700224300ustar00rootroot00000000000000// Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "hash/maphash" "runtime" "sync" "unsafe" "go.uber.org/atomic" ) var ( scopeRegistryKey = keyForPrefixedStringMaps // Metrics related. counterCardinalityName = "tally.internal.counter_cardinality" gaugeCardinalityName = "tally.internal.gauge_cardinality" histogramCardinalityName = "tally.internal.histogram_cardinality" scopeCardinalityName = "tally.internal.num_active_scopes" ) const ( // DefaultTagRedactValue is the default tag value to use when redacting DefaultTagRedactValue = "global" ) type scopeRegistry struct { seed maphash.Seed root *scope // We need a subscope per GOPROC so that we can take advantage of all the cpu available to the application. subscopes []*scopeBucket // Internal metrics related. omitCardinalityMetrics bool cardinalityMetricsTags map[string]string sanitizedCounterCardinalityName string sanitizedGaugeCardinalityName string sanitizedHistogramCardinalityName string sanitizedScopeCardinalityName string cachedCounterCardinalityGauge CachedGauge cachedGaugeCardinalityGauge CachedGauge cachedHistogramCardinalityGauge CachedGauge cachedScopeCardinalityGauge CachedGauge } type scopeBucket struct { mu sync.RWMutex s map[string]*scope } func newScopeRegistryWithShardCount( root *scope, shardCount uint, omitCardinalityMetrics bool, cardinalityMetricsTags map[string]string, ) *scopeRegistry { if shardCount == 0 { shardCount = uint(runtime.GOMAXPROCS(-1)) } r := &scopeRegistry{ root: root, subscopes: make([]*scopeBucket, shardCount), seed: maphash.MakeSeed(), omitCardinalityMetrics: omitCardinalityMetrics, sanitizedCounterCardinalityName: root.sanitizer.Name(counterCardinalityName), sanitizedGaugeCardinalityName: root.sanitizer.Name(gaugeCardinalityName), sanitizedHistogramCardinalityName: root.sanitizer.Name(histogramCardinalityName), sanitizedScopeCardinalityName: root.sanitizer.Name(scopeCardinalityName), cardinalityMetricsTags: map[string]string{ "version": Version, "host": DefaultTagRedactValue, "instance": DefaultTagRedactValue, }, } for k, v := range cardinalityMetricsTags { r.cardinalityMetricsTags[root.sanitizer.Key(k)] = root.sanitizer.Value(v) } for i := uint(0); i < shardCount; i++ { r.subscopes[i] = &scopeBucket{ s: make(map[string]*scope), } r.subscopes[i].s[scopeRegistryKey(root.prefix, root.tags)] = root } if r.root.cachedReporter != nil { r.cachedCounterCardinalityGauge = r.root.cachedReporter.AllocateGauge(r.sanitizedCounterCardinalityName, r.cardinalityMetricsTags) r.cachedGaugeCardinalityGauge = r.root.cachedReporter.AllocateGauge(r.sanitizedGaugeCardinalityName, r.cardinalityMetricsTags) r.cachedHistogramCardinalityGauge = r.root.cachedReporter.AllocateGauge(r.sanitizedHistogramCardinalityName, r.cardinalityMetricsTags) r.cachedScopeCardinalityGauge = r.root.cachedReporter.AllocateGauge(r.sanitizedScopeCardinalityName, r.cardinalityMetricsTags) } return r } func (r *scopeRegistry) Report(reporter StatsReporter) { defer r.purgeIfRootClosed() r.reportInternalMetrics() for _, subscopeBucket := range r.subscopes { subscopeBucket.mu.RLock() for name, s := range subscopeBucket.s { s.report(reporter) if s.closed.Load() { r.removeWithRLock(subscopeBucket, name) s.clearMetrics() } } subscopeBucket.mu.RUnlock() } } func (r *scopeRegistry) CachedReport() { defer r.purgeIfRootClosed() r.reportInternalMetrics() for _, subscopeBucket := range r.subscopes { subscopeBucket.mu.RLock() for name, s := range subscopeBucket.s { s.cachedReport() if s.closed.Load() { r.removeWithRLock(subscopeBucket, name) s.clearMetrics() } } subscopeBucket.mu.RUnlock() } } func (r *scopeRegistry) ForEachScope(f func(*scope)) { for _, subscopeBucket := range r.subscopes { subscopeBucket.mu.RLock() for _, s := range subscopeBucket.s { f(s) } subscopeBucket.mu.RUnlock() } } func (r *scopeRegistry) Subscope(parent *scope, prefix string, tags map[string]string) *scope { if r.root.closed.Load() || parent.closed.Load() { return NoopScope.(*scope) } var ( buf = keyForPrefixedStringMapsAsKey(make([]byte, 0, 256), prefix, parent.tags, tags) h maphash.Hash ) h.SetSeed(r.seed) _, _ = h.Write(buf) subscopeBucket := r.subscopes[h.Sum64()%uint64(len(r.subscopes))] subscopeBucket.mu.RLock() // buf is stack allocated and casting it to a string for lookup from the cache // as the memory layout of []byte is a superset of string the below casting is safe and does not do any alloc // However it cannot be used outside of the stack; a heap allocation is needed if that string needs to be stored // in the map as a key var ( unsanitizedKey = *(*string)(unsafe.Pointer(&buf)) sanitizedKey string ) s, ok := r.lockedLookup(subscopeBucket, unsanitizedKey) if ok { // If this subscope isn't closed or is a test scope, return it. // Otherwise, report it immediately and delete it so that a new // (functional) scope can be returned instead. if !s.closed.Load() || s.testScope { subscopeBucket.mu.RUnlock() return s } switch { case parent.reporter != nil: s.report(parent.reporter) case parent.cachedReporter != nil: s.cachedReport() } } tags = parent.copyAndSanitizeMap(tags) sanitizedKey = scopeRegistryKey(prefix, parent.tags, tags) // If a scope was found above but we didn't return, we need to remove the // scope from both keys. if ok { r.removeWithRLock(subscopeBucket, unsanitizedKey) r.removeWithRLock(subscopeBucket, sanitizedKey) s.clearMetrics() } subscopeBucket.mu.RUnlock() // Force-allocate the unsafe string as a safe string. Note that neither // string(x) nor x+"" will have the desired effect (the former is a nop, // and the latter will likely be elided), so append a new character and // truncate instead. // // ref: https://go.dev/play/p/sxhExUKSxCw unsanitizedKey = (unsanitizedKey + ".")[:len(unsanitizedKey)] subscopeBucket.mu.Lock() defer subscopeBucket.mu.Unlock() if s, ok := r.lockedLookup(subscopeBucket, sanitizedKey); ok { if _, ok = r.lockedLookup(subscopeBucket, unsanitizedKey); !ok { subscopeBucket.s[unsanitizedKey] = s } return s } allTags := mergeRightTags(parent.tags, tags) subscope := &scope{ separator: parent.separator, prefix: prefix, // NB(prateek): don't need to copy the tags here, // we assume the map provided is immutable. tags: allTags, reporter: parent.reporter, cachedReporter: parent.cachedReporter, baseReporter: parent.baseReporter, defaultBuckets: parent.defaultBuckets, sanitizer: parent.sanitizer, registry: parent.registry, counters: make(map[string]*counter), countersSlice: make([]*counter, 0, _defaultInitialSliceSize), gauges: make(map[string]*gauge), gaugesSlice: make([]*gauge, 0, _defaultInitialSliceSize), histograms: make(map[string]*histogram), histogramsSlice: make([]*histogram, 0, _defaultInitialSliceSize), timers: make(map[string]*timer), bucketCache: parent.bucketCache, done: make(chan struct{}), testScope: parent.testScope, } subscopeBucket.s[sanitizedKey] = subscope if _, ok := r.lockedLookup(subscopeBucket, unsanitizedKey); !ok { subscopeBucket.s[unsanitizedKey] = subscope } return subscope } func (r *scopeRegistry) lockedLookup(subscopeBucket *scopeBucket, key string) (*scope, bool) { ss, ok := subscopeBucket.s[key] return ss, ok } func (r *scopeRegistry) purgeIfRootClosed() { if !r.root.closed.Load() { return } for _, subscopeBucket := range r.subscopes { subscopeBucket.mu.Lock() for k, s := range subscopeBucket.s { _ = s.Close() s.clearMetrics() delete(subscopeBucket.s, k) } subscopeBucket.mu.Unlock() } } func (r *scopeRegistry) removeWithRLock(subscopeBucket *scopeBucket, key string) { // n.b. This function must lock the registry for writing and return it to an // RLocked state prior to exiting. Defer order is important (LIFO). subscopeBucket.mu.RUnlock() defer subscopeBucket.mu.RLock() subscopeBucket.mu.Lock() defer subscopeBucket.mu.Unlock() delete(subscopeBucket.s, key) } // Records internal Metrics' cardinalities. func (r *scopeRegistry) reportInternalMetrics() { if r.omitCardinalityMetrics { return } counters, gauges, histograms, scopes := atomic.Int64{}, atomic.Int64{}, atomic.Int64{}, atomic.Int64{} rootCounters, rootGauges, rootHistograms := atomic.Int64{}, atomic.Int64{}, atomic.Int64{} scopes.Inc() // Account for root scope. r.ForEachScope( func(ss *scope) { counterSliceLen, gaugeSliceLen, histogramSliceLen := int64(len(ss.countersSlice)), int64(len(ss.gaugesSlice)), int64(len(ss.histogramsSlice)) if ss.root { // Root scope is referenced across all buckets. rootCounters.Store(counterSliceLen) rootGauges.Store(gaugeSliceLen) rootHistograms.Store(histogramSliceLen) return } counters.Add(counterSliceLen) gauges.Add(gaugeSliceLen) histograms.Add(histogramSliceLen) scopes.Inc() }, ) counters.Add(rootCounters.Load()) gauges.Add(rootGauges.Load()) histograms.Add(rootHistograms.Load()) if r.root.reporter != nil { r.root.reporter.ReportGauge(r.sanitizedCounterCardinalityName, r.cardinalityMetricsTags, float64(counters.Load())) r.root.reporter.ReportGauge(r.sanitizedGaugeCardinalityName, r.cardinalityMetricsTags, float64(gauges.Load())) r.root.reporter.ReportGauge(r.sanitizedHistogramCardinalityName, r.cardinalityMetricsTags, float64(histograms.Load())) r.root.reporter.ReportGauge(r.sanitizedScopeCardinalityName, r.cardinalityMetricsTags, float64(scopes.Load())) } if r.root.cachedReporter != nil { r.cachedCounterCardinalityGauge.ReportGauge(float64(counters.Load())) r.cachedGaugeCardinalityGauge.ReportGauge(float64(gauges.Load())) r.cachedHistogramCardinalityGauge.ReportGauge(float64(histograms.Load())) r.cachedScopeCardinalityGauge.ReportGauge(float64(scopes.Load())) } } golang-github-uber-go-tally-4.1.11/scope_registry_external_test.go000066400000000000000000000073511456146544700253650ustar00rootroot00000000000000// Copyright (c) 2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally_test import ( "io" "sync" "testing" "time" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" "github.com/uber-go/tally/v4" "github.com/uber-go/tally/v4/tallymock" "go.uber.org/atomic" ) func TestTestScopesNotPruned(t *testing.T) { var ( root = tally.NewTestScope("", nil) subscope = root.SubScope("foo") counter = subscope.Counter("bar") ) counter.Inc(123) closer, ok := subscope.(io.Closer) require.True(t, ok) require.NoError(t, closer.Close()) subscope = root.SubScope("foo") counter = subscope.Counter("bar") counter.Inc(123) var ( snapshot = root.Snapshot() counters = snapshot.Counters() ) require.Len(t, counters, 1) require.Len(t, snapshot.Gauges(), 0) require.Len(t, snapshot.Timers(), 0) require.Len(t, snapshot.Histograms(), 0) val, ok := counters["foo.bar+"] require.True(t, ok) require.Equal(t, "foo.bar", val.Name()) require.EqualValues(t, 246, val.Value()) } func TestNoDefunctSubscopes(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() var ( tags = map[string]string{ "hello": "world", } mockreporter = tallymock.NewMockStatsReporter(ctrl) ready = make(chan struct{}) closed atomic.Bool wg sync.WaitGroup ) wg.Add(2) mockreporter.EXPECT(). ReportCounter("a", gomock.Any(), int64(123)). Do(func(_ string, _ map[string]string, _ int64) { wg.Done() }). Times(1) mockreporter.EXPECT(). ReportCounter("b", gomock.Any(), int64(456)). Do(func(_ string, _ map[string]string, _ int64) { wg.Done() }). Times(1) // Use flushing as a signal to determine if/when a closed scope // would be removed from the registry's cache. mockreporter.EXPECT(). Flush(). Do(func() { // Don't unblock the ready channel until we've explicitly // closed the scope. if !closed.Load() { return } select { case <-ready: default: close(ready) } }). MinTimes(1) root, _ := tally.NewRootScope(tally.ScopeOptions{ Reporter: mockreporter, OmitCardinalityMetrics: true, }, time.Millisecond) subscope := root.Tagged(tags) requireClose(t, subscope) subscope = root.Tagged(tags) // Signal and wait for the next flush to ensure that subscope can // be a closed scope. closed.Store(true) <-ready // Use the maybe-closed subscope for counter A. subscope.Counter("a").Inc(123) // Guarantee that counter B will not use a closed subscope. subscope = root.Tagged(tags) subscope.Counter("b").Inc(456) requireClose(t, root) wg.Wait() } func requireClose(t *testing.T, scope tally.Scope) { x, ok := scope.(io.Closer) require.True(t, ok) require.NoError(t, x.Close()) } golang-github-uber-go-tally-4.1.11/scope_registry_test.go000066400000000000000000000154631456146544700234660ustar00rootroot00000000000000// Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "fmt" "testing" "github.com/stretchr/testify/assert" ) var ( numInternalMetrics = 4 ) func TestVerifyCachedTaggedScopesAlloc(t *testing.T) { root, _ := NewRootScope(ScopeOptions{ Prefix: "funkytown", Reporter: NullStatsReporter, Tags: map[string]string{ "style": "funky", "hair": "wavy", "jefferson": "starship", }, }, 0) allocs := testing.AllocsPerRun(1000, func() { root.Tagged(map[string]string{ "foo": "bar", "baz": "qux", "qux": "quux", }) }) expected := 3.0 assert.True(t, allocs <= expected, "the cached tagged scopes should allocate at most %.0f allocations, but did allocate %.0f", expected, allocs) } func TestVerifyOmitCardinalityMetricsTags(t *testing.T) { r := newTestStatsReporter() _, closer := NewRootScope(ScopeOptions{ Reporter: r, OmitCardinalityMetrics: false, CardinalityMetricsTags: map[string]string{ "cardinality_tag_key": "cardinality_tag_value", }, }, 0) wantOmitCardinalityMetricsTags := map[string]string{ "cardinality_tag_key": "cardinality_tag_value", "version": Version, "host": "global", "instance": "global", } r.gg.Add(numInternalMetrics) closer.Close() r.WaitAll() assert.NotNil(t, r.gauges[counterCardinalityName], "counter cardinality should not be nil") assert.Equal( t, wantOmitCardinalityMetricsTags, r.gauges[counterCardinalityName].tags, "expected tags %v, got tags %v", wantOmitCardinalityMetricsTags, r.gauges[counterCardinalityName].tags, ) } func TestNewTestStatsReporterOneScope(t *testing.T) { r := newTestStatsReporter() root, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: false}, 0) s := root.(*scope) numFakeCounters := 3 numFakeGauges := 5 numFakeHistograms := 11 numScopes := 1 r.cg.Add(numFakeCounters) for c := 1; c <= numFakeCounters; c++ { s.Counter(fmt.Sprintf("counter-%d", c)).Inc(int64(c)) } r.gg.Add(numFakeGauges + numInternalMetrics) for g := 1; g <= numFakeGauges; g++ { s.Gauge(fmt.Sprintf("gauge_%d", g)).Update(float64(g)) } r.hg.Add(numFakeHistograms) for h := 1; h <= numFakeHistograms; h++ { s.Histogram(fmt.Sprintf("histogram_%d", h), MustMakeLinearValueBuckets(0, 1, 10)).RecordValue(float64(h)) } closer.Close() r.WaitAll() assert.NotNil(t, r.gauges[counterCardinalityName], "counter cardinality should not be nil") assert.Equal( t, numFakeCounters, int(r.gauges[counterCardinalityName].val), "expected %d counters, got %d counters", numFakeCounters, r.gauges[counterCardinalityName].val, ) assert.NotNil(t, r.gauges[gaugeCardinalityName], "gauge cardinality should not be nil") assert.Equal( t, numFakeGauges, int(r.gauges[gaugeCardinalityName].val), "expected %d gauges, got %d gauges", numFakeGauges, r.gauges[gaugeCardinalityName].val, ) assert.NotNil(t, r.gauges[histogramCardinalityName], "histogram cardinality should not be nil") assert.Equal( t, numFakeHistograms, int(r.gauges[histogramCardinalityName].val), "expected %d histograms, got %d histograms", numFakeHistograms, r.gauges[histogramCardinalityName].val, ) assert.NotNil(t, r.gauges[scopeCardinalityName], "scope cardinality should not be nil") assert.Equal( t, numScopes, int(r.gauges[scopeCardinalityName].val), "expected %d scopes, got %d scopes", numScopes, r.gauges[scopeCardinalityName].val, ) } func TestNewTestStatsReporterManyScopes(t *testing.T) { r := newTestStatsReporter() root, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: false}, 0) wantCounters, wantGauges, wantHistograms, wantScopes := 3, 2, 1, 2 s := root.(*scope) r.cg.Add(2) s.Counter("counter-foo").Inc(1) s.Counter("counter-bar").Inc(2) r.gg.Add(1 + numInternalMetrics) s.Gauge("gauge-foo").Update(3) r.hg.Add(1) s.Histogram("histogram-foo", MustMakeLinearValueBuckets(0, 1, 10)).RecordValue(4) ss := root.SubScope("sub-scope").(*scope) r.cg.Add(1) ss.Counter("counter-baz").Inc(5) r.gg.Add(1) ss.Gauge("gauge-bar").Update(6) closer.Close() r.WaitAll() assert.NotNil(t, r.gauges[counterCardinalityName], "counter cardinality should not be nil") assert.Equal( t, wantCounters, int(r.gauges[counterCardinalityName].val), "expected %d counters, got %d counters", wantCounters, r.gauges[counterCardinalityName].val, ) assert.NotNil(t, r.gauges[gaugeCardinalityName], "gauge cardinality should not be nil") assert.Equal( t, wantGauges, int(r.gauges[gaugeCardinalityName].val), "expected %d gauges, got %d gauges", wantGauges, r.gauges[gaugeCardinalityName].val, ) assert.NotNil(t, r.gauges[histogramCardinalityName], "histogram cardinality should not be nil") assert.Equal( t, wantHistograms, int(r.gauges[histogramCardinalityName].val), "expected %d histograms, got %d histograms", wantHistograms, r.gauges[histogramCardinalityName].val, ) assert.NotNil(t, r.gauges[scopeCardinalityName], "scope cardinality should not be nil") assert.Equal( t, wantScopes, int(r.gauges[scopeCardinalityName].val), "expected %d scopes, got %d scopes", wantScopes, r.gauges[scopeCardinalityName].val, ) } func TestForEachScopeConcurrent(t *testing.T) { var ( root = newRootScope(ScopeOptions{Prefix: "", Tags: nil}, 0) quit = make(chan struct{}) done = make(chan struct{}) ) go func() { defer close(done) for { select { case <-quit: return default: hello := root.Tagged(map[string]string{"a": "b"}).Counter("hello") hello.Inc(1) } } }() var c Counter = nil for { // Keep poking at the subscopes until the counter is written. root.registry.ForEachScope( func(ss *scope) { ss.cm.RLock() if ss.counters["hello"] != nil { c = ss.counters["hello"] } ss.cm.RUnlock() }, ) if c != nil { quit <- struct{}{} break } } <-done } golang-github-uber-go-tally-4.1.11/scope_test.go000066400000000000000000001023631456146544700215320ustar00rootroot00000000000000// Copyright (c) 2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "fmt" "math" "math/rand" "strconv" "strings" "sync" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } var ( // alphanumericSanitizerOpts is the options to create a sanitizer which uses // the alphanumeric SanitizeFn. alphanumericSanitizerOpts = SanitizeOptions{ NameCharacters: ValidCharacters{ Ranges: AlphanumericRange, Characters: UnderscoreDashCharacters, }, KeyCharacters: ValidCharacters{ Ranges: AlphanumericRange, Characters: UnderscoreDashCharacters, }, ValueCharacters: ValidCharacters{ Ranges: AlphanumericRange, Characters: UnderscoreDashCharacters, }, ReplacementCharacter: DefaultReplacementCharacter, } ) type testIntValue struct { val int64 tags map[string]string reporter *testStatsReporter } func (m *testIntValue) ReportCount(value int64) { m.val = value m.reporter.cg.Done() } func (m *testIntValue) ReportTimer(interval time.Duration) { m.val = int64(interval) m.reporter.tg.Done() } type testFloatValue struct { val float64 tags map[string]string reporter *testStatsReporter } func (m *testFloatValue) ReportGauge(value float64) { m.val = value m.reporter.gg.Done() } type testHistogramValue struct { tags map[string]string valueSamples map[float64]int durationSamples map[time.Duration]int } func newTestHistogramValue() *testHistogramValue { return &testHistogramValue{ valueSamples: make(map[float64]int), durationSamples: make(map[time.Duration]int), } } type testStatsReporter struct { cg sync.WaitGroup gg sync.WaitGroup tg sync.WaitGroup hg sync.WaitGroup counters map[string]*testIntValue gauges map[string]*testFloatValue timers map[string]*testIntValue histograms map[string]*testHistogramValue flushes int32 } // newTestStatsReporter returns a new TestStatsReporter func newTestStatsReporter() *testStatsReporter { return &testStatsReporter{ counters: make(map[string]*testIntValue), gauges: make(map[string]*testFloatValue), timers: make(map[string]*testIntValue), histograms: make(map[string]*testHistogramValue), } } func (r *testStatsReporter) getCounters() map[string]*testIntValue { dst := make(map[string]*testIntValue, len(r.counters)) for k, v := range r.counters { var ( parts = strings.Split(k, "+") name string ) if len(parts) > 0 { name = parts[0] } dst[name] = v } return dst } func (r *testStatsReporter) getGauges() map[string]*testFloatValue { dst := make(map[string]*testFloatValue, len(r.gauges)) for k, v := range r.gauges { var ( parts = strings.Split(k, "+") name string ) if len(parts) > 0 { name = parts[0] } dst[name] = v } return dst } func (r *testStatsReporter) getTimers() map[string]*testIntValue { dst := make(map[string]*testIntValue, len(r.timers)) for k, v := range r.timers { var ( parts = strings.Split(k, "+") name string ) if len(parts) > 0 { name = parts[0] } dst[name] = v } return dst } func (r *testStatsReporter) getHistograms() map[string]*testHistogramValue { dst := make(map[string]*testHistogramValue, len(r.histograms)) for k, v := range r.histograms { var ( parts = strings.Split(k, "+") name string ) if len(parts) > 0 { name = parts[0] } dst[name] = v } return dst } func (r *testStatsReporter) WaitAll() { r.cg.Wait() r.gg.Wait() r.tg.Wait() r.hg.Wait() } func (r *testStatsReporter) AllocateCounter( name string, tags map[string]string, ) CachedCount { counter := &testIntValue{ val: 0, tags: tags, reporter: r, } r.counters[name] = counter return counter } func (r *testStatsReporter) ReportCounter(name string, tags map[string]string, value int64) { r.counters[name] = &testIntValue{ val: value, tags: tags, } r.cg.Done() } func (r *testStatsReporter) AllocateGauge( name string, tags map[string]string, ) CachedGauge { gauge := &testFloatValue{ val: 0, tags: tags, reporter: r, } r.gauges[name] = gauge return gauge } func (r *testStatsReporter) ReportGauge(name string, tags map[string]string, value float64) { r.gauges[name] = &testFloatValue{ val: value, tags: tags, } r.gg.Done() } func (r *testStatsReporter) AllocateTimer( name string, tags map[string]string, ) CachedTimer { timer := &testIntValue{ val: 0, tags: tags, reporter: r, } r.timers[name] = timer return timer } func (r *testStatsReporter) ReportTimer(name string, tags map[string]string, interval time.Duration) { r.timers[name] = &testIntValue{ val: int64(interval), tags: tags, } r.tg.Done() } func (r *testStatsReporter) AllocateHistogram( name string, tags map[string]string, buckets Buckets, ) CachedHistogram { return testStatsReporterCachedHistogram{r, name, tags, buckets} } type testStatsReporterCachedHistogram struct { r *testStatsReporter name string tags map[string]string buckets Buckets } func (h testStatsReporterCachedHistogram) ValueBucket( bucketLowerBound, bucketUpperBound float64, ) CachedHistogramBucket { return testStatsReporterCachedHistogramValueBucket{h, bucketLowerBound, bucketUpperBound} } func (h testStatsReporterCachedHistogram) DurationBucket( bucketLowerBound, bucketUpperBound time.Duration, ) CachedHistogramBucket { return testStatsReporterCachedHistogramDurationBucket{h, bucketLowerBound, bucketUpperBound} } type testStatsReporterCachedHistogramValueBucket struct { histogram testStatsReporterCachedHistogram bucketLowerBound float64 bucketUpperBound float64 } func (b testStatsReporterCachedHistogramValueBucket) ReportSamples(v int64) { b.histogram.r.ReportHistogramValueSamples( b.histogram.name, b.histogram.tags, b.histogram.buckets, b.bucketLowerBound, b.bucketUpperBound, v, ) } type testStatsReporterCachedHistogramDurationBucket struct { histogram testStatsReporterCachedHistogram bucketLowerBound time.Duration bucketUpperBound time.Duration } func (b testStatsReporterCachedHistogramDurationBucket) ReportSamples(v int64) { b.histogram.r.ReportHistogramDurationSamples( b.histogram.name, b.histogram.tags, b.histogram.buckets, b.bucketLowerBound, b.bucketUpperBound, v, ) } func (r *testStatsReporter) ReportHistogramValueSamples( name string, tags map[string]string, buckets Buckets, bucketLowerBound float64, bucketUpperBound float64, samples int64, ) { key := KeyForPrefixedStringMap(name, tags) value, ok := r.histograms[key] if !ok { value = newTestHistogramValue() value.tags = tags r.histograms[key] = value } value.valueSamples[bucketUpperBound] = int(samples) r.hg.Done() } func (r *testStatsReporter) ReportHistogramDurationSamples( name string, tags map[string]string, buckets Buckets, bucketLowerBound time.Duration, bucketUpperBound time.Duration, samples int64, ) { key := KeyForPrefixedStringMap(name, tags) value, ok := r.histograms[key] if !ok { value = newTestHistogramValue() value.tags = tags r.histograms[key] = value } value.durationSamples[bucketUpperBound] = int(samples) r.hg.Done() } func (r *testStatsReporter) Capabilities() Capabilities { return capabilitiesReportingNoTagging } func (r *testStatsReporter) Flush() { atomic.AddInt32(&r.flushes, 1) } func TestWriteTimerImmediately(t *testing.T) { r := newTestStatsReporter() s, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: true}, 0) defer closer.Close() r.tg.Add(1) s.Timer("ticky").Record(time.Millisecond * 175) r.tg.Wait() } func TestWriteTimerClosureImmediately(t *testing.T) { r := newTestStatsReporter() s, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: true}, 0) defer closer.Close() r.tg.Add(1) tm := s.Timer("ticky") tm.Start().Stop() r.tg.Wait() } func TestWriteReportLoop(t *testing.T) { r := newTestStatsReporter() s, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: true}, 10) defer closer.Close() r.cg.Add(1) s.Counter("bar").Inc(1) r.gg.Add(1) s.Gauge("zed").Update(1) r.tg.Add(1) s.Timer("ticky").Record(time.Millisecond * 175) r.hg.Add(1) s.Histogram("baz", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) r.WaitAll() } func TestWriteReportLoopDefaultInterval(t *testing.T) { r := newTestStatsReporter() s, closer := NewRootScopeWithDefaultInterval( ScopeOptions{Reporter: r, OmitCardinalityMetrics: true}, ) defer closer.Close() r.cg.Add(1) s.Counter("bar").Inc(1) r.gg.Add(1) s.Gauge("zed").Update(1) r.tg.Add(1) s.Timer("ticky").Record(time.Millisecond * 175) r.hg.Add(1) s.Histogram("baz", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) r.WaitAll() } func TestCachedReportLoop(t *testing.T) { r := newTestStatsReporter() s, closer := NewRootScope(ScopeOptions{CachedReporter: r, OmitCardinalityMetrics: true}, 10) defer closer.Close() r.cg.Add(1) s.Counter("bar").Inc(1) r.gg.Add(1) s.Gauge("zed").Update(1) r.tg.Add(1) s.Timer("ticky").Record(time.Millisecond * 175) r.hg.Add(1) s.Histogram("baz", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) r.WaitAll() } func testReportLoopFlushOnce(t *testing.T, cached bool) { r := newTestStatsReporter() scopeOpts := ScopeOptions{CachedReporter: r, OmitCardinalityMetrics: true} if !cached { scopeOpts = ScopeOptions{Reporter: r, OmitCardinalityMetrics: true} } s, closer := NewRootScope(scopeOpts, 10*time.Minute) r.cg.Add(2) s.Counter("foobar").Inc(1) s.SubScope("baz").Counter("bar").Inc(1) r.gg.Add(2) s.Gauge("zed").Update(1) s.SubScope("baz").Gauge("zed").Update(1) r.tg.Add(2) s.Timer("ticky").Record(time.Millisecond * 175) s.SubScope("woof").Timer("sod").Record(time.Millisecond * 175) r.hg.Add(2) s.SubScope("woofers").Histogram("boo", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) s.Histogram("baz", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) closer.Close() r.WaitAll() v := atomic.LoadInt32(&r.flushes) assert.Equal(t, int32(1), v) } func TestCachedReporterFlushOnce(t *testing.T) { testReportLoopFlushOnce(t, true) } func TestReporterFlushOnce(t *testing.T) { testReportLoopFlushOnce(t, false) } func TestWriteOnce(t *testing.T) { r := newTestStatsReporter() root, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: true}, 0) defer closer.Close() s := root.(*scope) r.cg.Add(1) s.Counter("bar").Inc(1) r.gg.Add(1) s.Gauge("zed").Update(1) r.tg.Add(1) s.Timer("ticky").Record(time.Millisecond * 175) r.hg.Add(1) s.Histogram("baz", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) r.hg.Add(1) s.Histogram("bat", MustMakeLinearValueBuckets(1, 1, 3)).RecordValue(2.1) r.hg.Add(1) s.SubScope("test").Histogram("bat", MustMakeLinearValueBuckets(1, 1, 3)).RecordValue(1.1) r.hg.Add(1) s.SubScope("test").Histogram("bat", MustMakeLinearValueBuckets(1, 1, 3)).RecordValue(2.1) buckets := MustMakeLinearValueBuckets(100, 10, 3) r.hg.Add(1) s.SubScope("test").Histogram("qux", buckets).RecordValue(135.0) r.hg.Add(1) s.SubScope("test").Histogram("quux", buckets).RecordValue(101.0) r.hg.Add(1) s.SubScope("test2").Histogram("quux", buckets).RecordValue(101.0) s.reportLoopRun() r.WaitAll() var ( counters = r.getCounters() gauges = r.getGauges() timers = r.getTimers() histograms = r.getHistograms() ) assert.EqualValues(t, 1, counters["bar"].val) assert.EqualValues(t, 1, gauges["zed"].val) assert.EqualValues(t, time.Millisecond*175, timers["ticky"].val) assert.EqualValues(t, 1, histograms["baz"].valueSamples[50.0]) assert.EqualValues(t, 1, histograms["bat"].valueSamples[3.0]) assert.EqualValues(t, 1, histograms["test.bat"].valueSamples[2.0]) assert.EqualValues(t, 1, histograms["test.bat"].valueSamples[3.0]) assert.EqualValues(t, 1, histograms["test.qux"].valueSamples[math.MaxFloat64]) assert.EqualValues(t, 1, histograms["test.quux"].valueSamples[110.0]) assert.EqualValues(t, 1, histograms["test2.quux"].valueSamples[110.0]) r = newTestStatsReporter() s.reportLoopRun() counters = r.getCounters() gauges = r.getGauges() timers = r.getTimers() histograms = r.getHistograms() assert.Nil(t, counters["bar"]) assert.Nil(t, gauges["zed"]) assert.Nil(t, timers["ticky"]) assert.Nil(t, histograms["baz"]) assert.Nil(t, histograms["bat"]) assert.Nil(t, histograms["test.qux"]) } func TestHistogramSharedBucketMetrics(t *testing.T) { var ( r = newTestStatsReporter() scope = newRootScope(ScopeOptions{ Prefix: "", Tags: nil, CachedReporter: r, OmitCardinalityMetrics: true, }, 0) builder = func(s Scope) func(map[string]string) { buckets := MustMakeLinearValueBuckets(10, 10, 3) return func(tags map[string]string) { s.Tagged(tags).Histogram("hist", buckets).RecordValue(19.0) } } ) var ( wg = &sync.WaitGroup{} record = builder(scope) ) r.hg.Add(4) for i := 0; i < 10000; i++ { i := i wg.Add(1) go func() { defer wg.Done() val := strconv.Itoa(i % 4) record( map[string]string{ "key": val, }, ) time.Sleep(time.Duration(rand.Float64() * float64(time.Second))) }() } wg.Wait() scope.reportRegistry() r.WaitAll() unseen := map[string]struct{}{ "0": {}, "1": {}, "2": {}, "3": {}, } require.Equal(t, len(unseen), len(r.histograms)) for name, value := range r.histograms { if !strings.HasPrefix(name, "hist+") { continue } count, ok := value.valueSamples[20.0] require.True(t, ok) require.Equal(t, 2500, count) delete(unseen, value.tags["key"]) } require.Equal(t, 0, len(unseen), fmt.Sprintf("%v", unseen)) } func TestConcurrentUpdates(t *testing.T) { var ( r = newTestStatsReporter() wg = &sync.WaitGroup{} workerCount = 20 scopeCount = 4 countersPerScope = 4 counterIncrs = 5000 rs = newRootScope( ScopeOptions{ Prefix: "", Tags: nil, CachedReporter: r, OmitCardinalityMetrics: true, }, 0, ) scopes = []Scope{rs} counters []Counter ) // Instantiate Subscopes. for i := 1; i < scopeCount; i++ { scopes = append(scopes, rs.SubScope(fmt.Sprintf("subscope_%d", i))) } // Instantiate Counters. for sNum, s := range scopes { for cNum := 0; cNum < countersPerScope; cNum++ { counters = append(counters, s.Counter(fmt.Sprintf("scope_%d_counter_%d", sNum, cNum))) } } // Instantiate workers. r.cg.Add(scopeCount * countersPerScope) for worker := 0; worker < workerCount; worker++ { wg.Add(1) go func() { defer wg.Done() // Counter should have counterIncrs * workerCount. for i := 0; i < counterIncrs*len(counters); i++ { counters[i%len(counters)].Inc(1) } }() } wg.Wait() rs.reportRegistry() r.WaitAll() wantVal := int64(workerCount * counterIncrs) for _, gotCounter := range r.getCounters() { assert.Equal(t, gotCounter.val, wantVal) } } func TestCounterSanitized(t *testing.T) { r := newTestStatsReporter() root, closer := NewRootScope(ScopeOptions{ Reporter: r, SanitizeOptions: &alphanumericSanitizerOpts, OmitCardinalityMetrics: true, }, 0) defer closer.Close() s := root.(*scope) r.cg.Add(1) s.Counter("how?").Inc(1) r.gg.Add(1) s.Gauge("does!").Update(1) r.tg.Add(1) s.Timer("this!").Record(time.Millisecond * 175) r.hg.Add(1) s.Histogram("work1!?", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) s.report(r) r.WaitAll() var ( counters = r.getCounters() gauges = r.getGauges() timers = r.getTimers() histograms = r.getHistograms() ) assert.Nil(t, counters["how?"]) assert.EqualValues(t, 1, counters["how_"].val) assert.Nil(t, gauges["does!"]) assert.EqualValues(t, 1, gauges["does_"].val) assert.Nil(t, timers["this!"]) assert.EqualValues(t, time.Millisecond*175, timers["this_"].val) assert.Nil(t, histograms["work1!?"]) assert.EqualValues(t, 1, histograms["work1__"].valueSamples[50.0]) r = newTestStatsReporter() s.report(r) counters = r.getCounters() gauges = r.getGauges() timers = r.getTimers() histograms = r.getHistograms() assert.Nil(t, counters["how?"]) assert.Nil(t, counters["how_"]) assert.Nil(t, gauges["does!"]) assert.Nil(t, gauges["does_"]) assert.Nil(t, timers["this!"]) assert.Nil(t, timers["this_"]) assert.Nil(t, histograms["work1!?"]) assert.Nil(t, histograms["work1__"]) } func TestCachedReporter(t *testing.T) { r := newTestStatsReporter() root, closer := NewRootScope(ScopeOptions{CachedReporter: r, OmitCardinalityMetrics: true}, 0) defer closer.Close() s := root.(*scope) r.cg.Add(1) s.Counter("bar").Inc(1) r.gg.Add(1) s.Gauge("zed").Update(1) r.tg.Add(1) s.Timer("ticky").Record(time.Millisecond * 175) r.hg.Add(2) s.Histogram("baz", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) s.Histogram("qux", MustMakeLinearDurationBuckets(0, 10*time.Millisecond, 10)). RecordDuration(42 * time.Millisecond) s.cachedReport() r.WaitAll() var ( counters = r.getCounters() gauges = r.getGauges() timers = r.getTimers() histograms = r.getHistograms() ) assert.EqualValues(t, 1, counters["bar"].val) assert.EqualValues(t, 1, gauges["zed"].val) assert.EqualValues(t, time.Millisecond*175, timers["ticky"].val) assert.EqualValues(t, 1, histograms["baz"].valueSamples[50.0]) assert.EqualValues(t, 1, histograms["qux"].durationSamples[50*time.Millisecond]) } func TestRootScopeWithoutPrefix(t *testing.T) { r := newTestStatsReporter() root, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: true}, 0) defer closer.Close() s := root.(*scope) r.cg.Add(1) s.Counter("bar").Inc(1) s.Counter("bar").Inc(20) r.gg.Add(1) s.Gauge("zed").Update(1) r.tg.Add(1) s.Timer("blork").Record(time.Millisecond * 175) r.hg.Add(1) s.Histogram("baz", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) s.report(r) r.WaitAll() var ( counters = r.getCounters() gauges = r.getGauges() timers = r.getTimers() histograms = r.getHistograms() ) assert.EqualValues(t, 21, counters["bar"].val) assert.EqualValues(t, 1, gauges["zed"].val) assert.EqualValues(t, time.Millisecond*175, timers["blork"].val) assert.EqualValues(t, 1, histograms["baz"].valueSamples[50.0]) } func TestRootScopeWithPrefix(t *testing.T) { r := newTestStatsReporter() root, closer := NewRootScope( ScopeOptions{Prefix: "foo", Reporter: r, OmitCardinalityMetrics: true}, 0, ) defer closer.Close() s := root.(*scope) r.cg.Add(1) s.Counter("bar").Inc(1) s.Counter("bar").Inc(20) r.gg.Add(1) s.Gauge("zed").Update(1) r.tg.Add(1) s.Timer("blork").Record(time.Millisecond * 175) r.hg.Add(1) s.Histogram("baz", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) s.report(r) r.WaitAll() var ( counters = r.getCounters() gauges = r.getGauges() timers = r.getTimers() histograms = r.getHistograms() ) assert.EqualValues(t, 21, counters["foo.bar"].val) assert.EqualValues(t, 1, gauges["foo.zed"].val) assert.EqualValues(t, time.Millisecond*175, timers["foo.blork"].val) assert.EqualValues(t, 1, histograms["foo.baz"].valueSamples[50.0]) } func TestRootScopeWithDifferentSeparator(t *testing.T) { r := newTestStatsReporter() root, closer := NewRootScope( ScopeOptions{ Prefix: "foo", Separator: "_", Reporter: r, OmitCardinalityMetrics: true, }, 0, ) defer closer.Close() s := root.(*scope) r.cg.Add(1) s.Counter("bar").Inc(1) s.Counter("bar").Inc(20) r.gg.Add(1) s.Gauge("zed").Update(1) r.tg.Add(1) s.Timer("blork").Record(time.Millisecond * 175) r.hg.Add(1) s.Histogram("baz", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) s.report(r) r.WaitAll() var ( counters = r.getCounters() gauges = r.getGauges() timers = r.getTimers() histograms = r.getHistograms() ) assert.EqualValues(t, 21, counters["foo_bar"].val) assert.EqualValues(t, 1, gauges["foo_zed"].val) assert.EqualValues(t, time.Millisecond*175, timers["foo_blork"].val) assert.EqualValues(t, 1, histograms["foo_baz"].valueSamples[50.0]) } func TestSubScope(t *testing.T) { r := newTestStatsReporter() root, closer := NewRootScope( ScopeOptions{Prefix: "foo", Reporter: r, OmitCardinalityMetrics: true}, 0, ) defer closer.Close() tags := map[string]string{"foo": "bar"} s := root.Tagged(tags).SubScope("mork").(*scope) r.cg.Add(1) s.Counter("bar").Inc(1) s.Counter("bar").Inc(20) r.gg.Add(1) s.Gauge("zed").Update(1) r.tg.Add(1) s.Timer("blork").Record(time.Millisecond * 175) r.hg.Add(1) s.Histogram("baz", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) s.report(r) r.WaitAll() var ( counters = r.getCounters() gauges = r.getGauges() timers = r.getTimers() histograms = r.getHistograms() ) // Assert prefixed correctly assert.EqualValues(t, 21, counters["foo.mork.bar"].val) assert.EqualValues(t, 1, gauges["foo.mork.zed"].val) assert.EqualValues(t, time.Millisecond*175, timers["foo.mork.blork"].val) assert.EqualValues(t, 1, histograms["foo.mork.baz"].valueSamples[50.0]) // Assert tags inherited assert.Equal(t, tags, counters["foo.mork.bar"].tags) assert.Equal(t, tags, gauges["foo.mork.zed"].tags) assert.Equal(t, tags, timers["foo.mork.blork"].tags) assert.Equal(t, tags, histograms["foo.mork.baz"].tags) } func TestSubScopeClose(t *testing.T) { r := newTestStatsReporter() rs, closer := NewRootScope(ScopeOptions{Prefix: "foo", Reporter: r, OmitCardinalityMetrics: true}, 0) // defer closer.Close() _ = closer var ( root = rs.(*scope) s = root.SubScope("mork").(*scope) rootCounter = root.Counter("foo") subCounter = s.Counter("foo") ) // Emit a metric from both scopes. r.cg.Add(1) rootCounter.Inc(1) r.cg.Add(1) subCounter.Inc(1) // Verify that we got both metrics. root.reportRegistry() r.WaitAll() counters := r.getCounters() require.EqualValues(t, 1, counters["foo.foo"].val) require.EqualValues(t, 1, counters["foo.mork.foo"].val) // Close the subscope. We expect both metrics to still be reported, because // we won't have reported the registry before we update the metrics. require.NoError(t, s.Close()) // Create a subscope from the now-closed scope; it should nop. ns := s.SubScope("foobar") require.Equal(t, NoopScope, ns) // Emit a metric from all scopes. r.cg.Add(1) rootCounter.Inc(2) r.cg.Add(1) subCounter.Inc(2) // Verify that we still got both metrics. root.reportLoopRun() r.WaitAll() counters = r.getCounters() require.EqualValues(t, 2, counters["foo.foo"].val) require.EqualValues(t, 2, counters["foo.mork.foo"].val) // Emit a metric for both scopes. The root counter should succeed, and the // subscope counter should not update what's in the reporter. r.cg.Add(1) rootCounter.Inc(3) subCounter.Inc(3) root.reportLoopRun() r.WaitAll() time.Sleep(time.Second) // since we can't wg.Add the non-reported counter // We only expect the root scope counter; the subscope counter will be the // value previously held by the reporter, because it has not been udpated. counters = r.getCounters() require.EqualValues(t, 3, counters["foo.foo"].val) require.EqualValues(t, 2, counters["foo.mork.foo"].val) // Ensure that we can double-close harmlessly. require.NoError(t, s.Close()) // Create one more scope so that we can ensure it's defunct once the root is // closed. ns = root.SubScope("newscope") // Close the root scope. We should not be able to emit any more metrics, // because the root scope reports the registry prior to closing. require.NoError(t, closer.Close()) ns.Counter("newcounter").Inc(1) rootCounter.Inc(4) root.registry.Report(r) time.Sleep(time.Second) // since we can't wg.Add the non-reported counter // We do not expect any updates. counters = r.getCounters() require.EqualValues(t, 3, counters["foo.foo"].val) require.EqualValues(t, 2, counters["foo.mork.foo"].val) _, found := counters["newscope.newcounter"] require.False(t, found) // Ensure that we can double-close harmlessly. require.NoError(t, closer.Close()) } func TestTaggedSubScope(t *testing.T) { r := newTestStatsReporter() ts := map[string]string{"env": "test"} root, closer := NewRootScope( ScopeOptions{ Prefix: "foo", Tags: ts, Reporter: r, OmitCardinalityMetrics: true, }, 0, ) defer closer.Close() s := root.(*scope) tscope := root.Tagged(map[string]string{"service": "test"}).(*scope) scope := root r.cg.Add(1) scope.Counter("beep").Inc(1) r.cg.Add(1) tscope.Counter("boop").Inc(1) r.hg.Add(1) scope.Histogram("baz", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) r.hg.Add(1) tscope.Histogram("bar", MustMakeLinearValueBuckets(0, 10, 10)). RecordValue(42.42) s.report(r) tscope.report(r) r.cg.Wait() var ( counters = r.getCounters() histograms = r.getHistograms() ) assert.EqualValues(t, 1, counters["foo.beep"].val) assert.EqualValues(t, ts, counters["foo.beep"].tags) assert.EqualValues(t, 1, counters["foo.boop"].val) assert.EqualValues( t, map[string]string{ "env": "test", "service": "test", }, counters["foo.boop"].tags, ) assert.EqualValues(t, 1, histograms["foo.baz"].valueSamples[50.0]) assert.EqualValues(t, ts, histograms["foo.baz"].tags) assert.EqualValues(t, 1, histograms["foo.bar"].valueSamples[50.0]) assert.EqualValues( t, map[string]string{ "env": "test", "service": "test", }, histograms["foo.bar"].tags, ) } func TestTaggedSanitizedSubScope(t *testing.T) { r := newTestStatsReporter() ts := map[string]string{"env": "test:env"} root, closer := NewRootScope(ScopeOptions{ Prefix: "foo", Tags: ts, Reporter: r, SanitizeOptions: &alphanumericSanitizerOpts, OmitCardinalityMetrics: true, }, 0) defer closer.Close() s := root.(*scope) tscope := root.Tagged(map[string]string{"service": "test.service"}).(*scope) r.cg.Add(1) tscope.Counter("beep").Inc(1) s.report(r) tscope.report(r) r.cg.Wait() counters := r.getCounters() assert.EqualValues(t, 1, counters["foo_beep"].val) assert.EqualValues( t, map[string]string{ "env": "test_env", "service": "test_service", }, counters["foo_beep"].tags, ) } func TestTaggedExistingReturnsSameScope(t *testing.T) { r := newTestStatsReporter() for _, initialTags := range []map[string]string{ nil, {"env": "test"}, } { root, closer := NewRootScope( ScopeOptions{ Prefix: "foo", Tags: initialTags, Reporter: r, OmitCardinalityMetrics: true, }, 0, ) defer closer.Close() rootScope := root.(*scope) fooScope := root.Tagged(map[string]string{"foo": "bar"}).(*scope) assert.NotEqual(t, rootScope, fooScope) assert.Equal(t, fooScope, fooScope.Tagged(nil)) fooBarScope := fooScope.Tagged(map[string]string{"bar": "baz"}).(*scope) assert.NotEqual(t, fooScope, fooBarScope) assert.Equal(t, fooBarScope, fooScope.Tagged(map[string]string{"bar": "baz"}).(*scope)) } } func TestSnapshot(t *testing.T) { commonTags := map[string]string{"env": "test"} s := NewTestScope("foo", map[string]string{"env": "test"}) child := s.Tagged(map[string]string{"service": "test"}) s.Counter("beep").Inc(1) s.Gauge("bzzt").Update(2) s.Timer("brrr").Record(1 * time.Second) s.Timer("brrr").Record(2 * time.Second) s.Histogram("fizz", ValueBuckets{0, 2, 4}).RecordValue(1) s.Histogram("fizz", ValueBuckets{0, 2, 4}).RecordValue(5) s.Histogram("buzz", DurationBuckets{time.Second * 2, time.Second * 4}).RecordDuration(time.Second) child.Counter("boop").Inc(1) // Should be able to call Snapshot any number of times and get same result. for i := 0; i < 3; i++ { t.Run( fmt.Sprintf("attempt %d", i), func(t *testing.T) { snap := s.Snapshot() counters, gauges, timers, histograms := snap.Counters(), snap.Gauges(), snap.Timers(), snap.Histograms() assert.EqualValues(t, 1, counters["foo.beep+env=test"].Value()) assert.EqualValues(t, commonTags, counters["foo.beep+env=test"].Tags()) assert.EqualValues(t, 2, gauges["foo.bzzt+env=test"].Value()) assert.EqualValues(t, commonTags, gauges["foo.bzzt+env=test"].Tags()) assert.EqualValues( t, []time.Duration{ 1 * time.Second, 2 * time.Second, }, timers["foo.brrr+env=test"].Values(), ) assert.EqualValues(t, commonTags, timers["foo.brrr+env=test"].Tags()) assert.EqualValues( t, map[float64]int64{ 0: 0, 2: 1, 4: 0, math.MaxFloat64: 1, }, histograms["foo.fizz+env=test"].Values(), ) assert.EqualValues(t, map[time.Duration]int64(nil), histograms["foo.fizz+env=test"].Durations()) assert.EqualValues(t, commonTags, histograms["foo.fizz+env=test"].Tags()) assert.EqualValues(t, map[float64]int64(nil), histograms["foo.buzz+env=test"].Values()) assert.EqualValues( t, map[time.Duration]int64{ time.Second * 2: 1, time.Second * 4: 0, math.MaxInt64: 0, }, histograms["foo.buzz+env=test"].Durations(), ) assert.EqualValues(t, commonTags, histograms["foo.buzz+env=test"].Tags()) assert.EqualValues(t, 1, counters["foo.boop+env=test,service=test"].Value()) assert.EqualValues( t, map[string]string{ "env": "test", "service": "test", }, counters["foo.boop+env=test,service=test"].Tags(), ) }, ) } } func TestSnapshotConcurrent(t *testing.T) { var ( scope = NewTestScope("", nil) quit = make(chan struct{}) done = make(chan struct{}) ) go func() { defer close(done) for { select { case <-quit: return default: hello := scope.Tagged(map[string]string{"a": "b"}).Counter("hello") hello.Inc(1) } } }() var val CounterSnapshot for { val = scope.Snapshot().Counters()["hello+a=b"] if val != nil { quit <- struct{}{} break } } require.NotNil(t, val) <-done } func TestCapabilities(t *testing.T) { r := newTestStatsReporter() s, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: true}, 0) defer closer.Close() assert.True(t, s.Capabilities().Reporting()) assert.False(t, s.Capabilities().Tagging()) } func TestCapabilitiesNoReporter(t *testing.T) { s, closer := NewRootScope(ScopeOptions{}, 0) defer closer.Close() assert.False(t, s.Capabilities().Reporting()) assert.False(t, s.Capabilities().Tagging()) } func TestNilTagMerge(t *testing.T) { assert.Nil(t, nil, mergeRightTags(nil, nil)) } func TestScopeDefaultBuckets(t *testing.T) { r := newTestStatsReporter() root, closer := NewRootScope(ScopeOptions{ DefaultBuckets: DurationBuckets{ 0 * time.Millisecond, 30 * time.Millisecond, 60 * time.Millisecond, 90 * time.Millisecond, 120 * time.Millisecond, }, Reporter: r, OmitCardinalityMetrics: true, }, 0) defer closer.Close() s := root.(*scope) r.hg.Add(2) s.Histogram("baz", DefaultBuckets).RecordDuration(42 * time.Millisecond) s.Histogram("baz", DefaultBuckets).RecordDuration(84 * time.Millisecond) s.Histogram("baz", DefaultBuckets).RecordDuration(84 * time.Millisecond) s.report(r) r.WaitAll() histograms := r.getHistograms() assert.EqualValues(t, 1, histograms["baz"].durationSamples[60*time.Millisecond]) assert.EqualValues(t, 2, histograms["baz"].durationSamples[90*time.Millisecond]) } type testMets struct { c Counter } func newTestMets(scope Scope) testMets { return testMets{ c: scope.Counter("honk"), } } func TestReturnByValue(t *testing.T) { r := newTestStatsReporter() root, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: true}, 0) defer closer.Close() s := root.(*scope) mets := newTestMets(s) r.cg.Add(1) mets.c.Inc(3) s.report(r) r.cg.Wait() counters := r.getCounters() assert.EqualValues(t, 3, counters["honk"].val) } func TestScopeAvoidReportLoopRunOnClose(t *testing.T) { r := newTestStatsReporter() root, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: true}, 0) s := root.(*scope) s.reportLoopRun() assert.Equal(t, int32(1), atomic.LoadInt32(&r.flushes)) assert.NoError(t, closer.Close()) s.reportLoopRun() assert.Equal(t, int32(2), atomic.LoadInt32(&r.flushes)) } func TestScopeFlushOnClose(t *testing.T) { r := newTestStatsReporter() root, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: true}, time.Hour) r.cg.Add(1) root.Counter("foo").Inc(1) counters := r.getCounters() assert.Nil(t, counters["foo"]) assert.NoError(t, closer.Close()) counters = r.getCounters() assert.EqualValues(t, 1, counters["foo"].val) assert.NoError(t, closer.Close()) } golang-github-uber-go-tally-4.1.11/stats.go000066400000000000000000000303151456146544700205150ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "fmt" "math" "sort" "sync" "sync/atomic" "time" "github.com/uber-go/tally/v4/internal/identity" ) var ( capabilitiesNone = &capabilities{ reporting: false, tagging: false, } capabilitiesReportingNoTagging = &capabilities{ reporting: true, tagging: false, } capabilitiesReportingTagging = &capabilities{ reporting: true, tagging: true, } ) type capabilities struct { reporting bool tagging bool } func (c *capabilities) Reporting() bool { return c.reporting } func (c *capabilities) Tagging() bool { return c.tagging } type counter struct { prev int64 curr int64 cachedCount CachedCount } func newCounter(cachedCount CachedCount) *counter { return &counter{cachedCount: cachedCount} } func (c *counter) Inc(v int64) { atomic.AddInt64(&c.curr, v) } func (c *counter) value() int64 { curr := atomic.LoadInt64(&c.curr) prev := atomic.LoadInt64(&c.prev) if prev == curr { return 0 } atomic.StoreInt64(&c.prev, curr) return curr - prev } func (c *counter) report(name string, tags map[string]string, r StatsReporter) { delta := c.value() if delta == 0 { return } r.ReportCounter(name, tags, delta) } func (c *counter) cachedReport() { delta := c.value() if delta == 0 { return } c.cachedCount.ReportCount(delta) } func (c *counter) snapshot() int64 { return atomic.LoadInt64(&c.curr) - atomic.LoadInt64(&c.prev) } type gauge struct { updated uint64 curr uint64 cachedGauge CachedGauge } func newGauge(cachedGauge CachedGauge) *gauge { return &gauge{cachedGauge: cachedGauge} } func (g *gauge) Update(v float64) { atomic.StoreUint64(&g.curr, math.Float64bits(v)) atomic.StoreUint64(&g.updated, 1) } func (g *gauge) value() float64 { return math.Float64frombits(atomic.LoadUint64(&g.curr)) } func (g *gauge) report(name string, tags map[string]string, r StatsReporter) { if atomic.SwapUint64(&g.updated, 0) == 1 { r.ReportGauge(name, tags, g.value()) } } func (g *gauge) cachedReport() { if atomic.SwapUint64(&g.updated, 0) == 1 { g.cachedGauge.ReportGauge(g.value()) } } func (g *gauge) snapshot() float64 { return math.Float64frombits(atomic.LoadUint64(&g.curr)) } // NB(jra3): timers are a little special because they do no aggregate any data // at the timer level. The reporter buffers may timer entries and periodically // flushes. type timer struct { name string tags map[string]string reporter StatsReporter cachedTimer CachedTimer unreported timerValues } type timerValues struct { sync.RWMutex values []time.Duration } func newTimer( name string, tags map[string]string, r StatsReporter, cachedTimer CachedTimer, ) *timer { t := &timer{ name: name, tags: tags, reporter: r, cachedTimer: cachedTimer, } if r == nil { t.reporter = &timerNoReporterSink{timer: t} } return t } func (t *timer) Record(interval time.Duration) { if t.cachedTimer != nil { t.cachedTimer.ReportTimer(interval) } else { t.reporter.ReportTimer(t.name, t.tags, interval) } } func (t *timer) Start() Stopwatch { return NewStopwatch(globalNow(), t) } func (t *timer) RecordStopwatch(stopwatchStart time.Time) { d := globalNow().Sub(stopwatchStart) t.Record(d) } func (t *timer) snapshot() []time.Duration { t.unreported.RLock() snap := make([]time.Duration, len(t.unreported.values)) copy(snap, t.unreported.values) t.unreported.RUnlock() return snap } type timerNoReporterSink struct { sync.RWMutex timer *timer } func (r *timerNoReporterSink) ReportCounter( name string, tags map[string]string, value int64, ) { } func (r *timerNoReporterSink) ReportGauge( name string, tags map[string]string, value float64, ) { } func (r *timerNoReporterSink) ReportTimer( name string, tags map[string]string, interval time.Duration, ) { r.timer.unreported.Lock() r.timer.unreported.values = append(r.timer.unreported.values, interval) r.timer.unreported.Unlock() } func (r *timerNoReporterSink) ReportHistogramValueSamples( name string, tags map[string]string, buckets Buckets, bucketLowerBound float64, bucketUpperBound float64, samples int64, ) { } func (r *timerNoReporterSink) ReportHistogramDurationSamples( name string, tags map[string]string, buckets Buckets, bucketLowerBound time.Duration, bucketUpperBound time.Duration, samples int64, ) { } func (r *timerNoReporterSink) Capabilities() Capabilities { return capabilitiesReportingTagging } func (r *timerNoReporterSink) Flush() { } type sampleCounter struct { counter *counter cachedBucket CachedHistogramBucket } type histogram struct { htype histogramType name string tags map[string]string reporter StatsReporter specification Buckets buckets []histogramBucket samples []sampleCounter } type histogramType int const ( valueHistogramType histogramType = iota durationHistogramType ) func newHistogram( htype histogramType, name string, tags map[string]string, reporter StatsReporter, storage bucketStorage, cachedHistogram CachedHistogram, ) *histogram { h := &histogram{ htype: htype, name: name, tags: tags, reporter: reporter, specification: storage.buckets, buckets: storage.hbuckets, samples: make([]sampleCounter, len(storage.hbuckets)), } for i := range h.samples { h.samples[i].counter = newCounter(nil) if cachedHistogram != nil { switch htype { case durationHistogramType: h.samples[i].cachedBucket = cachedHistogram.DurationBucket( durationLowerBound(storage.hbuckets, i), storage.hbuckets[i].durationUpperBound, ) case valueHistogramType: h.samples[i].cachedBucket = cachedHistogram.ValueBucket( valueLowerBound(storage.hbuckets, i), storage.hbuckets[i].valueUpperBound, ) } } } return h } func (h *histogram) report(name string, tags map[string]string, r StatsReporter) { for i := range h.buckets { samples := h.samples[i].counter.value() if samples == 0 { continue } switch h.htype { case valueHistogramType: r.ReportHistogramValueSamples( name, tags, h.specification, valueLowerBound(h.buckets, i), h.buckets[i].valueUpperBound, samples, ) case durationHistogramType: r.ReportHistogramDurationSamples( name, tags, h.specification, durationLowerBound(h.buckets, i), h.buckets[i].durationUpperBound, samples, ) } } } func (h *histogram) cachedReport() { for i := range h.buckets { samples := h.samples[i].counter.value() if samples == 0 { continue } switch h.htype { case valueHistogramType: h.samples[i].cachedBucket.ReportSamples(samples) case durationHistogramType: h.samples[i].cachedBucket.ReportSamples(samples) } } } func (h *histogram) RecordValue(value float64) { if h.htype != valueHistogramType { return } // Find the highest inclusive of the bucket upper bound // and emit directly to it. Since we use BucketPairs to derive // buckets there will always be an inclusive bucket as // we always have a math.MaxFloat64 bucket. idx := sort.Search(len(h.buckets), func(i int) bool { return h.buckets[i].valueUpperBound >= value }) h.samples[idx].counter.Inc(1) } func (h *histogram) RecordDuration(value time.Duration) { if h.htype != durationHistogramType { return } // Find the highest inclusive of the bucket upper bound // and emit directly to it. Since we use BucketPairs to derive // buckets there will always be an inclusive bucket as // we always have a math.MaxInt64 bucket. idx := sort.Search(len(h.buckets), func(i int) bool { return h.buckets[i].durationUpperBound >= value }) h.samples[idx].counter.Inc(1) } func (h *histogram) Start() Stopwatch { return NewStopwatch(globalNow(), h) } func (h *histogram) RecordStopwatch(stopwatchStart time.Time) { d := globalNow().Sub(stopwatchStart) h.RecordDuration(d) } func (h *histogram) snapshotValues() map[float64]int64 { if h.htype != valueHistogramType { return nil } vals := make(map[float64]int64, len(h.buckets)) for i := range h.buckets { vals[h.buckets[i].valueUpperBound] = h.samples[i].counter.snapshot() } return vals } func (h *histogram) snapshotDurations() map[time.Duration]int64 { if h.htype != durationHistogramType { return nil } durations := make(map[time.Duration]int64, len(h.buckets)) for i := range h.buckets { durations[h.buckets[i].durationUpperBound] = h.samples[i].counter.snapshot() } return durations } type histogramBucket struct { valueUpperBound float64 durationUpperBound time.Duration } func durationLowerBound(buckets []histogramBucket, i int) time.Duration { if i <= 0 { return time.Duration(math.MinInt64) } return buckets[i-1].durationUpperBound } func valueLowerBound(buckets []histogramBucket, i int) float64 { if i <= 0 { return -math.MaxFloat64 } return buckets[i-1].valueUpperBound } type bucketStorage struct { buckets Buckets hbuckets []histogramBucket } func newBucketStorage( htype histogramType, buckets Buckets, ) bucketStorage { var ( pairs = BucketPairs(buckets) storage = bucketStorage{ buckets: buckets, hbuckets: make([]histogramBucket, 0, len(pairs)), } ) for _, pair := range pairs { storage.hbuckets = append(storage.hbuckets, histogramBucket{ valueUpperBound: pair.UpperBoundValue(), durationUpperBound: pair.UpperBoundDuration(), }) } return storage } type bucketCache struct { mtx sync.RWMutex cache map[uint64]bucketStorage } func newBucketCache() *bucketCache { return &bucketCache{ cache: make(map[uint64]bucketStorage), } } func (c *bucketCache) Get( htype histogramType, buckets Buckets, ) bucketStorage { id := getBucketsIdentity(buckets) c.mtx.RLock() storage, ok := c.cache[id] if !ok { c.mtx.RUnlock() c.mtx.Lock() storage = newBucketStorage(htype, buckets) c.cache[id] = storage c.mtx.Unlock() } else { c.mtx.RUnlock() if !bucketsEqual(buckets, storage.buckets) { storage = newBucketStorage(htype, buckets) } } return storage } // NullStatsReporter is an implementation of StatsReporter than simply does nothing. var NullStatsReporter StatsReporter = nullStatsReporter{} func (r nullStatsReporter) ReportCounter(name string, tags map[string]string, value int64) { } func (r nullStatsReporter) ReportGauge(name string, tags map[string]string, value float64) { } func (r nullStatsReporter) ReportTimer(name string, tags map[string]string, interval time.Duration) { } func (r nullStatsReporter) ReportHistogramValueSamples( name string, tags map[string]string, buckets Buckets, bucketLowerBound, bucketUpperBound float64, samples int64, ) { } func (r nullStatsReporter) ReportHistogramDurationSamples( name string, tags map[string]string, buckets Buckets, bucketLowerBound, bucketUpperBound time.Duration, samples int64, ) { } func (r nullStatsReporter) Capabilities() Capabilities { return capabilitiesNone } func (r nullStatsReporter) Flush() { } type nullStatsReporter struct{} func getBucketsIdentity(buckets Buckets) uint64 { switch b := buckets.(type) { case DurationBuckets: return identity.Durations(b.AsDurations()) case ValueBuckets: return identity.Float64s(b.AsValues()) default: panic(fmt.Sprintf("unexpected bucket type: %T", b)) } } golang-github-uber-go-tally-4.1.11/stats_benchmark_test.go000066400000000000000000000045231456146544700235700ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "testing" "time" ) func BenchmarkCounterInc(b *testing.B) { c := &counter{} for n := 0; n < b.N; n++ { c.Inc(1) } } func BenchmarkReportCounterNoData(b *testing.B) { c := &counter{} for n := 0; n < b.N; n++ { c.report("foo", nil, NullStatsReporter) } } func BenchmarkReportCounterWithData(b *testing.B) { c := &counter{} for n := 0; n < b.N; n++ { c.Inc(1) c.report("foo", nil, NullStatsReporter) } } func BenchmarkGaugeSet(b *testing.B) { g := &gauge{} for n := 0; n < b.N; n++ { g.Update(42) } } func BenchmarkReportGaugeNoData(b *testing.B) { g := &gauge{} for n := 0; n < b.N; n++ { g.report("bar", nil, NullStatsReporter) } } func BenchmarkReportGaugeWithData(b *testing.B) { g := &gauge{} for n := 0; n < b.N; n++ { g.Update(73) g.report("bar", nil, NullStatsReporter) } } func BenchmarkTimerStopwatch(b *testing.B) { t := &timer{ name: "bencher", tags: nil, reporter: NullStatsReporter, } for n := 0; n < b.N; n++ { t.Start().Stop() // start and stop } } func BenchmarkTimerReport(b *testing.B) { t := &timer{ name: "bencher", tags: nil, reporter: NullStatsReporter, } for n := 0; n < b.N; n++ { start := time.Now() t.Record(time.Since(start)) } } golang-github-uber-go-tally-4.1.11/stats_test.go000066400000000000000000000112761456146544700215610ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "math/rand" "testing" "time" "github.com/stretchr/testify/assert" ) type statsTestReporter struct { last interface{} valueSamples map[float64]int durationSamples map[time.Duration]int buckets Buckets } func newStatsTestReporter() *statsTestReporter { return &statsTestReporter{ valueSamples: make(map[float64]int), durationSamples: make(map[time.Duration]int), } } func (r *statsTestReporter) ReportCounter(name string, tags map[string]string, value int64) { r.last = value } func (r *statsTestReporter) ReportGauge(name string, tags map[string]string, value float64) { r.last = value } func (r *statsTestReporter) ReportTimer(name string, tags map[string]string, interval time.Duration) { r.last = interval } func (r *statsTestReporter) ReportHistogramValueSamples( name string, tags map[string]string, buckets Buckets, bucketLowerBound, bucketUpperBound float64, samples int64, ) { r.valueSamples[bucketUpperBound] = int(samples) r.buckets = buckets } func (r *statsTestReporter) ReportHistogramDurationSamples( name string, tags map[string]string, buckets Buckets, bucketLowerBound, bucketUpperBound time.Duration, samples int64, ) { r.durationSamples[bucketUpperBound] = int(samples) r.buckets = buckets } func (r *statsTestReporter) Capabilities() Capabilities { return capabilitiesReportingNoTagging } func (r *statsTestReporter) Flush() {} func TestCounter(t *testing.T) { counter := newCounter(nil) r := newStatsTestReporter() counter.Inc(1) counter.report("", nil, r) assert.Equal(t, int64(1), r.last) counter.Inc(1) counter.report("", nil, r) assert.Equal(t, int64(1), r.last) counter.Inc(1) counter.report("", nil, r) assert.Equal(t, int64(1), r.last) } func TestGauge(t *testing.T) { gauge := newGauge(nil) r := newStatsTestReporter() gauge.Update(42) gauge.report("", nil, r) assert.Equal(t, float64(42), r.last) gauge.Update(1234) gauge.Update(5678) gauge.report("", nil, r) assert.Equal(t, float64(5678), r.last) } func TestTimer(t *testing.T) { r := newStatsTestReporter() timer := newTimer("t1", nil, r, nil) timer.Record(42 * time.Millisecond) assert.Equal(t, 42*time.Millisecond, r.last) timer.Record(128 * time.Millisecond) assert.Equal(t, 128*time.Millisecond, r.last) } func TestHistogramValueSamples(t *testing.T) { r := newStatsTestReporter() buckets := MustMakeLinearValueBuckets(0, 10, 10) storage := newBucketStorage(valueHistogramType, buckets) h := newHistogram(valueHistogramType, "h1", nil, r, storage, nil) var offset float64 for i := 0; i < 3; i++ { h.RecordValue(offset + rand.Float64()*10) } offset = 50 for i := 0; i < 5; i++ { h.RecordValue(offset + rand.Float64()*10) } h.report(h.name, h.tags, r) assert.Equal(t, 3, r.valueSamples[10.0]) assert.Equal(t, 5, r.valueSamples[60.0]) assert.Equal(t, buckets, r.buckets) } func TestHistogramDurationSamples(t *testing.T) { r := newStatsTestReporter() buckets := MustMakeLinearDurationBuckets(0, 10*time.Millisecond, 10) storage := newBucketStorage(durationHistogramType, buckets) h := newHistogram(durationHistogramType, "h1", nil, r, storage, nil) var offset time.Duration for i := 0; i < 3; i++ { h.RecordDuration(offset + time.Duration(rand.Float64()*float64(10*time.Millisecond))) } offset = 50 * time.Millisecond for i := 0; i < 5; i++ { h.RecordDuration(offset + time.Duration(rand.Float64()*float64(10*time.Millisecond))) } h.report(h.name, h.tags, r) assert.Equal(t, 3, r.durationSamples[10*time.Millisecond]) assert.Equal(t, 5, r.durationSamples[60*time.Millisecond]) assert.Equal(t, buckets, r.buckets) } golang-github-uber-go-tally-4.1.11/statsd/000077500000000000000000000000001456146544700203305ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/statsd/README.md000066400000000000000000000013411456146544700216060ustar00rootroot00000000000000# A buffered statsd reporter See `examples/statsd_main.go` for an end to end example. Some emitted stats using the example listening with `nc 8125 -l -u`: ``` stats.my-service.test-histogram.100ms-200ms:2|c stats.my-service.test-histogram.300ms-400ms:1|c stats.my-service.test-histogram.600ms-800ms:1|c stats.my-service.test-counter:1|c stats.my-service.test-gauge:813|g ``` ## Options You can use either a basic or a buffered statsd client and pass it to the reporter along with options. The reporter options are: ```go // Options is a set of options for the tally reporter. type Options struct { // SampleRate is the metrics emission sample rate. If you // do not set this value it will be set to 1. SampleRate float32 } ``` golang-github-uber-go-tally-4.1.11/statsd/example/000077500000000000000000000000001456146544700217635ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/statsd/example/README.md000066400000000000000000000000531456146544700232400ustar00rootroot00000000000000# Statsd reporter example `go run ./*.go` golang-github-uber-go-tally-4.1.11/statsd/example/statsd_main.go000066400000000000000000000047261456146544700246310ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package main import ( "log" "math/rand" "time" "github.com/cactus/go-statsd-client/v5/statsd" tally "github.com/uber-go/tally/v4" statsdreporter "github.com/uber-go/tally/v4/statsd" ) // To view statsd emitted metrics locally you can use // netcat with "nc 8125 -l -u" func main() { statter, err := statsd.NewClientWithConfig(&statsd.ClientConfig{ Address: "127.0.0.1:8125", Prefix: "stats", UseBuffered: true, FlushInterval: 100 * time.Millisecond, FlushBytes: 1440, }) if err != nil { log.Fatalf("could not create statsd client: %v", err) } opts := statsdreporter.Options{} r := statsdreporter.NewReporter(statter, opts) scope, closer := tally.NewRootScope(tally.ScopeOptions{ Prefix: "my-service", Tags: map[string]string{}, Reporter: r, }, 1*time.Second) defer closer.Close() counter := scope.Counter("test-counter") gauge := scope.Gauge("test-gauge") timer := scope.Timer("test-timer") histogram := scope.Histogram("test-histogram", tally.DefaultBuckets) go func() { for { counter.Inc(1) time.Sleep(time.Second) } }() go func() { for { gauge.Update(rand.Float64() * 1000) time.Sleep(time.Second) } }() go func() { for { tsw := timer.Start() hsw := histogram.Start() time.Sleep(time.Duration(rand.Float64() * float64(time.Second))) tsw.Stop() hsw.Stop() } }() select {} } golang-github-uber-go-tally-4.1.11/statsd/reporter.go000066400000000000000000000107161456146544700225260ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package statsd import ( "fmt" "math" "strconv" "time" "github.com/cactus/go-statsd-client/v5/statsd" tally "github.com/uber-go/tally/v4" ) const ( // DefaultHistogramBucketNamePrecision is the default // precision to use when formatting the metric name // with the histogram bucket bound values. DefaultHistogramBucketNamePrecision = uint(6) ) type cactusStatsReporter struct { statter statsd.Statter sampleRate float32 bucketFmt string } // Options is a set of options for the tally reporter. type Options struct { // SampleRate is the metrics emission sample rate. If you // do not set this value it will be set to 1. SampleRate float32 // HistogramBucketNamePrecision is the precision to use when // formatting the metric name with the histogram bucket bound values. // By default this will be set to the const DefaultHistogramBucketPrecision. HistogramBucketNamePrecision uint } // NewReporter wraps a statsd.Statter for use with tally. Use either // statsd.NewClient or statsd.NewBufferedClient. func NewReporter(statsd statsd.Statter, opts Options) tally.StatsReporter { var nilSampleRate float32 if opts.SampleRate == nilSampleRate { opts.SampleRate = 1.0 } if opts.HistogramBucketNamePrecision == 0 { opts.HistogramBucketNamePrecision = DefaultHistogramBucketNamePrecision } return &cactusStatsReporter{ statter: statsd, sampleRate: opts.SampleRate, bucketFmt: "%." + strconv.Itoa(int(opts.HistogramBucketNamePrecision)) + "f", } } func (r *cactusStatsReporter) ReportCounter(name string, tags map[string]string, value int64) { r.statter.Inc(name, value, r.sampleRate) } func (r *cactusStatsReporter) ReportGauge(name string, tags map[string]string, value float64) { r.statter.Gauge(name, int64(value), r.sampleRate) } func (r *cactusStatsReporter) ReportTimer(name string, tags map[string]string, interval time.Duration) { r.statter.TimingDuration(name, interval, r.sampleRate) } func (r *cactusStatsReporter) ReportHistogramValueSamples( name string, tags map[string]string, buckets tally.Buckets, bucketLowerBound, bucketUpperBound float64, samples int64, ) { r.statter.Inc( fmt.Sprintf("%s.%s-%s", name, r.valueBucketString(bucketLowerBound), r.valueBucketString(bucketUpperBound)), samples, r.sampleRate) } func (r *cactusStatsReporter) ReportHistogramDurationSamples( name string, tags map[string]string, buckets tally.Buckets, bucketLowerBound, bucketUpperBound time.Duration, samples int64, ) { r.statter.Inc( fmt.Sprintf("%s.%s-%s", name, r.durationBucketString(bucketLowerBound), r.durationBucketString(bucketUpperBound)), samples, r.sampleRate) } func (r *cactusStatsReporter) valueBucketString( upperBound float64, ) string { if upperBound == math.MaxFloat64 { return "infinity" } if upperBound == -math.MaxFloat64 { return "-infinity" } return fmt.Sprintf(r.bucketFmt, upperBound) } func (r *cactusStatsReporter) durationBucketString( upperBound time.Duration, ) string { if upperBound == time.Duration(math.MaxInt64) { return "infinity" } if upperBound == time.Duration(math.MinInt64) { return "-infinity" } return upperBound.String() } func (r *cactusStatsReporter) Capabilities() tally.Capabilities { return r } func (r *cactusStatsReporter) Reporting() bool { return true } func (r *cactusStatsReporter) Tagging() bool { return false } func (r *cactusStatsReporter) Flush() { // no-op } golang-github-uber-go-tally-4.1.11/statsd/reporter_test.go000066400000000000000000000025251456146544700235640ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package statsd import ( "testing" "github.com/stretchr/testify/assert" ) func TestCapabilities(t *testing.T) { r := NewReporter(nil, Options{}) assert.True(t, r.Capabilities().Reporting()) assert.False(t, r.Capabilities().Tagging()) } golang-github-uber-go-tally-4.1.11/tallymock/000077500000000000000000000000001456146544700210255ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/tallymock/stats_reporter.go000066400000000000000000000116031456146544700244350ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/uber-go/tally (interfaces: StatsReporter) // Package tallymock is a generated GoMock package. package tallymock import ( reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" tally "github.com/uber-go/tally/v4" ) // MockStatsReporter is a mock of StatsReporter interface. type MockStatsReporter struct { ctrl *gomock.Controller recorder *MockStatsReporterMockRecorder } // MockStatsReporterMockRecorder is the mock recorder for MockStatsReporter. type MockStatsReporterMockRecorder struct { mock *MockStatsReporter } // NewMockStatsReporter creates a new mock instance. func NewMockStatsReporter(ctrl *gomock.Controller) *MockStatsReporter { mock := &MockStatsReporter{ctrl: ctrl} mock.recorder = &MockStatsReporterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockStatsReporter) EXPECT() *MockStatsReporterMockRecorder { return m.recorder } // Capabilities mocks base method. func (m *MockStatsReporter) Capabilities() tally.Capabilities { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Capabilities") ret0, _ := ret[0].(tally.Capabilities) return ret0 } // Capabilities indicates an expected call of Capabilities. func (mr *MockStatsReporterMockRecorder) Capabilities() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Capabilities", reflect.TypeOf((*MockStatsReporter)(nil).Capabilities)) } // Flush mocks base method. func (m *MockStatsReporter) Flush() { m.ctrl.T.Helper() m.ctrl.Call(m, "Flush") } // Flush indicates an expected call of Flush. func (mr *MockStatsReporterMockRecorder) Flush() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Flush", reflect.TypeOf((*MockStatsReporter)(nil).Flush)) } // ReportCounter mocks base method. func (m *MockStatsReporter) ReportCounter(arg0 string, arg1 map[string]string, arg2 int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReportCounter", arg0, arg1, arg2) } // ReportCounter indicates an expected call of ReportCounter. func (mr *MockStatsReporterMockRecorder) ReportCounter(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportCounter", reflect.TypeOf((*MockStatsReporter)(nil).ReportCounter), arg0, arg1, arg2) } // ReportGauge mocks base method. func (m *MockStatsReporter) ReportGauge(arg0 string, arg1 map[string]string, arg2 float64) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReportGauge", arg0, arg1, arg2) } // ReportGauge indicates an expected call of ReportGauge. func (mr *MockStatsReporterMockRecorder) ReportGauge(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportGauge", reflect.TypeOf((*MockStatsReporter)(nil).ReportGauge), arg0, arg1, arg2) } // ReportHistogramDurationSamples mocks base method. func (m *MockStatsReporter) ReportHistogramDurationSamples(arg0 string, arg1 map[string]string, arg2 tally.Buckets, arg3, arg4 time.Duration, arg5 int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReportHistogramDurationSamples", arg0, arg1, arg2, arg3, arg4, arg5) } // ReportHistogramDurationSamples indicates an expected call of ReportHistogramDurationSamples. func (mr *MockStatsReporterMockRecorder) ReportHistogramDurationSamples(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportHistogramDurationSamples", reflect.TypeOf((*MockStatsReporter)(nil).ReportHistogramDurationSamples), arg0, arg1, arg2, arg3, arg4, arg5) } // ReportHistogramValueSamples mocks base method. func (m *MockStatsReporter) ReportHistogramValueSamples(arg0 string, arg1 map[string]string, arg2 tally.Buckets, arg3, arg4 float64, arg5 int64) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReportHistogramValueSamples", arg0, arg1, arg2, arg3, arg4, arg5) } // ReportHistogramValueSamples indicates an expected call of ReportHistogramValueSamples. func (mr *MockStatsReporterMockRecorder) ReportHistogramValueSamples(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportHistogramValueSamples", reflect.TypeOf((*MockStatsReporter)(nil).ReportHistogramValueSamples), arg0, arg1, arg2, arg3, arg4, arg5) } // ReportTimer mocks base method. func (m *MockStatsReporter) ReportTimer(arg0 string, arg1 map[string]string, arg2 time.Duration) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReportTimer", arg0, arg1, arg2) } // ReportTimer indicates an expected call of ReportTimer. func (mr *MockStatsReporterMockRecorder) ReportTimer(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReportTimer", reflect.TypeOf((*MockStatsReporter)(nil).ReportTimer), arg0, arg1, arg2) } golang-github-uber-go-tally-4.1.11/thirdparty/000077500000000000000000000000001456146544700212205ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/000077500000000000000000000000001456146544700232575ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/000077500000000000000000000000001456146544700245005ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/000077500000000000000000000000001456146544700260005ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/000077500000000000000000000000001456146544700265465ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/000077500000000000000000000000001456146544700271535ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/000077500000000000000000000000001456146544700304535ustar00rootroot00000000000000application_exception.go000066400000000000000000000064241456146544700353120ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift const ( UNKNOWN_APPLICATION_EXCEPTION = 0 UNKNOWN_METHOD = 1 INVALID_MESSAGE_TYPE_EXCEPTION = 2 WRONG_METHOD_NAME = 3 BAD_SEQUENCE_ID = 4 MISSING_RESULT = 5 INTERNAL_ERROR = 6 PROTOCOL_ERROR = 7 ) // Application level Thrift exception type TApplicationException interface { TException TypeId() int32 Read(iprot TProtocol) (TApplicationException, error) Write(oprot TProtocol) error } type tApplicationException struct { message string type_ int32 } func (e tApplicationException) Error() string { return e.message } func NewTApplicationException(type_ int32, message string) TApplicationException { return &tApplicationException{message, type_} } func (p *tApplicationException) TypeId() int32 { return p.type_ } func (p *tApplicationException) Read(iprot TProtocol) (TApplicationException, error) { _, err := iprot.ReadStructBegin() if err != nil { return nil, err } message := "" type_ := int32(UNKNOWN_APPLICATION_EXCEPTION) for { _, ttype, id, err := iprot.ReadFieldBegin() if err != nil { return nil, err } if ttype == STOP { break } switch id { case 1: if ttype == STRING { if message, err = iprot.ReadString(); err != nil { return nil, err } } else { if err = SkipDefaultDepth(iprot, ttype); err != nil { return nil, err } } case 2: if ttype == I32 { if type_, err = iprot.ReadI32(); err != nil { return nil, err } } else { if err = SkipDefaultDepth(iprot, ttype); err != nil { return nil, err } } default: if err = SkipDefaultDepth(iprot, ttype); err != nil { return nil, err } } if err = iprot.ReadFieldEnd(); err != nil { return nil, err } } return NewTApplicationException(type_, message), iprot.ReadStructEnd() } func (p *tApplicationException) Write(oprot TProtocol) (err error) { err = oprot.WriteStructBegin("TApplicationException") if len(p.Error()) > 0 { err = oprot.WriteFieldBegin("message", STRING, 1) if err != nil { return } err = oprot.WriteString(p.Error()) if err != nil { return } err = oprot.WriteFieldEnd() if err != nil { return } } err = oprot.WriteFieldBegin("type", I32, 2) if err != nil { return } err = oprot.WriteI32(p.type_) if err != nil { return } err = oprot.WriteFieldEnd() if err != nil { return } err = oprot.WriteFieldStop() if err != nil { return } err = oprot.WriteStructEnd() return } application_exception_test.go000066400000000000000000000027531456146544700363520ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "testing" ) func TestTApplicationException(t *testing.T) { exc := NewTApplicationException(UNKNOWN_APPLICATION_EXCEPTION, "") if exc.Error() != "" { t.Fatalf("Expected empty string for exception but found '%s'", exc.Error()) } if exc.TypeId() != UNKNOWN_APPLICATION_EXCEPTION { t.Fatalf("Expected type UNKNOWN for exception but found '%v'", exc.TypeId()) } exc = NewTApplicationException(WRONG_METHOD_NAME, "junk_method") if exc.Error() != "junk_method" { t.Fatalf("Expected 'junk_method' for exception but found '%s'", exc.Error()) } if exc.TypeId() != WRONG_METHOD_NAME { t.Fatalf("Expected type WRONG_METHOD_NAME for exception but found '%v'", exc.TypeId()) } } binary_protocol.go000066400000000000000000000255641456146544700341440ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bytes" "encoding/binary" "errors" "fmt" "io" "math" ) type TBinaryProtocol struct { trans TRichTransport origTransport TTransport reader io.Reader writer io.Writer strictRead bool strictWrite bool buffer [64]byte } type TBinaryProtocolFactory struct { strictRead bool strictWrite bool } func NewTBinaryProtocolTransport(t TTransport) *TBinaryProtocol { return NewTBinaryProtocol(t, false, true) } func NewTBinaryProtocol(t TTransport, strictRead, strictWrite bool) *TBinaryProtocol { p := &TBinaryProtocol{origTransport: t, strictRead: strictRead, strictWrite: strictWrite} if et, ok := t.(TRichTransport); ok { p.trans = et } else { p.trans = NewTRichTransport(t) } p.reader = p.trans p.writer = p.trans return p } func NewTBinaryProtocolFactoryDefault() *TBinaryProtocolFactory { return NewTBinaryProtocolFactory(false, true) } func NewTBinaryProtocolFactory(strictRead, strictWrite bool) *TBinaryProtocolFactory { return &TBinaryProtocolFactory{strictRead: strictRead, strictWrite: strictWrite} } func (p *TBinaryProtocolFactory) GetProtocol(t TTransport) TProtocol { return NewTBinaryProtocol(t, p.strictRead, p.strictWrite) } /** * Writing Methods */ func (p *TBinaryProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error { if p.strictWrite { version := uint32(VERSION_1) | uint32(typeId) e := p.WriteI32(int32(version)) if e != nil { return e } e = p.WriteString(name) if e != nil { return e } e = p.WriteI32(seqId) return e } else { e := p.WriteString(name) if e != nil { return e } e = p.WriteByte(int8(typeId)) if e != nil { return e } e = p.WriteI32(seqId) return e } return nil } func (p *TBinaryProtocol) WriteMessageEnd() error { return nil } func (p *TBinaryProtocol) WriteStructBegin(name string) error { return nil } func (p *TBinaryProtocol) WriteStructEnd() error { return nil } func (p *TBinaryProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { e := p.WriteByte(int8(typeId)) if e != nil { return e } e = p.WriteI16(id) return e } func (p *TBinaryProtocol) WriteFieldEnd() error { return nil } func (p *TBinaryProtocol) WriteFieldStop() error { e := p.WriteByte(STOP) return e } func (p *TBinaryProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { e := p.WriteByte(int8(keyType)) if e != nil { return e } e = p.WriteByte(int8(valueType)) if e != nil { return e } e = p.WriteI32(int32(size)) return e } func (p *TBinaryProtocol) WriteMapEnd() error { return nil } func (p *TBinaryProtocol) WriteListBegin(elemType TType, size int) error { e := p.WriteByte(int8(elemType)) if e != nil { return e } e = p.WriteI32(int32(size)) return e } func (p *TBinaryProtocol) WriteListEnd() error { return nil } func (p *TBinaryProtocol) WriteSetBegin(elemType TType, size int) error { e := p.WriteByte(int8(elemType)) if e != nil { return e } e = p.WriteI32(int32(size)) return e } func (p *TBinaryProtocol) WriteSetEnd() error { return nil } func (p *TBinaryProtocol) WriteBool(value bool) error { if value { return p.WriteByte(1) } return p.WriteByte(0) } func (p *TBinaryProtocol) WriteByte(value int8) error { e := p.trans.WriteByte(byte(value)) return NewTProtocolException(e) } func (p *TBinaryProtocol) WriteI16(value int16) error { v := p.buffer[0:2] binary.BigEndian.PutUint16(v, uint16(value)) _, e := p.writer.Write(v) return NewTProtocolException(e) } func (p *TBinaryProtocol) WriteI32(value int32) error { v := p.buffer[0:4] binary.BigEndian.PutUint32(v, uint32(value)) _, e := p.writer.Write(v) return NewTProtocolException(e) } func (p *TBinaryProtocol) WriteI64(value int64) error { v := p.buffer[0:8] binary.BigEndian.PutUint64(v, uint64(value)) _, err := p.writer.Write(v) return NewTProtocolException(err) } func (p *TBinaryProtocol) WriteDouble(value float64) error { return p.WriteI64(int64(math.Float64bits(value))) } func (p *TBinaryProtocol) WriteString(value string) error { e := p.WriteI32(int32(len(value))) if e != nil { return e } _, err := p.trans.WriteString(value) return NewTProtocolException(err) } func (p *TBinaryProtocol) WriteBinary(value []byte) error { e := p.WriteI32(int32(len(value))) if e != nil { return e } _, err := p.writer.Write(value) return NewTProtocolException(err) } /** * Reading methods */ func (p *TBinaryProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { size, e := p.ReadI32() if e != nil { return "", typeId, 0, NewTProtocolException(e) } if size < 0 { typeId = TMessageType(size & 0x0ff) version := int64(int64(size) & VERSION_MASK) if version != VERSION_1 { return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Bad version in ReadMessageBegin")) } name, e = p.ReadString() if e != nil { return name, typeId, seqId, NewTProtocolException(e) } seqId, e = p.ReadI32() if e != nil { return name, typeId, seqId, NewTProtocolException(e) } return name, typeId, seqId, nil } if p.strictRead { return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Missing version in ReadMessageBegin")) } name, e2 := p.readStringBody(size) if e2 != nil { return name, typeId, seqId, e2 } b, e3 := p.ReadByte() if e3 != nil { return name, typeId, seqId, e3 } typeId = TMessageType(b) seqId, e4 := p.ReadI32() if e4 != nil { return name, typeId, seqId, e4 } return name, typeId, seqId, nil } func (p *TBinaryProtocol) ReadMessageEnd() error { return nil } func (p *TBinaryProtocol) ReadStructBegin() (name string, err error) { return } func (p *TBinaryProtocol) ReadStructEnd() error { return nil } func (p *TBinaryProtocol) ReadFieldBegin() (name string, typeId TType, seqId int16, err error) { t, err := p.ReadByte() typeId = TType(t) if err != nil { return name, typeId, seqId, err } if t != STOP { seqId, err = p.ReadI16() } return name, typeId, seqId, err } func (p *TBinaryProtocol) ReadFieldEnd() error { return nil } var invalidDataLength = NewTProtocolExceptionWithType(INVALID_DATA, errors.New("Invalid data length")) func (p *TBinaryProtocol) ReadMapBegin() (kType, vType TType, size int, err error) { k, e := p.ReadByte() if e != nil { err = NewTProtocolException(e) return } kType = TType(k) v, e := p.ReadByte() if e != nil { err = NewTProtocolException(e) return } vType = TType(v) size32, e := p.ReadI32() if e != nil { err = NewTProtocolException(e) return } if size32 < 0 { err = invalidDataLength return } size = int(size32) return kType, vType, size, nil } func (p *TBinaryProtocol) ReadMapEnd() error { return nil } func (p *TBinaryProtocol) ReadListBegin() (elemType TType, size int, err error) { b, e := p.ReadByte() if e != nil { err = NewTProtocolException(e) return } elemType = TType(b) size32, e := p.ReadI32() if e != nil { err = NewTProtocolException(e) return } if size32 < 0 { err = invalidDataLength return } size = int(size32) return } func (p *TBinaryProtocol) ReadListEnd() error { return nil } func (p *TBinaryProtocol) ReadSetBegin() (elemType TType, size int, err error) { b, e := p.ReadByte() if e != nil { err = NewTProtocolException(e) return } elemType = TType(b) size32, e := p.ReadI32() if e != nil { err = NewTProtocolException(e) return } if size32 < 0 { err = invalidDataLength return } size = int(size32) return elemType, size, nil } func (p *TBinaryProtocol) ReadSetEnd() error { return nil } func (p *TBinaryProtocol) ReadBool() (bool, error) { b, e := p.ReadByte() v := true if b != 1 { v = false } return v, e } func (p *TBinaryProtocol) ReadByte() (int8, error) { v, err := p.trans.ReadByte() return int8(v), err } func (p *TBinaryProtocol) ReadI16() (value int16, err error) { buf := p.buffer[0:2] err = p.readAll(buf) value = int16(binary.BigEndian.Uint16(buf)) return value, err } func (p *TBinaryProtocol) ReadI32() (value int32, err error) { buf := p.buffer[0:4] err = p.readAll(buf) value = int32(binary.BigEndian.Uint32(buf)) return value, err } func (p *TBinaryProtocol) ReadI64() (value int64, err error) { buf := p.buffer[0:8] err = p.readAll(buf) value = int64(binary.BigEndian.Uint64(buf)) return value, err } func (p *TBinaryProtocol) ReadDouble() (value float64, err error) { buf := p.buffer[0:8] err = p.readAll(buf) value = math.Float64frombits(binary.BigEndian.Uint64(buf)) return value, err } func (p *TBinaryProtocol) ReadString() (value string, err error) { size, e := p.ReadI32() if e != nil { return "", e } if size < 0 { err = invalidDataLength return } return p.readStringBody(size) } func (p *TBinaryProtocol) ReadBinary() ([]byte, error) { size, e := p.ReadI32() if e != nil { return nil, e } if size < 0 { return nil, invalidDataLength } if uint64(size) > p.trans.RemainingBytes() { return nil, invalidDataLength } isize := int(size) buf := make([]byte, isize) _, err := io.ReadFull(p.trans, buf) return buf, NewTProtocolException(err) } func (p *TBinaryProtocol) Flush() (err error) { return NewTProtocolException(p.trans.Flush()) } func (p *TBinaryProtocol) Skip(fieldType TType) (err error) { return SkipDefaultDepth(p, fieldType) } func (p *TBinaryProtocol) Transport() TTransport { return p.origTransport } func (p *TBinaryProtocol) readAll(buf []byte) error { _, err := io.ReadFull(p.reader, buf) return NewTProtocolException(err) } const readLimit = 32768 func (p *TBinaryProtocol) readStringBody(size int32) (value string, err error) { if size < 0 { return "", nil } if uint64(size) > p.trans.RemainingBytes() { return "", invalidDataLength } var ( buf bytes.Buffer e error b []byte ) switch { case int(size) <= len(p.buffer): b = p.buffer[:size] // avoids allocation for small reads case int(size) < readLimit: b = make([]byte, size) default: b = make([]byte, readLimit) } for size > 0 { _, e = io.ReadFull(p.trans, b) buf.Write(b) if e != nil { break } size -= readLimit if size < readLimit && size > 0 { b = b[:size] } } return buf.String(), NewTProtocolException(e) } binary_protocol_test.go000066400000000000000000000016751456146544700352000ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "testing" ) func TestReadWriteBinaryProtocol(t *testing.T) { ReadWriteProtocolTest(t, NewTBinaryProtocolFactoryDefault()) } buffered_transport.go000066400000000000000000000044031456146544700346220ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bufio" ) type TBufferedTransportFactory struct { size int } type TBufferedTransport struct { bufio.ReadWriter tp TTransport } func (p *TBufferedTransportFactory) GetTransport(trans TTransport) TTransport { return NewTBufferedTransport(trans, p.size) } func NewTBufferedTransportFactory(bufferSize int) *TBufferedTransportFactory { return &TBufferedTransportFactory{size: bufferSize} } func NewTBufferedTransport(trans TTransport, bufferSize int) *TBufferedTransport { return &TBufferedTransport{ ReadWriter: bufio.ReadWriter{ Reader: bufio.NewReaderSize(trans, bufferSize), Writer: bufio.NewWriterSize(trans, bufferSize), }, tp: trans, } } func (p *TBufferedTransport) IsOpen() bool { return p.tp.IsOpen() } func (p *TBufferedTransport) Open() (err error) { return p.tp.Open() } func (p *TBufferedTransport) Close() (err error) { return p.tp.Close() } func (p *TBufferedTransport) Read(b []byte) (int, error) { n, err := p.ReadWriter.Read(b) if err != nil { p.ReadWriter.Reader.Reset(p.tp) } return n, err } func (p *TBufferedTransport) Write(b []byte) (int, error) { n, err := p.ReadWriter.Write(b) if err != nil { p.ReadWriter.Writer.Reset(p.tp) } return n, err } func (p *TBufferedTransport) Flush() error { if err := p.ReadWriter.Flush(); err != nil { p.ReadWriter.Writer.Reset(p.tp) return err } return p.tp.Flush() } func (p *TBufferedTransport) RemainingBytes() (num_bytes uint64) { return p.tp.RemainingBytes() } buffered_transport_test.go000066400000000000000000000017241456146544700356640ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "testing" ) func TestBufferedTransport(t *testing.T) { trans := NewTBufferedTransport(NewTMemoryBuffer(), 10240) TransportTest(t, trans, trans) } compact_protocol.go000066400000000000000000000537531456146544700343070ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "encoding/binary" "fmt" "io" "math" ) const ( COMPACT_PROTOCOL_ID = 0x082 COMPACT_VERSION = 1 COMPACT_VERSION_MASK = 0x1f COMPACT_TYPE_MASK = 0x0E0 COMPACT_TYPE_BITS = 0x07 COMPACT_TYPE_SHIFT_AMOUNT = 5 ) type tCompactType byte const ( COMPACT_BOOLEAN_TRUE = 0x01 COMPACT_BOOLEAN_FALSE = 0x02 COMPACT_BYTE = 0x03 COMPACT_I16 = 0x04 COMPACT_I32 = 0x05 COMPACT_I64 = 0x06 COMPACT_DOUBLE = 0x07 COMPACT_BINARY = 0x08 COMPACT_LIST = 0x09 COMPACT_SET = 0x0A COMPACT_MAP = 0x0B COMPACT_STRUCT = 0x0C ) var ( ttypeToCompactType map[TType]tCompactType ) func init() { ttypeToCompactType = map[TType]tCompactType{ STOP: STOP, BOOL: COMPACT_BOOLEAN_TRUE, BYTE: COMPACT_BYTE, I16: COMPACT_I16, I32: COMPACT_I32, I64: COMPACT_I64, DOUBLE: COMPACT_DOUBLE, STRING: COMPACT_BINARY, LIST: COMPACT_LIST, SET: COMPACT_SET, MAP: COMPACT_MAP, STRUCT: COMPACT_STRUCT, } } type TCompactProtocolFactory struct{} func NewTCompactProtocolFactory() *TCompactProtocolFactory { return &TCompactProtocolFactory{} } func (p *TCompactProtocolFactory) GetProtocol(trans TTransport) TProtocol { return NewTCompactProtocol(trans) } type TCompactProtocol struct { trans TRichTransport origTransport TTransport // Used to keep track of the last field for the current and previous structs, // so we can do the delta stuff. lastField []int lastFieldId int // If we encounter a boolean field begin, save the TField here so it can // have the value incorporated. booleanFieldName string booleanFieldId int16 booleanFieldPending bool // If we read a field header, and it's a boolean field, save the boolean // value here so that readBool can use it. boolValue bool boolValueIsNotNull bool buffer [64]byte } // Create a TCompactProtocol given a TTransport func NewTCompactProtocol(trans TTransport) *TCompactProtocol { p := &TCompactProtocol{origTransport: trans, lastField: []int{}} if et, ok := trans.(TRichTransport); ok { p.trans = et } else { p.trans = NewTRichTransport(trans) } return p } // // Public Writing methods. // // Write a message header to the wire. Compact Protocol messages contain the // protocol version so we can migrate forwards in the future if need be. func (p *TCompactProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error { err := p.writeByteDirect(COMPACT_PROTOCOL_ID) if err != nil { return NewTProtocolException(err) } err = p.writeByteDirect((COMPACT_VERSION & COMPACT_VERSION_MASK) | ((byte(typeId) << COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_MASK)) if err != nil { return NewTProtocolException(err) } _, err = p.writeVarint32(seqid) if err != nil { return NewTProtocolException(err) } e := p.WriteString(name) return e } func (p *TCompactProtocol) WriteMessageEnd() error { return nil } // Write a struct begin. This doesn't actually put anything on the wire. We // use it as an opportunity to put special placeholder markers on the field // stack so we can get the field id deltas correct. func (p *TCompactProtocol) WriteStructBegin(name string) error { p.lastField = append(p.lastField, p.lastFieldId) p.lastFieldId = 0 return nil } // Write a struct end. This doesn't actually put anything on the wire. We use // this as an opportunity to pop the last field from the current struct off // of the field stack. func (p *TCompactProtocol) WriteStructEnd() error { p.lastFieldId = p.lastField[len(p.lastField)-1] p.lastField = p.lastField[:len(p.lastField)-1] return nil } func (p *TCompactProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { if typeId == BOOL { // we want to possibly include the value, so we'll wait. p.booleanFieldName, p.booleanFieldId, p.booleanFieldPending = name, id, true return nil } _, err := p.writeFieldBeginInternal(name, typeId, id, 0xFF) return NewTProtocolException(err) } // The workhorse of writeFieldBegin. It has the option of doing a // 'type override' of the type header. This is used specifically in the // boolean field case. func (p *TCompactProtocol) writeFieldBeginInternal(name string, typeId TType, id int16, typeOverride byte) (int, error) { // short lastField = lastField_.pop(); // if there's a type override, use that. var typeToWrite byte if typeOverride == 0xFF { typeToWrite = byte(p.getCompactType(typeId)) } else { typeToWrite = typeOverride } // check if we can use delta encoding for the field id fieldId := int(id) written := 0 if fieldId > p.lastFieldId && fieldId-p.lastFieldId <= 15 { // write them together err := p.writeByteDirect(byte((fieldId-p.lastFieldId)<<4) | typeToWrite) if err != nil { return 0, err } } else { // write them separate err := p.writeByteDirect(typeToWrite) if err != nil { return 0, err } err = p.WriteI16(id) written = 1 + 2 if err != nil { return 0, err } } p.lastFieldId = fieldId // p.lastField.Push(field.id); return written, nil } func (p *TCompactProtocol) WriteFieldEnd() error { return nil } func (p *TCompactProtocol) WriteFieldStop() error { err := p.writeByteDirect(STOP) return NewTProtocolException(err) } func (p *TCompactProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { if size == 0 { err := p.writeByteDirect(0) return NewTProtocolException(err) } _, err := p.writeVarint32(int32(size)) if err != nil { return NewTProtocolException(err) } err = p.writeByteDirect(byte(p.getCompactType(keyType))<<4 | byte(p.getCompactType(valueType))) return NewTProtocolException(err) } func (p *TCompactProtocol) WriteMapEnd() error { return nil } // Write a list header. func (p *TCompactProtocol) WriteListBegin(elemType TType, size int) error { _, err := p.writeCollectionBegin(elemType, size) return NewTProtocolException(err) } func (p *TCompactProtocol) WriteListEnd() error { return nil } // Write a set header. func (p *TCompactProtocol) WriteSetBegin(elemType TType, size int) error { _, err := p.writeCollectionBegin(elemType, size) return NewTProtocolException(err) } func (p *TCompactProtocol) WriteSetEnd() error { return nil } func (p *TCompactProtocol) WriteBool(value bool) error { v := byte(COMPACT_BOOLEAN_FALSE) if value { v = byte(COMPACT_BOOLEAN_TRUE) } if p.booleanFieldPending { // we haven't written the field header yet _, err := p.writeFieldBeginInternal(p.booleanFieldName, BOOL, p.booleanFieldId, v) p.booleanFieldPending = false return NewTProtocolException(err) } // we're not part of a field, so just write the value. err := p.writeByteDirect(v) return NewTProtocolException(err) } // Write a byte. Nothing to see here! func (p *TCompactProtocol) WriteByte(value int8) error { err := p.writeByteDirect(byte(value)) return NewTProtocolException(err) } // Write an I16 as a zigzag varint. func (p *TCompactProtocol) WriteI16(value int16) error { _, err := p.writeVarint32(p.int32ToZigzag(int32(value))) return NewTProtocolException(err) } // Write an i32 as a zigzag varint. func (p *TCompactProtocol) WriteI32(value int32) error { _, err := p.writeVarint32(p.int32ToZigzag(value)) return NewTProtocolException(err) } // Write an i64 as a zigzag varint. func (p *TCompactProtocol) WriteI64(value int64) error { _, err := p.writeVarint64(p.int64ToZigzag(value)) return NewTProtocolException(err) } // Write a double to the wire as 8 bytes. func (p *TCompactProtocol) WriteDouble(value float64) error { buf := p.buffer[0:8] binary.LittleEndian.PutUint64(buf, math.Float64bits(value)) _, err := p.trans.Write(buf) return NewTProtocolException(err) } // Write a string to the wire with a varint size preceding. func (p *TCompactProtocol) WriteString(value string) error { _, e := p.writeVarint32(int32(len(value))) if e != nil { return NewTProtocolException(e) } if len(value) > 0 { } _, e = p.trans.WriteString(value) return e } // Write a byte array, using a varint for the size. func (p *TCompactProtocol) WriteBinary(bin []byte) error { _, e := p.writeVarint32(int32(len(bin))) if e != nil { return NewTProtocolException(e) } if len(bin) > 0 { _, e = p.trans.Write(bin) return NewTProtocolException(e) } return nil } // // Reading methods. // // Read a message header. func (p *TCompactProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { protocolId, err := p.readByteDirect() if err != nil { return } if protocolId != COMPACT_PROTOCOL_ID { e := fmt.Errorf("Expected protocol id %02x but got %02x", COMPACT_PROTOCOL_ID, protocolId) return "", typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, e) } versionAndType, err := p.readByteDirect() if err != nil { return } version := versionAndType & COMPACT_VERSION_MASK typeId = TMessageType((versionAndType >> COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_BITS) if version != COMPACT_VERSION { e := fmt.Errorf("Expected version %02x but got %02x", COMPACT_VERSION, version) err = NewTProtocolExceptionWithType(BAD_VERSION, e) return } seqId, e := p.readVarint32() if e != nil { err = NewTProtocolException(e) return } name, err = p.ReadString() return } func (p *TCompactProtocol) ReadMessageEnd() error { return nil } // Read a struct begin. There's nothing on the wire for this, but it is our // opportunity to push a new struct begin marker onto the field stack. func (p *TCompactProtocol) ReadStructBegin() (name string, err error) { p.lastField = append(p.lastField, p.lastFieldId) p.lastFieldId = 0 return } // Doesn't actually consume any wire data, just removes the last field for // this struct from the field stack. func (p *TCompactProtocol) ReadStructEnd() error { // consume the last field we read off the wire. p.lastFieldId = p.lastField[len(p.lastField)-1] p.lastField = p.lastField[:len(p.lastField)-1] return nil } // Read a field header off the wire. func (p *TCompactProtocol) ReadFieldBegin() (name string, typeId TType, id int16, err error) { t, err := p.readByteDirect() if err != nil { return } // if it's a stop, then we can return immediately, as the struct is over. if (t & 0x0f) == STOP { return "", STOP, 0, nil } // mask off the 4 MSB of the type header. it could contain a field id delta. modifier := int16((t & 0xf0) >> 4) if modifier == 0 { // not a delta. look ahead for the zigzag varint field id. id, err = p.ReadI16() if err != nil { return } } else { // has a delta. add the delta to the last read field id. id = int16(p.lastFieldId) + modifier } typeId, e := p.getTType(tCompactType(t & 0x0f)) if e != nil { err = NewTProtocolException(e) return } // if this happens to be a boolean field, the value is encoded in the type if p.isBoolType(t) { // save the boolean value in a special instance variable. p.boolValue = (byte(t)&0x0f == COMPACT_BOOLEAN_TRUE) p.boolValueIsNotNull = true } // push the new field onto the field stack so we can keep the deltas going. p.lastFieldId = int(id) return } func (p *TCompactProtocol) ReadFieldEnd() error { return nil } // Read a map header off the wire. If the size is zero, skip reading the key // and value type. This means that 0-length maps will yield TMaps without the // "correct" types. func (p *TCompactProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, err error) { size32, e := p.readVarint32() if e != nil { err = NewTProtocolException(e) return } if size32 < 0 { err = invalidDataLength return } size = int(size32) keyAndValueType := byte(STOP) if size != 0 { keyAndValueType, err = p.readByteDirect() if err != nil { return } } keyType, _ = p.getTType(tCompactType(keyAndValueType >> 4)) valueType, _ = p.getTType(tCompactType(keyAndValueType & 0xf)) return } func (p *TCompactProtocol) ReadMapEnd() error { return nil } // Read a list header off the wire. If the list size is 0-14, the size will // be packed into the element type header. If it's a longer list, the 4 MSB // of the element type header will be 0xF, and a varint will follow with the // true size. func (p *TCompactProtocol) ReadListBegin() (elemType TType, size int, err error) { size_and_type, err := p.readByteDirect() if err != nil { return } size = int((size_and_type >> 4) & 0x0f) if size == 15 { size2, e := p.readVarint32() if e != nil { err = NewTProtocolException(e) return } if size2 < 0 { err = invalidDataLength return } size = int(size2) } elemType, e := p.getTType(tCompactType(size_and_type)) if e != nil { err = NewTProtocolException(e) return } return } func (p *TCompactProtocol) ReadListEnd() error { return nil } // Read a set header off the wire. If the set size is 0-14, the size will // be packed into the element type header. If it's a longer set, the 4 MSB // of the element type header will be 0xF, and a varint will follow with the // true size. func (p *TCompactProtocol) ReadSetBegin() (elemType TType, size int, err error) { return p.ReadListBegin() } func (p *TCompactProtocol) ReadSetEnd() error { return nil } // Read a boolean off the wire. If this is a boolean field, the value should // already have been read during readFieldBegin, so we'll just consume the // pre-stored value. Otherwise, read a byte. func (p *TCompactProtocol) ReadBool() (value bool, err error) { if p.boolValueIsNotNull { p.boolValueIsNotNull = false return p.boolValue, nil } v, err := p.readByteDirect() return v == COMPACT_BOOLEAN_TRUE, err } // Read a single byte off the wire. Nothing interesting here. func (p *TCompactProtocol) ReadByte() (int8, error) { v, err := p.readByteDirect() if err != nil { return 0, NewTProtocolException(err) } return int8(v), err } // Read an i16 from the wire as a zigzag varint. func (p *TCompactProtocol) ReadI16() (value int16, err error) { v, err := p.ReadI32() return int16(v), err } // Read an i32 from the wire as a zigzag varint. func (p *TCompactProtocol) ReadI32() (value int32, err error) { v, e := p.readVarint32() if e != nil { return 0, NewTProtocolException(e) } value = p.zigzagToInt32(v) return value, nil } // Read an i64 from the wire as a zigzag varint. func (p *TCompactProtocol) ReadI64() (value int64, err error) { v, e := p.readVarint64() if e != nil { return 0, NewTProtocolException(e) } value = p.zigzagToInt64(v) return value, nil } // No magic here - just read a double off the wire. func (p *TCompactProtocol) ReadDouble() (value float64, err error) { longBits := p.buffer[0:8] _, e := io.ReadFull(p.trans, longBits) if e != nil { return 0.0, NewTProtocolException(e) } return math.Float64frombits(p.bytesToUint64(longBits)), nil } // Reads a []byte (via readBinary), and then UTF-8 decodes it. func (p *TCompactProtocol) ReadString() (value string, err error) { length, e := p.readVarint32() if e != nil { return "", NewTProtocolException(e) } if length < 0 { return "", invalidDataLength } if uint64(length) > p.trans.RemainingBytes() { return "", invalidDataLength } if length == 0 { return "", nil } var buf []byte if length <= int32(len(p.buffer)) { buf = p.buffer[0:length] } else { buf = make([]byte, length) } _, e = io.ReadFull(p.trans, buf) return string(buf), NewTProtocolException(e) } // Read a []byte from the wire. func (p *TCompactProtocol) ReadBinary() (value []byte, err error) { length, e := p.readVarint32() if e != nil { return nil, NewTProtocolException(e) } if length == 0 { return []byte{}, nil } if length < 0 { return nil, invalidDataLength } if uint64(length) > p.trans.RemainingBytes() { return nil, invalidDataLength } buf := make([]byte, length) _, e = io.ReadFull(p.trans, buf) return buf, NewTProtocolException(e) } func (p *TCompactProtocol) Flush() (err error) { return NewTProtocolException(p.trans.Flush()) } func (p *TCompactProtocol) Skip(fieldType TType) (err error) { return SkipDefaultDepth(p, fieldType) } func (p *TCompactProtocol) Transport() TTransport { return p.origTransport } // // Internal writing methods // // Abstract method for writing the start of lists and sets. List and sets on // the wire differ only by the type indicator. func (p *TCompactProtocol) writeCollectionBegin(elemType TType, size int) (int, error) { if size <= 14 { return 1, p.writeByteDirect(byte(int32(size<<4) | int32(p.getCompactType(elemType)))) } err := p.writeByteDirect(0xf0 | byte(p.getCompactType(elemType))) if err != nil { return 0, err } m, err := p.writeVarint32(int32(size)) return 1 + m, err } // Write an i32 as a varint. Results in 1-5 bytes on the wire. // TODO(pomack): make a permanent buffer like writeVarint64? func (p *TCompactProtocol) writeVarint32(n int32) (int, error) { i32buf := p.buffer[0:5] idx := 0 for { if (n & ^0x7F) == 0 { i32buf[idx] = byte(n) idx++ // p.writeByteDirect(byte(n)); break // return; } else { i32buf[idx] = byte((n & 0x7F) | 0x80) idx++ // p.writeByteDirect(byte(((n & 0x7F) | 0x80))); u := uint32(n) n = int32(u >> 7) } } return p.trans.Write(i32buf[0:idx]) } // Write an i64 as a varint. Results in 1-10 bytes on the wire. func (p *TCompactProtocol) writeVarint64(n int64) (int, error) { varint64out := p.buffer[0:10] idx := 0 for { if (n & ^0x7F) == 0 { varint64out[idx] = byte(n) idx++ break } else { varint64out[idx] = byte((n & 0x7F) | 0x80) idx++ u := uint64(n) n = int64(u >> 7) } } return p.trans.Write(varint64out[0:idx]) } // Convert l into a zigzag long. This allows negative numbers to be // represented compactly as a varint. func (p *TCompactProtocol) int64ToZigzag(l int64) int64 { return (l << 1) ^ (l >> 63) } // Convert l into a zigzag long. This allows negative numbers to be // represented compactly as a varint. func (p *TCompactProtocol) int32ToZigzag(n int32) int32 { return (n << 1) ^ (n >> 31) } func (p *TCompactProtocol) fixedUint64ToBytes(n uint64, buf []byte) { binary.LittleEndian.PutUint64(buf, n) } func (p *TCompactProtocol) fixedInt64ToBytes(n int64, buf []byte) { binary.LittleEndian.PutUint64(buf, uint64(n)) } // Writes a byte without any possibility of all that field header nonsense. // Used internally by other writing methods that know they need to write a byte. func (p *TCompactProtocol) writeByteDirect(b byte) error { return p.trans.WriteByte(b) } // Writes a byte without any possibility of all that field header nonsense. func (p *TCompactProtocol) writeIntAsByteDirect(n int) (int, error) { return 1, p.writeByteDirect(byte(n)) } // // Internal reading methods // // Read an i32 from the wire as a varint. The MSB of each byte is set // if there is another byte to follow. This can read up to 5 bytes. func (p *TCompactProtocol) readVarint32() (int32, error) { // if the wire contains the right stuff, this will just truncate the i64 we // read and get us the right sign. v, err := p.readVarint64() return int32(v), err } // Read an i64 from the wire as a proper varint. The MSB of each byte is set // if there is another byte to follow. This can read up to 10 bytes. func (p *TCompactProtocol) readVarint64() (int64, error) { shift := uint(0) result := int64(0) for { b, err := p.readByteDirect() if err != nil { return 0, err } result |= int64(b&0x7f) << shift if (b & 0x80) != 0x80 { break } shift += 7 } return result, nil } // Read a byte, unlike ReadByte that reads Thrift-byte that is i8. func (p *TCompactProtocol) readByteDirect() (byte, error) { return p.trans.ReadByte() } // // encoding helpers // // Convert from zigzag int to int. func (p *TCompactProtocol) zigzagToInt32(n int32) int32 { u := uint32(n) return int32(u>>1) ^ -(n & 1) } // Convert from zigzag long to long. func (p *TCompactProtocol) zigzagToInt64(n int64) int64 { u := uint64(n) return int64(u>>1) ^ -(n & 1) } // Note that it's important that the mask bytes are long literals, // otherwise they'll default to ints, and when you shift an int left 56 bits, // you just get a messed up int. func (p *TCompactProtocol) bytesToInt64(b []byte) int64 { return int64(binary.LittleEndian.Uint64(b)) } // Note that it's important that the mask bytes are long literals, // otherwise they'll default to ints, and when you shift an int left 56 bits, // you just get a messed up int. func (p *TCompactProtocol) bytesToUint64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) } // // type testing and converting // func (p *TCompactProtocol) isBoolType(b byte) bool { return (b&0x0f) == COMPACT_BOOLEAN_TRUE || (b&0x0f) == COMPACT_BOOLEAN_FALSE } // Given a tCompactType constant, convert it to its corresponding // TType value. func (p *TCompactProtocol) getTType(t tCompactType) (TType, error) { switch byte(t) & 0x0f { case STOP: return STOP, nil case COMPACT_BOOLEAN_FALSE, COMPACT_BOOLEAN_TRUE: return BOOL, nil case COMPACT_BYTE: return BYTE, nil case COMPACT_I16: return I16, nil case COMPACT_I32: return I32, nil case COMPACT_I64: return I64, nil case COMPACT_DOUBLE: return DOUBLE, nil case COMPACT_BINARY: return STRING, nil case COMPACT_LIST: return LIST, nil case COMPACT_SET: return SET, nil case COMPACT_MAP: return MAP, nil case COMPACT_STRUCT: return STRUCT, nil } return STOP, TException(fmt.Errorf("don't know what type: %v", t&0x0f)) } // Given a TType value, find the appropriate TCompactProtocol.Types constant. func (p *TCompactProtocol) getCompactType(t TType) tCompactType { return ttypeToCompactType[t] } compact_protocol_test.go000066400000000000000000000033731456146544700353370ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bytes" "testing" ) func TestReadWriteCompactProtocol(t *testing.T) { ReadWriteProtocolTest(t, NewTCompactProtocolFactory()) transports := []TTransport{ NewTMemoryBuffer(), NewStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 16384))), NewTFramedTransport(NewTMemoryBuffer()), } for _, trans := range transports { p := NewTCompactProtocol(trans); ReadWriteBool(t, p, trans); p = NewTCompactProtocol(trans); ReadWriteByte(t, p, trans); p = NewTCompactProtocol(trans); ReadWriteI16(t, p, trans); p = NewTCompactProtocol(trans); ReadWriteI32(t, p, trans); p = NewTCompactProtocol(trans); ReadWriteI64(t, p, trans); p = NewTCompactProtocol(trans); ReadWriteDouble(t, p, trans); p = NewTCompactProtocol(trans); ReadWriteString(t, p, trans); p = NewTCompactProtocol(trans); ReadWriteBinary(t, p, trans); trans.Close(); } } debug_protocol.go000066400000000000000000000233351456146544700337400ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "log" ) type TDebugProtocol struct { Delegate TProtocol LogPrefix string } type TDebugProtocolFactory struct { Underlying TProtocolFactory LogPrefix string } func NewTDebugProtocolFactory(underlying TProtocolFactory, logPrefix string) *TDebugProtocolFactory { return &TDebugProtocolFactory{ Underlying: underlying, LogPrefix: logPrefix, } } func (t *TDebugProtocolFactory) GetProtocol(trans TTransport) TProtocol { return &TDebugProtocol{ Delegate: t.Underlying.GetProtocol(trans), LogPrefix: t.LogPrefix, } } func (tdp *TDebugProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error { err := tdp.Delegate.WriteMessageBegin(name, typeId, seqid) log.Printf("%sWriteMessageBegin(name=%#v, typeId=%#v, seqid=%#v) => %#v", tdp.LogPrefix, name, typeId, seqid, err) return err } func (tdp *TDebugProtocol) WriteMessageEnd() error { err := tdp.Delegate.WriteMessageEnd() log.Printf("%sWriteMessageEnd() => %#v", tdp.LogPrefix, err) return err } func (tdp *TDebugProtocol) WriteStructBegin(name string) error { err := tdp.Delegate.WriteStructBegin(name) log.Printf("%sWriteStructBegin(name=%#v) => %#v", tdp.LogPrefix, name, err) return err } func (tdp *TDebugProtocol) WriteStructEnd() error { err := tdp.Delegate.WriteStructEnd() log.Printf("%sWriteStructEnd() => %#v", tdp.LogPrefix, err) return err } func (tdp *TDebugProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { err := tdp.Delegate.WriteFieldBegin(name, typeId, id) log.Printf("%sWriteFieldBegin(name=%#v, typeId=%#v, id%#v) => %#v", tdp.LogPrefix, name, typeId, id, err) return err } func (tdp *TDebugProtocol) WriteFieldEnd() error { err := tdp.Delegate.WriteFieldEnd() log.Printf("%sWriteFieldEnd() => %#v", tdp.LogPrefix, err) return err } func (tdp *TDebugProtocol) WriteFieldStop() error { err := tdp.Delegate.WriteFieldStop() log.Printf("%sWriteFieldStop() => %#v", tdp.LogPrefix, err) return err } func (tdp *TDebugProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { err := tdp.Delegate.WriteMapBegin(keyType, valueType, size) log.Printf("%sWriteMapBegin(keyType=%#v, valueType=%#v, size=%#v) => %#v", tdp.LogPrefix, keyType, valueType, size, err) return err } func (tdp *TDebugProtocol) WriteMapEnd() error { err := tdp.Delegate.WriteMapEnd() log.Printf("%sWriteMapEnd() => %#v", tdp.LogPrefix, err) return err } func (tdp *TDebugProtocol) WriteListBegin(elemType TType, size int) error { err := tdp.Delegate.WriteListBegin(elemType, size) log.Printf("%sWriteListBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err) return err } func (tdp *TDebugProtocol) WriteListEnd() error { err := tdp.Delegate.WriteListEnd() log.Printf("%sWriteListEnd() => %#v", tdp.LogPrefix, err) return err } func (tdp *TDebugProtocol) WriteSetBegin(elemType TType, size int) error { err := tdp.Delegate.WriteSetBegin(elemType, size) log.Printf("%sWriteSetBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err) return err } func (tdp *TDebugProtocol) WriteSetEnd() error { err := tdp.Delegate.WriteSetEnd() log.Printf("%sWriteSetEnd() => %#v", tdp.LogPrefix, err) return err } func (tdp *TDebugProtocol) WriteBool(value bool) error { err := tdp.Delegate.WriteBool(value) log.Printf("%sWriteBool(value=%#v) => %#v", tdp.LogPrefix, value, err) return err } func (tdp *TDebugProtocol) WriteByte(value int8) error { err := tdp.Delegate.WriteByte(value) log.Printf("%sWriteByte(value=%#v) => %#v", tdp.LogPrefix, value, err) return err } func (tdp *TDebugProtocol) WriteI16(value int16) error { err := tdp.Delegate.WriteI16(value) log.Printf("%sWriteI16(value=%#v) => %#v", tdp.LogPrefix, value, err) return err } func (tdp *TDebugProtocol) WriteI32(value int32) error { err := tdp.Delegate.WriteI32(value) log.Printf("%sWriteI32(value=%#v) => %#v", tdp.LogPrefix, value, err) return err } func (tdp *TDebugProtocol) WriteI64(value int64) error { err := tdp.Delegate.WriteI64(value) log.Printf("%sWriteI64(value=%#v) => %#v", tdp.LogPrefix, value, err) return err } func (tdp *TDebugProtocol) WriteDouble(value float64) error { err := tdp.Delegate.WriteDouble(value) log.Printf("%sWriteDouble(value=%#v) => %#v", tdp.LogPrefix, value, err) return err } func (tdp *TDebugProtocol) WriteString(value string) error { err := tdp.Delegate.WriteString(value) log.Printf("%sWriteString(value=%#v) => %#v", tdp.LogPrefix, value, err) return err } func (tdp *TDebugProtocol) WriteBinary(value []byte) error { err := tdp.Delegate.WriteBinary(value) log.Printf("%sWriteBinary(value=%#v) => %#v", tdp.LogPrefix, value, err) return err } func (tdp *TDebugProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) { name, typeId, seqid, err = tdp.Delegate.ReadMessageBegin() log.Printf("%sReadMessageBegin() (name=%#v, typeId=%#v, seqid=%#v, err=%#v)", tdp.LogPrefix, name, typeId, seqid, err) return } func (tdp *TDebugProtocol) ReadMessageEnd() (err error) { err = tdp.Delegate.ReadMessageEnd() log.Printf("%sReadMessageEnd() err=%#v", tdp.LogPrefix, err) return } func (tdp *TDebugProtocol) ReadStructBegin() (name string, err error) { name, err = tdp.Delegate.ReadStructBegin() log.Printf("%sReadStructBegin() (name%#v, err=%#v)", tdp.LogPrefix, name, err) return } func (tdp *TDebugProtocol) ReadStructEnd() (err error) { err = tdp.Delegate.ReadStructEnd() log.Printf("%sReadStructEnd() err=%#v", tdp.LogPrefix, err) return } func (tdp *TDebugProtocol) ReadFieldBegin() (name string, typeId TType, id int16, err error) { name, typeId, id, err = tdp.Delegate.ReadFieldBegin() log.Printf("%sReadFieldBegin() (name=%#v, typeId=%#v, id=%#v, err=%#v)", tdp.LogPrefix, name, typeId, id, err) return } func (tdp *TDebugProtocol) ReadFieldEnd() (err error) { err = tdp.Delegate.ReadFieldEnd() log.Printf("%sReadFieldEnd() err=%#v", tdp.LogPrefix, err) return } func (tdp *TDebugProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, err error) { keyType, valueType, size, err = tdp.Delegate.ReadMapBegin() log.Printf("%sReadMapBegin() (keyType=%#v, valueType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, keyType, valueType, size, err) return } func (tdp *TDebugProtocol) ReadMapEnd() (err error) { err = tdp.Delegate.ReadMapEnd() log.Printf("%sReadMapEnd() err=%#v", tdp.LogPrefix, err) return } func (tdp *TDebugProtocol) ReadListBegin() (elemType TType, size int, err error) { elemType, size, err = tdp.Delegate.ReadListBegin() log.Printf("%sReadListBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err) return } func (tdp *TDebugProtocol) ReadListEnd() (err error) { err = tdp.Delegate.ReadListEnd() log.Printf("%sReadListEnd() err=%#v", tdp.LogPrefix, err) return } func (tdp *TDebugProtocol) ReadSetBegin() (elemType TType, size int, err error) { elemType, size, err = tdp.Delegate.ReadSetBegin() log.Printf("%sReadSetBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err) return } func (tdp *TDebugProtocol) ReadSetEnd() (err error) { err = tdp.Delegate.ReadSetEnd() log.Printf("%sReadSetEnd() err=%#v", tdp.LogPrefix, err) return } func (tdp *TDebugProtocol) ReadBool() (value bool, err error) { value, err = tdp.Delegate.ReadBool() log.Printf("%sReadBool() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) return } func (tdp *TDebugProtocol) ReadByte() (value int8, err error) { value, err = tdp.Delegate.ReadByte() log.Printf("%sReadByte() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) return } func (tdp *TDebugProtocol) ReadI16() (value int16, err error) { value, err = tdp.Delegate.ReadI16() log.Printf("%sReadI16() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) return } func (tdp *TDebugProtocol) ReadI32() (value int32, err error) { value, err = tdp.Delegate.ReadI32() log.Printf("%sReadI32() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) return } func (tdp *TDebugProtocol) ReadI64() (value int64, err error) { value, err = tdp.Delegate.ReadI64() log.Printf("%sReadI64() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) return } func (tdp *TDebugProtocol) ReadDouble() (value float64, err error) { value, err = tdp.Delegate.ReadDouble() log.Printf("%sReadDouble() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) return } func (tdp *TDebugProtocol) ReadString() (value string, err error) { value, err = tdp.Delegate.ReadString() log.Printf("%sReadString() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) return } func (tdp *TDebugProtocol) ReadBinary() (value []byte, err error) { value, err = tdp.Delegate.ReadBinary() log.Printf("%sReadBinary() (value=%#v, err=%#v)", tdp.LogPrefix, value, err) return } func (tdp *TDebugProtocol) Skip(fieldType TType) (err error) { err = tdp.Delegate.Skip(fieldType) log.Printf("%sSkip(fieldType=%#v) (err=%#v)", tdp.LogPrefix, fieldType, err) return } func (tdp *TDebugProtocol) Flush() (err error) { err = tdp.Delegate.Flush() log.Printf("%sFlush() (err=%#v)", tdp.LogPrefix, err) return } func (tdp *TDebugProtocol) Transport() TTransport { return tdp.Delegate.Transport() } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/deserializer.go000066400000000000000000000030151456146544700334630ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift type TDeserializer struct { Transport TTransport Protocol TProtocol } func NewTDeserializer() *TDeserializer { var transport TTransport transport = NewTMemoryBufferLen(1024) protocol := NewTBinaryProtocolFactoryDefault().GetProtocol(transport) return &TDeserializer{ transport, protocol} } func (t *TDeserializer) ReadString(msg TStruct, s string) (err error) { err = nil if _, err = t.Transport.Write([]byte(s)); err != nil { return } if err = msg.Read(t.Protocol); err != nil { return } return } func (t *TDeserializer) Read(msg TStruct, b []byte) (err error) { err = nil if _, err = t.Transport.Write(b); err != nil { return } if err = msg.Read(t.Protocol); err != nil { return } return } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/exception.go000066400000000000000000000026611456146544700330050ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "errors" ) // Generic Thrift exception type TException interface { error } // Prepends additional information to an error without losing the Thrift exception interface func PrependError(prepend string, err error) error { if t, ok := err.(TTransportException); ok { return NewTTransportException(t.TypeId(), prepend+t.Error()) } if t, ok := err.(TProtocolException); ok { return NewTProtocolExceptionWithType(t.TypeId(), errors.New(prepend+err.Error())) } if t, ok := err.(TApplicationException); ok { return NewTApplicationException(t.TypeId(), prepend+t.Error()) } return errors.New(prepend + err.Error()) } exception_test.go000066400000000000000000000041601456146544700337610ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "errors" "testing" ) func TestPrependError(t *testing.T) { err := NewTApplicationException(INTERNAL_ERROR, "original error") err2, ok := PrependError("Prepend: ", err).(TApplicationException) if !ok { t.Fatal("Couldn't cast error TApplicationException") } if err2.Error() != "Prepend: original error" { t.Fatal("Unexpected error string") } if err2.TypeId() != INTERNAL_ERROR { t.Fatal("Unexpected type error") } err3 := NewTProtocolExceptionWithType(INVALID_DATA, errors.New("original error")) err4, ok := PrependError("Prepend: ", err3).(TProtocolException) if !ok { t.Fatal("Couldn't cast error TProtocolException") } if err4.Error() != "Prepend: original error" { t.Fatal("Unexpected error string") } if err4.TypeId() != INVALID_DATA { t.Fatal("Unexpected type error") } err5 := NewTTransportException(TIMED_OUT, "original error") err6, ok := PrependError("Prepend: ", err5).(TTransportException) if !ok { t.Fatal("Couldn't cast error TTransportException") } if err6.Error() != "Prepend: original error" { t.Fatal("Unexpected error string") } if err6.TypeId() != TIMED_OUT { t.Fatal("Unexpected type error") } err7 := errors.New("original error") err8 := PrependError("Prepend: ", err7) if err8.Error() != "Prepend: original error" { t.Fatal("Unexpected error string") } } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/field.go000066400000000000000000000033531456146544700320710ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import "strconv" // Helper class that encapsulates field metadata. type field struct { name string typeId TType id int } func newField(n string, t TType, i int) *field { return &field{name: n, typeId: t, id: i} } func (p *field) Name() string { if p == nil { return "" } return p.name } func (p *field) TypeId() TType { if p == nil { return TType(VOID) } return p.typeId } func (p *field) Id() int { if p == nil { return -1 } return p.id } func (p *field) String() string { if p == nil { return "" } return "" } var ANONYMOUS_FIELD *field type fieldSlice []field func (p fieldSlice) Len() int { return len(p) } func (p fieldSlice) Less(i, j int) bool { return p[i].Id() < p[j].Id() } func (p fieldSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func init() { ANONYMOUS_FIELD = newField("", STOP, 0) } framed_transport.go000066400000000000000000000112721456146544700343000ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bufio" "bytes" "encoding/binary" "fmt" "io" ) const DEFAULT_MAX_LENGTH = 16384000 type TFramedTransport struct { transport TTransport buf bytes.Buffer reader *bufio.Reader frameSize uint32 //Current remaining size of the frame. if ==0 read next frame header buffer [4]byte maxLength uint32 } type tFramedTransportFactory struct { factory TTransportFactory maxLength uint32 } func NewTFramedTransportFactory(factory TTransportFactory) TTransportFactory { return &tFramedTransportFactory{factory: factory, maxLength: DEFAULT_MAX_LENGTH} } func NewTFramedTransportFactoryMaxLength(factory TTransportFactory, maxLength uint32) TTransportFactory { return &tFramedTransportFactory{factory: factory, maxLength: maxLength} } func (p *tFramedTransportFactory) GetTransport(base TTransport) TTransport { return NewTFramedTransportMaxLength(p.factory.GetTransport(base), p.maxLength) } func NewTFramedTransport(transport TTransport) *TFramedTransport { return &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: DEFAULT_MAX_LENGTH} } func NewTFramedTransportMaxLength(transport TTransport, maxLength uint32) *TFramedTransport { return &TFramedTransport{transport: transport, reader: bufio.NewReader(transport), maxLength: maxLength} } func (p *TFramedTransport) Open() error { return p.transport.Open() } func (p *TFramedTransport) IsOpen() bool { return p.transport.IsOpen() } func (p *TFramedTransport) Close() error { return p.transport.Close() } func (p *TFramedTransport) Read(buf []byte) (l int, err error) { if p.frameSize == 0 { p.frameSize, err = p.readFrameHeader() if err != nil { return } } if p.frameSize < uint32(len(buf)) { frameSize := p.frameSize tmp := make([]byte, p.frameSize) l, err = p.Read(tmp) copy(buf, tmp) if err == nil { err = NewTTransportExceptionFromError(fmt.Errorf("Not enough frame size %d to read %d bytes", frameSize, len(buf))) return } } got, err := p.reader.Read(buf) p.frameSize = p.frameSize - uint32(got) //sanity check if p.frameSize < 0 { return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "Negative frame size") } return got, NewTTransportExceptionFromError(err) } func (p *TFramedTransport) ReadByte() (c byte, err error) { if p.frameSize == 0 { p.frameSize, err = p.readFrameHeader() if err != nil { return } } if p.frameSize < 1 { return 0, NewTTransportExceptionFromError(fmt.Errorf("Not enough frame size %d to read %d bytes", p.frameSize, 1)) } c, err = p.reader.ReadByte() if err == nil { p.frameSize-- } return } func (p *TFramedTransport) Write(buf []byte) (int, error) { n, err := p.buf.Write(buf) return n, NewTTransportExceptionFromError(err) } func (p *TFramedTransport) WriteByte(c byte) error { return p.buf.WriteByte(c) } func (p *TFramedTransport) WriteString(s string) (n int, err error) { return p.buf.WriteString(s) } func (p *TFramedTransport) Flush() error { size := p.buf.Len() buf := p.buffer[:4] binary.BigEndian.PutUint32(buf, uint32(size)) _, err := p.transport.Write(buf) if err != nil { return NewTTransportExceptionFromError(err) } if size > 0 { if n, err := p.buf.WriteTo(p.transport); err != nil { print("Error while flushing write buffer of size ", size, " to transport, only wrote ", n, " bytes: ", err.Error(), "\n") return NewTTransportExceptionFromError(err) } } err = p.transport.Flush() return NewTTransportExceptionFromError(err) } func (p *TFramedTransport) readFrameHeader() (uint32, error) { buf := p.buffer[:4] if _, err := io.ReadFull(p.reader, buf); err != nil { return 0, err } size := binary.BigEndian.Uint32(buf) if size < 0 || size > p.maxLength { return 0, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, fmt.Sprintf("Incorrect frame size (%d)", size)) } return size, nil } func (p *TFramedTransport) RemainingBytes() (num_bytes uint64) { return uint64(p.frameSize) } framed_transport_test.go000066400000000000000000000017111456146544700353340ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "testing" ) func TestFramedTransport(t *testing.T) { trans := NewTFramedTransport(NewTMemoryBuffer()) TransportTest(t, trans, trans) } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/http_client.go000066400000000000000000000170061456146544700333230ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bytes" "io" "io/ioutil" "net/http" "net/url" "strconv" ) // Default to using the shared http client. Library users are // free to change this global client or specify one through // THttpClientOptions. var DefaultHttpClient *http.Client = http.DefaultClient type THttpClient struct { client *http.Client response *http.Response url *url.URL requestBuffer *bytes.Buffer header http.Header nsecConnectTimeout int64 nsecReadTimeout int64 } type THttpClientTransportFactory struct { options THttpClientOptions url string isPost bool } func (p *THttpClientTransportFactory) GetTransport(trans TTransport) TTransport { if trans != nil { t, ok := trans.(*THttpClient) if ok && t.url != nil { if t.requestBuffer != nil { t2, _ := NewTHttpPostClientWithOptions(t.url.String(), p.options) return t2 } t2, _ := NewTHttpClientWithOptions(t.url.String(), p.options) return t2 } } if p.isPost { s, _ := NewTHttpPostClientWithOptions(p.url, p.options) return s } s, _ := NewTHttpClientWithOptions(p.url, p.options) return s } type THttpClientOptions struct { // If nil, DefaultHttpClient is used Client *http.Client } func NewTHttpClientTransportFactory(url string) *THttpClientTransportFactory { return NewTHttpClientTransportFactoryWithOptions(url, THttpClientOptions{}) } func NewTHttpClientTransportFactoryWithOptions(url string, options THttpClientOptions) *THttpClientTransportFactory { return &THttpClientTransportFactory{url: url, isPost: false, options: options} } func NewTHttpPostClientTransportFactory(url string) *THttpClientTransportFactory { return NewTHttpPostClientTransportFactoryWithOptions(url, THttpClientOptions{}) } func NewTHttpPostClientTransportFactoryWithOptions(url string, options THttpClientOptions) *THttpClientTransportFactory { return &THttpClientTransportFactory{url: url, isPost: true, options: options} } func NewTHttpClientWithOptions(urlstr string, options THttpClientOptions) (TTransport, error) { parsedURL, err := url.Parse(urlstr) if err != nil { return nil, err } response, err := http.Get(urlstr) if err != nil { return nil, err } client := options.Client if client == nil { client = DefaultHttpClient } httpHeader := map[string][]string{"Content-Type": []string{"application/x-thrift"}} return &THttpClient{client: client, response: response, url: parsedURL, header: httpHeader}, nil } func NewTHttpClient(urlstr string) (TTransport, error) { return NewTHttpClientWithOptions(urlstr, THttpClientOptions{}) } func NewTHttpPostClientWithOptions(urlstr string, options THttpClientOptions) (TTransport, error) { parsedURL, err := url.Parse(urlstr) if err != nil { return nil, err } buf := make([]byte, 0, 1024) client := options.Client if client == nil { client = DefaultHttpClient } httpHeader := map[string][]string{"Content-Type": []string{"application/x-thrift"}} return &THttpClient{client: client, url: parsedURL, requestBuffer: bytes.NewBuffer(buf), header: httpHeader}, nil } func NewTHttpPostClient(urlstr string) (TTransport, error) { return NewTHttpPostClientWithOptions(urlstr, THttpClientOptions{}) } // Set the HTTP Header for this specific Thrift Transport // It is important that you first assert the TTransport as a THttpClient type // like so: // // httpTrans := trans.(THttpClient) // httpTrans.SetHeader("User-Agent","Thrift Client 1.0") func (p *THttpClient) SetHeader(key string, value string) { p.header.Add(key, value) } // Get the HTTP Header represented by the supplied Header Key for this specific Thrift Transport // It is important that you first assert the TTransport as a THttpClient type // like so: // // httpTrans := trans.(THttpClient) // hdrValue := httpTrans.GetHeader("User-Agent") func (p *THttpClient) GetHeader(key string) string { return p.header.Get(key) } // Deletes the HTTP Header given a Header Key for this specific Thrift Transport // It is important that you first assert the TTransport as a THttpClient type // like so: // // httpTrans := trans.(THttpClient) // httpTrans.DelHeader("User-Agent") func (p *THttpClient) DelHeader(key string) { p.header.Del(key) } func (p *THttpClient) Open() error { // do nothing return nil } func (p *THttpClient) IsOpen() bool { return p.response != nil || p.requestBuffer != nil } func (p *THttpClient) closeResponse() error { var err error if p.response != nil && p.response.Body != nil { // The docs specify that if keepalive is enabled and the response body is not // read to completion the connection will never be returned to the pool and // reused. Errors are being ignored here because if the connection is invalid // and this fails for some reason, the Close() method will do any remaining // cleanup. io.Copy(ioutil.Discard, p.response.Body) err = p.response.Body.Close() } p.response = nil return err } func (p *THttpClient) Close() error { if p.requestBuffer != nil { p.requestBuffer.Reset() p.requestBuffer = nil } return p.closeResponse() } func (p *THttpClient) Read(buf []byte) (int, error) { if p.response == nil { return 0, NewTTransportException(NOT_OPEN, "Response buffer is empty, no request.") } n, err := p.response.Body.Read(buf) if n > 0 && (err == nil || err == io.EOF) { return n, nil } return n, NewTTransportExceptionFromError(err) } func (p *THttpClient) ReadByte() (c byte, err error) { return readByte(p.response.Body) } func (p *THttpClient) Write(buf []byte) (int, error) { n, err := p.requestBuffer.Write(buf) return n, err } func (p *THttpClient) WriteByte(c byte) error { return p.requestBuffer.WriteByte(c) } func (p *THttpClient) WriteString(s string) (n int, err error) { return p.requestBuffer.WriteString(s) } func (p *THttpClient) Flush() error { // Close any previous response body to avoid leaking connections. p.closeResponse() req, err := http.NewRequest("POST", p.url.String(), p.requestBuffer) if err != nil { return NewTTransportExceptionFromError(err) } req.Header = p.header response, err := p.client.Do(req) if err != nil { return NewTTransportExceptionFromError(err) } if response.StatusCode != http.StatusOK { // Close the response to avoid leaking file descriptors. closeResponse does // more than just call Close(), so temporarily assign it and reuse the logic. p.response = response p.closeResponse() // TODO(pomack) log bad response return NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "HTTP Response code: "+strconv.Itoa(response.StatusCode)) } p.response = response return nil } func (p *THttpClient) RemainingBytes() (num_bytes uint64) { len := p.response.ContentLength if len >= 0 { return uint64(len) } const maxSize = ^uint64(0) return maxSize // the thruth is, we just don't know unless framed is used } http_client_test.go000066400000000000000000000051701456146544700343020ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "net/http" "testing" ) func TestHttpClient(t *testing.T) { l, addr := HttpClientSetupForTest(t) if l != nil { defer l.Close() } trans, err := NewTHttpPostClient("http://" + addr.String()) if err != nil { l.Close() t.Fatalf("Unable to connect to %s: %s", addr.String(), err) } TransportTest(t, trans, trans) } func TestHttpClientHeaders(t *testing.T) { l, addr := HttpClientSetupForTest(t) if l != nil { defer l.Close() } trans, err := NewTHttpPostClient("http://" + addr.String()) if err != nil { l.Close() t.Fatalf("Unable to connect to %s: %s", addr.String(), err) } TransportHeaderTest(t, trans, trans) } func TestHttpCustomClient(t *testing.T) { l, addr := HttpClientSetupForTest(t) if l != nil { defer l.Close() } httpTransport := &customHttpTransport{} trans, err := NewTHttpPostClientWithOptions("http://"+addr.String(), THttpClientOptions{ Client: &http.Client{ Transport: httpTransport, }, }) if err != nil { l.Close() t.Fatalf("Unable to connect to %s: %s", addr.String(), err) } TransportHeaderTest(t, trans, trans) if !httpTransport.hit { t.Fatalf("Custom client was not used") } } func TestHttpCustomClientPackageScope(t *testing.T) { l, addr := HttpClientSetupForTest(t) if l != nil { defer l.Close() } httpTransport := &customHttpTransport{} DefaultHttpClient = &http.Client{ Transport: httpTransport, } trans, err := NewTHttpPostClient("http://" + addr.String()) if err != nil { l.Close() t.Fatalf("Unable to connect to %s: %s", addr.String(), err) } TransportHeaderTest(t, trans, trans) if !httpTransport.hit { t.Fatalf("Custom client was not used") } } type customHttpTransport struct { hit bool } func (c *customHttpTransport) RoundTrip(req *http.Request) (*http.Response, error) { c.hit = true return http.DefaultTransport.RoundTrip(req) } http_transport.go000066400000000000000000000024611456146544700340210ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import "net/http" // NewThriftHandlerFunc is a function that create a ready to use Apache Thrift Handler function func NewThriftHandlerFunc(processor TProcessor, inPfactory, outPfactory TProtocolFactory) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add("Content-Type", "application/x-thrift") transport := NewStreamTransport(r.Body, w) processor.Process(inPfactory.GetProtocol(transport), outPfactory.GetProtocol(transport)) } } iostream_transport.go000066400000000000000000000123651456146544700346710ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bufio" "io" ) // StreamTransport is a Transport made of an io.Reader and/or an io.Writer type StreamTransport struct { io.Reader io.Writer isReadWriter bool closed bool } type StreamTransportFactory struct { Reader io.Reader Writer io.Writer isReadWriter bool } func (p *StreamTransportFactory) GetTransport(trans TTransport) TTransport { if trans != nil { t, ok := trans.(*StreamTransport) if ok { if t.isReadWriter { return NewStreamTransportRW(t.Reader.(io.ReadWriter)) } if t.Reader != nil && t.Writer != nil { return NewStreamTransport(t.Reader, t.Writer) } if t.Reader != nil && t.Writer == nil { return NewStreamTransportR(t.Reader) } if t.Reader == nil && t.Writer != nil { return NewStreamTransportW(t.Writer) } return &StreamTransport{} } } if p.isReadWriter { return NewStreamTransportRW(p.Reader.(io.ReadWriter)) } if p.Reader != nil && p.Writer != nil { return NewStreamTransport(p.Reader, p.Writer) } if p.Reader != nil && p.Writer == nil { return NewStreamTransportR(p.Reader) } if p.Reader == nil && p.Writer != nil { return NewStreamTransportW(p.Writer) } return &StreamTransport{} } func NewStreamTransportFactory(reader io.Reader, writer io.Writer, isReadWriter bool) *StreamTransportFactory { return &StreamTransportFactory{Reader: reader, Writer: writer, isReadWriter: isReadWriter} } func NewStreamTransport(r io.Reader, w io.Writer) *StreamTransport { return &StreamTransport{Reader: bufio.NewReader(r), Writer: bufio.NewWriter(w)} } func NewStreamTransportR(r io.Reader) *StreamTransport { return &StreamTransport{Reader: bufio.NewReader(r)} } func NewStreamTransportW(w io.Writer) *StreamTransport { return &StreamTransport{Writer: bufio.NewWriter(w)} } func NewStreamTransportRW(rw io.ReadWriter) *StreamTransport { bufrw := bufio.NewReadWriter(bufio.NewReader(rw), bufio.NewWriter(rw)) return &StreamTransport{Reader: bufrw, Writer: bufrw, isReadWriter: true} } func (p *StreamTransport) IsOpen() bool { return !p.closed } // implicitly opened on creation, can't be reopened once closed func (p *StreamTransport) Open() error { if !p.closed { return NewTTransportException(ALREADY_OPEN, "StreamTransport already open.") } else { return NewTTransportException(NOT_OPEN, "cannot reopen StreamTransport.") } } // Closes both the input and output streams. func (p *StreamTransport) Close() error { if p.closed { return NewTTransportException(NOT_OPEN, "StreamTransport already closed.") } p.closed = true closedReader := false if p.Reader != nil { c, ok := p.Reader.(io.Closer) if ok { e := c.Close() closedReader = true if e != nil { return e } } p.Reader = nil } if p.Writer != nil && (!closedReader || !p.isReadWriter) { c, ok := p.Writer.(io.Closer) if ok { e := c.Close() if e != nil { return e } } p.Writer = nil } return nil } // Flushes the underlying output stream if not null. func (p *StreamTransport) Flush() error { if p.Writer == nil { return NewTTransportException(NOT_OPEN, "Cannot flush null outputStream") } f, ok := p.Writer.(Flusher) if ok { err := f.Flush() if err != nil { return NewTTransportExceptionFromError(err) } } return nil } func (p *StreamTransport) Read(c []byte) (n int, err error) { n, err = p.Reader.Read(c) if err != nil { err = NewTTransportExceptionFromError(err) } return } func (p *StreamTransport) ReadByte() (c byte, err error) { f, ok := p.Reader.(io.ByteReader) if ok { c, err = f.ReadByte() } else { c, err = readByte(p.Reader) } if err != nil { err = NewTTransportExceptionFromError(err) } return } func (p *StreamTransport) Write(c []byte) (n int, err error) { n, err = p.Writer.Write(c) if err != nil { err = NewTTransportExceptionFromError(err) } return } func (p *StreamTransport) WriteByte(c byte) (err error) { f, ok := p.Writer.(io.ByteWriter) if ok { err = f.WriteByte(c) } else { err = writeByte(p.Writer, c) } if err != nil { err = NewTTransportExceptionFromError(err) } return } func (p *StreamTransport) WriteString(s string) (n int, err error) { f, ok := p.Writer.(stringWriter) if ok { n, err = f.WriteString(s) } else { n, err = p.Writer.Write([]byte(s)) } if err != nil { err = NewTTransportExceptionFromError(err) } return } func (p *StreamTransport) RemainingBytes() (num_bytes uint64) { const maxSize = ^uint64(0) return maxSize // the thruth is, we just don't know unless framed is used } iostream_transport_test.go000066400000000000000000000032311456146544700357200ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bytes" "testing" ) func TestStreamTransport(t *testing.T) { trans := NewStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 1024))) TransportTest(t, trans, trans) } func TestStreamTransportOpenClose(t *testing.T) { trans := NewStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 1024))) if !trans.IsOpen() { t.Fatal("StreamTransport should be already open") } if trans.Open() == nil { t.Fatal("StreamTransport should return error when open twice") } if trans.Close() != nil { t.Fatal("StreamTransport should not return error when closing open transport") } if trans.IsOpen() { t.Fatal("StreamTransport should not be open after close") } if trans.Close() == nil { t.Fatal("StreamTransport should return error when closing a non open transport") } if trans.Open() == nil { t.Fatal("StreamTransport should not be able to reopen") } } json_protocol.go000066400000000000000000000330141456146544700336160ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "encoding/base64" "fmt" ) const ( THRIFT_JSON_PROTOCOL_VERSION = 1 ) // for references to _ParseContext see tsimplejson_protocol.go // JSON protocol implementation for thrift. // // This protocol produces/consumes a simple output format // suitable for parsing by scripting languages. It should not be // confused with the full-featured TJSONProtocol. // type TJSONProtocol struct { *TSimpleJSONProtocol } // Constructor func NewTJSONProtocol(t TTransport) *TJSONProtocol { v := &TJSONProtocol{TSimpleJSONProtocol: NewTSimpleJSONProtocol(t)} v.parseContextStack = append(v.parseContextStack, int(_CONTEXT_IN_TOPLEVEL)) v.dumpContext = append(v.dumpContext, int(_CONTEXT_IN_TOPLEVEL)) return v } // Factory type TJSONProtocolFactory struct{} func (p *TJSONProtocolFactory) GetProtocol(trans TTransport) TProtocol { return NewTJSONProtocol(trans) } func NewTJSONProtocolFactory() *TJSONProtocolFactory { return &TJSONProtocolFactory{} } func (p *TJSONProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error { p.resetContextStack() // THRIFT-3735 if e := p.OutputListBegin(); e != nil { return e } if e := p.WriteI32(THRIFT_JSON_PROTOCOL_VERSION); e != nil { return e } if e := p.WriteString(name); e != nil { return e } if e := p.WriteByte(int8(typeId)); e != nil { return e } if e := p.WriteI32(seqId); e != nil { return e } return nil } func (p *TJSONProtocol) WriteMessageEnd() error { return p.OutputListEnd() } func (p *TJSONProtocol) WriteStructBegin(name string) error { if e := p.OutputObjectBegin(); e != nil { return e } return nil } func (p *TJSONProtocol) WriteStructEnd() error { return p.OutputObjectEnd() } func (p *TJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { if e := p.WriteI16(id); e != nil { return e } if e := p.OutputObjectBegin(); e != nil { return e } s, e1 := p.TypeIdToString(typeId) if e1 != nil { return e1 } if e := p.WriteString(s); e != nil { return e } return nil } func (p *TJSONProtocol) WriteFieldEnd() error { return p.OutputObjectEnd() } func (p *TJSONProtocol) WriteFieldStop() error { return nil } func (p *TJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { if e := p.OutputListBegin(); e != nil { return e } s, e1 := p.TypeIdToString(keyType) if e1 != nil { return e1 } if e := p.WriteString(s); e != nil { return e } s, e1 = p.TypeIdToString(valueType) if e1 != nil { return e1 } if e := p.WriteString(s); e != nil { return e } if e := p.WriteI64(int64(size)); e != nil { return e } return p.OutputObjectBegin() } func (p *TJSONProtocol) WriteMapEnd() error { if e := p.OutputObjectEnd(); e != nil { return e } return p.OutputListEnd() } func (p *TJSONProtocol) WriteListBegin(elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } func (p *TJSONProtocol) WriteListEnd() error { return p.OutputListEnd() } func (p *TJSONProtocol) WriteSetBegin(elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } func (p *TJSONProtocol) WriteSetEnd() error { return p.OutputListEnd() } func (p *TJSONProtocol) WriteBool(b bool) error { if b { return p.WriteI32(1) } return p.WriteI32(0) } func (p *TJSONProtocol) WriteByte(b int8) error { return p.WriteI32(int32(b)) } func (p *TJSONProtocol) WriteI16(v int16) error { return p.WriteI32(int32(v)) } func (p *TJSONProtocol) WriteI32(v int32) error { return p.OutputI64(int64(v)) } func (p *TJSONProtocol) WriteI64(v int64) error { return p.OutputI64(int64(v)) } func (p *TJSONProtocol) WriteDouble(v float64) error { return p.OutputF64(v) } func (p *TJSONProtocol) WriteString(v string) error { return p.OutputString(v) } func (p *TJSONProtocol) WriteBinary(v []byte) error { // JSON library only takes in a string, // not an arbitrary byte array, to ensure bytes are transmitted // efficiently we must convert this into a valid JSON string // therefore we use base64 encoding to avoid excessive escaping/quoting if e := p.OutputPreValue(); e != nil { return e } if _, e := p.write(JSON_QUOTE_BYTES); e != nil { return NewTProtocolException(e) } writer := base64.NewEncoder(base64.StdEncoding, p.writer) if _, e := writer.Write(v); e != nil { p.writer.Reset(p.trans) // THRIFT-3735 return NewTProtocolException(e) } if e := writer.Close(); e != nil { return NewTProtocolException(e) } if _, e := p.write(JSON_QUOTE_BYTES); e != nil { return NewTProtocolException(e) } return p.OutputPostValue() } // Reading methods. func (p *TJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { p.resetContextStack() // THRIFT-3735 if isNull, err := p.ParseListBegin(); isNull || err != nil { return name, typeId, seqId, err } version, err := p.ReadI32() if err != nil { return name, typeId, seqId, err } if version != THRIFT_JSON_PROTOCOL_VERSION { e := fmt.Errorf("Unknown Protocol version %d, expected version %d", version, THRIFT_JSON_PROTOCOL_VERSION) return name, typeId, seqId, NewTProtocolExceptionWithType(INVALID_DATA, e) } if name, err = p.ReadString(); err != nil { return name, typeId, seqId, err } bTypeId, err := p.ReadByte() typeId = TMessageType(bTypeId) if err != nil { return name, typeId, seqId, err } if seqId, err = p.ReadI32(); err != nil { return name, typeId, seqId, err } return name, typeId, seqId, nil } func (p *TJSONProtocol) ReadMessageEnd() error { err := p.ParseListEnd() return err } func (p *TJSONProtocol) ReadStructBegin() (name string, err error) { _, err = p.ParseObjectStart() return "", err } func (p *TJSONProtocol) ReadStructEnd() error { return p.ParseObjectEnd() } func (p *TJSONProtocol) ReadFieldBegin() (string, TType, int16, error) { b, _ := p.reader.Peek(1) if len(b) < 1 || b[0] == JSON_RBRACE[0] || b[0] == JSON_RBRACKET[0] { return "", STOP, -1, nil } fieldId, err := p.ReadI16() if err != nil { return "", STOP, fieldId, err } if _, err = p.ParseObjectStart(); err != nil { return "", STOP, fieldId, err } sType, err := p.ReadString() if err != nil { return "", STOP, fieldId, err } fType, err := p.StringToTypeId(sType) return "", fType, fieldId, err } func (p *TJSONProtocol) ReadFieldEnd() error { return p.ParseObjectEnd() } func (p *TJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, e error) { if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, VOID, 0, e } // read keyType sKeyType, e := p.ReadString() if e != nil { return keyType, valueType, size, e } keyType, e = p.StringToTypeId(sKeyType) if e != nil { return keyType, valueType, size, e } // read valueType sValueType, e := p.ReadString() if e != nil { return keyType, valueType, size, e } valueType, e = p.StringToTypeId(sValueType) if e != nil { return keyType, valueType, size, e } // read size iSize, e := p.ReadI64() if e != nil { return keyType, valueType, size, e } size = int(iSize) _, e = p.ParseObjectStart() return keyType, valueType, size, e } func (p *TJSONProtocol) ReadMapEnd() error { e := p.ParseObjectEnd() if e != nil { return e } return p.ParseListEnd() } func (p *TJSONProtocol) ReadListBegin() (elemType TType, size int, e error) { return p.ParseElemListBegin() } func (p *TJSONProtocol) ReadListEnd() error { return p.ParseListEnd() } func (p *TJSONProtocol) ReadSetBegin() (elemType TType, size int, e error) { return p.ParseElemListBegin() } func (p *TJSONProtocol) ReadSetEnd() error { return p.ParseListEnd() } func (p *TJSONProtocol) ReadBool() (bool, error) { value, err := p.ReadI32() return (value != 0), err } func (p *TJSONProtocol) ReadByte() (int8, error) { v, err := p.ReadI64() return int8(v), err } func (p *TJSONProtocol) ReadI16() (int16, error) { v, err := p.ReadI64() return int16(v), err } func (p *TJSONProtocol) ReadI32() (int32, error) { v, err := p.ReadI64() return int32(v), err } func (p *TJSONProtocol) ReadI64() (int64, error) { v, _, err := p.ParseI64() return v, err } func (p *TJSONProtocol) ReadDouble() (float64, error) { v, _, err := p.ParseF64() return v, err } func (p *TJSONProtocol) ReadString() (string, error) { var v string if err := p.ParsePreValue(); err != nil { return v, err } f, _ := p.reader.Peek(1) if len(f) > 0 && f[0] == JSON_QUOTE { p.reader.ReadByte() value, err := p.ParseStringBody() v = value if err != nil { return v, err } } else if len(f) > 0 && f[0] == JSON_NULL[0] { b := make([]byte, len(JSON_NULL)) _, err := p.reader.Read(b) if err != nil { return v, NewTProtocolException(err) } if string(b) != string(JSON_NULL) { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } } else { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } return v, p.ParsePostValue() } func (p *TJSONProtocol) ReadBinary() ([]byte, error) { var v []byte if err := p.ParsePreValue(); err != nil { return nil, err } f, _ := p.reader.Peek(1) if len(f) > 0 && f[0] == JSON_QUOTE { p.reader.ReadByte() value, err := p.ParseBase64EncodedBody() v = value if err != nil { return v, err } } else if len(f) > 0 && f[0] == JSON_NULL[0] { b := make([]byte, len(JSON_NULL)) _, err := p.reader.Read(b) if err != nil { return v, NewTProtocolException(err) } if string(b) != string(JSON_NULL) { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } } else { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } return v, p.ParsePostValue() } func (p *TJSONProtocol) Flush() (err error) { err = p.writer.Flush() if err == nil { err = p.trans.Flush() } return NewTProtocolException(err) } func (p *TJSONProtocol) Skip(fieldType TType) (err error) { return SkipDefaultDepth(p, fieldType) } func (p *TJSONProtocol) Transport() TTransport { return p.trans } func (p *TJSONProtocol) OutputElemListBegin(elemType TType, size int) error { if e := p.OutputListBegin(); e != nil { return e } s, e1 := p.TypeIdToString(elemType) if e1 != nil { return e1 } if e := p.WriteString(s); e != nil { return e } if e := p.WriteI64(int64(size)); e != nil { return e } return nil } func (p *TJSONProtocol) ParseElemListBegin() (elemType TType, size int, e error) { if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, 0, e } sElemType, err := p.ReadString() if err != nil { return VOID, size, err } elemType, err = p.StringToTypeId(sElemType) if err != nil { return elemType, size, err } nSize, err2 := p.ReadI64() size = int(nSize) return elemType, size, err2 } func (p *TJSONProtocol) readElemListBegin() (elemType TType, size int, e error) { if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, 0, e } sElemType, err := p.ReadString() if err != nil { return VOID, size, err } elemType, err = p.StringToTypeId(sElemType) if err != nil { return elemType, size, err } nSize, err2 := p.ReadI64() size = int(nSize) return elemType, size, err2 } func (p *TJSONProtocol) writeElemListBegin(elemType TType, size int) error { if e := p.OutputListBegin(); e != nil { return e } s, e1 := p.TypeIdToString(elemType) if e1 != nil { return e1 } if e := p.OutputString(s); e != nil { return e } if e := p.OutputI64(int64(size)); e != nil { return e } return nil } func (p *TJSONProtocol) TypeIdToString(fieldType TType) (string, error) { switch byte(fieldType) { case BOOL: return "tf", nil case BYTE: return "i8", nil case I16: return "i16", nil case I32: return "i32", nil case I64: return "i64", nil case DOUBLE: return "dbl", nil case STRING: return "str", nil case STRUCT: return "rec", nil case MAP: return "map", nil case SET: return "set", nil case LIST: return "lst", nil } e := fmt.Errorf("Unknown fieldType: %d", int(fieldType)) return "", NewTProtocolExceptionWithType(INVALID_DATA, e) } func (p *TJSONProtocol) StringToTypeId(fieldType string) (TType, error) { switch fieldType { case "tf": return TType(BOOL), nil case "i8": return TType(BYTE), nil case "i16": return TType(I16), nil case "i32": return TType(I32), nil case "i64": return TType(I64), nil case "dbl": return TType(DOUBLE), nil case "str": return TType(STRING), nil case "rec": return TType(STRUCT), nil case "map": return TType(MAP), nil case "set": return TType(SET), nil case "lst": return TType(LIST), nil } e := fmt.Errorf("Unknown type identifier: %s", fieldType) return TType(STOP), NewTProtocolExceptionWithType(INVALID_DATA, e) } json_protocol_test.go000066400000000000000000000516051456146544700346630ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "encoding/base64" "encoding/json" "fmt" "math" "strconv" "testing" ) func TestWriteJSONProtocolBool(t *testing.T) { thetype := "boolean" trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range BOOL_VALUES { if e := p.WriteBool(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() expected := "" if value { expected = "1" } else { expected = "0" } if s != expected { t.Fatalf("Bad value for %s %v: %s expected", thetype, value, s) } v := -1 if err := json.Unmarshal([]byte(s), &v); err != nil || (v != 0) != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() } trans.Close() } func TestReadJSONProtocolBool(t *testing.T) { thetype := "boolean" for _, value := range BOOL_VALUES { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) if value { trans.Write([]byte{'1'}) // not JSON_TRUE } else { trans.Write([]byte{'0'}) // not JSON_FALSE } trans.Flush() s := trans.String() v, e := p.ReadBool() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } vv := -1 if err := json.Unmarshal([]byte(s), &vv); err != nil || (vv != 0) != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, vv) } trans.Reset() trans.Close() } } func TestWriteJSONProtocolByte(t *testing.T) { thetype := "byte" trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range BYTE_VALUES { if e := p.WriteByte(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if s != fmt.Sprint(value) { t.Fatalf("Bad value for %s %v: %s", thetype, value, s) } v := int8(0) if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() } trans.Close() } func TestReadJSONProtocolByte(t *testing.T) { thetype := "byte" for _, value := range BYTE_VALUES { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) trans.WriteString(strconv.Itoa(int(value))) trans.Flush() s := trans.String() v, e := p.ReadByte() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() trans.Close() } } func TestWriteJSONProtocolI16(t *testing.T) { thetype := "int16" trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range INT16_VALUES { if e := p.WriteI16(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if s != fmt.Sprint(value) { t.Fatalf("Bad value for %s %v: %s", thetype, value, s) } v := int16(0) if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() } trans.Close() } func TestReadJSONProtocolI16(t *testing.T) { thetype := "int16" for _, value := range INT16_VALUES { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) trans.WriteString(strconv.Itoa(int(value))) trans.Flush() s := trans.String() v, e := p.ReadI16() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() trans.Close() } } func TestWriteJSONProtocolI32(t *testing.T) { thetype := "int32" trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range INT32_VALUES { if e := p.WriteI32(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if s != fmt.Sprint(value) { t.Fatalf("Bad value for %s %v: %s", thetype, value, s) } v := int32(0) if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() } trans.Close() } func TestReadJSONProtocolI32(t *testing.T) { thetype := "int32" for _, value := range INT32_VALUES { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) trans.WriteString(strconv.Itoa(int(value))) trans.Flush() s := trans.String() v, e := p.ReadI32() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() trans.Close() } } func TestWriteJSONProtocolI64(t *testing.T) { thetype := "int64" trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range INT64_VALUES { if e := p.WriteI64(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if s != fmt.Sprint(value) { t.Fatalf("Bad value for %s %v: %s", thetype, value, s) } v := int64(0) if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() } trans.Close() } func TestReadJSONProtocolI64(t *testing.T) { thetype := "int64" for _, value := range INT64_VALUES { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) trans.WriteString(strconv.FormatInt(value, 10)) trans.Flush() s := trans.String() v, e := p.ReadI64() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() trans.Close() } } func TestWriteJSONProtocolDouble(t *testing.T) { thetype := "double" trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range DOUBLE_VALUES { if e := p.WriteDouble(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if math.IsInf(value, 1) { if s != jsonQuote(JSON_INFINITY) { t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, jsonQuote(JSON_INFINITY)) } } else if math.IsInf(value, -1) { if s != jsonQuote(JSON_NEGATIVE_INFINITY) { t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, jsonQuote(JSON_NEGATIVE_INFINITY)) } } else if math.IsNaN(value) { if s != jsonQuote(JSON_NAN) { t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, jsonQuote(JSON_NAN)) } } else { if s != fmt.Sprint(value) { t.Fatalf("Bad value for %s %v: %s", thetype, value, s) } v := float64(0) if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } } trans.Reset() } trans.Close() } func TestReadJSONProtocolDouble(t *testing.T) { thetype := "double" for _, value := range DOUBLE_VALUES { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) n := NewNumericFromDouble(value) trans.WriteString(n.String()) trans.Flush() s := trans.String() v, e := p.ReadDouble() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if math.IsInf(value, 1) { if !math.IsInf(v, 1) { t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v) } } else if math.IsInf(value, -1) { if !math.IsInf(v, -1) { t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v) } } else if math.IsNaN(value) { if !math.IsNaN(v) { t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v) } } else { if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } } trans.Reset() trans.Close() } } func TestWriteJSONProtocolString(t *testing.T) { thetype := "string" trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) for _, value := range STRING_VALUES { if e := p.WriteString(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if s[0] != '"' || s[len(s)-1] != '"' { t.Fatalf("Bad value for %s '%v', wrote '%v', expected: %v", thetype, value, s, fmt.Sprint("\"", value, "\"")) } v := new(string) if err := json.Unmarshal([]byte(s), v); err != nil || *v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v) } trans.Reset() } trans.Close() } func TestReadJSONProtocolString(t *testing.T) { thetype := "string" for _, value := range STRING_VALUES { trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) trans.WriteString(jsonQuote(value)) trans.Flush() s := trans.String() v, e := p.ReadString() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } v1 := new(string) if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1) } trans.Reset() trans.Close() } } func TestWriteJSONProtocolBinary(t *testing.T) { thetype := "binary" value := protocol_bdata b64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata))) base64.StdEncoding.Encode(b64value, value) b64String := string(b64value) trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) if e := p.WriteBinary(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() expectedString := fmt.Sprint("\"", b64String, "\"") if s != expectedString { t.Fatalf("Bad value for %s %v\n wrote: \"%v\"\nexpected: \"%v\"", thetype, value, s, expectedString) } v1, err := p.ReadBinary() if err != nil { t.Fatalf("Unable to read binary: %s", err.Error()) } if len(v1) != len(value) { t.Fatalf("Invalid value for binary\nexpected: \"%v\"\n read: \"%v\"", value, v1) } for k, v := range value { if v1[k] != v { t.Fatalf("Invalid value for binary at %v\nexpected: \"%v\"\n read: \"%v\"", k, v, v1[k]) } } trans.Close() } func TestReadJSONProtocolBinary(t *testing.T) { thetype := "binary" value := protocol_bdata b64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata))) base64.StdEncoding.Encode(b64value, value) b64String := string(b64value) trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) trans.WriteString(jsonQuote(b64String)) trans.Flush() s := trans.String() v, e := p.ReadBinary() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if len(v) != len(value) { t.Fatalf("Bad value for %s value length %v, wrote: %v, received length: %v", thetype, len(value), s, len(v)) } for i := 0; i < len(v); i++ { if v[i] != value[i] { t.Fatalf("Bad value for %s at index %d value %v, wrote: %v, received: %v", thetype, i, value[i], s, v[i]) } } v1 := new(string) if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != b64String { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1) } trans.Reset() trans.Close() } func TestWriteJSONProtocolList(t *testing.T) { thetype := "list" trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) p.WriteListBegin(TType(DOUBLE), len(DOUBLE_VALUES)) for _, value := range DOUBLE_VALUES { if e := p.WriteDouble(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } } p.WriteListEnd() if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error()) } str := trans.String() str1 := new([]interface{}) err := json.Unmarshal([]byte(str), str1) if err != nil { t.Fatalf("Unable to decode %s, wrote: %s", thetype, str) } l := *str1 if len(l) < 2 { t.Fatalf("List must be at least of length two to include metadata") } if l[0] != "dbl" { t.Fatal("Invalid type for list, expected: ", STRING, ", but was: ", l[0]) } if int(l[1].(float64)) != len(DOUBLE_VALUES) { t.Fatal("Invalid length for list, expected: ", len(DOUBLE_VALUES), ", but was: ", l[1]) } for k, value := range DOUBLE_VALUES { s := l[k+2] if math.IsInf(value, 1) { if s.(string) != JSON_INFINITY { t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_INFINITY), str) } } else if math.IsInf(value, 0) { if s.(string) != JSON_NEGATIVE_INFINITY { t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY), str) } } else if math.IsNaN(value) { if s.(string) != JSON_NAN { t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NAN), str) } } else { if s.(float64) != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s'", thetype, value, s) } } trans.Reset() } trans.Close() } func TestWriteJSONProtocolSet(t *testing.T) { thetype := "set" trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) p.WriteSetBegin(TType(DOUBLE), len(DOUBLE_VALUES)) for _, value := range DOUBLE_VALUES { if e := p.WriteDouble(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } } p.WriteSetEnd() if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error()) } str := trans.String() str1 := new([]interface{}) err := json.Unmarshal([]byte(str), str1) if err != nil { t.Fatalf("Unable to decode %s, wrote: %s", thetype, str) } l := *str1 if len(l) < 2 { t.Fatalf("Set must be at least of length two to include metadata") } if l[0] != "dbl" { t.Fatal("Invalid type for set, expected: ", DOUBLE, ", but was: ", l[0]) } if int(l[1].(float64)) != len(DOUBLE_VALUES) { t.Fatal("Invalid length for set, expected: ", len(DOUBLE_VALUES), ", but was: ", l[1]) } for k, value := range DOUBLE_VALUES { s := l[k+2] if math.IsInf(value, 1) { if s.(string) != JSON_INFINITY { t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_INFINITY), str) } } else if math.IsInf(value, 0) { if s.(string) != JSON_NEGATIVE_INFINITY { t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY), str) } } else if math.IsNaN(value) { if s.(string) != JSON_NAN { t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NAN), str) } } else { if s.(float64) != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s'", thetype, value, s) } } trans.Reset() } trans.Close() } func TestWriteJSONProtocolMap(t *testing.T) { thetype := "map" trans := NewTMemoryBuffer() p := NewTJSONProtocol(trans) p.WriteMapBegin(TType(I32), TType(DOUBLE), len(DOUBLE_VALUES)) for k, value := range DOUBLE_VALUES { if e := p.WriteI32(int32(k)); e != nil { t.Fatalf("Unable to write %s key int32 value %v due to error: %s", thetype, k, e.Error()) } if e := p.WriteDouble(value); e != nil { t.Fatalf("Unable to write %s value float64 value %v due to error: %s", thetype, value, e.Error()) } } p.WriteMapEnd() if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error()) } str := trans.String() if str[0] != '[' || str[len(str)-1] != ']' { t.Fatalf("Bad value for %s, wrote: %q, in go: %v", thetype, str, DOUBLE_VALUES) } expectedKeyType, expectedValueType, expectedSize, err := p.ReadMapBegin() if err != nil { t.Fatalf("Error while reading map begin: %s", err.Error()) } if expectedKeyType != I32 { t.Fatal("Expected map key type ", I32, ", but was ", expectedKeyType) } if expectedValueType != DOUBLE { t.Fatal("Expected map value type ", DOUBLE, ", but was ", expectedValueType) } if expectedSize != len(DOUBLE_VALUES) { t.Fatal("Expected map size of ", len(DOUBLE_VALUES), ", but was ", expectedSize) } for k, value := range DOUBLE_VALUES { ik, err := p.ReadI32() if err != nil { t.Fatalf("Bad key for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, ik, strconv.Itoa(k), err.Error()) } if int(ik) != k { t.Fatalf("Bad key for %s index %v, wrote: %v, expected: %v", thetype, k, ik, k) } dv, err := p.ReadDouble() if err != nil { t.Fatalf("Bad value for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, dv, value, err.Error()) } s := strconv.FormatFloat(dv, 'g', 10, 64) if math.IsInf(value, 1) { if !math.IsInf(dv, 1) { t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, jsonQuote(JSON_INFINITY)) } } else if math.IsInf(value, 0) { if !math.IsInf(dv, 0) { t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY)) } } else if math.IsNaN(value) { if !math.IsNaN(dv) { t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, jsonQuote(JSON_NAN)) } } else { expected := strconv.FormatFloat(value, 'g', 10, 64) if s != expected { t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected %v", thetype, k, value, s, expected) } v := float64(0) if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } } } err = p.ReadMapEnd() if err != nil { t.Fatalf("Error while reading map end: %s", err.Error()) } trans.Close() } lowlevel_benchmarks_test.go000066400000000000000000000231551456146544700360160ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bytes" "testing" ) var binaryProtoF = NewTBinaryProtocolFactoryDefault() var compactProtoF = NewTCompactProtocolFactory() var buf = bytes.NewBuffer(make([]byte, 0, 1024)) var tfv = []TTransportFactory{ NewTMemoryBufferTransportFactory(1024), NewStreamTransportFactory(buf, buf, true), NewTFramedTransportFactory(NewTMemoryBufferTransportFactory(1024)), } func BenchmarkBinaryBool_0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteBool(b, p, trans) } } func BenchmarkBinaryByte_0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteByte(b, p, trans) } } func BenchmarkBinaryI16_0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI16(b, p, trans) } } func BenchmarkBinaryI32_0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI32(b, p, trans) } } func BenchmarkBinaryI64_0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI64(b, p, trans) } } func BenchmarkBinaryDouble_0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteDouble(b, p, trans) } } func BenchmarkBinaryString_0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteString(b, p, trans) } } func BenchmarkBinaryBinary_0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteBinary(b, p, trans) } } func BenchmarkBinaryBool_1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteBool(b, p, trans) } } func BenchmarkBinaryByte_1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteByte(b, p, trans) } } func BenchmarkBinaryI16_1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI16(b, p, trans) } } func BenchmarkBinaryI32_1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI32(b, p, trans) } } func BenchmarkBinaryI64_1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI64(b, p, trans) } } func BenchmarkBinaryDouble_1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteDouble(b, p, trans) } } func BenchmarkBinaryString_1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteString(b, p, trans) } } func BenchmarkBinaryBinary_1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteBinary(b, p, trans) } } func BenchmarkBinaryBool_2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteBool(b, p, trans) } } func BenchmarkBinaryByte_2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteByte(b, p, trans) } } func BenchmarkBinaryI16_2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI16(b, p, trans) } } func BenchmarkBinaryI32_2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI32(b, p, trans) } } func BenchmarkBinaryI64_2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI64(b, p, trans) } } func BenchmarkBinaryDouble_2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteDouble(b, p, trans) } } func BenchmarkBinaryString_2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteString(b, p, trans) } } func BenchmarkBinaryBinary_2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := binaryProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteBinary(b, p, trans) } } func BenchmarkCompactBool_0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteBool(b, p, trans) } } func BenchmarkCompactByte_0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteByte(b, p, trans) } } func BenchmarkCompactI16_0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI16(b, p, trans) } } func BenchmarkCompactI32_0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI32(b, p, trans) } } func BenchmarkCompactI64_0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI64(b, p, trans) } } func BenchmarkCompactDouble0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteDouble(b, p, trans) } } func BenchmarkCompactString0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteString(b, p, trans) } } func BenchmarkCompactBinary0(b *testing.B) { trans := tfv[0].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteBinary(b, p, trans) } } func BenchmarkCompactBool_1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteBool(b, p, trans) } } func BenchmarkCompactByte_1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteByte(b, p, trans) } } func BenchmarkCompactI16_1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI16(b, p, trans) } } func BenchmarkCompactI32_1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI32(b, p, trans) } } func BenchmarkCompactI64_1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI64(b, p, trans) } } func BenchmarkCompactDouble1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteDouble(b, p, trans) } } func BenchmarkCompactString1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteString(b, p, trans) } } func BenchmarkCompactBinary1(b *testing.B) { trans := tfv[1].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteBinary(b, p, trans) } } func BenchmarkCompactBool_2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteBool(b, p, trans) } } func BenchmarkCompactByte_2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteByte(b, p, trans) } } func BenchmarkCompactI16_2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI16(b, p, trans) } } func BenchmarkCompactI32_2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI32(b, p, trans) } } func BenchmarkCompactI64_2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteI64(b, p, trans) } } func BenchmarkCompactDouble2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteDouble(b, p, trans) } } func BenchmarkCompactString2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteString(b, p, trans) } } func BenchmarkCompactBinary2(b *testing.B) { trans := tfv[2].GetTransport(nil) p := compactProtoF.GetProtocol(trans) for i := 0; i < b.N; i++ { ReadWriteBinary(b, p, trans) } } memory_buffer.go000066400000000000000000000037511456146544700335720ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bytes" ) // Memory buffer-based implementation of the TTransport interface. type TMemoryBuffer struct { *bytes.Buffer size int } type TMemoryBufferTransportFactory struct { size int } func (p *TMemoryBufferTransportFactory) GetTransport(trans TTransport) TTransport { if trans != nil { t, ok := trans.(*TMemoryBuffer) if ok && t.size > 0 { return NewTMemoryBufferLen(t.size) } } return NewTMemoryBufferLen(p.size) } func NewTMemoryBufferTransportFactory(size int) *TMemoryBufferTransportFactory { return &TMemoryBufferTransportFactory{size: size} } func NewTMemoryBuffer() *TMemoryBuffer { return &TMemoryBuffer{Buffer: &bytes.Buffer{}, size: 0} } func NewTMemoryBufferLen(size int) *TMemoryBuffer { buf := make([]byte, 0, size) return &TMemoryBuffer{Buffer: bytes.NewBuffer(buf), size: size} } func (p *TMemoryBuffer) IsOpen() bool { return true } func (p *TMemoryBuffer) Open() error { return nil } func (p *TMemoryBuffer) Close() error { p.Buffer.Reset() return nil } // Flushing a memory buffer is a no-op func (p *TMemoryBuffer) Flush() error { return nil } func (p *TMemoryBuffer) RemainingBytes() (num_bytes uint64) { return uint64(p.Buffer.Len()) } memory_buffer_test.go000066400000000000000000000016701456146544700346270ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "testing" ) func TestMemoryBuffer(t *testing.T) { trans := NewTMemoryBufferLen(1024) TransportTest(t, trans, trans) } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/messagetype.go000066400000000000000000000021221456146544700333250ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift // Message type constants in the Thrift protocol. type TMessageType int32 const ( INVALID_TMESSAGE_TYPE TMessageType = 0 CALL TMessageType = 1 REPLY TMessageType = 2 EXCEPTION TMessageType = 3 ONEWAY TMessageType = 4 ) multiplexed_protocol.go000066400000000000000000000122071456146544700352020ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "fmt" "strings" ) /* TMultiplexedProtocol is a protocol-independent concrete decorator that allows a Thrift client to communicate with a multiplexing Thrift server, by prepending the service name to the function name during function calls. NOTE: THIS IS NOT USED BY SERVERS. On the server, use TMultiplexedProcessor to handle request from a multiplexing client. This example uses a single socket transport to invoke two services: socket := thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT) transport := thrift.NewTFramedTransport(socket) protocol := thrift.NewTBinaryProtocolTransport(transport) mp := thrift.NewTMultiplexedProtocol(protocol, "Calculator") service := Calculator.NewCalculatorClient(mp) mp2 := thrift.NewTMultiplexedProtocol(protocol, "WeatherReport") service2 := WeatherReport.NewWeatherReportClient(mp2) err := transport.Open() if err != nil { t.Fatal("Unable to open client socket", err) } fmt.Println(service.Add(2,2)) fmt.Println(service2.GetTemperature()) */ type TMultiplexedProtocol struct { TProtocol serviceName string } const MULTIPLEXED_SEPARATOR = ":" func NewTMultiplexedProtocol(protocol TProtocol, serviceName string) *TMultiplexedProtocol { return &TMultiplexedProtocol{ TProtocol: protocol, serviceName: serviceName, } } func (t *TMultiplexedProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error { if typeId == CALL || typeId == ONEWAY { return t.TProtocol.WriteMessageBegin(t.serviceName+MULTIPLEXED_SEPARATOR+name, typeId, seqid) } else { return t.TProtocol.WriteMessageBegin(name, typeId, seqid) } } /* TMultiplexedProcessor is a TProcessor allowing a single TServer to provide multiple services. To do so, you instantiate the processor and then register additional processors with it, as shown in the following example: var processor = thrift.NewTMultiplexedProcessor() firstProcessor := processor.RegisterProcessor("FirstService", firstProcessor) processor.registerProcessor( "Calculator", Calculator.NewCalculatorProcessor(&CalculatorHandler{}), ) processor.registerProcessor( "WeatherReport", WeatherReport.NewWeatherReportProcessor(&WeatherReportHandler{}), ) serverTransport, err := thrift.NewTServerSocketTimeout(addr, TIMEOUT) if err != nil { t.Fatal("Unable to create server socket", err) } server := thrift.NewTSimpleServer2(processor, serverTransport) server.Serve(); */ type TMultiplexedProcessor struct { serviceProcessorMap map[string]TProcessor DefaultProcessor TProcessor } func NewTMultiplexedProcessor() *TMultiplexedProcessor { return &TMultiplexedProcessor{ serviceProcessorMap: make(map[string]TProcessor), } } func (t *TMultiplexedProcessor) RegisterDefault(processor TProcessor) { t.DefaultProcessor = processor } func (t *TMultiplexedProcessor) RegisterProcessor(name string, processor TProcessor) { if t.serviceProcessorMap == nil { t.serviceProcessorMap = make(map[string]TProcessor) } t.serviceProcessorMap[name] = processor } func (t *TMultiplexedProcessor) Process(in, out TProtocol) (bool, TException) { name, typeId, seqid, err := in.ReadMessageBegin() if err != nil { return false, err } if typeId != CALL && typeId != ONEWAY { return false, fmt.Errorf("Unexpected message type %v", typeId) } //extract the service name v := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2) if len(v) != 2 { if t.DefaultProcessor != nil { smb := NewStoredMessageProtocol(in, name, typeId, seqid) return t.DefaultProcessor.Process(smb, out) } return false, fmt.Errorf("Service name not found in message name: %s. Did you forget to use a TMultiplexProtocol in your client?", name) } actualProcessor, ok := t.serviceProcessorMap[v[0]] if !ok { return false, fmt.Errorf("Service name not found: %s. Did you forget to call registerProcessor()?", v[0]) } smb := NewStoredMessageProtocol(in, v[1], typeId, seqid) return actualProcessor.Process(smb, out) } //Protocol that use stored message for ReadMessageBegin type storedMessageProtocol struct { TProtocol name string typeId TMessageType seqid int32 } func NewStoredMessageProtocol(protocol TProtocol, name string, typeId TMessageType, seqid int32) *storedMessageProtocol { return &storedMessageProtocol{protocol, name, typeId, seqid} } func (s *storedMessageProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) { return s.name, s.typeId, s.seqid, nil } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/numeric.go000066400000000000000000000076051456146544700324540ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "math" "strconv" ) type Numeric interface { Int64() int64 Int32() int32 Int16() int16 Byte() byte Int() int Float64() float64 Float32() float32 String() string isNull() bool } type numeric struct { iValue int64 dValue float64 sValue string isNil bool } var ( INFINITY Numeric NEGATIVE_INFINITY Numeric NAN Numeric ZERO Numeric NUMERIC_NULL Numeric ) func NewNumericFromDouble(dValue float64) Numeric { if math.IsInf(dValue, 1) { return INFINITY } if math.IsInf(dValue, -1) { return NEGATIVE_INFINITY } if math.IsNaN(dValue) { return NAN } iValue := int64(dValue) sValue := strconv.FormatFloat(dValue, 'g', 10, 64) isNil := false return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} } func NewNumericFromI64(iValue int64) Numeric { dValue := float64(iValue) sValue := strconv.FormatInt(iValue, 10) isNil := false return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} } func NewNumericFromI32(iValue int32) Numeric { dValue := float64(iValue) sValue := strconv.FormatInt(int64(iValue), 10) isNil := false return &numeric{iValue: int64(iValue), dValue: dValue, sValue: sValue, isNil: isNil} } func NewNumericFromString(sValue string) Numeric { if sValue == INFINITY.String() { return INFINITY } if sValue == NEGATIVE_INFINITY.String() { return NEGATIVE_INFINITY } if sValue == NAN.String() { return NAN } iValue, _ := strconv.ParseInt(sValue, 10, 64) dValue, _ := strconv.ParseFloat(sValue, 64) isNil := len(sValue) == 0 return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} } func NewNumericFromJSONString(sValue string, isNull bool) Numeric { if isNull { return NewNullNumeric() } if sValue == JSON_INFINITY { return INFINITY } if sValue == JSON_NEGATIVE_INFINITY { return NEGATIVE_INFINITY } if sValue == JSON_NAN { return NAN } iValue, _ := strconv.ParseInt(sValue, 10, 64) dValue, _ := strconv.ParseFloat(sValue, 64) return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNull} } func NewNullNumeric() Numeric { return &numeric{iValue: 0, dValue: 0.0, sValue: "", isNil: true} } func (p *numeric) Int64() int64 { return p.iValue } func (p *numeric) Int32() int32 { return int32(p.iValue) } func (p *numeric) Int16() int16 { return int16(p.iValue) } func (p *numeric) Byte() byte { return byte(p.iValue) } func (p *numeric) Int() int { return int(p.iValue) } func (p *numeric) Float64() float64 { return p.dValue } func (p *numeric) Float32() float32 { return float32(p.dValue) } func (p *numeric) String() string { return p.sValue } func (p *numeric) isNull() bool { return p.isNil } func init() { INFINITY = &numeric{iValue: 0, dValue: math.Inf(1), sValue: "Infinity", isNil: false} NEGATIVE_INFINITY = &numeric{iValue: 0, dValue: math.Inf(-1), sValue: "-Infinity", isNil: false} NAN = &numeric{iValue: 0, dValue: math.NaN(), sValue: "NaN", isNil: false} ZERO = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: false} NUMERIC_NULL = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: true} } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/pointerize.go000066400000000000000000000037751456146544700332060ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift /////////////////////////////////////////////////////////////////////////////// // This file is home to helpers that convert from various base types to // respective pointer types. This is necessary because Go does not permit // references to constants, nor can a pointer type to base type be allocated // and initialized in a single expression. // // E.g., this is not allowed: // // var ip *int = &5 // // But this *is* allowed: // // func IntPtr(i int) *int { return &i } // var ip *int = IntPtr(5) // // Since pointers to base types are commonplace as [optional] fields in // exported thrift structs, we factor such helpers here. /////////////////////////////////////////////////////////////////////////////// func Float32Ptr(v float32) *float32 { return &v } func Float64Ptr(v float64) *float64 { return &v } func IntPtr(v int) *int { return &v } func Int32Ptr(v int32) *int32 { return &v } func Int64Ptr(v int64) *int64 { return &v } func StringPtr(v string) *string { return &v } func Uint32Ptr(v uint32) *uint32 { return &v } func Uint64Ptr(v uint64) *uint64 { return &v } func BoolPtr(v bool) *bool { return &v } func ByteSlicePtr(v []byte) *[]byte { return &v } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/processor.go000066400000000000000000000021211456146544700330150ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift // A processor is a generic object which operates upon an input stream and // writes to some output stream. type TProcessor interface { Process(in, out TProtocol) (bool, TException) } type TProcessorFunction interface { Process(seqId int32, in, out TProtocol) (bool, TException) } processor_factory.go000066400000000000000000000033141456146544700344720ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift // The default processor factory just returns a singleton // instance. type TProcessorFactory interface { GetProcessor(trans TTransport) TProcessor } type tProcessorFactory struct { processor TProcessor } func NewTProcessorFactory(p TProcessor) TProcessorFactory { return &tProcessorFactory{processor: p} } func (p *tProcessorFactory) GetProcessor(trans TTransport) TProcessor { return p.processor } /** * The default processor factory just returns a singleton * instance. */ type TProcessorFunctionFactory interface { GetProcessorFunction(trans TTransport) TProcessorFunction } type tProcessorFunctionFactory struct { processor TProcessorFunction } func NewTProcessorFunctionFactory(p TProcessorFunction) TProcessorFunctionFactory { return &tProcessorFunctionFactory{processor: p} } func (p *tProcessorFunctionFactory) GetProcessorFunction(trans TTransport) TProcessorFunction { return p.processor } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/protocol.go000066400000000000000000000107271456146544700326520ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "errors" ) const ( VERSION_MASK = 0xffff0000 VERSION_1 = 0x80010000 ) type TProtocol interface { WriteMessageBegin(name string, typeId TMessageType, seqid int32) error WriteMessageEnd() error WriteStructBegin(name string) error WriteStructEnd() error WriteFieldBegin(name string, typeId TType, id int16) error WriteFieldEnd() error WriteFieldStop() error WriteMapBegin(keyType TType, valueType TType, size int) error WriteMapEnd() error WriteListBegin(elemType TType, size int) error WriteListEnd() error WriteSetBegin(elemType TType, size int) error WriteSetEnd() error WriteBool(value bool) error WriteByte(value int8) error WriteI16(value int16) error WriteI32(value int32) error WriteI64(value int64) error WriteDouble(value float64) error WriteString(value string) error WriteBinary(value []byte) error ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) ReadMessageEnd() error ReadStructBegin() (name string, err error) ReadStructEnd() error ReadFieldBegin() (name string, typeId TType, id int16, err error) ReadFieldEnd() error ReadMapBegin() (keyType TType, valueType TType, size int, err error) ReadMapEnd() error ReadListBegin() (elemType TType, size int, err error) ReadListEnd() error ReadSetBegin() (elemType TType, size int, err error) ReadSetEnd() error ReadBool() (value bool, err error) ReadByte() (value int8, err error) ReadI16() (value int16, err error) ReadI32() (value int32, err error) ReadI64() (value int64, err error) ReadDouble() (value float64, err error) ReadString() (value string, err error) ReadBinary() (value []byte, err error) Skip(fieldType TType) (err error) Flush() (err error) Transport() TTransport } // The maximum recursive depth the skip() function will traverse const DEFAULT_RECURSION_DEPTH = 64 // Skips over the next data element from the provided input TProtocol object. func SkipDefaultDepth(prot TProtocol, typeId TType) (err error) { return Skip(prot, typeId, DEFAULT_RECURSION_DEPTH) } // Skips over the next data element from the provided input TProtocol object. func Skip(self TProtocol, fieldType TType, maxDepth int) (err error) { if maxDepth <= 0 { return NewTProtocolExceptionWithType( DEPTH_LIMIT, errors.New("Depth limit exceeded")) } switch fieldType { case STOP: return case BOOL: _, err = self.ReadBool() return case BYTE: _, err = self.ReadByte() return case I16: _, err = self.ReadI16() return case I32: _, err = self.ReadI32() return case I64: _, err = self.ReadI64() return case DOUBLE: _, err = self.ReadDouble() return case STRING: _, err = self.ReadString() return case STRUCT: if _, err = self.ReadStructBegin(); err != nil { return err } for { _, typeId, _, _ := self.ReadFieldBegin() if typeId == STOP { break } err := Skip(self, typeId, maxDepth-1) if err != nil { return err } self.ReadFieldEnd() } return self.ReadStructEnd() case MAP: keyType, valueType, size, err := self.ReadMapBegin() if err != nil { return err } for i := 0; i < size; i++ { err := Skip(self, keyType, maxDepth-1) if err != nil { return err } self.Skip(valueType) } return self.ReadMapEnd() case SET: elemType, size, err := self.ReadSetBegin() if err != nil { return err } for i := 0; i < size; i++ { err := Skip(self, elemType, maxDepth-1) if err != nil { return err } } return self.ReadSetEnd() case LIST: elemType, size, err := self.ReadListBegin() if err != nil { return err } for i := 0; i < size; i++ { err := Skip(self, elemType, maxDepth-1) if err != nil { return err } } return self.ReadListEnd() } return nil } protocol_exception.go000066400000000000000000000036251456146544700346500ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "encoding/base64" ) // Thrift Protocol exception type TProtocolException interface { TException TypeId() int } const ( UNKNOWN_PROTOCOL_EXCEPTION = 0 INVALID_DATA = 1 NEGATIVE_SIZE = 2 SIZE_LIMIT = 3 BAD_VERSION = 4 NOT_IMPLEMENTED = 5 DEPTH_LIMIT = 6 ) type tProtocolException struct { typeId int message string } func (p *tProtocolException) TypeId() int { return p.typeId } func (p *tProtocolException) String() string { return p.message } func (p *tProtocolException) Error() string { return p.message } func NewTProtocolException(err error) TProtocolException { if err == nil { return nil } if e,ok := err.(TProtocolException); ok { return e } if _, ok := err.(base64.CorruptInputError); ok { return &tProtocolException{INVALID_DATA, err.Error()} } return &tProtocolException{UNKNOWN_PROTOCOL_EXCEPTION, err.Error()} } func NewTProtocolExceptionWithType(errType int, err error) TProtocolException { if err == nil { return nil } return &tProtocolException{errType, err.Error()} } protocol_factory.go000066400000000000000000000016741456146544700343230ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift // Factory interface for constructing protocol instances. type TProtocolFactory interface { GetProtocol(trans TTransport) TProtocol } protocol_test.go000066400000000000000000000345561456146544700336400ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bytes" "io/ioutil" "math" "net" "net/http" "testing" ) const PROTOCOL_BINARY_DATA_SIZE = 155 var ( data string // test data for writing protocol_bdata []byte // test data for writing; same as data BOOL_VALUES []bool BYTE_VALUES []int8 INT16_VALUES []int16 INT32_VALUES []int32 INT64_VALUES []int64 DOUBLE_VALUES []float64 STRING_VALUES []string ) func init() { protocol_bdata = make([]byte, PROTOCOL_BINARY_DATA_SIZE) for i := 0; i < PROTOCOL_BINARY_DATA_SIZE; i++ { protocol_bdata[i] = byte((i + 'a') % 255) } data = string(protocol_bdata) BOOL_VALUES = []bool{false, true, false, false, true} BYTE_VALUES = []int8{117, 0, 1, 32, 127, -128, -1} INT16_VALUES = []int16{459, 0, 1, -1, -128, 127, 32767, -32768} INT32_VALUES = []int32{459, 0, 1, -1, -128, 127, 32767, 2147483647, -2147483535} INT64_VALUES = []int64{459, 0, 1, -1, -128, 127, 32767, 2147483647, -2147483535, 34359738481, -35184372088719, -9223372036854775808, 9223372036854775807} DOUBLE_VALUES = []float64{459.3, 0.0, -1.0, 1.0, 0.5, 0.3333, 3.14159, 1.537e-38, 1.673e25, 6.02214179e23, -6.02214179e23, INFINITY.Float64(), NEGATIVE_INFINITY.Float64(), NAN.Float64()} STRING_VALUES = []string{"", "a", "st[uf]f", "st,u:ff with spaces", "stuff\twith\nescape\\characters'...\"lots{of}fun"} } type HTTPEchoServer struct{} type HTTPHeaderEchoServer struct{} func (p *HTTPEchoServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { buf, err := ioutil.ReadAll(req.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write(buf) } else { w.WriteHeader(http.StatusOK) w.Write(buf) } } func (p *HTTPHeaderEchoServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { buf, err := ioutil.ReadAll(req.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write(buf) } else { w.WriteHeader(http.StatusOK) w.Write(buf) } } func HttpClientSetupForTest(t *testing.T) (net.Listener, net.Addr) { addr, err := FindAvailableTCPServerPort(40000) if err != nil { t.Fatalf("Unable to find available tcp port addr: %s", err) return nil, addr } l, err := net.Listen(addr.Network(), addr.String()) if err != nil { t.Fatalf("Unable to setup tcp listener on %s: %s", addr.String(), err) return l, addr } go http.Serve(l, &HTTPEchoServer{}) return l, addr } func HttpClientSetupForHeaderTest(t *testing.T) (net.Listener, net.Addr) { addr, err := FindAvailableTCPServerPort(40000) if err != nil { t.Fatalf("Unable to find available tcp port addr: %s", err) return nil, addr } l, err := net.Listen(addr.Network(), addr.String()) if err != nil { t.Fatalf("Unable to setup tcp listener on %s: %s", addr.String(), err) return l, addr } go http.Serve(l, &HTTPHeaderEchoServer{}) return l, addr } func ReadWriteProtocolTest(t *testing.T, protocolFactory TProtocolFactory) { buf := bytes.NewBuffer(make([]byte, 0, 1024)) l, addr := HttpClientSetupForTest(t) defer l.Close() transports := []TTransportFactory{ NewTMemoryBufferTransportFactory(1024), NewStreamTransportFactory(buf, buf, true), NewTFramedTransportFactory(NewTMemoryBufferTransportFactory(1024)), NewTHttpPostClientTransportFactory("http://" + addr.String()), } for _, tf := range transports { trans := tf.GetTransport(nil) p := protocolFactory.GetProtocol(trans) ReadWriteBool(t, p, trans) trans.Close() } for _, tf := range transports { trans := tf.GetTransport(nil) p := protocolFactory.GetProtocol(trans) ReadWriteByte(t, p, trans) trans.Close() } for _, tf := range transports { trans := tf.GetTransport(nil) p := protocolFactory.GetProtocol(trans) ReadWriteI16(t, p, trans) trans.Close() } for _, tf := range transports { trans := tf.GetTransport(nil) p := protocolFactory.GetProtocol(trans) ReadWriteI32(t, p, trans) trans.Close() } for _, tf := range transports { trans := tf.GetTransport(nil) p := protocolFactory.GetProtocol(trans) ReadWriteI64(t, p, trans) trans.Close() } for _, tf := range transports { trans := tf.GetTransport(nil) p := protocolFactory.GetProtocol(trans) ReadWriteDouble(t, p, trans) trans.Close() } for _, tf := range transports { trans := tf.GetTransport(nil) p := protocolFactory.GetProtocol(trans) ReadWriteString(t, p, trans) trans.Close() } for _, tf := range transports { trans := tf.GetTransport(nil) p := protocolFactory.GetProtocol(trans) ReadWriteBinary(t, p, trans) trans.Close() } for _, tf := range transports { trans := tf.GetTransport(nil) p := protocolFactory.GetProtocol(trans) ReadWriteI64(t, p, trans) ReadWriteDouble(t, p, trans) ReadWriteBinary(t, p, trans) ReadWriteByte(t, p, trans) trans.Close() } } func ReadWriteBool(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(BOOL) thelen := len(BOOL_VALUES) err := p.WriteListBegin(thetype, thelen) if err != nil { t.Errorf("%s: %T %T %q Error writing list begin: %q", "ReadWriteBool", p, trans, err, thetype) } for k, v := range BOOL_VALUES { err = p.WriteBool(v) if err != nil { t.Errorf("%s: %T %T %q Error writing bool in list at index %d: %v", "ReadWriteBool", p, trans, err, k, v) } } p.WriteListEnd() if err != nil { t.Errorf("%s: %T %T %q Error writing list end: %v", "ReadWriteBool", p, trans, err, BOOL_VALUES) } p.Flush() thetype2, thelen2, err := p.ReadListBegin() if err != nil { t.Errorf("%s: %T %T %q Error reading list: %v", "ReadWriteBool", p, trans, err, BOOL_VALUES) } _, ok := p.(*TSimpleJSONProtocol) if !ok { if thetype != thetype2 { t.Errorf("%s: %T %T type %s != type %s", "ReadWriteBool", p, trans, thetype, thetype2) } if thelen != thelen2 { t.Errorf("%s: %T %T len %d != len %v", "ReadWriteBool", p, trans, thelen, thelen2) } } for k, v := range BOOL_VALUES { value, err := p.ReadBool() if err != nil { t.Errorf("%s: %T %T %q Error reading bool at index %d: %v", "ReadWriteBool", p, trans, err, k, v) } if v != value { t.Errorf("%s: index %d %q %q %v != %v", "ReadWriteBool", k, p, trans, v, value) } } err = p.ReadListEnd() if err != nil { t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteBool", p, trans, err) } } func ReadWriteByte(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(BYTE) thelen := len(BYTE_VALUES) err := p.WriteListBegin(thetype, thelen) if err != nil { t.Errorf("%s: %T %T %q Error writing list begin: %q", "ReadWriteByte", p, trans, err, thetype) } for k, v := range BYTE_VALUES { err = p.WriteByte(v) if err != nil { t.Errorf("%s: %T %T %q Error writing byte in list at index %d: %q", "ReadWriteByte", p, trans, err, k, v) } } err = p.WriteListEnd() if err != nil { t.Errorf("%s: %T %T %q Error writing list end: %q", "ReadWriteByte", p, trans, err, BYTE_VALUES) } err = p.Flush() if err != nil { t.Errorf("%s: %T %T %q Error flushing list of bytes: %q", "ReadWriteByte", p, trans, err, BYTE_VALUES) } thetype2, thelen2, err := p.ReadListBegin() if err != nil { t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteByte", p, trans, err, BYTE_VALUES) } _, ok := p.(*TSimpleJSONProtocol) if !ok { if thetype != thetype2 { t.Errorf("%s: %T %T type %s != type %s", "ReadWriteByte", p, trans, thetype, thetype2) } if thelen != thelen2 { t.Errorf("%s: %T %T len %d != len %d", "ReadWriteByte", p, trans, thelen, thelen2) } } for k, v := range BYTE_VALUES { value, err := p.ReadByte() if err != nil { t.Errorf("%s: %T %T %q Error reading byte at index %d: %q", "ReadWriteByte", p, trans, err, k, v) } if v != value { t.Errorf("%s: %T %T %d != %d", "ReadWriteByte", p, trans, v, value) } } err = p.ReadListEnd() if err != nil { t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteByte", p, trans, err) } } func ReadWriteI16(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(I16) thelen := len(INT16_VALUES) p.WriteListBegin(thetype, thelen) for _, v := range INT16_VALUES { p.WriteI16(v) } p.WriteListEnd() p.Flush() thetype2, thelen2, err := p.ReadListBegin() if err != nil { t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteI16", p, trans, err, INT16_VALUES) } _, ok := p.(*TSimpleJSONProtocol) if !ok { if thetype != thetype2 { t.Errorf("%s: %T %T type %s != type %s", "ReadWriteI16", p, trans, thetype, thetype2) } if thelen != thelen2 { t.Errorf("%s: %T %T len %d != len %d", "ReadWriteI16", p, trans, thelen, thelen2) } } for k, v := range INT16_VALUES { value, err := p.ReadI16() if err != nil { t.Errorf("%s: %T %T %q Error reading int16 at index %d: %q", "ReadWriteI16", p, trans, err, k, v) } if v != value { t.Errorf("%s: %T %T %d != %d", "ReadWriteI16", p, trans, v, value) } } err = p.ReadListEnd() if err != nil { t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteI16", p, trans, err) } } func ReadWriteI32(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(I32) thelen := len(INT32_VALUES) p.WriteListBegin(thetype, thelen) for _, v := range INT32_VALUES { p.WriteI32(v) } p.WriteListEnd() p.Flush() thetype2, thelen2, err := p.ReadListBegin() if err != nil { t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteI32", p, trans, err, INT32_VALUES) } _, ok := p.(*TSimpleJSONProtocol) if !ok { if thetype != thetype2 { t.Errorf("%s: %T %T type %s != type %s", "ReadWriteI32", p, trans, thetype, thetype2) } if thelen != thelen2 { t.Errorf("%s: %T %T len %d != len %d", "ReadWriteI32", p, trans, thelen, thelen2) } } for k, v := range INT32_VALUES { value, err := p.ReadI32() if err != nil { t.Errorf("%s: %T %T %q Error reading int32 at index %d: %q", "ReadWriteI32", p, trans, err, k, v) } if v != value { t.Errorf("%s: %T %T %d != %d", "ReadWriteI32", p, trans, v, value) } } if err != nil { t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteI32", p, trans, err) } } func ReadWriteI64(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(I64) thelen := len(INT64_VALUES) p.WriteListBegin(thetype, thelen) for _, v := range INT64_VALUES { p.WriteI64(v) } p.WriteListEnd() p.Flush() thetype2, thelen2, err := p.ReadListBegin() if err != nil { t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteI64", p, trans, err, INT64_VALUES) } _, ok := p.(*TSimpleJSONProtocol) if !ok { if thetype != thetype2 { t.Errorf("%s: %T %T type %s != type %s", "ReadWriteI64", p, trans, thetype, thetype2) } if thelen != thelen2 { t.Errorf("%s: %T %T len %d != len %d", "ReadWriteI64", p, trans, thelen, thelen2) } } for k, v := range INT64_VALUES { value, err := p.ReadI64() if err != nil { t.Errorf("%s: %T %T %q Error reading int64 at index %d: %q", "ReadWriteI64", p, trans, err, k, v) } if v != value { t.Errorf("%s: %T %T %q != %q", "ReadWriteI64", p, trans, v, value) } } if err != nil { t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteI64", p, trans, err) } } func ReadWriteDouble(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(DOUBLE) thelen := len(DOUBLE_VALUES) p.WriteListBegin(thetype, thelen) for _, v := range DOUBLE_VALUES { p.WriteDouble(v) } p.WriteListEnd() p.Flush() thetype2, thelen2, err := p.ReadListBegin() if err != nil { t.Errorf("%s: %T %T %q Error reading list: %v", "ReadWriteDouble", p, trans, err, DOUBLE_VALUES) } if thetype != thetype2 { t.Errorf("%s: %T %T type %s != type %s", "ReadWriteDouble", p, trans, thetype, thetype2) } if thelen != thelen2 { t.Errorf("%s: %T %T len %d != len %d", "ReadWriteDouble", p, trans, thelen, thelen2) } for k, v := range DOUBLE_VALUES { value, err := p.ReadDouble() if err != nil { t.Errorf("%s: %T %T %q Error reading double at index %d: %v", "ReadWriteDouble", p, trans, err, k, v) } if math.IsNaN(v) { if !math.IsNaN(value) { t.Errorf("%s: %T %T math.IsNaN(%v) != math.IsNaN(%v)", "ReadWriteDouble", p, trans, v, value) } } else if v != value { t.Errorf("%s: %T %T %v != %v", "ReadWriteDouble", p, trans, v, value) } } err = p.ReadListEnd() if err != nil { t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteDouble", p, trans, err) } } func ReadWriteString(t testing.TB, p TProtocol, trans TTransport) { thetype := TType(STRING) thelen := len(STRING_VALUES) p.WriteListBegin(thetype, thelen) for _, v := range STRING_VALUES { p.WriteString(v) } p.WriteListEnd() p.Flush() thetype2, thelen2, err := p.ReadListBegin() if err != nil { t.Errorf("%s: %T %T %q Error reading list: %q", "ReadWriteString", p, trans, err, STRING_VALUES) } _, ok := p.(*TSimpleJSONProtocol) if !ok { if thetype != thetype2 { t.Errorf("%s: %T %T type %s != type %s", "ReadWriteString", p, trans, thetype, thetype2) } if thelen != thelen2 { t.Errorf("%s: %T %T len %d != len %d", "ReadWriteString", p, trans, thelen, thelen2) } } for k, v := range STRING_VALUES { value, err := p.ReadString() if err != nil { t.Errorf("%s: %T %T %q Error reading string at index %d: %q", "ReadWriteString", p, trans, err, k, v) } if v != value { t.Errorf("%s: %T %T %v != %v", "ReadWriteString", p, trans, v, value) } } if err != nil { t.Errorf("%s: %T %T Unable to read list end: %q", "ReadWriteString", p, trans, err) } } func ReadWriteBinary(t testing.TB, p TProtocol, trans TTransport) { v := protocol_bdata p.WriteBinary(v) p.Flush() value, err := p.ReadBinary() if err != nil { t.Errorf("%s: %T %T Unable to read binary: %s", "ReadWriteBinary", p, trans, err.Error()) } if len(v) != len(value) { t.Errorf("%s: %T %T len(v) != len(value)... %d != %d", "ReadWriteBinary", p, trans, len(v), len(value)) } else { for i := 0; i < len(v); i++ { if v[i] != value[i] { t.Errorf("%s: %T %T %s != %s", "ReadWriteBinary", p, trans, v, value) } } } } rich_transport.go000066400000000000000000000033511456146544700337660ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import "io" type RichTransport struct { TTransport } // Wraps Transport to provide TRichTransport interface func NewTRichTransport(trans TTransport) *RichTransport { return &RichTransport{trans} } func (r *RichTransport) ReadByte() (c byte, err error) { return readByte(r.TTransport) } func (r *RichTransport) WriteByte(c byte) error { return writeByte(r.TTransport, c) } func (r *RichTransport) WriteString(s string) (n int, err error) { return r.Write([]byte(s)) } func (r *RichTransport) RemainingBytes() (num_bytes uint64) { return r.TTransport.RemainingBytes() } func readByte(r io.Reader) (c byte, err error) { v := [1]byte{0} n, err := r.Read(v[0:1]) if n > 0 && (err == nil || err == io.EOF) { return v[0], nil } if n > 0 && err != nil { return v[0], err } if err != nil { return 0, err } return v[0], nil } func writeByte(w io.Writer, c byte) error { v := [1]byte{c} _, err := w.Write(v[0:1]) return err } rich_transport_test.go000066400000000000000000000047541456146544700350350ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bytes" "errors" "io" "reflect" "testing" ) func TestEnsureTransportsAreRich(t *testing.T) { buf := bytes.NewBuffer(make([]byte, 0, 1024)) transports := []TTransportFactory{ NewTMemoryBufferTransportFactory(1024), NewStreamTransportFactory(buf, buf, true), NewTFramedTransportFactory(NewTMemoryBufferTransportFactory(1024)), NewTHttpPostClientTransportFactory("http://127.0.0.1"), } for _, tf := range transports { trans := tf.GetTransport(nil) _, ok := trans.(TRichTransport) if !ok { t.Errorf("Transport %s does not implement TRichTransport interface", reflect.ValueOf(trans)) } } } // TestReadByte tests whether readByte handles error cases correctly. func TestReadByte(t *testing.T) { for i, test := range readByteTests { v, err := readByte(test.r) if v != test.v { t.Fatalf("TestReadByte %d: value differs. Expected %d, got %d", i, test.v, test.r.v) } if err != test.err { t.Fatalf("TestReadByte %d: error differs. Expected %s, got %s", i, test.err, test.r.err) } } } var someError = errors.New("Some error") var readByteTests = []struct { r *mockReader v byte err error }{ {&mockReader{0, 55, io.EOF}, 0, io.EOF}, // reader sends EOF w/o data {&mockReader{0, 55, someError}, 0, someError}, // reader sends some other error {&mockReader{1, 55, nil}, 55, nil}, // reader sends data w/o error {&mockReader{1, 55, io.EOF}, 55, nil}, // reader sends data with EOF {&mockReader{1, 55, someError}, 55, someError}, // reader sends data withsome error } type mockReader struct { n int v byte err error } func (r *mockReader) Read(p []byte) (n int, err error) { if r.n > 0 { p[0] = r.v } return r.n, r.err } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/serializer.go000066400000000000000000000033701456146544700331560ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift type TSerializer struct { Transport *TMemoryBuffer Protocol TProtocol } type TStruct interface { Write(p TProtocol) error Read(p TProtocol) error } func NewTSerializer() *TSerializer { transport := NewTMemoryBufferLen(1024) protocol := NewTBinaryProtocolFactoryDefault().GetProtocol(transport) return &TSerializer{ transport, protocol} } func (t *TSerializer) WriteString(msg TStruct) (s string, err error) { t.Transport.Reset() if err = msg.Write(t.Protocol); err != nil { return } if err = t.Protocol.Flush(); err != nil { return } if err = t.Transport.Flush(); err != nil { return } return t.Transport.String(), nil } func (t *TSerializer) Write(msg TStruct) (b []byte, err error) { t.Transport.Reset() if err = msg.Write(t.Protocol); err != nil { return } if err = t.Protocol.Flush(); err != nil { return } if err = t.Transport.Flush(); err != nil { return } b = append(b, t.Transport.Bytes()...) return } serializer_test.go000066400000000000000000000111271456146544700341350ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "errors" "fmt" "testing" ) type ProtocolFactory interface { GetProtocol(t TTransport) TProtocol } func compareStructs(m, m1 MyTestStruct) (bool, error) { switch { case m.On != m1.On: return false, errors.New("Boolean not equal") case m.B != m1.B: return false, errors.New("Byte not equal") case m.Int16 != m1.Int16: return false, errors.New("Int16 not equal") case m.Int32 != m1.Int32: return false, errors.New("Int32 not equal") case m.Int64 != m1.Int64: return false, errors.New("Int64 not equal") case m.D != m1.D: return false, errors.New("Double not equal") case m.St != m1.St: return false, errors.New("String not equal") case len(m.Bin) != len(m1.Bin): return false, errors.New("Binary size not equal") case len(m.Bin) == len(m1.Bin): for i := range m.Bin { if m.Bin[i] != m1.Bin[i] { return false, errors.New("Binary not equal") } } case len(m.StringMap) != len(m1.StringMap): return false, errors.New("StringMap size not equal") case len(m.StringList) != len(m1.StringList): return false, errors.New("StringList size not equal") case len(m.StringSet) != len(m1.StringSet): return false, errors.New("StringSet size not equal") case m.E != m1.E: return false, errors.New("MyTestEnum not equal") default: return true, nil } return true, nil } func ProtocolTest1(test *testing.T, pf ProtocolFactory) (bool, error) { t := NewTSerializer() t.Protocol = pf.GetProtocol(t.Transport) var m = MyTestStruct{} m.On = true m.B = int8(0) m.Int16 = 1 m.Int32 = 2 m.Int64 = 3 m.D = 4.1 m.St = "Test" m.Bin = make([]byte, 10) m.StringMap = make(map[string]string, 5) m.StringList = make([]string, 5) m.StringSet = make(map[string]struct{}, 5) m.E = 2 s, err := t.WriteString(&m) if err != nil { return false, errors.New(fmt.Sprintf("Unable to Serialize struct\n\t %s", err)) } t1 := NewTDeserializer() t1.Protocol = pf.GetProtocol(t1.Transport) var m1 = MyTestStruct{} if err = t1.ReadString(&m1, s); err != nil { return false, errors.New(fmt.Sprintf("Unable to Deserialize struct\n\t %s", err)) } return compareStructs(m, m1) } func ProtocolTest2(test *testing.T, pf ProtocolFactory) (bool, error) { t := NewTSerializer() t.Protocol = pf.GetProtocol(t.Transport) var m = MyTestStruct{} m.On = false m.B = int8(0) m.Int16 = 1 m.Int32 = 2 m.Int64 = 3 m.D = 4.1 m.St = "Test" m.Bin = make([]byte, 10) m.StringMap = make(map[string]string, 5) m.StringList = make([]string, 5) m.StringSet = make(map[string]struct{}, 5) m.E = 2 s, err := t.WriteString(&m) if err != nil { return false, errors.New(fmt.Sprintf("Unable to Serialize struct\n\t %s", err)) } t1 := NewTDeserializer() t1.Protocol = pf.GetProtocol(t1.Transport) var m1 = MyTestStruct{} if err = t1.ReadString(&m1, s); err != nil { return false, errors.New(fmt.Sprintf("Unable to Deserialize struct\n\t %s", err)) } return compareStructs(m, m1) } func TestSerializer(t *testing.T) { var protocol_factories map[string]ProtocolFactory protocol_factories = make(map[string]ProtocolFactory) protocol_factories["Binary"] = NewTBinaryProtocolFactoryDefault() protocol_factories["Compact"] = NewTCompactProtocolFactory() //protocol_factories["SimpleJSON"] = NewTSimpleJSONProtocolFactory() - write only, can't be read back by design protocol_factories["JSON"] = NewTJSONProtocolFactory() var tests map[string]func(*testing.T, ProtocolFactory) (bool, error) tests = make(map[string]func(*testing.T, ProtocolFactory) (bool, error)) tests["Test 1"] = ProtocolTest1 tests["Test 2"] = ProtocolTest2 //tests["Test 3"] = ProtocolTest3 // Example of how to add additional tests for name, pf := range protocol_factories { for test, f := range tests { if s, err := f(t, pf); !s || err != nil { t.Errorf("%s Failed for %s protocol\n\t %s", test, name, err) } } } } serializer_types_test.go000066400000000000000000000417671456146544700353760ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift // Autogenerated by Thrift Compiler (1.0.0-dev) // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING /* THE FOLLOWING THRIFT FILE WAS USED TO CREATE THIS enum MyTestEnum { FIRST = 1, SECOND = 2, THIRD = 3, FOURTH = 4, } struct MyTestStruct { 1: bool on, 2: byte b, 3: i16 int16, 4: i32 int32, 5: i64 int64, 6: double d, 7: string st, 8: binary bin, 9: map stringMap, 10: list stringList, 11: set stringSet, 12: MyTestEnum e, } */ import ( "fmt" ) // (needed to ensure safety because of naive import list construction.) var _ = ZERO var _ = fmt.Printf var GoUnusedProtection__ int type MyTestEnum int64 const ( MyTestEnum_FIRST MyTestEnum = 1 MyTestEnum_SECOND MyTestEnum = 2 MyTestEnum_THIRD MyTestEnum = 3 MyTestEnum_FOURTH MyTestEnum = 4 ) func (p MyTestEnum) String() string { switch p { case MyTestEnum_FIRST: return "FIRST" case MyTestEnum_SECOND: return "SECOND" case MyTestEnum_THIRD: return "THIRD" case MyTestEnum_FOURTH: return "FOURTH" } return "" } func MyTestEnumFromString(s string) (MyTestEnum, error) { switch s { case "FIRST": return MyTestEnum_FIRST, nil case "SECOND": return MyTestEnum_SECOND, nil case "THIRD": return MyTestEnum_THIRD, nil case "FOURTH": return MyTestEnum_FOURTH, nil } return MyTestEnum(0), fmt.Errorf("not a valid MyTestEnum string") } func MyTestEnumPtr(v MyTestEnum) *MyTestEnum { return &v } type MyTestStruct struct { On bool `thrift:"on,1" json:"on"` B int8 `thrift:"b,2" json:"b"` Int16 int16 `thrift:"int16,3" json:"int16"` Int32 int32 `thrift:"int32,4" json:"int32"` Int64 int64 `thrift:"int64,5" json:"int64"` D float64 `thrift:"d,6" json:"d"` St string `thrift:"st,7" json:"st"` Bin []byte `thrift:"bin,8" json:"bin"` StringMap map[string]string `thrift:"stringMap,9" json:"stringMap"` StringList []string `thrift:"stringList,10" json:"stringList"` StringSet map[string]struct{} `thrift:"stringSet,11" json:"stringSet"` E MyTestEnum `thrift:"e,12" json:"e"` } func NewMyTestStruct() *MyTestStruct { return &MyTestStruct{} } func (p *MyTestStruct) GetOn() bool { return p.On } func (p *MyTestStruct) GetB() int8 { return p.B } func (p *MyTestStruct) GetInt16() int16 { return p.Int16 } func (p *MyTestStruct) GetInt32() int32 { return p.Int32 } func (p *MyTestStruct) GetInt64() int64 { return p.Int64 } func (p *MyTestStruct) GetD() float64 { return p.D } func (p *MyTestStruct) GetSt() string { return p.St } func (p *MyTestStruct) GetBin() []byte { return p.Bin } func (p *MyTestStruct) GetStringMap() map[string]string { return p.StringMap } func (p *MyTestStruct) GetStringList() []string { return p.StringList } func (p *MyTestStruct) GetStringSet() map[string]struct{} { return p.StringSet } func (p *MyTestStruct) GetE() MyTestEnum { return p.E } func (p *MyTestStruct) Read(iprot TProtocol) error { if _, err := iprot.ReadStructBegin(); err != nil { return PrependError(fmt.Sprintf("%T read error: ", p), err) } for { _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() if err != nil { return PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) } if fieldTypeId == STOP { break } switch fieldId { case 1: if err := p.readField1(iprot); err != nil { return err } case 2: if err := p.readField2(iprot); err != nil { return err } case 3: if err := p.readField3(iprot); err != nil { return err } case 4: if err := p.readField4(iprot); err != nil { return err } case 5: if err := p.readField5(iprot); err != nil { return err } case 6: if err := p.readField6(iprot); err != nil { return err } case 7: if err := p.readField7(iprot); err != nil { return err } case 8: if err := p.readField8(iprot); err != nil { return err } case 9: if err := p.readField9(iprot); err != nil { return err } case 10: if err := p.readField10(iprot); err != nil { return err } case 11: if err := p.readField11(iprot); err != nil { return err } case 12: if err := p.readField12(iprot); err != nil { return err } default: if err := iprot.Skip(fieldTypeId); err != nil { return err } } if err := iprot.ReadFieldEnd(); err != nil { return err } } if err := iprot.ReadStructEnd(); err != nil { return PrependError(fmt.Sprintf("%T read struct end error: ", p), err) } return nil } func (p *MyTestStruct) readField1(iprot TProtocol) error { if v, err := iprot.ReadBool(); err != nil { return PrependError("error reading field 1: ", err) } else { p.On = v } return nil } func (p *MyTestStruct) readField2(iprot TProtocol) error { if v, err := iprot.ReadByte(); err != nil { return PrependError("error reading field 2: ", err) } else { temp := int8(v) p.B = temp } return nil } func (p *MyTestStruct) readField3(iprot TProtocol) error { if v, err := iprot.ReadI16(); err != nil { return PrependError("error reading field 3: ", err) } else { p.Int16 = v } return nil } func (p *MyTestStruct) readField4(iprot TProtocol) error { if v, err := iprot.ReadI32(); err != nil { return PrependError("error reading field 4: ", err) } else { p.Int32 = v } return nil } func (p *MyTestStruct) readField5(iprot TProtocol) error { if v, err := iprot.ReadI64(); err != nil { return PrependError("error reading field 5: ", err) } else { p.Int64 = v } return nil } func (p *MyTestStruct) readField6(iprot TProtocol) error { if v, err := iprot.ReadDouble(); err != nil { return PrependError("error reading field 6: ", err) } else { p.D = v } return nil } func (p *MyTestStruct) readField7(iprot TProtocol) error { if v, err := iprot.ReadString(); err != nil { return PrependError("error reading field 7: ", err) } else { p.St = v } return nil } func (p *MyTestStruct) readField8(iprot TProtocol) error { if v, err := iprot.ReadBinary(); err != nil { return PrependError("error reading field 8: ", err) } else { p.Bin = v } return nil } func (p *MyTestStruct) readField9(iprot TProtocol) error { _, _, size, err := iprot.ReadMapBegin() if err != nil { return PrependError("error reading map begin: ", err) } tMap := make(map[string]string, size) p.StringMap = tMap for i := 0; i < size; i++ { var _key0 string if v, err := iprot.ReadString(); err != nil { return PrependError("error reading field 0: ", err) } else { _key0 = v } var _val1 string if v, err := iprot.ReadString(); err != nil { return PrependError("error reading field 0: ", err) } else { _val1 = v } p.StringMap[_key0] = _val1 } if err := iprot.ReadMapEnd(); err != nil { return PrependError("error reading map end: ", err) } return nil } func (p *MyTestStruct) readField10(iprot TProtocol) error { _, size, err := iprot.ReadListBegin() if err != nil { return PrependError("error reading list begin: ", err) } tSlice := make([]string, 0, size) p.StringList = tSlice for i := 0; i < size; i++ { var _elem2 string if v, err := iprot.ReadString(); err != nil { return PrependError("error reading field 0: ", err) } else { _elem2 = v } p.StringList = append(p.StringList, _elem2) } if err := iprot.ReadListEnd(); err != nil { return PrependError("error reading list end: ", err) } return nil } func (p *MyTestStruct) readField11(iprot TProtocol) error { _, size, err := iprot.ReadSetBegin() if err != nil { return PrependError("error reading set begin: ", err) } tSet := make(map[string]struct{}, size) p.StringSet = tSet for i := 0; i < size; i++ { var _elem3 string if v, err := iprot.ReadString(); err != nil { return PrependError("error reading field 0: ", err) } else { _elem3 = v } p.StringSet[_elem3] = struct{}{} } if err := iprot.ReadSetEnd(); err != nil { return PrependError("error reading set end: ", err) } return nil } func (p *MyTestStruct) readField12(iprot TProtocol) error { if v, err := iprot.ReadI32(); err != nil { return PrependError("error reading field 12: ", err) } else { temp := MyTestEnum(v) p.E = temp } return nil } func (p *MyTestStruct) Write(oprot TProtocol) error { if err := oprot.WriteStructBegin("MyTestStruct"); err != nil { return PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } if err := p.writeField1(oprot); err != nil { return err } if err := p.writeField2(oprot); err != nil { return err } if err := p.writeField3(oprot); err != nil { return err } if err := p.writeField4(oprot); err != nil { return err } if err := p.writeField5(oprot); err != nil { return err } if err := p.writeField6(oprot); err != nil { return err } if err := p.writeField7(oprot); err != nil { return err } if err := p.writeField8(oprot); err != nil { return err } if err := p.writeField9(oprot); err != nil { return err } if err := p.writeField10(oprot); err != nil { return err } if err := p.writeField11(oprot); err != nil { return err } if err := p.writeField12(oprot); err != nil { return err } if err := oprot.WriteFieldStop(); err != nil { return PrependError("write field stop error: ", err) } if err := oprot.WriteStructEnd(); err != nil { return PrependError("write struct stop error: ", err) } return nil } func (p *MyTestStruct) writeField1(oprot TProtocol) (err error) { if err := oprot.WriteFieldBegin("on", BOOL, 1); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 1:on: ", p), err) } if err := oprot.WriteBool(bool(p.On)); err != nil { return PrependError(fmt.Sprintf("%T.on (1) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return PrependError(fmt.Sprintf("%T write field end error 1:on: ", p), err) } return err } func (p *MyTestStruct) writeField2(oprot TProtocol) (err error) { if err := oprot.WriteFieldBegin("b", BYTE, 2); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 2:b: ", p), err) } if err := oprot.WriteByte(int8(p.B)); err != nil { return PrependError(fmt.Sprintf("%T.b (2) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return PrependError(fmt.Sprintf("%T write field end error 2:b: ", p), err) } return err } func (p *MyTestStruct) writeField3(oprot TProtocol) (err error) { if err := oprot.WriteFieldBegin("int16", I16, 3); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 3:int16: ", p), err) } if err := oprot.WriteI16(int16(p.Int16)); err != nil { return PrependError(fmt.Sprintf("%T.int16 (3) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return PrependError(fmt.Sprintf("%T write field end error 3:int16: ", p), err) } return err } func (p *MyTestStruct) writeField4(oprot TProtocol) (err error) { if err := oprot.WriteFieldBegin("int32", I32, 4); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 4:int32: ", p), err) } if err := oprot.WriteI32(int32(p.Int32)); err != nil { return PrependError(fmt.Sprintf("%T.int32 (4) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return PrependError(fmt.Sprintf("%T write field end error 4:int32: ", p), err) } return err } func (p *MyTestStruct) writeField5(oprot TProtocol) (err error) { if err := oprot.WriteFieldBegin("int64", I64, 5); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 5:int64: ", p), err) } if err := oprot.WriteI64(int64(p.Int64)); err != nil { return PrependError(fmt.Sprintf("%T.int64 (5) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return PrependError(fmt.Sprintf("%T write field end error 5:int64: ", p), err) } return err } func (p *MyTestStruct) writeField6(oprot TProtocol) (err error) { if err := oprot.WriteFieldBegin("d", DOUBLE, 6); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 6:d: ", p), err) } if err := oprot.WriteDouble(float64(p.D)); err != nil { return PrependError(fmt.Sprintf("%T.d (6) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return PrependError(fmt.Sprintf("%T write field end error 6:d: ", p), err) } return err } func (p *MyTestStruct) writeField7(oprot TProtocol) (err error) { if err := oprot.WriteFieldBegin("st", STRING, 7); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 7:st: ", p), err) } if err := oprot.WriteString(string(p.St)); err != nil { return PrependError(fmt.Sprintf("%T.st (7) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return PrependError(fmt.Sprintf("%T write field end error 7:st: ", p), err) } return err } func (p *MyTestStruct) writeField8(oprot TProtocol) (err error) { if err := oprot.WriteFieldBegin("bin", STRING, 8); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 8:bin: ", p), err) } if err := oprot.WriteBinary(p.Bin); err != nil { return PrependError(fmt.Sprintf("%T.bin (8) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return PrependError(fmt.Sprintf("%T write field end error 8:bin: ", p), err) } return err } func (p *MyTestStruct) writeField9(oprot TProtocol) (err error) { if err := oprot.WriteFieldBegin("stringMap", MAP, 9); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 9:stringMap: ", p), err) } if err := oprot.WriteMapBegin(STRING, STRING, len(p.StringMap)); err != nil { return PrependError("error writing map begin: ", err) } for k, v := range p.StringMap { if err := oprot.WriteString(string(k)); err != nil { return PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err) } if err := oprot.WriteString(string(v)); err != nil { return PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err) } } if err := oprot.WriteMapEnd(); err != nil { return PrependError("error writing map end: ", err) } if err := oprot.WriteFieldEnd(); err != nil { return PrependError(fmt.Sprintf("%T write field end error 9:stringMap: ", p), err) } return err } func (p *MyTestStruct) writeField10(oprot TProtocol) (err error) { if err := oprot.WriteFieldBegin("stringList", LIST, 10); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 10:stringList: ", p), err) } if err := oprot.WriteListBegin(STRING, len(p.StringList)); err != nil { return PrependError("error writing list begin: ", err) } for _, v := range p.StringList { if err := oprot.WriteString(string(v)); err != nil { return PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err) } } if err := oprot.WriteListEnd(); err != nil { return PrependError("error writing list end: ", err) } if err := oprot.WriteFieldEnd(); err != nil { return PrependError(fmt.Sprintf("%T write field end error 10:stringList: ", p), err) } return err } func (p *MyTestStruct) writeField11(oprot TProtocol) (err error) { if err := oprot.WriteFieldBegin("stringSet", SET, 11); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 11:stringSet: ", p), err) } if err := oprot.WriteSetBegin(STRING, len(p.StringSet)); err != nil { return PrependError("error writing set begin: ", err) } for v, _ := range p.StringSet { if err := oprot.WriteString(string(v)); err != nil { return PrependError(fmt.Sprintf("%T. (0) field write error: ", p), err) } } if err := oprot.WriteSetEnd(); err != nil { return PrependError("error writing set end: ", err) } if err := oprot.WriteFieldEnd(); err != nil { return PrependError(fmt.Sprintf("%T write field end error 11:stringSet: ", p), err) } return err } func (p *MyTestStruct) writeField12(oprot TProtocol) (err error) { if err := oprot.WriteFieldBegin("e", I32, 12); err != nil { return PrependError(fmt.Sprintf("%T write field begin error 12:e: ", p), err) } if err := oprot.WriteI32(int32(p.E)); err != nil { return PrependError(fmt.Sprintf("%T.e (12) field write error: ", p), err) } if err := oprot.WriteFieldEnd(); err != nil { return PrependError(fmt.Sprintf("%T write field end error 12:e: ", p), err) } return err } func (p *MyTestStruct) String() string { if p == nil { return "" } return fmt.Sprintf("MyTestStruct(%+v)", *p) } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/server.go000066400000000000000000000023701456146544700323120ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift type TServer interface { ProcessorFactory() TProcessorFactory ServerTransport() TServerTransport InputTransportFactory() TTransportFactory OutputTransportFactory() TTransportFactory InputProtocolFactory() TProtocolFactory OutputProtocolFactory() TProtocolFactory // Starts the server Serve() error // Stops the server. This is optional on a per-implementation basis. Not // all servers are required to be cleanly stoppable. Stop() error } server_socket.go000066400000000000000000000055771456146544700336170ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "net" "sync" "time" ) type TServerSocket struct { listener net.Listener addr net.Addr clientTimeout time.Duration // Protects the interrupted value to make it thread safe. mu sync.RWMutex interrupted bool } func NewTServerSocket(listenAddr string) (*TServerSocket, error) { return NewTServerSocketTimeout(listenAddr, 0) } func NewTServerSocketTimeout(listenAddr string, clientTimeout time.Duration) (*TServerSocket, error) { addr, err := net.ResolveTCPAddr("tcp", listenAddr) if err != nil { return nil, err } return &TServerSocket{addr: addr, clientTimeout: clientTimeout}, nil } func (p *TServerSocket) Listen() error { if p.IsListening() { return nil } l, err := net.Listen(p.addr.Network(), p.addr.String()) if err != nil { return err } p.listener = l return nil } func (p *TServerSocket) Accept() (TTransport, error) { p.mu.RLock() interrupted := p.interrupted p.mu.RUnlock() if interrupted { return nil, errTransportInterrupted } if p.listener == nil { return nil, NewTTransportException(NOT_OPEN, "No underlying server socket") } conn, err := p.listener.Accept() if err != nil { return nil, NewTTransportExceptionFromError(err) } return NewTSocketFromConnTimeout(conn, p.clientTimeout), nil } // Checks whether the socket is listening. func (p *TServerSocket) IsListening() bool { return p.listener != nil } // Connects the socket, creating a new socket object if necessary. func (p *TServerSocket) Open() error { if p.IsListening() { return NewTTransportException(ALREADY_OPEN, "Server socket already open") } if l, err := net.Listen(p.addr.Network(), p.addr.String()); err != nil { return err } else { p.listener = l } return nil } func (p *TServerSocket) Addr() net.Addr { if p.listener != nil { return p.listener.Addr() } return p.addr } func (p *TServerSocket) Close() error { defer func() { p.listener = nil }() if p.IsListening() { return p.listener.Close() } return nil } func (p *TServerSocket) Interrupt() error { p.mu.Lock() p.interrupted = true p.Close() p.mu.Unlock() return nil } server_socket_test.go000066400000000000000000000026211456146544700346410ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "fmt" "testing" ) func TestSocketIsntListeningAfterInterrupt(t *testing.T) { host := "127.0.0.1" port := 9090 addr := fmt.Sprintf("%s:%d", host, port) socket := CreateServerSocket(t, addr) socket.Listen() socket.Interrupt() newSocket := CreateServerSocket(t, addr) err := newSocket.Listen() defer newSocket.Interrupt() if err != nil { t.Fatalf("Failed to rebinds: %s", err) } } func CreateServerSocket(t *testing.T, addr string) *TServerSocket { socket, err := NewTServerSocket(addr) if err != nil { t.Fatalf("Failed to create server socket: %s", err) } return socket } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/server_test.go000066400000000000000000000015601456146544700333510ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "testing" ) func TestNothing(t *testing.T) { } server_transport.go000066400000000000000000000024461456146544700343530ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift // Server transport. Object which provides client transports. type TServerTransport interface { Listen() error Accept() (TTransport, error) Close() error // Optional method implementation. This signals to the server transport // that it should break out of any accept() or listen() that it is currently // blocked on. This method, if implemented, MUST be thread safe, as it may // be called from a different thread context than the other TServerTransport // methods. Interrupt() error } simple_json_protocol.go000066400000000000000000001065101456146544700351710ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "bufio" "bytes" "encoding/base64" "encoding/json" "fmt" "io" "math" "strconv" ) type _ParseContext int const ( _CONTEXT_IN_TOPLEVEL _ParseContext = 1 _CONTEXT_IN_LIST_FIRST _ParseContext = 2 _CONTEXT_IN_LIST _ParseContext = 3 _CONTEXT_IN_OBJECT_FIRST _ParseContext = 4 _CONTEXT_IN_OBJECT_NEXT_KEY _ParseContext = 5 _CONTEXT_IN_OBJECT_NEXT_VALUE _ParseContext = 6 ) func (p _ParseContext) String() string { switch p { case _CONTEXT_IN_TOPLEVEL: return "TOPLEVEL" case _CONTEXT_IN_LIST_FIRST: return "LIST-FIRST" case _CONTEXT_IN_LIST: return "LIST" case _CONTEXT_IN_OBJECT_FIRST: return "OBJECT-FIRST" case _CONTEXT_IN_OBJECT_NEXT_KEY: return "OBJECT-NEXT-KEY" case _CONTEXT_IN_OBJECT_NEXT_VALUE: return "OBJECT-NEXT-VALUE" } return "UNKNOWN-PARSE-CONTEXT" } // JSON protocol implementation for thrift. // // This protocol produces/consumes a simple output format // suitable for parsing by scripting languages. It should not be // confused with the full-featured TJSONProtocol. // type TSimpleJSONProtocol struct { trans TTransport parseContextStack []int dumpContext []int writer *bufio.Writer reader *bufio.Reader } // Constructor func NewTSimpleJSONProtocol(t TTransport) *TSimpleJSONProtocol { v := &TSimpleJSONProtocol{trans: t, writer: bufio.NewWriter(t), reader: bufio.NewReader(t), } v.parseContextStack = append(v.parseContextStack, int(_CONTEXT_IN_TOPLEVEL)) v.dumpContext = append(v.dumpContext, int(_CONTEXT_IN_TOPLEVEL)) return v } // Factory type TSimpleJSONProtocolFactory struct{} func (p *TSimpleJSONProtocolFactory) GetProtocol(trans TTransport) TProtocol { return NewTSimpleJSONProtocol(trans) } func NewTSimpleJSONProtocolFactory() *TSimpleJSONProtocolFactory { return &TSimpleJSONProtocolFactory{} } var ( JSON_COMMA []byte JSON_COLON []byte JSON_LBRACE []byte JSON_RBRACE []byte JSON_LBRACKET []byte JSON_RBRACKET []byte JSON_QUOTE byte JSON_QUOTE_BYTES []byte JSON_NULL []byte JSON_TRUE []byte JSON_FALSE []byte JSON_INFINITY string JSON_NEGATIVE_INFINITY string JSON_NAN string JSON_INFINITY_BYTES []byte JSON_NEGATIVE_INFINITY_BYTES []byte JSON_NAN_BYTES []byte json_nonbase_map_elem_bytes []byte ) func init() { JSON_COMMA = []byte{','} JSON_COLON = []byte{':'} JSON_LBRACE = []byte{'{'} JSON_RBRACE = []byte{'}'} JSON_LBRACKET = []byte{'['} JSON_RBRACKET = []byte{']'} JSON_QUOTE = '"' JSON_QUOTE_BYTES = []byte{'"'} JSON_NULL = []byte{'n', 'u', 'l', 'l'} JSON_TRUE = []byte{'t', 'r', 'u', 'e'} JSON_FALSE = []byte{'f', 'a', 'l', 's', 'e'} JSON_INFINITY = "Infinity" JSON_NEGATIVE_INFINITY = "-Infinity" JSON_NAN = "NaN" JSON_INFINITY_BYTES = []byte{'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'} JSON_NEGATIVE_INFINITY_BYTES = []byte{'-', 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y'} JSON_NAN_BYTES = []byte{'N', 'a', 'N'} json_nonbase_map_elem_bytes = []byte{']', ',', '['} } func jsonQuote(s string) string { b, _ := json.Marshal(s) s1 := string(b) return s1 } func jsonUnquote(s string) (string, bool) { s1 := new(string) err := json.Unmarshal([]byte(s), s1) return *s1, err == nil } func mismatch(expected, actual string) error { return fmt.Errorf("Expected '%s' but found '%s' while parsing JSON.", expected, actual) } func (p *TSimpleJSONProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error { p.resetContextStack() // THRIFT-3735 if e := p.OutputListBegin(); e != nil { return e } if e := p.WriteString(name); e != nil { return e } if e := p.WriteByte(int8(typeId)); e != nil { return e } if e := p.WriteI32(seqId); e != nil { return e } return nil } func (p *TSimpleJSONProtocol) WriteMessageEnd() error { return p.OutputListEnd() } func (p *TSimpleJSONProtocol) WriteStructBegin(name string) error { if e := p.OutputObjectBegin(); e != nil { return e } return nil } func (p *TSimpleJSONProtocol) WriteStructEnd() error { return p.OutputObjectEnd() } func (p *TSimpleJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { if e := p.WriteString(name); e != nil { return e } return nil } func (p *TSimpleJSONProtocol) WriteFieldEnd() error { //return p.OutputListEnd() return nil } func (p *TSimpleJSONProtocol) WriteFieldStop() error { return nil } func (p *TSimpleJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { if e := p.OutputListBegin(); e != nil { return e } if e := p.WriteByte(int8(keyType)); e != nil { return e } if e := p.WriteByte(int8(valueType)); e != nil { return e } return p.WriteI32(int32(size)) } func (p *TSimpleJSONProtocol) WriteMapEnd() error { return p.OutputListEnd() } func (p *TSimpleJSONProtocol) WriteListBegin(elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } func (p *TSimpleJSONProtocol) WriteListEnd() error { return p.OutputListEnd() } func (p *TSimpleJSONProtocol) WriteSetBegin(elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } func (p *TSimpleJSONProtocol) WriteSetEnd() error { return p.OutputListEnd() } func (p *TSimpleJSONProtocol) WriteBool(b bool) error { return p.OutputBool(b) } func (p *TSimpleJSONProtocol) WriteByte(b int8) error { return p.WriteI32(int32(b)) } func (p *TSimpleJSONProtocol) WriteI16(v int16) error { return p.WriteI32(int32(v)) } func (p *TSimpleJSONProtocol) WriteI32(v int32) error { return p.OutputI64(int64(v)) } func (p *TSimpleJSONProtocol) WriteI64(v int64) error { return p.OutputI64(int64(v)) } func (p *TSimpleJSONProtocol) WriteDouble(v float64) error { return p.OutputF64(v) } func (p *TSimpleJSONProtocol) WriteString(v string) error { return p.OutputString(v) } func (p *TSimpleJSONProtocol) WriteBinary(v []byte) error { // JSON library only takes in a string, // not an arbitrary byte array, to ensure bytes are transmitted // efficiently we must convert this into a valid JSON string // therefore we use base64 encoding to avoid excessive escaping/quoting if e := p.OutputPreValue(); e != nil { return e } if _, e := p.write(JSON_QUOTE_BYTES); e != nil { return NewTProtocolException(e) } writer := base64.NewEncoder(base64.StdEncoding, p.writer) if _, e := writer.Write(v); e != nil { p.writer.Reset(p.trans) // THRIFT-3735 return NewTProtocolException(e) } if e := writer.Close(); e != nil { return NewTProtocolException(e) } if _, e := p.write(JSON_QUOTE_BYTES); e != nil { return NewTProtocolException(e) } return p.OutputPostValue() } // Reading methods. func (p *TSimpleJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { p.resetContextStack() // THRIFT-3735 if isNull, err := p.ParseListBegin(); isNull || err != nil { return name, typeId, seqId, err } if name, err = p.ReadString(); err != nil { return name, typeId, seqId, err } bTypeId, err := p.ReadByte() typeId = TMessageType(bTypeId) if err != nil { return name, typeId, seqId, err } if seqId, err = p.ReadI32(); err != nil { return name, typeId, seqId, err } return name, typeId, seqId, nil } func (p *TSimpleJSONProtocol) ReadMessageEnd() error { return p.ParseListEnd() } func (p *TSimpleJSONProtocol) ReadStructBegin() (name string, err error) { _, err = p.ParseObjectStart() return "", err } func (p *TSimpleJSONProtocol) ReadStructEnd() error { return p.ParseObjectEnd() } func (p *TSimpleJSONProtocol) ReadFieldBegin() (string, TType, int16, error) { if err := p.ParsePreValue(); err != nil { return "", STOP, 0, err } b, _ := p.reader.Peek(1) if len(b) > 0 { switch b[0] { case JSON_RBRACE[0]: return "", STOP, 0, nil case JSON_QUOTE: p.reader.ReadByte() name, err := p.ParseStringBody() // simplejson is not meant to be read back into thrift // - see http://wiki.apache.org/thrift/ThriftUsageJava // - use JSON instead if err != nil { return name, STOP, 0, err } return name, STOP, -1, p.ParsePostValue() /* if err = p.ParsePostValue(); err != nil { return name, STOP, 0, err } if isNull, err := p.ParseListBegin(); isNull || err != nil { return name, STOP, 0, err } bType, err := p.ReadByte() thetype := TType(bType) if err != nil { return name, thetype, 0, err } id, err := p.ReadI16() return name, thetype, id, err */ } e := fmt.Errorf("Expected \"}\" or '\"', but found: '%s'", string(b)) return "", STOP, 0, NewTProtocolExceptionWithType(INVALID_DATA, e) } return "", STOP, 0, NewTProtocolException(io.EOF) } func (p *TSimpleJSONProtocol) ReadFieldEnd() error { return nil //return p.ParseListEnd() } func (p *TSimpleJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, e error) { if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, VOID, 0, e } // read keyType bKeyType, e := p.ReadByte() keyType = TType(bKeyType) if e != nil { return keyType, valueType, size, e } // read valueType bValueType, e := p.ReadByte() valueType = TType(bValueType) if e != nil { return keyType, valueType, size, e } // read size iSize, err := p.ReadI64() size = int(iSize) return keyType, valueType, size, err } func (p *TSimpleJSONProtocol) ReadMapEnd() error { return p.ParseListEnd() } func (p *TSimpleJSONProtocol) ReadListBegin() (elemType TType, size int, e error) { return p.ParseElemListBegin() } func (p *TSimpleJSONProtocol) ReadListEnd() error { return p.ParseListEnd() } func (p *TSimpleJSONProtocol) ReadSetBegin() (elemType TType, size int, e error) { return p.ParseElemListBegin() } func (p *TSimpleJSONProtocol) ReadSetEnd() error { return p.ParseListEnd() } func (p *TSimpleJSONProtocol) ReadBool() (bool, error) { var value bool if err := p.ParsePreValue(); err != nil { return value, err } f, _ := p.reader.Peek(1) if len(f) > 0 { switch f[0] { case JSON_TRUE[0]: b := make([]byte, len(JSON_TRUE)) _, err := p.reader.Read(b) if err != nil { return false, NewTProtocolException(err) } if string(b) == string(JSON_TRUE) { value = true } else { e := fmt.Errorf("Expected \"true\" but found: %s", string(b)) return value, NewTProtocolExceptionWithType(INVALID_DATA, e) } break case JSON_FALSE[0]: b := make([]byte, len(JSON_FALSE)) _, err := p.reader.Read(b) if err != nil { return false, NewTProtocolException(err) } if string(b) == string(JSON_FALSE) { value = false } else { e := fmt.Errorf("Expected \"false\" but found: %s", string(b)) return value, NewTProtocolExceptionWithType(INVALID_DATA, e) } break case JSON_NULL[0]: b := make([]byte, len(JSON_NULL)) _, err := p.reader.Read(b) if err != nil { return false, NewTProtocolException(err) } if string(b) == string(JSON_NULL) { value = false } else { e := fmt.Errorf("Expected \"null\" but found: %s", string(b)) return value, NewTProtocolExceptionWithType(INVALID_DATA, e) } default: e := fmt.Errorf("Expected \"true\", \"false\", or \"null\" but found: %s", string(f)) return value, NewTProtocolExceptionWithType(INVALID_DATA, e) } } return value, p.ParsePostValue() } func (p *TSimpleJSONProtocol) ReadByte() (int8, error) { v, err := p.ReadI64() return int8(v), err } func (p *TSimpleJSONProtocol) ReadI16() (int16, error) { v, err := p.ReadI64() return int16(v), err } func (p *TSimpleJSONProtocol) ReadI32() (int32, error) { v, err := p.ReadI64() return int32(v), err } func (p *TSimpleJSONProtocol) ReadI64() (int64, error) { v, _, err := p.ParseI64() return v, err } func (p *TSimpleJSONProtocol) ReadDouble() (float64, error) { v, _, err := p.ParseF64() return v, err } func (p *TSimpleJSONProtocol) ReadString() (string, error) { var v string if err := p.ParsePreValue(); err != nil { return v, err } f, _ := p.reader.Peek(1) if len(f) > 0 && f[0] == JSON_QUOTE { p.reader.ReadByte() value, err := p.ParseStringBody() v = value if err != nil { return v, err } } else if len(f) > 0 && f[0] == JSON_NULL[0] { b := make([]byte, len(JSON_NULL)) _, err := p.reader.Read(b) if err != nil { return v, NewTProtocolException(err) } if string(b) != string(JSON_NULL) { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } } else { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } return v, p.ParsePostValue() } func (p *TSimpleJSONProtocol) ReadBinary() ([]byte, error) { var v []byte if err := p.ParsePreValue(); err != nil { return nil, err } f, _ := p.reader.Peek(1) if len(f) > 0 && f[0] == JSON_QUOTE { p.reader.ReadByte() value, err := p.ParseBase64EncodedBody() v = value if err != nil { return v, err } } else if len(f) > 0 && f[0] == JSON_NULL[0] { b := make([]byte, len(JSON_NULL)) _, err := p.reader.Read(b) if err != nil { return v, NewTProtocolException(err) } if string(b) != string(JSON_NULL) { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } } else { e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f)) return v, NewTProtocolExceptionWithType(INVALID_DATA, e) } return v, p.ParsePostValue() } func (p *TSimpleJSONProtocol) Flush() (err error) { return NewTProtocolException(p.writer.Flush()) } func (p *TSimpleJSONProtocol) Skip(fieldType TType) (err error) { return SkipDefaultDepth(p, fieldType) } func (p *TSimpleJSONProtocol) Transport() TTransport { return p.trans } func (p *TSimpleJSONProtocol) OutputPreValue() error { cxt := _ParseContext(p.dumpContext[len(p.dumpContext)-1]) switch cxt { case _CONTEXT_IN_LIST, _CONTEXT_IN_OBJECT_NEXT_KEY: if _, e := p.write(JSON_COMMA); e != nil { return NewTProtocolException(e) } break case _CONTEXT_IN_OBJECT_NEXT_VALUE: if _, e := p.write(JSON_COLON); e != nil { return NewTProtocolException(e) } break } return nil } func (p *TSimpleJSONProtocol) OutputPostValue() error { cxt := _ParseContext(p.dumpContext[len(p.dumpContext)-1]) switch cxt { case _CONTEXT_IN_LIST_FIRST: p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_LIST)) break case _CONTEXT_IN_OBJECT_FIRST: p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) break case _CONTEXT_IN_OBJECT_NEXT_KEY: p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) break case _CONTEXT_IN_OBJECT_NEXT_VALUE: p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_KEY)) break } return nil } func (p *TSimpleJSONProtocol) OutputBool(value bool) error { if e := p.OutputPreValue(); e != nil { return e } var v string if value { v = string(JSON_TRUE) } else { v = string(JSON_FALSE) } switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) { case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: v = jsonQuote(v) default: } if e := p.OutputStringData(v); e != nil { return e } return p.OutputPostValue() } func (p *TSimpleJSONProtocol) OutputNull() error { if e := p.OutputPreValue(); e != nil { return e } if _, e := p.write(JSON_NULL); e != nil { return NewTProtocolException(e) } return p.OutputPostValue() } func (p *TSimpleJSONProtocol) OutputF64(value float64) error { if e := p.OutputPreValue(); e != nil { return e } var v string if math.IsNaN(value) { v = string(JSON_QUOTE) + JSON_NAN + string(JSON_QUOTE) } else if math.IsInf(value, 1) { v = string(JSON_QUOTE) + JSON_INFINITY + string(JSON_QUOTE) } else if math.IsInf(value, -1) { v = string(JSON_QUOTE) + JSON_NEGATIVE_INFINITY + string(JSON_QUOTE) } else { v = strconv.FormatFloat(value, 'g', -1, 64) switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) { case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: v = string(JSON_QUOTE) + v + string(JSON_QUOTE) default: } } if e := p.OutputStringData(v); e != nil { return e } return p.OutputPostValue() } func (p *TSimpleJSONProtocol) OutputI64(value int64) error { if e := p.OutputPreValue(); e != nil { return e } v := strconv.FormatInt(value, 10) switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) { case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: v = jsonQuote(v) default: } if e := p.OutputStringData(v); e != nil { return e } return p.OutputPostValue() } func (p *TSimpleJSONProtocol) OutputString(s string) error { if e := p.OutputPreValue(); e != nil { return e } if e := p.OutputStringData(jsonQuote(s)); e != nil { return e } return p.OutputPostValue() } func (p *TSimpleJSONProtocol) OutputStringData(s string) error { _, e := p.write([]byte(s)) return NewTProtocolException(e) } func (p *TSimpleJSONProtocol) OutputObjectBegin() error { if e := p.OutputPreValue(); e != nil { return e } if _, e := p.write(JSON_LBRACE); e != nil { return NewTProtocolException(e) } p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_FIRST)) return nil } func (p *TSimpleJSONProtocol) OutputObjectEnd() error { if _, e := p.write(JSON_RBRACE); e != nil { return NewTProtocolException(e) } p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] if e := p.OutputPostValue(); e != nil { return e } return nil } func (p *TSimpleJSONProtocol) OutputListBegin() error { if e := p.OutputPreValue(); e != nil { return e } if _, e := p.write(JSON_LBRACKET); e != nil { return NewTProtocolException(e) } p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_LIST_FIRST)) return nil } func (p *TSimpleJSONProtocol) OutputListEnd() error { if _, e := p.write(JSON_RBRACKET); e != nil { return NewTProtocolException(e) } p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] if e := p.OutputPostValue(); e != nil { return e } return nil } func (p *TSimpleJSONProtocol) OutputElemListBegin(elemType TType, size int) error { if e := p.OutputListBegin(); e != nil { return e } if e := p.WriteByte(int8(elemType)); e != nil { return e } if e := p.WriteI64(int64(size)); e != nil { return e } return nil } func (p *TSimpleJSONProtocol) ParsePreValue() error { if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) b, _ := p.reader.Peek(1) switch cxt { case _CONTEXT_IN_LIST: if len(b) > 0 { switch b[0] { case JSON_RBRACKET[0]: return nil case JSON_COMMA[0]: p.reader.ReadByte() if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } return nil default: e := fmt.Errorf("Expected \"]\" or \",\" in list context, but found \"%s\"", string(b)) return NewTProtocolExceptionWithType(INVALID_DATA, e) } } break case _CONTEXT_IN_OBJECT_NEXT_KEY: if len(b) > 0 { switch b[0] { case JSON_RBRACE[0]: return nil case JSON_COMMA[0]: p.reader.ReadByte() if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } return nil default: e := fmt.Errorf("Expected \"}\" or \",\" in object context, but found \"%s\"", string(b)) return NewTProtocolExceptionWithType(INVALID_DATA, e) } } break case _CONTEXT_IN_OBJECT_NEXT_VALUE: if len(b) > 0 { switch b[0] { case JSON_COLON[0]: p.reader.ReadByte() if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } return nil default: e := fmt.Errorf("Expected \":\" in object context, but found \"%s\"", string(b)) return NewTProtocolExceptionWithType(INVALID_DATA, e) } } break } return nil } func (p *TSimpleJSONProtocol) ParsePostValue() error { if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) switch cxt { case _CONTEXT_IN_LIST_FIRST: p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_LIST)) break case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) break case _CONTEXT_IN_OBJECT_NEXT_VALUE: p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_NEXT_KEY)) break } return nil } func (p *TSimpleJSONProtocol) readNonSignificantWhitespace() error { for { b, _ := p.reader.Peek(1) if len(b) < 1 { return nil } switch b[0] { case ' ', '\r', '\n', '\t': p.reader.ReadByte() continue default: break } break } return nil } func (p *TSimpleJSONProtocol) ParseStringBody() (string, error) { line, err := p.reader.ReadString(JSON_QUOTE) if err != nil { return "", NewTProtocolException(err) } l := len(line) // count number of escapes to see if we need to keep going i := 1 for ; i < l; i++ { if line[l-i-1] != '\\' { break } } if i&0x01 == 1 { v, ok := jsonUnquote(string(JSON_QUOTE) + line) if !ok { return "", NewTProtocolException(err) } return v, nil } s, err := p.ParseQuotedStringBody() if err != nil { return "", NewTProtocolException(err) } str := string(JSON_QUOTE) + line + s v, ok := jsonUnquote(str) if !ok { e := fmt.Errorf("Unable to parse as JSON string %s", str) return "", NewTProtocolExceptionWithType(INVALID_DATA, e) } return v, nil } func (p *TSimpleJSONProtocol) ParseQuotedStringBody() (string, error) { line, err := p.reader.ReadString(JSON_QUOTE) if err != nil { return "", NewTProtocolException(err) } l := len(line) // count number of escapes to see if we need to keep going i := 1 for ; i < l; i++ { if line[l-i-1] != '\\' { break } } if i&0x01 == 1 { return line, nil } s, err := p.ParseQuotedStringBody() if err != nil { return "", NewTProtocolException(err) } v := line + s return v, nil } func (p *TSimpleJSONProtocol) ParseBase64EncodedBody() ([]byte, error) { line, err := p.reader.ReadBytes(JSON_QUOTE) if err != nil { return line, NewTProtocolException(err) } line2 := line[0 : len(line)-1] l := len(line2) if (l % 4) != 0 { pad := 4 - (l % 4) fill := [...]byte{'=', '=', '='} line2 = append(line2, fill[:pad]...) l = len(line2) } output := make([]byte, base64.StdEncoding.DecodedLen(l)) n, err := base64.StdEncoding.Decode(output, line2) return output[0:n], NewTProtocolException(err) } func (p *TSimpleJSONProtocol) ParseI64() (int64, bool, error) { if err := p.ParsePreValue(); err != nil { return 0, false, err } var value int64 var isnull bool if p.safePeekContains(JSON_NULL) { p.reader.Read(make([]byte, len(JSON_NULL))) isnull = true } else { num, err := p.readNumeric() isnull = (num == nil) if !isnull { value = num.Int64() } if err != nil { return value, isnull, err } } return value, isnull, p.ParsePostValue() } func (p *TSimpleJSONProtocol) ParseF64() (float64, bool, error) { if err := p.ParsePreValue(); err != nil { return 0, false, err } var value float64 var isnull bool if p.safePeekContains(JSON_NULL) { p.reader.Read(make([]byte, len(JSON_NULL))) isnull = true } else { num, err := p.readNumeric() isnull = (num == nil) if !isnull { value = num.Float64() } if err != nil { return value, isnull, err } } return value, isnull, p.ParsePostValue() } func (p *TSimpleJSONProtocol) ParseObjectStart() (bool, error) { if err := p.ParsePreValue(); err != nil { return false, err } var b []byte b, err := p.reader.Peek(1) if err != nil { return false, err } if len(b) > 0 && b[0] == JSON_LBRACE[0] { p.reader.ReadByte() p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_FIRST)) return false, nil } else if p.safePeekContains(JSON_NULL) { return true, nil } e := fmt.Errorf("Expected '{' or null, but found '%s'", string(b)) return false, NewTProtocolExceptionWithType(INVALID_DATA, e) } func (p *TSimpleJSONProtocol) ParseObjectEnd() error { if isNull, err := p.readIfNull(); isNull || err != nil { return err } cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) if (cxt != _CONTEXT_IN_OBJECT_FIRST) && (cxt != _CONTEXT_IN_OBJECT_NEXT_KEY) { e := fmt.Errorf("Expected to be in the Object Context, but not in Object Context (%d)", cxt) return NewTProtocolExceptionWithType(INVALID_DATA, e) } line, err := p.reader.ReadString(JSON_RBRACE[0]) if err != nil { return NewTProtocolException(err) } for _, char := range line { switch char { default: e := fmt.Errorf("Expecting end of object \"}\", but found: \"%s\"", line) return NewTProtocolExceptionWithType(INVALID_DATA, e) case ' ', '\n', '\r', '\t', '}': break } } p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] return p.ParsePostValue() } func (p *TSimpleJSONProtocol) ParseListBegin() (isNull bool, err error) { if e := p.ParsePreValue(); e != nil { return false, e } var b []byte b, err = p.reader.Peek(1) if err != nil { return false, err } if len(b) >= 1 && b[0] == JSON_LBRACKET[0] { p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_LIST_FIRST)) p.reader.ReadByte() isNull = false } else if p.safePeekContains(JSON_NULL) { isNull = true } else { err = fmt.Errorf("Expected \"null\" or \"[\", received %q", b) } return isNull, NewTProtocolExceptionWithType(INVALID_DATA, err) } func (p *TSimpleJSONProtocol) ParseElemListBegin() (elemType TType, size int, e error) { if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, 0, e } bElemType, err := p.ReadByte() elemType = TType(bElemType) if err != nil { return elemType, size, err } nSize, err2 := p.ReadI64() size = int(nSize) return elemType, size, err2 } func (p *TSimpleJSONProtocol) ParseListEnd() error { if isNull, err := p.readIfNull(); isNull || err != nil { return err } cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) if cxt != _CONTEXT_IN_LIST { e := fmt.Errorf("Expected to be in the List Context, but not in List Context (%d)", cxt) return NewTProtocolExceptionWithType(INVALID_DATA, e) } line, err := p.reader.ReadString(JSON_RBRACKET[0]) if err != nil { return NewTProtocolException(err) } for _, char := range line { switch char { default: e := fmt.Errorf("Expecting end of list \"]\", but found: \"%s\"", line) return NewTProtocolExceptionWithType(INVALID_DATA, e) case ' ', '\n', '\r', '\t', rune(JSON_RBRACKET[0]): break } } p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] if _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) == _CONTEXT_IN_TOPLEVEL { return nil } return p.ParsePostValue() } func (p *TSimpleJSONProtocol) readSingleValue() (interface{}, TType, error) { e := p.readNonSignificantWhitespace() if e != nil { return nil, VOID, NewTProtocolException(e) } b, e := p.reader.Peek(1) if len(b) > 0 { c := b[0] switch c { case JSON_NULL[0]: buf := make([]byte, len(JSON_NULL)) _, e := p.reader.Read(buf) if e != nil { return nil, VOID, NewTProtocolException(e) } if string(JSON_NULL) != string(buf) { e = mismatch(string(JSON_NULL), string(buf)) return nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e) } return nil, VOID, nil case JSON_QUOTE: p.reader.ReadByte() v, e := p.ParseStringBody() if e != nil { return v, UTF8, NewTProtocolException(e) } if v == JSON_INFINITY { return INFINITY, DOUBLE, nil } else if v == JSON_NEGATIVE_INFINITY { return NEGATIVE_INFINITY, DOUBLE, nil } else if v == JSON_NAN { return NAN, DOUBLE, nil } return v, UTF8, nil case JSON_TRUE[0]: buf := make([]byte, len(JSON_TRUE)) _, e := p.reader.Read(buf) if e != nil { return true, BOOL, NewTProtocolException(e) } if string(JSON_TRUE) != string(buf) { e := mismatch(string(JSON_TRUE), string(buf)) return true, BOOL, NewTProtocolExceptionWithType(INVALID_DATA, e) } return true, BOOL, nil case JSON_FALSE[0]: buf := make([]byte, len(JSON_FALSE)) _, e := p.reader.Read(buf) if e != nil { return false, BOOL, NewTProtocolException(e) } if string(JSON_FALSE) != string(buf) { e := mismatch(string(JSON_FALSE), string(buf)) return false, BOOL, NewTProtocolExceptionWithType(INVALID_DATA, e) } return false, BOOL, nil case JSON_LBRACKET[0]: _, e := p.reader.ReadByte() return make([]interface{}, 0), LIST, NewTProtocolException(e) case JSON_LBRACE[0]: _, e := p.reader.ReadByte() return make(map[string]interface{}), STRUCT, NewTProtocolException(e) case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'e', 'E', '.', '+', '-', JSON_INFINITY[0], JSON_NAN[0]: // assume numeric v, e := p.readNumeric() return v, DOUBLE, e default: e := fmt.Errorf("Expected element in list but found '%s' while parsing JSON.", string(c)) return nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e) } } e = fmt.Errorf("Cannot read a single element while parsing JSON.") return nil, VOID, NewTProtocolExceptionWithType(INVALID_DATA, e) } func (p *TSimpleJSONProtocol) readIfNull() (bool, error) { cont := true for cont { b, _ := p.reader.Peek(1) if len(b) < 1 { return false, nil } switch b[0] { default: return false, nil case JSON_NULL[0]: cont = false break case ' ', '\n', '\r', '\t': p.reader.ReadByte() break } } if p.safePeekContains(JSON_NULL) { p.reader.Read(make([]byte, len(JSON_NULL))) return true, nil } return false, nil } func (p *TSimpleJSONProtocol) readQuoteIfNext() { b, _ := p.reader.Peek(1) if len(b) > 0 && b[0] == JSON_QUOTE { p.reader.ReadByte() } } func (p *TSimpleJSONProtocol) readNumeric() (Numeric, error) { isNull, err := p.readIfNull() if isNull || err != nil { return NUMERIC_NULL, err } hasDecimalPoint := false nextCanBeSign := true hasE := false MAX_LEN := 40 buf := bytes.NewBuffer(make([]byte, 0, MAX_LEN)) continueFor := true inQuotes := false for continueFor { c, err := p.reader.ReadByte() if err != nil { if err == io.EOF { break } return NUMERIC_NULL, NewTProtocolException(err) } switch c { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': buf.WriteByte(c) nextCanBeSign = false case '.': if hasDecimalPoint { e := fmt.Errorf("Unable to parse number with multiple decimal points '%s.'", buf.String()) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } if hasE { e := fmt.Errorf("Unable to parse number with decimal points in the exponent '%s.'", buf.String()) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } buf.WriteByte(c) hasDecimalPoint, nextCanBeSign = true, false case 'e', 'E': if hasE { e := fmt.Errorf("Unable to parse number with multiple exponents '%s%c'", buf.String(), c) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } buf.WriteByte(c) hasE, nextCanBeSign = true, true case '-', '+': if !nextCanBeSign { e := fmt.Errorf("Negative sign within number") return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } buf.WriteByte(c) nextCanBeSign = false case ' ', 0, '\t', '\n', '\r', JSON_RBRACE[0], JSON_RBRACKET[0], JSON_COMMA[0], JSON_COLON[0]: p.reader.UnreadByte() continueFor = false case JSON_NAN[0]: if buf.Len() == 0 { buffer := make([]byte, len(JSON_NAN)) buffer[0] = c _, e := p.reader.Read(buffer[1:]) if e != nil { return NUMERIC_NULL, NewTProtocolException(e) } if JSON_NAN != string(buffer) { e := mismatch(JSON_NAN, string(buffer)) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } if inQuotes { p.readQuoteIfNext() } return NAN, nil } else { e := fmt.Errorf("Unable to parse number starting with character '%c'", c) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } case JSON_INFINITY[0]: if buf.Len() == 0 || (buf.Len() == 1 && buf.Bytes()[0] == '+') { buffer := make([]byte, len(JSON_INFINITY)) buffer[0] = c _, e := p.reader.Read(buffer[1:]) if e != nil { return NUMERIC_NULL, NewTProtocolException(e) } if JSON_INFINITY != string(buffer) { e := mismatch(JSON_INFINITY, string(buffer)) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } if inQuotes { p.readQuoteIfNext() } return INFINITY, nil } else if buf.Len() == 1 && buf.Bytes()[0] == JSON_NEGATIVE_INFINITY[0] { buffer := make([]byte, len(JSON_NEGATIVE_INFINITY)) buffer[0] = JSON_NEGATIVE_INFINITY[0] buffer[1] = c _, e := p.reader.Read(buffer[2:]) if e != nil { return NUMERIC_NULL, NewTProtocolException(e) } if JSON_NEGATIVE_INFINITY != string(buffer) { e := mismatch(JSON_NEGATIVE_INFINITY, string(buffer)) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } if inQuotes { p.readQuoteIfNext() } return NEGATIVE_INFINITY, nil } else { e := fmt.Errorf("Unable to parse number starting with character '%c' due to existing buffer %s", c, buf.String()) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } case JSON_QUOTE: if !inQuotes { inQuotes = true } else { break } default: e := fmt.Errorf("Unable to parse number starting with character '%c'", c) return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } } if buf.Len() == 0 { e := fmt.Errorf("Unable to parse number from empty string ''") return NUMERIC_NULL, NewTProtocolExceptionWithType(INVALID_DATA, e) } return NewNumericFromJSONString(buf.String(), false), nil } // Safely peeks into the buffer, reading only what is necessary func (p *TSimpleJSONProtocol) safePeekContains(b []byte) bool { for i := 0; i < len(b); i++ { a, _ := p.reader.Peek(i + 1) if len(a) == 0 || a[i] != b[i] { return false } } return true } // Reset the context stack to its initial state. func (p *TSimpleJSONProtocol) resetContextStack() { p.parseContextStack = []int{int(_CONTEXT_IN_TOPLEVEL)} p.dumpContext = []int{int(_CONTEXT_IN_TOPLEVEL)} } func (p *TSimpleJSONProtocol) write(b []byte) (int, error) { n, err := p.writer.Write(b) if err != nil { p.writer.Reset(p.trans) // THRIFT-3735 } return n, err } simple_json_protocol_test.go000066400000000000000000000551751456146544700362420ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "encoding/base64" "encoding/json" "fmt" "math" "strconv" "strings" "testing" ) func TestWriteSimpleJSONProtocolBool(t *testing.T) { thetype := "boolean" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range BOOL_VALUES { if e := p.WriteBool(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if s != fmt.Sprint(value) { t.Fatalf("Bad value for %s %v: %s", thetype, value, s) } v := false if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() } trans.Close() } func TestReadSimpleJSONProtocolBool(t *testing.T) { thetype := "boolean" for _, value := range BOOL_VALUES { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) if value { trans.Write(JSON_TRUE) } else { trans.Write(JSON_FALSE) } trans.Flush() s := trans.String() v, e := p.ReadBool() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() trans.Close() } } func TestWriteSimpleJSONProtocolByte(t *testing.T) { thetype := "byte" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range BYTE_VALUES { if e := p.WriteByte(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if s != fmt.Sprint(value) { t.Fatalf("Bad value for %s %v: %s", thetype, value, s) } v := int8(0) if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() } trans.Close() } func TestReadSimpleJSONProtocolByte(t *testing.T) { thetype := "byte" for _, value := range BYTE_VALUES { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) trans.WriteString(strconv.Itoa(int(value))) trans.Flush() s := trans.String() v, e := p.ReadByte() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() trans.Close() } } func TestWriteSimpleJSONProtocolI16(t *testing.T) { thetype := "int16" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range INT16_VALUES { if e := p.WriteI16(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if s != fmt.Sprint(value) { t.Fatalf("Bad value for %s %v: %s", thetype, value, s) } v := int16(0) if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() } trans.Close() } func TestReadSimpleJSONProtocolI16(t *testing.T) { thetype := "int16" for _, value := range INT16_VALUES { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) trans.WriteString(strconv.Itoa(int(value))) trans.Flush() s := trans.String() v, e := p.ReadI16() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() trans.Close() } } func TestWriteSimpleJSONProtocolI32(t *testing.T) { thetype := "int32" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range INT32_VALUES { if e := p.WriteI32(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if s != fmt.Sprint(value) { t.Fatalf("Bad value for %s %v: %s", thetype, value, s) } v := int32(0) if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() } trans.Close() } func TestReadSimpleJSONProtocolI32(t *testing.T) { thetype := "int32" for _, value := range INT32_VALUES { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) trans.WriteString(strconv.Itoa(int(value))) trans.Flush() s := trans.String() v, e := p.ReadI32() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() trans.Close() } } func TestReadSimpleJSONProtocolI32Null(t *testing.T) { thetype := "int32" value := "null" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) trans.WriteString(value) trans.Flush() s := trans.String() v, e := p.ReadI32() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != 0 { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } trans.Reset() trans.Close() } func TestWriteSimpleJSONProtocolI64(t *testing.T) { thetype := "int64" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range INT64_VALUES { if e := p.WriteI64(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if s != fmt.Sprint(value) { t.Fatalf("Bad value for %s %v: %s", thetype, value, s) } v := int64(0) if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() } trans.Close() } func TestReadSimpleJSONProtocolI64(t *testing.T) { thetype := "int64" for _, value := range INT64_VALUES { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) trans.WriteString(strconv.FormatInt(value, 10)) trans.Flush() s := trans.String() v, e := p.ReadI64() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } trans.Reset() trans.Close() } } func TestReadSimpleJSONProtocolI64Null(t *testing.T) { thetype := "int32" value := "null" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) trans.WriteString(value) trans.Flush() s := trans.String() v, e := p.ReadI64() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != 0 { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } trans.Reset() trans.Close() } func TestWriteSimpleJSONProtocolDouble(t *testing.T) { thetype := "double" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range DOUBLE_VALUES { if e := p.WriteDouble(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if math.IsInf(value, 1) { if s != jsonQuote(JSON_INFINITY) { t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, jsonQuote(JSON_INFINITY)) } } else if math.IsInf(value, -1) { if s != jsonQuote(JSON_NEGATIVE_INFINITY) { t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, jsonQuote(JSON_NEGATIVE_INFINITY)) } } else if math.IsNaN(value) { if s != jsonQuote(JSON_NAN) { t.Fatalf("Bad value for %s %v, wrote: %v, expected: %v", thetype, value, s, jsonQuote(JSON_NAN)) } } else { if s != fmt.Sprint(value) { t.Fatalf("Bad value for %s %v: %s", thetype, value, s) } v := float64(0) if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } } trans.Reset() } trans.Close() } func TestReadSimpleJSONProtocolDouble(t *testing.T) { thetype := "double" for _, value := range DOUBLE_VALUES { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) n := NewNumericFromDouble(value) trans.WriteString(n.String()) trans.Flush() s := trans.String() v, e := p.ReadDouble() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if math.IsInf(value, 1) { if !math.IsInf(v, 1) { t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v) } } else if math.IsInf(value, -1) { if !math.IsInf(v, -1) { t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v) } } else if math.IsNaN(value) { if !math.IsNaN(v) { t.Fatalf("Bad value for %s %v, wrote: %v, received: %v", thetype, value, s, v) } } else { if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } } trans.Reset() trans.Close() } } func TestWriteSimpleJSONProtocolString(t *testing.T) { thetype := "string" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) for _, value := range STRING_VALUES { if e := p.WriteString(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if s[0] != '"' || s[len(s)-1] != '"' { t.Fatalf("Bad value for %s '%v', wrote '%v', expected: %v", thetype, value, s, fmt.Sprint("\"", value, "\"")) } v := new(string) if err := json.Unmarshal([]byte(s), v); err != nil || *v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v) } trans.Reset() } trans.Close() } func TestReadSimpleJSONProtocolString(t *testing.T) { thetype := "string" for _, value := range STRING_VALUES { trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) trans.WriteString(jsonQuote(value)) trans.Flush() s := trans.String() v, e := p.ReadString() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != value { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } v1 := new(string) if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1) } trans.Reset() trans.Close() } } func TestReadSimpleJSONProtocolStringNull(t *testing.T) { thetype := "string" value := "null" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) trans.WriteString(value) trans.Flush() s := trans.String() v, e := p.ReadString() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != "" { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } trans.Reset() trans.Close() } func TestWriteSimpleJSONProtocolBinary(t *testing.T) { thetype := "binary" value := protocol_bdata b64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata))) base64.StdEncoding.Encode(b64value, value) b64String := string(b64value) trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) if e := p.WriteBinary(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s value %v due to error flushing: %s", thetype, value, e.Error()) } s := trans.String() if s != fmt.Sprint("\"", b64String, "\"") { t.Fatalf("Bad value for %s %v\n wrote: %v\nexpected: %v", thetype, value, s, "\""+b64String+"\"") } v1 := new(string) if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != b64String { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1) } trans.Close() } func TestReadSimpleJSONProtocolBinary(t *testing.T) { thetype := "binary" value := protocol_bdata b64value := make([]byte, base64.StdEncoding.EncodedLen(len(protocol_bdata))) base64.StdEncoding.Encode(b64value, value) b64String := string(b64value) trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) trans.WriteString(jsonQuote(b64String)) trans.Flush() s := trans.String() v, e := p.ReadBinary() if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if len(v) != len(value) { t.Fatalf("Bad value for %s value length %v, wrote: %v, received length: %v", thetype, len(value), s, len(v)) } for i := 0; i < len(v); i++ { if v[i] != value[i] { t.Fatalf("Bad value for %s at index %d value %v, wrote: %v, received: %v", thetype, i, value[i], s, v[i]) } } v1 := new(string) if err := json.Unmarshal([]byte(s), v1); err != nil || *v1 != b64String { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, *v1) } trans.Reset() trans.Close() } func TestReadSimpleJSONProtocolBinaryNull(t *testing.T) { thetype := "binary" value := "null" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) trans.WriteString(value) trans.Flush() s := trans.String() b, e := p.ReadBinary() v := string(b) if e != nil { t.Fatalf("Unable to read %s value %v due to error: %s", thetype, value, e.Error()) } if v != "" { t.Fatalf("Bad value for %s value %v, wrote: %v, received: %v", thetype, value, s, v) } trans.Reset() trans.Close() } func TestWriteSimpleJSONProtocolList(t *testing.T) { thetype := "list" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) p.WriteListBegin(TType(DOUBLE), len(DOUBLE_VALUES)) for _, value := range DOUBLE_VALUES { if e := p.WriteDouble(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } } p.WriteListEnd() if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error()) } str := trans.String() str1 := new([]interface{}) err := json.Unmarshal([]byte(str), str1) if err != nil { t.Fatalf("Unable to decode %s, wrote: %s", thetype, str) } l := *str1 if len(l) < 2 { t.Fatalf("List must be at least of length two to include metadata") } if int(l[0].(float64)) != DOUBLE { t.Fatal("Invalid type for list, expected: ", DOUBLE, ", but was: ", l[0]) } if int(l[1].(float64)) != len(DOUBLE_VALUES) { t.Fatal("Invalid length for list, expected: ", len(DOUBLE_VALUES), ", but was: ", l[1]) } for k, value := range DOUBLE_VALUES { s := l[k+2] if math.IsInf(value, 1) { if s.(string) != JSON_INFINITY { t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_INFINITY), str) } } else if math.IsInf(value, 0) { if s.(string) != JSON_NEGATIVE_INFINITY { t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY), str) } } else if math.IsNaN(value) { if s.(string) != JSON_NAN { t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NAN), str) } } else { if s.(float64) != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s'", thetype, value, s) } } trans.Reset() } trans.Close() } func TestWriteSimpleJSONProtocolSet(t *testing.T) { thetype := "set" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) p.WriteSetBegin(TType(DOUBLE), len(DOUBLE_VALUES)) for _, value := range DOUBLE_VALUES { if e := p.WriteDouble(value); e != nil { t.Fatalf("Unable to write %s value %v due to error: %s", thetype, value, e.Error()) } } p.WriteSetEnd() if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error()) } str := trans.String() str1 := new([]interface{}) err := json.Unmarshal([]byte(str), str1) if err != nil { t.Fatalf("Unable to decode %s, wrote: %s", thetype, str) } l := *str1 if len(l) < 2 { t.Fatalf("Set must be at least of length two to include metadata") } if int(l[0].(float64)) != DOUBLE { t.Fatal("Invalid type for set, expected: ", DOUBLE, ", but was: ", l[0]) } if int(l[1].(float64)) != len(DOUBLE_VALUES) { t.Fatal("Invalid length for set, expected: ", len(DOUBLE_VALUES), ", but was: ", l[1]) } for k, value := range DOUBLE_VALUES { s := l[k+2] if math.IsInf(value, 1) { if s.(string) != JSON_INFINITY { t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_INFINITY), str) } } else if math.IsInf(value, 0) { if s.(string) != JSON_NEGATIVE_INFINITY { t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY), str) } } else if math.IsNaN(value) { if s.(string) != JSON_NAN { t.Fatalf("Bad value for %s at index %v %v, wrote: %q, expected: %q, originally wrote: %q", thetype, k, value, s, jsonQuote(JSON_NAN), str) } } else { if s.(float64) != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s'", thetype, value, s) } } trans.Reset() } trans.Close() } func TestWriteSimpleJSONProtocolMap(t *testing.T) { thetype := "map" trans := NewTMemoryBuffer() p := NewTSimpleJSONProtocol(trans) p.WriteMapBegin(TType(I32), TType(DOUBLE), len(DOUBLE_VALUES)) for k, value := range DOUBLE_VALUES { if e := p.WriteI32(int32(k)); e != nil { t.Fatalf("Unable to write %s key int32 value %v due to error: %s", thetype, k, e.Error()) } if e := p.WriteDouble(value); e != nil { t.Fatalf("Unable to write %s value float64 value %v due to error: %s", thetype, value, e.Error()) } } p.WriteMapEnd() if e := p.Flush(); e != nil { t.Fatalf("Unable to write %s due to error flushing: %s", thetype, e.Error()) } str := trans.String() if str[0] != '[' || str[len(str)-1] != ']' { t.Fatalf("Bad value for %s, wrote: %q, in go: %v", thetype, str, DOUBLE_VALUES) } l := strings.Split(str[1:len(str)-1], ",") if len(l) < 3 { t.Fatal("Expected list of at least length 3 for map for metadata, but was of length ", len(l)) } expectedKeyType, _ := strconv.Atoi(l[0]) expectedValueType, _ := strconv.Atoi(l[1]) expectedSize, _ := strconv.Atoi(l[2]) if expectedKeyType != I32 { t.Fatal("Expected map key type ", I32, ", but was ", l[0]) } if expectedValueType != DOUBLE { t.Fatal("Expected map value type ", DOUBLE, ", but was ", l[1]) } if expectedSize != len(DOUBLE_VALUES) { t.Fatal("Expected map size of ", len(DOUBLE_VALUES), ", but was ", l[2]) } for k, value := range DOUBLE_VALUES { strk := l[k*2+3] strv := l[k*2+4] ik, err := strconv.Atoi(strk) if err != nil { t.Fatalf("Bad value for %s index %v, wrote: %v, expected: %v, error: %s", thetype, k, strk, strconv.Itoa(k), err.Error()) } if ik != k { t.Fatalf("Bad value for %s index %v, wrote: %v, expected: %v", thetype, k, strk, k) } s := strv if math.IsInf(value, 1) { if s != jsonQuote(JSON_INFINITY) { t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, jsonQuote(JSON_INFINITY)) } } else if math.IsInf(value, 0) { if s != jsonQuote(JSON_NEGATIVE_INFINITY) { t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, jsonQuote(JSON_NEGATIVE_INFINITY)) } } else if math.IsNaN(value) { if s != jsonQuote(JSON_NAN) { t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected: %v", thetype, k, value, s, jsonQuote(JSON_NAN)) } } else { expected := strconv.FormatFloat(value, 'g', 10, 64) if s != expected { t.Fatalf("Bad value for %s at index %v %v, wrote: %v, expected %v", thetype, k, value, s, expected) } v := float64(0) if err := json.Unmarshal([]byte(s), &v); err != nil || v != value { t.Fatalf("Bad json-decoded value for %s %v, wrote: '%s', expected: '%v'", thetype, value, s, v) } } trans.Reset() } trans.Close() } simple_server.go000066400000000000000000000132141456146544700336030ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "log" "runtime/debug" "sync" ) // Simple, non-concurrent server for testing. type TSimpleServer struct { quit chan struct{} processorFactory TProcessorFactory serverTransport TServerTransport inputTransportFactory TTransportFactory outputTransportFactory TTransportFactory inputProtocolFactory TProtocolFactory outputProtocolFactory TProtocolFactory } func NewTSimpleServer2(processor TProcessor, serverTransport TServerTransport) *TSimpleServer { return NewTSimpleServerFactory2(NewTProcessorFactory(processor), serverTransport) } func NewTSimpleServer4(processor TProcessor, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer { return NewTSimpleServerFactory4(NewTProcessorFactory(processor), serverTransport, transportFactory, protocolFactory, ) } func NewTSimpleServer6(processor TProcessor, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer { return NewTSimpleServerFactory6(NewTProcessorFactory(processor), serverTransport, inputTransportFactory, outputTransportFactory, inputProtocolFactory, outputProtocolFactory, ) } func NewTSimpleServerFactory2(processorFactory TProcessorFactory, serverTransport TServerTransport) *TSimpleServer { return NewTSimpleServerFactory6(processorFactory, serverTransport, NewTTransportFactory(), NewTTransportFactory(), NewTBinaryProtocolFactoryDefault(), NewTBinaryProtocolFactoryDefault(), ) } func NewTSimpleServerFactory4(processorFactory TProcessorFactory, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer { return NewTSimpleServerFactory6(processorFactory, serverTransport, transportFactory, transportFactory, protocolFactory, protocolFactory, ) } func NewTSimpleServerFactory6(processorFactory TProcessorFactory, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer { return &TSimpleServer{ processorFactory: processorFactory, serverTransport: serverTransport, inputTransportFactory: inputTransportFactory, outputTransportFactory: outputTransportFactory, inputProtocolFactory: inputProtocolFactory, outputProtocolFactory: outputProtocolFactory, quit: make(chan struct{}, 1), } } func (p *TSimpleServer) ProcessorFactory() TProcessorFactory { return p.processorFactory } func (p *TSimpleServer) ServerTransport() TServerTransport { return p.serverTransport } func (p *TSimpleServer) InputTransportFactory() TTransportFactory { return p.inputTransportFactory } func (p *TSimpleServer) OutputTransportFactory() TTransportFactory { return p.outputTransportFactory } func (p *TSimpleServer) InputProtocolFactory() TProtocolFactory { return p.inputProtocolFactory } func (p *TSimpleServer) OutputProtocolFactory() TProtocolFactory { return p.outputProtocolFactory } func (p *TSimpleServer) Listen() error { return p.serverTransport.Listen() } func (p *TSimpleServer) AcceptLoop() error { for { client, err := p.serverTransport.Accept() if err != nil { select { case <-p.quit: return nil default: } return err } if client != nil { go func() { if err := p.processRequests(client); err != nil { log.Println("error processing request:", err) } }() } } } func (p *TSimpleServer) Serve() error { err := p.Listen() if err != nil { return err } p.AcceptLoop() return nil } var once sync.Once func (p *TSimpleServer) Stop() error { q := func() { p.quit <- struct{}{} p.serverTransport.Interrupt() } once.Do(q) return nil } func (p *TSimpleServer) processRequests(client TTransport) error { processor := p.processorFactory.GetProcessor(client) inputTransport := p.inputTransportFactory.GetTransport(client) outputTransport := p.outputTransportFactory.GetTransport(client) inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport) outputProtocol := p.outputProtocolFactory.GetProtocol(outputTransport) defer func() { if e := recover(); e != nil { log.Printf("panic in processor: %s: %s", e, debug.Stack()) } }() if inputTransport != nil { defer inputTransport.Close() } if outputTransport != nil { defer outputTransport.Close() } for { ok, err := processor.Process(inputProtocol, outputProtocol) if err, ok := err.(TTransportException); ok && err.TypeId() == END_OF_FILE { return nil } else if err != nil { log.Printf("error processing request: %s", err) return err } if err, ok := err.(TApplicationException); ok && err.TypeId() == UNKNOWN_METHOD { continue } if !ok { break } } return nil } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/socket.go000066400000000000000000000103251456146544700322730ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "net" "time" ) type TSocket struct { conn net.Conn addr net.Addr timeout time.Duration } // NewTSocket creates a net.Conn-backed TTransport, given a host and port // // Example: // trans, err := thrift.NewTSocket("localhost:9090") func NewTSocket(hostPort string) (*TSocket, error) { return NewTSocketTimeout(hostPort, 0) } // NewTSocketTimeout creates a net.Conn-backed TTransport, given a host and port // it also accepts a timeout as a time.Duration func NewTSocketTimeout(hostPort string, timeout time.Duration) (*TSocket, error) { //conn, err := net.DialTimeout(network, address, timeout) addr, err := net.ResolveTCPAddr("tcp", hostPort) if err != nil { return nil, err } return NewTSocketFromAddrTimeout(addr, timeout), nil } // Creates a TSocket from a net.Addr func NewTSocketFromAddrTimeout(addr net.Addr, timeout time.Duration) *TSocket { return &TSocket{addr: addr, timeout: timeout} } // Creates a TSocket from an existing net.Conn func NewTSocketFromConnTimeout(conn net.Conn, timeout time.Duration) *TSocket { return &TSocket{conn: conn, addr: conn.RemoteAddr(), timeout: timeout} } // Sets the socket timeout func (p *TSocket) SetTimeout(timeout time.Duration) error { p.timeout = timeout return nil } func (p *TSocket) pushDeadline(read, write bool) { var t time.Time if p.timeout > 0 { t = time.Now().Add(time.Duration(p.timeout)) } if read && write { p.conn.SetDeadline(t) } else if read { p.conn.SetReadDeadline(t) } else if write { p.conn.SetWriteDeadline(t) } } // Connects the socket, creating a new socket object if necessary. func (p *TSocket) Open() error { if p.IsOpen() { return NewTTransportException(ALREADY_OPEN, "Socket already connected.") } if p.addr == nil { return NewTTransportException(NOT_OPEN, "Cannot open nil address.") } if len(p.addr.Network()) == 0 { return NewTTransportException(NOT_OPEN, "Cannot open bad network name.") } if len(p.addr.String()) == 0 { return NewTTransportException(NOT_OPEN, "Cannot open bad address.") } var err error if p.conn, err = net.DialTimeout(p.addr.Network(), p.addr.String(), p.timeout); err != nil { return NewTTransportException(NOT_OPEN, err.Error()) } return nil } // Retrieve the underlying net.Conn func (p *TSocket) Conn() net.Conn { return p.conn } // Returns true if the connection is open func (p *TSocket) IsOpen() bool { if p.conn == nil { return false } return true } // Closes the socket. func (p *TSocket) Close() error { // Close the socket if p.conn != nil { err := p.conn.Close() if err != nil { return err } p.conn = nil } return nil } //Returns the remote address of the socket. func (p *TSocket) Addr() net.Addr { return p.addr } func (p *TSocket) Read(buf []byte) (int, error) { if !p.IsOpen() { return 0, NewTTransportException(NOT_OPEN, "Connection not open") } p.pushDeadline(true, false) n, err := p.conn.Read(buf) return n, NewTTransportExceptionFromError(err) } func (p *TSocket) Write(buf []byte) (int, error) { if !p.IsOpen() { return 0, NewTTransportException(NOT_OPEN, "Connection not open") } p.pushDeadline(false, true) return p.conn.Write(buf) } func (p *TSocket) Flush() error { return nil } func (p *TSocket) Interrupt() error { if !p.IsOpen() { return nil } return p.conn.Close() } func (p *TSocket) RemainingBytes() (num_bytes uint64) { const maxSize = ^uint64(0) return maxSize // the thruth is, we just don't know unless framed is used } ssl_server_socket.go000066400000000000000000000054461456146544700344730ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "net" "time" "crypto/tls" ) type TSSLServerSocket struct { listener net.Listener addr net.Addr clientTimeout time.Duration interrupted bool cfg *tls.Config } func NewTSSLServerSocket(listenAddr string, cfg *tls.Config) (*TSSLServerSocket, error) { return NewTSSLServerSocketTimeout(listenAddr, cfg, 0) } func NewTSSLServerSocketTimeout(listenAddr string, cfg *tls.Config, clientTimeout time.Duration) (*TSSLServerSocket, error) { addr, err := net.ResolveTCPAddr("tcp", listenAddr) if err != nil { return nil, err } return &TSSLServerSocket{addr: addr, clientTimeout: clientTimeout, cfg: cfg}, nil } func (p *TSSLServerSocket) Listen() error { if p.IsListening() { return nil } l, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg) if err != nil { return err } p.listener = l return nil } func (p *TSSLServerSocket) Accept() (TTransport, error) { if p.interrupted { return nil, errTransportInterrupted } if p.listener == nil { return nil, NewTTransportException(NOT_OPEN, "No underlying server socket") } conn, err := p.listener.Accept() if err != nil { return nil, NewTTransportExceptionFromError(err) } return NewTSSLSocketFromConnTimeout(conn, p.cfg, p.clientTimeout), nil } // Checks whether the socket is listening. func (p *TSSLServerSocket) IsListening() bool { return p.listener != nil } // Connects the socket, creating a new socket object if necessary. func (p *TSSLServerSocket) Open() error { if p.IsListening() { return NewTTransportException(ALREADY_OPEN, "Server socket already open") } if l, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg); err != nil { return err } else { p.listener = l } return nil } func (p *TSSLServerSocket) Addr() net.Addr { return p.addr } func (p *TSSLServerSocket) Close() error { defer func() { p.listener = nil }() if p.IsListening() { return p.listener.Close() } return nil } func (p *TSSLServerSocket) Interrupt() error { p.interrupted = true return nil } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/ssl_socket.go000066400000000000000000000113601456146544700331540ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "crypto/tls" "net" "time" ) type TSSLSocket struct { conn net.Conn // hostPort contains host:port (e.g. "asdf.com:12345"). The field is // only valid if addr is nil. hostPort string // addr is nil when hostPort is not "", and is only used when the // TSSLSocket is constructed from a net.Addr. addr net.Addr timeout time.Duration cfg *tls.Config } // NewTSSLSocket creates a net.Conn-backed TTransport, given a host and port and tls Configuration // // Example: // trans, err := thrift.NewTSSLSocket("localhost:9090", nil) func NewTSSLSocket(hostPort string, cfg *tls.Config) (*TSSLSocket, error) { return NewTSSLSocketTimeout(hostPort, cfg, 0) } // NewTSSLSocketTimeout creates a net.Conn-backed TTransport, given a host and port // it also accepts a tls Configuration and a timeout as a time.Duration func NewTSSLSocketTimeout(hostPort string, cfg *tls.Config, timeout time.Duration) (*TSSLSocket, error) { return &TSSLSocket{hostPort: hostPort, timeout: timeout, cfg: cfg}, nil } // Creates a TSSLSocket from a net.Addr func NewTSSLSocketFromAddrTimeout(addr net.Addr, cfg *tls.Config, timeout time.Duration) *TSSLSocket { return &TSSLSocket{addr: addr, timeout: timeout, cfg: cfg} } // Creates a TSSLSocket from an existing net.Conn func NewTSSLSocketFromConnTimeout(conn net.Conn, cfg *tls.Config, timeout time.Duration) *TSSLSocket { return &TSSLSocket{conn: conn, addr: conn.RemoteAddr(), timeout: timeout, cfg: cfg} } // Sets the socket timeout func (p *TSSLSocket) SetTimeout(timeout time.Duration) error { p.timeout = timeout return nil } func (p *TSSLSocket) pushDeadline(read, write bool) { var t time.Time if p.timeout > 0 { t = time.Now().Add(time.Duration(p.timeout)) } if read && write { p.conn.SetDeadline(t) } else if read { p.conn.SetReadDeadline(t) } else if write { p.conn.SetWriteDeadline(t) } } // Connects the socket, creating a new socket object if necessary. func (p *TSSLSocket) Open() error { var err error // If we have a hostname, we need to pass the hostname to tls.Dial for // certificate hostname checks. if p.hostPort != "" { if p.conn, err = tls.Dial("tcp", p.hostPort, p.cfg); err != nil { return NewTTransportException(NOT_OPEN, err.Error()) } } else { if p.IsOpen() { return NewTTransportException(ALREADY_OPEN, "Socket already connected.") } if p.addr == nil { return NewTTransportException(NOT_OPEN, "Cannot open nil address.") } if len(p.addr.Network()) == 0 { return NewTTransportException(NOT_OPEN, "Cannot open bad network name.") } if len(p.addr.String()) == 0 { return NewTTransportException(NOT_OPEN, "Cannot open bad address.") } if p.conn, err = tls.Dial(p.addr.Network(), p.addr.String(), p.cfg); err != nil { return NewTTransportException(NOT_OPEN, err.Error()) } } return nil } // Retrieve the underlying net.Conn func (p *TSSLSocket) Conn() net.Conn { return p.conn } // Returns true if the connection is open func (p *TSSLSocket) IsOpen() bool { if p.conn == nil { return false } return true } // Closes the socket. func (p *TSSLSocket) Close() error { // Close the socket if p.conn != nil { err := p.conn.Close() if err != nil { return err } p.conn = nil } return nil } func (p *TSSLSocket) Read(buf []byte) (int, error) { if !p.IsOpen() { return 0, NewTTransportException(NOT_OPEN, "Connection not open") } p.pushDeadline(true, false) n, err := p.conn.Read(buf) return n, NewTTransportExceptionFromError(err) } func (p *TSSLSocket) Write(buf []byte) (int, error) { if !p.IsOpen() { return 0, NewTTransportException(NOT_OPEN, "Connection not open") } p.pushDeadline(false, true) return p.conn.Write(buf) } func (p *TSSLSocket) Flush() error { return nil } func (p *TSSLSocket) Interrupt() error { if !p.IsOpen() { return nil } return p.conn.Close() } func (p *TSSLSocket) RemainingBytes() (num_bytes uint64) { const maxSize = ^uint64(0) return maxSize // the thruth is, we just don't know unless framed is used } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/transport.go000066400000000000000000000032371456146544700330430ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "errors" "io" ) var errTransportInterrupted = errors.New("Transport Interrupted") type Flusher interface { Flush() (err error) } type ReadSizeProvider interface { RemainingBytes() (num_bytes uint64) } // Encapsulates the I/O layer type TTransport interface { io.ReadWriteCloser Flusher ReadSizeProvider // Opens the transport for communication Open() error // Returns true if the transport is open IsOpen() bool } type stringWriter interface { WriteString(s string) (n int, err error) } // This is "enchanced" transport with extra capabilities. You need to use one of these // to construct protocol. // Notably, TSocket does not implement this interface, and it is always a mistake to use // TSocket directly in protocol. type TRichTransport interface { io.ReadWriter io.ByteReader io.ByteWriter stringWriter Flusher ReadSizeProvider } transport_exception.go000066400000000000000000000040061456146544700350350ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "errors" "io" ) type timeoutable interface { Timeout() bool } // Thrift Transport exception type TTransportException interface { TException TypeId() int Err() error } const ( UNKNOWN_TRANSPORT_EXCEPTION = 0 NOT_OPEN = 1 ALREADY_OPEN = 2 TIMED_OUT = 3 END_OF_FILE = 4 ) type tTransportException struct { typeId int err error } func (p *tTransportException) TypeId() int { return p.typeId } func (p *tTransportException) Error() string { return p.err.Error() } func (p *tTransportException) Err() error { return p.err } func NewTTransportException(t int, e string) TTransportException { return &tTransportException{typeId: t, err: errors.New(e)} } func NewTTransportExceptionFromError(e error) TTransportException { if e == nil { return nil } if t, ok := e.(TTransportException); ok { return t } switch v := e.(type) { case TTransportException: return v case timeoutable: if v.Timeout() { return &tTransportException{typeId: TIMED_OUT, err: e} } } if e == io.EOF { return &tTransportException{typeId: END_OF_FILE, err: e} } return &tTransportException{typeId: UNKNOWN_TRANSPORT_EXCEPTION, err: e} } transport_exception_test.go000066400000000000000000000034151456146544700360770ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "fmt" "io" "testing" ) type timeout struct{ timedout bool } func (t *timeout) Timeout() bool { return t.timedout } func (t *timeout) Error() string { return fmt.Sprintf("Timeout: %v", t.timedout) } func TestTExceptionTimeout(t *testing.T) { timeout := &timeout{true} exception := NewTTransportExceptionFromError(timeout) if timeout.Error() != exception.Error() { t.Fatalf("Error did not match: expected %q, got %q", timeout.Error(), exception.Error()) } if exception.TypeId() != TIMED_OUT { t.Fatalf("TypeId was not TIMED_OUT: expected %v, got %v", TIMED_OUT, exception.TypeId()) } } func TestTExceptionEOF(t *testing.T) { exception := NewTTransportExceptionFromError(io.EOF) if io.EOF.Error() != exception.Error() { t.Fatalf("Error did not match: expected %q, got %q", io.EOF.Error(), exception.Error()) } if exception.TypeId() != END_OF_FILE { t.Fatalf("TypeId was not END_OF_FILE: expected %v, got %v", END_OF_FILE, exception.TypeId()) } } transport_factory.go000066400000000000000000000026011456146544700345050ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift // Factory class used to create wrapped instance of Transports. // This is used primarily in servers, which get Transports from // a ServerTransport and then may want to mutate them (i.e. create // a BufferedTransport from the underlying base transport) type TTransportFactory interface { GetTransport(trans TTransport) TTransport } type tTransportFactory struct{} // Return a wrapped instance of the base Transport. func (p *tTransportFactory) GetTransport(trans TTransport) TTransport { return trans } func NewTTransportFactory() TTransportFactory { return &tTransportFactory{} } transport_test.go000066400000000000000000000137151456146544700340250ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "io" "net" "strconv" "testing" ) const TRANSPORT_BINARY_DATA_SIZE = 4096 var ( transport_bdata []byte // test data for writing; same as data transport_header map[string]string ) func init() { transport_bdata = make([]byte, TRANSPORT_BINARY_DATA_SIZE) for i := 0; i < TRANSPORT_BINARY_DATA_SIZE; i++ { transport_bdata[i] = byte((i + 'a') % 255) } transport_header = map[string]string{"key": "User-Agent", "value": "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36"} } func TransportTest(t *testing.T, writeTrans TTransport, readTrans TTransport) { buf := make([]byte, TRANSPORT_BINARY_DATA_SIZE) if !writeTrans.IsOpen() { t.Fatalf("Transport %T not open: %s", writeTrans, writeTrans) } if !readTrans.IsOpen() { t.Fatalf("Transport %T not open: %s", readTrans, readTrans) } _, err := writeTrans.Write(transport_bdata) if err != nil { t.Fatalf("Transport %T cannot write binary data of length %d: %s", writeTrans, len(transport_bdata), err) } err = writeTrans.Flush() if err != nil { t.Fatalf("Transport %T cannot flush write of binary data: %s", writeTrans, err) } n, err := io.ReadFull(readTrans, buf) if err != nil { t.Errorf("Transport %T cannot read binary data of length %d: %s", readTrans, TRANSPORT_BINARY_DATA_SIZE, err) } if n != TRANSPORT_BINARY_DATA_SIZE { t.Errorf("Transport %T read only %d instead of %d bytes of binary data", readTrans, n, TRANSPORT_BINARY_DATA_SIZE) } for k, v := range buf { if v != transport_bdata[k] { t.Fatalf("Transport %T read %d instead of %d for index %d of binary data 2", readTrans, v, transport_bdata[k], k) } } _, err = writeTrans.Write(transport_bdata) if err != nil { t.Fatalf("Transport %T cannot write binary data 2 of length %d: %s", writeTrans, len(transport_bdata), err) } err = writeTrans.Flush() if err != nil { t.Fatalf("Transport %T cannot flush write binary data 2: %s", writeTrans, err) } buf = make([]byte, TRANSPORT_BINARY_DATA_SIZE) read := 1 for n = 0; n < TRANSPORT_BINARY_DATA_SIZE && read != 0; { read, err = readTrans.Read(buf[n:]) if err != nil { t.Errorf("Transport %T cannot read binary data 2 of total length %d from offset %d: %s", readTrans, TRANSPORT_BINARY_DATA_SIZE, n, err) } n += read } if n != TRANSPORT_BINARY_DATA_SIZE { t.Errorf("Transport %T read only %d instead of %d bytes of binary data 2", readTrans, n, TRANSPORT_BINARY_DATA_SIZE) } for k, v := range buf { if v != transport_bdata[k] { t.Fatalf("Transport %T read %d instead of %d for index %d of binary data 2", readTrans, v, transport_bdata[k], k) } } } func TransportHeaderTest(t *testing.T, writeTrans TTransport, readTrans TTransport) { buf := make([]byte, TRANSPORT_BINARY_DATA_SIZE) if !writeTrans.IsOpen() { t.Fatalf("Transport %T not open: %s", writeTrans, writeTrans) } if !readTrans.IsOpen() { t.Fatalf("Transport %T not open: %s", readTrans, readTrans) } // Need to assert type of TTransport to THttpClient to expose the Setter httpWPostTrans := writeTrans.(*THttpClient) httpWPostTrans.SetHeader(transport_header["key"], transport_header["value"]) _, err := writeTrans.Write(transport_bdata) if err != nil { t.Fatalf("Transport %T cannot write binary data of length %d: %s", writeTrans, len(transport_bdata), err) } err = writeTrans.Flush() if err != nil { t.Fatalf("Transport %T cannot flush write of binary data: %s", writeTrans, err) } // Need to assert type of TTransport to THttpClient to expose the Getter httpRPostTrans := readTrans.(*THttpClient) readHeader := httpRPostTrans.GetHeader(transport_header["key"]) if err != nil { t.Errorf("Transport %T cannot read HTTP Header Value", httpRPostTrans) } if transport_header["value"] != readHeader { t.Errorf("Expected HTTP Header Value %s, got %s", transport_header["value"], readHeader) } n, err := io.ReadFull(readTrans, buf) if err != nil { t.Errorf("Transport %T cannot read binary data of length %d: %s", readTrans, TRANSPORT_BINARY_DATA_SIZE, err) } if n != TRANSPORT_BINARY_DATA_SIZE { t.Errorf("Transport %T read only %d instead of %d bytes of binary data", readTrans, n, TRANSPORT_BINARY_DATA_SIZE) } for k, v := range buf { if v != transport_bdata[k] { t.Fatalf("Transport %T read %d instead of %d for index %d of binary data 2", readTrans, v, transport_bdata[k], k) } } } func CloseTransports(t *testing.T, readTrans TTransport, writeTrans TTransport) { err := readTrans.Close() if err != nil { t.Errorf("Transport %T cannot close read transport: %s", readTrans, err) } if writeTrans != readTrans { err = writeTrans.Close() if err != nil { t.Errorf("Transport %T cannot close write transport: %s", writeTrans, err) } } } func FindAvailableTCPServerPort(startPort int) (net.Addr, error) { for i := startPort; i < 65535; i++ { s := "127.0.0.1:" + strconv.Itoa(i) l, err := net.Listen("tcp", s) if err == nil { l.Close() return net.ResolveTCPAddr("tcp", s) } } return nil, NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "Could not find available server port") } func valueInSlice(value string, slice []string) bool { for _, v := range slice { if value == v { return true } } return false } golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/type.go000066400000000000000000000027761456146544700317770ustar00rootroot00000000000000/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift // Type constants in the Thrift protocol type TType byte const ( STOP = 0 VOID = 1 BOOL = 2 BYTE = 3 I08 = 3 DOUBLE = 4 I16 = 6 I32 = 8 I64 = 10 STRING = 11 UTF7 = 11 STRUCT = 12 MAP = 13 SET = 14 LIST = 15 UTF8 = 16 UTF16 = 17 //BINARY = 18 wrong and unusued ) var typeNames = map[int]string{ STOP: "STOP", VOID: "VOID", BOOL: "BOOL", BYTE: "BYTE", DOUBLE: "DOUBLE", I16: "I16", I32: "I32", I64: "I64", STRING: "STRING", STRUCT: "STRUCT", MAP: "MAP", SET: "SET", LIST: "LIST", UTF8: "UTF8", UTF16: "UTF16", } func (p TType) String() string { if s, ok := typeNames[int(p)]; ok { return s } return "Unknown" } zlib_transport.go000066400000000000000000000060431456146544700340020ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "compress/zlib" "io" "log" ) // TZlibTransportFactory is a factory for TZlibTransport instances type TZlibTransportFactory struct { level int } // TZlibTransport is a TTransport implementation that makes use of zlib compression. type TZlibTransport struct { reader io.ReadCloser transport TTransport writer *zlib.Writer } // GetTransport constructs a new instance of NewTZlibTransport func (p *TZlibTransportFactory) GetTransport(trans TTransport) TTransport { t, _ := NewTZlibTransport(trans, p.level) return t } // NewTZlibTransportFactory constructs a new instance of NewTZlibTransportFactory func NewTZlibTransportFactory(level int) *TZlibTransportFactory { return &TZlibTransportFactory{level: level} } // NewTZlibTransport constructs a new instance of TZlibTransport func NewTZlibTransport(trans TTransport, level int) (*TZlibTransport, error) { w, err := zlib.NewWriterLevel(trans, level) if err != nil { log.Println(err) return nil, err } return &TZlibTransport{ writer: w, transport: trans, }, nil } // Close closes the reader and writer (flushing any unwritten data) and closes // the underlying transport. func (z *TZlibTransport) Close() error { if z.reader != nil { if err := z.reader.Close(); err != nil { return err } } if err := z.writer.Close(); err != nil { return err } return z.transport.Close() } // Flush flushes the writer and its underlying transport. func (z *TZlibTransport) Flush() error { if err := z.writer.Flush(); err != nil { return err } return z.transport.Flush() } // IsOpen returns true if the transport is open func (z *TZlibTransport) IsOpen() bool { return z.transport.IsOpen() } // Open opens the transport for communication func (z *TZlibTransport) Open() error { return z.transport.Open() } func (z *TZlibTransport) Read(p []byte) (int, error) { if z.reader == nil { r, err := zlib.NewReader(z.transport) if err != nil { return 0, NewTTransportExceptionFromError(err) } z.reader = r } return z.reader.Read(p) } // RemainingBytes returns the size in bytes of the data that is still to be // read. func (z *TZlibTransport) RemainingBytes() uint64 { return z.transport.RemainingBytes() } func (z *TZlibTransport) Write(p []byte) (int, error) { return z.writer.Write(p) } zlib_transport_test.go000066400000000000000000000020241456146544700350340ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 thrift import ( "compress/zlib" "testing" ) func TestZlibTransport(t *testing.T) { trans, err := NewTZlibTransport(NewTMemoryBuffer(), zlib.BestCompression) if err != nil { t.Fatal(err) } TransportTest(t, trans, trans) } golang-github-uber-go-tally-4.1.11/tools/000077500000000000000000000000001456146544700201665ustar00rootroot00000000000000golang-github-uber-go-tally-4.1.11/tools/doc.go000066400000000000000000000023541456146544700212660ustar00rootroot00000000000000// Copyright (c) 2022 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // Package tools adds dependencies on tools used while developing tally that // should not be included in its dependencies. package tools golang-github-uber-go-tally-4.1.11/tools/go.mod000066400000000000000000000007051456146544700212760ustar00rootroot00000000000000module github.com/uber-go/tally/tools go 1.18 require ( golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 honnef.co/go/tools v0.3.2 ) require ( github.com/BurntSushi/toml v1.1.0 // indirect golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect golang.org/x/tools v0.1.11 // indirect ) golang-github-uber-go-tally-4.1.11/tools/go.sum000066400000000000000000000047201456146544700213240ustar00rootroot00000000000000github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d h1:+W8Qf4iJtMGKkyAygcKohjxTk4JPsL9DpzApJ22m5Ic= golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY= golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= honnef.co/go/tools v0.3.2 h1:ytYb4rOqyp1TSa2EPvNVwtPQJctSELKaMyLfqNP4+34= honnef.co/go/tools v0.3.2/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= golang-github-uber-go-tally-4.1.11/tools/tools.go000066400000000000000000000023441456146544700216600ustar00rootroot00000000000000// Copyright (c) 2022 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. //go:build tools // +build tools package tools import ( _ "golang.org/x/lint/golint" _ "honnef.co/go/tools/cmd/staticcheck" ) golang-github-uber-go-tally-4.1.11/types.go000066400000000000000000000131161456146544700205230ustar00rootroot00000000000000// Copyright (c) 2021 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally import ( "fmt" "sort" "time" ) // Scope is a namespace wrapper around a stats reporter, ensuring that // all emitted values have a given prefix or set of tags. // // IMPORTANT: When using Prometheus reporters, users must take care to // not create metrics from both parent scopes and subscopes // that have the same metric name but different tag keys, // as metric allocation will panic. type Scope interface { // Counter returns the Counter object corresponding to the name. Counter(name string) Counter // Gauge returns the Gauge object corresponding to the name. Gauge(name string) Gauge // Timer returns the Timer object corresponding to the name. Timer(name string) Timer // Histogram returns the Histogram object corresponding to the name. // To use default value and duration buckets configured for the scope // simply pass tally.DefaultBuckets or nil. // You can use tally.ValueBuckets{x, y, ...} for value buckets. // You can use tally.DurationBuckets{x, y, ...} for duration buckets. // You can use tally.MustMakeLinearValueBuckets(start, width, count) for linear values. // You can use tally.MustMakeLinearDurationBuckets(start, width, count) for linear durations. // You can use tally.MustMakeExponentialValueBuckets(start, factor, count) for exponential values. // You can use tally.MustMakeExponentialDurationBuckets(start, factor, count) for exponential durations. Histogram(name string, buckets Buckets) Histogram // Tagged returns a new child scope with the given tags and current tags. Tagged(tags map[string]string) Scope // SubScope returns a new child scope appending a further name prefix. SubScope(name string) Scope // Capabilities returns a description of metrics reporting capabilities. Capabilities() Capabilities } // Counter is the interface for emitting counter type metrics. type Counter interface { // Inc increments the counter by a delta. Inc(delta int64) } // Gauge is the interface for emitting gauge metrics. type Gauge interface { // Update sets the gauges absolute value. Update(value float64) } // Timer is the interface for emitting timer metrics. type Timer interface { // Record a specific duration directly. Record(value time.Duration) // Start gives you back a specific point in time to report via Stop. Start() Stopwatch } // Histogram is the interface for emitting histogram metrics type Histogram interface { // RecordValue records a specific value directly. // Will use the configured value buckets for the histogram. RecordValue(value float64) // RecordDuration records a specific duration directly. // Will use the configured duration buckets for the histogram. RecordDuration(value time.Duration) // Start gives you a specific point in time to then record a duration. // Will use the configured duration buckets for the histogram. Start() Stopwatch } // Stopwatch is a helper for simpler tracking of elapsed time, use the // Stop() method to report time elapsed since its created back to the // timer or histogram. type Stopwatch struct { start time.Time recorder StopwatchRecorder } // NewStopwatch creates a new immutable stopwatch for recording the start // time to a stopwatch reporter. func NewStopwatch(start time.Time, r StopwatchRecorder) Stopwatch { return Stopwatch{start: start, recorder: r} } // Stop reports time elapsed since the stopwatch start to the recorder. func (sw Stopwatch) Stop() { sw.recorder.RecordStopwatch(sw.start) } // StopwatchRecorder is a recorder that is called when a stopwatch is // stopped with Stop(). type StopwatchRecorder interface { RecordStopwatch(stopwatchStart time.Time) } // Buckets is an interface that can represent a set of buckets // either as float64s or as durations. type Buckets interface { fmt.Stringer sort.Interface // AsValues returns a representation of the buckets as float64s AsValues() []float64 // AsDurations returns a representation of the buckets as time.Durations AsDurations() []time.Duration } // BucketPair describes the lower and upper bounds // for a derived bucket from a buckets set. type BucketPair interface { LowerBoundValue() float64 UpperBoundValue() float64 LowerBoundDuration() time.Duration UpperBoundDuration() time.Duration } // Capabilities is a description of metrics reporting capabilities. type Capabilities interface { // Reporting returns whether the reporter has the ability to actively report. Reporting() bool // Tagging returns whether the reporter has the capability for tagged metrics. Tagging() bool } golang-github-uber-go-tally-4.1.11/version.go000066400000000000000000000022741456146544700210470ustar00rootroot00000000000000// Copyright (c) 2024 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package tally // Version is the current version of the library. const Version = "4.1.11"