pax_global_header 0000666 0000000 0000000 00000000064 14561465447 0014532 g ustar 00root root 0000000 0000000 52 comment=cd0bbe2da091463a7d87d1dd2e5bf07a71fe5ff9
golang-github-uber-go-tally-4.1.11/ 0000775 0000000 0000000 00000000000 14561465447 0017026 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/.github/ 0000775 0000000 0000000 00000000000 14561465447 0020366 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/.github/workflows/ 0000775 0000000 0000000 00000000000 14561465447 0022423 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/.github/workflows/test.yml 0000664 0000000 0000000 00000001671 14561465447 0024132 0 ustar 00root root 0000000 0000000 name: 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/.gitignore 0000664 0000000 0000000 00000000524 14561465447 0021017 0 ustar 00root root 0000000 0000000 # 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.yml 0000664 0000000 0000000 00000000351 14561465447 0021136 0 ustar 00root root 0000000 0000000 language: 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/LICENSE 0000664 0000000 0000000 00000002041 14561465447 0020030 0 ustar 00root root 0000000 0000000 MIT 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/Makefile 0000664 0000000 0000000 00000005330 14561465447 0020467 0 ustar 00root root 0000000 0000000 # "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.md 0000664 0000000 0000000 00000016056 14561465447 0020315 0 ustar 00root root 0000000 0000000 # :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.sh 0000775 0000000 0000000 00000000444 14561465447 0022146 0 ustar 00root root 0000000 0000000 #!/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/ 0000775 0000000 0000000 00000000000 14561465447 0020461 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/example/README.md 0000664 0000000 0000000 00000000052 14561465447 0021735 0 ustar 00root root 0000000 0000000 # Print reporter example
`go run ./*.go`
golang-github-uber-go-tally-4.1.11/example/main.go 0000664 0000000 0000000 00000006506 14561465447 0021743 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000002502 14561465447 0021146 0 ustar 00root root 0000000 0000000 // 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.mod 0000664 0000000 0000000 00000000711 14561465447 0020133 0 ustar 00root root 0000000 0000000 module 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.sum 0000664 0000000 0000000 00000042535 14561465447 0020172 0 ustar 00root root 0000000 0000000 cloud.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.go 0000664 0000000 0000000 00000023466 14561465447 0021365 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000014612 14561465447 0022415 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14561465447 0021236 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/instrument/call.go 0000664 0000000 0000000 00000004300 14561465447 0022475 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000005111 14561465447 0023535 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000002702 14561465447 0022732 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14561465447 0020642 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/internal/cache/ 0000775 0000000 0000000 00000000000 14561465447 0021705 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/internal/cache/string_intern.go 0000664 0000000 0000000 00000003157 14561465447 0025127 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000004531 14561465447 0024135 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14561465447 0022473 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/internal/identity/accumulator.go 0000664 0000000 0000000 00000007047 14561465447 0025351 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000003425 14561465447 0030416 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000006405 14561465447 0021003 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000004751 14561465447 0022044 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14561465447 0017345 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/m3/config.go 0000664 0000000 0000000 00000005741 14561465447 0021150 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000004272 14561465447 0022205 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14561465447 0023017 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/m3/customtransports/buffered_read_transport.go 0000664 0000000 0000000 00000005620 14561465447 0030242 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000004763 14561465447 0031310 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000006667 14561465447 0027002 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000005363 14561465447 0030031 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14561465447 0021000 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/m3/example/README.md 0000664 0000000 0000000 00000000047 14561465447 0022260 0 ustar 00root root 0000000 0000000 # M3 reporter example
`go run ./*.go`
golang-github-uber-go-tally-4.1.11/m3/example/config.yaml 0000664 0000000 0000000 00000000203 14561465447 0023124 0 ustar 00root root 0000000 0000000 m3:
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.go 0000664 0000000 0000000 00000006241 14561465447 0024012 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000010343 14561465447 0022653 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000050136 14561465447 0021543 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000005702 14561465447 0024613 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000006542 14561465447 0025207 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000052652 14561465447 0022607 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000006114 14561465447 0022556 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000003467 14561465447 0023625 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000003334 14561465447 0021525 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000014261 14561465447 0022050 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14561465447 0020645 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/m3/thrift/Makefile 0000664 0000000 0000000 00000000744 14561465447 0022312 0 ustar 00root root 0000000 0000000 gen-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.diff 0000664 0000000 0000000 00000014616 14561465447 0023007 0 ustar 00root root 0000000 0000000 diff --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/ 0000775 0000000 0000000 00000000000 14561465447 0021173 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/m3/thrift/v1/constants.go 0000664 0000000 0000000 00000000576 14561465447 0023546 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000014534 14561465447 0022050 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000100414 14561465447 0023052 0 ustar 00root root 0000000 0000000 // 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.thrift 0000664 0000000 0000000 00000002217 14561465447 0022745 0 ustar 00root root 0000000 0000000 /**
* 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/ 0000775 0000000 0000000 00000000000 14561465447 0021174 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/m3/thrift/v2/constants.go 0000664 0000000 0000000 00000000576 14561465447 0023547 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000014503 14561465447 0022045 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000056430 14561465447 0023063 0 ustar 00root root 0000000 0000000 // 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.thrift 0000664 0000000 0000000 00000001161 14561465447 0022744 0 ustar 00root root 0000000 0000000 enum 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/ 0000775 0000000 0000000 00000000000 14561465447 0021356 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/m3/thriftudp/multitransport.go 0000664 0000000 0000000 00000007233 14561465447 0025021 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000013156 14561465447 0026061 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000016112 14561465447 0023742 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000021463 14561465447 0025006 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14561465447 0020160 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/multi/README.md 0000664 0000000 0000000 00000000503 14561465447 0021435 0 ustar 00root root 0000000 0000000 # 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.go 0000664 0000000 0000000 00000015046 14561465447 0022357 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000026225 14561465447 0023417 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000003641 14561465447 0020332 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14561465447 0021221 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/prometheus/README.md 0000664 0000000 0000000 00000006661 14561465447 0022511 0 ustar 00root root 0000000 0000000 # 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.go 0000664 0000000 0000000 00000012612 14561465447 0023017 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000005125 14561465447 0024057 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14561465447 0022654 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/prometheus/example/README.md 0000664 0000000 0000000 00000000057 14561465447 0024135 0 ustar 00root root 0000000 0000000 # Prometheus reporter example
`go run ./*.go`
golang-github-uber-go-tally-4.1.11/prometheus/example/prometheus_main.go 0000664 0000000 0000000 00000005026 14561465447 0026405 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000037555 14561465447 0023431 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000036261 14561465447 0024461 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000003332 14561465447 0023377 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000007727 14561465447 0021234 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000012255 14561465447 0021210 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000004067 14561465447 0022251 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000044043 14561465447 0020473 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000020342 14561465447 0023540 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000026075 14561465447 0022430 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000007351 14561465447 0025365 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000015463 14561465447 0023466 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000102363 14561465447 0021532 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000030315 14561465447 0020515 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000004523 14561465447 0023570 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000011276 14561465447 0021561 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14561465447 0020330 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/statsd/README.md 0000664 0000000 0000000 00000001341 14561465447 0021606 0 ustar 00root root 0000000 0000000 # 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/ 0000775 0000000 0000000 00000000000 14561465447 0021763 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/statsd/example/README.md 0000664 0000000 0000000 00000000053 14561465447 0023240 0 ustar 00root root 0000000 0000000 # Statsd reporter example
`go run ./*.go`
golang-github-uber-go-tally-4.1.11/statsd/example/statsd_main.go 0000664 0000000 0000000 00000004726 14561465447 0024631 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000010716 14561465447 0022526 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000002525 14561465447 0023564 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14561465447 0021025 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/tallymock/stats_reporter.go 0000664 0000000 0000000 00000011603 14561465447 0024435 0 ustar 00root root 0000000 0000000 // 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/ 0000775 0000000 0000000 00000000000 14561465447 0021220 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/thirdparty/github.com/ 0000775 0000000 0000000 00000000000 14561465447 0023257 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/ 0000775 0000000 0000000 00000000000 14561465447 0024500 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/ 0000775 0000000 0000000 00000000000 14561465447 0026000 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/ 0000775 0000000 0000000 00000000000 14561465447 0026546 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/ 0000775 0000000 0000000 00000000000 14561465447 0027153 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/thirdparty/github.com/apache/thrift/lib/go/thrift/ 0000775 0000000 0000000 00000000000 14561465447 0030453 5 ustar 00root root 0000000 0000000 application_exception.go 0000664 0000000 0000000 00000006424 14561465447 0035312 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000002753 14561465447 0036352 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000025564 14561465447 0034144 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000001675 14561465447 0035200 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000004403 14561465447 0034622 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000001724 14561465447 0035664 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000053753 14561465447 0034307 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000003373 14561465447 0035337 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000023335 14561465447 0033740 0 ustar 00root root 0000000 0000000 golang-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.go0000664 0000000 0000000 00000003015 14561465447 0033463 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000002661 14561465447 0033005 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000004160 14561465447 0033761 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000003353 14561465447 0032071 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000011272 14561465447 0034300 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000001711 14561465447 0035334 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000017006 14561465447 0033323 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000005170 14561465447 0034302 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000002461 14561465447 0034021 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000012365 14561465447 0034671 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000003231 14561465447 0035720 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000033014 14561465447 0033616 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000051605 14561465447 0034663 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000023155 14561465447 0036016 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000003751 14561465447 0033572 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000001670 14561465447 0034627 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000002122 14561465447 0033325 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000012207 14561465447 0035202 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000007605 14561465447 0032454 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000003775 14561465447 0033206 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000002121 14561465447 0033015 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000003314 14561465447 0034472 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000010727 14561465447 0032652 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000003625 14561465447 0034650 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000001674 14561465447 0034323 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000034556 14561465447 0033640 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000003351 14561465447 0033766 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000004754 14561465447 0035035 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000003370 14561465447 0033156 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000011127 14561465447 0034135 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000041767 14561465447 0035376 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000002370 14561465447 0032312 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000005577 14561465447 0033617 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000002621 14561465447 0034641 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000001560 14561465447 0033351 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000002446 14561465447 0034353 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000106510 14561465447 0035171 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000055175 14561465447 0036242 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000013214 14561465447 0033603 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000010325 14561465447 0032273 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000005446 14561465447 0034473 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000011360 14561465447 0033154 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000003237 14561465447 0033043 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000004006 14561465447 0035035 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000003415 14561465447 0036077 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000002601 14561465447 0034505 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000013715 14561465447 0034025 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000002776 14561465447 0031777 0 ustar 00root root 0000000 0000000 /*
* 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.go 0000664 0000000 0000000 00000006043 14561465447 0034002 0 ustar 00root root 0000000 0000000 golang-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.go 0000664 0000000 0000000 00000002024 14561465447 0035034 0 ustar 00root root 0000000 0000000 golang-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/ 0000775 0000000 0000000 00000000000 14561465447 0020166 5 ustar 00root root 0000000 0000000 golang-github-uber-go-tally-4.1.11/tools/doc.go 0000664 0000000 0000000 00000002354 14561465447 0021266 0 ustar 00root root 0000000 0000000 // 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.mod 0000664 0000000 0000000 00000000705 14561465447 0021276 0 ustar 00root root 0000000 0000000 module 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.sum 0000664 0000000 0000000 00000004720 14561465447 0021324 0 ustar 00root root 0000000 0000000 github.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.go 0000664 0000000 0000000 00000002344 14561465447 0021660 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000013116 14561465447 0020523 0 ustar 00root root 0000000 0000000 // 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.go 0000664 0000000 0000000 00000002274 14561465447 0021047 0 ustar 00root root 0000000 0000000 // 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"