From 94a3b8174c919267629d48ef8055cb61529dd339 Mon Sep 17 00:00:00 2001 From: Andrew Wilkins Date: Fri, 11 May 2018 16:45:33 +0800 Subject: [PATCH] vendor: add apm-agent-go, golang.org/x/sync --- NOTICE.txt | 115 +++ .../elastic/apm-agent-go/CONTRIBUTING.md | 78 ++ .../github.com/elastic/apm-agent-go/LICENSE | 13 + .../github.com/elastic/apm-agent-go/Makefile | 41 + .../github.com/elastic/apm-agent-go/README.md | 37 + .../elastic/apm-agent-go/appveyor.yml | 15 + .../elastic/apm-agent-go/capturebody.go | 103 +++ .../elastic/apm-agent-go/codecov.yml | 2 + .../elastic/apm-agent-go/context.go | 181 ++++ vendor/github.com/elastic/apm-agent-go/doc.go | 4 + vendor/github.com/elastic/apm-agent-go/env.go | 184 ++++ .../github.com/elastic/apm-agent-go/error.go | 362 ++++++++ .../elastic/apm-agent-go/gocontext.go | 71 ++ .../github.com/elastic/apm-agent-go/gofuzz.go | 253 ++++++ .../apm-agent-go/internal/apmdebug/debug.go | 45 + .../internal/apmhttputil/forwarded.go | 57 ++ .../internal/apmhttputil/remoteaddr.go | 40 + .../apm-agent-go/internal/apmhttputil/url.go | 91 ++ .../internal/apmstrings/truncate.go | 13 + .../internal/fastjson/LICENSE.easyjson.txt | 7 + .../apm-agent-go/internal/fastjson/doc.go | 10 + .../internal/fastjson/marshaler.go | 100 +++ .../apm-agent-go/internal/fastjson/writer.go | 160 ++++ .../apm-agent-go/internal/krtext/License | 19 + .../apm-agent-go/internal/krtext/Readme | 3 + .../apm-agent-go/internal/krtext/doc.go | 3 + .../apm-agent-go/internal/krtext/indent.go | 74 ++ .../apm-agent-go/internal/krtext/wrap.go | 86 ++ .../apm-agent-go/internal/pretty/License | 21 + .../apm-agent-go/internal/pretty/formatter.go | 328 +++++++ .../apm-agent-go/internal/pretty/pretty.go | 108 +++ .../apm-agent-go/internal/pretty/zero.go | 41 + .../internal/radix/LICENSE.goradix.txt | 20 + .../apm-agent-go/internal/radix/radix.go | 543 ++++++++++++ .../apm-agent-go/internal/uuid/LICENSE | 20 + .../apm-agent-go/internal/uuid/README.md | 74 ++ .../apm-agent-go/internal/uuid/codec.go | 206 +++++ .../apm-agent-go/internal/uuid/generator.go | 265 ++++++ .../apm-agent-go/internal/uuid/uuid.go | 161 ++++ .../github.com/elastic/apm-agent-go/logger.go | 11 + .../elastic/apm-agent-go/model/doc.go | 4 + .../elastic/apm-agent-go/model/gofuzz.go | 78 ++ .../elastic/apm-agent-go/model/maps.go | 32 + .../elastic/apm-agent-go/model/marshal.go | 473 ++++++++++ .../apm-agent-go/model/marshal_fastjson.go | 810 ++++++++++++++++++ .../elastic/apm-agent-go/model/model.go | 493 +++++++++++ .../elastic/apm-agent-go/model/payload.go | 23 + .../apm-agent-go/module/apmhttp/client.go | 73 ++ .../apm-agent-go/module/apmhttp/context.go | 23 + .../apm-agent-go/module/apmhttp/doc.go | 3 + .../apm-agent-go/module/apmhttp/handler.go | 284 ++++++ .../apm-agent-go/module/apmhttp/recovery.go | 39 + .../module/apmhttp/requestname.go | 28 + .../module/apmhttp/requestname_go19.go | 23 + .../elastic/apm-agent-go/sampler.go | 55 ++ .../elastic/apm-agent-go/sanitizer.go | 44 + .../github.com/elastic/apm-agent-go/sender.go | 143 ++++ .../github.com/elastic/apm-agent-go/span.go | 137 +++ .../elastic/apm-agent-go/spancontext.go | 46 + .../elastic/apm-agent-go/stacktrace.go | 35 + .../apm-agent-go/stacktrace/context.go | 83 ++ .../elastic/apm-agent-go/stacktrace/doc.go | 3 + .../elastic/apm-agent-go/stacktrace/frame.go | 17 + .../stacktrace/generate_library.bash | 68 ++ .../apm-agent-go/stacktrace/library.go | 243 ++++++ .../apm-agent-go/stacktrace/stacktrace.go | 139 +++ .../github.com/elastic/apm-agent-go/stats.go | 33 + .../github.com/elastic/apm-agent-go/tracer.go | 560 ++++++++++++ .../elastic/apm-agent-go/transaction.go | 144 ++++ .../elastic/apm-agent-go/transport/api.go | 18 + .../elastic/apm-agent-go/transport/debug.go | 31 + .../elastic/apm-agent-go/transport/default.go | 51 ++ .../elastic/apm-agent-go/transport/discard.go | 19 + .../elastic/apm-agent-go/transport/doc.go | 3 + .../elastic/apm-agent-go/transport/http.go | 245 ++++++ .../github.com/elastic/apm-agent-go/utils.go | 98 +++ .../elastic/apm-agent-go/utils_linux.go | 23 + .../elastic/apm-agent-go/utils_other.go | 8 + .../elastic/apm-agent-go/version.go | 6 + vendor/golang.org/x/sync/LICENSE | 27 + vendor/golang.org/x/sync/PATENTS | 22 + vendor/golang.org/x/sync/errgroup/errgroup.go | 67 ++ vendor/vendor.json | 110 +++ 83 files changed, 8829 insertions(+) create mode 100644 vendor/github.com/elastic/apm-agent-go/CONTRIBUTING.md create mode 100644 vendor/github.com/elastic/apm-agent-go/LICENSE create mode 100644 vendor/github.com/elastic/apm-agent-go/Makefile create mode 100644 vendor/github.com/elastic/apm-agent-go/README.md create mode 100644 vendor/github.com/elastic/apm-agent-go/appveyor.yml create mode 100644 vendor/github.com/elastic/apm-agent-go/capturebody.go create mode 100644 vendor/github.com/elastic/apm-agent-go/codecov.yml create mode 100644 vendor/github.com/elastic/apm-agent-go/context.go create mode 100644 vendor/github.com/elastic/apm-agent-go/doc.go create mode 100644 vendor/github.com/elastic/apm-agent-go/env.go create mode 100644 vendor/github.com/elastic/apm-agent-go/error.go create mode 100644 vendor/github.com/elastic/apm-agent-go/gocontext.go create mode 100644 vendor/github.com/elastic/apm-agent-go/gofuzz.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/apmdebug/debug.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/apmhttputil/forwarded.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/apmhttputil/remoteaddr.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/apmhttputil/url.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/apmstrings/truncate.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/fastjson/LICENSE.easyjson.txt create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/fastjson/doc.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/fastjson/marshaler.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/fastjson/writer.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/krtext/License create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/krtext/Readme create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/krtext/doc.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/krtext/indent.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/krtext/wrap.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/pretty/License create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/pretty/formatter.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/pretty/pretty.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/pretty/zero.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/radix/LICENSE.goradix.txt create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/radix/radix.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/uuid/LICENSE create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/uuid/README.md create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/uuid/codec.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/uuid/generator.go create mode 100644 vendor/github.com/elastic/apm-agent-go/internal/uuid/uuid.go create mode 100644 vendor/github.com/elastic/apm-agent-go/logger.go create mode 100644 vendor/github.com/elastic/apm-agent-go/model/doc.go create mode 100644 vendor/github.com/elastic/apm-agent-go/model/gofuzz.go create mode 100644 vendor/github.com/elastic/apm-agent-go/model/maps.go create mode 100644 vendor/github.com/elastic/apm-agent-go/model/marshal.go create mode 100644 vendor/github.com/elastic/apm-agent-go/model/marshal_fastjson.go create mode 100644 vendor/github.com/elastic/apm-agent-go/model/model.go create mode 100644 vendor/github.com/elastic/apm-agent-go/model/payload.go create mode 100644 vendor/github.com/elastic/apm-agent-go/module/apmhttp/client.go create mode 100644 vendor/github.com/elastic/apm-agent-go/module/apmhttp/context.go create mode 100644 vendor/github.com/elastic/apm-agent-go/module/apmhttp/doc.go create mode 100644 vendor/github.com/elastic/apm-agent-go/module/apmhttp/handler.go create mode 100644 vendor/github.com/elastic/apm-agent-go/module/apmhttp/recovery.go create mode 100644 vendor/github.com/elastic/apm-agent-go/module/apmhttp/requestname.go create mode 100644 vendor/github.com/elastic/apm-agent-go/module/apmhttp/requestname_go19.go create mode 100644 vendor/github.com/elastic/apm-agent-go/sampler.go create mode 100644 vendor/github.com/elastic/apm-agent-go/sanitizer.go create mode 100644 vendor/github.com/elastic/apm-agent-go/sender.go create mode 100644 vendor/github.com/elastic/apm-agent-go/span.go create mode 100644 vendor/github.com/elastic/apm-agent-go/spancontext.go create mode 100644 vendor/github.com/elastic/apm-agent-go/stacktrace.go create mode 100644 vendor/github.com/elastic/apm-agent-go/stacktrace/context.go create mode 100644 vendor/github.com/elastic/apm-agent-go/stacktrace/doc.go create mode 100644 vendor/github.com/elastic/apm-agent-go/stacktrace/frame.go create mode 100644 vendor/github.com/elastic/apm-agent-go/stacktrace/generate_library.bash create mode 100644 vendor/github.com/elastic/apm-agent-go/stacktrace/library.go create mode 100644 vendor/github.com/elastic/apm-agent-go/stacktrace/stacktrace.go create mode 100644 vendor/github.com/elastic/apm-agent-go/stats.go create mode 100644 vendor/github.com/elastic/apm-agent-go/tracer.go create mode 100644 vendor/github.com/elastic/apm-agent-go/transaction.go create mode 100644 vendor/github.com/elastic/apm-agent-go/transport/api.go create mode 100644 vendor/github.com/elastic/apm-agent-go/transport/debug.go create mode 100644 vendor/github.com/elastic/apm-agent-go/transport/default.go create mode 100644 vendor/github.com/elastic/apm-agent-go/transport/discard.go create mode 100644 vendor/github.com/elastic/apm-agent-go/transport/doc.go create mode 100644 vendor/github.com/elastic/apm-agent-go/transport/http.go create mode 100644 vendor/github.com/elastic/apm-agent-go/utils.go create mode 100644 vendor/github.com/elastic/apm-agent-go/utils_linux.go create mode 100644 vendor/github.com/elastic/apm-agent-go/utils_other.go create mode 100644 vendor/github.com/elastic/apm-agent-go/version.go create mode 100644 vendor/golang.org/x/sync/LICENSE create mode 100644 vendor/golang.org/x/sync/PATENTS create mode 100644 vendor/golang.org/x/sync/errgroup/errgroup.go diff --git a/NOTICE.txt b/NOTICE.txt index ef5d040daab..65b82e1b495 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -208,6 +208,87 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------- +Dependency: github.com/elastic/apm-agent-go +Version: v0.4.0 +Revision: a22839728ee5955c96524d56af60813250d26a5b +License type (autodetected): Apache-2.0 +./vendor/github.com/elastic/apm-agent-go/LICENSE: +-------------------------------------------------------------------- +Apache License 2.0 + + +-------------------------------------------------------------------- +Dependency: github.com/elastic/apm-agent-go/internal/fastjson +Version: v0.4.0 +Revision: a22839728ee5955c96524d56af60813250d26a5b +License type (autodetected): MIT +./vendor/github.com/elastic/apm-agent-go/internal/fastjson/LICENSE.easyjson.txt: +-------------------------------------------------------------------- +Copyright (c) 2016 Mail.Ru Group + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------- +Dependency: github.com/elastic/apm-agent-go/internal/radix +Version: v0.4.0 +Revision: a22839728ee5955c96524d56af60813250d26a5b +License type (autodetected): MIT +./vendor/github.com/elastic/apm-agent-go/internal/radix/LICENSE.goradix.txt: +-------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2014 Armon Dadgar + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------- +Dependency: github.com/elastic/apm-agent-go/internal/uuid +Version: v0.4.0 +Revision: a22839728ee5955c96524d56af60813250d26a5b +License type (autodetected): MIT +./vendor/github.com/elastic/apm-agent-go/internal/uuid/LICENSE: +-------------------------------------------------------------------- +Copyright (C) 2013-2018 by Maxim Bublis + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + -------------------------------------------------------------------- Dependency: github.com/elastic/beats Version: master @@ -2140,6 +2221,40 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------- +Dependency: golang.org/x/sync +Revision: 1d60e4601c6fd243af51cc01ddf169918a5407ca +License type (autodetected): BSD-3-Clause +./vendor/golang.org/x/sync/LICENSE: +-------------------------------------------------------------------- +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + -------------------------------------------------------------------- Dependency: golang.org/x/sys Revision: 37707fdb30a5b38865cfb95e5aab41707daec7fd diff --git a/vendor/github.com/elastic/apm-agent-go/CONTRIBUTING.md b/vendor/github.com/elastic/apm-agent-go/CONTRIBUTING.md new file mode 100644 index 00000000000..b6583fafef4 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/CONTRIBUTING.md @@ -0,0 +1,78 @@ +# Contributing to the Go APM Agent + +The Go APM Agent is open source and we love to receive contributions from our community — you! + +There are many ways to contribute, from writing tutorials or blog posts, improving the +documentation, submitting bug reports and feature requests or writing code. + +You can get in touch with us through [Discuss](https://discuss.elastic.co/c/apm). +Feedback and ideas are always welcome. + +## Code contributions + +If you have a bugfix or new feature that involves significant changes that you would like to +contribute, please find or open an issue to discuss the changes first. It may be that somebody +is already working on it, or that there are particular issues that you should know about before +implementing the change. + +For minor changes (e.g. fixing a typo), you can just send your changes. + +### Submitting your changes + +Generally, we require that you test any code you are adding or modifying. Once your changes are +ready to submit for review: + +1. Sign the Contributor License Agreement + + Please make sure you have signed our [Contributor License Agreement](https://www.elastic.co/contributor-agreement/). + We are not asking you to assign copyright to us, but to give us the right to distribute + your code without restriction. We ask this of all contributors in order to assure our + users of the origin and continuing existence of the code. You only need to sign the CLA once. + +2. Test your changes + + Run the test suite to make sure that nothing is broken. + See [testing](#testing) for details. + +3. Review your changes + + Before sending your changes for review, it pays to review it yourself first! + + If you're making significant changes, please familiarize yourself with [Effective Go](https://golang.org/doc/effective_go.html) + and [go/wiki/CodeReviewComments](https://github.com/golang/go/wiki/CodeReviewComments). + These documents will walk you through writing idiomatic Go code, which we strive for. + + Here are a few things to check: + - format the code with [gofmt](https://golang.org/cmd/gofmt/) or [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) + - lint your code using [golint](https://github.com/golang/lint) + - check for common errors using [go vet](https://golang.org/cmd/vet/) + +4. Rebase your changes + + Update your local repository with the most recent code from the main repo, and rebase your + branch on top of the latest master branch. We prefer your initial changes to be squashed + into a single commit. Later, if we ask you to make changes, add them as separate commits. + This makes them easier to review. As a final step before merging we will either ask you to + squash all commits yourself or we'll do it for you. + +5. Submit a pull request + + Push your local changes to your forked copy of the repository and [submit a pull request](https://help.github.com/articles/using-pull-requests). + In the pull request, choose a title which sums up the changes that you have made, and in + the body provide more details about what your changes do, and the reason for making them. + Also mention the number of the issue where discussion has taken place, or issues that are + fixed/closed by the changes, e.g. "Closes #123". + +6. Be patient + + We might not be able to review your code as fast as we would like to, but we'll do our + best to dedicate it the attention it deserves. Your effort is much appreciated! + +### Testing + +The tests currently do not require any external resources, so just run `go test ./...`. +We test with all versions of Go from 1.8 onwards using [Travis CI](https://travis-ci.org). + +We track code coverage. 100% coverage is not a goal, but please do check that your tests +adequately cover the code using `go test -cover`. + diff --git a/vendor/github.com/elastic/apm-agent-go/LICENSE b/vendor/github.com/elastic/apm-agent-go/LICENSE new file mode 100644 index 00000000000..23a4fd9ca71 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/LICENSE @@ -0,0 +1,13 @@ +Copyright 2018 Elasticsearch BV + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/github.com/elastic/apm-agent-go/Makefile b/vendor/github.com/elastic/apm-agent-go/Makefile new file mode 100644 index 00000000000..28724ba9898 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/Makefile @@ -0,0 +1,41 @@ +.PHONY: check +check: precheck test + +.PHONY: precheck +precheck: check-goimports check-lint check-vet + +.PHONY: check-goimports +check-goimports: + sh scripts/check_goimports.sh + +.PHONY: check-lint +check-lint: + golint -set_exit_status ./... + +.PHONY: check-vet +check-vet: + go vet ./... + +.PHONY: install +install: + go get -v -t ./... + +.PHONY: test +test: + go test -v ./... + +coverage.txt: + sh scripts/test_coverage.sh + +.PHONY: clean +clean: + rm -fr coverage.txt docs/html + +.PHONY: docs +docs: +ifdef ELASTIC_DOCS + $(ELASTIC_DOCS)/build_docs.pl --chunk=1 $(BUILD_DOCS_ARGS) --doc docs/index.asciidoc -out docs/html +else + @echo "\nELASTIC_DOCS is not defined.\n" + @exit 1 +endif diff --git a/vendor/github.com/elastic/apm-agent-go/README.md b/vendor/github.com/elastic/apm-agent-go/README.md new file mode 100644 index 00000000000..eb1bd18fa3b --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/README.md @@ -0,0 +1,37 @@ +[![GoDoc](https://godoc.org/github.com/elastic/apm-agent-go?status.svg)](http://godoc.org/github.com/elastic/apm-agent-go) +[![Travis-CI](https://travis-ci.org/elastic/apm-agent-go.svg)](https://travis-ci.org/elastic/apm-agent-go) +[![AppVeyor](https://ci.appveyor.com/api/projects/status/28fhswvqqc7p90f7?svg=true)](https://ci.appveyor.com/project/AndrewWilkins/apm-agent-go) +[![Go Report Card](https://goreportcard.com/badge/github.com/elastic/apm-agent-go)](https://goreportcard.com/report/github.com/elastic/apm-agent-go) +[![codecov.io](https://codecov.io/github/elastic/apm-agent-go/coverage.svg?branch=master)](https://codecov.io/github/elastic/apm-agent-go?branch=master) + +# apm-agent-go: APM Agent for Go (beta) + +This is the official Go package for [Elastic APM](https://www.elastic.co/solutions/apm). + +The Go agent enables you to trace the execution of operations in your application, +sending performance metrics and errors to the Elastic APM server. + +We'd love to hear your feedback, please take a minute to fill out our [survey](https://docs.google.com/forms/d/e/1FAIpQLScbW7D8m-otPO7cxqeg7XstWR8vMnxG6brnXLs_TFVSTHuHvg/viewform?usp=sf_link). + +## Installation + +```bash +go get -u github.com/elastic/apm-agent-go +``` + +## Requirements + +Tested with Go 1.8+ on Linux, Windows and MacOS. + +## License + +Apache 2.0. + +## Documentation + +[Elastic APM Go documentation](./docs/index.asciidoc). + +## Getting Help + +If you find a bug, please [report an issue](https://github.com/elastic/apm-agent-go/issues). +For any other assistance, please open or add to a topic on the [APM discuss forum](https://discuss.elastic.co/c/apm). diff --git a/vendor/github.com/elastic/apm-agent-go/appveyor.yml b/vendor/github.com/elastic/apm-agent-go/appveyor.yml new file mode 100644 index 00000000000..b5935e03262 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/appveyor.yml @@ -0,0 +1,15 @@ +version: build-{build}-{branch} +clone_folder: C:\Go\src\github.com\elastic\apm-agent-go +shallow_clone: true +deploy: off + +environment: + PATH: C:\msys64\mingw64\bin;%PATH% + +build_script: +- cmd: go get -d github.com/elastic/apm-server || true +- cmd: go get -t ./... + +test_script: +- cmd: go test -v ./... + diff --git a/vendor/github.com/elastic/apm-agent-go/capturebody.go b/vendor/github.com/elastic/apm-agent-go/capturebody.go new file mode 100644 index 00000000000..8850d8c2cf1 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/capturebody.go @@ -0,0 +1,103 @@ +package elasticapm + +import ( + "bytes" + "io" + "io/ioutil" + "net/http" + "net/url" + + "github.com/elastic/apm-agent-go/model" +) + +// CaptureBodyMode holds a value indicating how a tracer should capture +// HTTP request bodies: for transactions, for errors, for both, or neither. +type CaptureBodyMode int + +const ( + // CaptureBodyOff disables capturing of HTTP request bodies. This is + // the default mode. + CaptureBodyOff CaptureBodyMode = 0 + + // CaptureBodyErrors captures HTTP request bodies for only errors. + CaptureBodyErrors CaptureBodyMode = 1 + + // CaptureBodyTransactions captures HTTP request bodies for only + // transactions. + CaptureBodyTransactions CaptureBodyMode = 1 << 1 + + // CaptureBodyAll captures HTTP request bodies for both transactions + // and errors. + CaptureBodyAll CaptureBodyMode = CaptureBodyErrors | CaptureBodyTransactions +) + +// CaptureHTTPRequestBody replaces req.Body and returns a possibly nil +// BodyCapturer which can later be passed to Context.SetHTTPRequestBody +// for setting the request body in a transaction or error context. If the +// tracer is not configured to capture HTTP request bodies, then req.Body +// is left alone and nil is returned. +// +// This must be called before the request body is read. +func (t *Tracer) CaptureHTTPRequestBody(req *http.Request) *BodyCapturer { + if req.Body == nil { + return nil + } + t.captureBodyMu.RLock() + captureBody := t.captureBody + t.captureBodyMu.RUnlock() + if captureBody == CaptureBodyOff { + return nil + } + + type readerCloser struct { + io.Reader + io.Closer + } + bc := BodyCapturer{ + captureBody: captureBody, + request: req, + originalBody: req.Body, + } + req.Body = &readerCloser{ + Reader: io.TeeReader(req.Body, &bc.buffer), + Closer: req.Body, + } + return &bc +} + +// BodyCapturer is returned by Tracer.CaptureHTTPRequestBody to later be +// passed to Context.SetHTTPRequestBody. +type BodyCapturer struct { + captureBody CaptureBodyMode + originalBody io.ReadCloser + buffer bytes.Buffer + request *http.Request +} + +func (bc *BodyCapturer) setContext(out *model.RequestBody) bool { + if bc.request.PostForm != nil { + // We must copy the map in case we need to + // sanitize the values. Ideally we should only + // copy if sanitization is necessary, but body + // capture shouldn't typically be enabled so + // we don't currently optimize this. + postForm := make(url.Values, len(bc.request.PostForm)) + for k, v := range bc.request.PostForm { + vcopy := make([]string, len(v)) + copy(vcopy, v) + postForm[k] = vcopy + } + out.Form = postForm + return true + } + + // Read from the buffer and anything remaining in the body. + r := io.MultiReader(bytes.NewReader(bc.buffer.Bytes()), bc.originalBody) + all, err := ioutil.ReadAll(r) + if err != nil { + // TODO(axw) log error? + return false + } + out.Raw = string(all) + return true +} diff --git a/vendor/github.com/elastic/apm-agent-go/codecov.yml b/vendor/github.com/elastic/apm-agent-go/codecov.yml new file mode 100644 index 00000000000..880181831e5 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/codecov.yml @@ -0,0 +1,2 @@ +ignore: + - "internal/uuid" diff --git a/vendor/github.com/elastic/apm-agent-go/context.go b/vendor/github.com/elastic/apm-agent-go/context.go new file mode 100644 index 00000000000..0e9a4dee520 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/context.go @@ -0,0 +1,181 @@ +package elasticapm + +import ( + "fmt" + "net/http" + "strings" + + "github.com/elastic/apm-agent-go/internal/apmhttputil" + "github.com/elastic/apm-agent-go/model" +) + +// Context provides methods for setting transaction and error context. +type Context struct { + model model.Context + request model.Request + requestBody model.RequestBody + requestHeaders model.RequestHeaders + requestSocket model.RequestSocket + response model.Response + responseHeaders model.ResponseHeaders + user model.User + captureBodyMask CaptureBodyMode +} + +func (c *Context) build() *model.Context { + switch { + case c.model.Request != nil: + case c.model.Response != nil: + case c.model.User != nil: + case len(c.model.Custom) != 0: + case len(c.model.Tags) != 0: + default: + return nil + } + return &c.model +} + +func (c *Context) reset() { + modelContext := model.Context{ + // TODO(axw) reuse space for tags + Custom: c.model.Custom[:0], + } + *c = Context{ + model: modelContext, + captureBodyMask: c.captureBodyMask, + } +} + +// SetCustom sets a custom context key/value pair. If the key is invalid +// (contains '.', '*', or '"'), the call is a no-op. The value must be +// JSON-encodable. +// +// If value implements interface{AppendJSON([]byte) []byte}, that will be +// used to encode the value. Otherwise, value will be encoded using +// json.Marshal. As a special case, values of type map[string]interface{} +// will be traversed and values encoded according to the same rules. +func (c *Context) SetCustom(key string, value interface{}) { + if !validTagKey(key) { + return + } + c.model.Custom.Set(key, value) +} + +// SetTag sets a tag in the context. If the key is invalid +// (contains '.', '*', or '"'), the call is a no-op. +func (c *Context) SetTag(key, value string) { + if !validTagKey(key) { + return + } + value = truncateString(value) + if c.model.Tags == nil { + c.model.Tags = map[string]string{key: value} + } else { + c.model.Tags[key] = value + } +} + +// SetHTTPRequest sets details of the HTTP request in the context. +// +// This function may be used for either clients or servers. For +// server-side requests, various proxy forwarding headers are taken +// into account to reconstruct the URL, and determining the client +// address. +// +// If the request URL contains user info, it will be removed and +// excluded from the URL's "full" field. +// +// If the request contains HTTP Basic Authentication, the username +// from that will be recorded in the context. Otherwise, if the +// request contains user info in the URL (i.e. a client-side URL), +// that will be used. +func (c *Context) SetHTTPRequest(req *http.Request) { + // Special cases to avoid calling into fmt.Sprintf in most cases. + var httpVersion string + switch { + case req.ProtoMajor == 1 && req.ProtoMinor == 1: + httpVersion = "1.1" + case req.ProtoMajor == 2 && req.ProtoMinor == 0: + httpVersion = "2.0" + default: + httpVersion = fmt.Sprintf("%d.%d", req.ProtoMajor, req.ProtoMinor) + } + + var forwarded *apmhttputil.ForwardedHeader + if fwd := req.Header.Get("Forwarded"); fwd != "" { + parsed := apmhttputil.ParseForwarded(fwd) + forwarded = &parsed + } + c.request = model.Request{ + Body: c.request.Body, + URL: apmhttputil.RequestURL(req, forwarded), + Method: truncateString(req.Method), + HTTPVersion: httpVersion, + Cookies: req.Cookies(), + } + c.model.Request = &c.request + + c.requestHeaders = model.RequestHeaders{ + ContentType: req.Header.Get("Content-Type"), + Cookie: strings.Join(req.Header["Cookie"], ";"), + UserAgent: req.UserAgent(), + } + if c.requestHeaders != (model.RequestHeaders{}) { + c.request.Headers = &c.requestHeaders + } + + c.requestSocket = model.RequestSocket{ + Encrypted: req.TLS != nil, + RemoteAddress: apmhttputil.RemoteAddr(req, forwarded), + } + if c.requestSocket != (model.RequestSocket{}) { + c.request.Socket = &c.requestSocket + } + + username, _, ok := req.BasicAuth() + if !ok && req.URL.User != nil { + username = req.URL.User.Username() + } + c.user.Username = truncateString(username) + if c.user.Username != "" { + c.model.User = &c.user + } +} + +// SetHTTPRequestBody sets the request body in context given a (possibly nil) +// BodyCapturer returned by Tracer.CaptureHTTPRequestBody. +func (c *Context) SetHTTPRequestBody(bc *BodyCapturer) { + if bc == nil || bc.captureBody&c.captureBodyMask == 0 { + return + } + if bc.setContext(&c.requestBody) { + c.request.Body = &c.requestBody + } +} + +// SetHTTPResponseHeaders sets the HTTP response headers in the context. +func (c *Context) SetHTTPResponseHeaders(h http.Header) { + c.responseHeaders.ContentType = h.Get("Content-Type") + if c.responseHeaders.ContentType != "" { + c.response.Headers = &c.responseHeaders + c.model.Response = &c.response + } +} + +// SetHTTPResponseHeadersSent records whether or not response were sent. +func (c *Context) SetHTTPResponseHeadersSent(headersSent bool) { + c.response.HeadersSent = &headersSent + c.model.Response = &c.response +} + +// SetHTTPResponseFinished records whether or not the response was finished. +func (c *Context) SetHTTPResponseFinished(finished bool) { + c.response.Finished = &finished + c.model.Response = &c.response +} + +// SetHTTPStatusCode records the HTTP response status code. +func (c *Context) SetHTTPStatusCode(statusCode int) { + c.response.StatusCode = statusCode + c.model.Response = &c.response +} diff --git a/vendor/github.com/elastic/apm-agent-go/doc.go b/vendor/github.com/elastic/apm-agent-go/doc.go new file mode 100644 index 00000000000..daf61cc3e77 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/doc.go @@ -0,0 +1,4 @@ +// Package elasticapm provides an API for tracing +// transactions and capturing errors, sending the +// data to Elastic APM. +package elasticapm diff --git a/vendor/github.com/elastic/apm-agent-go/env.go b/vendor/github.com/elastic/apm-agent-go/env.go new file mode 100644 index 00000000000..0759afacc58 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/env.go @@ -0,0 +1,184 @@ +package elasticapm + +import ( + "fmt" + "math/rand" + "os" + "path/filepath" + "regexp" + "runtime" + "strconv" + "strings" + "time" + + "github.com/pkg/errors" +) + +const ( + envFlushInterval = "ELASTIC_APM_FLUSH_INTERVAL" + envMaxQueueSize = "ELASTIC_APM_MAX_QUEUE_SIZE" + envMaxSpans = "ELASTIC_APM_TRANSACTION_MAX_SPANS" + envTransactionSampleRate = "ELASTIC_APM_TRANSACTION_SAMPLE_RATE" + envSanitizeFieldNames = "ELASTIC_APM_SANITIZE_FIELD_NAMES" + envCaptureBody = "ELASTIC_APM_CAPTURE_BODY" + envServiceName = "ELASTIC_APM_SERVICE_NAME" + envServiceVersion = "ELASTIC_APM_SERVICE_VERSION" + envEnvironment = "ELASTIC_APM_ENVIRONMENT" + envSpanFramesMinDuration = "ELASTIC_APM_SPAN_FRAMES_MIN_DURATION" + envActive = "ELASTIC_APM_ACTIVE" + + defaultFlushInterval = 10 * time.Second + defaultMaxTransactionQueueSize = 500 + defaultMaxSpans = 500 + defaultCaptureBody = CaptureBodyOff + defaultSpanFramesMinDuration = 5 * time.Millisecond +) + +var ( + defaultSanitizedFieldNames = regexp.MustCompile(fmt.Sprintf("(?i:%s)", strings.Join([]string{ + "password", + "passwd", + "pwd", + "secret", + ".*key", + ".*token", + ".*session.*", + ".*credit.*", + ".*card.*", + }, "|"))) +) + +func initialFlushInterval() (time.Duration, error) { + value := os.Getenv(envFlushInterval) + if value == "" { + return defaultFlushInterval, nil + } + d, err := time.ParseDuration(value) + if err != nil { + // We allow the value to have no suffix, in which case + // we assume seconds, to be compatible with configuration + // for other Elastic APM agents. + var err2 error + d, err2 = time.ParseDuration(value + "s") + if err2 == nil { + err = nil + } + } + if err != nil { + return 0, errors.Wrapf(err, "failed to parse %s", envFlushInterval) + } + return d, nil +} + +func initialMaxTransactionQueueSize() (int, error) { + value := os.Getenv(envMaxQueueSize) + if value == "" { + return defaultMaxTransactionQueueSize, nil + } + size, err := strconv.Atoi(value) + if err != nil { + return 0, errors.Wrapf(err, "failed to parse %s", envMaxQueueSize) + } + return size, nil +} + +func initialMaxSpans() (int, error) { + value := os.Getenv(envMaxSpans) + if value == "" { + return defaultMaxSpans, nil + } + max, err := strconv.Atoi(value) + if err != nil { + return 0, errors.Wrapf(err, "failed to parse %s", envMaxSpans) + } + return max, nil +} + +// initialSampler returns a nil Sampler if all transactions should be sampled. +func initialSampler() (Sampler, error) { + value := os.Getenv(envTransactionSampleRate) + if value == "" || value == "1.0" { + return nil, nil + } + ratio, err := strconv.ParseFloat(value, 64) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse %s", envTransactionSampleRate) + } + if ratio < 0.0 || ratio > 1.0 { + return nil, errors.Errorf( + "invalid %s value %s: out of range [0,1.0]", + envTransactionSampleRate, value, + ) + } + source := rand.NewSource(time.Now().Unix()) + return NewRatioSampler(ratio, source), nil +} + +func initialSanitizedFieldNamesRegexp() (*regexp.Regexp, error) { + value := os.Getenv(envSanitizeFieldNames) + if value == "" { + return defaultSanitizedFieldNames, nil + } + re, err := regexp.Compile(fmt.Sprintf("(?i:%s)", value)) + if err != nil { + _, err = regexp.Compile(value) + return nil, errors.Wrapf(err, "invalid %s value", envSanitizeFieldNames) + } + return re, nil +} + +func initialCaptureBody() (CaptureBodyMode, error) { + value := os.Getenv(envCaptureBody) + if value == "" { + return defaultCaptureBody, nil + } + switch strings.TrimSpace(strings.ToLower(value)) { + case "all": + return CaptureBodyAll, nil + case "errors": + return CaptureBodyErrors, nil + case "transactions": + return CaptureBodyTransactions, nil + case "off": + return CaptureBodyOff, nil + } + return -1, errors.Errorf("invalid %s value %q", envCaptureBody, value) +} + +func initialService() (name, version, environment string) { + name = os.Getenv(envServiceName) + version = os.Getenv(envServiceVersion) + environment = os.Getenv(envEnvironment) + if name == "" { + name = filepath.Base(os.Args[0]) + if runtime.GOOS == "windows" { + name = strings.TrimSuffix(name, filepath.Ext(name)) + } + } + name = sanitizeServiceName(name) + return name, version, environment +} + +func initialSpanFramesMinDuration() (time.Duration, error) { + value := os.Getenv(envSpanFramesMinDuration) + if value == "" { + return defaultSpanFramesMinDuration, nil + } + d, err := time.ParseDuration(value) + if err != nil { + return 0, errors.Wrapf(err, "failed to parse %s", envSpanFramesMinDuration) + } + return d, nil +} + +func initialActive() (bool, error) { + value := os.Getenv(envActive) + if value == "" { + return true, nil + } + active, err := strconv.ParseBool(value) + if err != nil { + return false, errors.Wrapf(err, "failed to parse %s", envActive) + } + return active, nil +} diff --git a/vendor/github.com/elastic/apm-agent-go/error.go b/vendor/github.com/elastic/apm-agent-go/error.go new file mode 100644 index 00000000000..5eb53265a6c --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/error.go @@ -0,0 +1,362 @@ +package elasticapm + +import ( + "fmt" + "net" + "os" + "reflect" + "syscall" + "time" + + "github.com/pkg/errors" + + "github.com/elastic/apm-agent-go/internal/uuid" + "github.com/elastic/apm-agent-go/model" + "github.com/elastic/apm-agent-go/stacktrace" +) + +// Recover recovers panics, sending them as errors to +// the Elastic APM server. Recover is expected to be +// used in a deferred call. +// +// Recover simply calls t.Recovered(v, tx), +// where v is the recovered value, and then calls the +// resulting Error's Send method. +func (t *Tracer) Recover(tx *Transaction) { + v := recover() + if v == nil { + return + } + t.Recovered(v, tx).Send() +} + +// Recovered creates an Error with t.NewError(err), where +// err is either v (if v implements error), or otherwise +// fmt.Errorf("%v", v). The value v is expected to have +// come from a panic. +// +// The resulting error's Transaction will be set to tx, +func (t *Tracer) Recovered(v interface{}, tx *Transaction) *Error { + var e *Error + switch v := v.(type) { + case error: + e = t.NewError(v) + default: + e = t.NewError(fmt.Errorf("%v", v)) + } + e.Transaction = tx + return e +} + +// NewError returns a new Error with details taken from err. +// NewError will panic if called with a nil error. +// +// The exception message will be set to err.Error(). +// The exception module and type will be set to the package +// and type name of the cause of the error, respectively, +// where the cause has the same definition as given by +// github.com/pkg/errors. +// +// If err implements +// type interface { +// StackTrace() github.com/pkg/errors.StackTrace +// } +// or +// type interface { +// StackTrace() []stacktrace.Frame +// } +// then one of those will be used to set the error +// stacktrace. Otherwise, NewError will take a stacktrace. +// +// If err implements +// type interface {Type() string} +// then that will be used to set the error type. +// +// If err implements +// type interface {Code() string} +// or +// type interface {Code() float64} +// then one of those will be used to set the error code. +func (t *Tracer) NewError(err error) *Error { + if err == nil { + panic("NewError must be called with a non-nil error") + } + e := t.newError() + if uuid, err := uuid.NewV4(); err == nil { + e.ID = uuid.String() + } + e.model.Exception.Message = err.Error() + if e.model.Exception.Message == "" { + e.model.Exception.Message = "[EMPTY]" + } + initException(&e.model.Exception, errors.Cause(err)) + initStacktrace(e, err) + if e.stacktrace == nil { + e.SetStacktrace(2) + } + return e +} + +// NewErrorLog returns a new Error for the given ErrorLogRecord. +// +// The resulting Error's stacktrace will not be set. Call the +// SetStacktrace method to set it, if desired. +// +// If r.Message is empty, "[EMPTY]" will be used. +func (t *Tracer) NewErrorLog(r ErrorLogRecord) *Error { + e := t.newError() + e.model.Log = model.Log{ + Message: r.Message, + Level: truncateString(r.Level), + LoggerName: truncateString(r.LoggerName), + ParamMessage: truncateString(r.MessageFormat), + } + if e.model.Log.Message == "" { + e.model.Log.Message = "[EMPTY]" + } + return e +} + +// newError returns a new Error associated with the Tracer. +func (t *Tracer) newError() *Error { + e, _ := t.errorPool.Get().(*Error) + if e == nil { + e = &Error{ + tracer: t, + Context: Context{ + captureBodyMask: CaptureBodyErrors, + }, + } + } + e.Timestamp = time.Now() + return e +} + +// Error describes an error occurring in the monitored service. +type Error struct { + model model.Error + tracer *Tracer + stacktrace []stacktrace.Frame + modelStacktrace []model.StacktraceFrame + + // ID is the unique ID of the error. This is set by NewError, + // and can be used for correlating errors and logs records. + ID string + + // Culprit is the name of the function that caused the error. + // + // This is initially unset; if it remains unset by the time + // Send is invoked, and the error has a stacktrace, the first + // non-library frame in the stacktrace will be considered the + // culprit. + Culprit string + + // Transaction is the transaction to which the error correspoonds, + // if any. If this is set, the error's Send method must be called + // before the transaction's End method. + Transaction *Transaction + + // Timestamp records the time at which the error occurred. + // This is set when the Error object is created, but may + // be overridden any time before the Send method is called. + Timestamp time.Time + + // Handled records whether or not the error was handled. This + // only applies to "exception" errors (errors created with + // NewError, or Recovered), and is ignored by "log" errors. + Handled bool + + // Context holds the context for this error. + Context Context +} + +func (e *Error) reset() { + *e = Error{ + tracer: e.tracer, + stacktrace: e.stacktrace[:0], + modelStacktrace: e.modelStacktrace[:0], + Context: e.Context, + } + e.Context.reset() +} + +// Send enqueues the error for sending to the Elastic APM server. +// The Error must not be used after this. +func (e *Error) Send() { + select { + case e.tracer.errors <- e: + default: + // Enqueuing an error should never block. + e.tracer.statsMu.Lock() + e.tracer.stats.ErrorsDropped++ + e.tracer.statsMu.Unlock() + e.reset() + e.tracer.errorPool.Put(e) + } +} + +func (e *Error) setStacktrace() { + if len(e.stacktrace) == 0 { + return + } + e.modelStacktrace = appendModelStacktraceFrames(e.modelStacktrace, e.stacktrace) + e.model.Log.Stacktrace = e.modelStacktrace + e.model.Exception.Stacktrace = e.modelStacktrace +} + +func (e *Error) setCulprit() { + if e.Culprit != "" { + e.model.Culprit = e.Culprit + } else if e.modelStacktrace != nil { + e.model.Culprit = stacktraceCulprit(e.modelStacktrace) + } +} + +// stacktraceCulprit returns the first non-library stacktrace frame's +// function name. +func stacktraceCulprit(frames []model.StacktraceFrame) string { + for _, frame := range frames { + if !frame.LibraryFrame { + return frame.Function + } + } + return "" +} + +func initException(e *model.Exception, err error) { + setAttr := func(k string, v interface{}) { + if e.Attributes == nil { + e.Attributes = make(map[string]interface{}) + } + e.Attributes[k] = v + } + + // Set Module, Type, Attributes, and Code. + switch err := err.(type) { + case *net.OpError: + e.Module, e.Type = "net", "OpError" + setAttr("op", err.Op) + setAttr("net", err.Net) + setAttr("source", err.Source) + setAttr("addr", err.Addr) + case *os.LinkError: + e.Module, e.Type = "os", "LinkError" + setAttr("op", err.Op) + setAttr("old", err.Old) + setAttr("new", err.New) + case *os.PathError: + e.Module, e.Type = "os", "PathError" + setAttr("op", err.Op) + setAttr("path", err.Path) + case *os.SyscallError: + e.Module, e.Type = "os", "SyscallError" + setAttr("syscall", err.Syscall) + case syscall.Errno: + e.Module, e.Type = "syscall", "Errno" + e.Code.Number = float64(uintptr(err)) + default: + t := reflect.TypeOf(err) + if t.Name() == "" && t.Kind() == reflect.Ptr { + t = t.Elem() + } + e.Module, e.Type = t.PkgPath(), t.Name() + + // If the error implements Type, use that to + // override the type name determined through + // reflection. + if err, ok := err.(interface { + Type() string + }); ok { + e.Type = err.Type() + } + + // If the error implements a Code method, use + // that to set the exception code. + switch err := err.(type) { + case interface { + Code() string + }: + e.Code.String = err.Code() + case interface { + Code() float64 + }: + e.Code.Number = err.Code() + } + } + if errTemporary(err) { + setAttr("temporary", true) + } + if errTimeout(err) { + setAttr("timeout", true) + } + e.Code.String = truncateString(e.Code.String) + e.Type = truncateString(e.Type) +} + +func initStacktrace(e *Error, err error) { + type internalStackTracer interface { + StackTrace() []stacktrace.Frame + } + type errorsStackTracer interface { + StackTrace() errors.StackTrace + } + switch stackTracer := err.(type) { + case internalStackTracer: + e.stacktrace = append(e.stacktrace[:0], stackTracer.StackTrace()...) + case errorsStackTracer: + stackTrace := stackTracer.StackTrace() + pc := make([]uintptr, len(stackTrace)) + for i, frame := range stackTrace { + pc[i] = uintptr(frame) + } + e.stacktrace = stacktrace.AppendCallerFrames(e.stacktrace[:0], pc) + } +} + +// SetStacktrace sets the stacktrace for the error, +// skipping the first skip number of frames, excluding +// the SetStacktrace function. +func (e *Error) SetStacktrace(skip int) { + e.stacktrace = stacktrace.AppendStacktrace(e.stacktrace[:0], skip+1, -1) +} + +func errTemporary(err error) bool { + type temporaryError interface { + Temporary() bool + } + terr, ok := err.(temporaryError) + return ok && terr.Temporary() +} + +func errTimeout(err error) bool { + type timeoutError interface { + Timeout() bool + } + terr, ok := err.(timeoutError) + return ok && terr.Timeout() +} + +// ErrorLogRecord holds details of an error log record. +type ErrorLogRecord struct { + // Message holds the message for the log record, + // e.g. "failed to connect to %s". + // + // If this is empty, "[EMPTY]" will be used. + Message string + + // MessageFormat holds the non-interpolated format + // of the log record, e.g. "failed to connect to %s". + // + // This is optional. + MessageFormat string + + // Level holds the severity level of the log record. + // + // This is optional. + Level string + + // LoggerName holds the name of the logger used. + // + // This is optional. + LoggerName string +} diff --git a/vendor/github.com/elastic/apm-agent-go/gocontext.go b/vendor/github.com/elastic/apm-agent-go/gocontext.go new file mode 100644 index 00000000000..5d4d174337a --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/gocontext.go @@ -0,0 +1,71 @@ +package elasticapm + +import "context" + +// ContextWithSpan returns a copy of parent in which the given span +// is stored, associated with the key ContextSpanKey. +func ContextWithSpan(parent context.Context, s *Span) context.Context { + return context.WithValue(parent, contextSpanKey{}, s) +} + +// ContextWithTransaction returns a copy of parent in which the given +// transaction is stored, associated with the key ContextTransactionKey. +func ContextWithTransaction(parent context.Context, t *Transaction) context.Context { + return context.WithValue(parent, contextTransactionKey{}, t) +} + +// SpanFromContext returns the current Span in context, if any. The span must +// have been added to the context previously using either ContextWithSpan +// or SetSpanInContext. +func SpanFromContext(ctx context.Context) *Span { + span, _ := ctx.Value(contextSpanKey{}).(*Span) + return span +} + +// TransactionFromContext returns the current Transaction in context, if any. +// The transaction must have been added to the context previously using either +// ContextWithTransaction or SetTransactionInContext. +func TransactionFromContext(ctx context.Context) *Transaction { + tx, _ := ctx.Value(contextTransactionKey{}).(*Transaction) + return tx +} + +// StartSpan starts and returns a new Span within the sampled transaction +// and parent span in the context, if any. If the span isn't dropped, it +// will be stored in the resulting context. +// +// StartSpan always returns a non-nil Span. Its End method must be called +// when the span completes. +func StartSpan(ctx context.Context, name, spanType string) (*Span, context.Context) { + tx := TransactionFromContext(ctx) + span := tx.StartSpan(name, spanType, SpanFromContext(ctx)) + if !span.Dropped() { + ctx = context.WithValue(ctx, contextSpanKey{}, span) + } + return span, ctx +} + +// CaptureError returns a new Error related to the sampled transaction +// present in the context, if any, and calls its SetException method +// with the given error. The Error.Handled field will be set to true, +// and a stacktrace set. +// +// If there is no transaction in the context, or it is not being sampled, +// CaptureError returns nil. As a convenience, if the provided error is +// nil, then CaptureError will also return nil. +func CaptureError(ctx context.Context, err error) *Error { + if err == nil { + return nil + } + tx := TransactionFromContext(ctx) + if tx == nil || !tx.Sampled() { + return nil + } + e := tx.tracer.NewError(err) + e.Handled = true + e.Transaction = tx + return e +} + +type contextSpanKey struct{} +type contextTransactionKey struct{} diff --git a/vendor/github.com/elastic/apm-agent-go/gofuzz.go b/vendor/github.com/elastic/apm-agent-go/gofuzz.go new file mode 100644 index 00000000000..3b944cf8e5f --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/gofuzz.go @@ -0,0 +1,253 @@ +// +build gofuzz + +package elasticapm + +import ( + "context" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "github.com/pkg/errors" + + "github.com/elastic/apm-agent-go/internal/fastjson" + "github.com/elastic/apm-agent-go/model" + "github.com/elastic/apm-agent-go/stacktrace" + "github.com/elastic/apm-server/processor" + error_processor "github.com/elastic/apm-server/processor/error" + transaction_processor "github.com/elastic/apm-server/processor/transaction" +) + +func Fuzz(data []byte) int { + type Payload struct { + Service *model.Service `json:"service"` + Process *model.Process `json:"process,omitempty"` + System *model.System `json:"system,omitempty"` + Errors []*model.Error `json:"errors"` + Transactions []*model.Transaction `json:"transactions"` + } + var payload Payload + if err := json.Unmarshal(data, &payload); err != nil { + return 0 + } + + tracer := DefaultTracer + tracer.Transport = &gofuzzTransport{} + tracer.SetCaptureBody(CaptureBodyAll) + + setContext := func(in *model.Context, out *Context) error { + if in == nil { + return nil + } + for _, item := range in.Custom { + out.SetCustom(item.Key, item.Value) + } + for k, v := range in.Tags { + out.SetTag(k, v) + } + if in.Request != nil { + var body io.Reader + var postForm url.Values + if in.Request.Body != nil { + body = strings.NewReader(in.Request.Body.Raw) + if in.Request.Body.Form != nil { + postForm = in.Request.Body.Form + } + } + req, err := http.NewRequest(in.Request.Method, in.Request.URL.Full, body) + if err != nil { + return err + } + capturedBody := tracer.CaptureHTTPRequestBody(req) + if in.Request.Socket != nil { + req.RemoteAddr = in.Request.Socket.RemoteAddress + if in.Request.Socket.Encrypted { + req.TLS = new(tls.ConnectionState) + } + } + req.PostForm = postForm + if in.User != nil && in.User.Username != "" { + req.SetBasicAuth(in.User.Username, "") + } + + var major, minor int + if n, err := fmt.Sscanf(in.Request.HTTPVersion, "%d.%d", &major, &minor); err != nil { + return err + } else if n != 2 { + return errors.Errorf("invalid HTTP version %s", in.Request.HTTPVersion) + } + req.ProtoMajor = major + req.ProtoMinor = minor + + if in.Request.Headers != nil { + if in.Request.Headers.UserAgent != "" { + req.Header.Set("User-Agent", in.Request.Headers.UserAgent) + } + if in.Request.Headers.ContentType != "" { + req.Header.Set("Content-Type", in.Request.Headers.ContentType) + } + if in.Request.Headers.Cookie != "" { + for _, v := range strings.Split(in.Request.Headers.Cookie, ";") { + req.Header.Add("Cookie", v) + } + } + } + + out.SetHTTPRequest(req) + out.SetHTTPRequestBody(capturedBody) + } + if in.Response != nil { + out.SetHTTPStatusCode(in.Response.StatusCode) + if in.Response.Finished != nil { + out.SetHTTPResponseFinished(*in.Response.Finished) + } + if in.Response.HeadersSent != nil { + out.SetHTTPResponseHeadersSent(*in.Response.HeadersSent) + } + if in.Response.Headers != nil { + h := make(http.Header) + h.Set("Content-Type", in.Response.Headers.ContentType) + out.SetHTTPResponseHeaders(h) + } + } + return nil + } + + for _, t := range payload.Transactions { + if t == nil { + continue + } + tx := tracer.StartTransaction(t.Name, t.Type) + tx.Result = t.Result + tx.Timestamp = time.Time(t.Timestamp) + if setContext(t.Context, &tx.Context) != nil { + return 0 + } + for _, s := range t.Spans { + if s == nil { + continue + } + span := tx.StartSpan(s.Name, s.Type, nil) + span.Timestamp = tx.Timestamp.Add(time.Duration(s.Start * float64(time.Millisecond))) + if s.Context != nil && s.Context.Database != nil { + span.Context.SetDatabase(DatabaseSpanContext{ + Instance: s.Context.Database.Instance, + Statement: s.Context.Database.Statement, + Type: s.Context.Database.Type, + User: s.Context.Database.User, + }) + } + span.Duration = time.Duration(s.Duration * float64(time.Millisecond)) + span.End() + } + tx.Duration = time.Duration(t.Duration * float64(time.Millisecond)) + tx.End() + } + + for _, e := range payload.Errors { + if e == nil { + continue + } + var err *Error + if e.Log.Message != "" { + err = tracer.NewErrorLog(ErrorLogRecord{ + Message: e.Log.Message, + MessageFormat: e.Log.ParamMessage, + Level: e.Log.Level, + LoggerName: e.Log.LoggerName, + }) + } else { + ee := exceptionError{e.Exception} + if e.Exception.Code.String != "" { + err = tracer.NewError(stringCodeException{ee}) + } else { + err = tracer.NewError(float64CodeException{ee}) + } + } + if setContext(e.Context, &err.Context) != nil { + return 0 + } + err.Culprit = e.Culprit + err.Timestamp = time.Time(e.Timestamp) + err.Send() + } + + return 0 +} + +type float64CodeException struct { + exceptionError +} + +func (e float64CodeException) Code() float64 { + return e.x.Code.Number +} + +type stringCodeException struct { + exceptionError +} + +func (e stringCodeException) Code() string { + return e.x.Code.String +} + +type exceptionError struct { + x model.Exception +} + +func (e exceptionError) Type() string { + return e.x.Type +} + +func (e exceptionError) Error() string { + return e.x.Message +} + +func (e exceptionError) StackTrace() []stacktrace.Frame { + if len(e.x.Stacktrace) == 0 { + return nil + } + frames := make([]stacktrace.Frame, len(e.x.Stacktrace)) + for i, f := range e.x.Stacktrace { + frames[i].Function = f.Function + frames[i].File = f.File + frames[i].Line = f.Line + } + return frames +} + +type gofuzzTransport struct { + writer fastjson.Writer +} + +func (t *gofuzzTransport) SendErrors(ctx context.Context, payload *model.ErrorsPayload) error { + t.writer.Reset() + payload.MarshalFastJSON(&t.writer) + t.process(error_processor.NewProcessor()) + return nil +} + +func (t *gofuzzTransport) SendTransactions(ctx context.Context, payload *model.TransactionsPayload) error { + t.writer.Reset() + payload.MarshalFastJSON(&t.writer) + t.process(transaction_processor.NewProcessor()) + return nil +} + +func (t *gofuzzTransport) process(p processor.Processor) { + raw := make(map[string]interface{}) + if err := json.Unmarshal(t.writer.Bytes(), &raw); err != nil { + panic(err) + } + if err := p.Validate(raw); err != nil { + panic(err) + } + if _, err := p.Decode(raw); err != nil { + panic(err) + } +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/apmdebug/debug.go b/vendor/github.com/elastic/apm-agent-go/internal/apmdebug/debug.go new file mode 100644 index 00000000000..a11cda106a6 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/apmdebug/debug.go @@ -0,0 +1,45 @@ +package apmdebug + +import ( + "log" + "os" + "strings" +) + +var ( + // TraceTransport reports whether or not the transport methods + // should be traced. If true, messages will be sent to stderr + // for each SendTransactions and SendErrors call, proceeded by + // the results. + TraceTransport bool +) + +func init() { + v := os.Getenv("ELASTIC_APM_DEBUG") + if v == "" { + return + } + for _, field := range strings.Split(v, ",") { + pos := strings.IndexRune(field, '=') + if pos == -1 { + invalidField(field) + continue + } + k, _ := field[:pos], field[pos+1:] + switch k { + case "tracetransport": + TraceTransport = true + default: + unknownKey(k) + continue + } + } +} + +func unknownKey(key string) { + log.Println("unknown ELASTIC_APM_DEBUG field:", key) +} + +func invalidField(field string) { + log.Println("invalid ELASTIC_APM_DEBUG field:", field) +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/apmhttputil/forwarded.go b/vendor/github.com/elastic/apm-agent-go/internal/apmhttputil/forwarded.go new file mode 100644 index 00000000000..f3539e4b6eb --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/apmhttputil/forwarded.go @@ -0,0 +1,57 @@ +package apmhttputil + +import ( + "strconv" + "strings" +) + +// ForwardedHeader holds information extracted from a "Forwarded" HTTP header. +type ForwardedHeader struct { + For string + Host string + Proto string +} + +// ParseForwarded parses a "Forwarded" HTTP header. +func ParseForwarded(f string) ForwardedHeader { + // We only consider the first value in the sequence, + // if there are multiple. Disregard everything after + // the first comma. + if comma := strings.IndexRune(f, ','); comma != -1 { + f = f[:comma] + } + var result ForwardedHeader + for f != "" { + field := f + if semi := strings.IndexRune(f, ';'); semi != -1 { + field = f[:semi] + f = f[semi+1:] + } else { + f = "" + } + eq := strings.IndexRune(field, '=') + if eq == -1 { + // Malformed field, ignore. + continue + } + key := strings.TrimSpace(field[:eq]) + value := strings.TrimSpace(field[eq+1:]) + if len(value) > 0 && value[0] == '"' { + var err error + value, err = strconv.Unquote(value) + if err != nil { + // Malformed, ignore + continue + } + } + switch strings.ToLower(key) { + case "for": + result.For = value + case "host": + result.Host = value + case "proto": + result.Proto = value + } + } + return result +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/apmhttputil/remoteaddr.go b/vendor/github.com/elastic/apm-agent-go/internal/apmhttputil/remoteaddr.go new file mode 100644 index 00000000000..ca0ebfbd7be --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/apmhttputil/remoteaddr.go @@ -0,0 +1,40 @@ +package apmhttputil + +import ( + "net" + "net/http" + "strings" +) + +// RemoteAddr returns the remote address for the HTTP request. +// +// In order: +// - if the Forwarded header is set, then the first item in the +// list's "for" field is used, if it exists. The "for" value +// is returned even if it is an obfuscated identifier. +// - if the X-Real-Ip header is set, then its value is returned. +// - if the X-Forwarded-For header is set, then the first value +// in the comma-separated list is returned. +// - otherwise, the host portion of req.RemoteAddr is returned. +func RemoteAddr(req *http.Request, forwarded *ForwardedHeader) string { + if forwarded != nil { + if forwarded.For != "" { + remoteAddr, _, err := net.SplitHostPort(forwarded.For) + if err != nil { + remoteAddr = forwarded.For + } + return remoteAddr + } + } + if realIP := req.Header.Get("X-Real-Ip"); realIP != "" { + return realIP + } + if xff := req.Header.Get("X-Forwarded-For"); xff != "" { + if sep := strings.IndexRune(xff, ','); sep > 0 { + xff = xff[:sep] + } + return strings.TrimSpace(xff) + } + remoteAddr, _ := splitHost(req.RemoteAddr) + return remoteAddr +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/apmhttputil/url.go b/vendor/github.com/elastic/apm-agent-go/internal/apmhttputil/url.go new file mode 100644 index 00000000000..3af127ec1ee --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/apmhttputil/url.go @@ -0,0 +1,91 @@ +package apmhttputil + +import ( + "net" + "net/http" + "strings" + + "github.com/elastic/apm-agent-go/internal/apmstrings" + "github.com/elastic/apm-agent-go/model" +) + +// RequestURL returns a model.URL for req. +// +// If req contains an absolute URI, the values will be split and +// sanitized, but no further processing performed. For all other +// requests (i.e. most server-side requests), we reconstruct the +// URL based on various proxy forwarding headers and other request +// attributes. +func RequestURL(req *http.Request, forwarded *ForwardedHeader) model.URL { + out := model.URL{ + Path: truncateString(req.URL.Path), + Search: truncateString(req.URL.RawQuery), + Hash: truncateString(req.URL.Fragment), + } + if req.URL.Host != "" { + // Absolute URI: client-side or proxy request, so ignore the + // headers. + hostname, port := splitHost(req.URL.Host) + out.Hostname = truncateString(hostname) + out.Port = truncateString(port) + out.Protocol = truncateString(req.URL.Scheme) + return out + } + + // This is a server-side request URI, which contains only the path. + // We synthesize the full URL by extracting the host and protocol + // from headers, or inferring from other properties. + var fullHost string + if forwarded != nil && forwarded.Host != "" { + fullHost = forwarded.Host + out.Protocol = truncateString(forwarded.Proto) + } else if xfh := req.Header.Get("X-Forwarded-Host"); xfh != "" { + fullHost = xfh + } else { + fullHost = req.Host + } + hostname, port := splitHost(fullHost) + out.Hostname = truncateString(hostname) + out.Port = truncateString(port) + + // Protocol might be extracted from the Forwarded header. If it's not, + // look for various other headers. + if out.Protocol == "" { + if proto := req.Header.Get("X-Forwarded-Proto"); proto != "" { + out.Protocol = truncateString(proto) + } else if proto := req.Header.Get("X-Forwarded-Protocol"); proto != "" { + out.Protocol = truncateString(proto) + } else if proto := req.Header.Get("X-Url-Scheme"); proto != "" { + out.Protocol = truncateString(proto) + } else if req.Header.Get("Front-End-Https") == "on" { + out.Protocol = "https" + } else if req.Header.Get("X-Forwarded-Ssl") == "on" { + out.Protocol = "https" + } else if req.TLS != nil { + out.Protocol = "https" + } else { + // Assume http otherwise. + out.Protocol = "http" + } + } + return out +} + +func splitHost(in string) (host, port string) { + if strings.LastIndexByte(in, ':') == -1 { + // In the common (relative to other "errors") case that + // there is no colon, we can avoid allocations by not + // calling SplitHostPort. + return in, "" + } + host, port, err := net.SplitHostPort(in) + if err != nil { + return in, "" + } + return host, port +} + +func truncateString(s string) string { + // At the time of writing, all length limits are 1024. + return apmstrings.Truncate(s, 1024) +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/apmstrings/truncate.go b/vendor/github.com/elastic/apm-agent-go/internal/apmstrings/truncate.go new file mode 100644 index 00000000000..be1955d1591 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/apmstrings/truncate.go @@ -0,0 +1,13 @@ +package apmstrings + +// Truncate returns s truncated at n runes. +func Truncate(s string, n int) string { + var j int + for i := range s { + if j == n { + return s[:i] + } + j++ + } + return s +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/fastjson/LICENSE.easyjson.txt b/vendor/github.com/elastic/apm-agent-go/internal/fastjson/LICENSE.easyjson.txt new file mode 100644 index 00000000000..fbff658f70d --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/fastjson/LICENSE.easyjson.txt @@ -0,0 +1,7 @@ +Copyright (c) 2016 Mail.Ru Group + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/elastic/apm-agent-go/internal/fastjson/doc.go b/vendor/github.com/elastic/apm-agent-go/internal/fastjson/doc.go new file mode 100644 index 00000000000..4dff597607c --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/fastjson/doc.go @@ -0,0 +1,10 @@ +// Package fastjson provides a code generator and library for +// fast JSON encoding, limited to what apm-agent-go requires. +// +// fastjson reuses a small amount of easyjson's code for +// marshalling strings. Whereas easyjson relies on pooled +// memory, fastjson leaves memory allocation to the user. +// For apm-agent-go, we always encode from a single goroutine, +// so we can reuse a single buffer, and so we can almost +// always avoid allocations. +package fastjson diff --git a/vendor/github.com/elastic/apm-agent-go/internal/fastjson/marshaler.go b/vendor/github.com/elastic/apm-agent-go/internal/fastjson/marshaler.go new file mode 100644 index 00000000000..22a1f7e91ba --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/fastjson/marshaler.go @@ -0,0 +1,100 @@ +package fastjson + +import ( + "encoding/json" + "fmt" +) + +// Marshaler defines an interface that types can implement to provide +// fast JSON marshaling. +type Marshaler interface { + // MarshalFastJSON writes a JSON representation of the type to w. + MarshalFastJSON(w *Writer) +} + +// Appender defines an interface that types can implement to append +// their JSON representation to a buffer. If the value is not a valid +// JSON token, it will be rejected. +type Appender interface { + // AppendJSON appends the JSON representation of the value to the + // buffer, and returns the extended buffer. + AppendJSON([]byte) []byte +} + +// Marshal marshals v as JSON to w. +func Marshal(w *Writer, v interface{}) { + switch v := v.(type) { + case nil: + w.RawString("null") + case string: + w.String(v) + case uint: + w.Uint64(uint64(v)) + case uint8: + w.Uint64(uint64(v)) + case uint16: + w.Uint64(uint64(v)) + case uint32: + w.Uint64(uint64(v)) + case uint64: + w.Uint64(v) + case int: + w.Int64(int64(v)) + case int8: + w.Int64(int64(v)) + case int16: + w.Int64(int64(v)) + case int32: + w.Int64(int64(v)) + case int64: + w.Int64(v) + case float32: + w.Float32(v) + case float64: + w.Float64(v) + case bool: + w.Bool(v) + case map[string]interface{}: + if v == nil { + w.RawString("null") + return + } + w.RawByte('{') + first := true + for k, v := range v { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(k) + w.RawByte(':') + Marshal(w, v) + } + w.RawByte('}') + case Marshaler: + v.MarshalFastJSON(w) + case Appender: + w.buf = v.AppendJSON(w.buf) + default: + marshalReflect(w, v) + } +} + +func marshalReflect(w *Writer, v interface{}) { + defer func() { + if r := recover(); r != nil { + w.RawString(`{"__PANIC__":`) + w.String(fmt.Sprintf("panic calling MarshalJSON for type %T: %v", v, r)) + w.RawByte('}') + } + }() + raw, err := json.Marshal(v) + if err != nil { + w.RawString(`{"__ERROR__":`) + w.String(fmt.Sprint(err)) + w.RawByte('}') + return + } + w.RawBytes(raw) +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/fastjson/writer.go b/vendor/github.com/elastic/apm-agent-go/internal/fastjson/writer.go new file mode 100644 index 00000000000..f8231ba8b38 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/fastjson/writer.go @@ -0,0 +1,160 @@ +package fastjson + +import ( + "strconv" + "time" + "unicode/utf8" +) + +// Writer is a JSON writer. +type Writer struct { + buf []byte +} + +// Bytes returns the internal buffer. The result +// is invalidated when Reset is called. +func (w *Writer) Bytes() []byte { + return w.buf +} + +// Size returns the current size of the buffer. +func (w *Writer) Size() int { + return len(w.buf) +} + +// Rewind rewinds the buffer such that it has size bytes, +// dropping everything proceeding. +func (w *Writer) Rewind(size int) { + w.buf = w.buf[:size] +} + +// Reset resets the internal []byte buffer to empty. +func (w *Writer) Reset() { + w.buf = w.buf[:0] +} + +// RawByte appends c to the buffer. +func (w *Writer) RawByte(c byte) { + w.buf = append(w.buf, c) +} + +// RawBytes appends data, unmodified, to the buffer. +func (w *Writer) RawBytes(data []byte) { + w.buf = append(w.buf, data...) +} + +// RawString appends s to the buffer. +func (w *Writer) RawString(s string) { + w.buf = append(w.buf, s...) +} + +// Uint64 appends n to the buffer. +func (w *Writer) Uint64(n uint64) { + w.buf = strconv.AppendUint(w.buf, uint64(n), 10) +} + +// Int64 appends n to the buffer. +func (w *Writer) Int64(n int64) { + w.buf = strconv.AppendInt(w.buf, int64(n), 10) +} + +// Float32 appends n to the buffer. +func (w *Writer) Float32(n float32) { + w.buf = strconv.AppendFloat(w.buf, float64(n), 'g', -1, 32) +} + +// Float64 appends n to the buffer. +func (w *Writer) Float64(n float64) { + w.buf = strconv.AppendFloat(w.buf, float64(n), 'g', -1, 64) +} + +// Bool appends v to the buffer. +func (w *Writer) Bool(v bool) { + w.buf = strconv.AppendBool(w.buf, v) +} + +// Time appends t to the buffer, formatted according to layout. +func (w *Writer) Time(t time.Time, layout string) { + w.buf = t.AppendFormat(w.buf, layout) +} + +const chars = "0123456789abcdef" + +func isNotEscapedSingleChar(c byte, escapeHTML bool) bool { + // Note: might make sense to use a table if there are more chars to escape. With 4 chars + // it benchmarks the same. + if escapeHTML { + return c != '<' && c != '>' && c != '&' && c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf + } + return c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf +} + +// String appends s, quoted and escaped, to the buffer. +func (w *Writer) String(s string) { + w.RawByte('"') + w.StringContents(s) + w.RawByte('"') +} + +// StringContents is the same as String, but without the surrounding quotes. +func (w *Writer) StringContents(s string) { + // Portions of the string that contain no escapes are appended as + // byte slices. + + p := 0 // last non-escape symbol + + for i := 0; i < len(s); { + c := s[i] + + if isNotEscapedSingleChar(c, true) { + // single-width character, no escaping is required + i++ + continue + } else if c < utf8.RuneSelf { + // single-with character, need to escape + w.RawString(s[p:i]) + switch c { + case '\t': + w.RawString(`\t`) + case '\r': + w.RawString(`\r`) + case '\n': + w.RawString(`\n`) + case '\\': + w.RawString(`\\`) + case '"': + w.RawString(`\"`) + default: + w.RawString(`\u00`) + w.RawByte(chars[c>>4]) + w.RawByte(chars[c&0xf]) + } + + i++ + p = i + continue + } + + // broken utf + runeValue, runeWidth := utf8.DecodeRuneInString(s[i:]) + if runeValue == utf8.RuneError && runeWidth == 1 { + w.RawString(s[p:i]) + w.RawString(`\ufffd`) + i++ + p = i + continue + } + + // jsonp stuff - tab separator and line separator + if runeValue == '\u2028' || runeValue == '\u2029' { + w.RawString(s[p:i]) + w.RawString(`\u202`) + w.RawByte(chars[runeValue&0xf]) + i += runeWidth + p = i + continue + } + i += runeWidth + } + w.RawString(s[p:]) +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/krtext/License b/vendor/github.com/elastic/apm-agent-go/internal/krtext/License new file mode 100644 index 00000000000..480a3280599 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/krtext/License @@ -0,0 +1,19 @@ +Copyright 2012 Keith Rarick + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/elastic/apm-agent-go/internal/krtext/Readme b/vendor/github.com/elastic/apm-agent-go/internal/krtext/Readme new file mode 100644 index 00000000000..7e6e7c0687b --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/krtext/Readme @@ -0,0 +1,3 @@ +This is a Go package for manipulating paragraphs of text. + +See http://go.pkgdoc.org/github.com/kr/text for full documentation. diff --git a/vendor/github.com/elastic/apm-agent-go/internal/krtext/doc.go b/vendor/github.com/elastic/apm-agent-go/internal/krtext/doc.go new file mode 100644 index 00000000000..cf4c198f955 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/krtext/doc.go @@ -0,0 +1,3 @@ +// Package text provides rudimentary functions for manipulating text in +// paragraphs. +package text diff --git a/vendor/github.com/elastic/apm-agent-go/internal/krtext/indent.go b/vendor/github.com/elastic/apm-agent-go/internal/krtext/indent.go new file mode 100644 index 00000000000..4ebac45c092 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/krtext/indent.go @@ -0,0 +1,74 @@ +package text + +import ( + "io" +) + +// Indent inserts prefix at the beginning of each non-empty line of s. The +// end-of-line marker is NL. +func Indent(s, prefix string) string { + return string(IndentBytes([]byte(s), []byte(prefix))) +} + +// IndentBytes inserts prefix at the beginning of each non-empty line of b. +// The end-of-line marker is NL. +func IndentBytes(b, prefix []byte) []byte { + var res []byte + bol := true + for _, c := range b { + if bol && c != '\n' { + res = append(res, prefix...) + } + res = append(res, c) + bol = c == '\n' + } + return res +} + +// Writer indents each line of its input. +type indentWriter struct { + w io.Writer + bol bool + pre [][]byte + sel int + off int +} + +// NewIndentWriter makes a new write filter that indents the input +// lines. Each line is prefixed in order with the corresponding +// element of pre. If there are more lines than elements, the last +// element of pre is repeated for each subsequent line. +func NewIndentWriter(w io.Writer, pre ...[]byte) io.Writer { + return &indentWriter{ + w: w, + pre: pre, + bol: true, + } +} + +// The only errors returned are from the underlying indentWriter. +func (w *indentWriter) Write(p []byte) (n int, err error) { + for _, c := range p { + if w.bol { + var i int + i, err = w.w.Write(w.pre[w.sel][w.off:]) + w.off += i + if err != nil { + return n, err + } + } + _, err = w.w.Write([]byte{c}) + if err != nil { + return n, err + } + n++ + w.bol = c == '\n' + if w.bol { + w.off = 0 + if w.sel < len(w.pre)-1 { + w.sel++ + } + } + } + return n, nil +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/krtext/wrap.go b/vendor/github.com/elastic/apm-agent-go/internal/krtext/wrap.go new file mode 100644 index 00000000000..b09bb03736d --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/krtext/wrap.go @@ -0,0 +1,86 @@ +package text + +import ( + "bytes" + "math" +) + +var ( + nl = []byte{'\n'} + sp = []byte{' '} +) + +const defaultPenalty = 1e5 + +// Wrap wraps s into a paragraph of lines of length lim, with minimal +// raggedness. +func Wrap(s string, lim int) string { + return string(WrapBytes([]byte(s), lim)) +} + +// WrapBytes wraps b into a paragraph of lines of length lim, with minimal +// raggedness. +func WrapBytes(b []byte, lim int) []byte { + words := bytes.Split(bytes.Replace(bytes.TrimSpace(b), nl, sp, -1), sp) + var lines [][]byte + for _, line := range WrapWords(words, 1, lim, defaultPenalty) { + lines = append(lines, bytes.Join(line, sp)) + } + return bytes.Join(lines, nl) +} + +// WrapWords is the low-level line-breaking algorithm, useful if you need more +// control over the details of the text wrapping process. For most uses, either +// Wrap or WrapBytes will be sufficient and more convenient. +// +// WrapWords splits a list of words into lines with minimal "raggedness", +// treating each byte as one unit, accounting for spc units between adjacent +// words on each line, and attempting to limit lines to lim units. Raggedness +// is the total error over all lines, where error is the square of the +// difference of the length of the line and lim. Too-long lines (which only +// happen when a single word is longer than lim units) have pen penalty units +// added to the error. +func WrapWords(words [][]byte, spc, lim, pen int) [][][]byte { + n := len(words) + + length := make([][]int, n) + for i := 0; i < n; i++ { + length[i] = make([]int, n) + length[i][i] = len(words[i]) + for j := i + 1; j < n; j++ { + length[i][j] = length[i][j-1] + spc + len(words[j]) + } + } + + nbrk := make([]int, n) + cost := make([]int, n) + for i := range cost { + cost[i] = math.MaxInt32 + } + for i := n - 1; i >= 0; i-- { + if length[i][n-1] <= lim || i == n-1 { + cost[i] = 0 + nbrk[i] = n + } else { + for j := i + 1; j < n; j++ { + d := lim - length[i][j-1] + c := d*d + cost[j] + if length[i][j-1] > lim { + c += pen // too-long lines get a worse penalty + } + if c < cost[i] { + cost[i] = c + nbrk[i] = j + } + } + } + } + + var lines [][][]byte + i := 0 + for i < n { + lines = append(lines, words[i:nbrk[i]]) + i = nbrk[i] + } + return lines +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/pretty/License b/vendor/github.com/elastic/apm-agent-go/internal/pretty/License new file mode 100644 index 00000000000..05c783ccf68 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/pretty/License @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright 2012 Keith Rarick + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/elastic/apm-agent-go/internal/pretty/formatter.go b/vendor/github.com/elastic/apm-agent-go/internal/pretty/formatter.go new file mode 100644 index 00000000000..8868d5950b4 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/pretty/formatter.go @@ -0,0 +1,328 @@ +package pretty + +import ( + "fmt" + "io" + "reflect" + "strconv" + "text/tabwriter" + + "github.com/elastic/apm-agent-go/internal/krtext" +) + +type formatter struct { + v reflect.Value + force bool + quote bool +} + +// Formatter makes a wrapper, f, that will format x as go source with line +// breaks and tabs. Object f responds to the "%v" formatting verb when both the +// "#" and " " (space) flags are set, for example: +// +// fmt.Sprintf("%# v", Formatter(x)) +// +// If one of these two flags is not set, or any other verb is used, f will +// format x according to the usual rules of package fmt. +// In particular, if x satisfies fmt.Formatter, then x.Format will be called. +func Formatter(x interface{}) (f fmt.Formatter) { + return formatter{v: reflect.ValueOf(x), quote: true} +} + +func (fo formatter) String() string { + return fmt.Sprint(fo.v.Interface()) // unwrap it +} + +func (fo formatter) passThrough(f fmt.State, c rune) { + s := "%" + for i := 0; i < 128; i++ { + if f.Flag(i) { + s += string(i) + } + } + if w, ok := f.Width(); ok { + s += fmt.Sprintf("%d", w) + } + if p, ok := f.Precision(); ok { + s += fmt.Sprintf(".%d", p) + } + s += string(c) + fmt.Fprintf(f, s, fo.v.Interface()) +} + +func (fo formatter) Format(f fmt.State, c rune) { + if fo.force || c == 'v' && f.Flag('#') && f.Flag(' ') { + w := tabwriter.NewWriter(f, 4, 4, 1, ' ', 0) + p := &printer{tw: w, Writer: w, visited: make(map[visit]int)} + p.printValue(fo.v, true, fo.quote) + w.Flush() + return + } + fo.passThrough(f, c) +} + +type printer struct { + io.Writer + tw *tabwriter.Writer + visited map[visit]int + depth int +} + +func (p *printer) indent() *printer { + q := *p + q.tw = tabwriter.NewWriter(p.Writer, 4, 4, 1, ' ', 0) + q.Writer = text.NewIndentWriter(q.tw, []byte{'\t'}) + return &q +} + +func (p *printer) printInline(v reflect.Value, x interface{}, showType bool) { + if showType { + io.WriteString(p, v.Type().String()) + fmt.Fprintf(p, "(%#v)", x) + } else { + fmt.Fprintf(p, "%#v", x) + } +} + +// printValue must keep track of already-printed pointer values to avoid +// infinite recursion. +type visit struct { + v uintptr + typ reflect.Type +} + +func (p *printer) printValue(v reflect.Value, showType, quote bool) { + if p.depth > 10 { + io.WriteString(p, "!%v(DEPTH EXCEEDED)") + return + } + + switch v.Kind() { + case reflect.Bool: + p.printInline(v, v.Bool(), showType) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p.printInline(v, v.Int(), showType) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p.printInline(v, v.Uint(), showType) + case reflect.Float32, reflect.Float64: + p.printInline(v, v.Float(), showType) + case reflect.Complex64, reflect.Complex128: + fmt.Fprintf(p, "%#v", v.Complex()) + case reflect.String: + p.fmtString(v.String(), quote) + case reflect.Map: + t := v.Type() + if showType { + io.WriteString(p, t.String()) + } + writeByte(p, '{') + if nonzero(v) { + expand := !canInline(v.Type()) + pp := p + if expand { + writeByte(p, '\n') + pp = p.indent() + } + keys := v.MapKeys() + for i := 0; i < v.Len(); i++ { + showTypeInStruct := true + k := keys[i] + mv := v.MapIndex(k) + pp.printValue(k, false, true) + writeByte(pp, ':') + if expand { + writeByte(pp, '\t') + } + showTypeInStruct = t.Elem().Kind() == reflect.Interface + pp.printValue(mv, showTypeInStruct, true) + if expand { + io.WriteString(pp, ",\n") + } else if i < v.Len()-1 { + io.WriteString(pp, ", ") + } + } + if expand { + pp.tw.Flush() + } + } + writeByte(p, '}') + case reflect.Struct: + t := v.Type() + if v.CanAddr() { + addr := v.UnsafeAddr() + vis := visit{addr, t} + if vd, ok := p.visited[vis]; ok && vd < p.depth { + p.fmtString(t.String()+"{(CYCLIC REFERENCE)}", false) + break // don't print v again + } + p.visited[vis] = p.depth + } + + if showType { + io.WriteString(p, t.String()) + } + writeByte(p, '{') + if nonzero(v) { + expand := !canInline(v.Type()) + pp := p + if expand { + writeByte(p, '\n') + pp = p.indent() + } + for i := 0; i < v.NumField(); i++ { + showTypeInStruct := true + if f := t.Field(i); f.Name != "" { + io.WriteString(pp, f.Name) + writeByte(pp, ':') + if expand { + writeByte(pp, '\t') + } + showTypeInStruct = labelType(f.Type) + } + pp.printValue(getField(v, i), showTypeInStruct, true) + if expand { + io.WriteString(pp, ",\n") + } else if i < v.NumField()-1 { + io.WriteString(pp, ", ") + } + } + if expand { + pp.tw.Flush() + } + } + writeByte(p, '}') + case reflect.Interface: + switch e := v.Elem(); { + case e.Kind() == reflect.Invalid: + io.WriteString(p, "nil") + case e.IsValid(): + pp := *p + pp.depth++ + pp.printValue(e, showType, true) + default: + io.WriteString(p, v.Type().String()) + io.WriteString(p, "(nil)") + } + case reflect.Array, reflect.Slice: + t := v.Type() + if showType { + io.WriteString(p, t.String()) + } + if v.Kind() == reflect.Slice && v.IsNil() && showType { + io.WriteString(p, "(nil)") + break + } + if v.Kind() == reflect.Slice && v.IsNil() { + io.WriteString(p, "nil") + break + } + writeByte(p, '{') + expand := !canInline(v.Type()) + pp := p + if expand { + writeByte(p, '\n') + pp = p.indent() + } + for i := 0; i < v.Len(); i++ { + showTypeInSlice := t.Elem().Kind() == reflect.Interface + pp.printValue(v.Index(i), showTypeInSlice, true) + if expand { + io.WriteString(pp, ",\n") + } else if i < v.Len()-1 { + io.WriteString(pp, ", ") + } + } + if expand { + pp.tw.Flush() + } + writeByte(p, '}') + case reflect.Ptr: + e := v.Elem() + if !e.IsValid() { + writeByte(p, '(') + io.WriteString(p, v.Type().String()) + io.WriteString(p, ")(nil)") + } else { + pp := *p + pp.depth++ + writeByte(pp, '&') + pp.printValue(e, true, true) + } + case reflect.Chan: + x := v.Pointer() + if showType { + writeByte(p, '(') + io.WriteString(p, v.Type().String()) + fmt.Fprintf(p, ")(%#v)", x) + } else { + fmt.Fprintf(p, "%#v", x) + } + case reflect.Func: + io.WriteString(p, v.Type().String()) + io.WriteString(p, " {...}") + case reflect.UnsafePointer: + p.printInline(v, v.Pointer(), showType) + case reflect.Invalid: + io.WriteString(p, "nil") + } +} + +func canInline(t reflect.Type) bool { + switch t.Kind() { + case reflect.Map: + return !canExpand(t.Elem()) + case reflect.Struct: + for i := 0; i < t.NumField(); i++ { + if canExpand(t.Field(i).Type) { + return false + } + } + return true + case reflect.Interface: + return false + case reflect.Array, reflect.Slice: + return !canExpand(t.Elem()) + case reflect.Ptr: + return false + case reflect.Chan, reflect.Func, reflect.UnsafePointer: + return false + } + return true +} + +func canExpand(t reflect.Type) bool { + switch t.Kind() { + case reflect.Map, reflect.Struct, + reflect.Interface, reflect.Array, reflect.Slice, + reflect.Ptr: + return true + } + return false +} + +func labelType(t reflect.Type) bool { + switch t.Kind() { + case reflect.Interface, reflect.Struct: + return true + } + return false +} + +func (p *printer) fmtString(s string, quote bool) { + if quote { + s = strconv.Quote(s) + } + io.WriteString(p, s) +} + +func writeByte(w io.Writer, b byte) { + w.Write([]byte{b}) +} + +func getField(v reflect.Value, i int) reflect.Value { + val := v.Field(i) + if val.Kind() == reflect.Interface && !val.IsNil() { + val = val.Elem() + } + return val +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/pretty/pretty.go b/vendor/github.com/elastic/apm-agent-go/internal/pretty/pretty.go new file mode 100644 index 00000000000..49423ec7f54 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/pretty/pretty.go @@ -0,0 +1,108 @@ +// Package pretty provides pretty-printing for Go values. This is +// useful during debugging, to avoid wrapping long output lines in +// the terminal. +// +// It provides a function, Formatter, that can be used with any +// function that accepts a format string. It also provides +// convenience wrappers for functions in packages fmt and log. +package pretty + +import ( + "fmt" + "io" + "log" + "reflect" +) + +// Errorf is a convenience wrapper for fmt.Errorf. +// +// Calling Errorf(f, x, y) is equivalent to +// fmt.Errorf(f, Formatter(x), Formatter(y)). +func Errorf(format string, a ...interface{}) error { + return fmt.Errorf(format, wrap(a, false)...) +} + +// Fprintf is a convenience wrapper for fmt.Fprintf. +// +// Calling Fprintf(w, f, x, y) is equivalent to +// fmt.Fprintf(w, f, Formatter(x), Formatter(y)). +func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error error) { + return fmt.Fprintf(w, format, wrap(a, false)...) +} + +// Log is a convenience wrapper for log.Printf. +// +// Calling Log(x, y) is equivalent to +// log.Print(Formatter(x), Formatter(y)), but each operand is +// formatted with "%# v". +func Log(a ...interface{}) { + log.Print(wrap(a, true)...) +} + +// Logf is a convenience wrapper for log.Printf. +// +// Calling Logf(f, x, y) is equivalent to +// log.Printf(f, Formatter(x), Formatter(y)). +func Logf(format string, a ...interface{}) { + log.Printf(format, wrap(a, false)...) +} + +// Logln is a convenience wrapper for log.Printf. +// +// Calling Logln(x, y) is equivalent to +// log.Println(Formatter(x), Formatter(y)), but each operand is +// formatted with "%# v". +func Logln(a ...interface{}) { + log.Println(wrap(a, true)...) +} + +// Print pretty-prints its operands and writes to standard output. +// +// Calling Print(x, y) is equivalent to +// fmt.Print(Formatter(x), Formatter(y)), but each operand is +// formatted with "%# v". +func Print(a ...interface{}) (n int, errno error) { + return fmt.Print(wrap(a, true)...) +} + +// Printf is a convenience wrapper for fmt.Printf. +// +// Calling Printf(f, x, y) is equivalent to +// fmt.Printf(f, Formatter(x), Formatter(y)). +func Printf(format string, a ...interface{}) (n int, errno error) { + return fmt.Printf(format, wrap(a, false)...) +} + +// Println pretty-prints its operands and writes to standard output. +// +// Calling Print(x, y) is equivalent to +// fmt.Println(Formatter(x), Formatter(y)), but each operand is +// formatted with "%# v". +func Println(a ...interface{}) (n int, errno error) { + return fmt.Println(wrap(a, true)...) +} + +// Sprint is a convenience wrapper for fmt.Sprintf. +// +// Calling Sprint(x, y) is equivalent to +// fmt.Sprint(Formatter(x), Formatter(y)), but each operand is +// formatted with "%# v". +func Sprint(a ...interface{}) string { + return fmt.Sprint(wrap(a, true)...) +} + +// Sprintf is a convenience wrapper for fmt.Sprintf. +// +// Calling Sprintf(f, x, y) is equivalent to +// fmt.Sprintf(f, Formatter(x), Formatter(y)). +func Sprintf(format string, a ...interface{}) string { + return fmt.Sprintf(format, wrap(a, false)...) +} + +func wrap(a []interface{}, force bool) []interface{} { + w := make([]interface{}, len(a)) + for i, x := range a { + w[i] = formatter{v: reflect.ValueOf(x), force: force} + } + return w +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/pretty/zero.go b/vendor/github.com/elastic/apm-agent-go/internal/pretty/zero.go new file mode 100644 index 00000000000..abb5b6fc14c --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/pretty/zero.go @@ -0,0 +1,41 @@ +package pretty + +import ( + "reflect" +) + +func nonzero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Bool: + return v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() != 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() != 0 + case reflect.Float32, reflect.Float64: + return v.Float() != 0 + case reflect.Complex64, reflect.Complex128: + return v.Complex() != complex(0, 0) + case reflect.String: + return v.String() != "" + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + if nonzero(getField(v, i)) { + return true + } + } + return false + case reflect.Array: + for i := 0; i < v.Len(); i++ { + if nonzero(v.Index(i)) { + return true + } + } + return false + case reflect.Map, reflect.Interface, reflect.Slice, reflect.Ptr, reflect.Chan, reflect.Func: + return !v.IsNil() + case reflect.UnsafePointer: + return v.Pointer() != 0 + } + return true +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/radix/LICENSE.goradix.txt b/vendor/github.com/elastic/apm-agent-go/internal/radix/LICENSE.goradix.txt new file mode 100644 index 00000000000..a5df10e675d --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/radix/LICENSE.goradix.txt @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Armon Dadgar + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/elastic/apm-agent-go/internal/radix/radix.go b/vendor/github.com/elastic/apm-agent-go/internal/radix/radix.go new file mode 100644 index 00000000000..f9655a126b7 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/radix/radix.go @@ -0,0 +1,543 @@ +package radix + +import ( + "sort" + "strings" +) + +// WalkFn is used when walking the tree. Takes a +// key and value, returning if iteration should +// be terminated. +type WalkFn func(s string, v interface{}) bool + +// leafNode is used to represent a value +type leafNode struct { + key string + val interface{} +} + +// edge is used to represent an edge node +type edge struct { + label byte + node *node +} + +type node struct { + // leaf is used to store possible leaf + leaf *leafNode + + // prefix is the common prefix we ignore + prefix string + + // Edges should be stored in-order for iteration. + // We avoid a fully materialized slice to save memory, + // since in most cases we expect to be sparse + edges edges +} + +func (n *node) isLeaf() bool { + return n.leaf != nil +} + +func (n *node) addEdge(e edge) { + n.edges = append(n.edges, e) + n.edges.Sort() +} + +func (n *node) replaceEdge(e edge) { + num := len(n.edges) + idx := sort.Search(num, func(i int) bool { + return n.edges[i].label >= e.label + }) + if idx < num && n.edges[idx].label == e.label { + n.edges[idx].node = e.node + return + } + panic("replacing missing edge") +} + +func (n *node) getEdge(label byte) *node { + num := len(n.edges) + idx := sort.Search(num, func(i int) bool { + return n.edges[i].label >= label + }) + if idx < num && n.edges[idx].label == label { + return n.edges[idx].node + } + return nil +} + +func (n *node) delEdge(label byte) { + num := len(n.edges) + idx := sort.Search(num, func(i int) bool { + return n.edges[i].label >= label + }) + if idx < num && n.edges[idx].label == label { + copy(n.edges[idx:], n.edges[idx+1:]) + n.edges[len(n.edges)-1] = edge{} + n.edges = n.edges[:len(n.edges)-1] + } +} + +type edges []edge + +func (e edges) Len() int { + return len(e) +} + +func (e edges) Less(i, j int) bool { + return e[i].label < e[j].label +} + +func (e edges) Swap(i, j int) { + e[i], e[j] = e[j], e[i] +} + +func (e edges) Sort() { + sort.Sort(e) +} + +// Tree implements a radix tree. This can be treated as a +// Dictionary abstract data type. The main advantage over +// a standard hash map is prefix-based lookups and +// ordered iteration, +type Tree struct { + root *node + size int +} + +// New returns an empty Tree +func New() *Tree { + return NewFromMap(nil) +} + +// NewFromMap returns a new tree containing the keys +// from an existing map +func NewFromMap(m map[string]interface{}) *Tree { + t := &Tree{root: &node{}} + for k, v := range m { + t.Insert(k, v) + } + return t +} + +// Len is used to return the number of elements in the tree +func (t *Tree) Len() int { + return t.size +} + +// longestPrefix finds the length of the shared prefix +// of two strings +func longestPrefix(k1, k2 string) int { + max := len(k1) + if l := len(k2); l < max { + max = l + } + var i int + for i = 0; i < max; i++ { + if k1[i] != k2[i] { + break + } + } + return i +} + +// Insert is used to add a newentry or update +// an existing entry. Returns if updated. +func (t *Tree) Insert(s string, v interface{}) (interface{}, bool) { + var parent *node + n := t.root + search := s + for { + // Handle key exhaution + if len(search) == 0 { + if n.isLeaf() { + old := n.leaf.val + n.leaf.val = v + return old, true + } + + n.leaf = &leafNode{ + key: s, + val: v, + } + t.size++ + return nil, false + } + + // Look for the edge + parent = n + n = n.getEdge(search[0]) + + // No edge, create one + if n == nil { + e := edge{ + label: search[0], + node: &node{ + leaf: &leafNode{ + key: s, + val: v, + }, + prefix: search, + }, + } + parent.addEdge(e) + t.size++ + return nil, false + } + + // Determine longest prefix of the search key on match + commonPrefix := longestPrefix(search, n.prefix) + if commonPrefix == len(n.prefix) { + search = search[commonPrefix:] + continue + } + + // Split the node + t.size++ + child := &node{ + prefix: search[:commonPrefix], + } + parent.replaceEdge(edge{ + label: search[0], + node: child, + }) + + // Restore the existing node + child.addEdge(edge{ + label: n.prefix[commonPrefix], + node: n, + }) + n.prefix = n.prefix[commonPrefix:] + + // Create a new leaf node + leaf := &leafNode{ + key: s, + val: v, + } + + // If the new key is a subset, add to to this node + search = search[commonPrefix:] + if len(search) == 0 { + child.leaf = leaf + return nil, false + } + + // Create a new edge for the node + child.addEdge(edge{ + label: search[0], + node: &node{ + leaf: leaf, + prefix: search, + }, + }) + return nil, false + } +} + +// Delete is used to delete a key, returning the previous +// value and if it was deleted +func (t *Tree) Delete(s string) (interface{}, bool) { + var parent *node + var label byte + n := t.root + search := s + for { + // Check for key exhaution + if len(search) == 0 { + if !n.isLeaf() { + break + } + goto DELETE + } + + // Look for an edge + parent = n + label = search[0] + n = n.getEdge(label) + if n == nil { + break + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + } else { + break + } + } + return nil, false + +DELETE: + // Delete the leaf + leaf := n.leaf + n.leaf = nil + t.size-- + + // Check if we should delete this node from the parent + if parent != nil && len(n.edges) == 0 { + parent.delEdge(label) + } + + // Check if we should merge this node + if n != t.root && len(n.edges) == 1 { + n.mergeChild() + } + + // Check if we should merge the parent's other child + if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { + parent.mergeChild() + } + + return leaf.val, true +} + +// DeletePrefix is used to delete the subtree under a prefix +// Returns how many nodes were deleted +// Use this to delete large subtrees efficiently +func (t *Tree) DeletePrefix(s string) int { + return t.deletePrefix(nil, t.root, s) +} + +// delete does a recursive deletion +func (t *Tree) deletePrefix(parent, n *node, prefix string) int { + // Check for key exhaustion + if len(prefix) == 0 { + // Remove the leaf node + subTreeSize := 0 + //recursively walk from all edges of the node to be deleted + recursiveWalk(n, func(s string, v interface{}) bool { + subTreeSize++ + return false + }) + if n.isLeaf() { + n.leaf = nil + } + n.edges = nil // deletes the entire subtree + + // Check if we should merge the parent's other child + if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { + parent.mergeChild() + } + t.size -= subTreeSize + return subTreeSize + } + + // Look for an edge + label := prefix[0] + child := n.getEdge(label) + if child == nil || (!strings.HasPrefix(child.prefix, prefix) && !strings.HasPrefix(prefix, child.prefix)) { + return 0 + } + + // Consume the search prefix + if len(child.prefix) > len(prefix) { + prefix = prefix[len(prefix):] + } else { + prefix = prefix[len(child.prefix):] + } + return t.deletePrefix(n, child, prefix) +} + +func (n *node) mergeChild() { + e := n.edges[0] + child := e.node + n.prefix = n.prefix + child.prefix + n.leaf = child.leaf + n.edges = child.edges +} + +// Get is used to lookup a specific key, returning +// the value and if it was found +func (t *Tree) Get(s string) (interface{}, bool) { + n := t.root + search := s + for { + // Check for key exhaution + if len(search) == 0 { + if n.isLeaf() { + return n.leaf.val, true + } + break + } + + // Look for an edge + n = n.getEdge(search[0]) + if n == nil { + break + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + } else { + break + } + } + return nil, false +} + +// LongestPrefix is like Get, but instead of an +// exact match, it will return the longest prefix match. +func (t *Tree) LongestPrefix(s string) (string, interface{}, bool) { + var last *leafNode + n := t.root + search := s + for { + // Look for a leaf node + if n.isLeaf() { + last = n.leaf + } + + // Check for key exhaution + if len(search) == 0 { + break + } + + // Look for an edge + n = n.getEdge(search[0]) + if n == nil { + break + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + } else { + break + } + } + if last != nil { + return last.key, last.val, true + } + return "", nil, false +} + +// Minimum is used to return the minimum value in the tree +func (t *Tree) Minimum() (string, interface{}, bool) { + n := t.root + for { + if n.isLeaf() { + return n.leaf.key, n.leaf.val, true + } + if len(n.edges) > 0 { + n = n.edges[0].node + } else { + break + } + } + return "", nil, false +} + +// Maximum is used to return the maximum value in the tree +func (t *Tree) Maximum() (string, interface{}, bool) { + n := t.root + for { + if num := len(n.edges); num > 0 { + n = n.edges[num-1].node + continue + } + if n.isLeaf() { + return n.leaf.key, n.leaf.val, true + } + break + } + return "", nil, false +} + +// Walk is used to walk the tree +func (t *Tree) Walk(fn WalkFn) { + recursiveWalk(t.root, fn) +} + +// WalkPrefix is used to walk the tree under a prefix +func (t *Tree) WalkPrefix(prefix string, fn WalkFn) { + n := t.root + search := prefix + for { + // Check for key exhaution + if len(search) == 0 { + recursiveWalk(n, fn) + return + } + + // Look for an edge + n = n.getEdge(search[0]) + if n == nil { + break + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + + } else if strings.HasPrefix(n.prefix, search) { + // Child may be under our search prefix + recursiveWalk(n, fn) + return + } else { + break + } + } + +} + +// WalkPath is used to walk the tree, but only visiting nodes +// from the root down to a given leaf. Where WalkPrefix walks +// all the entries *under* the given prefix, this walks the +// entries *above* the given prefix. +func (t *Tree) WalkPath(path string, fn WalkFn) { + n := t.root + search := path + for { + // Visit the leaf values if any + if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { + return + } + + // Check for key exhaution + if len(search) == 0 { + return + } + + // Look for an edge + n = n.getEdge(search[0]) + if n == nil { + return + } + + // Consume the search prefix + if strings.HasPrefix(search, n.prefix) { + search = search[len(n.prefix):] + } else { + break + } + } +} + +// recursiveWalk is used to do a pre-order walk of a node +// recursively. Returns true if the walk should be aborted +func recursiveWalk(n *node, fn WalkFn) bool { + // Visit the leaf values if any + if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { + return true + } + + // Recurse on the children + for _, e := range n.edges { + if recursiveWalk(e.node, fn) { + return true + } + } + return false +} + +// ToMap is used to walk the tree and convert it into a map +func (t *Tree) ToMap() map[string]interface{} { + out := make(map[string]interface{}, t.size) + t.Walk(func(k string, v interface{}) bool { + out[k] = v + return false + }) + return out +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/uuid/LICENSE b/vendor/github.com/elastic/apm-agent-go/internal/uuid/LICENSE new file mode 100644 index 00000000000..926d5498702 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/uuid/LICENSE @@ -0,0 +1,20 @@ +Copyright (C) 2013-2018 by Maxim Bublis + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/elastic/apm-agent-go/internal/uuid/README.md b/vendor/github.com/elastic/apm-agent-go/internal/uuid/README.md new file mode 100644 index 00000000000..770284969a8 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/uuid/README.md @@ -0,0 +1,74 @@ +# UUID package for Go language + +[![Build Status](https://travis-ci.org/satori/go.uuid.svg?branch=master)](https://travis-ci.org/satori/go.uuid) +[![Coverage Status](https://coveralls.io/repos/github/satori/go.uuid/badge.svg?branch=master)](https://coveralls.io/github/satori/go.uuid) +[![GoDoc](http://godoc.org/github.com/satori/go.uuid?status.svg)](http://godoc.org/github.com/satori/go.uuid) + +This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing of UUIDs. + +With 100% test coverage and benchmarks out of box. + +Supported versions: +* Version 1, based on timestamp and MAC address (RFC 4122) +* Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1) +* Version 3, based on MD5 hashing (RFC 4122) +* Version 4, based on random numbers (RFC 4122) +* Version 5, based on SHA-1 hashing (RFC 4122) + +## Installation + +Use the `go` command: + + $ go get github.com/satori/go.uuid + +## Requirements + +UUID package requires Go >= 1.2. + +## Example + +```go +package main + +import ( + "fmt" + "github.com/satori/go.uuid" +) + +func main() { + // Creating UUID Version 4 + // panic on error + u1 := uuid.Must(uuid.NewV4()) + fmt.Printf("UUIDv4: %s\n", u1) + + // or error handling + u2, err := uuid.NewV4() + if err != nil { + fmt.Printf("Something went wrong: %s", err) + return + } + fmt.Printf("UUIDv4: %s\n", u2) + + // Parsing UUID from string input + u2, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + if err != nil { + fmt.Printf("Something went wrong: %s", err) + } + fmt.Printf("Successfully parsed: %s", u2) +} +``` + +## Documentation + +[Documentation](http://godoc.org/github.com/satori/go.uuid) is hosted at GoDoc project. + +## Links +* [RFC 4122](http://tools.ietf.org/html/rfc4122) +* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01) + +## Copyright + +Copyright (C) 2013-2018 by Maxim Bublis . + +UUID package released under MIT License. +See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details. diff --git a/vendor/github.com/elastic/apm-agent-go/internal/uuid/codec.go b/vendor/github.com/elastic/apm-agent-go/internal/uuid/codec.go new file mode 100644 index 00000000000..e15d741a65a --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/uuid/codec.go @@ -0,0 +1,206 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package uuid + +import ( + "bytes" + "encoding/hex" + "fmt" +) + +// FromBytes returns UUID converted from raw byte slice input. +// It will return error if the slice isn't 16 bytes long. +func FromBytes(input []byte) (u UUID, err error) { + err = u.UnmarshalBinary(input) + return +} + +// FromBytesOrNil returns UUID converted from raw byte slice input. +// Same behavior as FromBytes, but returns a Nil UUID on error. +func FromBytesOrNil(input []byte) UUID { + uuid, err := FromBytes(input) + if err != nil { + return Nil + } + return uuid +} + +// FromString returns UUID parsed from string input. +// Input is expected in a form accepted by UnmarshalText. +func FromString(input string) (u UUID, err error) { + err = u.UnmarshalText([]byte(input)) + return +} + +// FromStringOrNil returns UUID parsed from string input. +// Same behavior as FromString, but returns a Nil UUID on error. +func FromStringOrNil(input string) UUID { + uuid, err := FromString(input) + if err != nil { + return Nil + } + return uuid +} + +// MarshalText implements the encoding.TextMarshaler interface. +// The encoding is the same as returned by String. +func (u UUID) MarshalText() (text []byte, err error) { + text = []byte(u.String()) + return +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Following formats are supported: +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" +// "6ba7b8109dad11d180b400c04fd430c8" +// ABNF for supported UUID text representation follows: +// uuid := canonical | hashlike | braced | urn +// plain := canonical | hashlike +// canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct +// hashlike := 12hexoct +// braced := '{' plain '}' +// urn := URN ':' UUID-NID ':' plain +// URN := 'urn' +// UUID-NID := 'uuid' +// 12hexoct := 6hexoct 6hexoct +// 6hexoct := 4hexoct 2hexoct +// 4hexoct := 2hexoct 2hexoct +// 2hexoct := hexoct hexoct +// hexoct := hexdig hexdig +// hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | +// 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | +// 'A' | 'B' | 'C' | 'D' | 'E' | 'F' +func (u *UUID) UnmarshalText(text []byte) (err error) { + switch len(text) { + case 32: + return u.decodeHashLike(text) + case 36: + return u.decodeCanonical(text) + case 38: + return u.decodeBraced(text) + case 41: + fallthrough + case 45: + return u.decodeURN(text) + default: + return fmt.Errorf("uuid: incorrect UUID length: %s", text) + } +} + +// decodeCanonical decodes UUID string in format +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8". +func (u *UUID) decodeCanonical(t []byte) (err error) { + if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' { + return fmt.Errorf("uuid: incorrect UUID format %s", t) + } + + src := t[:] + dst := u[:] + + for i, byteGroup := range byteGroups { + if i > 0 { + src = src[1:] // skip dash + } + _, err = hex.Decode(dst[:byteGroup/2], src[:byteGroup]) + if err != nil { + return + } + src = src[byteGroup:] + dst = dst[byteGroup/2:] + } + + return +} + +// decodeHashLike decodes UUID string in format +// "6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodeHashLike(t []byte) (err error) { + src := t[:] + dst := u[:] + + if _, err = hex.Decode(dst, src); err != nil { + return err + } + return +} + +// decodeBraced decodes UUID string in format +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" or in format +// "{6ba7b8109dad11d180b400c04fd430c8}". +func (u *UUID) decodeBraced(t []byte) (err error) { + l := len(t) + + if t[0] != '{' || t[l-1] != '}' { + return fmt.Errorf("uuid: incorrect UUID format %s", t) + } + + return u.decodePlain(t[1 : l-1]) +} + +// decodeURN decodes UUID string in format +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in format +// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodeURN(t []byte) (err error) { + total := len(t) + + urnUUIDPrefix := t[:9] + + if !bytes.Equal(urnUUIDPrefix, urnPrefix) { + return fmt.Errorf("uuid: incorrect UUID format: %s", t) + } + + return u.decodePlain(t[9:total]) +} + +// decodePlain decodes UUID string in canonical format +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format +// "6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodePlain(t []byte) (err error) { + switch len(t) { + case 32: + return u.decodeHashLike(t) + case 36: + return u.decodeCanonical(t) + default: + return fmt.Errorf("uuid: incorrrect UUID length: %s", t) + } +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (u UUID) MarshalBinary() (data []byte, err error) { + data = u.Bytes() + return +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +// It will return error if the slice isn't 16 bytes long. +func (u *UUID) UnmarshalBinary(data []byte) (err error) { + if len(data) != Size { + err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) + return + } + copy(u[:], data) + + return +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/uuid/generator.go b/vendor/github.com/elastic/apm-agent-go/internal/uuid/generator.go new file mode 100644 index 00000000000..499dc35fb72 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/uuid/generator.go @@ -0,0 +1,265 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package uuid + +import ( + "crypto/md5" + "crypto/rand" + "crypto/sha1" + "encoding/binary" + "fmt" + "hash" + "io" + "net" + "os" + "sync" + "time" +) + +// Difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). +const epochStart = 122192928000000000 + +type epochFunc func() time.Time +type hwAddrFunc func() (net.HardwareAddr, error) + +var ( + global = newRFC4122Generator() + + posixUID = uint32(os.Getuid()) + posixGID = uint32(os.Getgid()) +) + +// NewV1 returns UUID based on current timestamp and MAC address. +func NewV1() (UUID, error) { + return global.NewV1() +} + +// NewV2 returns DCE Security UUID based on POSIX UID/GID. +func NewV2(domain byte) (UUID, error) { + return global.NewV2(domain) +} + +// NewV3 returns UUID based on MD5 hash of namespace UUID and name. +func NewV3(ns UUID, name string) UUID { + return global.NewV3(ns, name) +} + +// NewV4 returns random generated UUID. +func NewV4() (UUID, error) { + return global.NewV4() +} + +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. +func NewV5(ns UUID, name string) UUID { + return global.NewV5(ns, name) +} + +// Generator provides interface for generating UUIDs. +type Generator interface { + NewV1() (UUID, error) + NewV2(domain byte) (UUID, error) + NewV3(ns UUID, name string) UUID + NewV4() (UUID, error) + NewV5(ns UUID, name string) UUID +} + +// Default generator implementation. +type rfc4122Generator struct { + clockSequenceOnce sync.Once + hardwareAddrOnce sync.Once + storageMutex sync.Mutex + + rand io.Reader + + epochFunc epochFunc + hwAddrFunc hwAddrFunc + lastTime uint64 + clockSequence uint16 + hardwareAddr [6]byte +} + +func newRFC4122Generator() Generator { + return &rfc4122Generator{ + epochFunc: time.Now, + hwAddrFunc: defaultHWAddrFunc, + rand: rand.Reader, + } +} + +// NewV1 returns UUID based on current timestamp and MAC address. +func (g *rfc4122Generator) NewV1() (UUID, error) { + u := UUID{} + + timeNow, clockSeq, err := g.getClockSequence() + if err != nil { + return Nil, err + } + binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) + binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) + binary.BigEndian.PutUint16(u[8:], clockSeq) + + hardwareAddr, err := g.getHardwareAddr() + if err != nil { + return Nil, err + } + copy(u[10:], hardwareAddr) + + u.SetVersion(V1) + u.SetVariant(VariantRFC4122) + + return u, nil +} + +// NewV2 returns DCE Security UUID based on POSIX UID/GID. +func (g *rfc4122Generator) NewV2(domain byte) (UUID, error) { + u, err := g.NewV1() + if err != nil { + return Nil, err + } + + switch domain { + case DomainPerson: + binary.BigEndian.PutUint32(u[:], posixUID) + case DomainGroup: + binary.BigEndian.PutUint32(u[:], posixGID) + } + + u[9] = domain + + u.SetVersion(V2) + u.SetVariant(VariantRFC4122) + + return u, nil +} + +// NewV3 returns UUID based on MD5 hash of namespace UUID and name. +func (g *rfc4122Generator) NewV3(ns UUID, name string) UUID { + u := newFromHash(md5.New(), ns, name) + u.SetVersion(V3) + u.SetVariant(VariantRFC4122) + + return u +} + +// NewV4 returns random generated UUID. +func (g *rfc4122Generator) NewV4() (UUID, error) { + u := UUID{} + if _, err := g.rand.Read(u[:]); err != nil { + return Nil, err + } + u.SetVersion(V4) + u.SetVariant(VariantRFC4122) + + return u, nil +} + +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. +func (g *rfc4122Generator) NewV5(ns UUID, name string) UUID { + u := newFromHash(sha1.New(), ns, name) + u.SetVersion(V5) + u.SetVariant(VariantRFC4122) + + return u +} + +// Returns epoch and clock sequence. +func (g *rfc4122Generator) getClockSequence() (uint64, uint16, error) { + var err error + g.clockSequenceOnce.Do(func() { + buf := make([]byte, 2) + if _, err = g.rand.Read(buf); err != nil { + return + } + g.clockSequence = binary.BigEndian.Uint16(buf) + }) + if err != nil { + return 0, 0, err + } + + g.storageMutex.Lock() + defer g.storageMutex.Unlock() + + timeNow := g.getEpoch() + // Clock didn't change since last UUID generation. + // Should increase clock sequence. + if timeNow <= g.lastTime { + g.clockSequence++ + } + g.lastTime = timeNow + + return timeNow, g.clockSequence, nil +} + +// Returns hardware address. +func (g *rfc4122Generator) getHardwareAddr() ([]byte, error) { + var err error + g.hardwareAddrOnce.Do(func() { + if hwAddr, err := g.hwAddrFunc(); err == nil { + copy(g.hardwareAddr[:], hwAddr) + return + } + + // Initialize hardwareAddr randomly in case + // of real network interfaces absence. + if _, err = g.rand.Read(g.hardwareAddr[:]); err != nil { + return + } + // Set multicast bit as recommended by RFC 4122 + g.hardwareAddr[0] |= 0x01 + }) + if err != nil { + return []byte{}, err + } + return g.hardwareAddr[:], nil +} + +// Returns difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and current time. +func (g *rfc4122Generator) getEpoch() uint64 { + return epochStart + uint64(g.epochFunc().UnixNano()/100) +} + +// Returns UUID based on hashing of namespace UUID and name. +func newFromHash(h hash.Hash, ns UUID, name string) UUID { + u := UUID{} + h.Write(ns[:]) + h.Write([]byte(name)) + copy(u[:], h.Sum(nil)) + + return u +} + +// Returns hardware address. +func defaultHWAddrFunc() (net.HardwareAddr, error) { + ifaces, err := net.Interfaces() + if err != nil { + return []byte{}, err + } + for _, iface := range ifaces { + if len(iface.HardwareAddr) >= 6 { + return iface.HardwareAddr, nil + } + } + return []byte{}, fmt.Errorf("uuid: no HW address found") +} diff --git a/vendor/github.com/elastic/apm-agent-go/internal/uuid/uuid.go b/vendor/github.com/elastic/apm-agent-go/internal/uuid/uuid.go new file mode 100644 index 00000000000..a2b8e2ca2a1 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/internal/uuid/uuid.go @@ -0,0 +1,161 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Package uuid provides implementation of Universally Unique Identifier (UUID). +// Supported versions are 1, 3, 4 and 5 (as specified in RFC 4122) and +// version 2 (as specified in DCE 1.1). +package uuid + +import ( + "bytes" + "encoding/hex" +) + +// Size of a UUID in bytes. +const Size = 16 + +// UUID representation compliant with specification +// described in RFC 4122. +type UUID [Size]byte + +// UUID versions +const ( + _ byte = iota + V1 + V2 + V3 + V4 + V5 +) + +// UUID layout variants. +const ( + VariantNCS byte = iota + VariantRFC4122 + VariantMicrosoft + VariantFuture +) + +// UUID DCE domains. +const ( + DomainPerson = iota + DomainGroup + DomainOrg +) + +// String parse helpers. +var ( + urnPrefix = []byte("urn:uuid:") + byteGroups = []int{8, 4, 4, 4, 12} +) + +// Nil is special form of UUID that is specified to have all +// 128 bits set to zero. +var Nil = UUID{} + +// Predefined namespace UUIDs. +var ( + NamespaceDNS = Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) + NamespaceURL = Must(FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) + NamespaceOID = Must(FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) + NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) +) + +// Equal returns true if u1 and u2 equals, otherwise returns false. +func Equal(u1 UUID, u2 UUID) bool { + return bytes.Equal(u1[:], u2[:]) +} + +// Version returns algorithm version used to generate UUID. +func (u UUID) Version() byte { + return u[6] >> 4 +} + +// Variant returns UUID layout variant. +func (u UUID) Variant() byte { + switch { + case (u[8] >> 7) == 0x00: + return VariantNCS + case (u[8] >> 6) == 0x02: + return VariantRFC4122 + case (u[8] >> 5) == 0x06: + return VariantMicrosoft + case (u[8] >> 5) == 0x07: + fallthrough + default: + return VariantFuture + } +} + +// Bytes returns bytes slice representation of UUID. +func (u UUID) Bytes() []byte { + return u[:] +} + +// Returns canonical string representation of UUID: +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. +func (u UUID) String() string { + buf := make([]byte, 36) + + hex.Encode(buf[0:8], u[0:4]) + buf[8] = '-' + hex.Encode(buf[9:13], u[4:6]) + buf[13] = '-' + hex.Encode(buf[14:18], u[6:8]) + buf[18] = '-' + hex.Encode(buf[19:23], u[8:10]) + buf[23] = '-' + hex.Encode(buf[24:], u[10:]) + + return string(buf) +} + +// SetVersion sets version bits. +func (u *UUID) SetVersion(v byte) { + u[6] = (u[6] & 0x0f) | (v << 4) +} + +// SetVariant sets variant bits. +func (u *UUID) SetVariant(v byte) { + switch v { + case VariantNCS: + u[8] = (u[8]&(0xff>>1) | (0x00 << 7)) + case VariantRFC4122: + u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) + case VariantMicrosoft: + u[8] = (u[8]&(0xff>>3) | (0x06 << 5)) + case VariantFuture: + fallthrough + default: + u[8] = (u[8]&(0xff>>3) | (0x07 << 5)) + } +} + +// Must is a helper that wraps a call to a function returning (UUID, error) +// and panics if the error is non-nil. It is intended for use in variable +// initializations such as +// var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000")); +func Must(u UUID, err error) UUID { + if err != nil { + panic(err) + } + return u +} diff --git a/vendor/github.com/elastic/apm-agent-go/logger.go b/vendor/github.com/elastic/apm-agent-go/logger.go new file mode 100644 index 00000000000..c96d8dad8da --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/logger.go @@ -0,0 +1,11 @@ +package elasticapm + +// Logger is an interface for logging, used by the tracer +// to log tracer errors and other interesting events. +type Logger interface { + // Debugf logs a message at debug level. + Debugf(format string, args ...interface{}) + + // Errorf logs a message at error level. + Errorf(format string, args ...interface{}) +} diff --git a/vendor/github.com/elastic/apm-agent-go/model/doc.go b/vendor/github.com/elastic/apm-agent-go/model/doc.go new file mode 100644 index 00000000000..76de80d5ef7 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/model/doc.go @@ -0,0 +1,4 @@ +// Package model provides the Elastic APM model types. +// +// https://www.elastic.co/guide/en/apm/server/current/intake-api.html +package model diff --git a/vendor/github.com/elastic/apm-agent-go/model/gofuzz.go b/vendor/github.com/elastic/apm-agent-go/model/gofuzz.go new file mode 100644 index 00000000000..9d0d86be4f5 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/model/gofuzz.go @@ -0,0 +1,78 @@ +// +build gofuzz + +package model + +import ( + "bytes" + "encoding/json" + + "github.com/elastic/apm-agent-go/internal/fastjson" + error_processor "github.com/elastic/apm-server/processor/error" + transaction_processor "github.com/elastic/apm-server/processor/transaction" +) + +func Fuzz(data []byte) int { + type Payload struct { + Service *Service `json:"service"` + Process *Process `json:"process,omitempty"` + System *System `json:"system,omitempty"` + Errors []*Error `json:"errors"` + Transactions []*Transaction `json:"transactions"` + } + + var payload Payload + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + if err := decoder.Decode(&payload); err != nil { + return -1 + } + raw := make(map[string]interface{}) + if err := json.Unmarshal(data, &raw); err != nil { + return -1 + } + + if len(payload.Errors) != 0 { + p := error_processor.NewProcessor() + if err := p.Validate(raw); err != nil { + return 0 + } + payload := ErrorsPayload{ + Service: payload.Service, + Process: payload.Process, + System: payload.System, + Errors: payload.Errors, + } + var w fastjson.Writer + payload.MarshalFastJSON(&w) + raw := make(map[string]interface{}) + if err := json.Unmarshal(w.Bytes(), &raw); err != nil { + return -1 + } + if err := p.Validate(raw); err != nil { + panic(err) + } + } + + if len(payload.Transactions) != 0 { + p := transaction_processor.NewProcessor() + if err := p.Validate(raw); err != nil { + return 0 + } + payload := TransactionsPayload{ + Service: payload.Service, + Process: payload.Process, + System: payload.System, + Transactions: payload.Transactions, + } + var w fastjson.Writer + payload.MarshalFastJSON(&w) + raw := make(map[string]interface{}) + if err := json.Unmarshal(w.Bytes(), &raw); err != nil { + return -1 + } + if err := p.Validate(raw); err != nil { + panic(err) + } + } + return 0 +} diff --git a/vendor/github.com/elastic/apm-agent-go/model/maps.go b/vendor/github.com/elastic/apm-agent-go/model/maps.go new file mode 100644 index 00000000000..bc41df562e1 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/model/maps.go @@ -0,0 +1,32 @@ +package model + +import ( + "sort" +) + +// IfaceMap is a slice-representation of map[string]interface{}, +// optimized for fast JSON encoding. +// +// Slice items are expected to be ordered by key. +type IfaceMap []IfaceMapItem + +// IfaceMapItem holds a string key and arbitrary JSON-encodable value. +type IfaceMapItem struct { + // Key is the map item's key. + Key string + + // Value is an arbitrary JSON-encodable value. + Value interface{} +} + +// Set sets the map item with given key and value. +func (m *IfaceMap) Set(key string, value interface{}) { + i := sort.Search(len(*m), func(i int) bool { + return (*m)[i].Key >= key + }) + if i < len(*m) && (*m)[i].Key == key { + (*m)[i].Value = value + } else { + *m = append(*m, IfaceMapItem{Key: key, Value: value}) + } +} diff --git a/vendor/github.com/elastic/apm-agent-go/model/marshal.go b/vendor/github.com/elastic/apm-agent-go/model/marshal.go new file mode 100644 index 00000000000..edbda19ddcb --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/model/marshal.go @@ -0,0 +1,473 @@ +package model + +import ( + "encoding/hex" + "encoding/json" + "net/http" + "net/url" + "sort" + "strings" + "time" + + "github.com/pkg/errors" + + "github.com/elastic/apm-agent-go/internal/fastjson" +) + +//go:generate go run ../internal/fastjson/generate.go -f -o marshal_fastjson.go . + +const ( + // YYYY-MM-DDTHH:mm:ss.sssZ + dateTimeFormat = "2006-01-02T15:04:05.999Z" +) + +// MarshalFastJSON writes the JSON representation of t to w. +func (t Time) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('"') + w.Time(time.Time(t), dateTimeFormat) + w.RawByte('"') +} + +// UnmarshalJSON unmarshals the JSON data into t. +func (t *Time) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + time, err := time.Parse(dateTimeFormat, s) + if err != nil { + return err + } + *t = Time(time) + return nil +} + +// MarshalFastJSON writes the JSON representation of v to w. +func (v *URL) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + first := true + if v.Hash != "" { + const prefix = ",\"hash\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Hash) + } + if v.Hostname != "" { + const prefix = ",\"hostname\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Hostname) + } + if v.Path != "" { + const prefix = ",\"pathname\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Path) + } + if v.Port != "" { + const prefix = ",\"port\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Port) + } + schemeBegin := -1 + schemeEnd := -1 + if v.Protocol != "" { + before := w.Size() + const prefix = ",\"protocol\":\"" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + schemeBegin = w.Size() + if marshalScheme(w, v.Protocol) { + schemeEnd = w.Size() + w.RawByte('"') + } else { + w.Rewind(before) + } + } + if v.Search != "" { + const prefix = ",\"search\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Search) + } + if schemeEnd != -1 && v.Hostname != "" && v.Path != "" { + before := w.Size() + w.RawString(",\"full\":") + if !v.marshalFullURL(w, schemeBegin, schemeEnd) { + w.Rewind(before) + } + } + w.RawByte('}') +} + +func marshalScheme(w *fastjson.Writer, scheme string) bool { + // Canonicalize the scheme to lowercase. Don't use + // strings.ToLower, as it's too general and requires + // additional memory allocations. + // + // The scheme should start with a letter, and may + // then be followed by letters, digits, '+', '-', + // and '.'. We don't validate the scheme here, we + // just use those restrictions as a basis for + // optimization; anything not in that set will + // mean the full URL is omitted. + for i := 0; i < len(scheme); i++ { + c := scheme[i] + switch { + case c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '+' || c == '-' || c == '.': + w.RawByte(c) + case c >= 'A' && c <= 'Z': + w.RawByte(c + 'a' - 'A') + default: + return false + } + } + return true +} + +func (v *URL) marshalFullURL(w *fastjson.Writer, schemeBegin, schemeEnd int) bool { + w.RawByte('"') + before := w.Size() + w.RawBytes(w.Bytes()[schemeBegin:schemeEnd]) + w.RawString("://") + if strings.IndexByte(v.Hostname, ':') == -1 { + w.StringContents(v.Hostname) + } else { + w.RawByte('[') + w.StringContents(v.Hostname) + w.RawByte(']') + } + if v.Port != "" { + w.RawByte(':') + w.StringContents(v.Port) + } + w.StringContents(v.Path) + if v.Search != "" { + w.RawByte('?') + w.StringContents(v.Search) + } + if v.Hash != "" { + w.RawByte('#') + w.StringContents(v.Hash) + } + if n := w.Size() - before; n > 1024 { + // Truncate the full URL to 1024 bytes. + w.Rewind(w.Size() - n + 1024) + } + w.RawByte('"') + return true +} + +func (c *SpanCount) isZero() bool { + return *c == SpanCount{} +} + +func (d *SpanCountDropped) isZero() bool { + return *d == SpanCountDropped{} +} + +func (l *Log) isZero() bool { + return l.Message == "" +} + +func (e *Exception) isZero() bool { + return e.Message == "" +} + +func (c Cookies) isZero() bool { + return len(c) == 0 +} + +// MarshalFastJSON writes the JSON representation of c to w. +func (c Cookies) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + first := true +outer: + for i := len(c) - 1; i >= 0; i-- { + for j := i + 1; j < len(c); j++ { + if c[i].Name == c[j].Name { + continue outer + } + } + if first { + first = false + } else { + w.RawByte(',') + } + w.String(c[i].Name) + w.RawByte(':') + w.String(c[i].Value) + } + w.RawByte('}') +} + +// UnmarshalJSON unmarshals the JSON data into c. +func (c *Cookies) UnmarshalJSON(data []byte) error { + m := make(map[string]string) + if err := json.Unmarshal(data, &m); err != nil { + return err + } + *c = make([]*http.Cookie, 0, len(m)) + for k, v := range m { + *c = append(*c, &http.Cookie{ + Name: k, + Value: v, + }) + } + sort.Slice(*c, func(i, j int) bool { + return (*c)[i].Name < (*c)[j].Name + }) + return nil +} + +// isZero is used by fastjson to implement omitempty. +func (t *ErrorTransaction) isZero() bool { + return t.ID.isZero() +} + +// MarshalFastJSON writes the JSON representation of c to w. +func (c *ExceptionCode) MarshalFastJSON(w *fastjson.Writer) { + if c.String != "" { + w.String(c.String) + return + } + w.Float64(c.Number) +} + +// UnmarshalJSON unmarshals the JSON data into c. +func (c *ExceptionCode) UnmarshalJSON(data []byte) error { + var v interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v := v.(type) { + case string: + c.String = v + case float64: + c.Number = v + default: + return errors.Errorf("expected string or number, got %T", v) + } + return nil +} + +// isZero is used by fastjson to implement omitempty. +func (c *ExceptionCode) isZero() bool { + return c.String == "" && c.Number == 0 +} + +// MarshalFastJSON writes the JSON representation of c to w. +func (u *UserID) MarshalFastJSON(w *fastjson.Writer) { + if u.String != "" { + w.String(u.String) + return + } + w.Float64(u.Number) +} + +// UnmarshalJSON unmarshals the JSON data into c. +func (u *UserID) UnmarshalJSON(data []byte) error { + var v interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v := v.(type) { + case string: + u.String = v + case float64: + u.Number = v + default: + return errors.Errorf("expected string or number, got %T", v) + } + return nil +} + +// isZero is used by fastjson to implement omitempty. +func (u *UserID) isZero() bool { + return u.String == "" && u.Number == 0 +} + +// MarshalFastJSON writes the JSON representation of b to w. +func (b *RequestBody) MarshalFastJSON(w *fastjson.Writer) { + if b.Form != nil { + w.RawByte('{') + first := true + for k, v := range b.Form { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(k) + w.RawByte(':') + if len(v) == 1 { + // Just one item, add the item directly. + w.String(v[0]) + } else { + // Zero or multiple items, include them all. + w.RawByte('[') + first := true + for _, v := range v { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(v) + } + w.RawByte(']') + } + } + w.RawByte('}') + } else { + w.String(b.Raw) + } +} + +// UnmarshalJSON unmarshals the JSON data into b. +func (b *RequestBody) UnmarshalJSON(data []byte) error { + var v interface{} + if err := json.Unmarshal(data, &v); err != nil { + return err + } + switch v := v.(type) { + case string: + b.Raw = v + return nil + case map[string]interface{}: + form := make(url.Values, len(v)) + for k, v := range v { + switch v := v.(type) { + case string: + form.Set(k, v) + case []interface{}: + for _, v := range v { + switch v := v.(type) { + case string: + form.Add(k, v) + default: + return errors.Errorf("expected string, got %T", v) + } + } + default: + return errors.Errorf("expected string or []string, got %T", v) + } + } + b.Form = form + default: + return errors.Errorf("expected string or map, got %T", v) + } + return nil +} + +func (m IfaceMap) isZero() bool { + return len(m) == 0 +} + +// MarshalFastJSON writes the JSON representation of m to w. +func (m IfaceMap) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + first := true + for _, item := range m { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(item.Key) + w.RawByte(':') + fastjson.Marshal(w, item.Value) + } + w.RawByte('}') +} + +// UnmarshalJSON unmarshals the JSON data into m. +func (m *IfaceMap) UnmarshalJSON(data []byte) error { + var mm map[string]interface{} + if err := json.Unmarshal(data, &mm); err != nil { + return err + } + *m = make(IfaceMap, 0, len(mm)) + for k, v := range mm { + *m = append(*m, IfaceMapItem{Key: k, Value: v}) + } + sort.Slice(*m, func(i, j int) bool { + return (*m)[i].Key < (*m)[j].Key + }) + return nil +} + +// MarshalFastJSON exists to prevent code generation for IfaceMapItem. +func (*IfaceMapItem) MarshalFastJSON(*fastjson.Writer) { + panic("unreachable") +} + +func (id *UUID) isZero() bool { + return *id == UUID{} +} + +// UnmarshalJSON unmarshals the JSON data into id. +func (id *UUID) UnmarshalJSON(data []byte) error { + // NOTE(axw) UnmarshalJSON is provided only for tests; + // it should only ever be fed valid data, hence panics. + hexDecode := func(out, in []byte) { + if _, err := hex.Decode(out, in); err != nil { + panic(err) + } + } + data = data[1 : len(data)-1] + hexDecode(id[:4], data[:8]) + hexDecode(id[4:6], data[9:13]) + hexDecode(id[6:8], data[14:18]) + hexDecode(id[8:10], data[19:23]) + hexDecode(id[10:], data[24:]) + return nil +} + +// MarshalFastJSON writes the JSON representation of id to w. +func (id *UUID) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('"') + writeHex(w, id[:4]) + w.RawByte('-') + writeHex(w, id[4:6]) + w.RawByte('-') + writeHex(w, id[6:8]) + w.RawByte('-') + writeHex(w, id[8:10]) + w.RawByte('-') + writeHex(w, id[10:]) + w.RawByte('"') +} + +func writeHex(w *fastjson.Writer, v []byte) { + const hextable = "0123456789abcdef" + for _, v := range v { + w.RawByte(hextable[v>>4]) + w.RawByte(hextable[v&0x0f]) + } +} diff --git a/vendor/github.com/elastic/apm-agent-go/model/marshal_fastjson.go b/vendor/github.com/elastic/apm-agent-go/model/marshal_fastjson.go new file mode 100644 index 00000000000..1a924606268 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/model/marshal_fastjson.go @@ -0,0 +1,810 @@ +// Code generated by "go generate". DO NOT EDIT. + +package model + +import ( + "github.com/elastic/apm-agent-go/internal/fastjson" +) + +func (v *Service) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"agent\":") + v.Agent.MarshalFastJSON(w) + w.RawString(",\"name\":") + w.String(v.Name) + if v.Environment != "" { + w.RawString(",\"environment\":") + w.String(v.Environment) + } + if v.Framework != nil { + w.RawString(",\"framework\":") + v.Framework.MarshalFastJSON(w) + } + if v.Language != nil { + w.RawString(",\"language\":") + v.Language.MarshalFastJSON(w) + } + if v.Runtime != nil { + w.RawString(",\"runtime\":") + v.Runtime.MarshalFastJSON(w) + } + if v.Version != "" { + w.RawString(",\"version\":") + w.String(v.Version) + } + w.RawByte('}') +} + +func (v *Agent) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"name\":") + w.String(v.Name) + w.RawString(",\"version\":") + w.String(v.Version) + w.RawByte('}') +} + +func (v *Framework) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"name\":") + w.String(v.Name) + w.RawString(",\"version\":") + w.String(v.Version) + w.RawByte('}') +} + +func (v *Language) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"name\":") + w.String(v.Name) + if v.Version != "" { + w.RawString(",\"version\":") + w.String(v.Version) + } + w.RawByte('}') +} + +func (v *Runtime) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"name\":") + w.String(v.Name) + w.RawString(",\"version\":") + w.String(v.Version) + w.RawByte('}') +} + +func (v *System) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + first := true + if v.Architecture != "" { + const prefix = ",\"architecture\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Architecture) + } + if v.Hostname != "" { + const prefix = ",\"hostname\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Hostname) + } + if v.Platform != "" { + const prefix = ",\"platform\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Platform) + } + w.RawByte('}') +} + +func (v *Process) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"pid\":") + w.Int64(int64(v.Pid)) + if v.Argv != nil { + w.RawString(",\"argv\":") + w.RawByte('[') + for i, v := range v.Argv { + if i != 0 { + w.RawByte(',') + } + w.String(v) + } + w.RawByte(']') + } + if v.Ppid != nil { + w.RawString(",\"ppid\":") + w.Int64(int64(*v.Ppid)) + } + if v.Title != "" { + w.RawString(",\"title\":") + w.String(v.Title) + } + w.RawByte('}') +} + +func (v *Transaction) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"duration\":") + w.Float64(v.Duration) + w.RawString(",\"id\":") + v.ID.MarshalFastJSON(w) + w.RawString(",\"name\":") + w.String(v.Name) + w.RawString(",\"timestamp\":") + v.Timestamp.MarshalFastJSON(w) + w.RawString(",\"type\":") + w.String(v.Type) + if v.Context != nil { + w.RawString(",\"context\":") + v.Context.MarshalFastJSON(w) + } + if v.Result != "" { + w.RawString(",\"result\":") + w.String(v.Result) + } + if v.Sampled != nil { + w.RawString(",\"sampled\":") + w.Bool(*v.Sampled) + } + if !v.SpanCount.isZero() { + w.RawString(",\"span_count\":") + v.SpanCount.MarshalFastJSON(w) + } + if v.Spans != nil { + w.RawString(",\"spans\":") + w.RawByte('[') + for i, v := range v.Spans { + if i != 0 { + w.RawByte(',') + } + v.MarshalFastJSON(w) + } + w.RawByte(']') + } + w.RawByte('}') +} + +func (v *SpanCount) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + if !v.Dropped.isZero() { + w.RawString("\"dropped\":") + v.Dropped.MarshalFastJSON(w) + } + w.RawByte('}') +} + +func (v *SpanCountDropped) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"total\":") + w.Int64(int64(v.Total)) + w.RawByte('}') +} + +func (v *Span) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"duration\":") + w.Float64(v.Duration) + w.RawString(",\"name\":") + w.String(v.Name) + w.RawString(",\"start\":") + w.Float64(v.Start) + w.RawString(",\"type\":") + w.String(v.Type) + if v.Context != nil { + w.RawString(",\"context\":") + v.Context.MarshalFastJSON(w) + } + if v.ID != nil { + w.RawString(",\"id\":") + w.Int64(*v.ID) + } + if v.Parent != nil { + w.RawString(",\"parent\":") + w.Int64(*v.Parent) + } + if v.Stacktrace != nil { + w.RawString(",\"stacktrace\":") + w.RawByte('[') + for i, v := range v.Stacktrace { + if i != 0 { + w.RawByte(',') + } + v.MarshalFastJSON(w) + } + w.RawByte(']') + } + w.RawByte('}') +} + +func (v *SpanContext) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + if v.Database != nil { + w.RawString("\"db\":") + v.Database.MarshalFastJSON(w) + } + w.RawByte('}') +} + +func (v *DatabaseSpanContext) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + first := true + if v.Instance != "" { + const prefix = ",\"instance\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Instance) + } + if v.Statement != "" { + const prefix = ",\"statement\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Statement) + } + if v.Type != "" { + const prefix = ",\"type\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Type) + } + if v.User != "" { + const prefix = ",\"user\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.User) + } + w.RawByte('}') +} + +func (v *Context) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + first := true + if !v.Custom.isZero() { + const prefix = ",\"custom\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + v.Custom.MarshalFastJSON(w) + } + if v.Request != nil { + const prefix = ",\"request\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + v.Request.MarshalFastJSON(w) + } + if v.Response != nil { + const prefix = ",\"response\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + v.Response.MarshalFastJSON(w) + } + if v.Tags != nil { + const prefix = ",\"tags\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.RawByte('{') + { + first := true + for k, v := range v.Tags { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(k) + w.RawByte(':') + w.String(v) + } + } + w.RawByte('}') + } + if v.User != nil { + const prefix = ",\"user\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + v.User.MarshalFastJSON(w) + } + w.RawByte('}') +} + +func (v *User) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + first := true + if v.Email != "" { + const prefix = ",\"email\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Email) + } + if !v.ID.isZero() { + const prefix = ",\"id\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + v.ID.MarshalFastJSON(w) + } + if v.Username != "" { + const prefix = ",\"username\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Username) + } + w.RawByte('}') +} + +func (v *Error) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"timestamp\":") + v.Timestamp.MarshalFastJSON(w) + if v.Context != nil { + w.RawString(",\"context\":") + v.Context.MarshalFastJSON(w) + } + if v.Culprit != "" { + w.RawString(",\"culprit\":") + w.String(v.Culprit) + } + if !v.Exception.isZero() { + w.RawString(",\"exception\":") + v.Exception.MarshalFastJSON(w) + } + if v.ID != "" { + w.RawString(",\"id\":") + w.String(v.ID) + } + if !v.Log.isZero() { + w.RawString(",\"log\":") + v.Log.MarshalFastJSON(w) + } + if !v.Transaction.isZero() { + w.RawString(",\"transaction\":") + v.Transaction.MarshalFastJSON(w) + } + w.RawByte('}') +} + +func (v *ErrorTransaction) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"id\":") + v.ID.MarshalFastJSON(w) + w.RawByte('}') +} + +func (v *Exception) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"handled\":") + w.Bool(v.Handled) + w.RawString(",\"message\":") + w.String(v.Message) + if v.Attributes != nil { + w.RawString(",\"attributes\":") + w.RawByte('{') + { + first := true + for k, v := range v.Attributes { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(k) + w.RawByte(':') + fastjson.Marshal(w, v) + } + } + w.RawByte('}') + } + if !v.Code.isZero() { + w.RawString(",\"code\":") + v.Code.MarshalFastJSON(w) + } + if v.Module != "" { + w.RawString(",\"module\":") + w.String(v.Module) + } + if v.Stacktrace != nil { + w.RawString(",\"stacktrace\":") + w.RawByte('[') + for i, v := range v.Stacktrace { + if i != 0 { + w.RawByte(',') + } + v.MarshalFastJSON(w) + } + w.RawByte(']') + } + if v.Type != "" { + w.RawString(",\"type\":") + w.String(v.Type) + } + w.RawByte('}') +} + +func (v *StacktraceFrame) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"filename\":") + w.String(v.File) + w.RawString(",\"lineno\":") + w.Int64(int64(v.Line)) + if v.AbsolutePath != "" { + w.RawString(",\"abs_path\":") + w.String(v.AbsolutePath) + } + if v.Column != nil { + w.RawString(",\"colno\":") + w.Int64(int64(*v.Column)) + } + if v.ContextLine != "" { + w.RawString(",\"context_line\":") + w.String(v.ContextLine) + } + if v.Function != "" { + w.RawString(",\"function\":") + w.String(v.Function) + } + if v.LibraryFrame != false { + w.RawString(",\"library_frame\":") + w.Bool(v.LibraryFrame) + } + if v.Module != "" { + w.RawString(",\"module\":") + w.String(v.Module) + } + if v.PostContext != nil { + w.RawString(",\"post_context\":") + w.RawByte('[') + for i, v := range v.PostContext { + if i != 0 { + w.RawByte(',') + } + w.String(v) + } + w.RawByte(']') + } + if v.PreContext != nil { + w.RawString(",\"pre_context\":") + w.RawByte('[') + for i, v := range v.PreContext { + if i != 0 { + w.RawByte(',') + } + w.String(v) + } + w.RawByte(']') + } + if v.Vars != nil { + w.RawString(",\"vars\":") + w.RawByte('{') + { + first := true + for k, v := range v.Vars { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(k) + w.RawByte(':') + fastjson.Marshal(w, v) + } + } + w.RawByte('}') + } + w.RawByte('}') +} + +func (v *Log) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"message\":") + w.String(v.Message) + if v.Level != "" { + w.RawString(",\"level\":") + w.String(v.Level) + } + if v.LoggerName != "" { + w.RawString(",\"logger_name\":") + w.String(v.LoggerName) + } + if v.ParamMessage != "" { + w.RawString(",\"param_message\":") + w.String(v.ParamMessage) + } + if v.Stacktrace != nil { + w.RawString(",\"stacktrace\":") + w.RawByte('[') + for i, v := range v.Stacktrace { + if i != 0 { + w.RawByte(',') + } + v.MarshalFastJSON(w) + } + w.RawByte(']') + } + w.RawByte('}') +} + +func (v *Request) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"method\":") + w.String(v.Method) + w.RawString(",\"url\":") + v.URL.MarshalFastJSON(w) + if v.Body != nil { + w.RawString(",\"body\":") + v.Body.MarshalFastJSON(w) + } + if !v.Cookies.isZero() { + w.RawString(",\"cookies\":") + v.Cookies.MarshalFastJSON(w) + } + if v.Env != nil { + w.RawString(",\"env\":") + w.RawByte('{') + { + first := true + for k, v := range v.Env { + if first { + first = false + } else { + w.RawByte(',') + } + w.String(k) + w.RawByte(':') + w.String(v) + } + } + w.RawByte('}') + } + if v.Headers != nil { + w.RawString(",\"headers\":") + v.Headers.MarshalFastJSON(w) + } + if v.HTTPVersion != "" { + w.RawString(",\"http_version\":") + w.String(v.HTTPVersion) + } + if v.Socket != nil { + w.RawString(",\"socket\":") + v.Socket.MarshalFastJSON(w) + } + w.RawByte('}') +} + +func (v *RequestHeaders) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + first := true + if v.ContentType != "" { + const prefix = ",\"content-type\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.ContentType) + } + if v.Cookie != "" { + const prefix = ",\"cookie\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.Cookie) + } + if v.UserAgent != "" { + const prefix = ",\"user-agent\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.UserAgent) + } + w.RawByte('}') +} + +func (v *RequestSocket) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + first := true + if v.Encrypted != false { + const prefix = ",\"encrypted\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.Bool(v.Encrypted) + } + if v.RemoteAddress != "" { + const prefix = ",\"remote_address\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.String(v.RemoteAddress) + } + w.RawByte('}') +} + +func (v *Response) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + first := true + if v.Finished != nil { + const prefix = ",\"finished\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.Bool(*v.Finished) + } + if v.Headers != nil { + const prefix = ",\"headers\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + v.Headers.MarshalFastJSON(w) + } + if v.HeadersSent != nil { + const prefix = ",\"headers_sent\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.Bool(*v.HeadersSent) + } + if v.StatusCode != 0 { + const prefix = ",\"status_code\":" + if first { + first = false + w.RawString(prefix[1:]) + } else { + w.RawString(prefix) + } + w.Int64(int64(v.StatusCode)) + } + w.RawByte('}') +} + +func (v *ResponseHeaders) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + if v.ContentType != "" { + w.RawString("\"content-type\":") + w.String(v.ContentType) + } + w.RawByte('}') +} + +func (v *TransactionsPayload) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"service\":") + if v.Service == nil { + w.RawString("null") + } else { + v.Service.MarshalFastJSON(w) + } + w.RawString(",\"transactions\":") + if v.Transactions == nil { + w.RawString("null") + } else { + w.RawByte('[') + for i, v := range v.Transactions { + if i != 0 { + w.RawByte(',') + } + v.MarshalFastJSON(w) + } + w.RawByte(']') + } + if v.Process != nil { + w.RawString(",\"process\":") + v.Process.MarshalFastJSON(w) + } + if v.System != nil { + w.RawString(",\"system\":") + v.System.MarshalFastJSON(w) + } + w.RawByte('}') +} + +func (v *ErrorsPayload) MarshalFastJSON(w *fastjson.Writer) { + w.RawByte('{') + w.RawString("\"errors\":") + if v.Errors == nil { + w.RawString("null") + } else { + w.RawByte('[') + for i, v := range v.Errors { + if i != 0 { + w.RawByte(',') + } + v.MarshalFastJSON(w) + } + w.RawByte(']') + } + w.RawString(",\"service\":") + if v.Service == nil { + w.RawString("null") + } else { + v.Service.MarshalFastJSON(w) + } + if v.Process != nil { + w.RawString(",\"process\":") + v.Process.MarshalFastJSON(w) + } + if v.System != nil { + w.RawString(",\"system\":") + v.System.MarshalFastJSON(w) + } + w.RawByte('}') +} diff --git a/vendor/github.com/elastic/apm-agent-go/model/model.go b/vendor/github.com/elastic/apm-agent-go/model/model.go new file mode 100644 index 00000000000..b962e07031c --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/model/model.go @@ -0,0 +1,493 @@ +package model + +import ( + "net/http" + "net/url" + "time" +) + +// Service represents the service handling transactions being traced. +type Service struct { + // Name is the immutable name of the service. + Name string `json:"name"` + + // Version is the version of the service, if it has one. + Version string `json:"version,omitempty"` + + // Environment is the name of the service's environment, if it has + // one, e.g. "production" or "staging". + Environment string `json:"environment,omitempty"` + + // Agent holds information about the Elastic APM agent tracing this + // service's transactions. + Agent Agent `json:"agent"` + + // Framework holds information about the service's framework, if any. + Framework *Framework `json:"framework,omitempty"` + + // Language holds information about the programming language in which + // the service is written. + Language *Language `json:"language,omitempty"` + + // Runtime holds information about the programming language runtime + // running this service. + Runtime *Runtime `json:"runtime,omitempty"` +} + +// Agent holds information about the Elastic APM agent. +type Agent struct { + // Name is the name of the Elastic APM agent, e.g. "Go". + Name string `json:"name"` + + // Version is the version of the Elastic APM agent, e.g. "1.0.0". + Version string `json:"version"` +} + +// Framework holds information about the framework (typically web) +// used by the service. +type Framework struct { + // Name is the name of the framework. + Name string `json:"name"` + + // Version is the version of the framework. + Version string `json:"version"` +} + +// Language holds information about the programming language used. +type Language struct { + // Name is the name of the programming language. + Name string `json:"name"` + + // Version is the version of the programming language. + Version string `json:"version,omitempty"` +} + +// Runtime holds information about the programming language runtime. +type Runtime struct { + // Name is the name of the programming language runtime. + Name string `json:"name"` + + // Version is the version of the programming language runtime. + Version string `json:"version"` +} + +// System represents the system (operating system and machine) running the +// service. +type System struct { + // Architecture is the system's hardware architecture. + Architecture string `json:"architecture,omitempty"` + + // Hostname is the system's hostname. + Hostname string `json:"hostname,omitempty"` + + // Platform is the system's platform, or operating system name. + Platform string `json:"platform,omitempty"` +} + +// Process represents an operating system process. +type Process struct { + // Pid is the process ID. + Pid int `json:"pid"` + + // Ppid is the parent process ID, if known. + Ppid *int `json:"ppid,omitempty"` + + // Title is the title of the process. + Title string `json:"title,omitempty"` + + // Argv holds the command line arguments used to start the process. + Argv []string `json:"argv,omitempty"` +} + +// Transaction represents a transaction handled by the service. +type Transaction struct { + // ID holds the hex-formatted UUID of the transaction. + ID UUID `json:"id"` + + // Name holds the name of the transaction. + Name string `json:"name"` + + // Type identifies the service-domain specific type of the request, + // e.g. "request" or "backgroundjob". + Type string `json:"type"` + + // Timestamp holds the time at which the transaction started. + Timestamp Time `json:"timestamp"` + + // Duration records how long the transaction took to complete, + // in milliseconds. + Duration float64 `json:"duration"` + + // Result holds the result of the transaction, e.g. the status code + // for HTTP requests. + Result string `json:"result,omitempty"` + + // Context holds contextual information relating to the transaction. + Context *Context `json:"context,omitempty"` + + // Sampled indicates that the transaction was sampled, and + // includes all available information. Non-sampled transactions + // omit Context and Spans. + // + // If Sampled is unspecified (nil), it is equivalent to setting + // it to true. + Sampled *bool `json:"sampled,omitempty"` + + // SpanCount holds statistics on spans within a transaction. + SpanCount SpanCount `json:"span_count,omitempty"` + + // Spans holds the transaction's spans. + Spans []Span `json:"spans,omitempty"` +} + +// SpanCount holds statistics on spans within a transaction. +type SpanCount struct { + // Dropped holds statistics on dropped spans within a transaction. + Dropped SpanCountDropped `json:"dropped,omitempty"` +} + +// SpanCountDropped holds statistics on dropped spans. +type SpanCountDropped struct { + // Total holds the total number of spans dropped by the + // agent within a transaction. + Total int `json:"total"` +} + +// Span represents a span within a transaction. +type Span struct { + // Name holds the name of the span. + Name string `json:"name"` + + // Start is the start time of the span, in milliseconds relative to + // the containing transaction's timestamp. + Start float64 `json:"start"` + + // Duration holds the duration of the span, in milliseconds. + Duration float64 `json:"duration"` + + // Type identifies the service-domain specific type of the span, + // e.g. "db.postgresql.query". + Type string `json:"type"` + + // ID holds an identifier for the span, unique within its + // containing transaction. + ID *int64 `json:"id,omitempty"` + + // Parent holds the identifier of the parent span, if any. + Parent *int64 `json:"parent,omitempty"` + + // Context holds contextual information relating to the span. + Context *SpanContext `json:"context,omitempty"` + + // Stacktrace holds stack frames corresponding to the span. + Stacktrace []StacktraceFrame `json:"stacktrace,omitempty"` +} + +// SpanContext holds contextual information relating to the span. +type SpanContext struct { + // Database holds contextual information for database + // operation spans. + Database *DatabaseSpanContext `json:"db,omitempty"` +} + +// DatabaseSpanContext holds contextual information for database +// operation spans. +type DatabaseSpanContext struct { + // Instance holds the database instance name. + Instance string `json:"instance,omitempty"` + + // Statement holds the database statement (e.g. query). + Statement string `json:"statement,omitempty"` + + // Type holds the database type. For any SQL database, + // this should be "sql"; for others, the lower-cased + // database category, e.g. "cassandra", "hbase", "redis". + Type string `json:"type,omitempty"` + + // User holds the username used for database access. + User string `json:"user,omitempty"` +} + +// Context holds contextual information relating to a transaction or error. +type Context struct { + // Request holds details of the HTTP request relating to the + // transaction or error, if relevant. + Request *Request `json:"request,omitempty"` + + // Response holds details of the HTTP response relating to the + // transaction or error, if relevant. + Response *Response `json:"response,omitempty"` + + // User holds details of the authenticated user relating to the + // transaction or error, if relevant. + User *User `json:"user,omitempty"` + + // Custom holds arbitrary additional metadata. + Custom IfaceMap `json:"custom,omitempty"` + + // Tags holds user-defined key/value pairs. + Tags map[string]string `json:"tags,omitempty"` +} + +// User holds information about an authenticated user. +type User struct { + // Username holds the username of the user. + Username string `json:"username,omitempty"` + + // ID identifies the user, e.g. a primary key. This may be + // a string or number. + ID UserID `json:"id,omitempty"` + + // Email holds the email address of the user. + Email string `json:"email,omitempty"` +} + +// UserID represents a user ID as either a number or a string. +type UserID struct { + String string + Number float64 +} + +// Error represents an error occurring in the service. +type Error struct { + // Timestamp holds the time at which the error occurred. + Timestamp Time `json:"timestamp"` + + // ID holds a hex-formatted UUID for the error. + ID string `json:"id,omitempty"` + + // TransactionID holds the UUID of the transaction to which + // this error relates, if any. + Transaction ErrorTransaction `json:"transaction,omitempty"` + + // Culprit holds the name of the function which + // produced the error. + Culprit string `json:"culprit,omitempty"` + + // Context holds contextual information relating to the error. + Context *Context `json:"context,omitempty"` + + // Exception holds details of the exception (error or panic) + // to which this error relates. + Exception Exception `json:"exception,omitempty"` + + // Log holds additional information added when logging the error. + Log Log `json:"log,omitempty"` +} + +// ErrorTransaction identifies the transaction within which the error occurred. +type ErrorTransaction struct { + // ID is the UUID of the transaction. + ID UUID `json:"id"` +} + +// Exception represents an exception: an error or panic. +type Exception struct { + // Message holds the error message. + Message string `json:"message"` + + // Code holds the error code. This may be a number or a string. + Code ExceptionCode `json:"code,omitempty"` + + // Type holds the type of the exception. + Type string `json:"type,omitempty"` + + // Module holds the exception type's module namespace. + Module string `json:"module,omitempty"` + + // Attributes holds arbitrary exception-type specific attributes. + Attributes map[string]interface{} `json:"attributes,omitempty"` + + // Stacktrace holds stack frames corresponding to the exception. + Stacktrace []StacktraceFrame `json:"stacktrace,omitempty"` + + // Handled indicates whether or not the error was caught and handled. + Handled bool `json:"handled"` +} + +// ExceptionCode represents an exception code as either a number or a string. +type ExceptionCode struct { + String string + Number float64 +} + +// StacktraceFrame describes a stack frame. +type StacktraceFrame struct { + // AbsolutePath holds the absolute path of the source file for the + // stack frame. + AbsolutePath string `json:"abs_path,omitempty"` + + // File holds the base filename of the source file for the stack frame. + File string `json:"filename"` + + // Line holds the line number of the source for the stack frame. + Line int `json:"lineno"` + + // Column holds the column number of the source for the stack frame. + Column *int `json:"colno,omitempty"` + + // Module holds the module to which the frame belongs. For Go, we + // use the package path (e.g. "net/http"). + Module string `json:"module,omitempty"` + + // Function holds the name of the function to which the frame belongs. + Function string `json:"function,omitempty"` + + // LibraryFrame indicates whether or not the frame corresponds to + // library or user code. + LibraryFrame bool `json:"library_frame,omitempty"` + + // ContextLine holds the line of source code to which the frame + // corresponds. + ContextLine string `json:"context_line,omitempty"` + + // PreContext holds zero or more lines of source code preceding the + // line corresponding to the frame. + PreContext []string `json:"pre_context,omitempty"` + + // PostContext holds zero or more lines of source code proceeding the + // line corresponding to the frame. + PostContext []string `json:"post_context,omitempty"` + + // Vars holds local variables for this stack frame. + Vars map[string]interface{} `json:"vars,omitempty"` +} + +// Log holds additional information added when logging an error. +type Log struct { + // Message holds the logged error message. + Message string `json:"message"` + + // Level holds the severity of the log record. + Level string `json:"level,omitempty"` + + // LoggerName holds the name of the logger used. + LoggerName string `json:"logger_name,omitempty"` + + // ParamMessage holds a parameterized message, e.g. + // "Could not connect to %s". The string is not interpreted, + // but may be used for grouping errors. + ParamMessage string `json:"param_message,omitempty"` + + // Stacktrace holds stack frames corresponding to the error. + Stacktrace []StacktraceFrame `json:"stacktrace,omitempty"` +} + +// Request represents an HTTP request. +type Request struct { + // URL is the request URL. + URL URL `json:"url"` + + // Method holds the HTTP request method. + Method string `json:"method"` + + // Headers holds the request headers. + Headers *RequestHeaders `json:"headers,omitempty"` + + // Body holds the request body, if body capture is enabled. + Body *RequestBody `json:"body,omitempty"` + + // HTTPVersion holds the HTTP version of the request. + HTTPVersion string `json:"http_version,omitempty"` + + // Cookies holds the parsed cookies. + Cookies Cookies `json:"cookies,omitempty"` + + // Env holds environment information passed from the + // web framework to the request handler. + Env map[string]string `json:"env,omitempty"` + + // Socket holds transport-level information. + Socket *RequestSocket `json:"socket,omitempty"` +} + +// Cookies holds a collection of HTTP cookies. +type Cookies []*http.Cookie + +// RequestBody holds a request body. +// +// Exactly one of Raw or Form must be set. +type RequestBody struct { + // Raw holds the raw body content. + Raw string + + // Form holds the form data from POST, PATCH, or PUT body parameters. + Form url.Values +} + +// RequestHeaders holds a limited subset of HTTP request headers. +type RequestHeaders struct { + // ContentType holds the content-type header. + ContentType string `json:"content-type,omitempty"` + + // Cookie holds the cookies sent with the request, + // delimited by semi-colons. + Cookie string `json:"cookie,omitempty"` + + // UserAgent holds the user-agent header. + UserAgent string `json:"user-agent,omitempty"` +} + +// RequestSocket holds transport-level information relating to an HTTP request. +type RequestSocket struct { + // Encrypted indicates whether or not the request was sent + // as an SSL/HTTPS request. + Encrypted bool `json:"encrypted,omitempty"` + + // RemoteAddress holds the remote address for the request. + RemoteAddress string `json:"remote_address,omitempty"` +} + +// URL represents a request URL. +type URL struct { + // Full is the full URL, e.g. + // "https://example.com:443/search/?q=elasticsearch#top". + Full string `json:"full,omitempty"` + + // Protocol is the scheme of the URL, e.g. "https". + Protocol string `json:"protocol,omitempty"` + + // Hostname is the hostname for the URL, e.g. "example.com". + Hostname string `json:"hostname,omitempty"` + + // Port is the port number in the URL, e.g. "443". + Port string `json:"port,omitempty"` + + // Path is the path of the URL, e.g. "/search". + Path string `json:"pathname,omitempty"` + + // Search is the query string of the URL, e.g. "q=elasticsearch". + Search string `json:"search,omitempty"` + + // Hash is the fragment for references, e.g. "top" in the + // URL example provided for Full. + Hash string `json:"hash,omitempty"` +} + +// Response represents an HTTP response. +type Response struct { + // StatusCode holds the HTTP response status code. + StatusCode int `json:"status_code,omitempty"` + + // Headers holds the response headers. + Headers *ResponseHeaders `json:"headers,omitempty"` + + // HeadersSent indicates whether or not headers were sent + // to the client. + HeadersSent *bool `json:"headers_sent,omitempty"` + + // Finished indicates whether or not the response was finished. + Finished *bool `json:"finished,omitempty"` +} + +// ResponseHeaders holds a limited subset of HTTP respponse headers. +type ResponseHeaders struct { + // ContentType holds the content-type header. + ContentType string `json:"content-type,omitempty"` +} + +// Time is a timestamp, formatted as "YYYY-MM-DDTHH:mm:ss.sssZ". +type Time time.Time + +// UUID holds a 128-bit UUID. +type UUID [16]byte diff --git a/vendor/github.com/elastic/apm-agent-go/model/payload.go b/vendor/github.com/elastic/apm-agent-go/model/payload.go new file mode 100644 index 00000000000..cb7438cd81a --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/model/payload.go @@ -0,0 +1,23 @@ +package model + +// TransactionsPayload defines the payload structure expected +// by the transactions intake API. +// +// https://www.elastic.co/guide/en/apm/server/current/transaction-api.html +type TransactionsPayload struct { + Service *Service `json:"service"` + Process *Process `json:"process,omitempty"` + System *System `json:"system,omitempty"` + Transactions []Transaction `json:"transactions"` +} + +// ErrorsPayload defines the payload structure expected +// by the errors intake API. +// +// https://www.elastic.co/guide/en/apm/server/current/error-api.html +type ErrorsPayload struct { + Service *Service `json:"service"` + Process *Process `json:"process,omitempty"` + System *System `json:"system,omitempty"` + Errors []*Error `json:"errors"` +} diff --git a/vendor/github.com/elastic/apm-agent-go/module/apmhttp/client.go b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/client.go new file mode 100644 index 00000000000..7ed86cb19bd --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/client.go @@ -0,0 +1,73 @@ +package apmhttp + +import ( + "net/http" + + "github.com/elastic/apm-agent-go" +) + +// WrapClient returns a new *http.Client with all fields copied +// across, and the Transport field wrapped with WrapRoundTripper +// such that client requests are reported as spans to Elastic APM +// if their context contains a sampled transaction. +// +// If c is nil, then http.DefaultClient is wrapped. +func WrapClient(c *http.Client, o ...ClientOption) *http.Client { + if c == nil { + c = http.DefaultClient + } + copied := *c + copied.Transport = WrapRoundTripper(copied.Transport, o...) + return &copied +} + +// WrapRoundTripper returns an http.RoundTripper wrapping r, reporting each +// request as a span to Elastic APM, if the request's context contains a +// sampled transaction. +// +// If r is nil, then http.DefaultTransport is wrapped. +func WrapRoundTripper(r http.RoundTripper, o ...ClientOption) http.RoundTripper { + if r == nil { + r = http.DefaultTransport + } + rt := &roundTripper{ + r: r, + requestName: ClientRequestName, + requestIgnorer: ignoreNone, + } + for _, o := range o { + o(rt) + } + return rt +} + +type roundTripper struct { + r http.RoundTripper + requestName RequestNameFunc + requestIgnorer RequestIgnorerFunc +} + +// RoundTrip delegates to r.r, emitting a span if req's context +// contains a transaction. +func (r *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + if r.requestIgnorer(req) { + return r.r.RoundTrip(req) + } + ctx := req.Context() + tx := elasticapm.TransactionFromContext(ctx) + if tx == nil || !tx.Sampled() { + return r.r.RoundTrip(req) + } + + name := r.requestName(req) + spanType := "ext.http" + span := tx.StartSpan(name, spanType, elasticapm.SpanFromContext(ctx)) + defer span.End() + + ctx = elasticapm.ContextWithSpan(ctx, span) + req = RequestWithContext(ctx, req) + return r.r.RoundTrip(req) +} + +// ClientOption sets options for tracing client requests. +type ClientOption func(*roundTripper) diff --git a/vendor/github.com/elastic/apm-agent-go/module/apmhttp/context.go b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/context.go new file mode 100644 index 00000000000..12010deb3b0 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/context.go @@ -0,0 +1,23 @@ +package apmhttp + +import ( + "fmt" +) + +var standardStatusCodeResults = [...]string{ + "HTTP 1xx", + "HTTP 2xx", + "HTTP 3xx", + "HTTP 4xx", + "HTTP 5xx", +} + +// StatusCodeResult returns the transaction result value to use for the given +// status code. +func StatusCodeResult(statusCode int) string { + switch i := statusCode / 100; i { + case 1, 2, 3, 4, 5: + return standardStatusCodeResults[i-1] + } + return fmt.Sprintf("HTTP %d", statusCode) +} diff --git a/vendor/github.com/elastic/apm-agent-go/module/apmhttp/doc.go b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/doc.go new file mode 100644 index 00000000000..c38fd5bf942 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/doc.go @@ -0,0 +1,3 @@ +// Package apmhttp provides a tracing middleware http.Handler for +// servers, and a tracing http.RoundTripper for clients. +package apmhttp diff --git a/vendor/github.com/elastic/apm-agent-go/module/apmhttp/handler.go b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/handler.go new file mode 100644 index 00000000000..a2faee32e73 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/handler.go @@ -0,0 +1,284 @@ +package apmhttp + +import ( + "context" + "net/http" + + "github.com/elastic/apm-agent-go" +) + +// Wrap returns an http.Handler wrapping h, reporting each request as +// a transaction to Elastic APM. +// +// By default, the returned Handler will use elasticapm.DefaultTracer. +// Use WithTracer to specify an alternative tracer. +// +// By default, the returned Handler will recover panics, reporting +// them to the configured tracer. To override this behaviour, use +// WithRecovery. +func Wrap(h http.Handler, o ...ServerOption) http.Handler { + if h == nil { + panic("h == nil") + } + handler := &handler{ + handler: h, + tracer: elasticapm.DefaultTracer, + requestName: ServerRequestName, + requestIgnorer: ignoreNone, + } + for _, o := range o { + o(handler) + } + if handler.recovery == nil { + handler.recovery = NewTraceRecovery(handler.tracer) + } + return handler +} + +// handler wraps an http.Handler, reporting a new transaction for each request. +// +// The http.Request's context will be updated with the transaction. +type handler struct { + handler http.Handler + tracer *elasticapm.Tracer + recovery RecoveryFunc + requestName RequestNameFunc + requestIgnorer RequestIgnorerFunc +} + +// ServeHTTP delegates to h.Handler, tracing the transaction with +// h.Tracer, or elasticapm.DefaultTracer if h.Tracer is nil. +func (h *handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + if !h.tracer.Active() || h.requestIgnorer(req) { + h.handler.ServeHTTP(w, req) + return + } + tx := h.tracer.StartTransaction(h.requestName(req), "request") + ctx := elasticapm.ContextWithTransaction(req.Context(), tx) + req = RequestWithContext(ctx, req) + defer tx.End() + + finished := false + body := h.tracer.CaptureHTTPRequestBody(req) + w, resp := WrapResponseWriter(w) + defer func() { + if v := recover(); v != nil { + h.recovery(w, req, body, tx, v) + finished = true + } + SetTransactionContext(tx, req, resp, body, finished) + }() + h.handler.ServeHTTP(w, req) + finished = true +} + +// SetTransactionContext sets tx.Result and, if the transaction is being +// sampled, sets tx.Context with information from req, resp, and finished. +// +// The finished property indicates that the response was not completely +// written, e.g. because the handler panicked and we did not recover the +// panic. +func SetTransactionContext(tx *elasticapm.Transaction, req *http.Request, resp *Response, body *elasticapm.BodyCapturer, finished bool) { + tx.Result = StatusCodeResult(resp.StatusCode) + if !tx.Sampled() { + return + } + tx.Context.SetHTTPRequest(req) + tx.Context.SetHTTPRequestBody(body) + tx.Context.SetHTTPStatusCode(resp.StatusCode) + tx.Context.SetHTTPResponseHeaders(resp.Headers) + + if finished { + // Responses are always "finished" unless the handler panics + // and it is not recovered. Since we can't tell whether a panic + // will be recovered up the stack (but before reaching the + // net/http server code), we omit the Finished context if we + // don't know for sure it finished. + tx.Context.SetHTTPResponseFinished(finished) + } + if resp.HeadersWritten || len(resp.Headers) != 0 { + // We only set headers_sent if we know for sure + // that headers have been sent. Otherwise we + // leave it to indicate that we don't know. + tx.Context.SetHTTPResponseHeadersSent(resp.HeadersWritten) + } +} + +// WrapResponseWriter wraps an http.ResponseWriter and returns the wrapped +// value along with a *Response which will be filled in when the handler +// is called. The *Response value must not be inspected until after the +// request has been handled, to avoid data races. +// +// The returned http.ResponseWriter implements http.Pusher and http.Hijacker +// if and only if the provided http.ResponseWriter does. +func WrapResponseWriter(w http.ResponseWriter) (http.ResponseWriter, *Response) { + rw := responseWriter{ + ResponseWriter: w, + resp: Response{ + StatusCode: http.StatusOK, + Headers: w.Header(), + }, + } + h, _ := w.(http.Hijacker) + p, _ := w.(http.Pusher) + switch { + case h != nil && p != nil: + rwhp := &responseWriterHijackerPusher{ + responseWriter: rw, + Hijacker: h, + Pusher: p, + } + return rwhp, &rwhp.resp + case h != nil: + rwh := &responseWriterHijacker{ + responseWriter: rw, + Hijacker: h, + } + return rwh, &rwh.resp + case p != nil: + rwp := &responseWriterPusher{ + responseWriter: rw, + Pusher: p, + } + return rwp, &rwp.resp + } + return &rw, &rw.resp +} + +// Response records details of the HTTP response. +type Response struct { + // StatusCode records the HTTP status code set via WriteHeader. + StatusCode int + + // Headers holds the headers set in the ResponseWriter. + Headers http.Header + + // HeadersWritten records whether or not headers were written. + HeadersWritten bool +} + +type responseWriter struct { + http.ResponseWriter + resp Response +} + +// WriteHeader sets w.resp.StatusCode, and w.resp.HeadersWritten if there +// are any headers set on the ResponseWriter, and calls through to the +// embedded ResponseWriter. +func (w *responseWriter) WriteHeader(statusCode int) { + w.ResponseWriter.WriteHeader(statusCode) + w.resp.StatusCode = statusCode + w.resp.HeadersWritten = len(w.ResponseWriter.Header()) != 0 +} + +// Write sets w.resp.HeadersWritten if there are any headers set on the +// ResponseWriter, and calls through to the embedded ResponseWriter. +func (w *responseWriter) Write(data []byte) (int, error) { + n, err := w.ResponseWriter.Write(data) + w.resp.HeadersWritten = len(w.ResponseWriter.Header()) != 0 + return n, err +} + +// CloseNotify returns w.closeNotify() if w.closeNotify is non-nil, +// otherwise it returns nil. +func (w *responseWriter) CloseNotify() <-chan bool { + if closeNotifier, ok := w.ResponseWriter.(http.CloseNotifier); ok { + return closeNotifier.CloseNotify() + } + return nil +} + +// Flush calls w.flush() if w.flush is non-nil, otherwise +// it does nothing. +func (w *responseWriter) Flush() { + if flusher, ok := w.ResponseWriter.(http.Flusher); ok { + flusher.Flush() + } +} + +type responseWriterHijacker struct { + responseWriter + http.Hijacker +} + +type responseWriterPusher struct { + responseWriter + http.Pusher +} + +type responseWriterHijackerPusher struct { + responseWriter + http.Hijacker + http.Pusher +} + +func ignoreNone(*http.Request) bool { + return false +} + +// ServerOption sets options for tracing server requests. +type ServerOption func(*handler) + +// WithTracer returns a ServerOption which sets t as the tracer +// to use for tracing server requests. +func WithTracer(t *elasticapm.Tracer) ServerOption { + if t == nil { + panic("t == nil") + } + return func(h *handler) { + h.tracer = t + } +} + +// WithRecovery returns a ServerOption which sets r as the recovery +// function to use for tracing server requests. +func WithRecovery(r RecoveryFunc) ServerOption { + if r == nil { + panic("r == nil") + } + return func(h *handler) { + h.recovery = r + } +} + +// RequestNameFunc is the type of a function for use in +// WithServerRequestName. +type RequestNameFunc func(*http.Request) string + +// WithServerRequestName returns a ServerOption which sets r as the function +// to use to obtain the transaction name for the given server request. +func WithServerRequestName(r RequestNameFunc) ServerOption { + if r == nil { + panic("r == nil") + } + return func(h *handler) { + h.requestName = r + } +} + +// RequestIgnorerFunc is the type of a function for use in +// WithServerRequestIgnorer. +type RequestIgnorerFunc func(*http.Request) bool + +// WithServerRequestIgnorer returns a ServerOption which sets r as the +// function to use to determine whether or not a server request should +// be ignored. If r is nil, all requests will be reported. +func WithServerRequestIgnorer(r RequestIgnorerFunc) ServerOption { + if r == nil { + r = ignoreNone + } + return func(h *handler) { + h.requestIgnorer = r + } +} + +// RequestWithContext is equivalent to req.WithContext, except that the URL +// pointer is copied, rather than the contents. +func RequestWithContext(ctx context.Context, req *http.Request) *http.Request { + url := req.URL + req.URL = nil + reqCopy := req.WithContext(ctx) + reqCopy.URL = url + req.URL = url + return reqCopy +} diff --git a/vendor/github.com/elastic/apm-agent-go/module/apmhttp/recovery.go b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/recovery.go new file mode 100644 index 00000000000..85ae79eaaa3 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/recovery.go @@ -0,0 +1,39 @@ +package apmhttp + +import ( + "net/http" + + "github.com/elastic/apm-agent-go" +) + +// RecoveryFunc is the type of a function for use in WithRecovery. +type RecoveryFunc func( + w http.ResponseWriter, + req *http.Request, + body *elasticapm.BodyCapturer, + tx *elasticapm.Transaction, + recovered interface{}, +) + +// NewTraceRecovery returns a RecoveryFunc for use in WithRecovery. +// +// The returned RecoveryFunc will report recovered error to Elastic APM +// using the given Tracer, or elasticapm.DefaultTracer if t is nil. The +// error will be linked to the given transaction. +func NewTraceRecovery(t *elasticapm.Tracer) RecoveryFunc { + if t == nil { + t = elasticapm.DefaultTracer + } + return func( + w http.ResponseWriter, + req *http.Request, + body *elasticapm.BodyCapturer, + tx *elasticapm.Transaction, + recovered interface{}, + ) { + e := t.Recovered(recovered, tx) + e.Context.SetHTTPRequest(req) + e.Context.SetHTTPRequestBody(body) + e.Send() + } +} diff --git a/vendor/github.com/elastic/apm-agent-go/module/apmhttp/requestname.go b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/requestname.go new file mode 100644 index 00000000000..7dd07e52b83 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/requestname.go @@ -0,0 +1,28 @@ +// +build go1.10 + +package apmhttp + +import ( + "net/http" + "strings" +) + +// ServerRequestName returns the transaction name for the server request, req. +func ServerRequestName(req *http.Request) string { + var b strings.Builder + b.Grow(len(req.Method) + len(req.URL.Path) + 1) + b.WriteString(req.Method) + b.WriteByte(' ') + b.WriteString(req.URL.Path) + return b.String() +} + +// ClientRequestName returns the span name for the client request, req. +func ClientRequestName(req *http.Request) string { + var b strings.Builder + b.Grow(len(req.Method) + len(req.URL.Host) + 1) + b.WriteString(req.Method) + b.WriteByte(' ') + b.WriteString(req.URL.Host) + return b.String() +} diff --git a/vendor/github.com/elastic/apm-agent-go/module/apmhttp/requestname_go19.go b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/requestname_go19.go new file mode 100644 index 00000000000..cfd8121de54 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/module/apmhttp/requestname_go19.go @@ -0,0 +1,23 @@ +// +build !go1.10 + +package apmhttp + +import "net/http" + +// ServerRequestName returns the transaction name for the server request, req. +func ServerRequestName(req *http.Request) string { + buf := make([]byte, len(req.Method)+len(req.URL.Path)+1) + n := copy(buf, req.Method) + buf[n] = ' ' + copy(buf[n+1:], req.URL.Path) + return string(buf) +} + +// ClientRequestName returns the span name for the client request, req. +func ClientRequestName(req *http.Request) string { + buf := make([]byte, len(req.Method)+len(req.URL.Host)+1) + n := copy(buf, req.Method) + buf[n] = ' ' + copy(buf[n+1:], req.URL.Host) + return string(buf) +} diff --git a/vendor/github.com/elastic/apm-agent-go/sampler.go b/vendor/github.com/elastic/apm-agent-go/sampler.go new file mode 100644 index 00000000000..2d45a5c0568 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/sampler.go @@ -0,0 +1,55 @@ +package elasticapm + +import ( + "math/rand" + "sync" + + "github.com/pkg/errors" +) + +// Sampler provides a means of sampling transactions. +type Sampler interface { + // Sample indicates whether or not the transaction + // should be sampled. This method will be invoked + // by calls to Tracer.StartTransaction, so it must + // be goroutine-safe, and should avoid synchronization + // as far as possible. + Sample(*Transaction) bool +} + +// RatioSampler is a Sampler that samples probabilistically +// based on the given ratio within the range [0,1.0]. +// +// A ratio of 1.0 samples 100% of transactions, a ratio of 0.5 +// samples ~50%, and so on. +type RatioSampler struct { + mu sync.Mutex + rng *rand.Rand + r float64 +} + +// NewRatioSampler returns a new RatioSampler with the given ratio +// and math/rand.Source. The source will be called from multiple +// goroutines, but the sampler will internally synchronise access +// to it; the source should not be used by any other goroutines. +// +// If the ratio provided does not lie within the range [0,1.0], +// NewRatioSampler will panic. +func NewRatioSampler(r float64, source rand.Source) *RatioSampler { + if r < 0 || r > 1.0 { + panic(errors.Errorf("ratio %v out of range [0,1.0]", r)) + } + return &RatioSampler{ + rng: rand.New(source), + r: r, + } +} + +// Sample samples the transaction according to the configured +// ratio and pseudo-random source. +func (s *RatioSampler) Sample(*Transaction) bool { + s.mu.Lock() + v := s.rng.Float64() + s.mu.Unlock() + return s.r > v +} diff --git a/vendor/github.com/elastic/apm-agent-go/sanitizer.go b/vendor/github.com/elastic/apm-agent-go/sanitizer.go new file mode 100644 index 00000000000..da797b73d7d --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/sanitizer.go @@ -0,0 +1,44 @@ +package elasticapm + +import ( + "bytes" + "regexp" + + "github.com/elastic/apm-agent-go/model" +) + +const redacted = "[REDACTED]" + +// sanitizeRequest sanitizes HTTP request data, redacting +// the values of cookies and forms whose corresponding keys +// match the given regular expression. +func sanitizeRequest(r *model.Request, re *regexp.Regexp) { + var anyCookiesRedacted bool + for _, c := range r.Cookies { + if !re.MatchString(c.Name) { + continue + } + c.Value = redacted + anyCookiesRedacted = true + } + if anyCookiesRedacted && r.Headers != nil { + var b bytes.Buffer + for i, c := range r.Cookies { + if i != 0 { + b.WriteRune(';') + } + b.WriteString(c.String()) + } + r.Headers.Cookie = b.String() + } + if r.Body != nil && r.Body.Form != nil { + for key, values := range r.Body.Form { + if !re.MatchString(key) { + continue + } + for i := range values { + values[i] = redacted + } + } + } +} diff --git a/vendor/github.com/elastic/apm-agent-go/sender.go b/vendor/github.com/elastic/apm-agent-go/sender.go new file mode 100644 index 00000000000..4a5cddd6413 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/sender.go @@ -0,0 +1,143 @@ +package elasticapm + +import ( + "context" + + "github.com/elastic/apm-agent-go/model" + "github.com/elastic/apm-agent-go/stacktrace" +) + +type sender struct { + tracer *Tracer + cfg *tracerConfig + stats *TracerStats + + modelTransactions []model.Transaction + modelSpans []model.Span + modelStacktrace []model.StacktraceFrame +} + +// sendTransactions attempts to send enqueued transactions to the APM server, +// returning true if the transactions were successfully sent. +func (s *sender) sendTransactions(ctx context.Context, transactions []*Transaction) bool { + if len(transactions) == 0 { + return false + } + + s.modelTransactions = s.modelTransactions[:0] + s.modelSpans = s.modelSpans[:0] + s.modelStacktrace = s.modelStacktrace[:0] + var spanOffset int + var stacktraceOffset int + + for _, tx := range transactions { + s.modelTransactions = append(s.modelTransactions, model.Transaction{ + Name: truncateString(tx.Name), + Type: truncateString(tx.Type), + ID: tx.id, + Result: truncateString(tx.Result), + Timestamp: model.Time(tx.Timestamp.UTC()), + Duration: tx.Duration.Seconds() * 1000, + SpanCount: model.SpanCount{ + Dropped: model.SpanCountDropped{ + Total: tx.spansDropped, + }, + }, + }) + modelTx := &s.modelTransactions[len(s.modelTransactions)-1] + if tx.Sampled() { + modelTx.Context = tx.Context.build() + if s.cfg.sanitizedFieldNames != nil && modelTx.Context != nil && modelTx.Context.Request != nil { + sanitizeRequest(modelTx.Context.Request, s.cfg.sanitizedFieldNames) + } + for _, span := range tx.spans { + s.modelSpans = append(s.modelSpans, model.Span{ + ID: &span.id, + Name: truncateString(span.Name), + Type: truncateString(span.Type), + Start: span.Timestamp.Sub(tx.Timestamp).Seconds() * 1000, + Duration: span.Duration.Seconds() * 1000, + Context: span.Context.build(), + }) + modelSpan := &s.modelSpans[len(s.modelSpans)-1] + if span.parent != -1 { + modelSpan.Parent = &span.parent + } + s.modelStacktrace = appendModelStacktraceFrames(s.modelStacktrace, span.stacktrace) + modelSpan.Stacktrace = s.modelStacktrace[stacktraceOffset:] + stacktraceOffset += len(span.stacktrace) + s.setStacktraceContext(modelSpan.Stacktrace) + } + modelTx.Spans = s.modelSpans[spanOffset:] + spanOffset += len(tx.spans) + } else { + modelTx.Sampled = &tx.sampled + } + } + + service := makeService(s.tracer.Service.Name, s.tracer.Service.Version, s.tracer.Service.Environment) + payload := model.TransactionsPayload{ + Service: &service, + Process: s.tracer.process, + System: s.tracer.system, + Transactions: s.modelTransactions, + } + + if err := s.tracer.Transport.SendTransactions(ctx, &payload); err != nil { + if s.cfg.logger != nil { + s.cfg.logger.Debugf("sending transactions failed: %s", err) + } + s.stats.Errors.SendTransactions++ + return false + } + s.stats.TransactionsSent += uint64(len(transactions)) + return true +} + +// sendErrors attempts to send enqueued errors to the APM server, +// returning true if the errors were successfully sent. +func (s *sender) sendErrors(ctx context.Context, errors []*Error) bool { + if len(errors) == 0 { + return false + } + service := makeService(s.tracer.Service.Name, s.tracer.Service.Version, s.tracer.Service.Environment) + payload := model.ErrorsPayload{ + Service: &service, + Process: s.tracer.process, + System: s.tracer.system, + Errors: make([]*model.Error, len(errors)), + } + for i, e := range errors { + if e.Transaction != nil { + e.model.Transaction.ID = e.Transaction.id + } + s.setStacktraceContext(e.modelStacktrace) + e.setStacktrace() + e.setCulprit() + e.model.ID = e.ID + e.model.Timestamp = model.Time(e.Timestamp.UTC()) + e.model.Context = e.Context.build() + e.model.Exception.Handled = e.Handled + payload.Errors[i] = &e.model + } + if err := s.tracer.Transport.SendErrors(ctx, &payload); err != nil { + if s.cfg.logger != nil { + s.cfg.logger.Debugf("sending errors failed: %s", err) + } + s.stats.Errors.SendErrors++ + return false + } + s.stats.ErrorsSent += uint64(len(errors)) + return true +} + +func (s *sender) setStacktraceContext(stack []model.StacktraceFrame) { + if s.cfg.contextSetter == nil || len(stack) == 0 { + return + } + err := stacktrace.SetContext(s.cfg.contextSetter, stack, s.cfg.preContext, s.cfg.postContext) + if s.cfg.logger != nil { + s.cfg.logger.Debugf("setting context failed: %s", err) + } + s.stats.Errors.SetContext++ +} diff --git a/vendor/github.com/elastic/apm-agent-go/span.go b/vendor/github.com/elastic/apm-agent-go/span.go new file mode 100644 index 00000000000..f852d326bd2 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/span.go @@ -0,0 +1,137 @@ +package elasticapm + +import ( + "sync" + "time" + + "github.com/elastic/apm-agent-go/stacktrace" +) + +// droppedSpanPool holds *Spans which are used when the span +// is created for a nil or non-sampled Transaction, or one +// whose max spans limit has been reached. +var droppedSpanPool sync.Pool + +// StartSpan starts and returns a new Span within the transaction, +// with the specified name, type, and optional parent span, and +// with the start time set to the current time relative to the +// transaction's timestamp. +// +// StartSpan always returns a non-nil Span. Its End method must +// be called when the span completes. +func (tx *Transaction) StartSpan(name, spanType string, parent *Span) *Span { + if tx == nil || !tx.Sampled() { + return newDroppedSpan() + } + + var span *Span + tx.mu.Lock() + if tx.maxSpans > 0 && len(tx.spans) >= tx.maxSpans { + tx.spansDropped++ + tx.mu.Unlock() + return newDroppedSpan() + } + span, _ = tx.tracer.spanPool.Get().(*Span) + if span == nil { + span = &Span{ + Duration: -1, + parent: -1, + } + } + span.tx = tx + span.id = int64(len(tx.spans)) + tx.spans = append(tx.spans, span) + tx.mu.Unlock() + + span.Name = name + span.Type = spanType + span.Timestamp = time.Now() + if parent != nil { + span.parent = parent.id + } + return span +} + +// Span describes an operation within a transaction. +type Span struct { + tx *Transaction // nil if span is dropped + id int64 + parent int64 + Name string + Type string + Timestamp time.Time + Duration time.Duration + Context SpanContext + + mu sync.Mutex + stacktrace []stacktrace.Frame +} + +func newDroppedSpan() *Span { + span, _ := droppedSpanPool.Get().(*Span) + if span == nil { + span = &Span{} + } + return span +} + +func (s *Span) reset() { + *s = Span{ + Context: s.Context, + Duration: -1, + parent: -1, + stacktrace: s.stacktrace[:0], + } + s.Context.reset() +} + +// SetStacktrace sets the stacktrace for the span, +// skipping the first skip number of frames, +// excluding the SetStacktrace function. +func (s *Span) SetStacktrace(skip int) { + if s.Dropped() { + return + } + s.stacktrace = stacktrace.AppendStacktrace(s.stacktrace[:0], skip+1, -1) +} + +// Dropped indicates whether or not the span is dropped, meaning it will not +// be included in any transaction. Spans are dropped by Transaction.StartSpan +// if the transaction is nil, non-sampled, or the transaction's max spans +// limit has been reached. +// +// Dropped may be used to avoid any expensive computation required to set +// the span's context. +func (s *Span) Dropped() bool { + return s.tx == nil +} + +// End marks the s as being complete; s must not be used after this. +// +// If s.Duration has not been set, End will set it to the elapsed time +// since s.Timestamp. +func (s *Span) End() { + if s.Dropped() { + droppedSpanPool.Put(s) + return + } + s.mu.Lock() + if s.Duration < 0 { + s.Duration = time.Since(s.Timestamp) + } + if len(s.stacktrace) == 0 && s.Duration >= s.tx.spanFramesMinDuration { + s.SetStacktrace(1) + } + s.mu.Unlock() +} + +func (s *Span) finalize(end time.Time) { + s.mu.Lock() + if s.Duration < 0 { + // s.End was never called, so mark it as truncated and + // truncate its duration to the end of the transaction. + s.Type += ".truncated" + s.Duration = end.Sub(s.Timestamp) + } + s.mu.Unlock() +} diff --git a/vendor/github.com/elastic/apm-agent-go/spancontext.go b/vendor/github.com/elastic/apm-agent-go/spancontext.go new file mode 100644 index 00000000000..b0aefb3b3ad --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/spancontext.go @@ -0,0 +1,46 @@ +package elasticapm + +import ( + "github.com/elastic/apm-agent-go/model" +) + +// SpanContext provides methods for setting span context. +type SpanContext struct { + model model.SpanContext + database model.DatabaseSpanContext +} + +// DatabaseSpanContext holds database span context. +type DatabaseSpanContext struct { + // Instance holds the database instance name. + Instance string + + // Statement holds the statement executed in the span, + // e.g. "SELECT * FROM foo". + Statement string + + // Type holds the database type, e.g. "sql". + Type string + + // User holds the username used for database access. + User string +} + +func (c *SpanContext) build() *model.SpanContext { + switch { + case c.model.Database != nil: + default: + return nil + } + return &c.model +} + +func (c *SpanContext) reset() { + *c = SpanContext{} +} + +// SetDatabase sets the span context for database-related operations. +func (c *SpanContext) SetDatabase(db DatabaseSpanContext) { + c.database = model.DatabaseSpanContext(db) + c.model.Database = &c.database +} diff --git a/vendor/github.com/elastic/apm-agent-go/stacktrace.go b/vendor/github.com/elastic/apm-agent-go/stacktrace.go new file mode 100644 index 00000000000..0fc7dda8a17 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/stacktrace.go @@ -0,0 +1,35 @@ +package elasticapm + +import ( + "path/filepath" + + "github.com/elastic/apm-agent-go/model" + "github.com/elastic/apm-agent-go/stacktrace" +) + +func appendModelStacktraceFrames(out []model.StacktraceFrame, in []stacktrace.Frame) []model.StacktraceFrame { + for _, f := range in { + out = append(out, modelStacktraceFrame(f)) + } + return out +} + +func modelStacktraceFrame(in stacktrace.Frame) model.StacktraceFrame { + var abspath string + file := in.File + if file != "" { + if filepath.IsAbs(file) { + abspath = file + } + file = filepath.Base(file) + } + packagePath, function := stacktrace.SplitFunctionName(in.Function) + return model.StacktraceFrame{ + AbsolutePath: abspath, + File: file, + Line: in.Line, + Function: function, + Module: packagePath, + LibraryFrame: stacktrace.IsLibraryPackage(packagePath), + } +} diff --git a/vendor/github.com/elastic/apm-agent-go/stacktrace/context.go b/vendor/github.com/elastic/apm-agent-go/stacktrace/context.go new file mode 100644 index 00000000000..00b182d15d6 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/stacktrace/context.go @@ -0,0 +1,83 @@ +package stacktrace + +import ( + "bufio" + "net/http" + "os" + + "github.com/elastic/apm-agent-go/model" +) + +// SetContext sets the source context for the given stack frames, +// with the specified number of pre- and post- lines. +func SetContext(setter ContextSetter, frames []model.StacktraceFrame, pre, post int) error { + for i := 0; i < len(frames); i++ { + if err := setter.SetContext(&frames[i], pre, post); err != nil { + return err + } + } + return nil +} + +// ContextSetter is an interface that can be used for setting the source +// context for a stack frame. +type ContextSetter interface { + // SetContext sets the source context for the given stack frame, + // with the specified number of pre- and post- lines. + SetContext(frame *model.StacktraceFrame, pre, post int) error +} + +// FileSystemContextSetter returns a ContextSetter that sets context +// by reading file contents from the provided http.FileSystem. +func FileSystemContextSetter(fs http.FileSystem) ContextSetter { + if fs == nil { + panic("fs is nil") + } + return &fileSystemContextSetter{fs} +} + +type fileSystemContextSetter struct { + http.FileSystem +} + +func (s *fileSystemContextSetter) SetContext(frame *model.StacktraceFrame, pre, post int) error { + if frame.Line <= 0 { + return nil + } + f, err := s.Open(frame.AbsolutePath) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + defer f.Close() + + var lineno int + var line string + preLines := make([]string, 0, pre) + postLines := make([]string, 0, post) + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + lineno++ + if lineno > frame.Line+post { + break + } + switch { + case lineno == frame.Line: + line = scanner.Text() + case lineno < frame.Line && lineno >= frame.Line-pre: + preLines = append(preLines, scanner.Text()) + case lineno > frame.Line && lineno <= frame.Line+post: + postLines = append(postLines, scanner.Text()) + } + } + if err := scanner.Err(); err != nil { + return err + } + frame.ContextLine = line + frame.PreContext = preLines + frame.PostContext = postLines + return nil +} diff --git a/vendor/github.com/elastic/apm-agent-go/stacktrace/doc.go b/vendor/github.com/elastic/apm-agent-go/stacktrace/doc.go new file mode 100644 index 00000000000..5b30b20605e --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/stacktrace/doc.go @@ -0,0 +1,3 @@ +// Package stacktrace provides a simplified stack frame type, +// functions for obtaining stack frames, and related utilities. +package stacktrace diff --git a/vendor/github.com/elastic/apm-agent-go/stacktrace/frame.go b/vendor/github.com/elastic/apm-agent-go/stacktrace/frame.go new file mode 100644 index 00000000000..d193562dc54 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/stacktrace/frame.go @@ -0,0 +1,17 @@ +package stacktrace + +// Frame describes a stack frame. +type Frame struct { + // File is the filename of the location of the stack frame. + // This may be either the absolute or base name of the file. + File string + + // Line is the 1-based line number of the location of the + // stack frame, or zero if unknown. + Line int + + // Function is the name of the function name for this stack + // frame. This should be package-qualified, and may be split + // using stacktrace.SplitFunctionName. + Function string +} diff --git a/vendor/github.com/elastic/apm-agent-go/stacktrace/generate_library.bash b/vendor/github.com/elastic/apm-agent-go/stacktrace/generate_library.bash new file mode 100644 index 00000000000..1c8de0a61cc --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/stacktrace/generate_library.bash @@ -0,0 +1,68 @@ +#!/bin/bash + +set -e + +_PKGS=$(go list -f '{{printf "\t%q: true,\n" .ImportPath}}' "$@") + +cat > library.go < 0 { + pc = make([]uintptr, n) + pc = pc[:runtime.Callers(skip+1, pc)] + } else { + // n is negative, get all frames. + n = 0 + pc = make([]uintptr, 10) + for { + n += runtime.Callers(skip+n+1, pc[n:]) + if n < len(pc) { + pc = pc[:n] + break + } + pc = append(pc, 0) + } + } + return AppendCallerFrames(frames, pc) +} + +// AppendCallerFrames appends to frames for the PCs in callers, +// and returns the extended slice. +// +// See RuntimeFrame for information on what details are included. +func AppendCallerFrames(frames []Frame, callers []uintptr) []Frame { + if len(callers) == 0 { + return frames + } + runtimeFrames := runtime.CallersFrames(callers) + for { + runtimeFrame, more := runtimeFrames.Next() + frames = append(frames, RuntimeFrame(runtimeFrame)) + if !more { + break + } + } + return frames +} + +// RuntimeFrame returns a Frame based on the given runtime.Frame. +// +// The resulting Frame will have the file path, package-qualified +// function name, and line number set. The function name can be +// split using SplitFunctionName, and the absolute path of the +// file and its base name can be determined using standard filepath +// functions. +func RuntimeFrame(in runtime.Frame) Frame { + return Frame{ + File: in.File, + Function: in.Function, + Line: in.Line, + } +} + +// SplitFunctionName splits the function name as formatted in +// runtime.Frame.Function, and returns the package path and +// function name components. +func SplitFunctionName(in string) (packagePath, function string) { + function = in + if function == "" { + return "", "" + } + // The last part of a package path will always have "." + // encoded as "%2e", so we can pick off the package path + // by finding the last part of the package path, and then + // the proceeding ".". + // + // Unexported method names may contain the package path. + // In these cases, the method receiver will be enclosed + // in parentheses, so we can treat that as the start of + // the function name. + sep := strings.Index(function, ".(") + if sep >= 0 { + packagePath = unescape(function[:sep]) + function = function[sep+1:] + } else { + offset := 0 + if sep := strings.LastIndex(function, "/"); sep >= 0 { + offset = sep + } + if sep := strings.IndexRune(function[offset+1:], '.'); sep >= 0 { + packagePath = unescape(function[:offset+1+sep]) + function = function[offset+1+sep+1:] + } + } + return packagePath, function +} + +func unescape(s string) string { + var n int + for i := 0; i < len(s); i++ { + if s[i] == '%' { + n++ + } + } + if n == 0 { + return s + } + bytes := make([]byte, 0, len(s)-2*n) + for i := 0; i < len(s); i++ { + b := s[i] + if b == '%' && i+2 < len(s) { + b = fromhex(s[i+1])<<4 | fromhex(s[i+2]) + i += 2 + } + bytes = append(bytes, b) + } + return string(bytes) +} + +func fromhex(b byte) byte { + if b >= 'a' { + return 10 + b - 'a' + } + return b - '0' +} diff --git a/vendor/github.com/elastic/apm-agent-go/stats.go b/vendor/github.com/elastic/apm-agent-go/stats.go new file mode 100644 index 00000000000..a144eb1792b --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/stats.go @@ -0,0 +1,33 @@ +package elasticapm + +// TracerStats holds statistics for a Tracer. +type TracerStats struct { + Errors TracerStatsErrors + ErrorsSent uint64 + ErrorsDropped uint64 + TransactionsSent uint64 + TransactionsDropped uint64 +} + +// TracerStatsErrors holds error statistics for a Tracer. +type TracerStatsErrors struct { + SetContext uint64 + SendTransactions uint64 + SendErrors uint64 +} + +func (s TracerStats) isZero() bool { + return s == TracerStats{} +} + +// accumulate updates the stats by accumulating them with +// the values in rhs. +func (s *TracerStats) accumulate(rhs TracerStats) { + s.Errors.SetContext += rhs.Errors.SetContext + s.Errors.SendTransactions += rhs.Errors.SendTransactions + s.Errors.SendErrors += rhs.Errors.SendErrors + s.ErrorsSent += rhs.ErrorsSent + s.ErrorsDropped += rhs.ErrorsDropped + s.TransactionsSent += rhs.TransactionsSent + s.TransactionsDropped += rhs.TransactionsDropped +} diff --git a/vendor/github.com/elastic/apm-agent-go/tracer.go b/vendor/github.com/elastic/apm-agent-go/tracer.go new file mode 100644 index 00000000000..5c088fd8d6f --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/tracer.go @@ -0,0 +1,560 @@ +package elasticapm + +import ( + "context" + "fmt" + "log" + "regexp" + "strings" + "sync" + "time" + + "github.com/elastic/apm-agent-go/model" + "github.com/elastic/apm-agent-go/stacktrace" + "github.com/elastic/apm-agent-go/transport" +) + +const ( + defaultPreContext = 3 + defaultPostContext = 3 + transactionsChannelCap = 1000 + errorsChannelCap = 1000 + + // defaultMaxErrorQueueSize is the default maximum number + // of errors to enqueue in the tracer. When this fills up, + // errors will start being dropped (when the channel is + // also full). + defaultMaxErrorQueueSize = 1000 +) + +var ( + // DefaultTracer is the default global Tracer, set at package + // initialization time, configured via environment variables. + // + // This will always be initialized to a non-nil value. If any + // of the environment variables are invalid, the corresponding + // errors will be logged to stderr and the default values will + // be used instead. + DefaultTracer *Tracer +) + +func init() { + var opts options + opts.init(true) + DefaultTracer = newTracer(opts) +} + +type options struct { + flushInterval time.Duration + maxTransactionQueueSize int + maxSpans int + sampler Sampler + sanitizedFieldNames *regexp.Regexp + captureBody CaptureBodyMode + spanFramesMinDuration time.Duration + serviceName string + serviceVersion string + serviceEnvironment string + active bool +} + +func (opts *options) init(continueOnError bool) error { + var errs []error + flushInterval, err := initialFlushInterval() + if err != nil { + flushInterval = defaultFlushInterval + errs = append(errs, err) + } + + maxTransactionQueueSize, err := initialMaxTransactionQueueSize() + if err != nil { + maxTransactionQueueSize = defaultMaxTransactionQueueSize + errs = append(errs, err) + } + + maxSpans, err := initialMaxSpans() + if err != nil { + maxSpans = defaultMaxSpans + errs = append(errs, err) + } + + sampler, err := initialSampler() + if err != nil { + sampler = nil + errs = append(errs, err) + } + + sanitizedFieldNames, err := initialSanitizedFieldNamesRegexp() + if err != nil { + sanitizedFieldNames = defaultSanitizedFieldNames + errs = append(errs, err) + } + + captureBody, err := initialCaptureBody() + if err != nil { + captureBody = CaptureBodyOff + errs = append(errs, err) + } + + spanFramesMinDuration, err := initialSpanFramesMinDuration() + if err != nil { + spanFramesMinDuration = defaultSpanFramesMinDuration + errs = append(errs, err) + } + + active, err := initialActive() + if err != nil { + active = true + errs = append(errs, err) + } + + if len(errs) != 0 && !continueOnError { + return errs[0] + } + for _, err := range errs { + log.Printf("[elasticapm]: %s", err) + } + + opts.flushInterval = flushInterval + opts.maxTransactionQueueSize = maxTransactionQueueSize + opts.maxSpans = maxSpans + opts.sampler = sampler + opts.sanitizedFieldNames = sanitizedFieldNames + opts.captureBody = captureBody + opts.spanFramesMinDuration = spanFramesMinDuration + opts.serviceName, opts.serviceVersion, opts.serviceEnvironment = initialService() + opts.active = active + return nil +} + +// Tracer manages the sampling and sending of transactions to +// Elastic APM. +// +// Transactions are buffered until they are flushed (forcibly +// with a Flush call, or when the flush timer expires), or when +// the maximum transaction queue size is reached. Failure to +// send will be periodically retried. Once the queue limit has +// been reached, new transactions will replace older ones in +// the queue. +// +// Errors are sent as soon as possible, but will buffered and +// later sent in bulk if the tracer is busy, or otherwise cannot +// send to the server, e.g. due to network failure. There is +// a limit to the number of errors that will be buffered, and +// once that limit has been reached, new errors will be dropped +// until the queue is drained. +// +// The exported fields be altered or replaced any time up until +// any Tracer methods have been invoked. +type Tracer struct { + Transport transport.Transport + Service struct { + Name string + Version string + Environment string + } + + process *model.Process + system *model.System + + active bool + closing chan struct{} + closed chan struct{} + forceFlush chan chan<- struct{} + configCommands chan tracerConfigCommand + transactions chan *Transaction + errors chan *Error + + statsMu sync.Mutex + stats TracerStats + + maxSpansMu sync.RWMutex + maxSpans int + + spanFramesMinDurationMu sync.RWMutex + spanFramesMinDuration time.Duration + + samplerMu sync.RWMutex + sampler Sampler + + captureBodyMu sync.RWMutex + captureBody CaptureBodyMode + + errorPool sync.Pool + spanPool sync.Pool + transactionPool sync.Pool +} + +// NewTracer returns a new Tracer, using the default transport, +// initializing a Service with the specified name and version, +// or taking the service name and version from the environment +// if unspecified. +// +// If serviceName is empty, then the service name will be defined +// using the ELASTIC_APM_SERVER_NAME environment variable. +func NewTracer(serviceName, serviceVersion string) (*Tracer, error) { + var opts options + if err := opts.init(false); err != nil { + return nil, err + } + if serviceName != "" { + if err := validateServiceName(serviceName); err != nil { + return nil, err + } + opts.serviceName = serviceName + opts.serviceVersion = serviceVersion + } + return newTracer(opts), nil +} + +func newTracer(opts options) *Tracer { + t := &Tracer{ + Transport: transport.Default, + process: ¤tProcess, + system: &localSystem, + closing: make(chan struct{}), + closed: make(chan struct{}), + forceFlush: make(chan chan<- struct{}), + configCommands: make(chan tracerConfigCommand), + transactions: make(chan *Transaction, transactionsChannelCap), + errors: make(chan *Error, errorsChannelCap), + maxSpans: opts.maxSpans, + sampler: opts.sampler, + captureBody: opts.captureBody, + spanFramesMinDuration: opts.spanFramesMinDuration, + active: opts.active, + } + t.Service.Name = opts.serviceName + t.Service.Version = opts.serviceVersion + t.Service.Environment = opts.serviceEnvironment + + if !t.active { + close(t.closed) + return t + } + + go t.loop() + t.configCommands <- func(cfg *tracerConfig) { + cfg.flushInterval = opts.flushInterval + cfg.maxTransactionQueueSize = opts.maxTransactionQueueSize + cfg.maxErrorQueueSize = defaultMaxErrorQueueSize + cfg.sanitizedFieldNames = opts.sanitizedFieldNames + cfg.preContext = defaultPreContext + cfg.postContext = defaultPostContext + } + return t +} + +// Close closes the Tracer, preventing transactions from being +// sent to the APM server. +func (t *Tracer) Close() { + select { + case <-t.closing: + default: + close(t.closing) + } + <-t.closed +} + +// Flush waits for the Tracer to flush any transactions and errors it currently +// has queued to the APM server, the tracer is stopped, or the abort channel +// is signaled. +func (t *Tracer) Flush(abort <-chan struct{}) { + flushed := make(chan struct{}, 1) + select { + case t.forceFlush <- flushed: + select { + case <-abort: + case <-flushed: + case <-t.closed: + } + case <-t.closed: + } +} + +// Active reports whether the tracer is active. If the tracer is inactive, +// no transactions or errors will be sent to the Elastic APM server. +func (t *Tracer) Active() bool { + return t.active +} + +// SetFlushInterval sets the flush interval -- the amount of time +// to wait before flushing enqueued transactions to the APM server. +func (t *Tracer) SetFlushInterval(d time.Duration) { + t.sendConfigCommand(func(cfg *tracerConfig) { + cfg.flushInterval = d + }) +} + +// SetMaxTransactionQueueSize sets the maximum transaction queue size -- the +// maximum number of transactions to buffer before flushing to the APM server. +// If set to a non-positive value, the queue size is unlimited. +func (t *Tracer) SetMaxTransactionQueueSize(n int) { + t.sendConfigCommand(func(cfg *tracerConfig) { + cfg.maxTransactionQueueSize = n + }) +} + +// SetMaxErrorQueueSize sets the maximum error queue size -- the +// maximum number of errors to buffer before they will start getting +// dropped. If set to a non-positive value, the queue size is unlimited. +func (t *Tracer) SetMaxErrorQueueSize(n int) { + t.sendConfigCommand(func(cfg *tracerConfig) { + cfg.maxErrorQueueSize = n + }) +} + +// SetContextSetter sets the stacktrace.ContextSetter to be used for +// setting stacktrace source context. If nil (which is the initial +// value), no context will be set. +func (t *Tracer) SetContextSetter(setter stacktrace.ContextSetter) { + t.sendConfigCommand(func(cfg *tracerConfig) { + cfg.contextSetter = setter + }) +} + +// SetLogger sets the Logger to be used for logging the operation of +// the tracer. +func (t *Tracer) SetLogger(logger Logger) { + t.sendConfigCommand(func(cfg *tracerConfig) { + cfg.logger = logger + }) +} + +// SetSanitizedFieldNames sets the patterns that will be used to match +// cookie and form field names for sanitization. Fields matching any +// of the the supplied patterns will have their values redacted. If +// SetSanitizedFieldNames is called with no arguments, then no fields +// will be redacted. +func (t *Tracer) SetSanitizedFieldNames(patterns ...string) error { + var re *regexp.Regexp + if len(patterns) != 0 { + var err error + re, err = regexp.Compile(fmt.Sprintf("(?i:%s)", strings.Join(patterns, "|"))) + if err != nil { + return err + } + } + t.sendConfigCommand(func(cfg *tracerConfig) { + cfg.sanitizedFieldNames = re + }) + return nil +} + +func (t *Tracer) sendConfigCommand(cmd tracerConfigCommand) { + select { + case t.configCommands <- cmd: + case <-t.closing: + case <-t.closed: + } +} + +// SetSampler sets the sampler the tracer. It is valid to pass nil, +// in which case all transactions will be sampled. +func (t *Tracer) SetSampler(s Sampler) { + t.samplerMu.Lock() + t.sampler = s + t.samplerMu.Unlock() +} + +// SetMaxSpans sets the maximum number of spans that will be added +// to a transaction before dropping. If set to a non-positive value, +// the number of spans is unlimited. +func (t *Tracer) SetMaxSpans(n int) { + t.maxSpansMu.Lock() + t.maxSpans = n + t.maxSpansMu.Unlock() +} + +// SetSpanFramesMinDuration sets the minimum duration for a span after which +// we will capture its stack frames. +func (t *Tracer) SetSpanFramesMinDuration(d time.Duration) { + t.spanFramesMinDurationMu.Lock() + t.spanFramesMinDuration = d + t.spanFramesMinDurationMu.Unlock() +} + +// SetCaptureBody sets the HTTP request body capture mode. +func (t *Tracer) SetCaptureBody(mode CaptureBodyMode) { + t.captureBodyMu.Lock() + t.captureBody = mode + t.captureBodyMu.Unlock() +} + +// Stats returns the current TracerStats. This will return the most +// recent values even after the tracer has been closed. +func (t *Tracer) Stats() TracerStats { + t.statsMu.Lock() + stats := t.stats + t.statsMu.Unlock() + return stats +} + +func (t *Tracer) loop() { + defer close(t.closed) + + ctx, cancelContext := context.WithCancel(context.Background()) + defer cancelContext() + go func() { + select { + case <-t.closing: + cancelContext() + } + }() + + var cfg tracerConfig + var flushed chan<- struct{} + var flushC <-chan time.Time + var transactions []*Transaction + var errors []*Error + var statsUpdates TracerStats + sender := sender{ + tracer: t, + cfg: &cfg, + stats: &statsUpdates, + } + + errorsC := t.errors + forceFlush := t.forceFlush + flushTimer := time.NewTimer(0) + if !flushTimer.Stop() { + <-flushTimer.C + } + startTimer := func() { + if flushC != nil { + // Timer already started. + return + } + if !flushTimer.Stop() { + select { + case <-flushTimer.C: + default: + } + } + flushTimer.Reset(cfg.flushInterval) + flushC = flushTimer.C + } + receivedTransaction := func(tx *Transaction, stats *TracerStats) { + if cfg.maxTransactionQueueSize > 0 && len(transactions) >= cfg.maxTransactionQueueSize { + // The queue is full, so pop the oldest item. + // TODO(axw) use container/ring? implement + // ring buffer on top of slice? profile + n := uint64(len(transactions) - cfg.maxTransactionQueueSize + 1) + for _, tx := range transactions[:n] { + tx.reset() + t.transactionPool.Put(tx) + } + transactions = transactions[n:] + stats.TransactionsDropped += n + } + transactions = append(transactions, tx) + } + + for { + var sendTransactions bool + statsUpdates = TracerStats{} + + select { + case <-t.closing: + return + case cmd := <-t.configCommands: + cmd(&cfg) + if cfg.maxErrorQueueSize <= 0 || len(errors) < cfg.maxErrorQueueSize { + errorsC = t.errors + } + continue + case e := <-errorsC: + errors = append(errors, e) + case tx := <-t.transactions: + beforeLen := len(transactions) + receivedTransaction(tx, &statsUpdates) + if len(transactions) == beforeLen && flushC != nil { + // The queue was already full, and a retry + // timer is running; wait for it to fire. + t.statsMu.Lock() + t.stats.accumulate(statsUpdates) + t.statsMu.Unlock() + continue + } + if cfg.maxTransactionQueueSize <= 0 || len(transactions) < cfg.maxTransactionQueueSize { + startTimer() + continue + } + sendTransactions = true + case <-flushC: + flushC = nil + sendTransactions = true + case flushed = <-forceFlush: + // The caller has explicitly requested a flush, so + // drain any transactions buffered in the channel. + for n := len(t.transactions); n > 0; n-- { + tx := <-t.transactions + receivedTransaction(tx, &statsUpdates) + } + // flushed will be signaled, and forceFlush set back to + // t.forceFlush, when the queued transactions and/or + // errors are successfully sent. + forceFlush = nil + flushC = nil + sendTransactions = true + } + + if remainder := cfg.maxErrorQueueSize - len(errors); remainder > 0 { + // Drain any errors in the channel, up to the maximum queue size. + for n := len(t.errors); n > 0 && remainder > 0; n-- { + errors = append(errors, <-t.errors) + remainder-- + } + } + if sender.sendErrors(ctx, errors) { + for _, e := range errors { + e.reset() + t.errorPool.Put(e) + } + errors = errors[:0] + errorsC = t.errors + } else if len(errors) == cfg.maxErrorQueueSize { + errorsC = nil + } + if sendTransactions { + if sender.sendTransactions(ctx, transactions) { + for _, tx := range transactions { + tx.reset() + t.transactionPool.Put(tx) + } + transactions = transactions[:0] + } + } + + if !statsUpdates.isZero() { + t.statsMu.Lock() + t.stats.accumulate(statsUpdates) + t.statsMu.Unlock() + + if statsUpdates.Errors.SendTransactions != 0 || statsUpdates.Errors.SendErrors != 0 { + // Sending transactions or errors failed, start a new timer to resend. + startTimer() + continue + } + } + if sendTransactions && flushed != nil { + forceFlush = t.forceFlush + flushed <- struct{}{} + flushed = nil + } + } +} + +// tracerConfig holds the tracer's runtime configuration, which may be modified +// by sending a tracerConfigCommand to the tracer's configCommands channel. +type tracerConfig struct { + flushInterval time.Duration + maxTransactionQueueSize int + maxErrorQueueSize int + logger Logger + contextSetter stacktrace.ContextSetter + preContext, postContext int + sanitizedFieldNames *regexp.Regexp +} + +type tracerConfigCommand func(*tracerConfig) diff --git a/vendor/github.com/elastic/apm-agent-go/transaction.go b/vendor/github.com/elastic/apm-agent-go/transaction.go new file mode 100644 index 00000000000..8e7a3eaf9fd --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/transaction.go @@ -0,0 +1,144 @@ +package elasticapm + +import ( + cryptorand "crypto/rand" + "encoding/binary" + "math/rand" + "sync" + "time" +) + +// StartTransaction returns a new Transaction with the specified +// name and type, and with the start time set to the current time. +func (t *Tracer) StartTransaction(name, transactionType string, opts ...TransactionOption) *Transaction { + tx, _ := t.transactionPool.Get().(*Transaction) + if tx == nil { + tx = &Transaction{ + tracer: t, + Duration: -1, + Context: Context{ + captureBodyMask: CaptureBodyTransactions, + }, + } + var seed int64 + if err := binary.Read(cryptorand.Reader, binary.LittleEndian, &seed); err != nil { + seed = time.Now().UnixNano() + } + tx.rand = rand.New(rand.NewSource(seed)) + } + tx.Name = name + tx.Type = transactionType + + var txOpts transactionOptions + for _, o := range opts { + o(&txOpts) + } + + // Generate a random transaction ID. + binary.LittleEndian.PutUint64(tx.id[:8], tx.rand.Uint64()) + binary.LittleEndian.PutUint64(tx.id[8:], tx.rand.Uint64()) + + // Take a snapshot of the max spans config to ensure + // that once the maximum is reached, all future span + // creations are dropped. + t.maxSpansMu.RLock() + tx.maxSpans = t.maxSpans + t.maxSpansMu.RUnlock() + + t.spanFramesMinDurationMu.RLock() + tx.spanFramesMinDuration = t.spanFramesMinDuration + t.spanFramesMinDurationMu.RUnlock() + + t.samplerMu.RLock() + sampler := t.sampler + t.samplerMu.RUnlock() + tx.sampled = true + if sampler != nil && !sampler.Sample(tx) { + tx.sampled = false + } + tx.Timestamp = time.Now() + return tx +} + +// Transaction describes an event occurring in the monitored service. +type Transaction struct { + Name string + Type string + Timestamp time.Time + Duration time.Duration + Context Context + Result string + id [16]byte + + tracer *Tracer + sampled bool + maxSpans int + spanFramesMinDuration time.Duration + + mu sync.Mutex + spans []*Span + spansDropped int + rand *rand.Rand // for ID generation +} + +// reset resets the Transaction back to its zero state, so it can be reused +// in the transaction pool. +func (tx *Transaction) reset() { + for _, s := range tx.spans { + s.reset() + tx.tracer.spanPool.Put(s) + } + *tx = Transaction{ + tracer: tx.tracer, + spans: tx.spans[:0], + Context: tx.Context, + Duration: -1, + rand: tx.rand, + } + tx.Context.reset() +} + +// Discard discards a previously started transaction. The Transaction +// must not be used after this. +func (tx *Transaction) Discard() { + tx.reset() + tx.tracer.transactionPool.Put(tx) +} + +// Sampled reports whether or not the transaction is sampled. +func (tx *Transaction) Sampled() bool { + return tx.sampled +} + +// End enqueues tx for sending to the Elastic APM server; tx must not +// be used after this. +// +// If tx.Duration has not been set, End will set it to the elapsed +// time since tx.Timestamp. +func (tx *Transaction) End() { + if tx.Duration < 0 { + tx.Duration = time.Since(tx.Timestamp) + } + for _, s := range tx.spans { + s.finalize(tx.Timestamp.Add(tx.Duration)) + } + tx.enqueue() +} + +func (tx *Transaction) enqueue() { + select { + case tx.tracer.transactions <- tx: + default: + // Enqueuing a transaction should never block. + tx.tracer.statsMu.Lock() + tx.tracer.stats.TransactionsDropped++ + tx.tracer.statsMu.Unlock() + tx.reset() + tx.tracer.transactionPool.Put(tx) + } +} + +// TransactionOption sets options when starting a transaction. +type TransactionOption func(*transactionOptions) + +type transactionOptions struct{} diff --git a/vendor/github.com/elastic/apm-agent-go/transport/api.go b/vendor/github.com/elastic/apm-agent-go/transport/api.go new file mode 100644 index 00000000000..1708ad1c1a9 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/transport/api.go @@ -0,0 +1,18 @@ +package transport + +import ( + "context" + + "github.com/elastic/apm-agent-go/model" +) + +// Transport provides an interface for sending transactions and errors +// payloads to Elastic APM. Methods are not required to be safe for +// concurrent use; tracers should serialize the calls. +type Transport interface { + // SendTransactions sends the transactions payload to the server. + SendTransactions(context.Context, *model.TransactionsPayload) error + + // SendErrors sends the errors payload to the server. + SendErrors(context.Context, *model.ErrorsPayload) error +} diff --git a/vendor/github.com/elastic/apm-agent-go/transport/debug.go b/vendor/github.com/elastic/apm-agent-go/transport/debug.go new file mode 100644 index 00000000000..8dab0622800 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/transport/debug.go @@ -0,0 +1,31 @@ +package transport + +import ( + "context" + "log" + "sync/atomic" + + "github.com/elastic/apm-agent-go/internal/pretty" + "github.com/elastic/apm-agent-go/model" +) + +type debugTransport struct { + id uint64 + transport Transport +} + +func (dt *debugTransport) SendTransactions(ctx context.Context, p *model.TransactionsPayload) error { + id := atomic.AddUint64(&dt.id, 1) + log.Printf("elasticapm SendTransactions %d -> %# v", id, pretty.Formatter(p)) + err := dt.transport.SendTransactions(ctx, p) + log.Printf("elasticapm SendTransactions %d <- %v", id, err) + return err +} + +func (dt *debugTransport) SendErrors(ctx context.Context, p *model.ErrorsPayload) error { + id := atomic.AddUint64(&dt.id, 1) + log.Printf("elasticapm SendErrors %d -> %# v", id, pretty.Formatter(p)) + err := dt.transport.SendErrors(ctx, p) + log.Printf("elasticapm SendErrors %d <- %v", id, err) + return err +} diff --git a/vendor/github.com/elastic/apm-agent-go/transport/default.go b/vendor/github.com/elastic/apm-agent-go/transport/default.go new file mode 100644 index 00000000000..61b03b76770 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/transport/default.go @@ -0,0 +1,51 @@ +package transport + +import ( + "os" + + "github.com/elastic/apm-agent-go/internal/apmdebug" +) + +var ( + // Default is the default Transport, using the + // ELASTIC_APM_* environment variables. + // + // If ELASTIC_APM_SERVER_URL is not defined, then + // Defaultwill be set to Discard. If it is defined, + // but invalid, then Default will be set to a transport + // returning an error for every operation. + Default Transport + + // Discard is a Transport on which all operations + // succeed without doing anything. + Discard = discardTransport{} +) + +func init() { + _, _ = InitDefault() +} + +// InitDefault (re-)initializes Default, the default transport, returning +// its new value along with the error that will be returned by the transport +// if the environment variable configuration is invalid. The Transport returned +// is always non-nil. +func InitDefault() (Transport, error) { + t, err := getDefault() + if apmdebug.TraceTransport { + t = &debugTransport{transport: t} + } + Default = t + return t, err +} + +func getDefault() (Transport, error) { + url := os.Getenv(envServerURL) + if url == "" { + return Discard, nil + } + t, err := NewHTTPTransport(url, "") + if err != nil { + return discardTransport{err}, err + } + return t, nil +} diff --git a/vendor/github.com/elastic/apm-agent-go/transport/discard.go b/vendor/github.com/elastic/apm-agent-go/transport/discard.go new file mode 100644 index 00000000000..42dda4b068c --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/transport/discard.go @@ -0,0 +1,19 @@ +package transport + +import ( + "context" + + "github.com/elastic/apm-agent-go/model" +) + +type discardTransport struct { + err error +} + +func (t discardTransport) SendTransactions(context.Context, *model.TransactionsPayload) error { + return t.err +} + +func (t discardTransport) SendErrors(context.Context, *model.ErrorsPayload) error { + return t.err +} diff --git a/vendor/github.com/elastic/apm-agent-go/transport/doc.go b/vendor/github.com/elastic/apm-agent-go/transport/doc.go new file mode 100644 index 00000000000..a98f1d74143 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/transport/doc.go @@ -0,0 +1,3 @@ +// Package transport provides an interface and implementation +// for transporting data to the Elastic APM server. +package transport diff --git a/vendor/github.com/elastic/apm-agent-go/transport/http.go b/vendor/github.com/elastic/apm-agent-go/transport/http.go new file mode 100644 index 00000000000..27609b8d927 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/transport/http.go @@ -0,0 +1,245 @@ +package transport + +import ( + "bytes" + "compress/gzip" + "context" + "crypto/tls" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "os" + "strings" + + "github.com/pkg/errors" + + "github.com/elastic/apm-agent-go/internal/fastjson" + "github.com/elastic/apm-agent-go/model" +) + +const ( + transactionsPath = "/v1/transactions" + errorsPath = "/v1/errors" + + envSecretToken = "ELASTIC_APM_SECRET_TOKEN" + envServerURL = "ELASTIC_APM_SERVER_URL" + envVerifyServerCert = "ELASTIC_APM_VERIFY_SERVER_CERT" + + // gzipThresholdBytes is the minimum size of the uncompressed + // payload before we'll consider gzip-compressing it. + gzipThresholdBytes = 1024 +) + +var ( + // Take a copy of the http.DefaultTransport pointer, + // in case another package replaces the value later. + defaultHTTPTransport = http.DefaultTransport.(*http.Transport) +) + +// HTTPTransport is an implementation of Transport, sending payloads via +// a net/http client. +type HTTPTransport struct { + Client *http.Client + baseURL *url.URL + transactionsURL *url.URL + errorsURL *url.URL + headers http.Header + gzipHeaders http.Header + jsonWriter fastjson.Writer + gzipWriter *gzip.Writer + gzipBuffer bytes.Buffer +} + +// NewHTTPTransport returns a new HTTPTransport, which can be used for sending +// transactions and errors to the APM server at the specified URL, with the +// given secret token. +// +// If the URL specified is the empty string, then NewHTTPTransport will use the +// value of the ELASTIC_APM_SERVER_URL environment variable, if defined; if +// the environment variable is also undefined, then an error will be returned. +// The URL must be the base server URL, excluding any transactions or errors +// path. e.g. "http://elastic-apm.example:8200". +// +// If the secret token specified is the empty string, then NewHTTPTransport +// will use the value of the ELASTIC_APM_SECRET_TOKEN environment variable, if +// defined; if the environment variable is also undefined, then requests will +// not be authenticated. +// +// If ELASTIC_APM_VERIFY_SERVER_CERT is set to "false", then the transport +// will not verify the APM server's TLS certificate. +// +// The Client field will be initialized with a new http.Client configured from +// ELASTIC_APM_* environment variables. The Client field may be modified or +// replaced, e.g. in order to specify TLS root CAs. +func NewHTTPTransport(serverURL, secretToken string) (*HTTPTransport, error) { + if serverURL == "" { + serverURL = os.Getenv(envServerURL) + if serverURL == "" { + return nil, errors.Errorf( + "URL not specified, and $%s not set", envServerURL, + ) + } + } + req, err := http.NewRequest("POST", serverURL, nil) + if err != nil { + return nil, err + } + + client := &http.Client{} + if req.URL.Scheme == "https" && os.Getenv(envVerifyServerCert) == "false" { + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + } + client.Transport = &http.Transport{ + Proxy: defaultHTTPTransport.Proxy, + DialContext: defaultHTTPTransport.DialContext, + MaxIdleConns: defaultHTTPTransport.MaxIdleConns, + IdleConnTimeout: defaultHTTPTransport.IdleConnTimeout, + TLSHandshakeTimeout: defaultHTTPTransport.TLSHandshakeTimeout, + ExpectContinueTimeout: defaultHTTPTransport.ExpectContinueTimeout, + TLSClientConfig: tlsConfig, + } + } + + headers := make(http.Header) + headers.Set("Content-Type", "application/json") + if secretToken == "" { + secretToken = os.Getenv(envSecretToken) + } + if secretToken != "" { + headers.Set("Authorization", "Bearer "+secretToken) + } + + gzipHeaders := make(http.Header) + for k, v := range headers { + gzipHeaders[k] = v + } + gzipHeaders.Set("Content-Encoding", "gzip") + + t := &HTTPTransport{ + Client: client, + baseURL: req.URL, + transactionsURL: urlWithPath(req.URL, transactionsPath), + errorsURL: urlWithPath(req.URL, errorsPath), + headers: headers, + gzipHeaders: gzipHeaders, + } + t.gzipWriter = gzip.NewWriter(&t.gzipBuffer) + return t, nil +} + +// SendTransactions sends the transactions payload over HTTP. +func (t *HTTPTransport) SendTransactions(ctx context.Context, p *model.TransactionsPayload) error { + t.jsonWriter.Reset() + p.MarshalFastJSON(&t.jsonWriter) + req := requestWithContext(ctx, t.newTransactionsRequest()) + return t.sendPayload(req, "SendTransactions") +} + +// SendErrors sends the errors payload over HTTP. +func (t *HTTPTransport) SendErrors(ctx context.Context, p *model.ErrorsPayload) error { + t.jsonWriter.Reset() + p.MarshalFastJSON(&t.jsonWriter) + req := requestWithContext(ctx, t.newErrorsRequest()) + return t.sendPayload(req, "SendErrors") +} + +func (t *HTTPTransport) sendPayload(req *http.Request, op string) error { + buf := t.jsonWriter.Bytes() + var body io.Reader = bytes.NewReader(buf) + req.ContentLength = int64(len(buf)) + if req.ContentLength >= gzipThresholdBytes { + t.gzipBuffer.Reset() + t.gzipWriter.Reset(&t.gzipBuffer) + if _, err := io.Copy(t.gzipWriter, body); err != nil { + return err + } + if err := t.gzipWriter.Flush(); err != nil { + return err + } + req.ContentLength = int64(t.gzipBuffer.Len()) + body = &t.gzipBuffer + req.Header = t.gzipHeaders + } + req.Body = ioutil.NopCloser(body) + + resp, err := t.Client.Do(req) + if err != nil { + return errors.Wrapf(err, "sending request for %s failed", op) + } + defer resp.Body.Close() + switch resp.StatusCode { + case http.StatusOK, http.StatusAccepted: + return nil + } + + // apm-server will return 503 Service Unavailable + // if the data cannot be published to Elasticsearch, + // but there is no Retry-After header included, so + // we treat it as any other internal server error. + bodyContents, err := ioutil.ReadAll(resp.Body) + if err == nil { + resp.Body = ioutil.NopCloser(bytes.NewReader(bodyContents)) + } + return &HTTPError{ + Op: op, + Response: resp, + Message: strings.TrimSpace(string(bodyContents)), + } +} + +func (t *HTTPTransport) newTransactionsRequest() *http.Request { + return t.newRequest(t.transactionsURL) +} + +func (t *HTTPTransport) newErrorsRequest() *http.Request { + return t.newRequest(t.errorsURL) +} + +func (t *HTTPTransport) newRequest(url *url.URL) *http.Request { + req := &http.Request{ + Method: "POST", + URL: url, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Header: t.headers, + Host: url.Host, + } + return req +} + +func urlWithPath(url *url.URL, p string) *url.URL { + urlCopy := *url + urlCopy.Path += p + if urlCopy.RawPath != "" { + urlCopy.RawPath += p + } + return &urlCopy +} + +// HTTPError is an error returned by HTTPTransport methods when requests fail. +type HTTPError struct { + Op string + Response *http.Response + Message string +} + +func (e *HTTPError) Error() string { + msg := fmt.Sprintf("%s failed with %s", e.Op, e.Response.Status) + if e.Message != "" { + msg += ": " + e.Message + } + return msg +} + +func requestWithContext(ctx context.Context, req *http.Request) *http.Request { + url := req.URL + req.URL = nil + reqCopy := req.WithContext(ctx) + reqCopy.URL = url + req.URL = url + return reqCopy +} diff --git a/vendor/github.com/elastic/apm-agent-go/utils.go b/vendor/github.com/elastic/apm-agent-go/utils.go new file mode 100644 index 00000000000..557a556104a --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/utils.go @@ -0,0 +1,98 @@ +package elasticapm + +import ( + "os" + "regexp" + "runtime" + "strings" + + "github.com/pkg/errors" + + "github.com/elastic/apm-agent-go/internal/apmstrings" + "github.com/elastic/apm-agent-go/model" +) + +var ( + currentProcess model.Process + goAgent = model.Agent{Name: "go", Version: AgentVersion} + goLanguage = model.Language{Name: "go", Version: runtime.Version()} + goRuntime = model.Runtime{Name: runtime.Compiler, Version: runtime.Version()} + localSystem model.System + + serviceNameInvalidRegexp = regexp.MustCompile("[^" + serviceNameValidClass + "]") +) + +const ( + envHostname = "ELASTIC_APM_HOSTNAME" + + serviceNameValidClass = "a-zA-Z0-9 _-" +) + +func init() { + currentProcess = getCurrentProcess() + localSystem = getLocalSystem() +} + +func getCurrentProcess() model.Process { + ppid := os.Getppid() + title, err := currentProcessTitle() + if err != nil { + title = os.Args[0] + } + return model.Process{ + Pid: os.Getpid(), + Ppid: &ppid, + Title: truncateString(title), + Argv: os.Args, + } +} + +func makeService(name, version, environment string) model.Service { + return model.Service{ + Name: truncateString(name), + Version: truncateString(version), + Environment: truncateString(environment), + Agent: goAgent, + Language: &goLanguage, + Runtime: &goRuntime, + } +} + +func getLocalSystem() model.System { + system := model.System{ + Architecture: runtime.GOARCH, + Platform: runtime.GOOS, + } + system.Hostname = os.Getenv(envHostname) + if system.Hostname == "" { + if hostname, err := os.Hostname(); err == nil { + system.Hostname = hostname + } + } + system.Hostname = truncateString(system.Hostname) + return system +} + +func validTagKey(k string) bool { + return !strings.ContainsAny(k, `.*"`) +} + +func validateServiceName(name string) error { + idx := serviceNameInvalidRegexp.FindStringIndex(name) + if idx == nil { + return nil + } + return errors.Errorf( + "invalid service name %q: character %q is not in the allowed set (%s)", + name, name[idx[0]], serviceNameValidClass, + ) +} + +func sanitizeServiceName(name string) string { + return serviceNameInvalidRegexp.ReplaceAllString(name, "_") +} + +func truncateString(s string) string { + // At the time of writing, all length limits are 1024. + return apmstrings.Truncate(s, 1024) +} diff --git a/vendor/github.com/elastic/apm-agent-go/utils_linux.go b/vendor/github.com/elastic/apm-agent-go/utils_linux.go new file mode 100644 index 00000000000..177b0c14d7d --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/utils_linux.go @@ -0,0 +1,23 @@ +package elasticapm + +import ( + "bytes" + "syscall" + "unsafe" +) + +func currentProcessTitle() (string, error) { + // PR_GET_NAME (since Linux 2.6.11) + // Return the name of the calling thread, in the buffer pointed to by + // (char *) arg2. The buffer should allow space for up to 16 bytes; + // the returned string will be null-terminated. + var buf [16]byte + if _, _, errno := syscall.RawSyscall6( + syscall.SYS_PRCTL, syscall.PR_GET_NAME, + uintptr(unsafe.Pointer(&buf[0])), + 0, 0, 0, 0, + ); errno != 0 { + return "", errno + } + return string(buf[:bytes.IndexByte(buf[:], 0)]), nil +} diff --git a/vendor/github.com/elastic/apm-agent-go/utils_other.go b/vendor/github.com/elastic/apm-agent-go/utils_other.go new file mode 100644 index 00000000000..fec1219f531 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/utils_other.go @@ -0,0 +1,8 @@ +//+build !linux + +package elasticapm + +func currentProcessTitle() (string, error) { + // TODO(axw) + return "", nil +} diff --git a/vendor/github.com/elastic/apm-agent-go/version.go b/vendor/github.com/elastic/apm-agent-go/version.go new file mode 100644 index 00000000000..aca88eecc83 --- /dev/null +++ b/vendor/github.com/elastic/apm-agent-go/version.go @@ -0,0 +1,6 @@ +package elasticapm + +const ( + // AgentVersion is the Elastic APM Go Agent version. + AgentVersion = "0.4.0" +) diff --git a/vendor/golang.org/x/sync/LICENSE b/vendor/golang.org/x/sync/LICENSE new file mode 100644 index 00000000000..6a66aea5eaf --- /dev/null +++ b/vendor/golang.org/x/sync/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/sync/PATENTS b/vendor/golang.org/x/sync/PATENTS new file mode 100644 index 00000000000..733099041f8 --- /dev/null +++ b/vendor/golang.org/x/sync/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go new file mode 100644 index 00000000000..533438d91c1 --- /dev/null +++ b/vendor/golang.org/x/sync/errgroup/errgroup.go @@ -0,0 +1,67 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package errgroup provides synchronization, error propagation, and Context +// cancelation for groups of goroutines working on subtasks of a common task. +package errgroup + +import ( + "sync" + + "golang.org/x/net/context" +) + +// A Group is a collection of goroutines working on subtasks that are part of +// the same overall task. +// +// A zero Group is valid and does not cancel on error. +type Group struct { + cancel func() + + wg sync.WaitGroup + + errOnce sync.Once + err error +} + +// WithContext returns a new Group and an associated Context derived from ctx. +// +// The derived Context is canceled the first time a function passed to Go +// returns a non-nil error or the first time Wait returns, whichever occurs +// first. +func WithContext(ctx context.Context) (*Group, context.Context) { + ctx, cancel := context.WithCancel(ctx) + return &Group{cancel: cancel}, ctx +} + +// Wait blocks until all function calls from the Go method have returned, then +// returns the first non-nil error (if any) from them. +func (g *Group) Wait() error { + g.wg.Wait() + if g.cancel != nil { + g.cancel() + } + return g.err +} + +// Go calls the given function in a new goroutine. +// +// The first call to return a non-nil error cancels the group; its error will be +// returned by Wait. +func (g *Group) Go(f func() error) { + g.wg.Add(1) + + go func() { + defer g.wg.Done() + + if err := f(); err != nil { + g.errOnce.Do(func() { + g.err = err + if g.cancel != nil { + g.cancel() + } + }) + } + }() +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 23c28eb7832..b5cbb4a18a4 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -252,6 +252,110 @@ "revision": "c941a3d38792137bfc53a3a121f40cebf20b5512", "revisionTime": "2018-06-13T21:14:35Z" }, + { + "checksumSHA1": "iS7awdGQOMgYrHf2XvIiT5w6weA=", + "path": "github.com/elastic/apm-agent-go", + "revision": "a22839728ee5955c96524d56af60813250d26a5b", + "revisionTime": "2018-06-18T03:19:02Z", + "version": "v0.4.0", + "versionExact": "v0.4.0" + }, + { + "checksumSHA1": "oCqBwFulgX7XDIyO07faoDP6QmM=", + "path": "github.com/elastic/apm-agent-go/internal/apmdebug", + "revision": "a22839728ee5955c96524d56af60813250d26a5b", + "revisionTime": "2018-06-18T03:19:02Z", + "version": "v0.4.0", + "versionExact": "v0.4.0" + }, + { + "checksumSHA1": "7dJYXMln/aY2uE4kcet8ELcJVCo=", + "path": "github.com/elastic/apm-agent-go/internal/apmhttputil", + "revision": "a22839728ee5955c96524d56af60813250d26a5b", + "revisionTime": "2018-06-18T03:19:02Z", + "version": "v0.4.0", + "versionExact": "v0.4.0" + }, + { + "checksumSHA1": "V51r1XndkriNAkSEMQvmOZ6QEcw=", + "path": "github.com/elastic/apm-agent-go/internal/apmstrings", + "revision": "a22839728ee5955c96524d56af60813250d26a5b", + "revisionTime": "2018-06-18T03:19:02Z", + "version": "v0.4.0", + "versionExact": "v0.4.0" + }, + { + "checksumSHA1": "2FGOw1HeQLBVHCH3gHUkAo0QB+g=", + "path": "github.com/elastic/apm-agent-go/internal/fastjson", + "revision": "a22839728ee5955c96524d56af60813250d26a5b", + "revisionTime": "2018-06-18T03:19:02Z", + "version": "v0.4.0", + "versionExact": "v0.4.0" + }, + { + "checksumSHA1": "1NiCEYGuDSVQWFu2DqMSosoGHRo=", + "path": "github.com/elastic/apm-agent-go/internal/krtext", + "revision": "a22839728ee5955c96524d56af60813250d26a5b", + "revisionTime": "2018-06-18T03:19:02Z", + "version": "v0.4.0", + "versionExact": "v0.4.0" + }, + { + "checksumSHA1": "NkjOdCL/WD8wTejMHFTBrWz9vEE=", + "path": "github.com/elastic/apm-agent-go/internal/pretty", + "revision": "a22839728ee5955c96524d56af60813250d26a5b", + "revisionTime": "2018-06-18T03:19:02Z", + "version": "v0.4.0", + "versionExact": "v0.4.0" + }, + { + "checksumSHA1": "Ftir4VbNGnS1/O0fnMohEe3jPIQ=", + "path": "github.com/elastic/apm-agent-go/internal/radix", + "revision": "a22839728ee5955c96524d56af60813250d26a5b", + "revisionTime": "2018-06-18T03:19:02Z", + "version": "v0.4.0", + "versionExact": "v0.4.0" + }, + { + "checksumSHA1": "47nLeS8ZcPyy6uu1zEVoNLf/ja0=", + "path": "github.com/elastic/apm-agent-go/internal/uuid", + "revision": "a22839728ee5955c96524d56af60813250d26a5b", + "revisionTime": "2018-06-18T03:19:02Z", + "version": "v0.4.0", + "versionExact": "v0.4.0" + }, + { + "checksumSHA1": "V6BFYs8WcmU8fKCZSaav4ml8nxY=", + "path": "github.com/elastic/apm-agent-go/model", + "revision": "a22839728ee5955c96524d56af60813250d26a5b", + "revisionTime": "2018-06-18T03:19:02Z", + "version": "v0.4.0", + "versionExact": "v0.4.0" + }, + { + "checksumSHA1": "SLappMH97o3pnOexptBBvDNAtsc=", + "path": "github.com/elastic/apm-agent-go/module/apmhttp", + "revision": "a22839728ee5955c96524d56af60813250d26a5b", + "revisionTime": "2018-06-18T03:19:02Z", + "version": "v0.4.0", + "versionExact": "v0.4.0" + }, + { + "checksumSHA1": "oWBpOvqWZI37sEBIZaFz1SrcOkY=", + "path": "github.com/elastic/apm-agent-go/stacktrace", + "revision": "a22839728ee5955c96524d56af60813250d26a5b", + "revisionTime": "2018-06-18T03:19:02Z", + "version": "v0.4.0", + "versionExact": "v0.4.0" + }, + { + "checksumSHA1": "IJ2rTYMtCOXDSHA0EbT3BaJa05s=", + "path": "github.com/elastic/apm-agent-go/transport", + "revision": "a22839728ee5955c96524d56af60813250d26a5b", + "revisionTime": "2018-06-18T03:19:02Z", + "version": "v0.4.0", + "versionExact": "v0.4.0" + }, { "checksumSHA1": "1ehnSD4NWvAj66S6kqtSiFENecM=", "path": "github.com/elastic/beats/libbeat/api", @@ -1795,6 +1899,12 @@ "revision": "c941a3d38792137bfc53a3a121f40cebf20b5512", "revisionTime": "2018-06-13T21:14:35Z" }, + { + "checksumSHA1": "S0DP7Pn7sZUmXc55IzZnNvERu6s=", + "path": "golang.org/x/sync/errgroup", + "revision": "1d60e4601c6fd243af51cc01ddf169918a5407ca", + "revisionTime": "2018-03-14T17:21:19Z" + }, { "checksumSHA1": "CNHEeGnucEUlTHJrLS2kHtfNbws=", "origin": "github.com/elastic/beats/vendor/golang.org/x/sys/unix",