├── .github └── workflows │ └── test.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── check_license.sh ├── example ├── README.md └── main.go ├── generate.go ├── go.mod ├── go.sum ├── histogram.go ├── histogram_test.go ├── instrument ├── call.go ├── call_test.go └── types.go ├── internal ├── cache │ ├── string_intern.go │ └── tag_cache.go └── identity │ ├── accumulator.go │ └── accumulator_benchmark_test.go ├── key_gen.go ├── key_gen_test.go ├── m3 ├── config.go ├── config_test.go ├── customtransports │ ├── buffered_read_transport.go │ ├── buffered_read_transport_test.go │ ├── m3_calc_transport.go │ └── m3_calc_transport_test.go ├── example │ ├── README.md │ ├── config.yaml │ ├── local_server.go │ └── m3_main.go ├── reporter.go ├── reporter_benchmark_test.go ├── reporter_integration_test.go ├── reporter_test.go ├── resource_pool.go ├── resource_pool_test.go ├── sanitize.go ├── scope_test.go ├── thrift │ ├── Makefile │ ├── thrift.diff │ ├── v1 │ │ ├── constants.go │ │ ├── m3.go │ │ ├── ttypes.go │ │ └── v1.thrift │ └── v2 │ │ ├── constants.go │ │ ├── m3.go │ │ ├── ttypes.go │ │ └── v2.thrift └── thriftudp │ ├── multitransport.go │ ├── multitransport_test.go │ ├── transport.go │ └── transport_test.go ├── multi ├── README.md ├── reporter.go └── reporter_test.go ├── pool.go ├── prometheus ├── README.md ├── config.go ├── config_test.go ├── example │ ├── README.md │ └── prometheus_main.go ├── reporter.go ├── reporter_test.go └── sanitize.go ├── reporter.go ├── sanitize.go ├── sanitize_test.go ├── scope.go ├── scope_benchmark_test.go ├── scope_registry.go ├── scope_registry_external_test.go ├── scope_registry_test.go ├── scope_test.go ├── stats.go ├── stats_benchmark_test.go ├── stats_test.go ├── statsd ├── README.md ├── example │ ├── README.md │ └── statsd_main.go ├── reporter.go └── reporter_test.go ├── tallymock └── stats_reporter.go ├── thirdparty └── github.com │ └── apache │ └── thrift │ └── lib │ └── go │ └── thrift │ ├── application_exception.go │ ├── application_exception_test.go │ ├── binary_protocol.go │ ├── binary_protocol_test.go │ ├── buffered_transport.go │ ├── buffered_transport_test.go │ ├── compact_protocol.go │ ├── compact_protocol_test.go │ ├── debug_protocol.go │ ├── deserializer.go │ ├── exception.go │ ├── exception_test.go │ ├── field.go │ ├── framed_transport.go │ ├── framed_transport_test.go │ ├── http_client.go │ ├── http_client_test.go │ ├── http_transport.go │ ├── iostream_transport.go │ ├── iostream_transport_test.go │ ├── json_protocol.go │ ├── json_protocol_test.go │ ├── lowlevel_benchmarks_test.go │ ├── memory_buffer.go │ ├── memory_buffer_test.go │ ├── messagetype.go │ ├── multiplexed_protocol.go │ ├── numeric.go │ ├── pointerize.go │ ├── processor.go │ ├── processor_factory.go │ ├── protocol.go │ ├── protocol_exception.go │ ├── protocol_factory.go │ ├── protocol_test.go │ ├── rich_transport.go │ ├── rich_transport_test.go │ ├── serializer.go │ ├── serializer_test.go │ ├── serializer_types_test.go │ ├── server.go │ ├── server_socket.go │ ├── server_socket_test.go │ ├── server_test.go │ ├── server_transport.go │ ├── simple_json_protocol.go │ ├── simple_json_protocol_test.go │ ├── simple_server.go │ ├── socket.go │ ├── ssl_server_socket.go │ ├── ssl_socket.go │ ├── transport.go │ ├── transport_exception.go │ ├── transport_exception_test.go │ ├── transport_factory.go │ ├── transport_test.go │ ├── type.go │ ├── zlib_transport.go │ └── zlib_transport_test.go ├── tools ├── doc.go ├── go.mod ├── go.sum └── tools.go ├── types.go ├── utils.go ├── utils_test.go └── version.go /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: ['*'] 6 | tags: ['v*'] 7 | pull_request: 8 | branches: ['*'] 9 | 10 | jobs: 11 | 12 | test: 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | go: ["stable", "oldstable"] 17 | os: ["ubuntu-latest", "ubuntu-24.04-arm"] 18 | 19 | steps: 20 | - name: Checkout code 21 | uses: actions/checkout@v4 22 | 23 | - name: Setup Go 24 | uses: actions/setup-go@v5 25 | with: 26 | go-version: ${{ matrix.go }} 27 | 28 | - name: Lint 29 | if: matrix.latest 30 | run: make lint 31 | 32 | - name: Test 33 | run: make cover 34 | 35 | - name: Upload coverage to codecov.io 36 | uses: codecov/codecov-action@v1 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | vendor 10 | bin 11 | 12 | # Architecture specific extensions/prefixes 13 | *.[568vq] 14 | [568vq].out 15 | 16 | *.cgo1.go 17 | *.cgo2.c 18 | _cgo_defun.c 19 | _cgo_gotypes.go 20 | _cgo_export.* 21 | 22 | _testmain.go 23 | 24 | *.exe 25 | *.test 26 | *.prof 27 | *.pprof 28 | *.out 29 | *.log 30 | 31 | .DS_Store 32 | node_modules/ 33 | .idea/ 34 | cover.html 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # "go install"-ed binaries will be placed here during development. 2 | export GOBIN ?= $(shell pwd)/bin 3 | 4 | BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem 5 | GO_FILES = $(shell find . \ 6 | '(' -path '*/.*' -o -path './thirdparty/*' -prune ')' -o \ 7 | '(' -type f -a -name '*.go' ')' -print) 8 | MODULES = . ./tools 9 | 10 | LINT_IGNORE = m3/thrift\|thirdparty 11 | LICENSE_IGNORE = m3/thrift\|thirdparty 12 | STATICCHECK_IGNORE = m3/thrift\|thirdparty\|m3/resource_pool.go:.*releaseProto is unused\|m3/reporter.go:.* argument should be pointer-like to avoid allocations 13 | 14 | GOLINT = $(GOBIN)/golint 15 | STATICCHECK = $(GOBIN)/staticcheck 16 | 17 | .PHONY: all 18 | all: lint test 19 | 20 | .PHONY: lint 21 | lint: gofmt golint gomodtidy staticcheck license 22 | 23 | .PHONY: golint 24 | golint: $(GOLINT) 25 | @echo "Checking lint..." 26 | @$(eval LOG := $(shell mktemp -t log.XXXXX)) 27 | @$(GOLINT) ./... | grep -v '$(LINT_IGNORE)' > $(LOG) || true 28 | @[ ! -s "$(LOG)" ] || \ 29 | (echo "golint failed:" | \ 30 | cat - $(LOG) && false) 31 | 32 | $(GOLINT): tools/go.mod 33 | cd tools && go install golang.org/x/lint/golint 34 | 35 | .PHONY: staticcheck 36 | staticcheck: $(STATICCHECK) 37 | @echo "Checking staticcheck..." 38 | @$(eval LOG := $(shell mktemp -t log.XXXXX)) 39 | @$(STATICCHECK) ./... | grep -v '$(STATICCHECK_IGNORE)' > $(LOG) || true 40 | @[ ! -s "$(LOG)" ] || \ 41 | (echo "staticcheck failed:" | \ 42 | cat - $(LOG) && false) 43 | 44 | $(STATICCHECK): 45 | cd tools && go install honnef.co/go/tools/cmd/staticcheck 46 | 47 | .PHONY: gofmt 48 | gofmt: 49 | @echo "Checking formatting..." 50 | $(eval LOG := $(shell mktemp -t log.XXXXX)) 51 | @gofmt -e -s -l $(GO_FILES) | grep -v '$(LINT_IGNORE)' > $(LOG) || true 52 | @[ ! -s "$(LOG)" ] || \ 53 | (echo "gofmt failed. Please reformat the following files:" | \ 54 | cat - $(LOG) && false) 55 | 56 | 57 | .PHONY: gomodtidy 58 | gomodtidy: go.mod go.sum 59 | @echo "Checking go.mod and go.sum..." 60 | @$(foreach mod,$(MODULES),\ 61 | (cd $(mod) && go mod tidy) &&) true 62 | @if ! git diff --quiet $^; then \ 63 | echo "go mod tidy changed files:" && \ 64 | git status --porcelain $^ && \ 65 | false; \ 66 | fi 67 | 68 | .PHONY: license 69 | license: check_license.sh 70 | @echo "Checking for license headers..." 71 | $(eval LOG := $(shell mktemp -t log.XXXXX)) 72 | @./check_license.sh | grep -v '$(LICENSE_IGNORE)' > $(LOG) || true 73 | @[ ! -s "$(LOG)" ] || \ 74 | (echo "Missing license headers in some files:" | \ 75 | cat - $(LOG) && false) 76 | 77 | .PHONY: test 78 | test: 79 | go test -race -v ./... 80 | 81 | .PHONY: examples 82 | examples: 83 | mkdir -p ./bin 84 | go build -o ./bin/print_example ./example/ 85 | go build -o ./bin/m3_example ./m3/example/ 86 | go build -o ./bin/prometheus_example ./prometheus/example/ 87 | go build -o ./bin/statsd_example ./statsd/example/ 88 | 89 | .PHONY: cover 90 | cover: 91 | go test -cover -coverprofile=cover.out -coverpkg=./... -race -v ./... 92 | go tool cover -html=cover.out -o cover.html 93 | 94 | .PHONY: bench 95 | BENCH ?= . 96 | bench: 97 | go test -bench=$(BENCH) -run="^$$" $(BENCH_FLAGS) ./... 98 | 99 | -------------------------------------------------------------------------------- /check_license.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | ERROR_COUNT=0 4 | while read -r file 5 | do 6 | case "$(head -1 "${file}")" in 7 | *"Copyright (c) "*" Uber Technologies, Inc.") 8 | # everything's cool 9 | ;; 10 | *) 11 | echo "$file:missing license header." 12 | (( ERROR_COUNT++ )) 13 | ;; 14 | esac 15 | done < <(git ls-files "*\.go") 16 | exit "$ERROR_COUNT" 17 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Print reporter example 2 | 3 | `go run ./*.go` 4 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package main 22 | 23 | import ( 24 | "fmt" 25 | "time" 26 | 27 | tally "github.com/uber-go/tally/v4" 28 | ) 29 | 30 | type printStatsReporter struct{} 31 | 32 | func newPrintStatsReporter() tally.StatsReporter { 33 | return &printStatsReporter{} 34 | } 35 | 36 | func (r *printStatsReporter) ReportCounter(name string, _ map[string]string, value int64) { 37 | fmt.Printf("count %s %d\n", name, value) 38 | } 39 | 40 | func (r *printStatsReporter) ReportGauge(name string, _ map[string]string, value float64) { 41 | fmt.Printf("gauge %s %f\n", name, value) 42 | } 43 | 44 | func (r *printStatsReporter) ReportTimer(name string, _ map[string]string, interval time.Duration) { 45 | fmt.Printf("timer %s %s\n", name, interval.String()) 46 | } 47 | 48 | func (r *printStatsReporter) ReportHistogramValueSamples( 49 | name string, 50 | _ map[string]string, 51 | _ tally.Buckets, 52 | bucketLowerBound, 53 | bucketUpperBound float64, 54 | samples int64, 55 | ) { 56 | fmt.Printf("histogram %s bucket lower %f upper %f samples %d\n", 57 | name, bucketLowerBound, bucketUpperBound, samples) 58 | } 59 | 60 | func (r *printStatsReporter) ReportHistogramDurationSamples( 61 | name string, 62 | _ map[string]string, 63 | _ tally.Buckets, 64 | bucketLowerBound, 65 | bucketUpperBound time.Duration, 66 | samples int64, 67 | ) { 68 | fmt.Printf("histogram %s bucket lower %v upper %v samples %d\n", 69 | name, bucketLowerBound, bucketUpperBound, samples) 70 | } 71 | 72 | func (r *printStatsReporter) Capabilities() tally.Capabilities { 73 | return r 74 | } 75 | 76 | func (r *printStatsReporter) Reporting() bool { 77 | return true 78 | } 79 | 80 | func (r *printStatsReporter) Tagging() bool { 81 | return false 82 | } 83 | 84 | func (r *printStatsReporter) Flush() { 85 | fmt.Printf("flush\n") 86 | } 87 | 88 | func main() { 89 | reporter := newPrintStatsReporter() 90 | rootScope, closer := tally.NewRootScope(tally.ScopeOptions{ 91 | Reporter: reporter, 92 | }, time.Second) 93 | defer closer.Close() 94 | subScope := rootScope.SubScope("requests") 95 | 96 | bighand := time.NewTicker(time.Millisecond * 2300) 97 | littlehand := time.NewTicker(time.Millisecond * 10) 98 | hugehand := time.NewTicker(time.Millisecond * 5100) 99 | 100 | measureThing := rootScope.Gauge("thing") 101 | timings := rootScope.Timer("timings") 102 | tickCounter := subScope.Counter("ticks") 103 | 104 | // Spin forever, watch report get called 105 | go func() { 106 | for { 107 | select { 108 | case <-bighand.C: 109 | measureThing.Update(42.1) 110 | case <-littlehand.C: 111 | tickCounter.Inc(1) 112 | case <-hugehand.C: 113 | timings.Record(3200 * time.Millisecond) 114 | } 115 | } 116 | }() 117 | 118 | select {} 119 | } 120 | -------------------------------------------------------------------------------- /generate.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package tally 22 | 23 | import ( 24 | //go:generate mockgen -package tallymock -destination tallymock/stats_reporter.go -imports github.com/uber-go/tally github.com/uber-go/tally StatsReporter 25 | _ "github.com/golang/mock/mockgen/model" 26 | ) 27 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/uber-go/tally/v4 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/cactus/go-statsd-client/v5 v5.0.0 7 | github.com/golang/mock v1.6.0 8 | github.com/pkg/errors v0.9.1 9 | github.com/prometheus/client_golang v1.11.0 10 | github.com/prometheus/client_model v0.2.0 11 | github.com/stretchr/testify v1.9.0 12 | github.com/twmb/murmur3 v1.1.8 13 | go.uber.org/atomic v1.11.0 14 | go.uber.org/goleak v1.3.0 15 | gopkg.in/validator.v2 v2.0.0-20200605151824-2b28d334fa05 16 | gopkg.in/yaml.v2 v2.4.0 17 | ) 18 | 19 | require ( 20 | github.com/beorn7/perks v1.0.1 // indirect 21 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 22 | github.com/davecgh/go-spew v1.1.1 // indirect 23 | github.com/golang/protobuf v1.4.3 // indirect 24 | github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect 25 | github.com/pmezard/go-difflib v1.0.0 // indirect 26 | github.com/prometheus/common v0.26.0 // indirect 27 | github.com/prometheus/procfs v0.6.0 // indirect 28 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 // indirect 29 | google.golang.org/protobuf v1.26.0-rc.1 // indirect 30 | gopkg.in/yaml.v3 v3.0.1 // indirect 31 | ) 32 | -------------------------------------------------------------------------------- /instrument/call.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package instrument 22 | 23 | import ( 24 | tally "github.com/uber-go/tally/v4" 25 | ) 26 | 27 | const ( 28 | resultType = "result_type" 29 | resultTypeError = "error" 30 | resultTypeSuccess = "success" 31 | timingSuffix = "latency" 32 | ) 33 | 34 | // NewCall returns a Call that instruments a function using a given scope 35 | // and a label to name the metrics. 36 | // The following counters are created excluding {{ and }}: 37 | // {{name}}+result_type=success 38 | // {{name}}+result_type=error 39 | // The following timers are created excluding {{ and }} and replacing . with 40 | // the scope's separator: 41 | // {{name}}.latency 42 | func NewCall(scope tally.Scope, name string) Call { 43 | return &call{ 44 | err: scope.Tagged(map[string]string{resultType: resultTypeError}).Counter(name), 45 | success: scope.Tagged(map[string]string{resultType: resultTypeSuccess}).Counter(name), 46 | timing: scope.SubScope(name).Timer(timingSuffix), 47 | } 48 | } 49 | 50 | type call struct { 51 | success tally.Counter 52 | err tally.Counter 53 | timing tally.Timer 54 | } 55 | 56 | func (c *call) Exec(f ExecFn) error { 57 | sw := c.timing.Start() 58 | err := f() 59 | sw.Stop() 60 | 61 | if err != nil { 62 | c.err.Inc(1) 63 | return err 64 | } 65 | 66 | c.success.Inc(1) 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /instrument/call_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package instrument 22 | 23 | import ( 24 | "errors" 25 | "testing" 26 | "time" 27 | 28 | "github.com/uber-go/tally/v4" 29 | 30 | "github.com/stretchr/testify/assert" 31 | "github.com/stretchr/testify/require" 32 | ) 33 | 34 | func TestCallSuccess(t *testing.T) { 35 | s := tally.NewTestScope("", nil) 36 | 37 | sleepFor := time.Microsecond 38 | err := NewCall(s, "test_call").Exec(func() error { 39 | time.Sleep(time.Microsecond) 40 | return nil 41 | }) 42 | assert.Nil(t, err) 43 | 44 | snapshot := s.Snapshot() 45 | counters := snapshot.Counters() 46 | timers := snapshot.Timers() 47 | 48 | require.NotNil(t, counters["test_call+result_type=success"]) 49 | require.NotNil(t, timers["test_call.latency+"]) 50 | 51 | assert.Equal(t, int64(1), counters["test_call+result_type=success"].Value()) 52 | require.Equal(t, 1, len(timers["test_call.latency+"].Values())) 53 | assert.True(t, timers["test_call.latency+"].Values()[0] >= sleepFor) 54 | } 55 | 56 | func TestCallFail(t *testing.T) { 57 | s := tally.NewTestScope("", nil) 58 | 59 | sleepFor := time.Microsecond 60 | expected := errors.New("an error") 61 | err := NewCall(s, "test_call").Exec(func() error { 62 | time.Sleep(sleepFor) 63 | return expected 64 | }) 65 | assert.NotNil(t, err) 66 | assert.Equal(t, expected, err) 67 | 68 | snapshot := s.Snapshot() 69 | counters := snapshot.Counters() 70 | timers := snapshot.Timers() 71 | 72 | require.NotNil(t, counters["test_call+result_type=error"]) 73 | require.NotNil(t, timers["test_call.latency+"]) 74 | 75 | assert.Equal(t, int64(1), counters["test_call+result_type=error"].Value()) 76 | require.Equal(t, 1, len(timers["test_call.latency+"].Values())) 77 | assert.True(t, timers["test_call.latency+"].Values()[0] >= sleepFor) 78 | } 79 | -------------------------------------------------------------------------------- /instrument/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package instrument 22 | 23 | // ExecFn is an executable function that can be instrumented with a Call. 24 | type ExecFn func() error 25 | 26 | // Call allows tracking the successes, errors, and timing of functions. 27 | type Call interface { 28 | // Exec executes a function and records whether it succeeded or 29 | // failed, and the amount of time that it took. 30 | Exec(f ExecFn) error 31 | } 32 | -------------------------------------------------------------------------------- /internal/cache/string_intern.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package cache 22 | 23 | import ( 24 | "sync" 25 | ) 26 | 27 | // StringInterner interns strings. 28 | type StringInterner struct { 29 | entries map[string]string 30 | mtx sync.RWMutex 31 | } 32 | 33 | // NewStringInterner creates a new StringInterner. 34 | func NewStringInterner() *StringInterner { 35 | return &StringInterner{ 36 | entries: make(map[string]string), 37 | } 38 | } 39 | 40 | // Intern interns s. 41 | func (i *StringInterner) Intern(s string) string { 42 | i.mtx.RLock() 43 | x, ok := i.entries[s] 44 | i.mtx.RUnlock() 45 | 46 | if ok { 47 | return x 48 | } 49 | 50 | i.mtx.Lock() 51 | i.entries[s] = s 52 | i.mtx.Unlock() 53 | 54 | return s 55 | } 56 | -------------------------------------------------------------------------------- /internal/cache/tag_cache.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package cache 22 | 23 | import ( 24 | "sync" 25 | 26 | "github.com/uber-go/tally/v4/internal/identity" 27 | m3thrift "github.com/uber-go/tally/v4/m3/thrift/v2" 28 | ) 29 | 30 | // TagCache is an identity.Accumulator-based tag cache. 31 | type TagCache struct { 32 | entries map[uint64][]m3thrift.MetricTag 33 | mtx sync.RWMutex 34 | } 35 | 36 | // NewTagCache creates a new TagCache. 37 | func NewTagCache() *TagCache { 38 | return &TagCache{ 39 | entries: make(map[uint64][]m3thrift.MetricTag), 40 | } 41 | } 42 | 43 | // Get returns the cached value for key. 44 | func (c *TagCache) Get(key uint64) ([]m3thrift.MetricTag, bool) { 45 | c.mtx.RLock() 46 | defer c.mtx.RUnlock() 47 | 48 | entry, ok := c.entries[key] 49 | return entry, ok 50 | } 51 | 52 | // Set attempts to set the value of key as tslice, returning either tslice or 53 | // the pre-existing value if found. 54 | func (c *TagCache) Set(key uint64, tslice []m3thrift.MetricTag) []m3thrift.MetricTag { 55 | c.mtx.RLock() 56 | existing, ok := c.entries[key] 57 | c.mtx.RUnlock() 58 | 59 | if ok { 60 | return existing 61 | } 62 | 63 | c.mtx.Lock() 64 | defer c.mtx.Unlock() 65 | 66 | c.entries[key] = tslice 67 | return tslice 68 | } 69 | 70 | // Len returns the size of the cache, 71 | func (c *TagCache) Len() int { 72 | c.mtx.RLock() 73 | defer c.mtx.RUnlock() 74 | return len(c.entries) 75 | } 76 | 77 | // TagMapKey generates a new key based on tags. 78 | func TagMapKey(tags map[string]string) uint64 { 79 | return identity.StringStringMap(tags) 80 | } 81 | -------------------------------------------------------------------------------- /internal/identity/accumulator.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package identity 22 | 23 | import ( 24 | "math" 25 | "time" 26 | 27 | "github.com/twmb/murmur3" 28 | ) 29 | 30 | const ( 31 | _hashSeed uint64 = 23 32 | _hashFold uint64 = 31 33 | ) 34 | 35 | // Accumulator is a commutative folding accumulator. 36 | type Accumulator uint64 37 | 38 | // NewAccumulator creates a new Accumulator with a default seed value. 39 | // 40 | // n.b. Here and elsewhere, we use nosplit to avoid stack size checks, which 41 | // are unnecessary as memory width is bounded to each instance of `a` (a 42 | // uint64) and, potentially, a single stack-local loop temporary while 43 | // iterating. 44 | func NewAccumulator() Accumulator { 45 | return Accumulator(_hashSeed) 46 | } 47 | 48 | // NewAccumulatorWithSeed creates a new Accumulator with the provided seed value. 49 | func NewAccumulatorWithSeed(seed uint64) Accumulator { 50 | return Accumulator(seed) 51 | } 52 | 53 | // AddString hashes str and folds it into the accumulator. 54 | func (a Accumulator) AddString(str string) Accumulator { 55 | return a + Accumulator(murmur3.StringSum64(str)*_hashFold) 56 | } 57 | 58 | // AddUint64 folds u64 into the accumulator. 59 | func (a Accumulator) AddUint64(u64 uint64) Accumulator { 60 | return a + Accumulator(u64*_hashFold) 61 | } 62 | 63 | // Value returns the accumulated value. 64 | func (a Accumulator) Value() uint64 { 65 | return uint64(a) 66 | } 67 | 68 | // Durations returns the accumulated identity of durs. 69 | func Durations(durs []time.Duration) uint64 { 70 | if len(durs) == 0 { 71 | return 0 72 | } 73 | 74 | acc := NewAccumulator() 75 | 76 | // n.b. Wrapping due to overflow is okay here, since those values cannot be 77 | // represented by int64. 78 | for _, d := range durs { 79 | acc = acc.AddUint64(uint64(d)) 80 | } 81 | 82 | return acc.Value() 83 | } 84 | 85 | // Int64s returns the accumulated identity of i64s. 86 | func Int64s(i64s []int64) uint64 { 87 | if len(i64s) == 0 { 88 | return 0 89 | } 90 | 91 | acc := NewAccumulator() 92 | 93 | // n.b. Wrapping due to overflow is okay here, since those values cannot be 94 | // represented by int64. 95 | for _, i := range i64s { 96 | acc = acc.AddUint64(uint64(i)) 97 | } 98 | 99 | return acc.Value() 100 | } 101 | 102 | // Float64s returns the accumulated identity of f64s. 103 | func Float64s(f64s []float64) uint64 { 104 | if len(f64s) == 0 { 105 | return 0 106 | } 107 | 108 | // n.b. Wrapping due to overflow is okay here, since those values cannot be 109 | // represented by int64. 110 | acc := NewAccumulator() 111 | 112 | for _, f := range f64s { 113 | acc = acc.AddUint64(math.Float64bits(f)) 114 | } 115 | 116 | return acc.Value() 117 | } 118 | 119 | // StringStringMap returns the accumulated identity of m. 120 | func StringStringMap(m map[string]string) uint64 { 121 | if len(m) == 0 { 122 | return 0 123 | } 124 | 125 | acc := NewAccumulator() 126 | for k, v := range m { 127 | acc = acc.AddString(k + "=" + v) 128 | } 129 | 130 | return acc.Value() 131 | } 132 | -------------------------------------------------------------------------------- /internal/identity/accumulator_benchmark_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package identity_test 22 | 23 | import ( 24 | "fmt" 25 | "testing" 26 | 27 | "github.com/uber-go/tally/v4/internal/identity" 28 | ) 29 | 30 | func BenchmarkAccumulator_StringStringMap(b *testing.B) { 31 | cases := []struct { 32 | keys int 33 | }{ 34 | {keys: 0}, 35 | {keys: 1}, 36 | {keys: 2}, 37 | {keys: 4}, 38 | {keys: 8}, 39 | {keys: 16}, 40 | {keys: 32}, 41 | } 42 | 43 | bench := func(b *testing.B, m map[string]string) { 44 | b.ReportAllocs() 45 | b.ResetTimer() 46 | 47 | for i := 0; i < b.N; i++ { 48 | identity.StringStringMap(m) 49 | } 50 | } 51 | 52 | for _, tt := range cases { 53 | b.Run(fmt.Sprintf("%d keys", tt.keys), func(b *testing.B) { 54 | m := make(map[string]string) 55 | for i := 0; i < tt.keys; i++ { 56 | s := fmt.Sprintf("abcdefghij%d", i) 57 | m[s] = s 58 | } 59 | 60 | bench(b, m) 61 | }) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /key_gen.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package tally 22 | 23 | const ( 24 | prefixSplitter = '+' 25 | keyPairSplitter = ',' 26 | keyNameSplitter = '=' 27 | nilString = "" 28 | ) 29 | 30 | // KeyForStringMap generates a unique key for a map string set combination. 31 | func KeyForStringMap( 32 | stringMap map[string]string, 33 | ) string { 34 | return KeyForPrefixedStringMap(nilString, stringMap) 35 | } 36 | 37 | // KeyForPrefixedStringMap generates a unique key for a 38 | // a prefix and a map string set combination. 39 | func KeyForPrefixedStringMap( 40 | prefix string, 41 | stringMap map[string]string, 42 | ) string { 43 | return keyForPrefixedStringMaps(prefix, stringMap) 44 | } 45 | 46 | // keyForPrefixedStringMapsAsKey writes a key using the prefix and the tags in a canonical form to 47 | // the given input byte slice and returns a reference to the byte slice. Callers of this method can 48 | // use a stack allocated byte slice to remove heap allocation. 49 | func keyForPrefixedStringMapsAsKey(buf []byte, prefix string, maps ...map[string]string) []byte { 50 | // stack allocated 51 | keys := make([]string, 0, 32) 52 | for _, m := range maps { 53 | for k := range m { 54 | keys = append(keys, k) 55 | } 56 | } 57 | 58 | insertionSort(keys) 59 | 60 | if prefix != nilString { 61 | buf = append(buf, prefix...) 62 | buf = append(buf, prefixSplitter) 63 | } 64 | 65 | var lastKey string // last key written to the buffer 66 | for _, k := range keys { 67 | if len(lastKey) > 0 { 68 | if k == lastKey { 69 | // Already wrote this key. 70 | continue 71 | } 72 | buf = append(buf, keyPairSplitter) 73 | } 74 | lastKey = k 75 | 76 | buf = append(buf, k...) 77 | buf = append(buf, keyNameSplitter) 78 | 79 | // Find and write the value for this key. Rightmost map takes 80 | // precedence. 81 | for j := len(maps) - 1; j >= 0; j-- { 82 | if v, ok := maps[j][k]; ok { 83 | buf = append(buf, v...) 84 | break 85 | } 86 | } 87 | } 88 | 89 | return buf 90 | } 91 | 92 | // keyForPrefixedStringMaps generates a unique key for a prefix and a series 93 | // of maps containing tags. 94 | // 95 | // If a key occurs in multiple maps, keys on the right take precedence. 96 | func keyForPrefixedStringMaps(prefix string, maps ...map[string]string) string { 97 | return string(keyForPrefixedStringMapsAsKey(make([]byte, 0, 256), prefix, maps...)) 98 | } 99 | 100 | func insertionSort(keys []string) { 101 | n := len(keys) 102 | for i := 1; i < n; i++ { 103 | for j := i; j > 0 && keys[j] < keys[j-1]; j-- { 104 | keys[j], keys[j-1] = keys[j-1], keys[j] 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /key_gen_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package tally 22 | 23 | import ( 24 | "math/rand" 25 | "sort" 26 | "testing" 27 | 28 | "github.com/stretchr/testify/assert" 29 | ) 30 | 31 | func TestKeyForPrefixedStringMaps(t *testing.T) { 32 | tests := []struct { 33 | desc string 34 | prefix string 35 | maps []map[string]string 36 | want string 37 | }{ 38 | { 39 | desc: "no maps", 40 | prefix: "foo", 41 | want: "foo+", 42 | }, 43 | { 44 | desc: "disjoint maps", 45 | prefix: "foo", 46 | maps: []map[string]string{ 47 | { 48 | "a": "foo", 49 | "b": "bar", 50 | }, 51 | { 52 | "c": "baz", 53 | "d": "qux", 54 | }, 55 | }, 56 | want: "foo+a=foo,b=bar,c=baz,d=qux", 57 | }, 58 | { 59 | desc: "map overlap", 60 | prefix: "foo", 61 | maps: []map[string]string{ 62 | { 63 | "a": "1", 64 | "b": "1", 65 | "c": "1", 66 | "d": "1", 67 | }, 68 | {"b": "2"}, 69 | {"c": "3"}, 70 | {"d": "4"}, 71 | }, 72 | want: "foo+a=1,b=2,c=3,d=4", 73 | }, 74 | } 75 | 76 | for _, tt := range tests { 77 | t.Run(tt.desc, func(t *testing.T) { 78 | got := keyForPrefixedStringMaps(tt.prefix, tt.maps...) 79 | assert.Equal(t, tt.want, got) 80 | }) 81 | } 82 | } 83 | 84 | func TestInsertionSort(t *testing.T) { 85 | chars := []byte("abcdefghijklmnopqrstuvwxyz") 86 | n := len(chars) 87 | var expected []string 88 | var actual []string 89 | for i := 0; i < 12; i++ { 90 | s := rand.Intn(20) + 5 91 | var key []byte 92 | for j := 0; j < s; j++ { 93 | key = append(key, chars[rand.Intn(n)]) 94 | } 95 | expected = append(expected, string(key)) 96 | actual = append(actual, string(key)) 97 | } 98 | sort.Strings(expected) 99 | insertionSort(actual) 100 | assert.Equal(t, expected, actual) 101 | } 102 | -------------------------------------------------------------------------------- /m3/config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package m3 22 | 23 | // Configuration is a configuration for a M3 reporter. 24 | type Configuration struct { 25 | // HostPort is the host and port of the M3 server. 26 | HostPort string `yaml:"hostPort" validate:"nonzero"` 27 | 28 | // HostPorts are the host and port of the M3 server. 29 | HostPorts []string `yaml:"hostPorts"` 30 | 31 | // Service is the service tag to that this client emits. 32 | Service string `yaml:"service" validate:"nonzero"` 33 | 34 | // Env is the env tag to use that this client emits. 35 | Env string `yaml:"env" validate:"nonzero"` 36 | 37 | // CommonTags are tags that are common for all metrics this client emits. 38 | CommonTags map[string]string `yaml:"tags" ` 39 | 40 | // Queue is the maximum metric queue size of client. 41 | Queue int `yaml:"queue"` 42 | 43 | // PacketSize is the maximum packet size for a batch of metrics. 44 | PacketSize int32 `yaml:"packetSize"` 45 | 46 | // IncludeHost is whether or not to include host tag. 47 | IncludeHost bool `yaml:"includeHost"` 48 | 49 | // HistogramBucketTagPrecision is precision to use when formatting the metric tag 50 | // with the histogram bucket bound values. 51 | HistogramBucketTagPrecision uint `yaml:"histogramBucketTagPrecision"` 52 | 53 | // InternalTags are tags that should be added to all internal metrics 54 | // emitted by the reporter. 55 | InternalTags map[string]string `yaml:"internalTags"` 56 | } 57 | 58 | // NewReporter creates a new M3 reporter from this configuration. 59 | func (c Configuration) NewReporter() (Reporter, error) { 60 | hostPorts := c.HostPorts 61 | if len(hostPorts) == 0 { 62 | hostPorts = []string{c.HostPort} 63 | } 64 | return NewReporter(Options{ 65 | HostPorts: hostPorts, 66 | Service: c.Service, 67 | Env: c.Env, 68 | CommonTags: c.CommonTags, 69 | MaxQueueSize: c.Queue, 70 | MaxPacketSizeBytes: c.PacketSize, 71 | IncludeHost: c.IncludeHost, 72 | HistogramBucketTagPrecision: c.HistogramBucketTagPrecision, 73 | InternalTags: c.InternalTags, 74 | }) 75 | } 76 | -------------------------------------------------------------------------------- /m3/config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package m3 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/assert" 27 | "github.com/stretchr/testify/require" 28 | "github.com/uber-go/tally/v4/m3/thriftudp" 29 | ) 30 | 31 | func TestConfigSimple(t *testing.T) { 32 | c := Configuration{ 33 | HostPort: "127.0.0.1:9052", 34 | Service: "my-service", 35 | Env: "test", 36 | } 37 | r, err := c.NewReporter() 38 | require.NoError(t, err) 39 | 40 | reporter := r.(*reporter) 41 | _, ok := reporter.client.Transport.(*thriftudp.TUDPTransport) 42 | assert.True(t, ok) 43 | assert.True(t, tagEquals(reporter.commonTags, "service", "my-service")) 44 | assert.True(t, tagEquals(reporter.commonTags, "env", "test")) 45 | assert.Equal(t, 0, len(c.InternalTags)) 46 | } 47 | 48 | func TestConfigMulti(t *testing.T) { 49 | c := Configuration{ 50 | HostPorts: []string{"127.0.0.1:9052", "127.0.0.1:9062"}, 51 | Service: "my-service", 52 | Env: "test", 53 | } 54 | r, err := c.NewReporter() 55 | require.NoError(t, err) 56 | 57 | reporter := r.(*reporter) 58 | _, ok := reporter.client.Transport.(*thriftudp.TMultiUDPTransport) 59 | assert.True(t, ok) 60 | assert.True(t, tagEquals(reporter.commonTags, "service", "my-service")) 61 | assert.True(t, tagEquals(reporter.commonTags, "env", "test")) 62 | } 63 | -------------------------------------------------------------------------------- /m3/customtransports/buffered_read_transport.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package customtransport 22 | 23 | import ( 24 | "bytes" 25 | 26 | "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" 27 | ) 28 | 29 | // TBufferedReadTransport is a thrift.TTransport that reads from a buffer 30 | type TBufferedReadTransport struct { 31 | readBuf *bytes.Buffer 32 | } 33 | 34 | // NewTBufferedReadTransport creates a buffer backed TTransport 35 | func NewTBufferedReadTransport(readBuf *bytes.Buffer) (*TBufferedReadTransport, error) { 36 | return &TBufferedReadTransport{readBuf: readBuf}, nil 37 | } 38 | 39 | // IsOpen does nothing as transport is not maintaining the connection 40 | // Required to maintain thrift.TTransport interface 41 | func (p *TBufferedReadTransport) IsOpen() bool { 42 | return true 43 | } 44 | 45 | // Open does nothing as transport is not maintaining the connection 46 | // Required to maintain thrift.TTransport interface 47 | func (p *TBufferedReadTransport) Open() error { 48 | return nil 49 | } 50 | 51 | // Close does nothing as transport is not maintaining the connection 52 | // Required to maintain thrift.TTransport interface 53 | func (p *TBufferedReadTransport) Close() error { 54 | return nil 55 | } 56 | 57 | // Read reads bytes from the local buffer and puts them in the specified buf 58 | func (p *TBufferedReadTransport) Read(buf []byte) (int, error) { 59 | in, err := p.readBuf.Read(buf) 60 | return in, thrift.NewTTransportExceptionFromError(err) 61 | } 62 | 63 | // RemainingBytes returns the number of bytes left to be read from the readBuf 64 | func (p *TBufferedReadTransport) RemainingBytes() uint64 { 65 | return uint64(p.readBuf.Len()) 66 | } 67 | 68 | // Write writes bytes into the read buffer 69 | // Required to maintain thrift.TTransport interface 70 | func (p *TBufferedReadTransport) Write(buf []byte) (int, error) { 71 | p.readBuf = bytes.NewBuffer(buf) 72 | return len(buf), nil 73 | } 74 | 75 | // Flush does nothing as udp server does not write responses back 76 | // Required to maintain thrift.TTransport interface 77 | func (p *TBufferedReadTransport) Flush() error { 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /m3/customtransports/buffered_read_transport_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package customtransport 22 | 23 | import ( 24 | "bytes" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/require" 28 | ) 29 | 30 | // TestTBufferedReadTransport tests the TBufferedReadTransport 31 | func TestTBufferedReadTransport(t *testing.T) { 32 | buffer := bytes.NewBuffer([]byte("testString")) 33 | trans, err := NewTBufferedReadTransport(buffer) 34 | require.NotNil(t, trans) 35 | require.NoError(t, err) 36 | require.Equal(t, uint64(10), trans.RemainingBytes()) 37 | 38 | firstRead := make([]byte, 4) 39 | n, err := trans.Read(firstRead) 40 | require.NoError(t, err) 41 | require.Equal(t, 4, n) 42 | require.Equal(t, []byte("test"), firstRead) 43 | require.Equal(t, uint64(6), trans.RemainingBytes()) 44 | 45 | secondRead := make([]byte, 7) 46 | n, err = trans.Read(secondRead) 47 | require.Equal(t, 6, n) 48 | require.NoError(t, err) 49 | require.Equal(t, []byte("String"), secondRead[0:6]) 50 | require.Equal(t, uint64(0), trans.RemainingBytes()) 51 | } 52 | 53 | // TestTBufferedReadTransportEmptyFunctions tests the empty functions in TBufferedReadTransport 54 | func TestTBufferedReadTransportEmptyFunctions(t *testing.T) { 55 | byteArr := make([]byte, 1) 56 | trans, err := NewTBufferedReadTransport(bytes.NewBuffer(byteArr)) 57 | require.NotNil(t, trans) 58 | require.NoError(t, err) 59 | 60 | err = trans.Open() 61 | require.NoError(t, err) 62 | 63 | err = trans.Close() 64 | require.NoError(t, err) 65 | 66 | err = trans.Flush() 67 | require.NoError(t, err) 68 | 69 | n, err := trans.Write(byteArr) 70 | require.Equal(t, 1, n) 71 | require.NoError(t, err) 72 | 73 | isOpen := trans.IsOpen() 74 | require.True(t, isOpen) 75 | } 76 | -------------------------------------------------------------------------------- /m3/customtransports/m3_calc_transport.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package customtransport 22 | 23 | // TCalcTransport is a thrift TTransport that is used to calculate how many 24 | // bytes are used when writing a thrift element. 25 | type TCalcTransport struct { 26 | count int32 27 | } 28 | 29 | // GetCount returns the number of bytes that would be written 30 | // Required to maintain thrift.TTransport interface 31 | func (p *TCalcTransport) GetCount() int32 { 32 | return p.count 33 | } 34 | 35 | // ResetCount resets the number of bytes written to 0 36 | func (p *TCalcTransport) ResetCount() { 37 | p.count = 0 38 | } 39 | 40 | // Write adds the number of bytes written to the count 41 | // Required to maintain thrift.TTransport interface 42 | func (p *TCalcTransport) Write(buf []byte) (int, error) { 43 | p.count += int32(len(buf)) 44 | return len(buf), nil 45 | } 46 | 47 | // WriteByte adds 1 to the count 48 | // Required to maintain thrift.TRichTransport interface 49 | func (p *TCalcTransport) WriteByte(byte) error { 50 | p.count++ 51 | return nil 52 | } 53 | 54 | // WriteString adds the length of the string to the count 55 | // Required to maintain thrift.TRichTransport interface 56 | func (p *TCalcTransport) WriteString(s string) (int, error) { 57 | p.count += int32(len(s)) 58 | return len(s), nil 59 | } 60 | 61 | // IsOpen does nothing as transport is not maintaining a connection 62 | // Required to maintain thrift.TTransport interface 63 | func (p *TCalcTransport) IsOpen() bool { 64 | return true 65 | } 66 | 67 | // Open does nothing as transport is not maintaining a connection 68 | // Required to maintain thrift.TTransport interface 69 | func (p *TCalcTransport) Open() error { 70 | return nil 71 | } 72 | 73 | // Close does nothing as transport is not maintaining a connection 74 | // Required to maintain thrift.TTransport interface 75 | func (p *TCalcTransport) Close() error { 76 | return nil 77 | } 78 | 79 | // Read does nothing as it's not required for calculations 80 | // Required to maintain thrift.TTransport interface 81 | func (p *TCalcTransport) Read(buf []byte) (int, error) { 82 | return 0, nil 83 | } 84 | 85 | // ReadByte does nothing as it's not required for calculations 86 | // Required to maintain thrift.TRichTransport interface 87 | func (p *TCalcTransport) ReadByte() (byte, error) { 88 | return 0, nil 89 | } 90 | 91 | // RemainingBytes returns the max number of bytes (same as Thrift's StreamTransport) as we 92 | // do not know how many bytes we have left. 93 | func (p *TCalcTransport) RemainingBytes() uint64 { 94 | const maxSize = ^uint64(0) 95 | return maxSize 96 | } 97 | 98 | // Flush does nothing as it's not required for calculations 99 | // Required to maintain thrift.TTransport interface 100 | func (p *TCalcTransport) Flush() error { 101 | return nil 102 | } 103 | -------------------------------------------------------------------------------- /m3/customtransports/m3_calc_transport_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package customtransport 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/require" 27 | "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" 28 | ) 29 | 30 | // Make sure that TCalcTransport implements TRichTransport. 31 | // We use TRichTransport instead of TTransport to avoid unnecessary allocations 32 | // when writing string fields. See tests in 33 | var _ thrift.TRichTransport = (*TCalcTransport)(nil) 34 | 35 | func TestTCalcTransport(t *testing.T) { 36 | trans := &TCalcTransport{} 37 | require.Nil(t, trans.Open()) 38 | require.True(t, trans.IsOpen()) 39 | require.EqualValues(t, 0, trans.GetCount()) 40 | 41 | testString1 := "test" 42 | testString2 := "string" 43 | n, err := trans.Write([]byte(testString1)) 44 | require.Equal(t, len(testString1), n) 45 | require.NoError(t, err) 46 | require.EqualValues(t, len(testString1), trans.GetCount()) 47 | n, err = trans.Write([]byte(testString2)) 48 | require.EqualValues(t, len(testString2), n) 49 | require.NoError(t, err) 50 | require.EqualValues(t, len(testString1)+len(testString2), trans.GetCount()) 51 | 52 | trans.ResetCount() 53 | n, err = trans.WriteString(testString1) 54 | require.EqualValues(t, len(testString1), n) 55 | require.NoError(t, err) 56 | require.EqualValues(t, len(testString1), trans.GetCount()) 57 | 58 | err = trans.WriteByte('a') 59 | require.NoError(t, err) 60 | require.EqualValues(t, len(testString1)+1, trans.GetCount()) 61 | 62 | n, err = trans.Read([]byte(testString1)) 63 | require.NoError(t, err) 64 | require.EqualValues(t, 0, n) 65 | 66 | b, err := trans.ReadByte() 67 | require.NoError(t, err) 68 | require.Equal(t, byte(0), b) 69 | 70 | require.Equal(t, ^uint64(0), trans.RemainingBytes()) 71 | 72 | trans.ResetCount() 73 | require.EqualValues(t, 0, trans.GetCount()) 74 | 75 | err = trans.Flush() 76 | require.NoError(t, err) 77 | require.Nil(t, trans.Close()) 78 | } 79 | -------------------------------------------------------------------------------- /m3/example/README.md: -------------------------------------------------------------------------------- 1 | # M3 reporter example 2 | 3 | `go run ./*.go` 4 | -------------------------------------------------------------------------------- /m3/example/config.yaml: -------------------------------------------------------------------------------- 1 | m3: 2 | hostPort: 127.0.0.1:6396 3 | service: m3example 4 | env: staging 5 | tags: 6 | cluster: local 7 | region: us-east 8 | 9 | -------------------------------------------------------------------------------- /m3/example/local_server.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package main 22 | 23 | import ( 24 | "bytes" 25 | "fmt" 26 | "net" 27 | "sync/atomic" 28 | 29 | "github.com/uber-go/tally/v4/m3" 30 | customtransport "github.com/uber-go/tally/v4/m3/customtransports" 31 | m3thrift "github.com/uber-go/tally/v4/m3/thrift/v1" 32 | "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" 33 | ) 34 | 35 | type batchCallback func(batch *m3thrift.MetricBatch) 36 | 37 | type localM3Server struct { 38 | Service *localM3Service 39 | Addr string 40 | protocol m3.Protocol 41 | processor thrift.TProcessor 42 | conn *net.UDPConn 43 | closed int32 44 | } 45 | 46 | func newLocalM3Server( 47 | listenAddr string, 48 | protocol m3.Protocol, 49 | fn batchCallback, 50 | ) (*localM3Server, error) { 51 | udpAddr, err := net.ResolveUDPAddr("udp", listenAddr) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | service := newLocalM3Service(fn) 57 | processor := m3thrift.NewM3Processor(service) 58 | conn, err := net.ListenUDP(udpAddr.Network(), udpAddr) 59 | if err != nil { 60 | return nil, err 61 | } 62 | 63 | return &localM3Server{ 64 | Service: service, 65 | Addr: conn.LocalAddr().String(), 66 | conn: conn, 67 | protocol: protocol, 68 | processor: processor, 69 | }, nil 70 | } 71 | 72 | func (f *localM3Server) Serve() error { 73 | readBuf := make([]byte, 65536) 74 | for { 75 | n, err := f.conn.Read(readBuf) 76 | if err != nil { 77 | if atomic.LoadInt32(&f.closed) == 0 { 78 | return fmt.Errorf("failed to read: %v", err) 79 | } 80 | return nil 81 | } 82 | trans, _ := customtransport.NewTBufferedReadTransport(bytes.NewBuffer(readBuf[0:n])) 83 | var proto thrift.TProtocol 84 | if f.protocol == m3.Compact { 85 | proto = thrift.NewTCompactProtocol(trans) 86 | } else { 87 | proto = thrift.NewTBinaryProtocolTransport(trans) 88 | } 89 | 90 | if _, err = f.processor.Process(proto, proto); err != nil { 91 | fmt.Println("Error processing thrift metric:", err) 92 | } 93 | } 94 | } 95 | 96 | func (f *localM3Server) Close() error { 97 | atomic.AddInt32(&f.closed, 1) 98 | return f.conn.Close() 99 | } 100 | 101 | type localM3Service struct { 102 | fn batchCallback 103 | } 104 | 105 | func newLocalM3Service(fn batchCallback) *localM3Service { 106 | return &localM3Service{fn: fn} 107 | } 108 | 109 | func (m *localM3Service) EmitMetricBatch(batch *m3thrift.MetricBatch) (err error) { 110 | m.fn(batch) 111 | return thrift.NewTTransportException(thrift.END_OF_FILE, "complete") 112 | } 113 | -------------------------------------------------------------------------------- /m3/example/m3_main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package main 22 | 23 | import ( 24 | "flag" 25 | "fmt" 26 | "io/ioutil" 27 | "log" 28 | "math/rand" 29 | "os" 30 | "time" 31 | 32 | tally "github.com/uber-go/tally/v4" 33 | "github.com/uber-go/tally/v4/m3" 34 | m3thrift "github.com/uber-go/tally/v4/m3/thrift/v1" 35 | 36 | validator "gopkg.in/validator.v2" 37 | yaml "gopkg.in/yaml.v2" 38 | ) 39 | 40 | var configFileArg = flag.String("config", "config.yaml", "YAML config file path") 41 | 42 | type config struct { 43 | M3 m3.Configuration `yaml:"m3"` 44 | } 45 | 46 | func configFromFile(file string) (*config, error) { 47 | fd, err := os.Open(file) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | defer fd.Close() 53 | 54 | data, err := ioutil.ReadAll(fd) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | cfg := &config{} 60 | 61 | if err := yaml.Unmarshal(data, cfg); err != nil { 62 | return nil, err 63 | } 64 | if err := validator.Validate(cfg); err != nil { 65 | return nil, err 66 | } 67 | 68 | return cfg, nil 69 | } 70 | 71 | func main() { 72 | flag.Parse() 73 | configFile := *configFileArg 74 | if configFile == "" { 75 | flag.Usage() 76 | return 77 | } 78 | 79 | cfg, err := configFromFile(configFile) 80 | if err != nil { 81 | log.Fatalf("failed to read config file %s: %v", configFile, err) 82 | } 83 | 84 | r, err := cfg.M3.NewReporter() 85 | if err != nil { 86 | log.Fatalf("failed to create reporter: %v", err) 87 | } 88 | 89 | scope, closer := tally.NewRootScope(tally.ScopeOptions{ 90 | CachedReporter: r, 91 | CardinalityMetricsTags: cfg.M3.InternalTags, 92 | }, 1*time.Second) 93 | 94 | defer closer.Close() 95 | 96 | counter := scope.Tagged(map[string]string{ 97 | "foo": "bar", 98 | }).Counter("test_counter") 99 | 100 | gauge := scope.Tagged(map[string]string{ 101 | "foo": "baz", 102 | }).Gauge("test_gauge") 103 | 104 | timer := scope.Tagged(map[string]string{ 105 | "foo": "qux", 106 | }).Timer("test_timer_summary") 107 | 108 | histogram := scope.Tagged(map[string]string{ 109 | "foo": "quk", 110 | }).Histogram("test_histogram", tally.DefaultBuckets) 111 | 112 | go func() { 113 | for { 114 | counter.Inc(1) 115 | time.Sleep(time.Second) 116 | } 117 | }() 118 | 119 | go func() { 120 | for { 121 | gauge.Update(rand.Float64() * 1000) 122 | time.Sleep(time.Second) 123 | } 124 | }() 125 | 126 | go func() { 127 | for { 128 | tsw := timer.Start() 129 | hsw := histogram.Start() 130 | time.Sleep(time.Duration(rand.Float64() * float64(time.Second))) 131 | tsw.Stop() 132 | hsw.Stop() 133 | } 134 | }() 135 | 136 | srv, err := newLocalM3Server("127.0.0.1:6396", m3.Compact, func(b *m3thrift.MetricBatch) { 137 | for _, m := range b.Metrics { 138 | tags := make(map[string]string) 139 | for tag := range b.CommonTags { 140 | tags[tag.GetTagName()] = tag.GetTagValue() 141 | } 142 | for tag := range m.Tags { 143 | tags[tag.GetTagName()] = tag.GetTagValue() 144 | } 145 | metVal := m.GetMetricValue() 146 | switch { 147 | case metVal != nil && metVal.Count != nil: 148 | fmt.Printf("counter value: %d, tags: %v\n", metVal.Count.GetI64Value(), tags) 149 | case metVal != nil && metVal.Gauge != nil && metVal.Gauge.I64Value != nil: 150 | fmt.Printf("gauge value: %d, tags: %v\n", metVal.Gauge.GetI64Value(), tags) 151 | case metVal != nil && metVal.Gauge != nil && metVal.Gauge.DValue != nil: 152 | fmt.Printf("gauge value: %f, tags: %v\n", metVal.Gauge.GetDValue(), tags) 153 | case metVal != nil && metVal.Timer != nil: 154 | fmt.Printf("timer value: %v, tags: %v\n", time.Duration(metVal.Timer.GetI64Value()), tags) 155 | } 156 | } 157 | }) 158 | if err != nil { 159 | log.Fatalf("failed to create test listen server: %v", err) 160 | } 161 | if err := srv.Serve(); err != nil { 162 | log.Fatalf("failed to serve test listen server: %v", err) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /m3/reporter_benchmark_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package m3 22 | 23 | import ( 24 | "fmt" 25 | "testing" 26 | "time" 27 | 28 | tally "github.com/uber-go/tally/v4" 29 | ) 30 | 31 | func BenchmarkNewMetric(b *testing.B) { 32 | r, _ := NewReporter(Options{ 33 | HostPorts: []string{"127.0.0.1:9052"}, 34 | Service: "test-service", 35 | CommonTags: defaultCommonTags, 36 | }) 37 | defer r.Close() 38 | benchReporter := r.(*reporter) 39 | b.ResetTimer() 40 | 41 | for n := 0; n < b.N; n++ { 42 | benchReporter.newMetric("foo", nil, counterType) 43 | } 44 | } 45 | 46 | func BenchmarkEmitMetrics(b *testing.B) { 47 | r, _ := NewReporter(Options{ 48 | HostPorts: []string{"127.0.0.1:9052"}, 49 | Service: "test-service", 50 | CommonTags: defaultCommonTags, 51 | }) 52 | defer r.Close() 53 | 54 | benchReporter := r.(*reporter) 55 | 56 | counters := make([]tally.CachedCount, 100) 57 | for i := range counters { 58 | counters[i] = r.AllocateCounter(fmt.Sprintf("foo-%v", i), nil /* tags */) 59 | } 60 | 61 | for n := 0; n < b.N; n++ { 62 | for _, c := range counters { 63 | c.ReportCount(1) 64 | } 65 | 66 | benchReporter.Flush() 67 | } 68 | } 69 | 70 | func BenchmarkCalulateSize(b *testing.B) { 71 | r, _ := NewReporter(Options{ 72 | HostPorts: []string{"127.0.0.1:9052"}, 73 | Service: "test-service", 74 | CommonTags: defaultCommonTags, 75 | }) 76 | defer r.Close() 77 | benchReporter := r.(*reporter) 78 | 79 | met := benchReporter.newMetric("foo", map[string]string{"domain": "foo"}, counterType) 80 | met.Value.Count = 123456 81 | 82 | b.ResetTimer() 83 | 84 | for n := 0; n < b.N; n++ { 85 | benchReporter.calculateSize(met) 86 | } 87 | } 88 | 89 | func BenchmarkTimer(b *testing.B) { 90 | r, _ := NewReporter(Options{ 91 | HostPorts: []string{"127.0.0.1:9052"}, 92 | Service: "test-service", 93 | CommonTags: defaultCommonTags, 94 | MaxQueueSize: DefaultMaxQueueSize, 95 | }) 96 | 97 | defer r.Close() 98 | 99 | benchReporter := r.(*reporter) 100 | 101 | go func() { 102 | // Blindly consume metrics 103 | for range benchReporter.metCh { 104 | // nop 105 | } 106 | }() 107 | 108 | timer := benchReporter.AllocateTimer("foo", nil) 109 | 110 | b.ResetTimer() 111 | 112 | for n := 0; n < b.N; n++ { 113 | timer.ReportTimer(time.Duration(n) * time.Millisecond) 114 | } 115 | 116 | b.StopTimer() 117 | } 118 | -------------------------------------------------------------------------------- /m3/reporter_integration_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package m3 22 | 23 | import ( 24 | "fmt" 25 | "io/ioutil" 26 | "os" 27 | "os/exec" 28 | "path" 29 | "path/filepath" 30 | "sync" 31 | "testing" 32 | 33 | "github.com/stretchr/testify/require" 34 | ) 35 | 36 | var mainFileFmt = ` 37 | package main 38 | 39 | import ( 40 | "time" 41 | 42 | tally "github.com/uber-go/tally/v4" 43 | "github.com/uber-go/tally/v4/m3" 44 | ) 45 | 46 | func main() { 47 | r, err := m3.NewReporter(m3.Options{ 48 | HostPorts: []string{"%s"}, 49 | Service: "test-service", 50 | Env: "test", 51 | }) 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | scope, closer := tally.NewRootScope(tally.ScopeOptions{ 57 | CachedReporter: r, 58 | OmitCardinalityMetrics: true, 59 | }, 5 * time.Second) 60 | defer closer.Close() 61 | 62 | scope.Counter("my-counter").Inc(42) 63 | scope.Gauge("my-gauge").Update(123) 64 | scope.Timer("my-timer").Record(456 * time.Millisecond) 65 | } 66 | ` 67 | 68 | // TestIntegrationProcessFlushOnExit tests whether data is correctly flushed 69 | // when the scope is closed for shortly lived programs 70 | func TestIntegrationProcessFlushOnExit(t *testing.T) { 71 | for i := 0; i < 5; i++ { 72 | testProcessFlushOnExit(t, i) 73 | } 74 | } 75 | 76 | func testProcessFlushOnExit(t *testing.T, i int) { 77 | dir, err := os.MkdirTemp(".", "foo") 78 | require.NoError(t, err) 79 | defer os.RemoveAll(dir) 80 | 81 | dir, err = filepath.Abs(dir) 82 | require.NoError(t, err) 83 | 84 | var wg sync.WaitGroup 85 | server := newFakeM3Server(t, &wg, true, Compact) 86 | go server.Serve() 87 | defer server.Close() 88 | 89 | mainFile := path.Join(dir, "main.go") 90 | mainFileContents := fmt.Sprintf(mainFileFmt, server.Addr) 91 | 92 | fileErr := ioutil.WriteFile(mainFile, []byte(mainFileContents), 0o666) 93 | require.NoError(t, fileErr) 94 | 95 | binary := path.Join(dir, "m3testemit") 96 | 97 | // build 98 | cmd := exec.Command("go", "build", "-o", binary, mainFile) 99 | cmd.Dir = dir 100 | output, err := cmd.CombinedOutput() 101 | require.NoError(t, err, fmt.Sprintf("output:\n\n%s", output)) 102 | 103 | // run, do not sleep at end of the main program as per 104 | // main program source code 105 | wg.Add(1) 106 | require.NoError(t, exec.Command(binary).Run()) 107 | 108 | // Wait for fake M3 server to receive the batch 109 | wg.Wait() 110 | 111 | require.Equal(t, 1, len(server.Service.getBatches())) 112 | require.NotNil(t, server.Service.getBatches()[0]) 113 | // 3 metrics are emitted by mainFileFmt plus various other internal metrics. 114 | require.Equal(t, internalMetrics+3, len(server.Service.getBatches()[0].GetMetrics())) 115 | metrics := server.Service.getBatches()[0].GetMetrics() 116 | fmt.Printf("Test %d emitted:\n%v\n", i, metrics) 117 | } 118 | -------------------------------------------------------------------------------- /m3/resource_pool.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package m3 22 | 23 | import ( 24 | tally "github.com/uber-go/tally/v4" 25 | customtransport "github.com/uber-go/tally/v4/m3/customtransports" 26 | m3thrift "github.com/uber-go/tally/v4/m3/thrift/v2" 27 | "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" 28 | ) 29 | 30 | const ( 31 | batchPoolSize = 10 32 | metricPoolSize = DefaultMaxQueueSize 33 | protoPoolSize = 10 34 | ) 35 | 36 | type resourcePool struct { 37 | metricSlicePool *tally.ObjectPool 38 | metricTagSlicePool *tally.ObjectPool 39 | protoPool *tally.ObjectPool 40 | } 41 | 42 | func newResourcePool(protoFac thrift.TProtocolFactory) *resourcePool { 43 | metricSlicePool := tally.NewObjectPool(batchPoolSize) 44 | metricSlicePool.Init(func() interface{} { 45 | return make([]m3thrift.Metric, 0, batchPoolSize) 46 | }) 47 | 48 | metricTagSlicePool := tally.NewObjectPool(DefaultMaxQueueSize) 49 | metricTagSlicePool.Init(func() interface{} { 50 | return make([]m3thrift.MetricTag, 0, batchPoolSize) 51 | }) 52 | 53 | protoPool := tally.NewObjectPool(protoPoolSize) 54 | protoPool.Init(func() interface{} { 55 | return protoFac.GetProtocol(&customtransport.TCalcTransport{}) 56 | }) 57 | 58 | return &resourcePool{ 59 | metricSlicePool: metricSlicePool, 60 | metricTagSlicePool: metricTagSlicePool, 61 | protoPool: protoPool, 62 | } 63 | } 64 | 65 | func (r *resourcePool) getMetricSlice() []m3thrift.Metric { 66 | return r.metricSlicePool.Get().([]m3thrift.Metric) 67 | } 68 | 69 | func (r *resourcePool) getMetricTagSlice() []m3thrift.MetricTag { 70 | return r.metricTagSlicePool.Get().([]m3thrift.MetricTag) 71 | } 72 | 73 | func (r *resourcePool) getProto() thrift.TProtocol { 74 | o := r.protoPool.Get() 75 | return o.(thrift.TProtocol) 76 | } 77 | 78 | //nolint:unused 79 | func (r *resourcePool) releaseProto(proto thrift.TProtocol) { 80 | calc := proto.Transport().(*customtransport.TCalcTransport) 81 | calc.ResetCount() 82 | r.protoPool.Put(proto) 83 | } 84 | 85 | func (r *resourcePool) releaseMetricSlice(metrics []m3thrift.Metric) { 86 | for i := 0; i < len(metrics); i++ { 87 | metrics[i].Tags = nil 88 | } 89 | 90 | r.metricSlicePool.Put(metrics[:0]) 91 | } 92 | 93 | //nolint:unused 94 | func (r *resourcePool) releaseMetricTagSlice(tags []m3thrift.MetricTag) { 95 | r.metricSlicePool.Put(tags[:0]) 96 | } 97 | -------------------------------------------------------------------------------- /m3/resource_pool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package m3 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/require" 27 | m3thrift "github.com/uber-go/tally/v4/m3/thrift/v2" 28 | "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" 29 | ) 30 | 31 | func TestM3ResourcePoolMetric(t *testing.T) { 32 | p := newResourcePool(thrift.NewTCompactProtocolFactory()) 33 | 34 | metrics := p.getMetricSlice() 35 | metrics = append(metrics, m3thrift.Metric{}) 36 | require.Equal(t, 1, len(metrics)) 37 | p.releaseMetricSlice(metrics) 38 | metrics = p.getMetricSlice() 39 | require.Equal(t, 0, len(metrics)) 40 | 41 | tags := p.getMetricTagSlice() 42 | tags = append(tags, m3thrift.MetricTag{}) 43 | require.Equal(t, 1, len(tags)) 44 | p.releaseMetricTagSlice(tags) 45 | tags = p.getMetricTagSlice() 46 | require.Equal(t, 0, len(tags)) 47 | } 48 | -------------------------------------------------------------------------------- /m3/sanitize.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package m3 22 | 23 | import ( 24 | tally "github.com/uber-go/tally/v4" 25 | ) 26 | 27 | // DefaultSanitizerOpts are the options for the default M3 sanitizer. 28 | var DefaultSanitizerOpts = tally.SanitizeOptions{ 29 | NameCharacters: tally.ValidCharacters{ 30 | Ranges: tally.AlphanumericRange, 31 | Characters: tally.UnderscoreDashDotCharacters, 32 | }, 33 | KeyCharacters: tally.ValidCharacters{ 34 | Ranges: tally.AlphanumericRange, 35 | Characters: tally.UnderscoreDashCharacters, 36 | }, 37 | ValueCharacters: tally.ValidCharacters{ 38 | Ranges: tally.AlphanumericRange, 39 | Characters: tally.UnderscoreDashDotCharacters, 40 | }, 41 | ReplacementCharacter: tally.DefaultReplacementCharacter, 42 | } 43 | -------------------------------------------------------------------------------- /m3/thrift/Makefile: -------------------------------------------------------------------------------- 1 | gen-thrift: 2 | @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 3 | @echo Generated v1 Go Thrift in metrics/m3/thrift/v1. 4 | @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 5 | @echo Generated v2 Go Thrift in metrics/m3/thrift/v2. 6 | @git apply thrift.diff 7 | @echo Applied thrift.diff. 8 | -------------------------------------------------------------------------------- /m3/thrift/v1/constants.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (0.9.3) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | 4 | package v1 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" 10 | ) 11 | 12 | // (needed to ensure safety because of naive import list construction.) 13 | var _ = thrift.ZERO 14 | var _ = fmt.Printf 15 | var _ = bytes.Equal 16 | 17 | func init() { 18 | } 19 | -------------------------------------------------------------------------------- /m3/thrift/v1/v1.thrift: -------------------------------------------------------------------------------- 1 | /** 2 | * Different types of values that m3 emits. Each metric 3 | * must contain one of these values 4 | */ 5 | union MetricValue { 6 | 1: optional CountValue count 7 | 2: optional GaugeValue gauge 8 | 3: optional TimerValue timer 9 | } 10 | 11 | /** 12 | * Different types of count values 13 | */ 14 | union CountValue { 15 | 1: optional i64 i64Value 16 | } 17 | 18 | /** 19 | * Different types of gauge values 20 | */ 21 | union GaugeValue { 22 | 1: optional i64 i64Value 23 | 2: optional double dValue 24 | } 25 | 26 | /** 27 | * Different types of timer values 28 | */ 29 | union TimerValue { 30 | 1: optional i64 i64Value 31 | 2: optional double dValue 32 | } 33 | 34 | /** 35 | * Tags that can be applied to a metric 36 | */ 37 | struct MetricTag { 38 | 1: string tagName, 39 | 2: optional string tagValue 40 | } 41 | 42 | /** 43 | * The metric that is being emitted 44 | */ 45 | struct Metric { 46 | 1: string name, 47 | 2: optional MetricValue metricValue, 48 | 3: optional i64 timestamp, 49 | 4: optional set tags 50 | } 51 | 52 | /** 53 | * Structure that holds a group of metrics which share 54 | * common properties like the cluster and service. 55 | */ 56 | struct MetricBatch { 57 | 1: list metrics 58 | 2: optional set commonTags 59 | } 60 | 61 | /** 62 | * M3 Metrics Service 63 | */ 64 | service M3 { 65 | 66 | /** 67 | * Emits a batch of metrics. 68 | */ 69 | oneway void emitMetricBatch(1: MetricBatch batch) 70 | } 71 | -------------------------------------------------------------------------------- /m3/thrift/v2/constants.go: -------------------------------------------------------------------------------- 1 | // Autogenerated by Thrift Compiler (0.9.3) 2 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 3 | 4 | package v2 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" 10 | ) 11 | 12 | // (needed to ensure safety because of naive import list construction.) 13 | var _ = thrift.ZERO 14 | var _ = fmt.Printf 15 | var _ = bytes.Equal 16 | 17 | func init() { 18 | } 19 | -------------------------------------------------------------------------------- /m3/thrift/v2/v2.thrift: -------------------------------------------------------------------------------- 1 | enum MetricType { 2 | INVALID = 0 3 | COUNTER = 1 4 | GAUGE = 2 5 | TIMER = 3 6 | } 7 | 8 | struct MetricValue { 9 | 1: required MetricType metricType 10 | 2: required i64 count 11 | 3: required double gauge 12 | 4: required i64 timer 13 | } 14 | 15 | struct MetricTag { 16 | 1: required string name 17 | 2: required string value 18 | } 19 | 20 | struct Metric { 21 | 1: required string name 22 | 2: required MetricValue value 23 | 3: required i64 timestamp 24 | 4: optional list tags 25 | } 26 | 27 | struct MetricBatch { 28 | 1: required list metrics 29 | 2: optional list commonTags 30 | } 31 | 32 | service M3 { 33 | oneway void emitMetricBatchV2(1: MetricBatch batch) 34 | } -------------------------------------------------------------------------------- /m3/thriftudp/multitransport.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package thriftudp 22 | 23 | import ( 24 | "fmt" 25 | 26 | "github.com/uber-go/tally/v4/thirdparty/github.com/apache/thrift/lib/go/thrift" 27 | ) 28 | 29 | // TMultiUDPTransport does multiUDP as a thrift.TTransport 30 | type TMultiUDPTransport struct { 31 | transports []thrift.TTransport 32 | } 33 | 34 | // NewTMultiUDPClientTransport creates a set of net.UDPConn-backed TTransports for Thrift clients 35 | // All writes are buffered and flushed in one UDP packet. If locHostPort is not "", it 36 | // will be used as the local address for the connection 37 | // Example: 38 | // trans, err := thriftudp.NewTMultiUDPClientTransport([]string{"192.168.1.1:9090","192.168.1.2:9090"}, "") 39 | func NewTMultiUDPClientTransport( 40 | destHostPorts []string, 41 | locHostPort string, 42 | ) (*TMultiUDPTransport, error) { 43 | var transports []thrift.TTransport 44 | for i := range destHostPorts { 45 | trans, err := NewTUDPClientTransport(destHostPorts[i], locHostPort) 46 | if err != nil { 47 | return nil, err 48 | } 49 | transports = append(transports, trans) 50 | } 51 | 52 | return &TMultiUDPTransport{transports: transports}, nil 53 | } 54 | 55 | // Open the connections of the underlying transports 56 | func (p *TMultiUDPTransport) Open() error { 57 | for _, trans := range p.transports { 58 | if err := trans.Open(); err != nil { 59 | return err 60 | } 61 | } 62 | return nil 63 | } 64 | 65 | // IsOpen returns true if the connections of the underlying transports are open 66 | func (p *TMultiUDPTransport) IsOpen() bool { 67 | for _, trans := range p.transports { 68 | if open := trans.IsOpen(); !open { 69 | return false 70 | } 71 | } 72 | return true 73 | } 74 | 75 | // Close closes the connections of the underlying transports 76 | func (p *TMultiUDPTransport) Close() error { 77 | for _, trans := range p.transports { 78 | if err := trans.Close(); err != nil { 79 | return err 80 | } 81 | } 82 | return nil 83 | } 84 | 85 | // Read is not supported for multiple underlying transports 86 | func (p *TMultiUDPTransport) Read(buf []byte) (int, error) { 87 | // Not applicable, required by TTransport however 88 | return 0, fmt.Errorf("not supported") 89 | } 90 | 91 | // RemainingBytes is not supported for multiple underlying transports 92 | func (p *TMultiUDPTransport) RemainingBytes() uint64 { 93 | // Not applicable, required by TTransport however 94 | return 0 95 | } 96 | 97 | // Write writes specified buf to the write buffer of underlying transports 98 | func (p *TMultiUDPTransport) Write(buff []byte) (int, error) { 99 | n := 0 100 | for _, trans := range p.transports { 101 | written, err := trans.Write(buff) 102 | if err != nil { 103 | return n, err 104 | } 105 | if written > n { 106 | n = written 107 | } 108 | } 109 | return n, nil 110 | } 111 | 112 | // Flush flushes the write buffer of the underlying transports 113 | func (p *TMultiUDPTransport) Flush() error { 114 | for _, trans := range p.transports { 115 | if err := trans.Flush(); err != nil { 116 | return err 117 | } 118 | } 119 | return nil 120 | } 121 | -------------------------------------------------------------------------------- /multi/README.md: -------------------------------------------------------------------------------- 1 | # A buffered multi reporter 2 | 3 | Combine reporters to emit to many backends. 4 | 5 | Multiple `tally.StatsReporter` as a single reporter: 6 | ```go 7 | reporter := NewMultiReporter(statsdReporter, ...) 8 | ``` 9 | 10 | Multiple `tally.CachedStatsReporter` as a single reporter: 11 | ```go 12 | reporter := NewMultiCachedReporter(m3Reporter, promReporter, ...) 13 | ``` 14 | -------------------------------------------------------------------------------- /pool.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package tally 22 | 23 | // ObjectPool is an minimalistic object pool to avoid 24 | // any circular dependencies on any other object pool. 25 | type ObjectPool struct { 26 | values chan interface{} 27 | alloc func() interface{} 28 | } 29 | 30 | // NewObjectPool creates a new pool. 31 | func NewObjectPool(size int) *ObjectPool { 32 | return &ObjectPool{ 33 | values: make(chan interface{}, size), 34 | } 35 | } 36 | 37 | // Init initializes the object pool. 38 | func (p *ObjectPool) Init(alloc func() interface{}) { 39 | p.alloc = alloc 40 | 41 | for i := 0; i < cap(p.values); i++ { 42 | p.values <- p.alloc() 43 | } 44 | } 45 | 46 | // Get gets an object from the pool. 47 | func (p *ObjectPool) Get() interface{} { 48 | var v interface{} 49 | select { 50 | case v = <-p.values: 51 | default: 52 | v = p.alloc() 53 | } 54 | return v 55 | } 56 | 57 | // Put puts an object back to the pool. 58 | func (p *ObjectPool) Put(obj interface{}) { 59 | select { 60 | case p.values <- obj: 61 | default: 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /prometheus/README.md: -------------------------------------------------------------------------------- 1 | # A buffered Prometheus reporter 2 | 3 | See `examples/prometheus_main.go` for an end to end example. 4 | 5 | ## Options 6 | 7 | You can use a specific Prometheus registry, and you can use 8 | either summaries or histograms for timers. 9 | 10 | The reporter options are: 11 | 12 | ```go 13 | // Options is a set of options for the tally reporter. 14 | type Options struct { 15 | // Registerer is the prometheus registerer to register 16 | // metrics with. Use nil to specify the default registerer. 17 | Registerer prom.Registerer 18 | 19 | // DefaultTimerType is the default type timer type to create 20 | // when using timers. It's default value is a histogram timer type. 21 | DefaultTimerType TimerType 22 | 23 | // DefaultHistogramBuckets is the default histogram buckets 24 | // to use. Use nil to specify the default histogram buckets. 25 | DefaultHistogramBuckets []float64 26 | 27 | // DefaultSummaryObjectives is the default summary objectives 28 | // to use. Use nil to specify the default summary objectives. 29 | DefaultSummaryObjectives map[float64]float64 30 | 31 | // OnRegisterError defines a method to call to when registering 32 | // a metric with the registerer fails. Use nil to specify 33 | // to panic by default when registering a metric fails. 34 | OnRegisterError func(err error) 35 | } 36 | ``` 37 | 38 | The timer types are: 39 | 40 | ```go 41 | // TimerType describes a type of timer 42 | type TimerType int 43 | 44 | const ( 45 | // SummaryTimerType is a timer type that reports into a summary 46 | SummaryTimerType TimerType = iota 47 | 48 | // HistogramTimerType is a timer type that reports into a histogram 49 | HistogramTimerType 50 | ) 51 | ``` 52 | 53 | You can also pre-register help description text ahead of using a metric 54 | that will be named and tagged identically with `tally`. You can also 55 | access the Prometheus HTTP handler directly. 56 | 57 | The returned reporter interface: 58 | 59 | ```go 60 | // Reporter is a Prometheus backed tally reporter. 61 | type Reporter interface { 62 | tally.CachedStatsReporter 63 | 64 | // HTTPHandler provides the Prometheus HTTP scrape handler. 65 | HTTPHandler() http.Handler 66 | 67 | // RegisterCounter is a helper method to initialize a counter 68 | // in the Prometheus backend with a given help text. 69 | // If not called explicitly, the Reporter will create one for 70 | // you on first use, with a not super helpful HELP string. 71 | RegisterCounter( 72 | name string, 73 | tagKeys []string, 74 | desc string, 75 | ) (*prom.CounterVec, error) 76 | 77 | // RegisterGauge is a helper method to initialize a gauge 78 | // in the prometheus backend with a given help text. 79 | // If not called explicitly, the Reporter will create one for 80 | // you on first use, with a not super helpful HELP string. 81 | RegisterGauge( 82 | name string, 83 | tagKeys []string, 84 | desc string, 85 | ) (*prom.GaugeVec, error) 86 | 87 | // RegisterTimer is a helper method to initialize a timer 88 | // summary or histogram vector in the prometheus backend 89 | // with a given help text. 90 | // If not called explicitly, the Reporter will create one for 91 | // you on first use, with a not super helpful HELP string. 92 | // You may pass opts as nil to get the default timer type 93 | // and objectives/buckets. 94 | // You may also pass objectives/buckets as nil in opts to 95 | // get the default objectives/buckets for the specified 96 | // timer type. 97 | RegisterTimer( 98 | name string, 99 | tagKeys []string, 100 | desc string, 101 | opts *RegisterTimerOptions, 102 | ) (TimerUnion, error) 103 | } 104 | ``` 105 | 106 | The register timer options: 107 | 108 | ```go 109 | // RegisterTimerOptions provides options when registering a timer on demand. 110 | // By default you can pass nil for the options to get the reporter defaults. 111 | type RegisterTimerOptions struct { 112 | TimerType TimerType 113 | HistogramBuckets []float64 114 | SummaryObjectives map[float64]float64 115 | } 116 | ``` 117 | 118 | 119 | -------------------------------------------------------------------------------- /prometheus/config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package prometheus 22 | 23 | import ( 24 | "io/ioutil" 25 | "net" 26 | "os" 27 | "path" 28 | "testing" 29 | "time" 30 | 31 | "github.com/stretchr/testify/assert" 32 | "github.com/stretchr/testify/require" 33 | ) 34 | 35 | func TestListenErrorCallsOnRegisterError(t *testing.T) { 36 | // Ensure that Listen error calls default OnRegisterError to panic 37 | listener, err := net.Listen("tcp", "127.0.0.1:0") 38 | require.NoError(t, err) 39 | 40 | defer func() { _ = listener.Close() }() 41 | 42 | assert.NotPanics(t, func() { 43 | cfg := Configuration{ 44 | ListenAddress: listener.Addr().String(), 45 | OnError: "log", 46 | } 47 | _, _ = cfg.NewReporter(ConfigurationOptions{}) 48 | time.Sleep(time.Second) 49 | }) 50 | } 51 | 52 | func TestUnixDomainSocketListener(t *testing.T) { 53 | dir, err := ioutil.TempDir("", "tally-test-prometheus") 54 | require.NoError(t, err) 55 | 56 | defer os.RemoveAll(dir) 57 | 58 | uds := path.Join(dir, "test-metrics.sock") 59 | cfg := Configuration{ListenNetwork: "unix", ListenAddress: uds} 60 | _, _ = cfg.NewReporter(ConfigurationOptions{}) 61 | 62 | time.Sleep(time.Second) 63 | _, err = os.Stat(uds) 64 | require.NoError(t, err) 65 | require.NoError(t, os.Remove(uds)) 66 | } 67 | 68 | func TestTcpListener(t *testing.T) { 69 | cases := map[string]Configuration{ 70 | "127.0.0.1:0": {ListenAddress: "127.0.0.1:0"}, 71 | "tcp://127.0.0.1:0": {ListenNetwork: "tcp", ListenAddress: "127.0.0.1:0"}, 72 | "tcp4://127.0.0.1:0": {ListenNetwork: "tcp4", ListenAddress: "127.0.0.1:0"}, 73 | } 74 | 75 | for cn, cc := range cases { 76 | t.Run(cn, func(t *testing.T) { 77 | assert.NotPanics(t, func() { 78 | _, _ = cc.NewReporter(ConfigurationOptions{}) 79 | time.Sleep(time.Second) 80 | }) 81 | }) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /prometheus/example/README.md: -------------------------------------------------------------------------------- 1 | # Prometheus reporter example 2 | 3 | `go run ./*.go` 4 | -------------------------------------------------------------------------------- /prometheus/example/prometheus_main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package main 22 | 23 | import ( 24 | "fmt" 25 | "math/rand" 26 | "net/http" 27 | "time" 28 | 29 | tally "github.com/uber-go/tally/v4" 30 | promreporter "github.com/uber-go/tally/v4/prometheus" 31 | ) 32 | 33 | func main() { 34 | r := promreporter.NewReporter(promreporter.Options{}) 35 | 36 | // Note: `promreporter.DefaultSeparator` is "_". 37 | // Prometheus doesnt like metrics with "." or "-" in them. 38 | scope, closer := tally.NewRootScope(tally.ScopeOptions{ 39 | Prefix: "my_service", 40 | Tags: map[string]string{}, 41 | CachedReporter: r, 42 | Separator: promreporter.DefaultSeparator, 43 | }, 1*time.Second) 44 | defer closer.Close() 45 | 46 | counter := scope.Tagged(map[string]string{ 47 | "foo": "bar", 48 | }).Counter("test_counter") 49 | 50 | gauge := scope.Tagged(map[string]string{ 51 | "foo": "baz", 52 | }).Gauge("test_gauge") 53 | 54 | timer := scope.Tagged(map[string]string{ 55 | "foo": "qux", 56 | }).Timer("test_timer_summary") 57 | 58 | histogram := scope.Tagged(map[string]string{ 59 | "foo": "quk", 60 | }).Histogram("test_histogram", tally.DefaultBuckets) 61 | 62 | go func() { 63 | for { 64 | counter.Inc(1) 65 | time.Sleep(time.Second) 66 | } 67 | }() 68 | 69 | go func() { 70 | for { 71 | gauge.Update(rand.Float64() * 1000) 72 | time.Sleep(time.Second) 73 | } 74 | }() 75 | 76 | go func() { 77 | for { 78 | tsw := timer.Start() 79 | hsw := histogram.Start() 80 | time.Sleep(time.Duration(rand.Float64() * float64(time.Second))) 81 | tsw.Stop() 82 | hsw.Stop() 83 | } 84 | }() 85 | 86 | http.Handle("/metrics", r.HTTPHandler()) 87 | fmt.Printf("Serving :8080/metrics\n") 88 | fmt.Printf("%v\n", http.ListenAndServe(":8080", nil)) 89 | select {} 90 | } 91 | -------------------------------------------------------------------------------- /prometheus/sanitize.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package prometheus 22 | 23 | import ( 24 | tally "github.com/uber-go/tally/v4" 25 | ) 26 | 27 | // DefaultSanitizerOpts are the options for the default Prometheus sanitizer. 28 | var DefaultSanitizerOpts = tally.SanitizeOptions{ 29 | NameCharacters: tally.ValidCharacters{ 30 | Ranges: tally.AlphanumericRange, 31 | Characters: tally.UnderscoreCharacters, 32 | }, 33 | KeyCharacters: tally.ValidCharacters{ 34 | Ranges: tally.AlphanumericRange, 35 | Characters: tally.UnderscoreCharacters, 36 | }, 37 | ValueCharacters: tally.ValidCharacters{ 38 | Ranges: tally.AlphanumericRange, 39 | Characters: tally.UnderscoreCharacters, 40 | }, 41 | ReplacementCharacter: tally.DefaultReplacementCharacter, 42 | } 43 | -------------------------------------------------------------------------------- /reporter.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package tally 22 | 23 | import "time" 24 | 25 | // BaseStatsReporter implements the shared reporter methods. 26 | type BaseStatsReporter interface { 27 | // Capabilities returns the capabilities description of the reporter. 28 | Capabilities() Capabilities 29 | 30 | // Flush asks the reporter to flush all reported values. 31 | Flush() 32 | } 33 | 34 | // StatsReporter is a backend for Scopes to report metrics to. 35 | type StatsReporter interface { 36 | BaseStatsReporter 37 | 38 | // ReportCounter reports a counter value 39 | ReportCounter( 40 | name string, 41 | tags map[string]string, 42 | value int64, 43 | ) 44 | 45 | // ReportGauge reports a gauge value 46 | ReportGauge( 47 | name string, 48 | tags map[string]string, 49 | value float64, 50 | ) 51 | 52 | // ReportTimer reports a timer value 53 | ReportTimer( 54 | name string, 55 | tags map[string]string, 56 | interval time.Duration, 57 | ) 58 | 59 | // ReportHistogramValueSamples reports histogram samples for a bucket 60 | ReportHistogramValueSamples( 61 | name string, 62 | tags map[string]string, 63 | buckets Buckets, 64 | bucketLowerBound, 65 | bucketUpperBound float64, 66 | samples int64, 67 | ) 68 | 69 | // ReportHistogramDurationSamples reports histogram samples for a bucket 70 | ReportHistogramDurationSamples( 71 | name string, 72 | tags map[string]string, 73 | buckets Buckets, 74 | bucketLowerBound, 75 | bucketUpperBound time.Duration, 76 | samples int64, 77 | ) 78 | } 79 | 80 | // CachedStatsReporter is a backend for Scopes that pre allocates all 81 | // counter, gauges, timers & histograms. This is harder to implement but more performant. 82 | type CachedStatsReporter interface { 83 | BaseStatsReporter 84 | 85 | // AllocateCounter pre allocates a counter data structure with name & tags. 86 | AllocateCounter( 87 | name string, 88 | tags map[string]string, 89 | ) CachedCount 90 | 91 | // AllocateGauge pre allocates a gauge data structure with name & tags. 92 | AllocateGauge( 93 | name string, 94 | tags map[string]string, 95 | ) CachedGauge 96 | 97 | // AllocateTimer pre allocates a timer data structure with name & tags. 98 | AllocateTimer( 99 | name string, 100 | tags map[string]string, 101 | ) CachedTimer 102 | 103 | // AllocateHistogram pre allocates a histogram data structure with name, tags, 104 | // value buckets and duration buckets. 105 | AllocateHistogram( 106 | name string, 107 | tags map[string]string, 108 | buckets Buckets, 109 | ) CachedHistogram 110 | } 111 | 112 | // CachedCount interface for reporting an individual counter 113 | type CachedCount interface { 114 | ReportCount(value int64) 115 | } 116 | 117 | // CachedGauge interface for reporting an individual gauge 118 | type CachedGauge interface { 119 | ReportGauge(value float64) 120 | } 121 | 122 | // CachedTimer interface for reporting an individual timer 123 | type CachedTimer interface { 124 | ReportTimer(interval time.Duration) 125 | } 126 | 127 | // CachedHistogram interface for reporting histogram samples to buckets 128 | type CachedHistogram interface { 129 | ValueBucket( 130 | bucketLowerBound, bucketUpperBound float64, 131 | ) CachedHistogramBucket 132 | DurationBucket( 133 | bucketLowerBound, bucketUpperBound time.Duration, 134 | ) CachedHistogramBucket 135 | } 136 | 137 | // CachedHistogramBucket interface for reporting histogram samples to a specific bucket 138 | type CachedHistogramBucket interface { 139 | ReportSamples(value int64) 140 | } 141 | -------------------------------------------------------------------------------- /sanitize_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package tally 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/require" 27 | ) 28 | 29 | func newTestSanitizer() SanitizeFn { 30 | c := &ValidCharacters{ 31 | Ranges: AlphanumericRange, 32 | Characters: UnderscoreDashCharacters, 33 | } 34 | return c.sanitizeFn(DefaultReplacementCharacter) 35 | } 36 | 37 | func TestSanitizeIdentifierAllValidCharacters(t *testing.T) { 38 | allValidChars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" 39 | fn := newTestSanitizer() 40 | require.Equal(t, allValidChars, fn(allValidChars)) 41 | } 42 | 43 | func TestSanitizeTestCases(t *testing.T) { 44 | fn := newTestSanitizer() 45 | type testCase struct { 46 | input string 47 | output string 48 | } 49 | 50 | testCases := []testCase{ 51 | {"abcdef0AxS-s_Z", "abcdef0AxS-s_Z"}, 52 | {"a:b", "a_b"}, 53 | {"a! b", "a__b"}, 54 | {"?bZ", "_bZ"}, 55 | } 56 | 57 | for _, tc := range testCases { 58 | require.Equal(t, tc.output, fn(tc.input)) 59 | } 60 | } 61 | 62 | func BenchmarkSanitizeFn(b *testing.B) { 63 | sanitize := newTestSanitizer() 64 | b.ResetTimer() 65 | for i := 0; i < b.N; i++ { 66 | _ = sanitize("foo bar") 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /scope_registry_external_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package tally_test 22 | 23 | import ( 24 | "io" 25 | "sync" 26 | "testing" 27 | "time" 28 | 29 | "github.com/golang/mock/gomock" 30 | "github.com/stretchr/testify/require" 31 | "github.com/uber-go/tally/v4" 32 | "github.com/uber-go/tally/v4/tallymock" 33 | "go.uber.org/atomic" 34 | ) 35 | 36 | func TestTestScopesNotPruned(t *testing.T) { 37 | var ( 38 | root = tally.NewTestScope("", nil) 39 | subscope = root.SubScope("foo") 40 | counter = subscope.Counter("bar") 41 | ) 42 | 43 | counter.Inc(123) 44 | 45 | closer, ok := subscope.(io.Closer) 46 | require.True(t, ok) 47 | require.NoError(t, closer.Close()) 48 | 49 | subscope = root.SubScope("foo") 50 | counter = subscope.Counter("bar") 51 | counter.Inc(123) 52 | 53 | var ( 54 | snapshot = root.Snapshot() 55 | counters = snapshot.Counters() 56 | ) 57 | require.Len(t, counters, 1) 58 | require.Len(t, snapshot.Gauges(), 0) 59 | require.Len(t, snapshot.Timers(), 0) 60 | require.Len(t, snapshot.Histograms(), 0) 61 | 62 | val, ok := counters["foo.bar+"] 63 | require.True(t, ok) 64 | require.Equal(t, "foo.bar", val.Name()) 65 | require.EqualValues(t, 246, val.Value()) 66 | } 67 | 68 | func TestNoDefunctSubscopes(t *testing.T) { 69 | ctrl := gomock.NewController(t) 70 | defer ctrl.Finish() 71 | 72 | var ( 73 | tags = map[string]string{ 74 | "hello": "world", 75 | } 76 | mockreporter = tallymock.NewMockStatsReporter(ctrl) 77 | ready = make(chan struct{}) 78 | closed atomic.Bool 79 | wg sync.WaitGroup 80 | ) 81 | wg.Add(2) 82 | 83 | mockreporter.EXPECT(). 84 | ReportCounter("a", gomock.Any(), int64(123)). 85 | Do(func(_ string, _ map[string]string, _ int64) { 86 | wg.Done() 87 | }). 88 | Times(1) 89 | mockreporter.EXPECT(). 90 | ReportCounter("b", gomock.Any(), int64(456)). 91 | Do(func(_ string, _ map[string]string, _ int64) { 92 | wg.Done() 93 | }). 94 | Times(1) 95 | 96 | // Use flushing as a signal to determine if/when a closed scope 97 | // would be removed from the registry's cache. 98 | mockreporter.EXPECT(). 99 | Flush(). 100 | Do(func() { 101 | // Don't unblock the ready channel until we've explicitly 102 | // closed the scope. 103 | if !closed.Load() { 104 | return 105 | } 106 | 107 | select { 108 | case <-ready: 109 | default: 110 | close(ready) 111 | } 112 | }). 113 | MinTimes(1) 114 | 115 | root, _ := tally.NewRootScope(tally.ScopeOptions{ 116 | Reporter: mockreporter, 117 | OmitCardinalityMetrics: true, 118 | }, time.Millisecond) 119 | 120 | subscope := root.Tagged(tags) 121 | requireClose(t, subscope) 122 | subscope = root.Tagged(tags) 123 | 124 | // Signal and wait for the next flush to ensure that subscope can 125 | // be a closed scope. 126 | closed.Store(true) 127 | <-ready 128 | 129 | // Use the maybe-closed subscope for counter A. 130 | subscope.Counter("a").Inc(123) 131 | 132 | // Guarantee that counter B will not use a closed subscope. 133 | subscope = root.Tagged(tags) 134 | subscope.Counter("b").Inc(456) 135 | 136 | requireClose(t, root) 137 | wg.Wait() 138 | } 139 | 140 | func requireClose(t *testing.T, scope tally.Scope) { 141 | x, ok := scope.(io.Closer) 142 | require.True(t, ok) 143 | require.NoError(t, x.Close()) 144 | } 145 | -------------------------------------------------------------------------------- /stats_benchmark_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package tally 22 | 23 | import ( 24 | "testing" 25 | "time" 26 | ) 27 | 28 | func BenchmarkCounterInc(b *testing.B) { 29 | c := &counter{} 30 | for n := 0; n < b.N; n++ { 31 | c.Inc(1) 32 | } 33 | } 34 | 35 | func BenchmarkReportCounterNoData(b *testing.B) { 36 | c := &counter{} 37 | for n := 0; n < b.N; n++ { 38 | c.report("foo", nil, NullStatsReporter) 39 | } 40 | } 41 | 42 | func BenchmarkReportCounterWithData(b *testing.B) { 43 | c := &counter{} 44 | for n := 0; n < b.N; n++ { 45 | c.Inc(1) 46 | c.report("foo", nil, NullStatsReporter) 47 | } 48 | } 49 | 50 | func BenchmarkGaugeSet(b *testing.B) { 51 | g := &gauge{} 52 | for n := 0; n < b.N; n++ { 53 | g.Update(42) 54 | } 55 | } 56 | 57 | func BenchmarkReportGaugeNoData(b *testing.B) { 58 | g := &gauge{} 59 | for n := 0; n < b.N; n++ { 60 | g.report("bar", nil, NullStatsReporter) 61 | } 62 | } 63 | 64 | func BenchmarkReportGaugeWithData(b *testing.B) { 65 | g := &gauge{} 66 | for n := 0; n < b.N; n++ { 67 | g.Update(73) 68 | g.report("bar", nil, NullStatsReporter) 69 | } 70 | } 71 | 72 | func BenchmarkTimerStopwatch(b *testing.B) { 73 | t := &timer{ 74 | name: "bencher", 75 | tags: nil, 76 | reporter: NullStatsReporter, 77 | } 78 | for n := 0; n < b.N; n++ { 79 | t.Start().Stop() // start and stop 80 | } 81 | } 82 | 83 | func BenchmarkTimerReport(b *testing.B) { 84 | t := &timer{ 85 | name: "bencher", 86 | tags: nil, 87 | reporter: NullStatsReporter, 88 | } 89 | for n := 0; n < b.N; n++ { 90 | start := time.Now() 91 | t.Record(time.Since(start)) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /statsd/README.md: -------------------------------------------------------------------------------- 1 | # A buffered statsd reporter 2 | 3 | See `examples/statsd_main.go` for an end to end example. 4 | 5 | Some emitted stats using the example listening with `nc 8125 -l -u`: 6 | 7 | ``` 8 | stats.my-service.test-histogram.100ms-200ms:2|c 9 | stats.my-service.test-histogram.300ms-400ms:1|c 10 | stats.my-service.test-histogram.600ms-800ms:1|c 11 | stats.my-service.test-counter:1|c 12 | stats.my-service.test-gauge:813|g 13 | ``` 14 | 15 | ## Options 16 | 17 | You can use either a basic or a buffered statsd client 18 | and pass it to the reporter along with options. 19 | 20 | The reporter options are: 21 | 22 | ```go 23 | // Options is a set of options for the tally reporter. 24 | type Options struct { 25 | // SampleRate is the metrics emission sample rate. If you 26 | // do not set this value it will be set to 1. 27 | SampleRate float32 28 | } 29 | ``` 30 | -------------------------------------------------------------------------------- /statsd/example/README.md: -------------------------------------------------------------------------------- 1 | # Statsd reporter example 2 | 3 | `go run ./*.go` 4 | -------------------------------------------------------------------------------- /statsd/example/statsd_main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package main 22 | 23 | import ( 24 | "log" 25 | "math/rand" 26 | "time" 27 | 28 | "github.com/cactus/go-statsd-client/v5/statsd" 29 | tally "github.com/uber-go/tally/v4" 30 | statsdreporter "github.com/uber-go/tally/v4/statsd" 31 | ) 32 | 33 | // To view statsd emitted metrics locally you can use 34 | // netcat with "nc 8125 -l -u" 35 | func main() { 36 | statter, err := statsd.NewClientWithConfig(&statsd.ClientConfig{ 37 | Address: "127.0.0.1:8125", 38 | Prefix: "stats", 39 | UseBuffered: true, 40 | FlushInterval: 100 * time.Millisecond, 41 | FlushBytes: 1440, 42 | }) 43 | if err != nil { 44 | log.Fatalf("could not create statsd client: %v", err) 45 | } 46 | 47 | opts := statsdreporter.Options{} 48 | r := statsdreporter.NewReporter(statter, opts) 49 | 50 | scope, closer := tally.NewRootScope(tally.ScopeOptions{ 51 | Prefix: "my-service", 52 | Tags: map[string]string{}, 53 | Reporter: r, 54 | }, 1*time.Second) 55 | defer closer.Close() 56 | 57 | counter := scope.Counter("test-counter") 58 | 59 | gauge := scope.Gauge("test-gauge") 60 | 61 | timer := scope.Timer("test-timer") 62 | 63 | histogram := scope.Histogram("test-histogram", tally.DefaultBuckets) 64 | 65 | go func() { 66 | for { 67 | counter.Inc(1) 68 | time.Sleep(time.Second) 69 | } 70 | }() 71 | 72 | go func() { 73 | for { 74 | gauge.Update(rand.Float64() * 1000) 75 | time.Sleep(time.Second) 76 | } 77 | }() 78 | 79 | go func() { 80 | for { 81 | tsw := timer.Start() 82 | hsw := histogram.Start() 83 | time.Sleep(time.Duration(rand.Float64() * float64(time.Second))) 84 | tsw.Stop() 85 | hsw.Stop() 86 | } 87 | }() 88 | 89 | select {} 90 | } 91 | -------------------------------------------------------------------------------- /statsd/reporter_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package statsd 22 | 23 | import ( 24 | "testing" 25 | 26 | "github.com/stretchr/testify/assert" 27 | ) 28 | 29 | func TestCapabilities(t *testing.T) { 30 | r := NewReporter(nil, Options{}) 31 | assert.True(t, r.Capabilities().Reporting()) 32 | assert.False(t, r.Capabilities().Tagging()) 33 | } 34 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/application_exception.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | const ( 23 | UNKNOWN_APPLICATION_EXCEPTION = 0 24 | UNKNOWN_METHOD = 1 25 | INVALID_MESSAGE_TYPE_EXCEPTION = 2 26 | WRONG_METHOD_NAME = 3 27 | BAD_SEQUENCE_ID = 4 28 | MISSING_RESULT = 5 29 | INTERNAL_ERROR = 6 30 | PROTOCOL_ERROR = 7 31 | ) 32 | 33 | // Application level Thrift exception 34 | type TApplicationException interface { 35 | TException 36 | TypeId() int32 37 | Read(iprot TProtocol) (TApplicationException, error) 38 | Write(oprot TProtocol) error 39 | } 40 | 41 | type tApplicationException struct { 42 | message string 43 | type_ int32 44 | } 45 | 46 | func (e tApplicationException) Error() string { 47 | return e.message 48 | } 49 | 50 | func NewTApplicationException(type_ int32, message string) TApplicationException { 51 | return &tApplicationException{message, type_} 52 | } 53 | 54 | func (p *tApplicationException) TypeId() int32 { 55 | return p.type_ 56 | } 57 | 58 | func (p *tApplicationException) Read(iprot TProtocol) (TApplicationException, error) { 59 | _, err := iprot.ReadStructBegin() 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | message := "" 65 | type_ := int32(UNKNOWN_APPLICATION_EXCEPTION) 66 | 67 | for { 68 | _, ttype, id, err := iprot.ReadFieldBegin() 69 | if err != nil { 70 | return nil, err 71 | } 72 | if ttype == STOP { 73 | break 74 | } 75 | switch id { 76 | case 1: 77 | if ttype == STRING { 78 | if message, err = iprot.ReadString(); err != nil { 79 | return nil, err 80 | } 81 | } else { 82 | if err = SkipDefaultDepth(iprot, ttype); err != nil { 83 | return nil, err 84 | } 85 | } 86 | case 2: 87 | if ttype == I32 { 88 | if type_, err = iprot.ReadI32(); err != nil { 89 | return nil, err 90 | } 91 | } else { 92 | if err = SkipDefaultDepth(iprot, ttype); err != nil { 93 | return nil, err 94 | } 95 | } 96 | default: 97 | if err = SkipDefaultDepth(iprot, ttype); err != nil { 98 | return nil, err 99 | } 100 | } 101 | if err = iprot.ReadFieldEnd(); err != nil { 102 | return nil, err 103 | } 104 | } 105 | return NewTApplicationException(type_, message), iprot.ReadStructEnd() 106 | } 107 | 108 | func (p *tApplicationException) Write(oprot TProtocol) (err error) { 109 | err = oprot.WriteStructBegin("TApplicationException") 110 | if len(p.Error()) > 0 { 111 | err = oprot.WriteFieldBegin("message", STRING, 1) 112 | if err != nil { 113 | return 114 | } 115 | err = oprot.WriteString(p.Error()) 116 | if err != nil { 117 | return 118 | } 119 | err = oprot.WriteFieldEnd() 120 | if err != nil { 121 | return 122 | } 123 | } 124 | err = oprot.WriteFieldBegin("type", I32, 2) 125 | if err != nil { 126 | return 127 | } 128 | err = oprot.WriteI32(p.type_) 129 | if err != nil { 130 | return 131 | } 132 | err = oprot.WriteFieldEnd() 133 | if err != nil { 134 | return 135 | } 136 | err = oprot.WriteFieldStop() 137 | if err != nil { 138 | return 139 | } 140 | err = oprot.WriteStructEnd() 141 | return 142 | } 143 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/application_exception_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "testing" 24 | ) 25 | 26 | func TestTApplicationException(t *testing.T) { 27 | exc := NewTApplicationException(UNKNOWN_APPLICATION_EXCEPTION, "") 28 | if exc.Error() != "" { 29 | t.Fatalf("Expected empty string for exception but found '%s'", exc.Error()) 30 | } 31 | if exc.TypeId() != UNKNOWN_APPLICATION_EXCEPTION { 32 | t.Fatalf("Expected type UNKNOWN for exception but found '%v'", exc.TypeId()) 33 | } 34 | exc = NewTApplicationException(WRONG_METHOD_NAME, "junk_method") 35 | if exc.Error() != "junk_method" { 36 | t.Fatalf("Expected 'junk_method' for exception but found '%s'", exc.Error()) 37 | } 38 | if exc.TypeId() != WRONG_METHOD_NAME { 39 | t.Fatalf("Expected type WRONG_METHOD_NAME for exception but found '%v'", exc.TypeId()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/binary_protocol_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "testing" 24 | ) 25 | 26 | func TestReadWriteBinaryProtocol(t *testing.T) { 27 | ReadWriteProtocolTest(t, NewTBinaryProtocolFactoryDefault()) 28 | } 29 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/buffered_transport.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "bufio" 24 | ) 25 | 26 | type TBufferedTransportFactory struct { 27 | size int 28 | } 29 | 30 | type TBufferedTransport struct { 31 | bufio.ReadWriter 32 | tp TTransport 33 | } 34 | 35 | func (p *TBufferedTransportFactory) GetTransport(trans TTransport) TTransport { 36 | return NewTBufferedTransport(trans, p.size) 37 | } 38 | 39 | func NewTBufferedTransportFactory(bufferSize int) *TBufferedTransportFactory { 40 | return &TBufferedTransportFactory{size: bufferSize} 41 | } 42 | 43 | func NewTBufferedTransport(trans TTransport, bufferSize int) *TBufferedTransport { 44 | return &TBufferedTransport{ 45 | ReadWriter: bufio.ReadWriter{ 46 | Reader: bufio.NewReaderSize(trans, bufferSize), 47 | Writer: bufio.NewWriterSize(trans, bufferSize), 48 | }, 49 | tp: trans, 50 | } 51 | } 52 | 53 | func (p *TBufferedTransport) IsOpen() bool { 54 | return p.tp.IsOpen() 55 | } 56 | 57 | func (p *TBufferedTransport) Open() (err error) { 58 | return p.tp.Open() 59 | } 60 | 61 | func (p *TBufferedTransport) Close() (err error) { 62 | return p.tp.Close() 63 | } 64 | 65 | func (p *TBufferedTransport) Read(b []byte) (int, error) { 66 | n, err := p.ReadWriter.Read(b) 67 | if err != nil { 68 | p.ReadWriter.Reader.Reset(p.tp) 69 | } 70 | return n, err 71 | } 72 | 73 | func (p *TBufferedTransport) Write(b []byte) (int, error) { 74 | n, err := p.ReadWriter.Write(b) 75 | if err != nil { 76 | p.ReadWriter.Writer.Reset(p.tp) 77 | } 78 | return n, err 79 | } 80 | 81 | func (p *TBufferedTransport) Flush() error { 82 | if err := p.ReadWriter.Flush(); err != nil { 83 | p.ReadWriter.Writer.Reset(p.tp) 84 | return err 85 | } 86 | return p.tp.Flush() 87 | } 88 | 89 | func (p *TBufferedTransport) RemainingBytes() (num_bytes uint64) { 90 | return p.tp.RemainingBytes() 91 | } 92 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/buffered_transport_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "testing" 24 | ) 25 | 26 | func TestBufferedTransport(t *testing.T) { 27 | trans := NewTBufferedTransport(NewTMemoryBuffer(), 10240) 28 | TransportTest(t, trans, trans) 29 | } 30 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/compact_protocol_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "bytes" 24 | "testing" 25 | ) 26 | 27 | func TestReadWriteCompactProtocol(t *testing.T) { 28 | ReadWriteProtocolTest(t, NewTCompactProtocolFactory()) 29 | transports := []TTransport{ 30 | NewTMemoryBuffer(), 31 | NewStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 16384))), 32 | NewTFramedTransport(NewTMemoryBuffer()), 33 | } 34 | for _, trans := range transports { 35 | p := NewTCompactProtocol(trans); 36 | ReadWriteBool(t, p, trans); 37 | p = NewTCompactProtocol(trans); 38 | ReadWriteByte(t, p, trans); 39 | p = NewTCompactProtocol(trans); 40 | ReadWriteI16(t, p, trans); 41 | p = NewTCompactProtocol(trans); 42 | ReadWriteI32(t, p, trans); 43 | p = NewTCompactProtocol(trans); 44 | ReadWriteI64(t, p, trans); 45 | p = NewTCompactProtocol(trans); 46 | ReadWriteDouble(t, p, trans); 47 | p = NewTCompactProtocol(trans); 48 | ReadWriteString(t, p, trans); 49 | p = NewTCompactProtocol(trans); 50 | ReadWriteBinary(t, p, trans); 51 | trans.Close(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/deserializer.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | type TDeserializer struct { 23 | Transport TTransport 24 | Protocol TProtocol 25 | } 26 | 27 | func NewTDeserializer() *TDeserializer { 28 | var transport TTransport 29 | transport = NewTMemoryBufferLen(1024) 30 | 31 | protocol := NewTBinaryProtocolFactoryDefault().GetProtocol(transport) 32 | 33 | return &TDeserializer{ 34 | transport, 35 | protocol} 36 | } 37 | 38 | func (t *TDeserializer) ReadString(msg TStruct, s string) (err error) { 39 | err = nil 40 | if _, err = t.Transport.Write([]byte(s)); err != nil { 41 | return 42 | } 43 | if err = msg.Read(t.Protocol); err != nil { 44 | return 45 | } 46 | return 47 | } 48 | 49 | func (t *TDeserializer) Read(msg TStruct, b []byte) (err error) { 50 | err = nil 51 | if _, err = t.Transport.Write(b); err != nil { 52 | return 53 | } 54 | if err = msg.Read(t.Protocol); err != nil { 55 | return 56 | } 57 | return 58 | } 59 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/exception.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "errors" 24 | ) 25 | 26 | // Generic Thrift exception 27 | type TException interface { 28 | error 29 | } 30 | 31 | // Prepends additional information to an error without losing the Thrift exception interface 32 | func PrependError(prepend string, err error) error { 33 | if t, ok := err.(TTransportException); ok { 34 | return NewTTransportException(t.TypeId(), prepend+t.Error()) 35 | } 36 | if t, ok := err.(TProtocolException); ok { 37 | return NewTProtocolExceptionWithType(t.TypeId(), errors.New(prepend+err.Error())) 38 | } 39 | if t, ok := err.(TApplicationException); ok { 40 | return NewTApplicationException(t.TypeId(), prepend+t.Error()) 41 | } 42 | 43 | return errors.New(prepend + err.Error()) 44 | } 45 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/exception_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "errors" 24 | "testing" 25 | ) 26 | 27 | func TestPrependError(t *testing.T) { 28 | err := NewTApplicationException(INTERNAL_ERROR, "original error") 29 | err2, ok := PrependError("Prepend: ", err).(TApplicationException) 30 | if !ok { 31 | t.Fatal("Couldn't cast error TApplicationException") 32 | } 33 | if err2.Error() != "Prepend: original error" { 34 | t.Fatal("Unexpected error string") 35 | } 36 | if err2.TypeId() != INTERNAL_ERROR { 37 | t.Fatal("Unexpected type error") 38 | } 39 | 40 | err3 := NewTProtocolExceptionWithType(INVALID_DATA, errors.New("original error")) 41 | err4, ok := PrependError("Prepend: ", err3).(TProtocolException) 42 | if !ok { 43 | t.Fatal("Couldn't cast error TProtocolException") 44 | } 45 | if err4.Error() != "Prepend: original error" { 46 | t.Fatal("Unexpected error string") 47 | } 48 | if err4.TypeId() != INVALID_DATA { 49 | t.Fatal("Unexpected type error") 50 | } 51 | 52 | err5 := NewTTransportException(TIMED_OUT, "original error") 53 | err6, ok := PrependError("Prepend: ", err5).(TTransportException) 54 | if !ok { 55 | t.Fatal("Couldn't cast error TTransportException") 56 | } 57 | if err6.Error() != "Prepend: original error" { 58 | t.Fatal("Unexpected error string") 59 | } 60 | if err6.TypeId() != TIMED_OUT { 61 | t.Fatal("Unexpected type error") 62 | } 63 | 64 | err7 := errors.New("original error") 65 | err8 := PrependError("Prepend: ", err7) 66 | if err8.Error() != "Prepend: original error" { 67 | t.Fatal("Unexpected error string") 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/field.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import "strconv" 23 | 24 | // Helper class that encapsulates field metadata. 25 | type field struct { 26 | name string 27 | typeId TType 28 | id int 29 | } 30 | 31 | func newField(n string, t TType, i int) *field { 32 | return &field{name: n, typeId: t, id: i} 33 | } 34 | 35 | func (p *field) Name() string { 36 | if p == nil { 37 | return "" 38 | } 39 | return p.name 40 | } 41 | 42 | func (p *field) TypeId() TType { 43 | if p == nil { 44 | return TType(VOID) 45 | } 46 | return p.typeId 47 | } 48 | 49 | func (p *field) Id() int { 50 | if p == nil { 51 | return -1 52 | } 53 | return p.id 54 | } 55 | 56 | func (p *field) String() string { 57 | if p == nil { 58 | return "" 59 | } 60 | return "" 61 | } 62 | 63 | var ANONYMOUS_FIELD *field 64 | 65 | type fieldSlice []field 66 | 67 | func (p fieldSlice) Len() int { 68 | return len(p) 69 | } 70 | 71 | func (p fieldSlice) Less(i, j int) bool { 72 | return p[i].Id() < p[j].Id() 73 | } 74 | 75 | func (p fieldSlice) Swap(i, j int) { 76 | p[i], p[j] = p[j], p[i] 77 | } 78 | 79 | func init() { 80 | ANONYMOUS_FIELD = newField("", STOP, 0) 81 | } 82 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/framed_transport_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "testing" 24 | ) 25 | 26 | func TestFramedTransport(t *testing.T) { 27 | trans := NewTFramedTransport(NewTMemoryBuffer()) 28 | TransportTest(t, trans, trans) 29 | } 30 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/http_client_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "net/http" 24 | "testing" 25 | ) 26 | 27 | func TestHttpClient(t *testing.T) { 28 | l, addr := HttpClientSetupForTest(t) 29 | if l != nil { 30 | defer l.Close() 31 | } 32 | trans, err := NewTHttpPostClient("http://" + addr.String()) 33 | if err != nil { 34 | l.Close() 35 | t.Fatalf("Unable to connect to %s: %s", addr.String(), err) 36 | } 37 | TransportTest(t, trans, trans) 38 | } 39 | 40 | func TestHttpClientHeaders(t *testing.T) { 41 | l, addr := HttpClientSetupForTest(t) 42 | if l != nil { 43 | defer l.Close() 44 | } 45 | trans, err := NewTHttpPostClient("http://" + addr.String()) 46 | if err != nil { 47 | l.Close() 48 | t.Fatalf("Unable to connect to %s: %s", addr.String(), err) 49 | } 50 | TransportHeaderTest(t, trans, trans) 51 | } 52 | 53 | func TestHttpCustomClient(t *testing.T) { 54 | l, addr := HttpClientSetupForTest(t) 55 | if l != nil { 56 | defer l.Close() 57 | } 58 | 59 | httpTransport := &customHttpTransport{} 60 | 61 | trans, err := NewTHttpPostClientWithOptions("http://"+addr.String(), THttpClientOptions{ 62 | Client: &http.Client{ 63 | Transport: httpTransport, 64 | }, 65 | }) 66 | if err != nil { 67 | l.Close() 68 | t.Fatalf("Unable to connect to %s: %s", addr.String(), err) 69 | } 70 | TransportHeaderTest(t, trans, trans) 71 | 72 | if !httpTransport.hit { 73 | t.Fatalf("Custom client was not used") 74 | } 75 | } 76 | 77 | func TestHttpCustomClientPackageScope(t *testing.T) { 78 | l, addr := HttpClientSetupForTest(t) 79 | if l != nil { 80 | defer l.Close() 81 | } 82 | httpTransport := &customHttpTransport{} 83 | DefaultHttpClient = &http.Client{ 84 | Transport: httpTransport, 85 | } 86 | 87 | trans, err := NewTHttpPostClient("http://" + addr.String()) 88 | if err != nil { 89 | l.Close() 90 | t.Fatalf("Unable to connect to %s: %s", addr.String(), err) 91 | } 92 | TransportHeaderTest(t, trans, trans) 93 | 94 | if !httpTransport.hit { 95 | t.Fatalf("Custom client was not used") 96 | } 97 | } 98 | 99 | type customHttpTransport struct { 100 | hit bool 101 | } 102 | 103 | func (c *customHttpTransport) RoundTrip(req *http.Request) (*http.Response, error) { 104 | c.hit = true 105 | return http.DefaultTransport.RoundTrip(req) 106 | } 107 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/http_transport.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import "net/http" 23 | 24 | // NewThriftHandlerFunc is a function that create a ready to use Apache Thrift Handler function 25 | func NewThriftHandlerFunc(processor TProcessor, 26 | inPfactory, outPfactory TProtocolFactory) func(w http.ResponseWriter, r *http.Request) { 27 | 28 | return func(w http.ResponseWriter, r *http.Request) { 29 | w.Header().Add("Content-Type", "application/x-thrift") 30 | 31 | transport := NewStreamTransport(r.Body, w) 32 | processor.Process(inPfactory.GetProtocol(transport), outPfactory.GetProtocol(transport)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/iostream_transport_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "bytes" 24 | "testing" 25 | ) 26 | 27 | func TestStreamTransport(t *testing.T) { 28 | trans := NewStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 1024))) 29 | TransportTest(t, trans, trans) 30 | } 31 | 32 | func TestStreamTransportOpenClose(t *testing.T) { 33 | trans := NewStreamTransportRW(bytes.NewBuffer(make([]byte, 0, 1024))) 34 | if !trans.IsOpen() { 35 | t.Fatal("StreamTransport should be already open") 36 | } 37 | if trans.Open() == nil { 38 | t.Fatal("StreamTransport should return error when open twice") 39 | } 40 | if trans.Close() != nil { 41 | t.Fatal("StreamTransport should not return error when closing open transport") 42 | } 43 | if trans.IsOpen() { 44 | t.Fatal("StreamTransport should not be open after close") 45 | } 46 | if trans.Close() == nil { 47 | t.Fatal("StreamTransport should return error when closing a non open transport") 48 | } 49 | if trans.Open() == nil { 50 | t.Fatal("StreamTransport should not be able to reopen") 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/memory_buffer.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "bytes" 24 | ) 25 | 26 | // Memory buffer-based implementation of the TTransport interface. 27 | type TMemoryBuffer struct { 28 | *bytes.Buffer 29 | size int 30 | } 31 | 32 | type TMemoryBufferTransportFactory struct { 33 | size int 34 | } 35 | 36 | func (p *TMemoryBufferTransportFactory) GetTransport(trans TTransport) TTransport { 37 | if trans != nil { 38 | t, ok := trans.(*TMemoryBuffer) 39 | if ok && t.size > 0 { 40 | return NewTMemoryBufferLen(t.size) 41 | } 42 | } 43 | return NewTMemoryBufferLen(p.size) 44 | } 45 | 46 | func NewTMemoryBufferTransportFactory(size int) *TMemoryBufferTransportFactory { 47 | return &TMemoryBufferTransportFactory{size: size} 48 | } 49 | 50 | func NewTMemoryBuffer() *TMemoryBuffer { 51 | return &TMemoryBuffer{Buffer: &bytes.Buffer{}, size: 0} 52 | } 53 | 54 | func NewTMemoryBufferLen(size int) *TMemoryBuffer { 55 | buf := make([]byte, 0, size) 56 | return &TMemoryBuffer{Buffer: bytes.NewBuffer(buf), size: size} 57 | } 58 | 59 | func (p *TMemoryBuffer) IsOpen() bool { 60 | return true 61 | } 62 | 63 | func (p *TMemoryBuffer) Open() error { 64 | return nil 65 | } 66 | 67 | func (p *TMemoryBuffer) Close() error { 68 | p.Buffer.Reset() 69 | return nil 70 | } 71 | 72 | // Flushing a memory buffer is a no-op 73 | func (p *TMemoryBuffer) Flush() error { 74 | return nil 75 | } 76 | 77 | func (p *TMemoryBuffer) RemainingBytes() (num_bytes uint64) { 78 | return uint64(p.Buffer.Len()) 79 | } 80 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/memory_buffer_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "testing" 24 | ) 25 | 26 | func TestMemoryBuffer(t *testing.T) { 27 | trans := NewTMemoryBufferLen(1024) 28 | TransportTest(t, trans, trans) 29 | } 30 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/messagetype.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | // Message type constants in the Thrift protocol. 23 | type TMessageType int32 24 | 25 | const ( 26 | INVALID_TMESSAGE_TYPE TMessageType = 0 27 | CALL TMessageType = 1 28 | REPLY TMessageType = 2 29 | EXCEPTION TMessageType = 3 30 | ONEWAY TMessageType = 4 31 | ) 32 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/numeric.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "math" 24 | "strconv" 25 | ) 26 | 27 | type Numeric interface { 28 | Int64() int64 29 | Int32() int32 30 | Int16() int16 31 | Byte() byte 32 | Int() int 33 | Float64() float64 34 | Float32() float32 35 | String() string 36 | isNull() bool 37 | } 38 | 39 | type numeric struct { 40 | iValue int64 41 | dValue float64 42 | sValue string 43 | isNil bool 44 | } 45 | 46 | var ( 47 | INFINITY Numeric 48 | NEGATIVE_INFINITY Numeric 49 | NAN Numeric 50 | ZERO Numeric 51 | NUMERIC_NULL Numeric 52 | ) 53 | 54 | func NewNumericFromDouble(dValue float64) Numeric { 55 | if math.IsInf(dValue, 1) { 56 | return INFINITY 57 | } 58 | if math.IsInf(dValue, -1) { 59 | return NEGATIVE_INFINITY 60 | } 61 | if math.IsNaN(dValue) { 62 | return NAN 63 | } 64 | iValue := int64(dValue) 65 | sValue := strconv.FormatFloat(dValue, 'g', 10, 64) 66 | isNil := false 67 | return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} 68 | } 69 | 70 | func NewNumericFromI64(iValue int64) Numeric { 71 | dValue := float64(iValue) 72 | sValue := strconv.FormatInt(iValue, 10) 73 | isNil := false 74 | return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} 75 | } 76 | 77 | func NewNumericFromI32(iValue int32) Numeric { 78 | dValue := float64(iValue) 79 | sValue := strconv.FormatInt(int64(iValue), 10) 80 | isNil := false 81 | return &numeric{iValue: int64(iValue), dValue: dValue, sValue: sValue, isNil: isNil} 82 | } 83 | 84 | func NewNumericFromString(sValue string) Numeric { 85 | if sValue == INFINITY.String() { 86 | return INFINITY 87 | } 88 | if sValue == NEGATIVE_INFINITY.String() { 89 | return NEGATIVE_INFINITY 90 | } 91 | if sValue == NAN.String() { 92 | return NAN 93 | } 94 | iValue, _ := strconv.ParseInt(sValue, 10, 64) 95 | dValue, _ := strconv.ParseFloat(sValue, 64) 96 | isNil := len(sValue) == 0 97 | return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} 98 | } 99 | 100 | func NewNumericFromJSONString(sValue string, isNull bool) Numeric { 101 | if isNull { 102 | return NewNullNumeric() 103 | } 104 | if sValue == JSON_INFINITY { 105 | return INFINITY 106 | } 107 | if sValue == JSON_NEGATIVE_INFINITY { 108 | return NEGATIVE_INFINITY 109 | } 110 | if sValue == JSON_NAN { 111 | return NAN 112 | } 113 | iValue, _ := strconv.ParseInt(sValue, 10, 64) 114 | dValue, _ := strconv.ParseFloat(sValue, 64) 115 | return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNull} 116 | } 117 | 118 | func NewNullNumeric() Numeric { 119 | return &numeric{iValue: 0, dValue: 0.0, sValue: "", isNil: true} 120 | } 121 | 122 | func (p *numeric) Int64() int64 { 123 | return p.iValue 124 | } 125 | 126 | func (p *numeric) Int32() int32 { 127 | return int32(p.iValue) 128 | } 129 | 130 | func (p *numeric) Int16() int16 { 131 | return int16(p.iValue) 132 | } 133 | 134 | func (p *numeric) Byte() byte { 135 | return byte(p.iValue) 136 | } 137 | 138 | func (p *numeric) Int() int { 139 | return int(p.iValue) 140 | } 141 | 142 | func (p *numeric) Float64() float64 { 143 | return p.dValue 144 | } 145 | 146 | func (p *numeric) Float32() float32 { 147 | return float32(p.dValue) 148 | } 149 | 150 | func (p *numeric) String() string { 151 | return p.sValue 152 | } 153 | 154 | func (p *numeric) isNull() bool { 155 | return p.isNil 156 | } 157 | 158 | func init() { 159 | INFINITY = &numeric{iValue: 0, dValue: math.Inf(1), sValue: "Infinity", isNil: false} 160 | NEGATIVE_INFINITY = &numeric{iValue: 0, dValue: math.Inf(-1), sValue: "-Infinity", isNil: false} 161 | NAN = &numeric{iValue: 0, dValue: math.NaN(), sValue: "NaN", isNil: false} 162 | ZERO = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: false} 163 | NUMERIC_NULL = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: true} 164 | } 165 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/pointerize.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | /////////////////////////////////////////////////////////////////////////////// 23 | // This file is home to helpers that convert from various base types to 24 | // respective pointer types. This is necessary because Go does not permit 25 | // references to constants, nor can a pointer type to base type be allocated 26 | // and initialized in a single expression. 27 | // 28 | // E.g., this is not allowed: 29 | // 30 | // var ip *int = &5 31 | // 32 | // But this *is* allowed: 33 | // 34 | // func IntPtr(i int) *int { return &i } 35 | // var ip *int = IntPtr(5) 36 | // 37 | // Since pointers to base types are commonplace as [optional] fields in 38 | // exported thrift structs, we factor such helpers here. 39 | /////////////////////////////////////////////////////////////////////////////// 40 | 41 | func Float32Ptr(v float32) *float32 { return &v } 42 | func Float64Ptr(v float64) *float64 { return &v } 43 | func IntPtr(v int) *int { return &v } 44 | func Int32Ptr(v int32) *int32 { return &v } 45 | func Int64Ptr(v int64) *int64 { return &v } 46 | func StringPtr(v string) *string { return &v } 47 | func Uint32Ptr(v uint32) *uint32 { return &v } 48 | func Uint64Ptr(v uint64) *uint64 { return &v } 49 | func BoolPtr(v bool) *bool { return &v } 50 | func ByteSlicePtr(v []byte) *[]byte { return &v } 51 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/processor.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | // A processor is a generic object which operates upon an input stream and 23 | // writes to some output stream. 24 | type TProcessor interface { 25 | Process(in, out TProtocol) (bool, TException) 26 | } 27 | 28 | type TProcessorFunction interface { 29 | Process(seqId int32, in, out TProtocol) (bool, TException) 30 | } 31 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/processor_factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | // The default processor factory just returns a singleton 23 | // instance. 24 | type TProcessorFactory interface { 25 | GetProcessor(trans TTransport) TProcessor 26 | } 27 | 28 | type tProcessorFactory struct { 29 | processor TProcessor 30 | } 31 | 32 | func NewTProcessorFactory(p TProcessor) TProcessorFactory { 33 | return &tProcessorFactory{processor: p} 34 | } 35 | 36 | func (p *tProcessorFactory) GetProcessor(trans TTransport) TProcessor { 37 | return p.processor 38 | } 39 | 40 | /** 41 | * The default processor factory just returns a singleton 42 | * instance. 43 | */ 44 | type TProcessorFunctionFactory interface { 45 | GetProcessorFunction(trans TTransport) TProcessorFunction 46 | } 47 | 48 | type tProcessorFunctionFactory struct { 49 | processor TProcessorFunction 50 | } 51 | 52 | func NewTProcessorFunctionFactory(p TProcessorFunction) TProcessorFunctionFactory { 53 | return &tProcessorFunctionFactory{processor: p} 54 | } 55 | 56 | func (p *tProcessorFunctionFactory) GetProcessorFunction(trans TTransport) TProcessorFunction { 57 | return p.processor 58 | } 59 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/protocol_exception.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "encoding/base64" 24 | ) 25 | 26 | // Thrift Protocol exception 27 | type TProtocolException interface { 28 | TException 29 | TypeId() int 30 | } 31 | 32 | const ( 33 | UNKNOWN_PROTOCOL_EXCEPTION = 0 34 | INVALID_DATA = 1 35 | NEGATIVE_SIZE = 2 36 | SIZE_LIMIT = 3 37 | BAD_VERSION = 4 38 | NOT_IMPLEMENTED = 5 39 | DEPTH_LIMIT = 6 40 | ) 41 | 42 | type tProtocolException struct { 43 | typeId int 44 | message string 45 | } 46 | 47 | func (p *tProtocolException) TypeId() int { 48 | return p.typeId 49 | } 50 | 51 | func (p *tProtocolException) String() string { 52 | return p.message 53 | } 54 | 55 | func (p *tProtocolException) Error() string { 56 | return p.message 57 | } 58 | 59 | func NewTProtocolException(err error) TProtocolException { 60 | if err == nil { 61 | return nil 62 | } 63 | if e,ok := err.(TProtocolException); ok { 64 | return e 65 | } 66 | if _, ok := err.(base64.CorruptInputError); ok { 67 | return &tProtocolException{INVALID_DATA, err.Error()} 68 | } 69 | return &tProtocolException{UNKNOWN_PROTOCOL_EXCEPTION, err.Error()} 70 | } 71 | 72 | func NewTProtocolExceptionWithType(errType int, err error) TProtocolException { 73 | if err == nil { 74 | return nil 75 | } 76 | return &tProtocolException{errType, err.Error()} 77 | } 78 | 79 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/protocol_factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | // Factory interface for constructing protocol instances. 23 | type TProtocolFactory interface { 24 | GetProtocol(trans TTransport) TProtocol 25 | } 26 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/rich_transport.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import "io" 23 | 24 | type RichTransport struct { 25 | TTransport 26 | } 27 | 28 | // Wraps Transport to provide TRichTransport interface 29 | func NewTRichTransport(trans TTransport) *RichTransport { 30 | return &RichTransport{trans} 31 | } 32 | 33 | func (r *RichTransport) ReadByte() (c byte, err error) { 34 | return readByte(r.TTransport) 35 | } 36 | 37 | func (r *RichTransport) WriteByte(c byte) error { 38 | return writeByte(r.TTransport, c) 39 | } 40 | 41 | func (r *RichTransport) WriteString(s string) (n int, err error) { 42 | return r.Write([]byte(s)) 43 | } 44 | 45 | func (r *RichTransport) RemainingBytes() (num_bytes uint64) { 46 | return r.TTransport.RemainingBytes() 47 | } 48 | 49 | func readByte(r io.Reader) (c byte, err error) { 50 | v := [1]byte{0} 51 | n, err := r.Read(v[0:1]) 52 | if n > 0 && (err == nil || err == io.EOF) { 53 | return v[0], nil 54 | } 55 | if n > 0 && err != nil { 56 | return v[0], err 57 | } 58 | if err != nil { 59 | return 0, err 60 | } 61 | return v[0], nil 62 | } 63 | 64 | func writeByte(w io.Writer, c byte) error { 65 | v := [1]byte{c} 66 | _, err := w.Write(v[0:1]) 67 | return err 68 | } 69 | 70 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/rich_transport_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "bytes" 24 | "errors" 25 | "io" 26 | "reflect" 27 | "testing" 28 | ) 29 | 30 | func TestEnsureTransportsAreRich(t *testing.T) { 31 | buf := bytes.NewBuffer(make([]byte, 0, 1024)) 32 | 33 | transports := []TTransportFactory{ 34 | NewTMemoryBufferTransportFactory(1024), 35 | NewStreamTransportFactory(buf, buf, true), 36 | NewTFramedTransportFactory(NewTMemoryBufferTransportFactory(1024)), 37 | NewTHttpPostClientTransportFactory("http://127.0.0.1"), 38 | } 39 | for _, tf := range transports { 40 | trans := tf.GetTransport(nil) 41 | _, ok := trans.(TRichTransport) 42 | if !ok { 43 | t.Errorf("Transport %s does not implement TRichTransport interface", reflect.ValueOf(trans)) 44 | } 45 | } 46 | } 47 | 48 | // TestReadByte tests whether readByte handles error cases correctly. 49 | func TestReadByte(t *testing.T) { 50 | for i, test := range readByteTests { 51 | v, err := readByte(test.r) 52 | if v != test.v { 53 | t.Fatalf("TestReadByte %d: value differs. Expected %d, got %d", i, test.v, test.r.v) 54 | } 55 | if err != test.err { 56 | t.Fatalf("TestReadByte %d: error differs. Expected %s, got %s", i, test.err, test.r.err) 57 | } 58 | } 59 | } 60 | 61 | var someError = errors.New("Some error") 62 | var readByteTests = []struct { 63 | r *mockReader 64 | v byte 65 | err error 66 | }{ 67 | {&mockReader{0, 55, io.EOF}, 0, io.EOF}, // reader sends EOF w/o data 68 | {&mockReader{0, 55, someError}, 0, someError}, // reader sends some other error 69 | {&mockReader{1, 55, nil}, 55, nil}, // reader sends data w/o error 70 | {&mockReader{1, 55, io.EOF}, 55, nil}, // reader sends data with EOF 71 | {&mockReader{1, 55, someError}, 55, someError}, // reader sends data withsome error 72 | } 73 | 74 | type mockReader struct { 75 | n int 76 | v byte 77 | err error 78 | } 79 | 80 | func (r *mockReader) Read(p []byte) (n int, err error) { 81 | if r.n > 0 { 82 | p[0] = r.v 83 | } 84 | return r.n, r.err 85 | } 86 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/serializer.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | type TSerializer struct { 23 | Transport *TMemoryBuffer 24 | Protocol TProtocol 25 | } 26 | 27 | type TStruct interface { 28 | Write(p TProtocol) error 29 | Read(p TProtocol) error 30 | } 31 | 32 | func NewTSerializer() *TSerializer { 33 | transport := NewTMemoryBufferLen(1024) 34 | protocol := NewTBinaryProtocolFactoryDefault().GetProtocol(transport) 35 | 36 | return &TSerializer{ 37 | transport, 38 | protocol} 39 | } 40 | 41 | func (t *TSerializer) WriteString(msg TStruct) (s string, err error) { 42 | t.Transport.Reset() 43 | 44 | if err = msg.Write(t.Protocol); err != nil { 45 | return 46 | } 47 | 48 | if err = t.Protocol.Flush(); err != nil { 49 | return 50 | } 51 | if err = t.Transport.Flush(); err != nil { 52 | return 53 | } 54 | 55 | return t.Transport.String(), nil 56 | } 57 | 58 | func (t *TSerializer) Write(msg TStruct) (b []byte, err error) { 59 | t.Transport.Reset() 60 | 61 | if err = msg.Write(t.Protocol); err != nil { 62 | return 63 | } 64 | 65 | if err = t.Protocol.Flush(); err != nil { 66 | return 67 | } 68 | 69 | if err = t.Transport.Flush(); err != nil { 70 | return 71 | } 72 | 73 | b = append(b, t.Transport.Bytes()...) 74 | return 75 | } 76 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | type TServer interface { 23 | ProcessorFactory() TProcessorFactory 24 | ServerTransport() TServerTransport 25 | InputTransportFactory() TTransportFactory 26 | OutputTransportFactory() TTransportFactory 27 | InputProtocolFactory() TProtocolFactory 28 | OutputProtocolFactory() TProtocolFactory 29 | 30 | // Starts the server 31 | Serve() error 32 | // Stops the server. This is optional on a per-implementation basis. Not 33 | // all servers are required to be cleanly stoppable. 34 | Stop() error 35 | } 36 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/server_socket.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "net" 24 | "sync" 25 | "time" 26 | ) 27 | 28 | type TServerSocket struct { 29 | listener net.Listener 30 | addr net.Addr 31 | clientTimeout time.Duration 32 | 33 | // Protects the interrupted value to make it thread safe. 34 | mu sync.RWMutex 35 | interrupted bool 36 | } 37 | 38 | func NewTServerSocket(listenAddr string) (*TServerSocket, error) { 39 | return NewTServerSocketTimeout(listenAddr, 0) 40 | } 41 | 42 | func NewTServerSocketTimeout(listenAddr string, clientTimeout time.Duration) (*TServerSocket, error) { 43 | addr, err := net.ResolveTCPAddr("tcp", listenAddr) 44 | if err != nil { 45 | return nil, err 46 | } 47 | return &TServerSocket{addr: addr, clientTimeout: clientTimeout}, nil 48 | } 49 | 50 | func (p *TServerSocket) Listen() error { 51 | if p.IsListening() { 52 | return nil 53 | } 54 | l, err := net.Listen(p.addr.Network(), p.addr.String()) 55 | if err != nil { 56 | return err 57 | } 58 | p.listener = l 59 | return nil 60 | } 61 | 62 | func (p *TServerSocket) Accept() (TTransport, error) { 63 | p.mu.RLock() 64 | interrupted := p.interrupted 65 | p.mu.RUnlock() 66 | 67 | if interrupted { 68 | return nil, errTransportInterrupted 69 | } 70 | if p.listener == nil { 71 | return nil, NewTTransportException(NOT_OPEN, "No underlying server socket") 72 | } 73 | conn, err := p.listener.Accept() 74 | if err != nil { 75 | return nil, NewTTransportExceptionFromError(err) 76 | } 77 | return NewTSocketFromConnTimeout(conn, p.clientTimeout), nil 78 | } 79 | 80 | // Checks whether the socket is listening. 81 | func (p *TServerSocket) IsListening() bool { 82 | return p.listener != nil 83 | } 84 | 85 | // Connects the socket, creating a new socket object if necessary. 86 | func (p *TServerSocket) Open() error { 87 | if p.IsListening() { 88 | return NewTTransportException(ALREADY_OPEN, "Server socket already open") 89 | } 90 | if l, err := net.Listen(p.addr.Network(), p.addr.String()); err != nil { 91 | return err 92 | } else { 93 | p.listener = l 94 | } 95 | return nil 96 | } 97 | 98 | func (p *TServerSocket) Addr() net.Addr { 99 | if p.listener != nil { 100 | return p.listener.Addr() 101 | } 102 | return p.addr 103 | } 104 | 105 | func (p *TServerSocket) Close() error { 106 | defer func() { 107 | p.listener = nil 108 | }() 109 | if p.IsListening() { 110 | return p.listener.Close() 111 | } 112 | return nil 113 | } 114 | 115 | func (p *TServerSocket) Interrupt() error { 116 | p.mu.Lock() 117 | p.interrupted = true 118 | p.Close() 119 | p.mu.Unlock() 120 | 121 | return nil 122 | } 123 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/server_socket_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "fmt" 24 | "testing" 25 | ) 26 | 27 | func TestSocketIsntListeningAfterInterrupt(t *testing.T) { 28 | host := "127.0.0.1" 29 | port := 9090 30 | addr := fmt.Sprintf("%s:%d", host, port) 31 | 32 | socket := CreateServerSocket(t, addr) 33 | socket.Listen() 34 | socket.Interrupt() 35 | 36 | newSocket := CreateServerSocket(t, addr) 37 | err := newSocket.Listen() 38 | defer newSocket.Interrupt() 39 | if err != nil { 40 | t.Fatalf("Failed to rebinds: %s", err) 41 | } 42 | } 43 | 44 | func CreateServerSocket(t *testing.T, addr string) *TServerSocket { 45 | socket, err := NewTServerSocket(addr) 46 | if err != nil { 47 | t.Fatalf("Failed to create server socket: %s", err) 48 | } 49 | return socket 50 | } 51 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/server_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "testing" 24 | ) 25 | 26 | func TestNothing(t *testing.T) { 27 | 28 | } 29 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/server_transport.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | // Server transport. Object which provides client transports. 23 | type TServerTransport interface { 24 | Listen() error 25 | Accept() (TTransport, error) 26 | Close() error 27 | 28 | // Optional method implementation. This signals to the server transport 29 | // that it should break out of any accept() or listen() that it is currently 30 | // blocked on. This method, if implemented, MUST be thread safe, as it may 31 | // be called from a different thread context than the other TServerTransport 32 | // methods. 33 | Interrupt() error 34 | } 35 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/socket.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "net" 24 | "time" 25 | ) 26 | 27 | type TSocket struct { 28 | conn net.Conn 29 | addr net.Addr 30 | timeout time.Duration 31 | } 32 | 33 | // NewTSocket creates a net.Conn-backed TTransport, given a host and port 34 | // 35 | // Example: 36 | // trans, err := thrift.NewTSocket("localhost:9090") 37 | func NewTSocket(hostPort string) (*TSocket, error) { 38 | return NewTSocketTimeout(hostPort, 0) 39 | } 40 | 41 | // NewTSocketTimeout creates a net.Conn-backed TTransport, given a host and port 42 | // it also accepts a timeout as a time.Duration 43 | func NewTSocketTimeout(hostPort string, timeout time.Duration) (*TSocket, error) { 44 | //conn, err := net.DialTimeout(network, address, timeout) 45 | addr, err := net.ResolveTCPAddr("tcp", hostPort) 46 | if err != nil { 47 | return nil, err 48 | } 49 | return NewTSocketFromAddrTimeout(addr, timeout), nil 50 | } 51 | 52 | // Creates a TSocket from a net.Addr 53 | func NewTSocketFromAddrTimeout(addr net.Addr, timeout time.Duration) *TSocket { 54 | return &TSocket{addr: addr, timeout: timeout} 55 | } 56 | 57 | // Creates a TSocket from an existing net.Conn 58 | func NewTSocketFromConnTimeout(conn net.Conn, timeout time.Duration) *TSocket { 59 | return &TSocket{conn: conn, addr: conn.RemoteAddr(), timeout: timeout} 60 | } 61 | 62 | // Sets the socket timeout 63 | func (p *TSocket) SetTimeout(timeout time.Duration) error { 64 | p.timeout = timeout 65 | return nil 66 | } 67 | 68 | func (p *TSocket) pushDeadline(read, write bool) { 69 | var t time.Time 70 | if p.timeout > 0 { 71 | t = time.Now().Add(time.Duration(p.timeout)) 72 | } 73 | if read && write { 74 | p.conn.SetDeadline(t) 75 | } else if read { 76 | p.conn.SetReadDeadline(t) 77 | } else if write { 78 | p.conn.SetWriteDeadline(t) 79 | } 80 | } 81 | 82 | // Connects the socket, creating a new socket object if necessary. 83 | func (p *TSocket) Open() error { 84 | if p.IsOpen() { 85 | return NewTTransportException(ALREADY_OPEN, "Socket already connected.") 86 | } 87 | if p.addr == nil { 88 | return NewTTransportException(NOT_OPEN, "Cannot open nil address.") 89 | } 90 | if len(p.addr.Network()) == 0 { 91 | return NewTTransportException(NOT_OPEN, "Cannot open bad network name.") 92 | } 93 | if len(p.addr.String()) == 0 { 94 | return NewTTransportException(NOT_OPEN, "Cannot open bad address.") 95 | } 96 | var err error 97 | if p.conn, err = net.DialTimeout(p.addr.Network(), p.addr.String(), p.timeout); err != nil { 98 | return NewTTransportException(NOT_OPEN, err.Error()) 99 | } 100 | return nil 101 | } 102 | 103 | // Retrieve the underlying net.Conn 104 | func (p *TSocket) Conn() net.Conn { 105 | return p.conn 106 | } 107 | 108 | // Returns true if the connection is open 109 | func (p *TSocket) IsOpen() bool { 110 | if p.conn == nil { 111 | return false 112 | } 113 | return true 114 | } 115 | 116 | // Closes the socket. 117 | func (p *TSocket) Close() error { 118 | // Close the socket 119 | if p.conn != nil { 120 | err := p.conn.Close() 121 | if err != nil { 122 | return err 123 | } 124 | p.conn = nil 125 | } 126 | return nil 127 | } 128 | 129 | //Returns the remote address of the socket. 130 | func (p *TSocket) Addr() net.Addr { 131 | return p.addr 132 | } 133 | 134 | func (p *TSocket) Read(buf []byte) (int, error) { 135 | if !p.IsOpen() { 136 | return 0, NewTTransportException(NOT_OPEN, "Connection not open") 137 | } 138 | p.pushDeadline(true, false) 139 | n, err := p.conn.Read(buf) 140 | return n, NewTTransportExceptionFromError(err) 141 | } 142 | 143 | func (p *TSocket) Write(buf []byte) (int, error) { 144 | if !p.IsOpen() { 145 | return 0, NewTTransportException(NOT_OPEN, "Connection not open") 146 | } 147 | p.pushDeadline(false, true) 148 | return p.conn.Write(buf) 149 | } 150 | 151 | func (p *TSocket) Flush() error { 152 | return nil 153 | } 154 | 155 | func (p *TSocket) Interrupt() error { 156 | if !p.IsOpen() { 157 | return nil 158 | } 159 | return p.conn.Close() 160 | } 161 | 162 | func (p *TSocket) RemainingBytes() (num_bytes uint64) { 163 | const maxSize = ^uint64(0) 164 | return maxSize // the thruth is, we just don't know unless framed is used 165 | } 166 | 167 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/ssl_server_socket.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "net" 24 | "time" 25 | "crypto/tls" 26 | ) 27 | 28 | type TSSLServerSocket struct { 29 | listener net.Listener 30 | addr net.Addr 31 | clientTimeout time.Duration 32 | interrupted bool 33 | cfg *tls.Config 34 | } 35 | 36 | func NewTSSLServerSocket(listenAddr string, cfg *tls.Config) (*TSSLServerSocket, error) { 37 | return NewTSSLServerSocketTimeout(listenAddr, cfg, 0) 38 | } 39 | 40 | func NewTSSLServerSocketTimeout(listenAddr string, cfg *tls.Config, clientTimeout time.Duration) (*TSSLServerSocket, error) { 41 | addr, err := net.ResolveTCPAddr("tcp", listenAddr) 42 | if err != nil { 43 | return nil, err 44 | } 45 | return &TSSLServerSocket{addr: addr, clientTimeout: clientTimeout, cfg: cfg}, nil 46 | } 47 | 48 | func (p *TSSLServerSocket) Listen() error { 49 | if p.IsListening() { 50 | return nil 51 | } 52 | l, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg) 53 | if err != nil { 54 | return err 55 | } 56 | p.listener = l 57 | return nil 58 | } 59 | 60 | func (p *TSSLServerSocket) Accept() (TTransport, error) { 61 | if p.interrupted { 62 | return nil, errTransportInterrupted 63 | } 64 | if p.listener == nil { 65 | return nil, NewTTransportException(NOT_OPEN, "No underlying server socket") 66 | } 67 | conn, err := p.listener.Accept() 68 | if err != nil { 69 | return nil, NewTTransportExceptionFromError(err) 70 | } 71 | return NewTSSLSocketFromConnTimeout(conn, p.cfg, p.clientTimeout), nil 72 | } 73 | 74 | // Checks whether the socket is listening. 75 | func (p *TSSLServerSocket) IsListening() bool { 76 | return p.listener != nil 77 | } 78 | 79 | // Connects the socket, creating a new socket object if necessary. 80 | func (p *TSSLServerSocket) Open() error { 81 | if p.IsListening() { 82 | return NewTTransportException(ALREADY_OPEN, "Server socket already open") 83 | } 84 | if l, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg); err != nil { 85 | return err 86 | } else { 87 | p.listener = l 88 | } 89 | return nil 90 | } 91 | 92 | func (p *TSSLServerSocket) Addr() net.Addr { 93 | return p.addr 94 | } 95 | 96 | func (p *TSSLServerSocket) Close() error { 97 | defer func() { 98 | p.listener = nil 99 | }() 100 | if p.IsListening() { 101 | return p.listener.Close() 102 | } 103 | return nil 104 | } 105 | 106 | func (p *TSSLServerSocket) Interrupt() error { 107 | p.interrupted = true 108 | return nil 109 | } 110 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/transport.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "errors" 24 | "io" 25 | ) 26 | 27 | var errTransportInterrupted = errors.New("Transport Interrupted") 28 | 29 | type Flusher interface { 30 | Flush() (err error) 31 | } 32 | 33 | type ReadSizeProvider interface { 34 | RemainingBytes() (num_bytes uint64) 35 | } 36 | 37 | 38 | // Encapsulates the I/O layer 39 | type TTransport interface { 40 | io.ReadWriteCloser 41 | Flusher 42 | ReadSizeProvider 43 | 44 | // Opens the transport for communication 45 | Open() error 46 | 47 | // Returns true if the transport is open 48 | IsOpen() bool 49 | } 50 | 51 | type stringWriter interface { 52 | WriteString(s string) (n int, err error) 53 | } 54 | 55 | 56 | // This is "enchanced" transport with extra capabilities. You need to use one of these 57 | // to construct protocol. 58 | // Notably, TSocket does not implement this interface, and it is always a mistake to use 59 | // TSocket directly in protocol. 60 | type TRichTransport interface { 61 | io.ReadWriter 62 | io.ByteReader 63 | io.ByteWriter 64 | stringWriter 65 | Flusher 66 | ReadSizeProvider 67 | } 68 | 69 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/transport_exception.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "errors" 24 | "io" 25 | ) 26 | 27 | type timeoutable interface { 28 | Timeout() bool 29 | } 30 | 31 | // Thrift Transport exception 32 | type TTransportException interface { 33 | TException 34 | TypeId() int 35 | Err() error 36 | } 37 | 38 | const ( 39 | UNKNOWN_TRANSPORT_EXCEPTION = 0 40 | NOT_OPEN = 1 41 | ALREADY_OPEN = 2 42 | TIMED_OUT = 3 43 | END_OF_FILE = 4 44 | ) 45 | 46 | type tTransportException struct { 47 | typeId int 48 | err error 49 | } 50 | 51 | func (p *tTransportException) TypeId() int { 52 | return p.typeId 53 | } 54 | 55 | func (p *tTransportException) Error() string { 56 | return p.err.Error() 57 | } 58 | 59 | func (p *tTransportException) Err() error { 60 | return p.err 61 | } 62 | 63 | func NewTTransportException(t int, e string) TTransportException { 64 | return &tTransportException{typeId: t, err: errors.New(e)} 65 | } 66 | 67 | func NewTTransportExceptionFromError(e error) TTransportException { 68 | if e == nil { 69 | return nil 70 | } 71 | 72 | if t, ok := e.(TTransportException); ok { 73 | return t 74 | } 75 | 76 | switch v := e.(type) { 77 | case TTransportException: 78 | return v 79 | case timeoutable: 80 | if v.Timeout() { 81 | return &tTransportException{typeId: TIMED_OUT, err: e} 82 | } 83 | } 84 | 85 | if e == io.EOF { 86 | return &tTransportException{typeId: END_OF_FILE, err: e} 87 | } 88 | 89 | return &tTransportException{typeId: UNKNOWN_TRANSPORT_EXCEPTION, err: e} 90 | } 91 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/transport_exception_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "fmt" 24 | "io" 25 | 26 | "testing" 27 | ) 28 | 29 | type timeout struct{ timedout bool } 30 | 31 | func (t *timeout) Timeout() bool { 32 | return t.timedout 33 | } 34 | 35 | func (t *timeout) Error() string { 36 | return fmt.Sprintf("Timeout: %v", t.timedout) 37 | } 38 | 39 | func TestTExceptionTimeout(t *testing.T) { 40 | timeout := &timeout{true} 41 | exception := NewTTransportExceptionFromError(timeout) 42 | if timeout.Error() != exception.Error() { 43 | t.Fatalf("Error did not match: expected %q, got %q", timeout.Error(), exception.Error()) 44 | } 45 | 46 | if exception.TypeId() != TIMED_OUT { 47 | t.Fatalf("TypeId was not TIMED_OUT: expected %v, got %v", TIMED_OUT, exception.TypeId()) 48 | } 49 | } 50 | 51 | func TestTExceptionEOF(t *testing.T) { 52 | exception := NewTTransportExceptionFromError(io.EOF) 53 | if io.EOF.Error() != exception.Error() { 54 | t.Fatalf("Error did not match: expected %q, got %q", io.EOF.Error(), exception.Error()) 55 | } 56 | 57 | if exception.TypeId() != END_OF_FILE { 58 | t.Fatalf("TypeId was not END_OF_FILE: expected %v, got %v", END_OF_FILE, exception.TypeId()) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/transport_factory.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | // Factory class used to create wrapped instance of Transports. 23 | // This is used primarily in servers, which get Transports from 24 | // a ServerTransport and then may want to mutate them (i.e. create 25 | // a BufferedTransport from the underlying base transport) 26 | type TTransportFactory interface { 27 | GetTransport(trans TTransport) TTransport 28 | } 29 | 30 | type tTransportFactory struct{} 31 | 32 | // Return a wrapped instance of the base Transport. 33 | func (p *tTransportFactory) GetTransport(trans TTransport) TTransport { 34 | return trans 35 | } 36 | 37 | func NewTTransportFactory() TTransportFactory { 38 | return &tTransportFactory{} 39 | } 40 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/type.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | // Type constants in the Thrift protocol 23 | type TType byte 24 | 25 | const ( 26 | STOP = 0 27 | VOID = 1 28 | BOOL = 2 29 | BYTE = 3 30 | I08 = 3 31 | DOUBLE = 4 32 | I16 = 6 33 | I32 = 8 34 | I64 = 10 35 | STRING = 11 36 | UTF7 = 11 37 | STRUCT = 12 38 | MAP = 13 39 | SET = 14 40 | LIST = 15 41 | UTF8 = 16 42 | UTF16 = 17 43 | //BINARY = 18 wrong and unusued 44 | ) 45 | 46 | var typeNames = map[int]string{ 47 | STOP: "STOP", 48 | VOID: "VOID", 49 | BOOL: "BOOL", 50 | BYTE: "BYTE", 51 | DOUBLE: "DOUBLE", 52 | I16: "I16", 53 | I32: "I32", 54 | I64: "I64", 55 | STRING: "STRING", 56 | STRUCT: "STRUCT", 57 | MAP: "MAP", 58 | SET: "SET", 59 | LIST: "LIST", 60 | UTF8: "UTF8", 61 | UTF16: "UTF16", 62 | } 63 | 64 | func (p TType) String() string { 65 | if s, ok := typeNames[int(p)]; ok { 66 | return s 67 | } 68 | return "Unknown" 69 | } 70 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/zlib_transport.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "compress/zlib" 24 | "io" 25 | "log" 26 | ) 27 | 28 | // TZlibTransportFactory is a factory for TZlibTransport instances 29 | type TZlibTransportFactory struct { 30 | level int 31 | } 32 | 33 | // TZlibTransport is a TTransport implementation that makes use of zlib compression. 34 | type TZlibTransport struct { 35 | reader io.ReadCloser 36 | transport TTransport 37 | writer *zlib.Writer 38 | } 39 | 40 | // GetTransport constructs a new instance of NewTZlibTransport 41 | func (p *TZlibTransportFactory) GetTransport(trans TTransport) TTransport { 42 | t, _ := NewTZlibTransport(trans, p.level) 43 | return t 44 | } 45 | 46 | // NewTZlibTransportFactory constructs a new instance of NewTZlibTransportFactory 47 | func NewTZlibTransportFactory(level int) *TZlibTransportFactory { 48 | return &TZlibTransportFactory{level: level} 49 | } 50 | 51 | // NewTZlibTransport constructs a new instance of TZlibTransport 52 | func NewTZlibTransport(trans TTransport, level int) (*TZlibTransport, error) { 53 | w, err := zlib.NewWriterLevel(trans, level) 54 | if err != nil { 55 | log.Println(err) 56 | return nil, err 57 | } 58 | 59 | return &TZlibTransport{ 60 | writer: w, 61 | transport: trans, 62 | }, nil 63 | } 64 | 65 | // Close closes the reader and writer (flushing any unwritten data) and closes 66 | // the underlying transport. 67 | func (z *TZlibTransport) Close() error { 68 | if z.reader != nil { 69 | if err := z.reader.Close(); err != nil { 70 | return err 71 | } 72 | } 73 | if err := z.writer.Close(); err != nil { 74 | return err 75 | } 76 | return z.transport.Close() 77 | } 78 | 79 | // Flush flushes the writer and its underlying transport. 80 | func (z *TZlibTransport) Flush() error { 81 | if err := z.writer.Flush(); err != nil { 82 | return err 83 | } 84 | return z.transport.Flush() 85 | } 86 | 87 | // IsOpen returns true if the transport is open 88 | func (z *TZlibTransport) IsOpen() bool { 89 | return z.transport.IsOpen() 90 | } 91 | 92 | // Open opens the transport for communication 93 | func (z *TZlibTransport) Open() error { 94 | return z.transport.Open() 95 | } 96 | 97 | func (z *TZlibTransport) Read(p []byte) (int, error) { 98 | if z.reader == nil { 99 | r, err := zlib.NewReader(z.transport) 100 | if err != nil { 101 | return 0, NewTTransportExceptionFromError(err) 102 | } 103 | z.reader = r 104 | } 105 | 106 | return z.reader.Read(p) 107 | } 108 | 109 | // RemainingBytes returns the size in bytes of the data that is still to be 110 | // read. 111 | func (z *TZlibTransport) RemainingBytes() uint64 { 112 | return z.transport.RemainingBytes() 113 | } 114 | 115 | func (z *TZlibTransport) Write(p []byte) (int, error) { 116 | return z.writer.Write(p) 117 | } 118 | -------------------------------------------------------------------------------- /thirdparty/github.com/apache/thrift/lib/go/thrift/zlib_transport_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | package thrift 21 | 22 | import ( 23 | "compress/zlib" 24 | "testing" 25 | ) 26 | 27 | func TestZlibTransport(t *testing.T) { 28 | trans, err := NewTZlibTransport(NewTMemoryBuffer(), zlib.BestCompression) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | TransportTest(t, trans, trans) 33 | } 34 | -------------------------------------------------------------------------------- /tools/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // Package tools adds dependencies on tools used while developing tally that 22 | // should not be included in its dependencies. 23 | package tools 24 | -------------------------------------------------------------------------------- /tools/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/uber-go/tally/tools 2 | 3 | go 1.18 4 | 5 | require ( 6 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 7 | honnef.co/go/tools v0.3.2 8 | ) 9 | 10 | require ( 11 | github.com/BurntSushi/toml v1.1.0 // indirect 12 | golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d // indirect 13 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect 14 | golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect 15 | golang.org/x/tools v0.1.11 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /tools/go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= 2 | github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= 3 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 4 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 5 | golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d h1:+W8Qf4iJtMGKkyAygcKohjxTk4JPsL9DpzApJ22m5Ic= 6 | golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= 7 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= 8 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 9 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 10 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= 11 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 12 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 13 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 14 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 15 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 16 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 17 | golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= 18 | golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 19 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 20 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 21 | golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY= 22 | golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= 23 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 24 | honnef.co/go/tools v0.3.2 h1:ytYb4rOqyp1TSa2EPvNVwtPQJctSELKaMyLfqNP4+34= 25 | honnef.co/go/tools v0.3.2/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= 26 | -------------------------------------------------------------------------------- /tools/tools.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | //go:build tools 22 | // +build tools 23 | 24 | package tools 25 | 26 | import ( 27 | _ "golang.org/x/lint/golint" 28 | _ "honnef.co/go/tools/cmd/staticcheck" 29 | ) 30 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package tally 22 | 23 | import ( 24 | "math" 25 | "time" 26 | ) 27 | 28 | func safeFloat64ToInt64(v float64) int64 { 29 | if v >= float64(math.MaxInt64) { 30 | return math.MaxInt64 31 | } 32 | if v <= float64(math.MinInt64) { 33 | return math.MinInt64 34 | } 35 | return int64(v) 36 | } 37 | 38 | func safeDurationSum(a, b time.Duration) time.Duration { 39 | sum := a + b 40 | switch { 41 | case b > 0 && sum < a: // Overflow 42 | return time.Duration(math.MaxInt64) 43 | case b < 0 && sum > a: // Underflow 44 | return time.Duration(math.MinInt64) 45 | default: 46 | return sum 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package tally 22 | 23 | import ( 24 | "math" 25 | "testing" 26 | "time" 27 | 28 | "github.com/stretchr/testify/assert" 29 | ) 30 | 31 | func TestSafeFloat64ToInt64(t *testing.T) { 32 | assert.Equal(t, int64(math.MaxInt64), safeFloat64ToInt64(float64(math.MaxInt64)*2)) 33 | assert.Equal(t, int64(math.MaxInt64), safeFloat64ToInt64(float64(math.MaxInt64)+1000)) 34 | 35 | assert.Equal(t, int64(math.MinInt64), safeFloat64ToInt64(float64(math.MinInt64)*2)) 36 | assert.Equal(t, int64(math.MinInt64), safeFloat64ToInt64(float64(math.MinInt64)-1000)) 37 | 38 | assert.Equal(t, int64(1000), safeFloat64ToInt64(1000)) 39 | } 40 | 41 | func TestSafeDurationSum(t *testing.T) { 42 | maxDuration := time.Duration(math.MaxInt64) 43 | assert.Equal(t, maxDuration, safeDurationSum(maxDuration, 1)) 44 | 45 | minDuration := time.Duration(math.MinInt64) 46 | assert.Equal(t, minDuration, safeDurationSum(minDuration, -1)) 47 | 48 | assert.Equal(t, 10*time.Second, safeDurationSum(3*time.Second, 7*time.Second)) 49 | } 50 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | package tally 22 | 23 | // Version is the current version of the library. 24 | const Version = "4.1.17" 25 | --------------------------------------------------------------------------------