├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── agent.go ├── example └── main.go ├── example_test.go ├── go.mod ├── go.sum ├── jaeger.go ├── jaeger_test.go └── propagation ├── http.go └── http_test.go /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | labels: bug 5 | --- 6 | 7 | Please answer these questions before submitting a bug report. 8 | 9 | ### What version of the Exporter are you using? 10 | 11 | 12 | ### What version of OpenCensus are you using? 13 | 14 | 15 | ### What version of Go are you using? 16 | 17 | 18 | ### What did you do? 19 | If possible, provide a recipe for reproducing the error. 20 | 21 | 22 | ### What did you expect to see? 23 | 24 | 25 | ### What did you see instead? 26 | 27 | 28 | ### Additional context 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | labels: feature-request 5 | --- 6 | 7 | **NB:** Before opening a feature request against this repo, consider whether the feature should/could be implemented in exporter libraries in other languages. If so, please [open an issue on opencensus-specs](https://github.com/census-instrumentation/opencensus-specs/issues/new) first. 8 | 9 | 10 | 11 | **Is your feature request related to a problem? Please describe.** 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 13 | 14 | **Describe the solution you'd like** 15 | A clear and concise description of what you want to happen. 16 | 17 | **Describe alternatives you've considered** 18 | A clear and concise description of any alternative solutions or features you've considered. 19 | 20 | **Additional context** 21 | Add any other context or screenshots about the feature request here. 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go_import_path: contrib.go.opencensus.io 4 | 5 | go: 6 | - 1.11.x 7 | 8 | env: 9 | global: 10 | GO111MODULE=on 11 | 12 | before_script: 13 | - make install-tools 14 | 15 | script: 16 | - make travis-ci 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # TODO: Fix this on windows. 2 | ALL_SRC := $(shell find . -name '*.go' \ 3 | -not -path './vendor/*' \ 4 | -not -path '*/gen-go/*' \ 5 | -type f | sort) 6 | ALL_PKGS := $(shell go list $(sort $(dir $(ALL_SRC)))) 7 | 8 | GOTEST_OPT?=-v -race -timeout 30s 9 | GOTEST_OPT_WITH_COVERAGE = $(GOTEST_OPT) -coverprofile=coverage.txt -covermode=atomic 10 | GOTEST=go test 11 | GOFMT=gofmt 12 | GOLINT=golint 13 | GOVET=go vet 14 | EMBEDMD=embedmd 15 | # TODO decide if we need to change these names. 16 | README_FILES := $(shell find . -name '*README.md' | sort | tr '\n' ' ') 17 | 18 | 19 | .DEFAULT_GOAL := fmt-lint-vet-embedmd-test 20 | 21 | .PHONY: fmt-lint-vet-embedmd-test 22 | fmt-lint-vet-embedmd-test: fmt lint vet embedmd test 23 | 24 | # TODO enable test-with-coverage in tavis 25 | .PHONY: travis-ci 26 | travis-ci: fmt lint vet embedmd test test-386 27 | 28 | all-pkgs: 29 | @echo $(ALL_PKGS) | tr ' ' '\n' | sort 30 | 31 | all-srcs: 32 | @echo $(ALL_SRC) | tr ' ' '\n' | sort 33 | 34 | .PHONY: test 35 | test: 36 | $(GOTEST) $(GOTEST_OPT) $(ALL_PKGS) 37 | 38 | .PHONY: test-386 39 | test-386: 40 | GOARCH=386 $(GOTEST) -v -timeout 30s $(ALL_PKGS) 41 | 42 | .PHONY: test-with-coverage 43 | test-with-coverage: 44 | $(GOTEST) $(GOTEST_OPT_WITH_COVERAGE) $(ALL_PKGS) 45 | 46 | .PHONY: fmt 47 | fmt: 48 | @FMTOUT=`$(GOFMT) -s -l $(ALL_SRC) 2>&1`; \ 49 | if [ "$$FMTOUT" ]; then \ 50 | echo "$(GOFMT) FAILED => gofmt the following files:\n"; \ 51 | echo "$$FMTOUT\n"; \ 52 | exit 1; \ 53 | else \ 54 | echo "Fmt finished successfully"; \ 55 | fi 56 | 57 | .PHONY: lint 58 | lint: 59 | @LINTOUT=`$(GOLINT) $(ALL_PKGS) 2>&1`; \ 60 | if [ "$$LINTOUT" ]; then \ 61 | echo "$(GOLINT) FAILED => clean the following lint errors:\n"; \ 62 | echo "$$LINTOUT\n"; \ 63 | exit 1; \ 64 | else \ 65 | echo "Lint finished successfully"; \ 66 | fi 67 | 68 | .PHONY: vet 69 | vet: 70 | # TODO: Understand why go vet downloads "github.com/google/go-cmp v0.2.0" 71 | @VETOUT=`$(GOVET) ./... | grep -v "go: downloading" 2>&1`; \ 72 | if [ "$$VETOUT" ]; then \ 73 | echo "$(GOVET) FAILED => go vet the following files:\n"; \ 74 | echo "$$VETOUT\n"; \ 75 | exit 1; \ 76 | else \ 77 | echo "Vet finished successfully"; \ 78 | fi 79 | 80 | .PHONY: embedmd 81 | embedmd: 82 | @EMBEDMDOUT=`$(EMBEDMD) -d $(README_FILES) 2>&1`; \ 83 | if [ "$$EMBEDMDOUT" ]; then \ 84 | echo "$(EMBEDMD) FAILED => embedmd the following files:\n"; \ 85 | echo "$$EMBEDMDOUT\n"; \ 86 | exit 1; \ 87 | else \ 88 | echo "Embedmd finished successfully"; \ 89 | fi 90 | 91 | .PHONY: install-tools 92 | install-tools: 93 | go get -u golang.org/x/tools/cmd/cover 94 | go get -u golang.org/x/lint/golint 95 | go get -u github.com/rakyll/embedmd 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **Warning** 2 | > 3 | > OpenCensus and OpenTracing have merged to form [OpenTelemetry](https://opentelemetry.io), which serves as the next major version of OpenCensus and OpenTracing. 4 | > 5 | > OpenTelemetry has now reached feature parity with OpenCensus, with tracing and metrics SDKs available in .NET, Golang, Java, NodeJS, and Python. **All OpenCensus Github repositories, except [census-instrumentation/opencensus-python](https://github.com/census-instrumentation/opencensus-python), will be archived on July 31st, 2023**. We encourage users to migrate to OpenTelemetry by this date. 6 | > 7 | > To help you gradually migrate your instrumentation to OpenTelemetry, bridges are available in Java, Go, Python, and JS. [**Read the full blog post to learn more**](https://opentelemetry.io/blog/2023/sunsetting-opencensus/). 8 | 9 | # OpenCensus Go Jaeger Exporter 10 | 11 | [![Build Status](https://travis-ci.org/census-ecosystem/opencensus-go-exporter-jaeger.svg?branch=master)](https://travis-ci.org/census-ecosystem/opencensus-go-exporter-jaeger) [![GoDoc][godoc-image]][godoc-url] 12 | 13 | Provides OpenCensus exporter support for Jaeger. 14 | 15 | ## Installation 16 | 17 | ``` 18 | $ go get -u contrib.go.opencensus.io/exporter/jaeger 19 | ``` 20 | 21 | [godoc-image]: https://godoc.org/contrib.go.opencensus.io/exporter/jaeger?status.svg 22 | [godoc-url]: https://godoc.org/contrib.go.opencensus.io/exporter/jaeger 23 | -------------------------------------------------------------------------------- /agent.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, OpenCensus Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package jaeger 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "net" 21 | 22 | "github.com/uber/jaeger-client-go/thrift" 23 | "github.com/uber/jaeger-client-go/thrift-gen/agent" 24 | "github.com/uber/jaeger-client-go/thrift-gen/jaeger" 25 | "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" 26 | ) 27 | 28 | // udpPacketMaxLength is the max size of UDP packet we want to send, synced with jaeger-agent 29 | const udpPacketMaxLength = 65000 30 | 31 | // agentClientUDP is a UDP client to Jaeger agent that implements gen.Agent interface. 32 | type agentClientUDP struct { 33 | agent.Agent 34 | io.Closer 35 | 36 | connUDP *net.UDPConn 37 | client *agent.AgentClient 38 | maxPacketSize int // max size of datagram in bytes 39 | thriftBuffer *thrift.TMemoryBuffer // buffer used to calculate byte size of a span 40 | } 41 | 42 | // newAgentClientUDP creates a client that sends spans to Jaeger Agent over UDP. 43 | func newAgentClientUDP(hostPort string, maxPacketSize int) (*agentClientUDP, error) { 44 | if maxPacketSize == 0 { 45 | maxPacketSize = udpPacketMaxLength 46 | } 47 | 48 | thriftBuffer := thrift.NewTMemoryBufferLen(maxPacketSize) 49 | protocolFactory := thrift.NewTCompactProtocolFactory() 50 | client := agent.NewAgentClientFactory(thriftBuffer, protocolFactory) 51 | 52 | destAddr, err := net.ResolveUDPAddr("udp", hostPort) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | connUDP, err := net.DialUDP(destAddr.Network(), nil, destAddr) 58 | if err != nil { 59 | return nil, err 60 | } 61 | if err := connUDP.SetWriteBuffer(maxPacketSize); err != nil { 62 | return nil, err 63 | } 64 | 65 | clientUDP := &agentClientUDP{ 66 | connUDP: connUDP, 67 | client: client, 68 | maxPacketSize: maxPacketSize, 69 | thriftBuffer: thriftBuffer} 70 | return clientUDP, nil 71 | } 72 | 73 | // EmitBatch implements EmitBatch() of Agent interface 74 | func (a *agentClientUDP) EmitBatch(batch *jaeger.Batch) error { 75 | a.thriftBuffer.Reset() 76 | a.client.SeqId = 0 // we have no need for distinct SeqIds for our one-way UDP messages 77 | if err := a.client.EmitBatch(batch); err != nil { 78 | return err 79 | } 80 | if a.thriftBuffer.Len() > a.maxPacketSize { 81 | return fmt.Errorf("Data does not fit within one UDP packet; size %d, max %d, spans %d", 82 | a.thriftBuffer.Len(), a.maxPacketSize, len(batch.Spans)) 83 | } 84 | _, err := a.connUDP.Write(a.thriftBuffer.Bytes()) 85 | return err 86 | } 87 | 88 | // EmitZipkinBatch implements EmitZipkinBatch() of Agent interface 89 | func EmitZipkinBatch(spans []*zipkincore.Span) (err error) { 90 | return fmt.Errorf("not implemented") 91 | } 92 | 93 | // Close implements Close() of io.Closer and closes the underlying UDP connection. 94 | func (a *agentClientUDP) Close() error { 95 | return a.connUDP.Close() 96 | } 97 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017, OpenCensus Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Command jaeger is an example program that creates spans 16 | // and uploads to Jaeger. 17 | package main 18 | 19 | import ( 20 | "context" 21 | "log" 22 | 23 | "contrib.go.opencensus.io/exporter/jaeger" 24 | "go.opencensus.io/trace" 25 | ) 26 | 27 | func main() { 28 | ctx := context.Background() 29 | 30 | // Register the Jaeger exporter to be able to retrieve 31 | // the collected spans. 32 | exporter, err := jaeger.NewExporter(jaeger.Options{ 33 | CollectorEndpoint: "http://localhost:14268/api/traces", 34 | Process: jaeger.Process{ 35 | ServiceName: "trace-demo", 36 | }, 37 | }) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | trace.RegisterExporter(exporter) 42 | 43 | // For demoing purposes, always sample. In a production application, you should 44 | // configure this to a trace.ProbabilitySampler set at the desired 45 | // probability. 46 | trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()}) 47 | 48 | ctx, span := trace.StartSpan(ctx, "/foo") 49 | bar(ctx) 50 | span.End() 51 | 52 | exporter.Flush() 53 | } 54 | 55 | func bar(ctx context.Context) { 56 | ctx, span := trace.StartSpan(ctx, "/bar") 57 | defer span.End() 58 | 59 | // Do bar... 60 | } 61 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, OpenCensus Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package jaeger_test 16 | 17 | import ( 18 | "log" 19 | 20 | "contrib.go.opencensus.io/exporter/jaeger" 21 | "go.opencensus.io/trace" 22 | ) 23 | 24 | func ExampleNewExporter_collector() { 25 | // Register the Jaeger exporter to be able to retrieve 26 | // the collected spans. 27 | exporter, err := jaeger.NewExporter(jaeger.Options{ 28 | Endpoint: "http://localhost:14268", 29 | Process: jaeger.Process{ 30 | ServiceName: "trace-demo", 31 | }, 32 | }) 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | trace.RegisterExporter(exporter) 37 | } 38 | 39 | func ExampleNewExporter_agent() { 40 | // Register the Jaeger exporter to be able to retrieve 41 | // the collected spans. 42 | exporter, err := jaeger.NewExporter(jaeger.Options{ 43 | AgentEndpoint: "localhost:6831", 44 | Process: jaeger.Process{ 45 | ServiceName: "trace-demo", 46 | }, 47 | }) 48 | if err != nil { 49 | log.Fatal(err) 50 | } 51 | trace.RegisterExporter(exporter) 52 | } 53 | 54 | // ExampleNewExporter_processTags shows how to set ProcessTags 55 | // on a Jaeger exporter. These tags will be added to the exported 56 | // Jaeger process. 57 | func ExampleNewExporter_processTags() { 58 | // Register the Jaeger exporter to be able to retrieve 59 | // the collected spans. 60 | exporter, err := jaeger.NewExporter(jaeger.Options{ 61 | AgentEndpoint: "localhost:6831", 62 | Process: jaeger.Process{ 63 | ServiceName: "trace-demo", 64 | Tags: []jaeger.Tag{ 65 | jaeger.StringTag("ip", "127.0.0.1"), 66 | jaeger.BoolTag("demo", true), 67 | }, 68 | }, 69 | }) 70 | if err != nil { 71 | log.Fatal(err) 72 | } 73 | trace.RegisterExporter(exporter) 74 | } 75 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module contrib.go.opencensus.io/exporter/jaeger 2 | 3 | require ( 4 | github.com/uber/jaeger-client-go v2.25.0+incompatible 5 | go.opencensus.io v0.22.4 6 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect 7 | google.golang.org/api v0.29.0 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 12 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 13 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 14 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 15 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 16 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 17 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 18 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 19 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 20 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 21 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 22 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 23 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 24 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 25 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 26 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 27 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 28 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 29 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 30 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 31 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 32 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 33 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 34 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 35 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 36 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 37 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 38 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 39 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 40 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 41 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 42 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 43 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= 44 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 45 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 46 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 47 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 48 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 49 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 50 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 51 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 52 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 53 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 54 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 55 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 56 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 57 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 58 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 59 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 60 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 61 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 62 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 63 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 64 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 65 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 66 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 67 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 68 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 69 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 70 | github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= 71 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 72 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 73 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 74 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 75 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 76 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 77 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 78 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 79 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 80 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 81 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 82 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 83 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 84 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 85 | github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U= 86 | github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= 87 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 88 | go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= 89 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 90 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 91 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 92 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 93 | go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= 94 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 95 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 96 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 97 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 98 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 99 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 100 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 101 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 102 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 103 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 104 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 105 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 106 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 107 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 108 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 109 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 110 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 111 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 112 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 113 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 114 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 115 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 116 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 117 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 118 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 119 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 120 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 121 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 122 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 123 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 124 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 125 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 126 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 127 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 128 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 129 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 130 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 131 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 132 | golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= 133 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 134 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 135 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 136 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 137 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 138 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 139 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 140 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 141 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 142 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 143 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 144 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 145 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 146 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 147 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 148 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 149 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 150 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 151 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 152 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 153 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 154 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= 155 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 156 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 157 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 158 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 159 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= 160 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 161 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 162 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 163 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 164 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 165 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 166 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 167 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 168 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 169 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 170 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 171 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 172 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 173 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 174 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 175 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 176 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 177 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 178 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 179 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 180 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 181 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 182 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 183 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 184 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 185 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 186 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 187 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 188 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 189 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 190 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 191 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 192 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 193 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 194 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 195 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 196 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 197 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 198 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 199 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 200 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 201 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 202 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 203 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 204 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 205 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 206 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 207 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 208 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 209 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 210 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 211 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 212 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 213 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 214 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 215 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 216 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 217 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 218 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 219 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 220 | google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI= 221 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 222 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 223 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 224 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 225 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 226 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 227 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 228 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 229 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 230 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 231 | google.golang.org/api v0.29.0 h1:BaiDisFir8O4IJxvAabCGGkQ6yCJegNQqSVoYUNAnbk= 232 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 233 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 234 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 235 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 236 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 237 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 238 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 239 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 240 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 241 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 242 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 243 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 244 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 245 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 246 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 247 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 248 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 249 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 250 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 251 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 252 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 253 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 254 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 255 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 256 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 257 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 258 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 259 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 260 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 261 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 262 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 263 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 264 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 265 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 266 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 267 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 268 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 269 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 270 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 271 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 272 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 273 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 274 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 275 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 276 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 277 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 278 | -------------------------------------------------------------------------------- /jaeger.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, OpenCensus Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package jaeger contains an OpenCensus tracing exporter for Jaeger. 16 | package jaeger // import "contrib.go.opencensus.io/exporter/jaeger" 17 | 18 | import ( 19 | "bytes" 20 | "encoding/binary" 21 | "errors" 22 | "fmt" 23 | "io" 24 | "io/ioutil" 25 | "log" 26 | "net/http" 27 | 28 | "github.com/uber/jaeger-client-go/thrift" 29 | "github.com/uber/jaeger-client-go/thrift-gen/jaeger" 30 | 31 | "go.opencensus.io/trace" 32 | "google.golang.org/api/support/bundler" 33 | ) 34 | 35 | const defaultServiceName = "OpenCensus" 36 | 37 | // Options are the options to be used when initializing a Jaeger exporter. 38 | type Options struct { 39 | // Endpoint is the Jaeger HTTP Thrift endpoint. 40 | // For example, http://localhost:14268. 41 | // 42 | // Deprecated: Use CollectorEndpoint instead. 43 | Endpoint string 44 | 45 | // CollectorEndpoint is the full url to the Jaeger HTTP Thrift collector. 46 | // For example, http://localhost:14268/api/traces 47 | CollectorEndpoint string 48 | 49 | // AgentEndpoint instructs exporter to send spans to jaeger-agent at this address. 50 | // For example, localhost:6831. 51 | AgentEndpoint string 52 | 53 | // OnError is the hook to be called when there is 54 | // an error occurred when uploading the stats data. 55 | // If no custom hook is set, errors are logged. 56 | // Optional. 57 | OnError func(err error) 58 | 59 | // Username to be used if basic auth is required. 60 | // Optional. 61 | Username string 62 | 63 | // Password to be used if basic auth is required. 64 | // Optional. 65 | Password string 66 | 67 | // ServiceName is the Jaeger service name. 68 | // Deprecated: Specify Process instead. 69 | ServiceName string 70 | 71 | // Process contains the information about the exporting process. 72 | Process Process 73 | 74 | //BufferMaxCount defines the total number of traces that can be buffered in memory 75 | BufferMaxCount int 76 | } 77 | 78 | // NewExporter returns a trace.Exporter implementation that exports 79 | // the collected spans to Jaeger. 80 | func NewExporter(o Options) (*Exporter, error) { 81 | if o.Endpoint == "" && o.CollectorEndpoint == "" && o.AgentEndpoint == "" { 82 | return nil, errors.New("missing endpoint for Jaeger exporter") 83 | } 84 | 85 | var endpoint string 86 | var client *agentClientUDP 87 | var err error 88 | if o.Endpoint != "" { 89 | endpoint = o.Endpoint + "/api/traces?format=jaeger.thrift" 90 | log.Printf("Endpoint has been deprecated. Please use CollectorEndpoint instead.") 91 | } else if o.CollectorEndpoint != "" { 92 | endpoint = o.CollectorEndpoint 93 | } else { 94 | client, err = newAgentClientUDP(o.AgentEndpoint, udpPacketMaxLength) 95 | if err != nil { 96 | return nil, err 97 | } 98 | } 99 | onError := func(err error) { 100 | if o.OnError != nil { 101 | o.OnError(err) 102 | return 103 | } 104 | log.Printf("Error when uploading spans to Jaeger: %v", err) 105 | } 106 | service := o.Process.ServiceName 107 | if service == "" && o.ServiceName != "" { 108 | // fallback to old service name if specified 109 | service = o.ServiceName 110 | } else if service == "" { 111 | service = defaultServiceName 112 | } 113 | tags := make([]*jaeger.Tag, len(o.Process.Tags)) 114 | for i, tag := range o.Process.Tags { 115 | tags[i] = attributeToTag(tag.key, tag.value) 116 | } 117 | e := &Exporter{ 118 | endpoint: endpoint, 119 | agentEndpoint: o.AgentEndpoint, 120 | client: client, 121 | username: o.Username, 122 | password: o.Password, 123 | process: &jaeger.Process{ 124 | ServiceName: service, 125 | Tags: tags, 126 | }, 127 | } 128 | bundler := bundler.NewBundler((*jaeger.Span)(nil), func(bundle interface{}) { 129 | if err := e.upload(bundle.([]*jaeger.Span)); err != nil { 130 | onError(err) 131 | } 132 | }) 133 | 134 | // Set BufferedByteLimit with the total number of spans that are permissible to be held in memory. 135 | // This needs to be done since the size of messages is always set to 1. Failing to set this would allow 136 | // 1G messages to be held in memory since that is the default value of BufferedByteLimit. 137 | if o.BufferMaxCount != 0 { 138 | bundler.BufferedByteLimit = o.BufferMaxCount 139 | } 140 | 141 | e.bundler = bundler 142 | return e, nil 143 | } 144 | 145 | // Process contains the information exported to jaeger about the source 146 | // of the trace data. 147 | type Process struct { 148 | // ServiceName is the Jaeger service name. 149 | ServiceName string 150 | 151 | // Tags are added to Jaeger Process exports 152 | Tags []Tag 153 | } 154 | 155 | // Tag defines a key-value pair 156 | // It is limited to the possible conversions to *jaeger.Tag by attributeToTag 157 | type Tag struct { 158 | key string 159 | value interface{} 160 | } 161 | 162 | // BoolTag creates a new tag of type bool, exported as jaeger.TagType_BOOL 163 | func BoolTag(key string, value bool) Tag { 164 | return Tag{key, value} 165 | } 166 | 167 | // StringTag creates a new tag of type string, exported as jaeger.TagType_STRING 168 | func StringTag(key string, value string) Tag { 169 | return Tag{key, value} 170 | } 171 | 172 | // Int64Tag creates a new tag of type int64, exported as jaeger.TagType_LONG 173 | func Int64Tag(key string, value int64) Tag { 174 | return Tag{key, value} 175 | } 176 | 177 | // Exporter is an implementation of trace.Exporter that uploads spans to Jaeger. 178 | type Exporter struct { 179 | endpoint string 180 | agentEndpoint string 181 | process *jaeger.Process 182 | bundler *bundler.Bundler 183 | client *agentClientUDP 184 | 185 | username, password string 186 | } 187 | 188 | var _ trace.Exporter = (*Exporter)(nil) 189 | 190 | // ExportSpan exports a SpanData to Jaeger. 191 | func (e *Exporter) ExportSpan(data *trace.SpanData) { 192 | e.bundler.Add(spanDataToThrift(data), 1) 193 | // TODO(jbd): Handle oversized bundlers. 194 | } 195 | 196 | // As per the OpenCensus Status code mapping in 197 | // https://opencensus.io/tracing/span/status/ 198 | // the status is OK if the code is 0. 199 | const opencensusStatusCodeOK = 0 200 | 201 | func spanDataToThrift(data *trace.SpanData) *jaeger.Span { 202 | tags := make([]*jaeger.Tag, 0, len(data.Attributes)) 203 | for k, v := range data.Attributes { 204 | tag := attributeToTag(k, v) 205 | if tag != nil { 206 | tags = append(tags, tag) 207 | } 208 | } 209 | 210 | tags = append(tags, 211 | attributeToTag("status.code", data.Status.Code), 212 | attributeToTag("status.message", data.Status.Message), 213 | ) 214 | 215 | // Ensure that if Status.Code is not OK, that we set the "error" tag on the Jaeger span. 216 | // See Issue https://github.com/census-instrumentation/opencensus-go/issues/1041 217 | if data.Status.Code != opencensusStatusCodeOK { 218 | tags = append(tags, attributeToTag("error", true)) 219 | } 220 | 221 | var logs []*jaeger.Log 222 | for _, a := range data.Annotations { 223 | fields := make([]*jaeger.Tag, 0, len(a.Attributes)) 224 | for k, v := range a.Attributes { 225 | tag := attributeToTag(k, v) 226 | if tag != nil { 227 | fields = append(fields, tag) 228 | } 229 | } 230 | fields = append(fields, attributeToTag("message", a.Message)) 231 | logs = append(logs, &jaeger.Log{ 232 | Timestamp: a.Time.UnixNano() / 1000, 233 | Fields: fields, 234 | }) 235 | } 236 | var refs []*jaeger.SpanRef 237 | for _, link := range data.Links { 238 | refs = append(refs, &jaeger.SpanRef{ 239 | TraceIdHigh: bytesToInt64(link.TraceID[0:8]), 240 | TraceIdLow: bytesToInt64(link.TraceID[8:16]), 241 | SpanId: bytesToInt64(link.SpanID[:]), 242 | }) 243 | } 244 | return &jaeger.Span{ 245 | TraceIdHigh: bytesToInt64(data.TraceID[0:8]), 246 | TraceIdLow: bytesToInt64(data.TraceID[8:16]), 247 | SpanId: bytesToInt64(data.SpanID[:]), 248 | ParentSpanId: bytesToInt64(data.ParentSpanID[:]), 249 | OperationName: name(data), 250 | Flags: int32(data.TraceOptions), 251 | StartTime: data.StartTime.UnixNano() / 1000, 252 | Duration: data.EndTime.Sub(data.StartTime).Nanoseconds() / 1000, 253 | Tags: tags, 254 | Logs: logs, 255 | References: refs, 256 | } 257 | } 258 | 259 | func name(sd *trace.SpanData) string { 260 | n := sd.Name 261 | switch sd.SpanKind { 262 | case trace.SpanKindClient: 263 | n = "Sent." + n 264 | case trace.SpanKindServer: 265 | n = "Recv." + n 266 | } 267 | return n 268 | } 269 | 270 | func attributeToTag(key string, a interface{}) *jaeger.Tag { 271 | var tag *jaeger.Tag 272 | switch value := a.(type) { 273 | case bool: 274 | tag = &jaeger.Tag{ 275 | Key: key, 276 | VBool: &value, 277 | VType: jaeger.TagType_BOOL, 278 | } 279 | case string: 280 | tag = &jaeger.Tag{ 281 | Key: key, 282 | VStr: &value, 283 | VType: jaeger.TagType_STRING, 284 | } 285 | case int64: 286 | tag = &jaeger.Tag{ 287 | Key: key, 288 | VLong: &value, 289 | VType: jaeger.TagType_LONG, 290 | } 291 | case int32: 292 | v := int64(value) 293 | tag = &jaeger.Tag{ 294 | Key: key, 295 | VLong: &v, 296 | VType: jaeger.TagType_LONG, 297 | } 298 | case float64: 299 | v := float64(value) 300 | tag = &jaeger.Tag{ 301 | Key: key, 302 | VDouble: &v, 303 | VType: jaeger.TagType_DOUBLE, 304 | } 305 | } 306 | return tag 307 | } 308 | 309 | // Flush waits for exported trace spans to be uploaded. 310 | // 311 | // This is useful if your program is ending and you do not want to lose recent spans. 312 | func (e *Exporter) Flush() { 313 | e.bundler.Flush() 314 | } 315 | 316 | func (e *Exporter) upload(spans []*jaeger.Span) error { 317 | batch := &jaeger.Batch{ 318 | Spans: spans, 319 | Process: e.process, 320 | } 321 | if e.endpoint != "" { 322 | return e.uploadCollector(batch) 323 | } 324 | return e.uploadAgent(batch) 325 | } 326 | 327 | func (e *Exporter) uploadAgent(batch *jaeger.Batch) error { 328 | return e.client.EmitBatch(batch) 329 | } 330 | 331 | func (e *Exporter) uploadCollector(batch *jaeger.Batch) error { 332 | body, err := serialize(batch) 333 | if err != nil { 334 | return err 335 | } 336 | req, err := http.NewRequest("POST", e.endpoint, body) 337 | if err != nil { 338 | return err 339 | } 340 | if e.username != "" && e.password != "" { 341 | req.SetBasicAuth(e.username, e.password) 342 | } 343 | req.Header.Set("Content-Type", "application/x-thrift") 344 | 345 | resp, err := http.DefaultClient.Do(req) 346 | if err != nil { 347 | return err 348 | } 349 | 350 | io.Copy(ioutil.Discard, resp.Body) 351 | resp.Body.Close() 352 | 353 | if resp.StatusCode < 200 || resp.StatusCode >= 300 { 354 | return fmt.Errorf("failed to upload traces; HTTP status code: %d", resp.StatusCode) 355 | } 356 | return nil 357 | } 358 | 359 | func serialize(obj thrift.TStruct) (*bytes.Buffer, error) { 360 | buf := thrift.NewTMemoryBuffer() 361 | if err := obj.Write(thrift.NewTBinaryProtocolTransport(buf)); err != nil { 362 | return nil, err 363 | } 364 | return buf.Buffer, nil 365 | } 366 | 367 | func bytesToInt64(buf []byte) int64 { 368 | u := binary.BigEndian.Uint64(buf) 369 | return int64(u) 370 | } 371 | -------------------------------------------------------------------------------- /jaeger_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, OpenCensus Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package jaeger 16 | 17 | import ( 18 | "fmt" 19 | "reflect" 20 | "testing" 21 | "time" 22 | 23 | "github.com/uber/jaeger-client-go/thrift-gen/jaeger" 24 | 25 | "go.opencensus.io/trace" 26 | "sort" 27 | ) 28 | 29 | // TODO(jbd): Test export. 30 | 31 | func Test_bytesToInt64(t *testing.T) { 32 | type args struct { 33 | } 34 | tests := []struct { 35 | buf []byte 36 | want int64 37 | }{ 38 | { 39 | buf: []byte{255, 0, 0, 0, 0, 0, 0, 0}, 40 | want: -72057594037927936, 41 | }, 42 | { 43 | buf: []byte{0, 0, 0, 0, 0, 0, 0, 1}, 44 | want: 1, 45 | }, 46 | { 47 | buf: []byte{0, 0, 0, 0, 0, 0, 0, 0}, 48 | want: 0, 49 | }, 50 | } 51 | for _, tt := range tests { 52 | t.Run(fmt.Sprintf("%d", tt.want), func(t *testing.T) { 53 | if got := bytesToInt64(tt.buf); got != tt.want { 54 | t.Errorf("bytesToInt64() = \n%v, \n want \n%v", got, tt.want) 55 | } 56 | }) 57 | } 58 | } 59 | 60 | func Test_spanDataToThrift(t *testing.T) { 61 | now := time.Now() 62 | 63 | answerValue := int64(42) 64 | keyValue := "value" 65 | resultValue := true 66 | statusCodeValue := int64(2) 67 | doubleValue := float64(123.456) 68 | boolTrue := true 69 | statusMessage := "error" 70 | 71 | tests := []struct { 72 | name string 73 | data *trace.SpanData 74 | want *jaeger.Span 75 | }{ 76 | { 77 | name: "no parent", 78 | data: &trace.SpanData{ 79 | SpanContext: trace.SpanContext{ 80 | TraceID: trace.TraceID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, 81 | SpanID: trace.SpanID{1, 2, 3, 4, 5, 6, 7, 8}, 82 | }, 83 | Name: "/foo", 84 | StartTime: now, 85 | EndTime: now, 86 | Attributes: map[string]interface{}{ 87 | "double": doubleValue, 88 | "key": keyValue, 89 | }, 90 | Annotations: []trace.Annotation{ 91 | { 92 | Time: now, 93 | Message: statusMessage, 94 | Attributes: map[string]interface{}{ 95 | "answer": answerValue, 96 | }, 97 | }, 98 | { 99 | Time: now, 100 | Message: statusMessage, 101 | Attributes: map[string]interface{}{ 102 | "result": resultValue, 103 | }, 104 | }, 105 | }, 106 | Status: trace.Status{Code: trace.StatusCodeUnknown, Message: "error"}, 107 | }, 108 | want: &jaeger.Span{ 109 | TraceIdLow: 651345242494996240, 110 | TraceIdHigh: 72623859790382856, 111 | SpanId: 72623859790382856, 112 | OperationName: "/foo", 113 | StartTime: now.UnixNano() / 1000, 114 | Duration: 0, 115 | Tags: []*jaeger.Tag{ 116 | {Key: "double", VType: jaeger.TagType_DOUBLE, VDouble: &doubleValue}, 117 | {Key: "key", VType: jaeger.TagType_STRING, VStr: &keyValue}, 118 | {Key: "error", VType: jaeger.TagType_BOOL, VBool: &boolTrue}, 119 | {Key: "status.code", VType: jaeger.TagType_LONG, VLong: &statusCodeValue}, 120 | {Key: "status.message", VType: jaeger.TagType_STRING, VStr: &statusMessage}, 121 | }, 122 | Logs: []*jaeger.Log{ 123 | {Timestamp: now.UnixNano() / 1000, Fields: []*jaeger.Tag{ 124 | {Key: "answer", VType: jaeger.TagType_LONG, VLong: &answerValue}, 125 | {Key: "message", VType: jaeger.TagType_STRING, VStr: &statusMessage}, 126 | }}, 127 | {Timestamp: now.UnixNano() / 1000, Fields: []*jaeger.Tag{ 128 | {Key: "result", VType: jaeger.TagType_BOOL, VBool: &resultValue}, 129 | {Key: "message", VType: jaeger.TagType_STRING, VStr: &statusMessage}, 130 | }}, 131 | }, 132 | }, 133 | }, 134 | } 135 | for _, tt := range tests { 136 | t.Run(tt.name, func(t *testing.T) { 137 | got := spanDataToThrift(tt.data) 138 | sort.Slice(got.Tags, func(i, j int) bool { 139 | return got.Tags[i].Key < got.Tags[j].Key 140 | }) 141 | sort.Slice(tt.want.Tags, func(i, j int) bool { 142 | return tt.want.Tags[i].Key < tt.want.Tags[j].Key 143 | }) 144 | if !reflect.DeepEqual(got, tt.want) { 145 | t.Errorf("spanDataToThrift()\nGot:\n%v\nWant;\n%v", got, tt.want) 146 | } 147 | }) 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /propagation/http.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019, OpenCensus Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package propagation implement uber-trace-id header propagation used 16 | // by Jaeger. 17 | package propagation 18 | 19 | import ( 20 | "encoding/hex" 21 | "fmt" 22 | "net/http" 23 | "strconv" 24 | "strings" 25 | 26 | "go.opencensus.io/trace" 27 | "go.opencensus.io/trace/propagation" 28 | ) 29 | 30 | const ( 31 | httpHeader = `uber-trace-id` 32 | maxLenHeader = 200 33 | ) 34 | 35 | var _ propagation.HTTPFormat = (*HTTPFormat)(nil) 36 | 37 | // HTTPFormat implements propagation.HTTPFormat to propagate 38 | // traces in HTTP headers for Jaeger traces. 39 | type HTTPFormat struct{} 40 | 41 | // SpanContextFromRequest extracts a Jaeger Trace span context from incoming requests. 42 | func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) { 43 | h := req.Header.Get(httpHeader) 44 | 45 | if h == "" || len(h) > maxLenHeader { 46 | return trace.SpanContext{}, false 47 | } 48 | 49 | // Parse the trace id field. 50 | traceHeaderParts := strings.Split(h, `:`) 51 | if len(traceHeaderParts) != 4 { 52 | return trace.SpanContext{}, false 53 | } 54 | 55 | traceID, err := hex.DecodeString(traceHeaderParts[0]) 56 | if err != nil { 57 | return trace.SpanContext{}, false 58 | } 59 | if len(traceID) == 8 { 60 | copy(sc.TraceID[8:16], traceID) 61 | } else { 62 | copy(sc.TraceID[:], traceID) 63 | } 64 | 65 | spanID, err := hex.DecodeString(traceHeaderParts[1]) 66 | if err != nil { 67 | return trace.SpanContext{}, false 68 | } 69 | copy(sc.SpanID[:], spanID) 70 | 71 | opt, err := strconv.Atoi(traceHeaderParts[3]) 72 | 73 | if err != nil { 74 | return trace.SpanContext{}, false 75 | } 76 | 77 | sc.TraceOptions = trace.TraceOptions(opt) 78 | 79 | return sc, true 80 | } 81 | 82 | // SpanContextToRequest modifies the given request to include a Jaeger Trace header. 83 | func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) { 84 | header := fmt.Sprintf("%s:%s:%s:%d", 85 | strings.Replace(sc.TraceID.String(), "0000000000000000", "", 1), //Replacing 0 if string is 8bit 86 | sc.SpanID.String(), 87 | "", //Parent span deprecated and will therefore be ignored. 88 | int64(sc.TraceOptions)) 89 | req.Header.Set(httpHeader, header) 90 | } 91 | -------------------------------------------------------------------------------- /propagation/http_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019, OpenCensus Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package propagation 16 | 17 | import ( 18 | "net/http" 19 | "reflect" 20 | "testing" 21 | 22 | "go.opencensus.io/trace" 23 | ) 24 | 25 | func TestHTTPFormat(t *testing.T) { 26 | format := &HTTPFormat{} 27 | traceID := [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 66, 179, 103, 245, 105, 105, 242, 156} 28 | traceID2 := [16]byte{66, 179, 103, 245, 105, 105, 242, 156, 66, 179, 103, 245, 105, 105, 242, 156} 29 | spanID1 := [8]byte{104, 185, 184, 89, 243, 185, 19, 51} 30 | spanID2 := [8]byte{67, 211, 230, 84, 180, 39, 182, 139} 31 | tests := []struct { 32 | incoming string 33 | wantSpanContext trace.SpanContext 34 | }{ 35 | { 36 | incoming: "42b367f56969f29c:68b9b859f3b91333::1", 37 | wantSpanContext: trace.SpanContext{ 38 | TraceID: traceID, 39 | SpanID: spanID1, 40 | TraceOptions: 1, 41 | }, 42 | }, 43 | { 44 | incoming: "42b367f56969f29c:43d3e654b427b68b::0", 45 | wantSpanContext: trace.SpanContext{ 46 | TraceID: traceID, 47 | SpanID: spanID2, 48 | TraceOptions: 0, 49 | }, 50 | }, 51 | { 52 | incoming: "42b367f56969f29c42b367f56969f29c:43d3e654b427b68b::0", 53 | wantSpanContext: trace.SpanContext{ 54 | TraceID: traceID2, 55 | SpanID: spanID2, 56 | TraceOptions: 0, 57 | }, 58 | }, 59 | } 60 | for _, tt := range tests { 61 | t.Run(tt.incoming, func(t *testing.T) { 62 | req, _ := http.NewRequest("GET", "http://example.com", nil) 63 | req.Header.Add(httpHeader, tt.incoming) 64 | sc, ok := format.SpanContextFromRequest(req) 65 | if !ok { 66 | t.Errorf("exporter.SpanContextFromRequest() = false; want true") 67 | } 68 | if got, want := sc, tt.wantSpanContext; !reflect.DeepEqual(got, want) { 69 | t.Errorf("exporter.SpanContextFromRequest() returned span context %v; want %v", got, want) 70 | } 71 | 72 | req, _ = http.NewRequest("GET", "http://example.com", nil) 73 | format.SpanContextToRequest(sc, req) 74 | 75 | if got, want := req.Header.Get(httpHeader), tt.incoming; got != want { 76 | t.Errorf("exporter.SpanContextToRequest() returned header %q; want %q", got, want) 77 | } 78 | }) 79 | } 80 | } 81 | --------------------------------------------------------------------------------